webpack打包優化
vue-cli3以後,我們修改webpack配置,需要自己在項目根路徑下建立vue.config.js檔案。
一、 配置 proxy 跨域
v使用vue-cli發開項目,在本地開發環境中,如果遇到跨域的問題。可以通過配置proxy的方式,解決跨域問題:
module.exports = {
devServer: {
open: false, // 自動啟動浏覽器
host: '0.0.0.0', // localhost
port: 6060, // 端口号
hotOnly: false, // 熱更新
overlay: {
// 當出現編譯器錯誤或警告時,在浏覽器中顯示全屏覆寫層
warnings: false,
errors: true
},
proxy: {
//配置跨域
'/api': {
target: 'https://www.test.com', // 接口的域名
// ws: true, // 是否啟用websockets
changOrigin: true, // 開啟代理,在本地建立一個虛拟服務端
pathRewrite: {
'^/api': '/'
}
}
}
}
}
配置完成後,當我們在去請求https://www.test.com/v1/api/userinfo接口時,就可以這麼寫
this.axios({
url:'/api/v1/api/userinfo',
method:'get'
}).then(res=>{
//......
})
二、配置 alias 别名
使用vue-cli開發項目,最大特色是元件化。元件中頻繁引用其他元件或插件。我們可以把一些常用的路徑定義成簡短的名字。友善開發中使用。
//加載path子產品
const path = require('path')
//定義resolve方法,把相對路徑轉換成絕對路徑
const resolve = dir => path.join(__dirname, dir)
module.exports = {
chainWebpack: config => {
// 添加别名
config.resolve.alias
.set('@', resolve('src'))
.set('assets', resolve('src/assets'))
.set('api', resolve('src/api'))
.set('views', resolve('src/views'))
.set('components', resolve('src/components'))
}
}
配置完成後,我們在項目中可以這樣寫路徑
//之前這麼寫
import Home from '../views/Home.vue'
//配置alias别名後
import Home from 'views/Home.vue'
//也可以這麼寫
import Home from '@/views/Home.vue'
項目結束後打包前webpack配置
目的:
- 提高打包速度
- 減小項目體積、提高首屏加載速度
- 提高使用者體驗(骨架屏)
項目開發完成後,運作npm run build進行打包操作。打包前對webpack配置。
module.exports = {
publicPath: './', // 靜态資源路徑(預設/,打包後會白屏)
outputDir: 'dist', // 打包後檔案的目錄 (預設為dist)
assetsDir: 'static', // outputDir的靜态資源(js、css、img、fonts)目錄 預設為‘’沒有單獨目錄js/css/img在根目錄中。
}
一、去除生産環境sourceMap
問題: vue項目打包之後js檔案夾中,會自動生成一些map檔案,占用相當一部分空間
sourceMap資源映射檔案,存的是打包前後的代碼位置,友善開發使用,這個占用相當一部分空間
sourceMap資源映射檔案,存的是打包前後的代碼位置,友善開發使用,這個占用相當一部分空間
生産環境是不需要
sourceMap
的,如下配置可以去除
module.exports = {
//去除生産環境的productionSourceMap
productionSourceMap: false,
}
去除
sourceMap
前後對比,減少了很大體積。
前:dist大小為7M
後:dist大小為3M
二、去除console.log列印以及注釋
下載下傳插件
cnpm install uglifyjs-webpack-plugin --save-dev
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const isProduction = process.env.NODE_ENV === 'production';
configureWebpack: config => {
const plugins = [];
if (isProduction) {
plugins.push(
new UglifyJsPlugin({
uglifyOptions: {
output: {
comments: false, // 去掉注釋
},
warnings: false,
compress: {
drop_console: true,
drop_debugger: false,
pure_funcs: ['console.log']//移除console
}
}
})
)
}
},
結論:重新打包,dist體積減少并不大。因為congsole.log()以及注釋并不會占用太多體積(也就10-30kb)
三、使用CDN 加速優化
cdn優化是指把第三方庫比如(vue,vue-router,axios)通過cdn的方式引入項目中,這樣vendor.js會顯著減少,并且大大提升項目的首頁加載速度,下面是具體操作
const isProduction = process.env.NODE_ENV === 'production';
// externals
const externals = {
vue: 'Vue',
'vue-router': 'VueRouter',
vuex: 'Vuex',
vant: 'vant',
axios: 'axios'
}
// CDN外鍊,會插入到index.html中
const cdn = {
// 開發環境
dev: {
css: [],
js: []
},
// 生産環境
build: {
css: ['https://cdn.jsdelivr.net/npm/[email protected]/lib/index.css'],
js: [
'https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js',
'https://cdn.jsdelivr.net/npm/[email protected]/dist/vue-router.min.js',
'https://cdn.jsdelivr.net/npm/[email protected]/dist/axios.min.js',
'https://cdn.jsdelivr.net/npm/[email protected]/dist/vuex.min.js',
'https://cdn.jsdelivr.net/npm/[email protected]/lib/vant.min.js'
]
}
}
module.exports = {
configureWebpack: config => {
// 為生産環境修改配置...
if (isProduction) {
// externals
config.externals = externals
}
},
chainWebpack: config => {
/**
* 添加CDN參數到htmlWebpackPlugin配置中
*/
config.plugin('html').tap(args => {
if (isProduction) {
args[0].cdn = cdn.build
} else {
args[0].cdn = cdn.dev
}
return args
})
}
}
在 public/index.html 中添加
<!-- 使用CDN的CSS檔案 -->
<% for (var i in
htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.css) { %>
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style" />
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet" />
<% } %>
<!-- 使用CDN加速的JS檔案,配置在vue.config.js下 -->
<% for (var i in
htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<% } %>
總結:配置了cdn引入,1.1M體積較少到660kb。效果很明顯。
四、對資源檔案進行壓縮
下載下傳插件
cnpm i compression-webpack-plugin -D
const CompressionWebpackPlugin = require('compression-webpack-plugin')
module.exports = {
// 根據你的實際情況更改這裡
publicPath,
assetsDir: 'assets',
lintOnSave: true,
configureWebpack: {
plugins:[
new CompressionWebpackPlugin({
filename: '[path].gz[query]',
algorithm: 'gzip',
// test: /\.js$|\.html$|\.json$|\.css/,
test: /\.js$|\.json$|\.css/,
threshold: 10240, // 隻有大小大于該值的資源會被處理
minRatio: 0.8, // 隻有壓縮率小于這個值的資源才會被處理
// deleteOriginalAssets: true // 删除原檔案
})
],
},
}
五、隻打包改變的檔案
const { HashedModuleIdsPlugin } = require(‘webpack’);
configureWebpack: config => {
const plugins = [];
plugins.push(
new HashedModuleIdsPlugin()
)
}
六、公共代碼抽離
從webpack4開始官方移除了commonchunk插件,改用了optimization屬性進行更加靈活的配置,這也應該是從V3更新到V4的代碼修改過程中最為複雜的一部分
splitChunks: {
chunks: "async”,//預設作用于異步chunk,值為all/initial/async/function(chunk),值為function時第一個參數為周遊所有入口chunk時的chunk子產品,chunk._modules為chunk所有依賴的子產品,通過chunk的名字和所有依賴子產品的resource可以自由配置,會抽取所有滿足條件chunk的公有子產品,以及子產品的所有依賴子產品,包括css
minSize: 30000, //表示在壓縮前的最小子產品大小,預設值是30kb
minChunks: 1, // 表示被引用次數,預設為1;
maxAsyncRequests: 5, //所有異步請求不得超過5個
maxInitialRequests: 3, //初始話并行請求不得超過3個
automaticNameDelimiter:'~',//名稱分隔符,預設是~
name: true, //打包後的名稱,預設是chunk的名字通過分隔符(預設是~)分隔
cacheGroups: { //設定緩存組用來抽取滿足不同規則的chunk,下面以生成common為例
common: {
name: 'common', //抽取的chunk的名字
chunks(chunk) { //同外層的參數配置,覆寫外層的chunks,以chunk為次元進行抽取
},
test(module, chunks) { //可以為字元串,正規表達式,函數,以module為次元進行抽取,隻要是滿足條件的module都會被抽取到該common的chunk中,為函數時第一個參數是周遊到的每一個子產品,第二個參數是每一個引用到該子產品的chunks數組。自己嘗試過程中發現不能提取出css,待進一步驗證。
},
priority: 10, //優先級,一個chunk很可能滿足多個緩存組,會被抽取到優先級高的緩存組中
minChunks: 2, //最少被幾個chunk引用
reuseExistingChunk: true,// 如果該chunk中引用了已經被抽取的chunk,直接引用該chunk,不會重複打包代碼
enforce: true // 如果cacheGroup中沒有設定minSize,則據此判斷是否使用上層的minSize,true:則使用0,false:使用上層minSize
}
}
}
公共子產品抽離
舉例:
項目中分别有a.js, b.js, page1.js, page2.js這四個JS檔案, page1.js 和
page2.js中同時都引用了a.js, b.js, 這時候想把a.js, b.js抽離出來合并成一個公共的js,然後在page1,page2中自動引入這個公共的js,怎麼配置呢?
第三方子產品抽離
如下:
// 公共代碼抽離
configureWebpack: config => {
//....
//優化項配置
config.optimization = {
splitChunks: { // 分割代碼塊
cacheGroups: {
vendor: {//第三方庫抽離
chunks: 'all',
test: /node_modules/,
name: 'vendor',
minChunks: 1,//在分割之前,這個代碼塊最小應該被引用的次數
maxInitialRequests: 5,
minSize: 0,//大于0個位元組
priority: 100//權重
},
common: { //公用子產品抽離
chunks: 'all',
test: /[\\/]src[\\/]js[\\/]/,
name: 'common',
minChunks: 2,在分割之前,這個代碼塊最小應該被引用的次數
maxInitialRequests: 5,
minSize: 0,//大于0個位元組
priority: 60
},
styles: { //樣式抽離
name: 'styles',
test: /\.(sa|sc|c)ss$/,
chunks: 'all',
enforce: true
},
runtimeChunk: {
name: 'manifest'
}
}
}
}
}
七、配置 打包分析
安裝
cnpm i webpack-bundle-analyzer -D
const BundleAnalyzerPlugin = require(‘webpack-bundle-analyzer’).BundleAnalyzerPlugin
module.exports = {
chainWebpack: config => {
// 打包分析
if (IS_PROD) {
config.plugin(‘webpack-report’).use(BundleAnalyzerPlugin, [
{
analyzerMode: ‘static’
}
])
}
}
}
八、骨架屏
安裝插件
npm install vue-skeleton-webpack-plugin
在src下建立Skeleton檔案夾,其中建立index.js以及index.vue,在其中寫入以下内容,其中,骨架屏的index.vue頁面樣式請自行編輯
import Vue from 'vue'
import home from './index.vue'
import list from './list.vue'
export default new Vue({
components: {
home,
list
},
template: `
<div>
<home id="home" style="display:none"/>
<list id="list" style="display:none"/>
</div>
`
})
index.vue(骨架屏頁面) list.vue同理
<template>
<div class="skeleton-wrapper">
<header class="skeleton-header"></header>
<section class="skeleton-block">
<img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTA4MCAyNjEiPjxkZWZzPjxwYXRoIGlkPSJiIiBkPSJNMCAwaDEwODB2MjYwSDB6Ii8+PGZpbHRlciBpZD0iYSIgd2lkdGg9IjIwMCUiIGhlaWdodD0iMjAwJSIgeD0iLTUwJSIgeT0iLTUwJSIgZmlsdGVyVW5pdHM9Im9iamVjdEJvdW5kaW5nQm94Ij48ZmVPZmZzZXQgZHk9Ii0xIiBpbj0iU291cmNlQWxwaGEiIHJlc3VsdD0ic2hhZG93T2Zmc2V0T3V0ZXIxIi8+PGZlQ29sb3JNYXRyaXggaW49InNoYWRvd09mZnNldE91dGVyMSIgdmFsdWVzPSIwIDAgMCAwIDAuOTMzMzMzMzMzIDAgMCAwIDAgMC45MzMzMzMzMzMgMCAwIDAgMCAwLjkzMzMzMzMzMyAwIDAgMCAxIDAiLz48L2ZpbHRlcj48L2RlZnM+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDEpIj48dXNlIGZpbGw9IiMwMDAiIGZpbHRlcj0idXJsKCNhKSIgeGxpbms6aHJlZj0iI2IiLz48dXNlIGZpbGw9IiNGRkYiIHhsaW5rOmhyZWY9IiNiIi8+PHBhdGggZmlsbD0iI0Y2RjZGNiIgZD0iTTIzMCA0NGg1MzN2NDZIMjMweiIvPjxyZWN0IHdpZHRoPSIxNzIiIGhlaWdodD0iMTcyIiB4PSIzMCIgeT0iNDQiIGZpbGw9IiNGNkY2RjYiIHJ4PSI0Ii8+PHBhdGggZmlsbD0iI0Y2RjZGNiIgZD0iTTIzMCAxMThoMzY5djMwSDIzMHpNMjMwIDE4MmgzMjN2MzBIMjMwek04MTIgMTE1aDIzOHYzOUg4MTJ6TTgwOCAxODRoMjQydjMwSDgwOHpNOTE3IDQ4aDEzM3YzN0g5MTd6Ii8+PC9nPjwvc3ZnPg==">
<img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTA4MCAyNjEiPjxkZWZzPjxwYXRoIGlkPSJiIiBkPSJNMCAwaDEwODB2MjYwSDB6Ii8+PGZpbHRlciBpZD0iYSIgd2lkdGg9IjIwMCUiIGhlaWdodD0iMjAwJSIgeD0iLTUwJSIgeT0iLTUwJSIgZmlsdGVyVW5pdHM9Im9iamVjdEJvdW5kaW5nQm94Ij48ZmVPZmZzZXQgZHk9Ii0xIiBpbj0iU291cmNlQWxwaGEiIHJlc3VsdD0ic2hhZG93T2Zmc2V0T3V0ZXIxIi8+PGZlQ29sb3JNYXRyaXggaW49InNoYWRvd09mZnNldE91dGVyMSIgdmFsdWVzPSIwIDAgMCAwIDAuOTMzMzMzMzMzIDAgMCAwIDAgMC45MzMzMzMzMzMgMCAwIDAgMCAwLjkzMzMzMzMzMyAwIDAgMCAxIDAiLz48L2ZpbHRlcj48L2RlZnM+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDEpIj48dXNlIGZpbGw9IiMwMDAiIGZpbHRlcj0idXJsKCNhKSIgeGxpbms6aHJlZj0iI2IiLz48dXNlIGZpbGw9IiNGRkYiIHhsaW5rOmhyZWY9IiNiIi8+PHBhdGggZmlsbD0iI0Y2RjZGNiIgZD0iTTIzMCA0NGg1MzN2NDZIMjMweiIvPjxyZWN0IHdpZHRoPSIxNzIiIGhlaWdodD0iMTcyIiB4PSIzMCIgeT0iNDQiIGZpbGw9IiNGNkY2RjYiIHJ4PSI0Ii8+PHBhdGggZmlsbD0iI0Y2RjZGNiIgZD0iTTIzMCAxMThoMzY5djMwSDIzMHpNMjMwIDE4MmgzMjN2MzBIMjMwek04MTIgMTE1aDIzOHYzOUg4MTJ6TTgwOCAxODRoMjQydjMwSDgwOHpNOTE3IDQ4aDEzM3YzN0g5MTd6Ii8+PC9nPjwvc3ZnPg==">
</section>
</div>
</template>
<script>
export default {
name: 'skeleton'
}
</script>
<style scoped>
.skeleton-header {
height: 40px;
background: #1976d2;
padding:0;
margin: 0;
width: 100%;
}
.skeleton-block {
display: flex;
flex-direction: column;
padding-top: 8px;
}
</style>
vue.config.js 配置
//骨架屏渲染
const SkeletonWebpackPlugin = require('vue-skeleton-webpack-plugin')
//path引入
const path = require('path')
//configureWebpack子產品中寫入内容
// 骨架屏渲染
config.plugins.push(new SkeletonWebpackPlugin({
webpackConfig: {
entry: {
app: path.join(__dirname, './src/Skeleton/index.js'),
},
},
minimize: true,
quiet: true,
// 如果不設定那麼所有的路由都會共享這個骨架屏元件
router: {
mode: 'hash',
// 給對應的路由設定對應的骨架屏元件,skeletonId的值根據元件設定的id
routes: [
{ path: '/home', skeletonId: 'home' },
{ path: '/list', skeletonId: 'list' },
]
}))
九、圖檔壓縮
下載下傳插件
npm install image-webpack-loader --save-dev
module.exports = {
// 根據你的實際情況更改這裡
publicPath,
assetsDir: 'assets',
lintOnSave: true,
// image 壓縮 定義在chainWebpack中
chainWebpack: config => {
config.module
.rule('images')
.use('image-webpack-loader')
.loader('image-webpack-loader')
.options({
bypassOnDebug: true
})
.end()}
}
此插件容易下載下傳失敗,導緻運作報錯
- 若安裝過
先解除安裝image-webpack-loader
//npm 安裝的npm 則npm 移除
npm uninstall image-webpack-loader
//如果yarn安裝的,則yarn 移除
yarn remove image-webpack-loader
- 使用 cnpm , 這一步意思就是安裝 cnpm 然後将全局的 registry 設定成阿裡的鏡像,國内阿裡比較快
npm install cnpm -g --registry=https://registry.npm.taobao.org
- 使用 cnpm 安裝 image-webpack-loader 會發現很快就安裝好了,【手動滑稽】
cnpm install --save-dev image-webpack-loader