1. 程式人生 > >webpack-DllPlugin優化打包效能(基於vue-cli)

webpack-DllPlugin優化打包效能(基於vue-cli)

1.介紹

Dll這個概念應該是借鑑了Windows系統的dll。一個dll包,就是一個純純的依賴庫,它本身不能執行,是用來給你的app引用的。

打包dll的時候,Webpack會將所有包含的庫做一個索引,寫在一個manifest檔案中,而引用dll的程式碼(dll user)在打包的時候,只需要讀取這個manifest檔案,就可以了。

簡單說

將靜態資原始檔(執行依賴包)與原始檔分開打包,先使用DllPlugin給靜態資源打包,再使用DllReferencePlugin讓原始檔引用資原始檔。

2.預打包依賴模組

在根目錄建立一個webpack.dll.config.js

的配置檔案

var path = require("path");
var webpack = require("webpack");

module.exports = {
  // 你想要打包的模組的陣列
  entry: {
    vendor: ['vue', 'lodash', 'vuex', 'axios', 'vue-router', 'element-ui']
  },
  output: {
    path: path.join(__dirname, './static/js'), // 打包後文件輸出的位置
    filename: '[name].dll.js',
    library:
'[name]_library' // vendor.dll.js中暴露出的全域性變數名。 // 主要是給DllPlugin中的name使用, // 故這裡需要和webpack.DllPlugin中的`name: '[name]_library',`保持一致。 }, plugins: [ new webpack.DllPlugin({ path: path.join(__dirname, '.', '[name]-manifest.json'), name: '[name]_library', context: __dirname }
), // 壓縮打包的檔案,與該文章主線無關 new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }) ] };

重點:這裡引入的Dllplugin外掛,該外掛將生成一個manifest.json檔案,該檔案供webpack.config.js中加入的DllReferencePlugin使用,使我們所編寫的原始檔能正確地訪問到我們所需要的靜態資源(執行時依賴包)。

  • path:manifest.json生成的資料夾及名字,該專案讓它生成在了根目錄下。
  • name:和output. library保持一致即可。
  • context:選填,manifest檔案中請求的上下文,預設為該webpack檔案上下文。(!!!我在學習這個外掛時一直沒有成功的坑點之一!!,這個上下文必須必須同webpack.config.js中DllReferencePlugin外掛的context所指向的上下文保持一致!!)

編寫完之後就可以預打包資原始檔了

## 以指定webpack檔案執行webpack打包
webpack --config ./webpack.dll.config.js

還可以把這句加到package.json裡面

"scripts": {
    "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
    "build": "cross-env NODE_ENV=production webpack --progress",
    "dll": "webpack --config ./webpack.dll.config.js"
  },

打包完成後,會在static檔案下面有個js檔案,其內有個vendor.dll.js,除此之外根目錄下還生成了vendor-manifest.json.現在我們已經不再需要將使用的那些包同源檔案一起打包了,但是這也需要在原始檔的webpack中配置DllReferencePlugin使用vendor-manifest.json來引用這個dll。

3.打包原始檔

這一步我們只需要改寫vue-cli為我們生成好的webpack.config.js即可:

var path = require('path')
var webpack = require('webpack')

module.exports = {
  entry: './src/main.js',
  output: {
    path: path.resolve(__dirname, './dist'),
    publicPath: '/dist/',
    filename: 'build.js'
  },
  module: {
    // ...(省略未複製,並不是刪除了module裡的東西)
  },
  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js'
    }
  },
  devServer: {
    historyApiFallback: true,
    noInfo: true
  },
  performance: {
    hints: false
  },
  devtool: '#eval-source-map',
  plugins: [
    new webpack.DllReferencePlugin({
      context: __dirname,
      manifest: require('./vendor-manifest.json')
    })
  ]
}

if (process.env.NODE_ENV === 'production') {
  module.exports.devtool = '#source-map'
  // http://vue-loader.vuejs.org/en/workflow/production.html
  module.exports.plugins = (module.exports.plugins || []).concat([
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: '"production"'
      }
    }),
    new webpack.optimize.UglifyJsPlugin({
      sourceMap: true,
      compress: {
        warnings: false
      }
    }),
    new webpack.LoaderOptionsPlugin({
      minimize: true
    })
  ])
}

該檔案裡主要是添加了plugins配置:

plugins: [
    new webpack.DllReferencePlugin({
      context: __dirname,
      manifest: require('./vendor-manifest.json')
    })
  ]

context:與Dllplugin裡的context所指向的上下文保持一致,這裡都是指向了根目錄。
manifest:引入Dllplugin所生成的的manifest

最後

我們需要手動在根目錄的index.html裡引入所生成的dll庫。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>dll-test</title>
  </head>
  <body>
    <div id="app"></div>
    <script src="./static/js/vendor.dll.js"></script>
    <script src="/dist/build.js"></script>
  </body>
</html>

