2022 年最新前端 Vue 項(xiàng)目重構(gòu)總結(jié)
大廠技術(shù)??高級(jí)前端??Node進(jìn)階
點(diǎn)擊上方?程序員成長(zhǎng)指北,關(guān)注公眾號(hào)
回復(fù)1,加入高級(jí)Node交流群
本文主要內(nèi)容
對(duì)于老舊的項(xiàng)目,升級(jí)webpack的時(shí)我的操作步驟。 基于項(xiàng)目的產(chǎn)品定位和業(yè)務(wù)發(fā)展走勢(shì),在重構(gòu)時(shí)我可以從哪方面入手和思考。
本文在項(xiàng)目迭代,優(yōu)化中一直修改,所以用時(shí)一年。如果能給您帶來幫助,希望各位大佬可以動(dòng)動(dòng)小手給我點(diǎn)贊????,您的點(diǎn)贊是我寫文的最好的肯定!謝謝??
背景及解決方法
因?yàn)楣镜漠a(chǎn)品是把同類型的業(yè)務(wù)軟件在不同電商平臺(tái)上架,所以新開的項(xiàng)目是把老代碼移植過來,刪改拼湊后上架。因此文件目錄零散,引入導(dǎo)出混亂,代碼冗余,風(fēng)格不夠統(tǒng)一規(guī)范。這些毛病導(dǎo)致代碼可讀性和維護(hù)性很低,且樣式?jīng)_突很多,奇怪的難復(fù)現(xiàn)的bug也很多。我就計(jì)劃在迭代填坑中,對(duì)項(xiàng)目進(jìn)行一次改造優(yōu)化。我的解決步驟如下:
第一步:了解基本業(yè)務(wù)(三個(gè)月左右),值班時(shí)多詢問客戶建議,了解用戶的使用習(xí)慣和對(duì)我們軟件的風(fēng)格定位。對(duì)同類型的競(jìng)品(八個(gè)左右,我們軟件做的還可以)進(jìn)行了詳細(xì)的調(diào)研,分析我們產(chǎn)品的不足和優(yōu)勢(shì)。同時(shí)和產(chǎn)品積極溝通項(xiàng)目后期開發(fā)方向和計(jì)劃。我覺得項(xiàng)目在我接手的時(shí)候,功能目前只完成了計(jì)劃的20%,還有很高的完善空間。這個(gè)過程大概花費(fèi)了半年吧。 第二步:基于項(xiàng)目目錄混亂,我先對(duì)項(xiàng)目的目錄根據(jù)功能模塊重新劃分,把router路由path對(duì)應(yīng)文件路徑,利于后期模塊查找。 第三步:對(duì)基本資源對(duì)公共資源統(tǒng)一入口,這樣有利于后期資源的管理維護(hù),在代碼上也不用做重復(fù)引入的操作。例如css的公共css統(tǒng)一入口引入,公共變量通過loader可全局使用,不再手動(dòng)引入。對(duì)font采用動(dòng)態(tài)加載,刪除本地存儲(chǔ)的svg文件。 第四步:因?yàn)轫?xiàng)目的webpack版本是2,且為全手寫,考慮到后期同事維護(hù)和我本人計(jì)劃性分步迭代升級(jí)的改造方式,我沒有采用vue-cli,也采用了全手配。具體升級(jí)步驟在下面也有書寫 第五步:項(xiàng)目?jī)?nèi)部高復(fù)用邏輯封裝和內(nèi)部代碼邏輯優(yōu)化。因?yàn)槲覀冺?xiàng)目面對(duì)的是b端客戶,有很多數(shù)據(jù)的查詢,我就寫了一個(gè)具有列表的查詢,分頁(yè),搜索功能的model,配合vuex就很方便。
項(xiàng)目webpack升級(jí)
配置步驟
先把入口文件main.js的所有代碼都注釋掉,在根目錄創(chuàng)建文件夾:webpack(打包腳本的文件夾),webpack文件夾下創(chuàng)建 webpack.common.js(webpack通用配置)、webpack.development.js(webpack開發(fā)環(huán)境下配置腳本)、webpack.product.js(webpack生產(chǎn)環(huán)境下配置腳本)三個(gè)文件。
在main.js引入一個(gè)最簡(jiǎn)單的.vue文件,只有template模版,配置vue-loader,使項(xiàng)目正常運(yùn)行。在script腳本處編寫命令行:
{
????"dev":?"webpack-dev-server??./webpack/webpack.common.js?--mode='development'",????
????"build":?"webpack?--config?./webpack/webpack.common.js??--mode='production'"
??}
復(fù)制代碼
在.vue文件中寫js代碼,配置 babel,使項(xiàng)目正常運(yùn)行。 在.vue文件中寫css、less代碼,配置 css和less,使項(xiàng)目正常運(yùn)行。 在.vue文件中引入圖片,字體等,配置 靜態(tài)資源,使項(xiàng)目正常運(yùn)行。 在app.vue文件內(nèi)只引入簡(jiǎn)單組件,再嘗試引入一個(gè)頁(yè)面,使項(xiàng)目正常運(yùn)行。此時(shí)項(xiàng)目的基礎(chǔ)配置完成 區(qū)分環(huán)境變量,分開打包并配置腳本命令并優(yōu)化打包腳本
資源打包
vue配置
vue-loader: 允許你以一種名為單文件組件 \(SFCs\)[2]的格式撰寫 Vue 組件
npm?i?-D?vue-loader
復(fù)制代碼
modules配置
const?{?VueLoaderPlugin?}?=?require('vue-loader')
{
????output:?{
????????path:?path.resolve(__dirname,?'../dist'),
????????filename:?'[name].[chunkhash].js',
????????chunkFilename:?'[name].[chunkhash].js',
????????publicPath:?'/'
????},
????plugins:?[
????????new?VueLoaderPlugin(),
????],
????module:?{
??????rules:?[
????????????{
????????????????test:?/\.vue$/,
????????????????use:?[
????????????????????{
????????????????????loader:?'cache-loader'
????????????????????},
????????????????????{
????????????????????loader:?'vue-loader',
????????????????????options:?{
????????????????????????transformAssetUrls:?{
????????????????????????video:?['src',?'poster'],
????????????????????????source:?'src',
????????????????????????img:?'src',
????????????????????????image:?['xlink:href',?'href'],
????????????????????????use:?['xlink:href',?'href']
????????????????????????},
????????????????????????cssSourceMap:?true,
????????????????????????hotReload:?true,
????????????????????????compilerOptions:?{
????????????????????????preserveWhitespace:?true
????????????????????????}
????????????????????}
????????????????????}
????????????????],
????????????????exclude:?/node_modules/
????????????},
????????]
????},
}
復(fù)制代碼
驗(yàn)證:在終端輸入命令行npm run build無報(bào)錯(cuò),dist內(nèi)如下圖展示
babel配置
@babel/core: 把js 代碼分析成ast ,方便各個(gè)插件分析語(yǔ)法進(jìn)行相應(yīng)的處理
@babel/cli: 是_babel_?提供的命令行工具,用于命令行下編譯源代碼
babel-loader: 在Webpack打包的時(shí)候,用Babel將ES6的代碼轉(zhuǎn)換成ES5版本的,開啟緩存cacheDirectory:true,可以在node_modules/.cache內(nèi)看到緩存文件
@babel/preset-env: 可以根據(jù)配置的目標(biāo)瀏覽器或者運(yùn)行環(huán)境來自動(dòng)將ES2015+的代碼轉(zhuǎn)換為es5,配置useBuiltIns:true可實(shí)現(xiàn)按需引入。配置corejs:3指定corejs的版本。
core-js: 它是JavaScript標(biāo)準(zhǔn)庫(kù)的polyfill,盡可能的進(jìn)行模塊化,讓你能選擇你需要的功能。
可參考文章:\# babel兼容性實(shí)現(xiàn)方案[3]
npm?install?--save-dev?@babel/core?@babel/cli?@babel/preset-env?babel-loader?@babel/plugin-transform-runtime
復(fù)制代碼
.babelrc配置
{
????"presets":?[
????????["@babel/preset-env",?{
????????????"useBuiltIns":?"usage",
????????????"corejs":?3,
????????????"targets":?{
??????????????"browsers":?[">?1%",?"last?2?versions",?"not?ie?<=?8"]
????????????}
??????????}]
????],
????"plugins":?[
??????"@babel/plugin-transform-runtime"
????]
}
復(fù)制代碼
module配置
{
????test:?/\.js$/,
????use:?[
??????{
????????loader:?'babel-loader',
????????options:?{
??????????presets:?['@babel/preset-env'],
??????????babelrc:?true,
??????????cacheDirectory:?true?//?啟用緩存
????????}
??????}
????],
????exclude:?/node_modules/
}
復(fù)制代碼
"babel": "babel src/index.js \--out-dir dist" 命令來編譯 src/index.js測(cè)試文件npm run babel打包后的結(jié)果驗(yàn)證 
css
vue-style-loader: 把js 代碼分析成ast ,方便各個(gè)插件分析語(yǔ)法進(jìn)行相應(yīng)的處理
css-loader: 解析css文件中的@import和url語(yǔ)句,處理css-modules,并將結(jié)果作為一個(gè)js模塊返回
postcss-loader: 將css3轉(zhuǎn)為低版本瀏覽器兼容寫法,及兼容未來版本的css寫法,加載對(duì)應(yīng)的插件
autoprefixer:解析CSS文件并且添加瀏覽器前綴到CSS內(nèi)容里
postcss:使用插件去轉(zhuǎn)換CSS的工具
less-loader: 將less代碼轉(zhuǎn)譯為瀏覽器可以識(shí)別的CSS代碼
style-resources-loader:導(dǎo)入css 預(yù)處理器的一些公共的樣式文件變量
npm?install?--save-dev?vue-style-loader?css-loader?postcss-loader?autoprefixer?postcss?less-loader?style-resources-loader
復(fù)制代碼
module配置
{
????????test:?/\.less$/,
????????use:?[
??????????'vue-style-loader',
??????????{
????????????loader:?'css-loader',
????????????options:?{
??????????????importLoaders:?3
????????????}
??????????},
??????????{
????????????loader:?'postcss-loader',
????????????options:?{
??????????????indent:?'postcss',
??????????????plugins:?(loader)?=>?[
????????????????require('autoprefixer')()?//?添加前綴
??????????????],
??????????????sourceMap:?false
????????????}
??????????},
??????????{
????????????loader:?'less-loader',
????????????options:?{
??????????????javascriptEnabled:?true,
??????????????sourceMap:?true
????????????}
??????????},
??????????{
????????????loader:?'style-resources-loader',
????????????options:?{
??????????????patterns:?[
????????????????path.resolve(__dirname,?'../src/assets/css/variables/*.less'),
??????????????],
??????????????injector:?(source,?resources)?=>?{
????????????????const?combineAll?=?type?=>?resources
??????????????????.filter(({?file?})?=>?file.includes(type))
??????????????????.map(({?content?})?=>?content)
??????????????????.join('')
????????????????return?combineAll('variables')?+?combineAll('mixins')?+?source
??????????????}
????????????}
??????????}
????????],
????????exclude:?/node_modules/
??????},
復(fù)制代碼
npm run build打包后的結(jié)果驗(yàn)證 
less
style-resources-loader: 避免重復(fù)在每個(gè)樣式文件中@import導(dǎo)入,在各個(gè)css 文件中能夠直接使用 變量和公共的樣式
在css配置的基礎(chǔ)上,最后面添加style-resources-loader,這樣就再也不用手動(dòng)引入css變量
{
????????loader:?'style-resources-loader',
????????options:?{
??????????patterns:?[
????????????path.resolve(__dirname,?'../src/assets/css/variables/*.less')
??????????],
??????????injector:?(source,?resources)?=>?{
????????????const?combineAll?=?type?=>?resources
??????????????.filter(({?file?})?=>?file.includes(type))
??????????????.map(({?content?})?=>?content)
??????????????.join('')
????????????return?combineAll('variables')?+?combineAll('mixins')?+?source
??????????}
}
復(fù)制代碼
npm run build打包后的結(jié)果驗(yàn)證
MiniCssExtractPlugin: 提取JS中的CSS樣式,用 link 外部引入,減少JS文件的大小
const?MiniCssExtractPlugin?=?require('mini-css-extract-plugin')
{
????plugins:?[
????????new?MiniCssExtractPlugin({
??????????filename:?'[name].[contenthash].css',
??????????chunkFilename:?'[id].[contenthash].css',
??????????ignoreOrder:?true
????????}),
????]
}
復(fù)制代碼
把上面的 vue-style-loader 替換為 MiniCssExtractPlugin.loader
npm run build打包后的結(jié)果驗(yàn)證 
圖片&svg&音頻&font
svg-sprite-loader: 把js 代碼分析成ast ,方便各個(gè)插件分析語(yǔ)法進(jìn)行相應(yīng)的處理
url-loader: 解析css文件中的@import和url語(yǔ)句,處理css-modules,并將結(jié)果作為一個(gè)js模塊返回
npm?install?--save-dev?svg-sprite-loader?url-loader
復(fù)制代碼
?{
????????test:?/\.svg$/,
????????loader:?'svg-sprite-loader',
????????include:?[path.join(__dirname,?'..',?'src/assets/icon')],
????????options:?{
??????????symbolId:?'[name]',
??????????name:?path.posix.join('static',?'img/[name].[hash:7].[ext]')
????????}
??????},
??????{
????????test:?/\.(png|jpe?g|gif)(\?.*)?$/,
????????loader:?'url-loader',
????????exclude:?/node_modules/,
????????options:?{
??????????limit:?10000,
??????????name:?path.posix.join('static',?'img/[name].[hash:7].[ext]')
????????}
??????},
??????{
????????test:?/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
????????loader:?'url-loader',
????????exclude:?/node_modules/,
????????options:?{
??????????limit:?10000,
??????????name:?path.posix.join('static',?'media/[name].[hash:7].[ext]')
????????}
??????},
??????{
????????test:?/\.(woff|woff2?|eot|ttf|otf)(\?.*)?$/,
????????loader:?'url-loader',
????????options:?{
??????????limit:?10000,
??????????name:?path.posix.join('static',?'fonts/[name].[hash:7].[ext]')
????????}
??????}
復(fù)制代碼
公共部分webpack.common.js優(yōu)化
1. externals 排除外部依賴打包到bundle中
externals:?{
??'vue':?'Vue',
}
復(fù)制代碼
npm run build打包后的結(jié)果驗(yàn)證:設(shè)置externals后,dist內(nèi)找不到vue.js的package包了。下圖為設(shè)置前的截圖
2. resolve 縮小查找范圍 降低查找速度
resolve:?{
????extensions:?['.js',?'.vue',?'.json'],
????alias:?{
??????'vue$':?'vue/dist/vue.esm.js',
??????'@':?path.join(__dirname,?'..',?'src'),
??????'@services':?path.join(__dirname,?'..',?'src/api/services.js'),
??????'@productsManagement':?path.join(__dirname,?'..',?'src/modules/productsManagement')
????}
??},
復(fù)制代碼
3. cache-loader 緩存
cache-loader: 在一些性能開銷較大的 loader 之前添加 cache-loader,以便將結(jié)果緩存到磁盤里,此處寫在vue-loader的前面
?{
????test:?/\.vue$/,
????use:?[
??????{
????????loader:?'cache-loader'
??????},
??????{
????????loader:?'vue-loader'
??????}
????]
??},
復(fù)制代碼
npm run build打包后的結(jié)果驗(yàn)證:可以在node_modules下的.cache看到緩存的文件
3. plugins
DefinePlugin 變量替換WebpackBar 打包進(jìn)度展示FriendlyErrorsWebpackPlugin 配置終端輸出日志HtmlWebpackPlugin 動(dòng)態(tài)生成htmlLodashModuleReplacementPlugin 按需引入VueLoaderPlugin 熱重載HardSourceWebpackPlugin 緩存 webpack 內(nèi)部模塊thread-loader 多線程打包
const?WebpackBar?=?require('webpackbar')
const?FriendlyErrorsWebpackPlugin?=?require('friendly-errors-webpack-plugin')
const?LodashModuleReplacementPlugin?=?require('lodash-webpack-plugin')
const?HardSourceWebpackPlugin?=?require('hard-source-webpack-plugin')
const?argv?=?require('yargs-parser')(process.argv.slice(-3))
const?mode?=?argv.mode?||?'development'
const?isDev?=?mode?===?'development'
const?jsWorkerPool?=?{
??poolTimeout:?2000
}
plugins:?[
????new?webpack.DefinePlugin({
??????'process.env':?JSON.stringify(mode),
??????'process.env.BUILD_ENV':?JSON.stringify(mode)
????}),
????new?WebpackBar({
??????name:?isDev???'development'?:?'production',
??????color:?isDev???'#00953a'?:?'#f2a900'
????}),
????new?FriendlyErrorsWebpackPlugin(),
????new?LodashModuleReplacementPlugin(),
????new?VueLoaderPlugin(),
????new?HtmlWebpackPlugin({
??????filename:?'index.html',
??????template:?'index.html',
??????inject:?true,
??????minify:?{
????????removeComments:?true,
????????collapseWhitespace:?true,
????????removeAttributeQuotes:?true
??????}
????}),
????new?HardSourceWebpackPlugin({}),
],
復(fù)制代碼
HardSourceWebpackPlugin: 為模塊提供中間緩存,緩存默認(rèn)的存放路徑是:?node_modules/.cache/hard-source,首次構(gòu)建時(shí)間沒有太大變化,但是第二次開始,構(gòu)建時(shí)間大約可以節(jié)約 80%
npm run build打包后,HardSourceWebpackPlugin的結(jié)果驗(yàn)證 
LodashModuleReplacementPlugin: 該插件將會(huì)移除你未用到的lodash特性 npm run build打包后,LodashModuleReplacementPlugin的結(jié)果驗(yàn)證 
thread-loader: 把這個(gè) loader 放置在其他 loader 之前, 放置在這個(gè) loader 之后的 loader 就會(huì)在一個(gè)單獨(dú)的 worker 池(worker pool)中運(yùn)行,加快打包速度。這里先不實(shí)驗(yàn),因?yàn)?thread-loader 適合在耗時(shí)的 loader 上使用,不然反而會(huì)減慢速度。
4. optimization splitChunks& runtimeChunk(manifest)
splitChunks: 提取被重復(fù)引入的文件,單獨(dú)生成一個(gè)或多個(gè)文件,這樣避免在多入口重復(fù)打包文件
script-ext-html-webpack-plugin: 將 runtimeChunk 內(nèi)聯(lián)到我們的 index.html
runtimeChunk: 作用是將包含chunks映射關(guān)系的list單獨(dú)從app.js里提取出來,因?yàn)槊恳粋€(gè)chunk的id基本都是基于內(nèi)容hash出來的,所以你每次改動(dòng)都會(huì)影響它,如果不把它提取出來的話,等于app.js每次都會(huì)改變,緩存就失效了
const?ScriptExtHtmlWebpackPlugin?=?require('script-ext-html-webpack-plugin')
output:?{
??????path:?path.resolve(__dirname,?'../dist'),
??????filename:?'[name].[chunkhash].js',
??????chunkFilename:?'[name].[chunkhash].js',
??????publicPath:?'/'
},
plugins:[
????new?ScriptExtHtmlWebpackPlugin({
??????inline:?/runtime\..*\.js$/
????}),
],
optimization:?{
????runtimeChunk:?true,?//?構(gòu)建出runtime~xx文件
????splitChunks:?{
??????name:?true,?//?自動(dòng)處理文件名
??????chunks:?'all',
??????automaticNameDelimiter:?'-',
??????cacheGroups:?{
????????vendors:?{
??????????test:?/[\\/]node_modules[\\/]/,
??????????priority:?10,
??????????name:?'vendors',
??????????chunks:?'initial'
????????},
????????commons:?{
??????????name:?'commons',
??????????minChunks:?2,
??????????priority:?5,
??????????test:?path.join(__dirname,?'..',?'src/components'),
??????????reuseExistingChunk:?true
????????}
??????}
????}
??}
復(fù)制代碼
先在src下創(chuàng)建a.js、b.js文件,在main.js通過 動(dòng)態(tài)加載 import()引入,webpackChunkName為按需引入后打包的名稱。npm run build打包后,runtimeChunk的結(jié)果驗(yàn)證

