关于vue.js:vuecli3-vue2-保留-webpack-支持-vite-成功实践

50次阅读

共计 6473 个字符,预计需要花费 17 分钟才能阅读完成。

大家好!

文本是为了晋升开发效率及体验实际诞生的。

我的项目背景:

  • 脚手架:vue-cli3,具体为 "@vue/cli-service": "^3.4.1"
  • 库:vue2,具体为:"vue": "2.6.12"
  • 备注:没有 typescript , 非 ssr

痛点:随着工夫的推移、业务的一直迭代,依赖、性能、代码越来越多,我的项目本地启动比较慢、开发热更新比较慢。

改良指标:保留原来的 webpack, 反对 vite。并且尽可能的缩小改变,缩小保护老本。

思考:

  • vite 生产环境用的是 rollup,rollup 更适宜打包库。
  • vite 打包提效并没有进步太多。
  • 保留原来的 webpack 形式,尽可能的保障生产的稳固与平安。

实际

次要是解决三方面:

  • 配置文件需依据 vue.config.js 减少 vite.config.js
  • 入口文件 index.html 反对 vite 的形式
  • 配置 vite 启动命令
  • 可能须要

    • 路由懒加载,vite 须要非凡解决
    • 解决 commonjs 与 esModule 的引入与混用

减少 vite.config.js

在我的项目根目录创立一个 vite.config.js

装置所需依赖

npm i vite vite-plugin-vue2 -D

依据 vue.config.jsvite.config.js 中减少对应配置

// 若改了该文件逻辑,请对照着改一下 vue.config.js
import path from 'path'
import fs from 'fs'
import {defineConfig} from 'vite'
import config from './config'
import {createVuePlugin} from 'vite-plugin-vue2'
import {injectHtml} from 'vite-plugin-html'

const resolve = dir => path.join(__dirname, dir)

const alias = {
  vue: 'vue/dist/vue.esm.js',
  '@': resolve('src'),
}

const publicPath = '/'
const mode = 'development'

// https://vitejs.dev/config/
export default defineConfig({
  base: publicPath,
  plugins: [createVuePlugin(), 
  ],
  // 这些是注入我的项目中的变量,若有 process 结尾的,都须要在这儿注入,vite 默认不会注入 process 相干的变量及值
  define: {'process.env.NODE_ENV': JSON.stringify(mode),
    'process.env.publicPath': JSON.stringify(publicPath),
  },
  resolve: {
    // 配置别名
    alias,
    // 导入时想要省略的扩展名列表,必须退出.vue,因为 vite 默认不反对省略 .vue 后缀
    extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
  },
  server: {
    // 容许跨域
    cors: true,
    proxy: {// 可间接拷贝 vue.config.js 中对应的 proxy}
  }
})

sass 相干

配置非凡阐明:若我的项目中有用是 sass 预处理器, 且用到变量了。

须要装置 sass@1.32.13,不能装置比 1.32 更高的版本,跟高版本存在这个问题 Sass 报错: Using / for division is deprecated

npm i sass@1.32.13  -D
export default defineConfig({
  ...
  css: {
    // 给 sass 传递选项
    preprocessorOptions: {
      scss: {
        charset: false, // 最新版 sass 才反对
        additionalData: `@import "src/sass/common/var.scss";`,
      }
    },
  },
  ...
})

备注:如果生产环境用 vite 打包,采纳 sass@1.32.13 将会遇到这个问题 vite2 打包呈现正告,”@charset” must be the first,该如何打消呢?,然而 sass@1.32.13 不反对文档中的配置。所以这能够算是生产环境不必 vite 的一个起因。

还需全局替换 /deep/::v-deep

入口文件 index.html 反对 vite 的形式

因为 vite 的我的项目要求 index.html 文件在根目录,且有入口文件配置。

所以将 index.html 从 public 目录移到根目录。并退出

<% if (typeof isVite === 'boolean' && isVite) { %>
  <script type="module" src="/src/main.js"></script>
<% } %>

这样配置是为了能让 webpack、vite 形式都都反对,共用一个文件

而为了在 index.html 文件注入 isVite 变量,须要装置

npm i vite-plugin-html -D

须要在 vite.config.js 中配置

...
import {injectHtml} from 'vite-plugin-html'

export default defineConfig({
  ...
  plugins: [
    ...
    injectHtml({
      data: {
        title: 'vite-plugin-html-example',
        isVite: true
      },
    }),
  ],
  define: {'process.env.isVite': JSON.stringify(true)
  },
  ...
})

配置 vite 启动命令

最初在 package.json 中减少脚本

"scripts": {"vite-start": "vite"}

路由懒加载,vite 须要非凡解决

vue 实现路由懒加载的形式是这样的

  const Home = import(/* webpackChunkName: "[home]" */ `@/page/home.vue`)

  const routes = [
    {
      path: `/home`,
      name: 'home',
      component: Home
    },
  ]

然而 vite 不反对,解决

const modules = import.meta.glob('@/page/*.vue')

const Home = modules['@/page/home.vue']
const modules = import.meta.glob('@/page/*.vue')

// vite 会生成代码
const modules = {'@/page/home.vue': () => import('@/page/home.vue'),
  '@/page/page1.vue': () => import('@/page/page1.vue'),
  '@/page/page2.vue': () => import('@/page/page2.vue'),
  ...
}

参考:vite-Glob 导入

所以可封装一下:

function loadPage (view) {if (process.env.isVite) {const modules = import.meta.glob('../pages/*.vue')
    return modules[`../pages/${view}.vue`]
  }
  
  return () => import(/* webpackChunkName: "[request]" */ `@/pages/${view}`)
}

// 应用:const routes = [
  {
    path: `/home`,
    name: 'home',
    component: loadPage('home'),
  },
  {
    path: `/404`,
    name: '404',
    component: loadPage('404'),
  },
]

