跟着Vue-cli來'學'並'改'Webpack之 打包優化

首先,咱們要知道什麼要用webpack來打包,這樣打包有那些好處。咱們能夠簡單的列出如下幾點:javascript

  • 單文件組件 (.vue文件)
  • 優化Vue構建過程 (alias等)
  • 瀏覽器緩存管理
  • 代碼分離 (懶加載等)

這篇文章的重點講的就是webpack打包之優化瀏覽器緩存管理,vue-cli生成的腳手架的配置中,已經作了不少對於打包,利用緩存的優化處理,本文未來學校其中知識,而且作出改動。css

瞭解瀏覽器的緩存原理

在此以前呢,咱們須要先了解瀏覽器緩存是怎麼工做的,先抄了一張圖。html

  • 瀏覽器: 我須要 test.js
  • 服務器:找到了給你,而且在259200秒(一個月)內別來找我
  • 瀏覽器:好的,那我緩存到磁盤裏

過了一個星期,再次訪問這個頁面vue

  • 瀏覽器:我須要test.js,緩存期限還在,直接從磁盤讀取
  • 服務器:沒我卵事
  • 用戶:哇塞打開頁面好快

應產品經理需求更改了一個圖標java

  • 瀏覽器:我須要test.js,緩存期限還在,直接從磁盤讀取
  • 產品經理:發佈了嗎?你肯定?怎麼沒效果啊?
  • 服務器:吃瓜

弄清了原理,咱們就知道怎麼去破壞緩存機制,讓瀏覽器請求到新的文件。node

清楚緩存技術

ctrl+F5 強制刷新頁面

手動強制刷新頁面,可是用戶不是程序員啊,他們怎麼會知道須要強制刷新呢,因此這個方案給用戶確定是不可行的。webpack

更改文件

  1. 修改文件的名字:test.js -> test.v2.js
  2. 修改文件的路徑:/static/test.js -> /static/v2/test.js
  3. 加 query string : test.js -> test.js?v=qwer

咱們瞭解完了如何清楚緩存,再來看看Vue-cli模版中是如何進行配置的ios

Code Splitting(代碼分割)

什麼是代碼分割

咱們直接生成一個Vue-cli的新項目,安裝依賴後直接運行 npm run build命令,並打開/dist/js文件目錄程序員

發現有3個js文件,這就是webpack將代碼進行了分割。web

爲何要進行代碼分割

  1. 分離業務代碼和第三方庫( vendor )
  2. 按需加載(利用 import() 語法)

之因此把業務代碼和第三方庫代碼分離出來,是由於產品經理的需求是源源不斷的,所以業務代碼更新頻率大,相反第三方庫代碼更新迭代相對較慢且能夠鎖版本,因此能夠充分利用瀏覽器的緩存來加載這些第三方庫。

而按需加載的適用場景,好比說「訪問某個路由的時候再去加載對應的組件」,用戶不必定會訪問全部的路由,因此不必把全部路由對應的組件都先在開始的加載完;更典型的例子是「某些用戶他們的權限只能訪問某些頁面」,因此不必把他們沒權限訪問的頁面的代碼也加載

剖析 vue-cli 的 webpack Code Splitting

分離業務代碼和第三方庫( vendor )

vue-cli中使用了 CommonsChunkPlugin這個webpack插件來提取框架代碼。 打開 webpack.prod.conf.js 文件,找到下面這段代碼

new webpack.optimize.CommonsChunkPlugin({
  name: 'vendor',
  minChunks: function (module, count) {
    // any required modules inside node_modules are extracted to vendor
    return (
      module.resource &&
      /\.js$/.test(module.resource) &&
      module.resource.indexOf(
        path.join(__dirname, '../node_modules')
      ) === 0
    )
  }
}),
複製代碼

這段代碼在打包的時候把 node_modules 下面的 ,而且名字是 .js 結尾的,而且不是重複的模塊提取到vender裏面。

因此打包後應該會生成app.js(業務代碼)、vender.js(框架代碼)這個兩個文件,細心的同窗可能會發現還有個 manifest.js,在後面咱們講進行解讀。

按需加載(利用 import() 語法)

若是咱們修改一下hello組件的加載方式改成路由懶加載(import()語法),在進行打包

// import Hello from '@/components/Hello'

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Hello',
      // component: Hello,
      component: () => import('@/components/Hello')
    }
  ]
})
複製代碼

很明顯的看到,打包後有4個js文件,仔細的同窗還發現,app.js文件的大小加上新多出文件的大小,正好等於沒有分割打包的app的大小。 這樣等於異步加載的組件,是單獨打包成了一個js,在頁面首次加載的時候不須要加載他,等到請求相應的頁面的時候在去服務器請求它,減少了頁面首屏加載的時間。

vue-cli /webpack.prod.conf.js 中配置 output.chunkFilename 規定了打包異步文件的格式

output: {
    path: config.build.assetsRoot,
    filename: utils.assetsPath('js/[name].[chunkhash].js'),
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') 
  },
複製代碼

如何利用利用瀏覽器緩存

目標