npm run build打包后,splitChunks的結(jié)果驗(yàn)證 
npm run build打包后,cript-ext-html-webpack-plugin的結(jié)果驗(yàn)證 
5. stats bundle 配置終端輸出日志
stats: 后端打包腳本是通過docker部署,所以需要配置webpack輸出信息,不然info都是黑白的,看日志的時(shí)候比較費(fèi)勁
stats:?{
????colors:?true,
????modules:?false,
????children:?false,
????chunks:?false,
????chunkModules:?false
??}
復(fù)制代碼
開發(fā)環(huán)境優(yōu)化
1. devtool 調(diào)試方式
在webpack.common.js內(nèi)配置
const?merge?=?require('webpack-merge')
const?argv?=?require('yargs-parser')(process.argv.slice(-3))
const?mode?=?argv.mode?||?'development'
const?mergeConfig?=?require(`./webpack.${mode}.js`)
const?common?=?merge(commonConfig,?mergeConfig)
module.exports?=?common
復(fù)制代碼
devtool:?'cheap-module-eval-source-map',
復(fù)制代碼
2. devServer & HotModuleReplacementPlugin
這個(gè)一般都會(huì),就不贅述了
plugins:[?
????new?webpack.HotModuleReplacementPlugin()
],
devServer:?{
????historyApiFallback:?true,
????overlay:?{
??????errors:?true
????},
????//?通知文件更改
????watchOptions:?{
??????poll:?true
????},
????open:?false,
????hot:?true,
????proxy:?{
??????'/api':?{
????????target:?'http://localhost:10080/',
????????changeOrigin:?true,
????????pathRewrite:?{
??????????'^/api':?'/api'
????????}
??????}
????},
????host:?'0.0.0.0',
????port:?8000
},
復(fù)制代碼
生產(chǎn)環(huán)境優(yōu)化
todo: 考慮說出每個(gè)插件的作用 zhuanlan.zhihu.com/p/102632472[4]
1. plugins
CleanWebpackPlugin: 刪除dist
OptimizeCssAssetsPlugin: css優(yōu)化壓縮插件
cssnano: 一個(gè)?PostCSS[5]?插件,可以添加到你的構(gòu)建流程中,用于確保最終生成的 用于生產(chǎn)環(huán)境的 CSS 樣式表文件盡可能的小。CompressionPlugin: 壓縮生成gzip
const?{CleanWebpackPlugin}?=?require('clean-webpack-plugin')
const?MiniCssExtractPlugin?=?require('mini-css-extract-plugin')
const?OptimizeCssAssetsPlugin?=?require('optimize-css-assets-webpack-plugin')
const?CompressionPlugin?=?require('compression-webpack-plugin')
plugins:?[
????new?CleanWebpackPlugin(),
????new?OptimizeCssAssetsPlugin({
??????assetNameRegExp:?/\.less$/g,
??????cssProcessor:?require('cssnano'),
??????cssProcessorPluginOptions:?{
????????preset:?['default',?{
??????????discardComments:?{?removeAll:?true?},
??????????normalizeUnicode:?false,?//?建議false,否則在使用unicode-range的時(shí)候會(huì)產(chǎn)生亂碼
??????????safe:?true?//?避免?cssnano?重新計(jì)算?z-index
????????}]
??????},
??????canPrint:?true
????}),
????new?CompressionPlugin({
??????algorithm:?'gzip',?//?'brotliCompress'
??????test:?/\.js$|\.html$|\.css/,?//?+?$|\.svg$|\.png$|\.jpg
??????threshold:?10240,?//?對(duì)超過10k的數(shù)據(jù)壓縮
??????deleteOriginalAssets:?false?//?不刪除原文件
????})
??],
??optimization:?{
????moduleIds:?'size',
????minimizer:?[
??????//?這樣配置會(huì)存在只有css壓縮的問題,這時(shí)webpack4原本自己配置好的js壓縮會(huì)無效?,需要重新配置UglifyJsPlugin(用于壓縮js,webpack4內(nèi)置了)一下
??????//?https://www.jianshu.com/p/dd9afa5c4d0f
??????new?OptimizeCssAssetsPlugin({})
????]
??},
復(fù)制代碼
在后端項(xiàng)目ngix配置內(nèi)
gzip?on;
gzip_min_length?1k;
gzip_buffers?4?16k;
gzip_http_version?1.1;
gzip_comp_level?5;
gzip_types?text/plain?application/javascript?application/x-javascript?text/css?application/xml?text/javascript?application/x-httpd-php?image/jpeg?image/gif?image/png;
gzip_disable?"MSIE?[1-6]\.";
gzip_vary?on;
復(fù)制代碼
npm run build打包后, OptimizeCssAssetsPlugin的結(jié)果驗(yàn)證
npm run build打包后, CompressionPlugin 的結(jié)果驗(yàn)證

