1. 程式人生 > 實用技巧 >前端工程webpack打包優化方法

前端工程webpack打包優化方法

為提高前端工程webpack打包速度,對工程進行改造

第一部分:vue-cli2工程

可以採用js分包+壓縮混淆升級+CDN加速+gzip壓縮等手段進行加速

(1)分包,在webpack的配置中新增

module.exports = {
  externals: {
    vue: 'Vue',
    moment: 'moment',
    rxjs: 'Rx',
    ramda: 'R',
    'vue-router': 'VueRouter',
    jquery: 'jQuery',
    'element-ui': 'ELEMENT',
    axios: 'axios',
    qs: 
'Qs', vuex: 'Vuex', 'echarts': 'echarts', lodash: { //如果我們的庫執行在Node.js環境中,import _ from 'lodash'等價於const _ = require('lodash') commonjs: "lodash", commonjs2: "lodash", //同上 amd: "lodash", //如果我們的庫使用require.js等載入,等價於 define(["lodash"], factory); root: "_", //如果我們的庫在瀏覽器中使用,需要提供一個全域性的變數‘_’,等價於 var _ = (window._) or (_);
} }, }

(2)分包後,這些被排除的包將不會被打包進入vendor中去,那麼我們就必須使用CDN來提供這些包的功能

在入口檔案index.html中新增對應的js檔案

使用CDN的優點:

1.可以充分利用客戶端快取,大大減少每次webpack打包的vendor的體積,提高打包速度

2.每次工程迭代時,這些包將不需要更新,也不需要客戶端重新載入3

3.使用者在第一次載入之後,後面的版本迭代大部分檔案將直接讀快取,不需要再次載入外部依賴檔案

缺點:工程部署必須能夠連線外網,內網部署的工程將不能夠使用CDN,可以通過在內網維護一個自己的簡單的CDN庫來解決

<!DOCTYPE html
> <html> <head> <meta charset="utf-8"> <link crossorigin="anonymous" href="https://lib.baomitu.com/element-ui/2.3.9/theme-chalk/index.css" rel="stylesheet"> </head> <body> <div id="app"></div> <!-- built files will be auto injected --> <script src="https://lib.baomitu.com/jquery/3.2.1/jquery.min.js"></script> <script src="https://lib.baomitu.com/vue/2.5.2/vue.js"></script> <script src="https://lib.baomitu.com/vue-router/2.7.0/vue-router.min.js"></script> <script src="https://lib.baomitu.com/moment.js/2.18.1/moment.min.js"></script> <script src="https://lib.baomitu.com/moment.js/2.18.1/locale/zh-cn.js"></script> <script src="https://lib.baomitu.com/rxjs/5.4.3/Rx.min.js"></script> <script src="https://lib.baomitu.com/ramda/0.24.1/ramda.min.js"></script> <script crossorigin="anonymous" src="https://lib.baomitu.com/lodash.js/4.17.0/lodash.min.js"></script> <script crossorigin="anonymous" src="https://lib.baomitu.com/element-ui/2.3.9/index.js"></script> <script crossorigin="anonymous" src="https://lib.baomitu.com/axios/0.16.2/axios.min.js"></script> <script crossorigin="anonymous" src="https://lib.baomitu.com/vuex/3.0.1/vuex.min.js"></script> <script crossorigin="anonymous" src="https://lib.baomitu.com/qs/6.5.1/qs.min.js"></script> <script crossorigin="anonymous" src="https://lib.baomitu.com/xlsx/0.14.2/xlsx.min.js"></script> </body> </html>

(3)使用uglifyjs-webpack-plugin,新版的uglifyjs-webpack-plugin能夠支援es6語法

在webpack.prod.config.js中新增

const UglifyJsPlugin = require('uglifyjs-webpack-plugin')

module.exports= {
  // 其他配置...,
  plugins:[
    new UglifyJsPlugin({
      cache: true,
      uglifyOptions: {
        compress: {
          drop_debugger: true,
          drop_console: true,
        },
        ecma: 6,
        output: {
          comments: false,
          beautify: false,
        },
        warnings: false,
      },
      sourceMap: false,
      parallel: true,
    }),
  ]
}

