使用 vue-cli 構(gòu)建項(xiàng)目中的 Webpack優(yōu)化

webpack 升級(jí) 3.x

根據(jù)官方的 release 聲明,webpack 3.x 的一個(gè)顯著特性是 Scope Hoisting豁陆。之前 webpack 構(gòu)建時(shí)會(huì)把每個(gè)模塊單獨(dú)的放到一個(gè)閉包函數(shù)中像啼,這會(huì)增加 javascript 的執(zhí)行時(shí)間。 3.x 之后埠巨,webpack 則會(huì)將有聯(lián)系的模塊义辕,放到一個(gè)大的閉包函數(shù)里面去虾标,這樣可以減少構(gòu)建出來的文件大小和 javascript 的執(zhí)行時(shí)間。要讓 Scope Hoisting 起作用灌砖,只需要添加下面的插件:

module.exports = {  
  plugins: [
    new webpack.optimize.ModuleConcatenationPlugin()
  ]
};

這條優(yōu)化主要是針對(duì) javascript 執(zhí)行效率的璧函,自己在項(xiàng)目中使用時(shí),對(duì)于構(gòu)建的時(shí)間并沒有顯著提升基显。

減小文件搜索范圍

這一點(diǎn)的優(yōu)化可以讓 webpack 不必自己遍歷去搜索模塊蘸吓,而可以通過我們定義的路徑,快速定位模塊撩幽。配置代碼如下:

resolve: {
  //...
  // modules選項(xiàng)配置库继,指定node_modules文件夾所在的位置
  //節(jié)省webpack去查找的時(shí)間
  modules: [resolve('node_modules')], 
  alias: {
    // vue-cli自帶生成,'vue$' 表示嚴(yán)格匹配 'vue' 這個(gè)字段
    'vue$': 'vue/dist/vue.esm.js', 
    '@': resolve('src'), // vue-cli自帶生成
    'api': resolve('src/api')
    //... 其他配置
  }
}

resolve.modules是用來配置模塊庫(通常是 node_modules)所在的位置窜醉。默認(rèn)的配置會(huì)采用向上遞歸搜索的方式去尋找宪萄,為了減少搜索范圍,可以直接寫明 node_modules 的全路徑榨惰。

而alias是指配置別名拜英。通過編寫alias,既能讓webpack查找文件定位更快琅催,在開發(fā)的時(shí)候居凶,也能少些很多相對(duì)路徑的../..虫给,在引入模塊的時(shí)候也會(huì)很方便。

運(yùn)行時(shí)構(gòu)建

Vue官網(wǎng)的安裝指南里面有這么一段話

當(dāng)使用 vue-loader 或 vueify 的時(shí)候侠碧,*.vue 文件內(nèi)部的模板會(huì)在構(gòu)建時(shí)預(yù)編譯成 JavaScript抹估。你在最終打好的包里實(shí)際上是不需要編譯器的,因?yàn)橹皇怯眠\(yùn)行時(shí)構(gòu)建即可弄兜。
因?yàn)檫\(yùn)行時(shí)構(gòu)建相比完整版縮減了 30% 的體積药蜻,你應(yīng)該盡可能使用這個(gè)版本。

但是在 vue-cli 構(gòu)建出來的項(xiàng)目配置中挨队,我們發(fā)現(xiàn)它還是使用了完整版構(gòu)建谷暮。那把它改正運(yùn)行時(shí)構(gòu)建會(huì)怎么樣呢?

resolve: {
  //... 
  alias: {
    /* vue-cli自帶生成盛垦,'vue$' 表示嚴(yán)格匹配 'vue' 這個(gè)字段 */
    // 'vue$': 'vue/dist/vue.esm.js', /* 自帶生成完整版 */
    'vue$': 'vue/dist/vue.runtime.esm.js', /* 改為運(yùn)行時(shí) */
    //... 
  }
}

npm run dev 之后會(huì)發(fā)現(xiàn)報(bào)了這么一個(gè)錯(cuò)

vue 報(bào)錯(cuò)

我們明明使用了 vue-loader湿弦,按照官網(wǎng)的描述,為什么還會(huì)提示需要 vue 包含編譯器的版本呢腾夯?原因在于我們實(shí)例化 vue 的時(shí)候颊埃,還是使用了需要編譯器的寫法,在入口文件 main.js 最下面蝶俱,找到實(shí)例化 vue 的代碼

