背景
公司有个老项目,打包以及热更新都需要很长的时间,于是调研了下esbuild,决定使用esbuild-loader来提升构建速度
通过修改vue-cli源码输出打包花费的时间
为了方量化统计,我们需要改下Vue-cli的代码,显示打包或者server构建所需要的时间
build : {项目地址}\node_modules@vue\cli-service\lib\commands\build\index.js
serve: {项目地址}\node_modules@vue\cli-service\lib\commands\serve.js
build代码修改:
let startTime = ''
async function build (args, api, options) {
// ... other code
startTime = Date.parse(new Date())
// ... other code
return new Promise((resolve, reject) => {
webpack(webpackConfig, (err, stats) => {
stopSpinner(false)
if (!args.silent) {
const targetDirShort = path.relative(
api.service.context,
targetDir
)
log(formatStats(stats, targetDirShort, api))
if (args.target === 'app' && !isLegacyBuild) {
if (!args.watch) {
// 输出这次打包所花费的时间
console.log('打包花费时间:'+( Date.parse(new Date()) - startTime)/1000 + '秒')
done(`Build complete. The ${chalk.cyan(targetDirShort)} directory is ready to be deployed.`)
info(`Check out deployment instructions at ${chalk.cyan(`https://cli.vuejs.org/guide/deployment.html`)}\n`)
} else {
done(`Build complete. Watching for changes...`)
}
}
}
// test-only signal
if (process.env.VUE_CLI_TEST) {
console.log('Build complete.')
}
resolve()
})
})
}
build源代码位置:
serve代码修改:
let startTime = ''
//...other code
return new Promise((resolve, reject) => {
compiler.hooks.done.tap('vue-cli-service serve', stats => {
//...other code
const networkUrl = publicUrl
? publicUrl.replace(/([^/])$/, '$1/')
: urls.lanUrlForTerminal
// 输出花费的时间
console.log('打包花费时间:'+( Date.parse(new Date()) - startTime)/1000 + '秒')
console.log()
console.log(` App running at:`)
console.log(` - Local: ${chalk.cyan(urls.localUrlForTerminal)} ${copied}`)
//...other code
})
})
serve源代码位置
\
使用esBuild 替换 babel 对js进行处理
安装esbuild-loader
yarn add esbuild-loader -D
修改 vue.config.js文件,这里使用了 ESBuildMinifyPlugin 这个插件,来进行代码压缩,所以项目中其他同功能的plugin就可以移除了,比如uglifyjs-webpack-plugin
、terser
等,ESBuildMinifyPlugin
的具体的配置可以看esbuild
的官网 esbuild.github.io/,这里只添加了代码压缩以及删除生产环境的console debugger
const { ESBuildMinifyPlugin } = require('esbuild-loader')
module.exports = {
chainWebpack: (config) => {
const rule = config.module.rule('js');
// 清理自带的babel-loader
rule.uses.clear();
// 添加esbuild-loader
rule.use('esbuild-loader').loader('esbuild-loader');
config.optimization.minimizers.delete('terser');
// 生产模式下对代码进行压缩
if (isProduction) {
config.optimization
.minimizer('esbuild')
.use(ESBuildMinifyPlugin, [{ minify: true, css: true, drop:['console', 'debugger']}]);
}
}
}
针对jsx提供支持
如果项目中使用了jsx语法,则需要安装@lancercomet/vue2-jsx-runtime
这个包来处理添加配置如下
yarn add @lancercomet/vue2-jsx-runtime -D
const jsRule = config.module.rule('js').test(/.jsx?$/)
jsRule // 使用sbuild-loader
.use(['thread-loader', 'esbuild-loader'])
.loader('esbuild-loader')
.options({
target: 'es2015',
loader: 'jsx',
jsx: 'automatic',
jsxImportSource: '@lancercomet/vue2-jsx-runtime'
})
.end()
做完这一步,可能还会出现以下情况,webpack无法识别 mjs文件的导出
Can't import the named export 'xxxx' from non EcmaScript module (only default export is available)
还需要添加以下配置才能解决这个问题
config.module
.rule('mjs$')
.test(/.mjs$/)
.include
.add(/node_modules/)
.end()
.type('javascript/auto');
效果
可以看出来,对于开发模式的构建,提升其实不是特别大,但是对于生产环境构建来说,提升还是很明显的,达到了43%
命令 | babel-loader花费时间 | esbuild-loader花费时间 | 提升 |
---|---|---|---|
npm run serve | 64s | 42s | 18% |
npm run build | 78s | 44s | 43% |
下面是更加具体的显示
优化前:
npm run serve
- with cache
- without cache
npm run build
- with cache
- without cache
优化后:
npm run serve
- without cache
- with cache
npm run build
- without cache
- with cache