2. optimization
moduleIds 持久化緩存 如何看打包后的chunk效果[6]
optimization.moduleIds: 'size'
2. UglifyJsPlugin
?minimizer:?[
??new?UglifyJsPlugin({
????exclude:?/\.min\.js$/,
????parallel:?os.cpus().length,
????cache:?true,
????sourceMap:?true,
????uglifyOptions:?{
??????compress:?{
????????warnings:?false,
????????drop_console:?true,
????????collapse_vars:?true,
????????reduce_vars:?true
??????},
??????output:?{
????????beautify:?false,
????????comments:?false
??????}
????}
??})
]
復(fù)制代碼

項(xiàng)目?jī)?yōu)化之動(dòng)態(tài)加載
對(duì)于項(xiàng)目里比較大的組建都可以使用 es6的 import() 動(dòng)態(tài)加載,添加webpackChunkName魔法注釋。
不常用的modal、draw等彈出框,可以對(duì)這些組件異步延遲加載,從首屏加載的代碼剝離路由懶加載
{
??path:?'/productsManagement/allProducts',
??name:?'AllProducts',
??component:?()?=>?import(
????/*?webpackChunkName:?`AllProducts`?*/
????/*?webpackMode:?"lazy"?*/
????'@productsManagement/allProducts/DyProductList'),
??meta:?{
????keepAlive:?true,
????requiresAuth:?true
??}
},
復(fù)制代碼
成果對(duì)比
打包速度
優(yōu)化前,生產(chǎn)環(huán)境第一次打包時(shí)間:49038ms
優(yōu)化前,生產(chǎn)環(huán)境第二次打包時(shí)間:70113ms
優(yōu)化后,生產(chǎn)環(huán)境第一次打包時(shí)間:47663ms
優(yōu)化后,生產(chǎn)環(huán)境第二次打包時(shí)間:13738ms, 快了70% 
打包后資源大小展示
以下是整理文件,分包后打包未使用gzip后的生產(chǎn)包,使用gzip會(huì)有更小的包加載
靜態(tài)資源整理
icon
我寫了一個(gè)icon組建vue-midou-icon[7] 。此組建支持iconfont平臺(tái)對(duì)接,不需手動(dòng)下載icon。使用方式如下:
//?注冊(cè)
import?MdUi?from?'vue-midou-icon'
//?在main.js內(nèi)導(dǎo)入
import?"vue-midou-icon/lib/midou.css"?
const?IconFont?=?MdUi.createFromIconfontCN({
??scriptUrl:[
????'your-iconfont-symbbol-url'
??],
??//?name可以不寫?默認(rèn)為?md-icon
??name:?'your-iconfont-component-name',
})
Vue.use(IconFont)
//組建使用
"iconType"?class="className">
</your-iconfont-component-name>
復(fù)制代碼
images
圖片需要壓縮,壓縮地址[8]。可以根據(jù)圖片的實(shí)際大小,多次壓縮。 當(dāng)圖片比較小。就可以考慮放在本地。在webpack的配置中進(jìn)行打包處理 如果圖片是gif動(dòng)態(tài)圖。可以考慮讓ui逐幀截取后再做圖片預(yù)加載。如何做預(yù)加載可以參考 頁(yè)面圖片預(yù)加載與懶加載策略[9] 長(zhǎng)列表圖片使用element—ui的image,支持懶加載
fonts
我們的項(xiàng)目里有在線編輯圖片,所以要加載很多ui字體。每個(gè)字體包在轉(zhuǎn)換之前有17M。通過壓縮轉(zhuǎn)換字體,把otf轉(zhuǎn)換成woff。就變成了5kb,不過字體會(huì)稍微有點(diǎn)點(diǎn)改變。點(diǎn)擊進(jìn)入字體壓縮地址[10]
css
css變量規(guī)范,使用style-resources-loader[11]全局注入 公共css分開整理,統(tǒng)一入口引入 業(yè)務(wù)css與組建統(tǒng)一文件夾
代碼優(yōu)化
目錄整理與模塊劃分
原來的目錄結(jié)構(gòu)的公共組件和頁(yè)面業(yè)務(wù)組件沒有分開,所以組件越來越多,且有路由直接加載components里的組件的現(xiàn)象。目錄結(jié)構(gòu)混亂。沒有模塊劃分的概念。
公共邏輯抽離 createLoading、modelExtengs、listModel、baseModel
modelExtend[12] 類目dva的 dva-model-extend,點(diǎn)擊名稱可查看代碼 createBaseModel[13] 列表請(qǐng)求的篩選和查詢封裝,點(diǎn)擊名稱可查看代碼 createLoadingPlugin[14] 類目dva的 action的loading中間件,點(diǎn)擊名稱可查看代碼
下面是代碼使用demo
在store.js中的注冊(cè)
import?Vue?from?'vue'
import?Vuex?from?'vuex'
import?createLoadingPlugin?from?'./plugins/createLoadingPlugin'
import?{setBaseModelConfig}?from?'@commonModels/createBaseModel.js'
import?productManagement?from?'./modules/productManagement'
import?customerSetting?from?'./modules/customerSetting'
setBaseModelConfig({
??//?列表獲取
??getList:?(response)?=>?{
????let?tableData
????tableData?=?response.items
????return?{
??????tableData,
??????total:?response.total
????}
??},
??//?參數(shù)格式化?
??formatParmas:?(parmas)?=>?{
????//?合并分頁(yè)和篩選的數(shù)據(jù)
????return?{
??????...parmas.pagination,
??????...parmas.filters
????}
??},
??//?錯(cuò)誤警告
??handleError:?(err,?self)?=>?{
????self._vm.$message({
??????message:?`${err}`,
??????type:?'error'
????})
??},
??//?分頁(yè)配置
??pagination:?{
????page_size:?10,
????page_index:?1
??}
})
Vue.use(Vuex)
const?modules?=?{
??...productManagement,
??...customerSetting
}
export?default?new?Vuex.Store({
??modules,
??plugins:?[createLoadingPlugin({Vue})]
})
復(fù)制代碼
[15]在vuex文件中的掛載
import?createBaseModel?from?'@commonModels/createBaseModel.js'
import?modelExtend?from?'@commonModels/modelExtend.js'
import?services?from?'@services'
const?model?=?modelExtend(
??createBaseModel({
????fetch:?services.userCapturePage
??}),
??{
????namespaced:?true,
????state:?()?=>?({
????}),
????actions:?{
??????async?fetch?({commit,?state,?dispatch},?payload)?{
????????await?dispatch('query',?{?...payload?})
??????}
????},
????getters:?{
????}
??})
export?default?model
復(fù)制代碼
[16]在**.vue**文件中的調(diào)用
??????????:data="tableData"
??????v-loading="loading"
??????style="width:?100%">
??????"empty"?/>
??????????????prop="create_time"
????????label="復(fù)制時(shí)間"
????????width="180">
??????
??????????????prop="url"
????????label="復(fù)制鏈接">
??????
????
??????????@size-change="handleSizeChange"
??????@current-change="handleCurrentChange"
??????:current-page="pagination.page_index"
??????class="pt-20?right?mr-20"
??????:page-size="pagination.page_size"
??????:page-sizes="[10,?20,?50,?100]"
??????layout="total,?sizes,?prev,?pager,?next,?jumper"
??????:total="total"
????>
????
復(fù)制代碼
利用require.context自動(dòng)注冊(cè)
const?requireDirectives?=?require.context(
??'@/dirname',
??false,
??/([\w\W]*)\.(vue|js)$/
)
export?const?registerDirectives?=?()?=>
??requireDirectives.keys().forEach(fileName?=>?{
????const?directiveConfig?=?requireDirectives(fileName)
????const?directiveName?=?fileName.split('/').pop().replace(/\.\w+$/,?'')
????Vue.directive(
??????directiveName,
??????directiveConfig.default?||?directiveConfig
????)
??})
復(fù)制代碼
求點(diǎn)贊
邊開發(fā)邊做的,個(gè)人感覺還有很多不夠完善的地方。希望大家看到了可以不吝賜教,感激不盡。。如果覺得不錯(cuò),求點(diǎn)贊,謝謝各位大佬!!!!??
關(guān)于本文
來源:kris和小土豆
https://juejin.cn/post/7050400511828164644
Node 社群
我組建了一個(gè)氛圍特別好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你對(duì)Node.js學(xué)習(xí)感興趣的話(后續(xù)有計(jì)劃也可以),我們可以一起進(jìn)行Node.js相關(guān)的交流、學(xué)習(xí)、共建。下方加 考拉 好友回復(fù)「Node」即可。
如果你覺得這篇內(nèi)容對(duì)你有幫助,我想請(qǐng)你幫我2個(gè)小忙:
1. 點(diǎn)個(gè)「在看」,讓更多人也能看到這篇文章 2. 訂閱官方博客?www.inode.club?讓我們一起成長(zhǎng) 點(diǎn)贊和在看就是最大的支持??