/* vue-cli 自動(dòng)生成 */
new Vue({
  el: '#app',
  router,
  store,
  template: '<App/>',
  components: { App }
})

為了能使用運(yùn)行時(shí)構(gòu)建班利,我們需要把代碼改成不需要編譯器的形式

/* 修改之后 */
new Vue({
  el: '#app',
  router,
  store,
  // template: '<App/>', /* 如果 Vue 選項(xiàng)中包含渲染函數(shù),該模板將被忽略 */
  // components: { App },
  render: h => h(App), // 使用render方式引入組件
})

修改之后榨呆,運(yùn)行沒有問題罗标。通過這種修改方式,在項(xiàng)目里就可以用更小的 vue 文件進(jìn)行構(gòu)建了积蜻。

happypack

webpack 的構(gòu)建是單進(jìn)程的闯割。采用 happypack 可以改為多進(jìn)程構(gòu)建,從而提高構(gòu)建速度竿拆。但是我在項(xiàng)目中使用了之后宙拉,構(gòu)建速度并沒有網(wǎng)上描述的提升那么明顯。

首先安裝:npm install happypack --save-dev

然后修改 webpack 配置文件:

const os = require('os');
const HappyPack  = require('happypack');
const happThreadPool = HappyPack.ThreadPool({size: os.cpus().length}); // 采用多進(jìn)程丙笋,進(jìn)程數(shù)由CPU核數(shù)決定
//...
module.exports = {
  plugins: [
    // ...
    new HappyPack({
      id: 'js',
      cache: true,
      loaders: ['babel-loader?cacheDirectory=true'], // ?cacheDirectory=true 開啟babel-loader的緩存
      threadPool: happThreadPool
    })
  ],
  module: {
    rules: [
      {
        // vue-loader 似乎有兼容性問題谢澈,故沒有使用
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig ,
      },
      {
        test: /\.js$/,
        loader: ['happypack/loader?id=js'], // 將loader換成happypack
        include: [resolve('src')],
      },
    ]
  }
}

DllPlugin和DllReferencePlugin

在日常工作中,每當(dāng)修改了業(yè)務(wù)代碼之后御板,一些第三方的锥忿,體積比較大的模塊也會(huì)被重新打包,極大的浪費(fèi)了時(shí)間怠肋。這時(shí)我們就可以使用 DLL 預(yù)先把一些第三方模塊提前打包缎谷,以后修改業(yè)務(wù)代碼再構(gòu)建時(shí)就不會(huì)構(gòu)建這些模塊了。所以,DLL 的作用列林,簡(jiǎn)單的說是把之前一步完成的構(gòu)建分成了兩步:

  1. 將一些第三方的模塊抽離出來(比如 vue、echarts酪惭、axios等)希痴,預(yù)先構(gòu)建一遍
  2. 構(gòu)建其他模塊和業(yè)務(wù)代碼

因?yàn)榈谌降哪K變動(dòng)并不會(huì)頻繁,在第一步構(gòu)建之后的 bundle 就可以長(zhǎng)期的放在項(xiàng)目里春感,后續(xù)的構(gòu)建時(shí)只需要進(jìn)行第二步砌创,這樣會(huì)節(jié)省大量的構(gòu)建時(shí)間。

build 文件夾里建立一個(gè) webpack.dll.conf.js鲫懒,我們將一些常用的依賴打包成dll嫩实。

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

module.exports = {
  // dll 包含模塊的名字
  entry: {
    vendor: ['vue/dist/vue.runtime.esm.js', 'lodash', 'vuex', 'axios', 'vue-router', 'element-ui']
  },
  output: {
    path: path.join(__dirname, './static/js'), 
    filename: '[name].dll.js',
    // 給DllPlugin中的name使用,需要和 webpack.DllPlugin 中的 name 字段保持一致
    library: '[name]_library'
  },
  plugins: [
    new webpack.DllPlugin({
      path: path.join(__dirname, '.', '[name]-manifest.json'),
      // 和上面的 library 保持一致
      name: '[name]_library', 
      context: __dirname
    }),
  ]
};

在 package.json 中加入:

"scripts": {
    "dll": "webpack --config ./build/webpack.dll.config.js"
  },