假設咱們如今有不少不少的靜態文件,而後每次須要更新不少不少的文件,那是否是要手動地一個一個地修改文件的名字呢?咱們的理想固然是:哪一個文件更新了,就自動地生成一個新的文件名。

另外,若是咱們打包出來的靜態文件只有一個單獨的 JavaScript 文件 app.js ,那麼每次改動一點代碼,app.js 的文件名確定都會變。但實際上,我只改動了某個模塊的代碼(其餘模塊並無修改),就破壞了其餘模塊的緩存,這顯然沒有充分利用到緩存啊。咱們的目標是:

哪一個模塊更新了破壞他的緩存,沒更新的模塊繼續利用緩存。

步驟1:增長hash值

上文中咱們提到清楚緩存的三種方式:修改文件名,修改路徑,給url加參數,webpack的作法是修改文件名。 vue-cli /webpack.prod.conf.js 中 output.chunkFilename 規定了打包異步文件的格式

output: {
    path: config.build.assetsRoot,
    filename: utils.assetsPath('js/[name].[chunkhash].js'),   
    // 規定文件名爲 js文件夾下 Chunk.name . hash值 .js 的文件
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
    // 規定文件名爲 js文件夾下 module id. hash值 .js 的文件
  },
複製代碼

這樣的話給每一個文件加上了hash值,那個文件發生了變化hash值就會改變

步驟2:提取manifast文件

爲何要提取manifast文件呢?

緣由是 vendor chunk 裏面包含了 webpack 的 runtime 代碼(用來解析和加載模塊之類的運行時代碼)

這樣會致使:即便你沒有更改引入模塊(vendor的模塊沒有發生變更的狀況下,你僅僅修改了其餘代碼) 也會致使 vendor 的chunkhash值發生變化,從而破壞了緩存,達不到預期效果

vue-cli /webpack.prod.conf.js 提取 manifast

new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      minChunks: Infinity
    }),
複製代碼

步驟3:根據模塊的相對路徑生成一個四位數的hash做爲模塊id

webpack 裏每一個模塊都有一個 module id ,module id 是該模塊在模塊依賴關係圖裏按順序分配的序號,若是這個 module id 發生了變化,那麼他的 chunkhash 也會發生變化。

這樣會致使:若是你引入一個新的模塊,會致使 module id 總體發生改變,可能會致使全部文件的chunkhash發生變化,這顯然不是咱們想要的

這裏須要用 HashedModuleIdsPlugin ,根據模塊的相對路徑生成一個四位數的hash做爲模塊id,這樣就算引入了新的模塊,也不會影響 module id 的值,只要模塊的路徑不改變的話。

vue-cli /webpack.prod.conf.js

new webpack.HashedModuleIdsPlugin()
複製代碼

完成目標

至此若是咱們改了某個模塊的代碼,是不會破壞其餘模塊的緩存,這就是咱們想要實現的持久性緩存。

改造vue-cli中的webpack提高首頁加載速度

分析

咱們首先來看一個實際項目

運行 npm run build --report 能夠查看打包分佈圖 咱們發現最大的文件仍是vendor,大部分框架代碼都打包在這裏面,而這些框架代碼是不常變化的,也不須要每次進行打包。因此咱們能夠想辦法把他們提取出來,掛到cdn上面去。

具體步驟

以 vue, vue-router,element-ui爲例

步驟1 index.html cdn引入框架

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>demo-vue-project</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/element-ui/2.0.8/theme-chalk/index.css">
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
    <script src="https://cdn.bootcss.com/vue/2.5.13/vue.min.js"></script>
    <script src="https://cdn.bootcss.com/vue-router/2.7.0/vue-router.min.js"></script>
    <script src="https://cdn.bootcss.com/element-ui/2.0.7/index.js"></script>
  </body>
</html>
複製代碼

步驟2 修改 build/webpack.base.conf.js

module.exports = {
  ...
  externals: {
    'vue': 'Vue',
    'vue-router': 'VueRouter',
    'element-ui': 'ELEMENT'
  },
  ...
}
複製代碼

步驟3 修改框架註冊方式

修改 src/router/index.js

// import Vue from 'vue'
import VueRouter from 'vue-router'
// 註釋掉
// Vue.use(VueRouter)
...
複製代碼

修改 src/main.js

import Vue from 'vue'
import App from './App'
import router from './router'
import ELEMENT from 'element-ui'
// import 'element-ui/lib/theme-chalk/index.css'

Vue.config.productionTip = false

Vue.use(ELEMENT)
Vue.prototype.$http = axios

/* eslint-disable no-new */
new Vue({
  el: '#app',
  store,
  template: '<App/>',
  components: { App }
})
複製代碼

打包

打包後咱們發現vendor體積大大減少,由於庫代碼都用cdn加載了。可是這樣會致使請求資源增多也有響應的代價,這僅可算是一個思路。 首屏問題的最終解決方案仍是SSR

總結

至此 vue-cli中的打包配置,也有一些瞭解了。我的吐槽下webpack是真的複雜。觀望和期待 parcel能來帶不同的體驗。

相關文章
相關標籤/搜索