然而 webpack 不反对 import.meta,须要 loader 解决。解决:在本地建一个文件 webpack-import-meta-loader.js。

// 起源:https://github.com/KmjKoishi/webpack-import-meta-loader-fixed
// 是对 @open-wc/webpack-import-meta-loader 这个的修复
// 次要是修复 当 this.rootContext 不存在的判断解决。构建生产环境时不存在

/* eslint-disable */
// @ts-nocheck
const path = require('path');
function toBrowserPath(filePath, _path = path) {return filePath.replace(new RegExp(_path.sep === '\\' ? '\\\\' : _path.sep, 'g'), '/');
};
const regex = /import\.meta/g;

/**
 * Webpack loader to rewrite `import.meta` in modules with url data to the source code file location.
 *
 * @example
 * return import.meta;
 * // becomes: return ({url: `${window.location.protocol}//${window.location.host}/relative/path/to/file.js` });
 *
 * return import.meta.url;
 * // becomes: return ({url: `${window.location.protocol}//${window.location.host}/relative/path/to/file.js` }).url;
 */
module.exports = function (source) {const path = require('path');

  const relativePath = this.context.substring(this.context.indexOf(this.rootContext) + (this.rootContext && this.rootContext.length >= 0 ? (this.rootContext.length + 1) : 0),
    this.resource.lastIndexOf(path.sep) + 1,
  );

  const browserPath = toBrowserPath(relativePath);

  const fileName = this.resource.substring(this.resource.lastIndexOf(path.sep) + 1);

  let found = false;
  let rewrittenSource = source.replace(regex, () => {
    found = true;
    return `({url: getAbsoluteUrl('${browserPath}/${fileName}') })`;
  });

  if (found) {
    return `
      function getAbsoluteUrl(relativeUrl) {
        const publicPath = __webpack_public_path__;
        let url = '';
        if (!publicPath || publicPath.indexOf('://') < 0) {url += window.location.protocol + '//' + window.location.host;}
        if (publicPath) {url += publicPath;} else {url += '/';}
        return url + relativeUrl;
      }
${rewrittenSource}`;
  } else {return source;}
};

vue.config.js 批改配置:

const resolve = dir => require('path').join(__dirname, dir)

module.exports = {
  ...
  configureWebpack: {
    ...
    module: {
      rules: {
        ...
        {
          test: /index.js$/,
          use: [resolve('webpack-import-meta-loader'),
            'babel-loader'
          ],
          include: [resolve('src/router')]
        }
      }
    }
  }
  ...
}

解决 commonjs 与 esModule 的引入与混用

混用

webpack 形式下,若你的 src 我的项目源码中存在混用 commonjs 与 esModule。

计划一:不改源码,在 vite.config.js 中加配置,把 commonjs 转换为 esModule

装置 npm i cjs2esmodule -D

在 vite.config.js 中加配置

export default defineConfig({plugins: [cjs2esmVitePlugin()]
})

如果这个计划,能让你的我的项目运行失常。否则,可能须要采纳计划二。

计划二:把 src 代码中的 commonjs 语法本人手动改为 esModule

引入

如果你的我的项目在有一个 config.js, 被 vue.config.js 中应用。那么你可能须要解决。

vue.config.js 必须是 commonjs 语法的文件,能力被应用,否则会报错。

vite.config.js 既能够 esModule 语法,也能够 commonjs 语法。默认是 esModule 语法。

若下面混用,你是采纳计划二,而且 src 代码中也用了 config.js。那么你只能把 config.js 改成 esModule 的形式。此时 vue.config.js 不反对了,采纳的计划是依据 config.js 主动生成一个 config-cjs.js。

目标是缩小前期保护老本。

// transformConfig2cjs.js

// 运行我的项目的时候,会依据 config.js 主动生成文件:config-cjs.js, 以便让 vue-config.js 能间接应用
const {transformAsync} = require('@babel/core');
const plugin = require('@babel/plugin-transform-modules-commonjs')
const fs = require('fs')
const resolve = dir => require('path').join(__dirname, dir)
async function transfrom () {const inputPath = resolve('config.js')
  const input = fs.readFileSync(inputPath, 'utf-8')
  const {code} = await transformAsync(input, {
    sourceType: 'module',
    plugins: [plugin]
  });
  
  fs.writeFileSync(resolve('./config-cjs.js'), code);
}
transfrom()

而后在 vue.config.js 原来引入的 config.js 的中央改为 config-cjs.

最初在 package.json 中扭转下脚本,每次都从新生成一个最新的配置。

"scripts": {
  "transformConfig2cjs": "node transformConfig2cjs.js",
  "serve": "npm run transformConfig2cjs && vue-cli-service serve",
  "build": "npm run transformConfig2cjs && vue-cli-service build",
}

总结

遇到很多坑,都是语法相干的,最终都 11 解决了!

也尝试过一些其余计划,然而不能用,我的我的项目没有失效:

  • wp2vite

反对了之后,开发是真的很高效!

心愿这篇文章对有须要的有所帮忙。

正文完
 0