這裡也很講究,也是我之前失敗的原因之一!!我之前直接把的後面,導致一直報錯!其實這裡稍微動動腦筋就能明白,我們在main.js中引入的各種包,而main.js最終被打包為了build.js,那麼我們肯定要先把包引進來才能正確使用build.js啊!所以vendor.dll.js必須放在build.js之前引入。

其實還有一個問題

在webpack.config.js中:

  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js'
    }
  },

這就代表main.js中的import Vue from ‘vue’ 其實是引用的’vue/dist/vue.esm.js’,而webpack.dll.config.js並不知道vue指代的是’vue/dist/vue.esm.js’,所以我們需要修改webpack.dll.config.js配置:

  vendor: ['vue', 'lodash', 'vuex', 'axios', 'vue-router', 'element-ui']
  },
  entry: {
    vendor: ['vue/dist/vue.esm.js', 'lodash', 'vuex', 'axios', 'vue-router', 'element-ui']
  },

由於我們修改了webpack.dll.config.js,所以我們需要重新打包:

## 重新打包dll
npm run dll
## 重新打包原始檔
npm run build

到這裡優化就結束了,但是有個小麻煩,我們每次上線的時候需要新建資料夾,然後把index.html複製貼上進去,再把dist目錄貼上進去,還要把static貼上進去再上線,我們不希望那麼麻煩。

4.提取整個資料夾

首先安裝幾個外掛:

npm install html-webpack-plugin copy-webpack-plugin clean-webpack-plugin --save-dev

然後我們修改webpack.config.js配置:

var path = require('path')
var webpack = require('webpack')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var CopyWebpackPlugin = require('copy-webpack-plugin')
var CleanWebpackPlugin = require('clean-webpack-plugin')

module.exports = {
  entry: './src/main.js',
  output: {
    path: path.resolve(__dirname, './dist'),
    publicPath: '/',
    filename: 'build.[hash].js'
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          loaders: {
          }
          // other vue-loader options go here
        }
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        exclude: /node_modules/
      },
      {
        test: /\.(png|jpg|gif|svg)$/,
        loader: 'file-loader',
        options: {
          name: '[name].[ext]?[hash]'
        }
      }
    ]
  },
  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js'
    }
  },
  devServer: {
    historyApiFallback: true,
    noInfo: true,
    contentBase: path.join(__dirname, 'dist')
  },
  performance: {
    hints: false
  },
  devtool: '#eval-source-map',
  plugins: [
    new webpack.DllReferencePlugin({
      context: __dirname,
      manifest: require('./vendor-manifest.json')
    }),
    new HtmlWebpackPlugin({
      inject: true,
      template: './index.html',
      filename: 'index.html'
    }),
    new CopyWebpackPlugin([
      { from: 'static', to: 'static' }
    ]),
    new CleanWebpackPlugin(['dist'])
  ]
}

if (process.env.NODE_ENV === 'production') {
  module.exports.devtool = '#source-map'
  // http://vue-loader.vuejs.org/en/workflow/production.html
  module.exports.plugins = (module.exports.plugins || []).concat([
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: '"production"'
      }
    }),
    new webpack.optimize.UglifyJsPlugin({
      sourceMap: true,
      compress: {
        warnings: false
      }
    }),
    new webpack.LoaderOptionsPlugin({
      minimize: true
    })
  ])
}
差別一:
 output: {
    path: path.resolve(__dirname, './dist'),
    publicPath: '/',
    filename: 'build.[hash].js'
  },
  • 加了hash值,清除瀏覽器快取
  • publicPath: ‘/’: 由於index.html需要打包到dist目錄下,故’build.[hash].js’的引用路徑由/dist/變為/
差別二:

主要是在plugins新添加了三個外掛(千萬不能加在if裡,否則開發環境會出錯):

new HtmlWebpackPlugin({
  inject: true,
  template: './index.html'
}),
new CopyWebpackPlugin([
  {from: 'static', to:'static'}
]),
new CleanWebpackPlugin(['dist'])
  • html-webpack-plugin:我們要index.html生成在dist目錄裡,故引用html-webpack-plugin,它預設將生成的index.html打包在output的資料夾下,由於index.html需要引用打包好的靜態dll,且vue需要掛載在根元件div#app上,故需要引入模版,模版為根目錄下index.html:
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>dll-test</title>
  </head>
  <body>
    <div id="app"></div>
    <script src="./static/js/vendor.dll.js"></script>
  </body>
</html>
  • inject:true:將打包好的js檔案注入在該html的body底部,保證了script的載入順序。

  • copy-webpack-plugin:由於我們要引入static/js/中的dll,且是在dist檔案下,故用該外掛將打包好的靜態資料夾拷貝到打包目錄dist下。

  • clean-webpack-plugin:打包前清除dist目錄

差別三:
  devServer: {
    historyApiFallback: true,
    noInfo: true,
    contentBase: path.join(__dirname, 'dist')
  },

contentBase: path.join(__dirname, ‘dist’),這是為了在dev環境下,提供index.html的目錄改為dist,與生產環境的訪問檔案路徑保持一致。

最後

npm run dev就可以愉快的開發啦,npm run build就可以以極速打包好dist目錄且上線啦。!!!