痛点
随着时间的推移、业务的不断迭代,依赖、功能、代码越来越多,项目本地启动比较慢、开发热更新比较慢。从数据上来看,从打开项目到运行起来,Time≈30s(25063ms + 4588ms).
改造目标
- 保留原有的各模式下的打包方式。
- 开发模式下增加支持
Vite
构建打包 - 以最小破坏性为原则,尽可能减少改动,减少维护成本。
Milestones
- 配置
vite
相关启动命令 - 根据
vue.config.js
无损增加vite.config.js
- 入口文件
index.html
支持Vite
,保持原文件不改动 - 处理
PoseCSS
或一些loader
, 处理全局引入公共样式文件,如variables.less
等 - 处理样式文件中导入文件路径
~@
开头的兼容 - 解决
CommonJS
的一些引入
遇到问题
[ERROR] No matching export in "browser-external:timers" for import "setTimeout"
node_modules/@xxx/es/vc-calendar/src/Picker.js:16:9:
16 │ import { setTimeout } from 'timers';
╵ ~~~~~~~~~~
ERROR 4:28:16 PM [vite] error while updating dependencies: 16:28:16
Error: Build failed with 1 error:
node_modules/@xxx/es/vc-calendar/src/Picker.js:16:9: ERROR: No matching export in "browser-external:timers" for import "setTimeout"
at failureErrorWithLog (/.../node_modules/esbuild/lib/main.js:1602:15)
从日志入手,看到是@xxx
(包名有改动)中有个组件引用了一个timers
的包。最直接的解决方案是手动安装所缺的包,但是究其原因,由于 Vitejs 不会自行填充 NodeJS 库,因此无法在浏览器中使用诸如Buffer
、fs
和path
之类的核心库。
在这里多提一句,当遇到一大串报错什么的情况,很多同学都害怕去耐心分析问题。这是不能成长的。遇到错别怕,很多时候debug大半天,还不如一行一行看错误信息来得快。
vite.config.js
import { defineConfig } from 'vite';
import { createVuePlugin } from 'vite-plugin-vue2';
import { createHtmlPlugin } from 'vite-plugin-html';
import { cjs2esmVitePlugin } from 'cjs2esmodule';
// import legacy from '@vitejs/plugin-legacy';
import eslint from '@rollup/plugin-eslint';
import dotenv from 'dotenv';
const path = require( 'path' );
const resolve = (dir) => {
return path.resolve( __dirname, dir );
};
const vueConfig = require( './vue.config.js' );
const vAlias = Object.assign( vueConfig.configureWebpack.resolve.alias, {
'~@': resolve( 'src' ),
'~@xxx': resolve( 'node_modules/@xxx' ),
} );
export default defineConfig( ({ mode }) => {
dotenv.config();
dotenv.config( { path: `./.env.${mode}` } );
const env = process.env;
const {
VUE_APP_NAME: appName,
VUE_APP_BUILD_MODE: buildMode,
VUE_APP_BASE_URL: baseUrl,
VUE_APP_PORT: appPort = 3001,
VUE_APP_GATEWAY_PATH: GATEWAY_PATH = 'pscpadmin',
} = env;
return {
base: buildMode === 'PROD' ? baseUrl : '/',
envPrefix: 'VUE_APP_',
define: {
'process.env': JSON.stringify( env ),
},
esbuild: false,
resolve: {
alias: vAlias, // '~@': path.resolve(__dirname, 'src'),
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', ".json", '.vue'],
},
css: {
preprocessorOptions: {
// 全局变量注入
less: {
// 当 common.less 和 variables.less 拥有相同变量时, common.less 优化级更高
// 这里的优先级更高
modifyVars: {
hack: `true; @import (reference) "${resolve( './src/styles/common.less' )}";`,
},
javascriptEnabled: true,
additionalData: `@import "${resolve( './src/styles/variables.less' )}";`,
},
},
},
// 为了与某些 Rollup 插件兼容,可能需要强制执行插件的顺序
// 可以使用 enforce 修饰符来强制插件的位置
plugins: [
{
...eslint({
include: ['**/*.{js,ts,vue}'],
exclude: [/^(?=.*type=(template|style)).*&/],
}),
enforce: 'pre',
apply: 'serve',
},
cjs2esmVitePlugin(), // 将 commonJS 转成 ES Module
// 在这里保持了原有的文件结构处理
createHtmlPlugin( {
template: './public/index.html',
entry: './src/main.ts',
inject: {
data: {
BASE_URL: baseUrl + 'dev/',
VUE_APP_NAME: appName,
},
},
} ),
createVuePlugin( {
jsx: true,
jsxOptions: { compositionAPI: true },
} ),
// legacy({
// // targets: ['defaults', 'not IE 11'],
// targets: ['ie >= 9'],
// additionalLegacyPolyfills: ['regenerator-runtime/runtime'],
// }),
],
optimizeDeps: {
// exclude: ['@xxx'], // 忽略预编译的包
},
server: {
host: true,
port: appPort,
open: true,
proxy: {
[`/${GATEWAY_PATH}`]: {
target: 'http://proxy.domain.cn/', // test
changeOrigin: true,
bypass: (req, res, proxyOption) => {
console.log( `当前请求代理:${req.url} -> ${proxyOption.target}` );
},
},
},
},
};
} );
总结
一切回归文档。在接触一样新的事物的时候,最好也是最优的方法是过一篇文档。这样可以让我们有了“大局观”。