webpack打包vue反编译_2020年你必须知道的webpack打包优化方法
本文字?jǐn)?shù):3534字
預(yù)計(jì)閱讀時(shí)間:10分鐘
隨著我們的項(xiàng)目項(xiàng)目越做越大,引入的第三方庫(kù)會(huì)越來(lái)越多,打包的依賴也越來(lái)越多,每次 build 的時(shí)間越來(lái)越長(zhǎng),打包出來(lái)的文件會(huì)越來(lái)越大。最糟糕的是單頁(yè)面應(yīng)用首頁(yè)白屏?xí)r間長(zhǎng),用戶體驗(yàn)差。此時(shí)優(yōu)化 webpack 打包方法不可回避。下面我們來(lái)整理一下常用的webpack打包優(yōu)化方法。關(guān)于優(yōu)化,首先我們要明確幾個(gè)問(wèn)題:優(yōu)化的目的
很明顯,我們優(yōu)化的最終目的就是提高頁(yè)面加載速度,提高產(chǎn)品用戶體驗(yàn),主要包括以下幾個(gè)方面:
減小打包后的文件大小
首頁(yè)按需引入文件,減少白屏?xí)r間
優(yōu)化 webpack 打包時(shí)間
分析 webpack 打包性能瓶頸
明確目標(biāo)之后,首先我們來(lái)分析一下 webpack 打包性能瓶頸,找出問(wèn)題所在,然后才能對(duì)癥下藥。
1、webpack-bundle-analyzer 分析體積
webpack-bundle-analyzer 可以掃碼打包后內(nèi)容并構(gòu)建其成可視化的界面,我們可以在可視化的界面中找出不必要的依賴或者體積過(guò)大的包,有針對(duì)性地進(jìn)行優(yōu)化。
vue-cli3 需要安裝依賴 webpack-bundle-analyzer
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
plugins:[
new BundleAnalyzerPlugin(),
]
- vue-cli2 直接在命令行輸入 npm run build --report, 構(gòu)建完成后會(huì)在 8888 端口展示大小
- 打包出的文件中都包含了什么,以及模塊之間的依賴關(guān)系
- 每個(gè)文件的大小在總體中的占比,找出較大的文件,思考一下為什么,是否有替換方案,是否使用了它包含了不必要的依賴?
- 是否有重復(fù)的依賴項(xiàng),對(duì)此可以如何優(yōu)化?
- 每個(gè)文件的壓縮后的大小。
2、測(cè)量構(gòu)建時(shí)間
我們可以通過(guò) speed-measure-webpack-plugin 測(cè)量你的 webpack 構(gòu)建期間各個(gè)階段花費(fèi)的時(shí)間。步驟一:安裝依賴包
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const smp = new SpeedMeasurePlugin()
// ...
module.exports = {
configureWebpack: smp.wrap({
plugins: [new BundleAnalyzerPlugin()]
})
}
打包構(gòu)建后會(huì)看到以下輸出
從以上的界面中,我們可以得到以下信息:
- 分析整個(gè)打包總耗時(shí);
- 每個(gè)插件和 loader 的耗時(shí)情況;
找出問(wèn)題所在后我們開始來(lái)總結(jié)一下優(yōu)化方法。
1、 按需加載
單頁(yè)面應(yīng)用最大的一個(gè)問(wèn)題就是,他把整個(gè)工程作為一個(gè)入口打包成一個(gè)模塊,所以在首頁(yè)會(huì)加載了一些沒(méi)用到的資源,造成首頁(yè)渲染速度慢,“白屏?xí)r間”過(guò)長(zhǎng),給用戶不好的體驗(yàn)。我們可以從以下幾個(gè)方面進(jìn)行按需加載:
1.1 路由組件按需加載
const router = [{
path: '/index',
component: resolve => require.ensure([], () => resolve(require('@/components/index')))
},
{
path: '/about',
component: resolve => require.ensure([], () => resolve(require('@/components/about')))
}
]
1.2 第三方組件和插件。按需加載需引入第三方組件
// 引入全部組件import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)
// 按需引入組件
import { Button } from 'element-ui'
Vue.component(Button.name, Button)
1.3 對(duì)于一些插件,如果只是在個(gè)別組件中用的到,也可以不要在 main.js 里面引入,而是在組件中按需引入
// 在main.js引入import Vue from vue
import Vuelidate from 'vuelidate'
Vue.use(Vuelidate)
// 按組件按需引入
import { Vuelidate } from 'vuelidate'
2、優(yōu)化 loader 配置
- 優(yōu)化正則匹配——減少文件查詢時(shí)間
- 通過(guò)cacheDirectory選項(xiàng)開啟緩存——減少再次打包時(shí)間
- 通過(guò) include、exclude 來(lái)減少被處理的文件。
rules: [
{
test: /\.js$/,
loader: 'babel-loader?cacheDirectory',
include: [resolve('src')]
}
]
}
注意:保存和讀取這些緩存文件會(huì)有一些時(shí)間開銷,所以請(qǐng)只對(duì)性能開銷較大的 loader 使用此 loader。
3、優(yōu)化文件路徑——省下搜索文件的時(shí)間
- extension 配置之后可以不用在 require 或是 import 的時(shí)候加文件擴(kuò)展名,會(huì)依次嘗試添加擴(kuò)展名進(jìn)行匹配。
- alias 通過(guò)配置別名可以加快 webpack 查找模塊的速度。
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
}
},
- module.noParse:讓 webpack 忽略對(duì)部分沒(méi)采用模塊化的文件的遞歸解析處理,這樣做的好處是能提高構(gòu)建性能。因?yàn)槿?jQuery 、echart 等庫(kù)龐大又沒(méi)有采用模塊化標(biāo)準(zhǔn),讓 webpack 去解析這些文件耗時(shí)又沒(méi)有意義。
noParse:/jquery/, //不去解析jquery中的依賴庫(kù)
...
},
4、生產(chǎn)環(huán)境關(guān)閉 sourceMap
- sourceMap 本質(zhì)上是一種映射關(guān)系,打包出來(lái)的 js 文件中的代碼可以映射到代碼文件的具體位置,這種映射關(guān)系會(huì)幫助我們直接找到在源代碼中的錯(cuò)誤。
- 打包速度減慢,生產(chǎn)文件變大,所以開發(fā)環(huán)境使用 sourceMap,生產(chǎn)環(huán)境則關(guān)閉。
5、代碼壓縮
uglifyJsPlugin 是 vue-cli 默認(rèn)使用的壓縮代碼方式,用來(lái)對(duì)js文件進(jìn)行壓縮,從而減小js文件的大小,加速load速度。它使用的是單線程壓縮代碼,打包時(shí)間較慢,所以可以在開發(fā)環(huán)境將其關(guān)閉,生產(chǎn)環(huán)境部署時(shí)再把它打開。
plugins: [new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false
}
},
sourceMap: true,
parallel: true
})
ParallelUglifyPlugin 開啟多個(gè)子進(jìn)程,把對(duì)多個(gè)文件壓縮的工作分別給多個(gè)子進(jìn)程去完成,每個(gè)子進(jìn)程其實(shí)還是通過(guò) UglifyJS 去壓縮代碼,但是變成了并行執(zhí)行。
ParallelUglifyPlugin 還可以緩存壓縮后的結(jié)果,下次遇到一樣的輸入時(shí)直接從緩存中獲取壓縮后的結(jié)果并返回。
plugins: [new ParallelUglifyPlugin({
//cacheDir 用于配置緩存存放的目錄路徑。
cacheDir: '.cache/',
sourceMap: true,
uglifyJS: {
output: {
comments: false
},
compress: {
warnings: false
}
}
})
]
打包速度和打包后的文件大小對(duì)比
| 不用插件 | 14.6M | 32s |
| UglifyJsPlugin | 12.9M | 33s |
| ParallelUglifyPlugin | 7.98M | 17s |
從上面可以看出,無(wú)論是打包時(shí)間還是打包后的文件大小,ParallelUglifyPlugin的方法都是最優(yōu)的。
6、提取公共代碼
在用 webpack 打包的時(shí)候,對(duì)于一些不經(jīng)常更新的第三方庫(kù),比如 react,lodash,vue 我們希望能和自己的代碼分離開,這樣既能減小打包的總體積,也能避免單個(gè)包體積過(guò)大。
webpack 社區(qū)有以下兩種方案:
6.1、CommonsChunkPlugin 及 splitChunks
webpack3 使用 CommonsChunkPlugin 的實(shí)現(xiàn):
CommonsChunkPlugin主要是用來(lái)提取第三方庫(kù)和公共模塊,避免首屏加載的bundle文件或者按需加載的bundle文件體積過(guò)大,從而導(dǎo)致加載時(shí)間過(guò)長(zhǎng),這是webpack 優(yōu)化打包的一大利器。
plugins: [new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function(module, count) {
console.log(module.resource, `引用次數(shù)${count}`)
//"有正在處理文件" + "這個(gè)文件是 .js 后綴" + "這個(gè)文件是在 node_modules 中"
return module.resource && /\.js$/.test(module.resource) && module.resource.indexOf(path.join(__dirname, './node_modules')) === 0
}
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'common',
chunks: 'initial',
minChunks: 2
})
]
CommonsChunkPlugin 的配置參數(shù)
- name:可以是已經(jīng)存在的chunk(一般指入口文件)對(duì)應(yīng)的name,那么就會(huì)把公共模塊代碼合并到這個(gè)chunk上;否則,會(huì)創(chuàng)建名字為name的commons chunk進(jìn)行合并
- filename:指定commons chunk的文件名。
- chunks:指定source chunk,即指定從哪些chunk當(dāng)中去找公共模塊,省略該選項(xiàng)的時(shí)候,默認(rèn)就是entry chunks
- minChunks:既可以是數(shù)字,也可以是函數(shù),還可以是Infinity。
webpack4 使用 splitChunks 的實(shí)現(xiàn):splitChunks 是webpack有一個(gè)默認(rèn)配置,這也符合webpack4的開箱即用的特性
module.exports = {optimization: {
splitChunks: {
cacheGroups: {
vendor: {
priority: 1, //添加權(quán)重
test: /node_modules/, //把這個(gè)目錄下符合下面幾個(gè)條件的庫(kù)抽離出來(lái)
chunks: 'initial', //剛開始就要抽離
minChunks: 2 //重復(fù)2次使用的時(shí)候需要抽離出來(lái)
},
common: {
//公共的模塊
chunks: 'initial',
minChunks: 2
}
}
}
}
}
6.2、DLLPlugin
這是在一個(gè)額外的獨(dú)立的 webpack 設(shè)置中創(chuàng)建一個(gè)只有 dll 的 bundle(dll-only-bundle)。這個(gè)插件會(huì)生成一個(gè)名為 manifest.json 的文件,這個(gè)文件是用來(lái)讓 DLLReferencePlugin 映射到相關(guān)的依賴上去的。
使用步驟如下
6.2.1、在build下創(chuàng)建 webpack.dll.config.js
const path = require('path')const webpack = require('webpack')
module.exports = {
entry: {
vendor: [
'vue-router',
'vuex',
'vue/dist/vue.common.js',
'vue/dist/vue.js',
'vue-loader/lib/component-normalizer.js',
'vue',
'axios',
'echarts'
]
},
output: {
path: path.resolve('./dist'),
filename: '[name].dll.js',
library: '[name]_library'
},
plugins: [
new webpack.DllPlugin({
path: path.resolve('./dist', '[name]-manifest.json'),
name: '[name]_library'
}),
// 建議加上代碼壓縮插件,否則dll包會(huì)比較大。
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})
]
}
6.2.2、在 webpack.prod.conf.js 的 plugin 后面加入配置
new webpack.DllReferencePlugin({manifest: require('../dist/vendor-manifest.json')
})
6.2.3、package.json文件中添加快捷命令(build:dll)
"scripts": {"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"lint": "eslint --ext .js,.vue src",
"build": "node build/build.js",
"build:dll": "webpack --config build/webpack.dll.conf.js"
}
生產(chǎn)環(huán)境打包的時(shí)候先npm run build:dll命令會(huì)在打包目錄下生成 vendor-manifest.json 文件與 vendor.dll.js 文件。然后npm run build生產(chǎn)其他文件。
6.2.4、根目錄下的入口index.html加入引用
<script type="text/javascript" src="./vendor.dll.js">script>7、CDN 優(yōu)化
CDN 的全稱是 Content Delivery Network,即內(nèi)容分發(fā)網(wǎng)絡(luò)。CDN 是構(gòu)建在網(wǎng)絡(luò)之上的內(nèi)容分發(fā)網(wǎng)絡(luò),依靠部署在各地的邊緣服務(wù)器,通過(guò)中心平臺(tái)的負(fù)載均衡、內(nèi)容分發(fā)、調(diào)度等功能模塊,使用戶就近獲取所需內(nèi)容,降低網(wǎng)絡(luò)擁塞,提高用戶訪問(wèn)響應(yīng)速度和命中率。CDN 的關(guān)鍵技術(shù)主要有內(nèi)容存儲(chǔ)和分發(fā)技術(shù)。
隨著項(xiàng)目越做越大,依賴的第三方 npm 包越來(lái)越多,構(gòu)建之后的文件也會(huì)越來(lái)越大。再加上又是單頁(yè)應(yīng)用,這就會(huì)導(dǎo)致在網(wǎng)速較慢或者服務(wù)器帶寬有限的情況出現(xiàn)長(zhǎng)時(shí)間的白屏。此時(shí)我們可以使用CDN的方法,優(yōu)化網(wǎng)絡(luò)加載速度。
7.1、將 vue、vue-router、vuex、element-ui 和 axios 這五個(gè)庫(kù),全部改為通過(guò) CDN 鏈接獲取,在 index.html里插入 相應(yīng)鏈接。
<head><link rel="stylesheet" href="https://cdn.bootcss.com/element-ui/2.0.7/theme-chalk/index.css" />
head>
<body>
<div id="app">div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js">script>
<script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.min.js">script>
<script src="https://cdn.bootcss.com/vuex/3.1.0/vuex.min.js">script>
<script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.min.js">script>
<script src="https://cdn.bootcss.com/element-ui/2.6.1/index.js">script>
body>
7.2、在 webpack.config.js 配置文件
module.exports = {···
externals: {
'vue': 'Vue',
'vuex': 'Vuex',
'vue-router': 'VueRouter',
'element-ui': 'ELEMENT',
'Axios':'axios'
}
},
7.3、卸載依賴的 npm 包
npm uninstall axios element-ui vue vue-router vuex7.4、修改 main.js 文件里之前的引包方式
// import Vue from 'vue'// import ElementUI from 'element-ui'
// import 'element-ui/lib/theme-chalk/index.css'
// import VueRouter from 'vue-router'
import App from './App.vue'
import routes from './router'
import utils from './utils/Utils'
Vue.use(ELEMENT)
Vue.use(VueRouter)
const router = new VueRouter({
mode: 'hash', //路由的模式
routes
})
new Vue({
router,
el: '#app',
render: h => h(App)
})
8、多進(jìn)程解析和處理文件
由于運(yùn)行在 Node.js 之上的 webpack 是單線程模型的,所以 webpack 需要處理的事情需要一件一件的做,不能多件事一起做。當(dāng) webpack 需要打包大量文件時(shí),打包時(shí)間就會(huì)比較漫長(zhǎng)。
以下兩個(gè)方法能讓 webpack 在同一時(shí)刻處理多個(gè)任務(wù)發(fā)揮多核 CPU 電腦的功能,提升構(gòu)建速度。
8.1、thread loader
把這個(gè) thread loader 放置在其他 loader 之前, 放置在這個(gè) loader 之后的 loader 就會(huì)在一個(gè)單獨(dú)的 worker 池(worker pool)中運(yùn)行。
在 worker 池(worker pool)中運(yùn)行的 loader 是受到限制的。例如:
- 這些 loader 不能產(chǎn)生新的文件。
- 這些 loader 不能使用定制的 loader API(也就是說(shuō),通過(guò)插件)。
- 這些 loader 無(wú)法獲取 webpack 的選項(xiàng)設(shè)置。
每個(gè) worker 都是一個(gè)單獨(dú)的有 600ms 限制的 node.js 進(jìn)程。同時(shí)跨進(jìn)程的數(shù)據(jù)交換也會(huì)被限制。
module.exports = {module: {
rules: [
{
test: /\.js$/,
include: path.resolve('src'),
use: ['thread-loader', 'expensive-loader']
}
]
}
}
8.2、HappyPack
HappyPack 能讓 webpack 把任務(wù)分解給多個(gè)子進(jìn)程去并發(fā)的執(zhí)行,子進(jìn)程處理完后再把結(jié)果發(fā)送給主進(jìn)程。要注意的是 HappyPack 對(duì) file-loader、url-loader 支持的不友好,所以不建議對(duì)該 loader 使用。
使用方法如下:
8.2.1. HappyPack 插件安裝
npm i -D happypack8.2.2. webpack.base.conf.js 文件對(duì) module.rules 進(jìn)行配置
module: {rules: [
{
test: /\.js$/,
use: ['happypack/loader?id=babel'],
include: [resolve('src'), resolve('test')],
exclude: path.resolve(__dirname, 'node_modules')
},
{
test: /\.vue$/,
use: ['happypack/loader?id=vue']
}
]
}
8.2.3. 在生產(chǎn)環(huán)境 webpack.prod.conf.js 文件進(jìn)行配置
const HappyPack = require('happypack')// 構(gòu)造出共享進(jìn)程池,在進(jìn)程池中包含5個(gè)子進(jìn)程
const HappyPackThreadPool = HappyPack.ThreadPool({ size: 5 })
plugins: [
new HappyPack({
// 用唯一的標(biāo)識(shí)符id,來(lái)代表當(dāng)前的HappyPack是用來(lái)處理一類特定的文件
id: 'babel',
// 如何處理.js文件,用法和Loader配置中一樣
loaders: ['babel-loader?cacheDirectory'],
threadPool: HappyPackThreadPool
}),
new HappyPack({
id: 'vue', // 用唯一的標(biāo)識(shí)符id,來(lái)代表當(dāng)前的HappyPack是用來(lái)處理一類特定的文件
loaders: [
{
loader: 'vue-loader',
options: vueLoaderConfig
}
],
threadPool: HappyPackThreadPool
})
]
注意,當(dāng)項(xiàng)目較小時(shí),多線程打包反而會(huì)使打包速度變慢。
總結(jié)
聚焦全棧,專注分享 TypeScript、Web API、Node.js、Deno 等全棧干貨。
總結(jié)
以上是生活随笔為你收集整理的webpack打包vue反编译_2020年你必须知道的webpack打包优化方法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: myisam为什么比innodb查询快_
- 下一篇: vue 只在父级容器移动_Vue易遗忘的