運(yùn)行 npm run dll 之后窥岩,會(huì)在 static/js 文件夾下生成 vender.dll.js甲献,會(huì)在 build 文件夾下生成 vender-manifest.json。這個(gè) vender-manifest.json 會(huì)被 webpack.base.config.js 中加入的新插件 DllReferencePlugin 使用颂翼,以便于業(yè)務(wù)代碼能正確地訪問到構(gòu)建好的第三方模塊的晃洒。

webpack.base.config.js 中,添加 DllReferencePlugin

module.exports = {
  //... 其他配置
  
  plugins: [
    // ... 其他插件
    new webpack.DllReferencePlugin({
      context: __dirname,
      manifest: require('./vendor-mainfest.json') // 指向之前生成的 vendor-mainfest.json
    })
  ]
}

最后朦乏,在項(xiàng)目輸出的index.html里球及,最先引入這個(gè)js:

<body>
    <div id="app"></div>
    <script src="./static/js/vendor.dll.js"></script>
    <script src="/dist/build.js"></script>
</body>

這樣只要第三方模塊不變,我們之后的上線構(gòu)建都不用運(yùn)行 npm run dll了呻疹。只構(gòu)建業(yè)務(wù)代碼和一些體積比較小的包吃引,構(gòu)建速度是非常快的刽锤。

devtools

// vue-cli 默認(rèn)配置
devtool: config.build.productionSourceMap ? '#source-map' : false,

devtools 決定了 source-map 的生成方式镊尺。source-map的作用這里不深入展開,有興趣可以參考阮一峰老師的?這篇文章姑蓝。下圖是 webpack 官網(wǎng)給出的 devtools 幾種配置選項(xiàng):

devtool配置項(xiàng)

vue-cli 在生產(chǎn)環(huán)境默認(rèn)采用的模式是 source-map鹅心,可以發(fā)現(xiàn),這種模式不管對(duì)于 build 還是 rebuild纺荧,都會(huì)?增加時(shí)間旭愧。可以根據(jù)項(xiàng)目需求更改配置宙暇。我在生產(chǎn)環(huán)境使用的是eval模式输枯,因?yàn)槲矣X得生產(chǎn)環(huán)境為了減少請(qǐng)求和混淆代碼可以不用加上source-map,畢竟很少在生產(chǎn)環(huán)境進(jìn)行debug占贫。開發(fā)環(huán)境我采用 cheap-module-eval-source-map 模式桃熄。這樣針對(duì)生產(chǎn)環(huán)境再進(jìn)行構(gòu)建,會(huì)發(fā)現(xiàn)時(shí)間也是大幅度減少了型奥。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瞳收,一起剝皮案震驚了整個(gè)濱河市碉京,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌螟深,老刑警劉巖谐宙,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異界弧,居然都是意外死亡凡蜻,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門垢箕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來划栓,“玉大人,你說我怎么就攤上這事条获≈臆瘢” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵月匣,是天一觀的道長(zhǎng)钻洒。 經(jīng)常有香客問我,道長(zhǎng)锄开,這世上最難降的妖魔是什么素标? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮萍悴,結(jié)果婚禮上头遭,老公的妹妹穿的比我還像新娘。我一直安慰自己癣诱,他們只是感情好计维,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著撕予,像睡著了一般鲫惶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上实抡,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天欠母,我揣著相機(jī)與錄音,去河邊找鬼吆寨。 笑死赏淌,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的啄清。 我是一名探鬼主播六水,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了掷贾?” 一聲冷哼從身側(cè)響起睛榄,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎想帅,沒想到半個(gè)月后懈费,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡博脑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了票罐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片叉趣。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖该押,靈堂內(nèi)的尸體忽然破棺而出疗杉,到底是詐尸還是另有隱情,我是刑警寧澤蚕礼,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布烟具,位于F島的核電站,受9級(jí)特大地震影響奠蹬,放射性物質(zhì)發(fā)生泄漏朝聋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一囤躁、第九天 我趴在偏房一處隱蔽的房頂上張望冀痕。 院中可真熱鬧,春花似錦狸演、人聲如沸言蛇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽腊尚。三九已至,卻和暖如春满哪,著一層夾襖步出監(jiān)牢的瞬間婿斥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來泰國打工翩瓜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留受扳,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓兔跌,卻偏偏與公主長(zhǎng)得像勘高,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容