(4)使用開發環境資源快取,提高第二次及以後的啟動速度

在webpack.dev.conf.js中,新增配置

const HardSourceWebpackPlugin = require('hard-source-webpack-plugin')

module.exports = {
  // ...其他配置
  plugins: [
     // 快取加速二次構建速度
    new HardSourceWebpackPlugin({
      // Either an absolute path or relative to webpack's options.context.
      // 設定快取在磁碟中存放的路徑
      cacheDirectory: './../disk/.cache/hard-source/[confighash]',
      // Either a string of object hash function given a webpack config.
      recordsPath: './../disk/.cache/hard-source/[confighash]/records.json',
      configHash: function (webpackConfig) {
        // node-object-hash on npm can be used to build this.
        return require('node-object-hash')({ sort: false }).hash(webpackConfig);
      },
      // An object.
      info: {
        // 'none' or 'test'.
        mode: 'none',
        // 'debug', 'log', 'info', 'warn', or 'error'.
        level: 'debug',
      },
      // Clean up large, old caches automatically.
      cachePrune: {
        // Caches younger than `maxAge` are not considered for deletion. They must
        // be at least this (default: 2 days) old in milliseconds.
        maxAge: 2 * 24 * 60 * 60 * 1000,
        // All caches together must be larger than `sizeThreshold` before any
        // caches will be deleted. Together they must be at least this
        // (default: 50 MB) big in bytes.
        sizeThreshold: 100 * 1024 * 1024
      },
    }),


  ]
}

第二部分:vue/cli3+版本

vue/cli3+版本里面的webpack配置作者進行了內建

我們需要在vue.config.js中進行配置

(1)

const path = require('path')
const webpack = require('webpack')
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin')
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin')

// 拼接路徑
function resolve (dir) {
  return path.join(__dirname, dir)
}

const baseUrlObject = {
  development: '',
  beta: '//fezz.wormpex.com/ripei-fe-web',
  prod: '//fezz.blibee.com/ripei-fe-web',
  production: '//fezz.blibee.com/ripei-fe-web'
}

const env = process.env.NODE_ENV || 'production'

// 基礎路徑 注意釋出到生產環境之前要先修改這裡
const baseUrl = baseUrlObject[env] || process.env.VUE_APP_BASE_URL
const IS_DEV = process.env.VUE_APP_NODE_ENV === 'development'

console.log(process.env.NODE_ENV)
console.log(process.env.VUE_APP_BASE_URL)
console.log(baseUrl)

// 用於開發環境下,快取第一次編譯的檔案,提高二次構建速度
const devPlugins = [
  // 快取加速二次構建速度
  new HardSourceWebpackPlugin({
    // Either an absolute path or relative to webpack's options.context.
    // 設定快取在磁碟中存放的路徑
    cacheDirectory: './../disk/.cache/hard-source/[confighash]',
    // Either a string of object hash function given a webpack config.
    recordsPath: './../disk/.cache/hard-source/[confighash]/records.json',
    configHash: function (webpackConfig) {
      // node-object-hash on npm can be used to build this.
      return require('node-object-hash')({ sort: false }).hash(webpackConfig);
    },
    // An object.
    info: {
      // 'none' or 'test'.
      mode: 'none',
      // 'debug', 'log', 'info', 'warn', or 'error'.
      level: 'debug',
    },
    // Clean up large, old caches automatically.
    cachePrune: {
      // Caches younger than `maxAge` are not considered for deletion. They must
      // be at least this (default: 2 days) old in milliseconds.
      maxAge: 4 * 24 * 60 * 60 * 1000,
      // All caches together must be larger than `sizeThreshold` before any
      // caches will be deleted. Together they must be at least this
      // (default: 50 MB) big in bytes.
      sizeThreshold: 100 * 1024 * 1024
    },
  }),
  new HardSourceWebpackPlugin.ExcludeModulePlugin([
    {
      test: /mini-css-extract-plugin[\\/]dist[\\/]loader/
    }
  ])
]

