首先,咱們要知道什麼要用webpack來打包,這樣打包有那些好處。咱們能夠簡單的列出如下幾點:javascript
這篇文章的重點講的就是webpack打包之優化瀏覽器緩存管理,vue-cli生成的腳手架的配置中,已經作了不少對於打包,利用緩存的優化處理,本文未來學校其中知識,而且作出改動。css
在此以前呢,咱們須要先了解瀏覽器緩存是怎麼工做的,先抄了一張圖。html
過了一個星期,再次訪問這個頁面vue
應產品經理需求更改了一個圖標java
弄清了原理,咱們就知道怎麼去破壞緩存機制,讓瀏覽器請求到新的文件。node
手動強制刷新頁面,可是用戶不是程序員啊,他們怎麼會知道須要強制刷新呢,因此這個方案給用戶確定是不可行的。webpack
咱們瞭解完了如何清楚緩存,再來看看Vue-cli模版中是如何進行配置的ios
咱們直接生成一個Vue-cli的新項目,安裝依賴後直接運行 npm run build命令,並打開/dist/js文件目錄程序員
發現有3個js文件,這就是webpack將代碼進行了分割。web
之因此把業務代碼和第三方庫代碼分離出來,是由於產品經理的需求是源源不斷的,所以業務代碼更新頻率大,相反第三方庫代碼更新迭代相對較慢且能夠鎖版本,因此能夠充分利用瀏覽器的緩存來加載這些第三方庫。
而按需加載的適用場景,好比說「訪問某個路由的時候再去加載對應的組件」,用戶不必定會訪問全部的路由,因此不必把全部路由對應的組件都先在開始的加載完;更典型的例子是「某些用戶他們的權限只能訪問某些頁面」,因此不必把他們沒權限訪問的頁面的代碼也加載
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,在後面咱們講進行解讀。
若是咱們修改一下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 的文件名確定都會變。但實際上,我只改動了某個模塊的代碼(其餘模塊並無修改),就破壞了其餘模塊的緩存,這顯然沒有充分利用到緩存啊。咱們的目標是:
哪一個模塊更新了破壞他的緩存,沒更新的模塊繼續利用緩存。
上文中咱們提到清楚緩存的三種方式:修改文件名,修改路徑,給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值就會改變
爲何要提取manifast文件呢?
緣由是 vendor chunk 裏面包含了 webpack 的 runtime 代碼(用來解析和加載模塊之類的運行時代碼)
這樣會致使:即便你沒有更改引入模塊(vendor的模塊沒有發生變更的狀況下,你僅僅修改了其餘代碼) 也會致使 vendor
的chunkhash值發生變化,從而破壞了緩存,達不到預期效果
vue-cli /webpack.prod.conf.js
提取 manifast
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity
}),
複製代碼
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()
複製代碼
至此若是咱們改了某個模塊的代碼,是不會破壞其餘模塊的緩存,這就是咱們想要實現的持久性緩存。
咱們首先來看一個實際項目
運行 npm run build --report 能夠查看打包分佈圖 咱們發現最大的文件仍是vendor,大部分框架代碼都打包在這裏面,而這些框架代碼是不常變化的,也不須要每次進行打包。因此咱們能夠想辦法把他們提取出來,掛到cdn上面去。
以 vue, vue-router,element-ui爲例
<!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>
複製代碼
module.exports = {
...
externals: {
'vue': 'Vue',
'vue-router': 'VueRouter',
'element-ui': 'ELEMENT'
},
...
}
複製代碼
修改 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能來帶不同的體驗。