1.webpack核心概念
entry: 一個(gè)可執(zhí)行模塊或庫的入口文件。
chunk :多個(gè)文件組成的一個(gè)代碼塊终蒂,例如把一個(gè)可執(zhí)行模塊和它所有依賴的模塊組合和一個(gè) chunk 這體現(xiàn)了webpack的打包機(jī)制雹顺。
loader :文件轉(zhuǎn)換器,例如把es6轉(zhuǎn)換為es5霎苗,scss轉(zhuǎn)換為css白热。
plugin :插件庸诱,用于擴(kuò)展webpack的功能,在webpack構(gòu)建生命周期的節(jié)點(diǎn)上加入擴(kuò)展hook為webpack加入功能晤揣。
2.webpack構(gòu)建流程
從啟動(dòng)webpack構(gòu)建到輸出結(jié)果經(jīng)歷了一系列過程,它們是:
2.1 解析webpack配置參數(shù)朱灿,合并從shell傳入和webpack.config.js文件里配置的參數(shù)昧识,生產(chǎn)最后的配置結(jié)果。
2.2 注冊(cè)所有配置的插件盗扒,好讓插件監(jiān)聽webpack構(gòu)建生命周期的事件節(jié)點(diǎn)跪楞,以做出對(duì)應(yīng)的反應(yīng)。
2.3 從配置的entry入口文件開始解析文件構(gòu)建AST語法樹侣灶,找出每個(gè)文件所依賴的文件甸祭,遞歸下去。
2.4 在解析文件遞歸的過程中根據(jù)文件類型和loader配置找出合適的loader用來對(duì)文件進(jìn)行轉(zhuǎn)換褥影。
2.5 遞歸完后得到每個(gè)文件的最終結(jié)果池户,根據(jù)entry配置生成代碼塊chunk。
2.6 輸出所有chunk到文件系統(tǒng)凡怎。
3 概括
webpack是一個(gè)打包模塊化js的工具校焦,可以通過loader轉(zhuǎn)換文件,通過plugin擴(kuò)展功能统倒。
4.Webpack的Code Splitting實(shí)現(xiàn)按需加載
4.1. 什么是Code Splitting?**
在最開始使用Webpack的時(shí)候, 都是將所有的js文件全部打包到一個(gè)build.js
文件中(文件名取決與在webpack.config.js
文件中output.filename
), 但是在大型項(xiàng)目中, build.js
可能過大, 導(dǎo)致頁面加載時(shí)間過長(zhǎng). 這個(gè)時(shí)候就需要code splitting
, code splitting
就是將文件分割成塊(chunk)
, 我們可以定義一些分割點(diǎn)(split point)
, 根據(jù)這些分割點(diǎn)對(duì)文件進(jìn)行分塊, 并實(shí)現(xiàn)按需加載.
4.2 Code Splitting的作用?**
-
第三方類庫單獨(dú)打包:
由于第三方類庫的內(nèi)容基本不會(huì)改變, 可以將其與業(yè)務(wù)代碼分離出來, 這樣就可以最大化的利用瀏覽器的緩存機(jī)制, 減少請(qǐng)求. -
按需加載:
Webpack支持定義分割點(diǎn), 通過require.ensure
進(jìn)行按需加載.
4.3 如何進(jìn)行Code Splitting?**
下面的代碼是基于vue-cli
的webpack-simple
模板生成的演示文檔
//cmd
vue init webpack-simple code_spliting_demo
(一) 第三方類庫單獨(dú)打包
我們假設(shè)項(xiàng)目中引入了jquery.js
和respond.js
, 那么我們可以在webpack.config.js
中配置多入口來進(jìn)行將這兩個(gè)第三方類庫單獨(dú)打包.
-
在
webpack.config.js
進(jìn)行配置//webpack.config.js //在entry中添加相應(yīng)第三方類庫 entry: { bundle: './src/main.js', vendor: ['./src/lib/jquery-1.10.2.min.js', './src/lib/respond.min.js'] } //在plugins中添加CommonChunkPlugin plugins:[ new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', filename: 'vendor.bundle.js' }) ]
-
執(zhí)行
npm run build
, 此時(shí)dist
目錄下生成了兩個(gè)文件, 分別是build.js
和vendor.bundle.js
npm run build后的生成文件 -
在
index.html
中引入, 注意:vendor.bundle.js
優(yōu)先于build.js
引入//index.html <script src="/dist/vendor.bundle.js"></script> <script src="/dist/build.js"></script>
(二) 按需加載
我們可以在router
中進(jìn)行配置, 實(shí)現(xiàn)組件的按需加載, 在一些單個(gè)組件文件較大的時(shí)候, 采用按需加載能夠減少build.js
的體積, 優(yōu)化加載速度(如果組件的體積較小, 那么采用按需加載會(huì)增加額外的http請(qǐng)求
, 反倒增加了加載時(shí)間)
-
這里, 我們?cè)黾?個(gè)組件,分別是
A.vue
,B.vue
,C.vue
//A.vue <template> <h1>這里是A.vue組件</h1> </template> //B.vue <template> <h1>這里是B.vue組件</h1> </template> //C.vue <template> <h1>這里是C.vue組件</h1> </template>
-
在路由中進(jìn)行配置 (注意:這里是為了方便, 是在
app.js
中添加的路由, 在實(shí)際的項(xiàng)目中, 路由應(yīng)該單獨(dú)抽取出來)//app.js import Vue from 'vue' import App from './App.vue' import VueRouter from 'vue-router' Vue.use(VueRouter) //AMD規(guī)范的異步載入 const ComA = resolve => require(['./components/A.vue' ], resolve); const ComB = resolve => require(['./components/B.vue' ], resolve); const ComC = resolve => require(['./components/C.vue' ], resolve); const router = new VueRouter({ routes: [ { name: 'component-A', path: '/a', component: ComA }, { name: 'component-B', path: '/b', component: ComB }, { name: 'component-C', path: '/c', component: ComC } ] }) new Vue({ el: '#app', router: router, render: h => h(App) })
在
webpack.config.js
中進(jìn)行配置output.chunkFilename
,
//webpack.config.js
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: 'build.js',
//添加chundkFilename
chunkFilename: '[name].[chunkhash:5].chunk.js'
}
-
執(zhí)行
npm run build
, 此時(shí)dist
目錄下生成了5個(gè)文件, 多出的3個(gè)文件,就是對(duì)應(yīng)的A.vue
,B.vue
,C.vue
這三個(gè)組件npm run build后生成的文件
CMD規(guī)范的異步載入
剛才在路由引入的時(shí)候, 使用的是AMD規(guī)范
的異步載入. webpack
提供了require.ensure()
這個(gè)方法實(shí)現(xiàn)CMD規(guī)范
的異步載入. 這同樣也是webpack
推薦的載入方式.想深入了解ensure
, 請(qǐng)點(diǎn)擊《webpack代碼分離 ensure 看了還不懂寨典,你打我》
- 下面的代碼是使用
require.ensure()
方法對(duì)路由進(jìn)行配置
//app.js
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
//AMD風(fēng)格的異步加載
// const ComA = resolve => require(['./components/A.vue' ], resolve);
// const ComB = resolve => require(['./components/B.vue' ], resolve);
// const ComC = resolve => require(['./components/C.vue' ], resolve);
//CMD風(fēng)格的異步加載
const ComA = resolve => require.ensure([], () => resolve(require('./components/A.vue')));
const ComB = resolve => require.ensure([], () => resolve(require('./components/B.vue')));
const ComC = resolve => require.ensure([], () => resolve(require('./components/C.vue')));
const router = new VueRouter({
routes: [
{
name: 'component-A',
path: '/a',
component: ComA
},
{
name: 'component-B',
path: '/b',
component: ComB
},
{
name: 'component-C',
path: '/c',
component: ComC
}
]
})
new Vue({
el: '#app',
router: router,
render: h => h(App)
})
-
執(zhí)行
npm run build
后,dist
目錄下同樣生成5個(gè)文件npm run build后生成的文件
webpack Tree Shaking
隨著縮小和樹搖動(dòng),我們的捆綁現(xiàn)在變小了幾個(gè)字節(jié)房匆!雖然在這個(gè)人為的例子中看起來似乎并不多耸成,但是當(dāng)處理具有復(fù)雜依賴樹的較大應(yīng)用程序時(shí),樹抖動(dòng)會(huì)導(dǎo)致束大小顯著減少浴鸿。清除無用代碼井氢,減少文件體積。
webpack scope hoisting 范圍提升
為了檢測(cè)這些導(dǎo)入鏈可以在哪里展平并轉(zhuǎn)換為一個(gè)內(nèi)聯(lián)函數(shù)赚楚,而不會(huì)影響我們的代碼毙沾。我們不僅保存了額外的函數(shù)調(diào)用,還訪問了模塊數(shù)組宠页,因此我們的代碼運(yùn)行速度比以前更快左胞。