const configPlugins = [
  new webpack.ProvidePlugin({
    $: 'jquery',
    jQuery: 'jquery',
    'windows.jQuery': 'jquery'
  }),
  new webpack.DllReferencePlugin({
    context: process.cwd(),
    manifest: require('./public/vendor/vendor-manifest.json')
  }),
  // 將 dll 注入到 生成的 html 模板中
  new AddAssetHtmlPlugin({
    // dll檔案位置
    filepath: path.resolve(__dirname, './public/vendor/*.js'),
    // dll 引用路徑
    publicPath: `${baseUrl}/vendor`,
    // dll最終輸出的目錄
    outputPath: './vendor'
  })
]

module.exports = {
  publicPath: baseUrl, // 根據你的實際情況更改這裡
  lintOnSave: true,
  configureWebpack: { // 引入jquery
    // externals: { // 引入百度地圖
    //   'BMap': 'BMap'
    // },
    plugins: IS_DEV ? configPlugins.concat(devPlugins) : configPlugins
  },
  devServer: {
    https: false,
    publicPath: baseUrl, // 和 baseUrl 保持一致
    // 代理設定
    proxy: {
     
    }
  },
  // webpack 設定
  chainWebpack: config => {
    // 修復HMR
    config.resolve.symlinks(true)
    // svg
    const svgRule = config.module.rule('svg')
    svgRule.uses.clear()
    svgRule
      .include
      .add(resolve('src/assets/svg-icons/icons'))
      .end()
      .use('svg-sprite-loader')
      .loader('svg-sprite-loader')
      .options({
        symbolId: 'd2-[name]'
      })
      .end()
    // image exclude
    const imagesRule = config.module.rule('images')
    imagesRule
      .test(/\.(png|jpe?g|gif|webp|svg)(\?.*)?$/)
      .exclude
      .add(resolve('src/assets/svg-icons/icons'))
      .end()
    // 重新設定 alias
    config.resolve.alias
      .set('@', resolve('src'))
    // babel-polyfill 加入 entry
    const entry = config.entry('app')
    entry
      .add('babel-polyfill')
      .end()
  }
}

(2)使用DllPlugin進行包的拆分

在根目錄下建立webpack.dll.conf.js

/**
 * 配置vue-cli4工程的DllPlugins,用以減少熱更新和打包時的檔案數量,提高熱更新和打包的速度
 * 參考文獻:https://blog.csdn.net/DongFuPanda/article/details/104866788
 * 步驟
 */

const path = require('path')
const webpack = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

// dll檔案存放的目錄
const dllPath = 'public/vendor'

module.exports = {
  entry: {
    // 需要提取的庫檔案
    vendor: ['vue', 'vue-router', 'vuex', 'axios', 'jquery', 'moment', 'ramda', 'element-ui', 'core-js', 'lodash']
  },
  output: {
    path: path.join(__dirname, dllPath),
    filename: '[name].[hash].dll.js',
    // vendor.dll.js中暴露出的全域性變數名
    // 保持與 webpack.DllPlugin 中名稱一致
    library: '[name]_[hash]'
  },
  plugins: [
    // 清除之前的dll檔案
    new CleanWebpackPlugin(),
    // 設定環境變數
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify('production')
      }
    }),
    // manifest.json 描述動態連結庫包含了哪些內容
    new webpack.DllPlugin({
      path: path.join(__dirname, dllPath, '[name]-manifest.json'),
      // 保持與 output.library 中名稱一致
      name: '[name]_[hash]',
      context: process.cwd()
    })
  ]
}

在package.json中新增

 "scripts": {
    "start": "vue-cli-service serve --open --mode development",
    "serve": "vue-cli-service serve --open --mode development",
    "build": "vue-cli-service build",
    "beta": "vue-cli-service build --mode beta",
    "lint": "vue-cli-service lint",
    "dll": "webpack -p --progress --config ./webpack.dll.conf.js"
  },

使用webpack-bundle-analyzer 進行程式碼打包體積分析

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin


module.exports = {
  // ...其他配置
  plugins:[
       // new BundleAnalyzerPlugin()
  ]
}