天天看點

webpack打包優化webpack打包優化

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會顯著減少,并且大大提升項目的首頁加載速度,下面是具體操作
webpack打包優化webpack打包優化
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()}
}
           

此插件容易下載下傳失敗,導緻運作報錯

  1. 若安裝過

    image-webpack-loader

    先解除安裝
//npm 安裝的npm 則npm 移除
npm uninstall image-webpack-loader
//如果yarn安裝的,則yarn 移除
yarn remove image-webpack-loader 
           
  1. 使用 cnpm , 這一步意思就是安裝 cnpm 然後将全局的 registry 設定成阿裡的鏡像,國内阿裡比較快
npm install cnpm -g --registry=https://registry.npm.taobao.org
           
  1. 使用 cnpm 安裝 image-webpack-loader 會發現很快就安裝好了,【手動滑稽】
cnpm install --save-dev image-webpack-loader