本文主要介紹如何在vuecli3生成的專案中,打包輸出時刪除console.log和使用dllplugin,並記錄了配置過程中踩到的坑。 (本人水平有限~希望大家多多指出有誤的地方)
一、生產環境中刪除console.log
在開發程式碼中寫的console.log,可以通過配置webpack4中的terser-webpack-plugin外掛達成目的,compress引數配置如下:
module.exports = {
optimization: {
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
warnings: false,
drop_console: true,
drop_debugger: true,
pure_funcs: ['console.log']
},
},
}),
],
},
};
複製程式碼
而@vue/cli-service的配置原始碼也是使用了terser-webpack-plugin外掛進行Tree Shaking,以下是@vue/cli-service/prod.js的原始碼
module.exports = (api, options) => {
api.chainWebpack(webpackConfig => {
if (process.env.NODE_ENV === 'production') {
const isLegacyBundle = process.env.VUE_CLI_MODERN_MODE && !process.env.VUE_CLI_MODERN_BUILD
const getAssetPath = require('../util/getAssetPath')
const filename = getAssetPath(
options,
`js/[name]${isLegacyBundle ? `-legacy` : ``}${options.filenameHashing ? '.[contenthash:8]' : ''}.js`
)
webpackConfig
.mode('production')
.devtool(options.productionSourceMap ? 'source-map' : false)
.output
.filename(filename)
.chunkFilename(filename)
// keep module.id stable when vendor modules does not change
webpackConfig
.plugin('hash-module-ids')
.use(require('webpack/lib/HashedModuleIdsPlugin'), [{
hashDigest: 'hex'
}])
// disable optimization during tests to speed things up
if (process.env.VUE_CLI_TEST) {
webpackConfig.optimization.minimize(false)
} else {
const TerserPlugin = require('terser-webpack-plugin')
const terserOptions = require('./terserOptions')
webpackConfig.optimization.minimizer([
new TerserPlugin(terserOptions(options))
])
}
}
})
}
複製程式碼
在 vue.config.js 中的 configureWebpack 選項提供一個物件會被 webpack-merge 合併入最終的 webpack 配置,因此vue-cli3構建的專案中只需要修改terserOptions即可,vue.config.js配置如下:
module.exports = {
publicPath: '/',
outputDir: 'dist',
devServer: {
port: 8080,
https: false,
hotOnly: true,
disableHostCheck: true,
open: true,
},
productionSourceMap: false, // 生產打包時不輸出map檔案,增加打包速度
configureWebpack: config => {
if (process.env.NODE_ENV === 'production') {
config.optimization.minimizer[0].options.terserOptions.compress.warnings = false
config.optimization.minimizer[0].options.terserOptions.compress.drop_console = true
config.optimization.minimizer[0].options.terserOptions.compress.drop_debugger = true
config.optimization.minimizer[0].options.terserOptions.compress.pure_funcs = ['console.log']
}
}
}
複製程式碼
配置完成後使用 vue inspect --mode=production > output.js
命令審查專案的 webpack 配置,optimization.minimizer的輸出如下:
optimization: {
minimizer: [
{
options: {
test: /\.m?js(\?.*)?$/i,
chunkFilter: () => true,
warningsFilter: () => true,
extractComments: false,
sourceMap: false,
cache: true,
cacheKeys: defaultCacheKeys => defaultCacheKeys,
parallel: true,
include: undefined,
exclude: undefined,
minify: undefined,
terserOptions: {
output: {
comments: /^\**!|@preserve|@license|@cc_on/i
},
compress: {
arrows: false,
collapse_vars: false,
comparisons: false,
computed_props: false,
hoist_funs: false,
hoist_props: false,
hoist_vars: false,
inline: false,
loops: false,
negate_iife: false,
properties: false,
reduce_funcs: false,
reduce_vars: false,
switches: false,
toplevel: false,
typeofs: false,
booleans: true,
if_return: true,
sequences: true,
unused: true,
conditionals: true,
dead_code: true,
evaluate: true,
warnings: false,
drop_console: true,
drop_debugger: true,
pure_funcs: [
'console.log'
]
},
mangle: {
safari10: true
}
}
}
}
],
}
複製程式碼
到此完成刪除console.log的配置,接下來記錄一下我踩到的坑~
坑1:在vue.config.js中直接使用terser-webpack-plugin後,通過vue inpect審查發現新增的compress引數並沒有直接進入原有的terserOptions,而是minimizer陣列新增了一個物件。這樣導致vue-cli原有的terser-webpack-plugin配置失效。打包會以cache和parallel為false的配置下進行打包輸出,打包速度變慢,因此後來採取直接修改terserOptions。
minimizer陣列新增了一個物件:
options: {
test: /\.m?js(\?.*)?$/i,
chunkFilter: () => true,
warningsFilter: () => true,
extractComments: false,
sourceMap: false,
cache: false,
cacheKeys: defaultCacheKeys => defaultCacheKeys,
parallel: false,
include: undefined,
exclude: undefined,
minify: undefined,
terserOptions: {
output: {
comments: /^\**!|@preserve|@license|@cc_on/i
},
compress: {
warnings: false,
drop_console: true,
drop_debugger: true,
pure_funcs: [
'console.log'
]
}
}
}
複製程式碼
坑2(未解決):在給.eslintrc.js的rules配置了no-console的情況下,修改程式碼後的首次打包eslint-loader總會在終端上報 error: Unexpected console statement (no-console)
,雖然打包過程中報錯,但是最終的輸出程式碼是沒有console.log的;(使用babel-plugin-transform-remove-console刪除console.log也會出現這種情況)
檢視@vue/cli-plugin-eslint的原始碼發現eslint的cache屬性為true,所以再次打包就不會對未修改的檔案進行檢測。
Eslint Node.js API對cache引數解釋如下:
cache - Operate only on changed files (default: false). Corresponds to --cache.
cli-plugin-eslint使用eslint-loader的關鍵程式碼如下:
api.chainWebpack(webpackConfig => {
webpackConfig.resolveLoader.modules.prepend(path.join(__dirname, 'node_modules'))
webpackConfig.module
.rule('eslint')
.pre()
.exclude
.add(/node_modules/)
.add(require('path').dirname(require.resolve('@vue/cli-service')))
.end()
.test(/\.(vue|(j|t)sx?)$/)
.use('eslint-loader')
.loader('eslint-loader')
.options({
extensions,
cache: true,
cacheIdentifier,
emitWarning: options.lintOnSave !== 'error',
emitError: options.lintOnSave === 'error',
eslintPath: resolveModule('eslint', cwd) || require.resolve('eslint'),
formatter:
loadModule('eslint/lib/formatters/codeframe', cwd, true) ||
require('eslint/lib/formatters/codeframe')
})
})
複製程式碼
如果要終端不輸出eslint的錯誤,可以在vue.config.js配置lintOnSave: process.env.NODE_ENV !== 'production'
生產環境構建時禁用,但是這樣與在eslintrc.js的rules中配置'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off'
的目的自相矛盾。
那麼是否有辦法讓eslint-loader在terser-webpack-plugin或者babel-plugin-transform-remove-console之後進行檢測呢?還是說配置了刪除console.log就沒必要配置'no-console'呢?希望有大神能回答我這個疑惑!
二、使用dllPlugin優化打包速度
網上已經有很多文章介紹dllPlugin的使用方法,這裡就不介紹dllPlugin的詳細配置說明了。本文只介紹一下針對vue-cli3專案使用webapck-chain方式的配置程式碼,所以就直接貼程式碼啦~
新增webpack.dll.config.js,程式碼如下:
const path = require('path')
const CleanWebpackPlugin = require('clean-webpack-plugin')
const webpack = require('webpack')
module.exports = {
mode: 'production',
entry: {
vendor: ['vue/dist/vue.runtime.esm.js', 'vuex', 'vue-router', 'element-ui'],
util: ['lodash']
},
output: {
filename: '[name].dll.js',
path: path.resolve(__dirname, 'dll'),
library: 'dll_[name]'
},
plugins: [
new CleanWebpackPlugin(), // clean-wepback-plugin目前已經更新到2.0.0,不需要傳引數path
new webpack.DllPlugin({
name: 'dll_[name]',
path: path.join(__dirname, 'dll', '[name].manifest.json'),
context: __dirname
})
]
}
複製程式碼
在vue.config.js新增DllReferencePlugin,最終程式碼如下:
const webpack = require('webpack')
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin')
const path = require('path')
const dllReference = (config) => {
config.plugin('vendorDll')
.use(webpack.DllReferencePlugin, [{
context: __dirname,
manifest: require('./dll/vendor.manifest.json')
}])
config.plugin('utilDll')
.use(webpack.DllReferencePlugin, [{
context: __dirname,
manifest: require('./dll/util.manifest.json')
}])
config.plugin('addAssetHtml')
.use(AddAssetHtmlPlugin, [
[
{
filepath: require.resolve(path.resolve(__dirname, 'dll/vendor.dll.js')),
outputPath: 'dll',
publicPath: '/dll'
},
{
filepath: require.resolve(path.resolve(__dirname, 'dll/util.dll.js')),
outputPath: 'dll',
publicPath: '/dll'
}
]
])
.after('html')
}
module.exports = {
publicPath: '/',
outputDir: 'dist',
devServer: {
port: 8080,
https: false,
hotOnly: true,
disableHostCheck: true,
open: true,
},
productionSourceMap: false, // 生產打包時不輸出map檔案,增加打包速度
chainWebpack: config => {
if (process.env.NODE_ENV === 'production') {
dllReference(config)
}
},
configureWebpack: config => {
if (process.env.NODE_ENV === 'production') {
config.optimization.minimizer[0].options.terserOptions.compress.warnings = false
config.optimization.minimizer[0].options.terserOptions.compress.drop_console = true
config.optimization.minimizer[0].options.terserOptions.compress.drop_debugger = true
config.optimization.minimizer[0].options.terserOptions.compress.pure_funcs = ['console.log']
}
}
}
複製程式碼
有3個地方需要說明一下:
1、webpack.dll.config.js檔案中的entry.vendor使用'vue/dist/vue.runtime.esm.js'作為vue的入口,是根據vue inspect > output.js
的檔案中resolve.alias決定的;(vue.runtime.esm.js還是vue.esm.js取決於vue create構建時的選擇)
resolve: {
alias: {
'@': '/Users/saki_bc/bccgithub/vue-webpack-demo/src',
vue$: 'vue/dist/vue.runtime.esm.js'
},
}
複製程式碼
2、在開發環境中不使用dllPlugin是因為chrome的vue devtool是不能檢測壓縮後的vue原始碼,使得沒辦法用vue devtool觀察vue專案的元件和資料狀態;
3、add-asset-html-webpack-plugin外掛必須在html-webpack-plugin之後使用,因此這裡要用webpack-chain來進行配置;至於為什麼'html'代表html-webpack-plugin,是因為@vue/cli-servide/lib/config/app.js裡是用plugin('html')來對映的,關鍵原始碼片段如下:
const HTMLPlugin = require('html-webpack-plugin')
webpackConfig.plugin('html')
.use(HTMLPlugin, [htmlOptions])
複製程式碼
4、這裡不使用在index.html裡新增script標籤的方式引入dll檔案,是因為當vue路由使用history模式,並且路由配置首頁重定向到其他url的情況下,在首頁重新整理頁面後dll檔案會以重定向後的url的根目錄引用,導致報錯找不到dll檔案。
如:dll的正確引用情況是http://www.xxx.com/vendor.dll.js
,重新整理重定向後變成 http://www.xxx.com/xxx/vendor.dll.js
;即使在index.html使用絕對路徑也是會出現這樣的情況,目前還不知道是不是html-webpack-plugin的bug;
結語
這次優化實踐仍然存在不少疑惑和且考慮的地方,webpack的更新發展也越來越快,vue-cli使用webpack-chain作為核心方式也增加了不少學習成本,接下來還需要閱讀相關原始碼,發現專案中配置不合理的地方~
也希望各位大家能分享一下使用webpack4過程中踩到的坑~