webpack原理

1.webpack核心概念

entry: 一個(gè)可執(zhí)行模塊或庫(kù)的入口文件。
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)歷了一系列過(guò)程,它們是:

2.1 解析webpack配置參數(shù),合并從shell傳入和webpack.config.js文件里配置的參數(shù),生產(chǎn)最后的配置結(jié)果。

2.2 注冊(cè)所有配置的插件,好讓插件監(jiān)聽(tīng)webpack構(gòu)建生命周期的事件節(jié)點(diǎn),以做出對(duì)應(yīng)的反應(yīng)。

2.3 從配置的entry入口文件開(kāi)始解析文件構(gòu)建AST語(yǔ)法樹,找出每個(gè)文件所依賴的文件,遞歸下去。

2.4 在解析文件遞歸的過(guò)程中根據(jù)文件類型和loader配置找出合適的loader用來(lái)對(duì)文件進(jìn)行轉(zhuǎn)換。

2.5 遞歸完后得到每個(gè)文件的最終結(jié)果,根據(jù)entry配置生成代碼塊chunk。

2.6 輸出所有chunk到文件系統(tǒng)。

3 概括

webpack是一個(gè)打包模塊化js的工具,可以通過(guò)loader轉(zhuǎn)換文件,通過(guò)plugin擴(kuò)展功能。

4.Webpack的Code Splitting實(shí)現(xiàn)按需加載

4.1. 什么是Code Splitting?**

在最開(kāi)始使用Webpack的時(shí)候, 都是將所有的js文件全部打包到一個(gè)build.js文件中(文件名取決與在webpack.config.js文件中output.filename), 但是在大型項(xiàng)目中, build.js可能過(guò)大, 導(dǎo)致頁(yè)面加載時(shí)間過(guò)長(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的作用?**

  1. 第三方類庫(kù)單獨(dú)打包:
    由于第三方類庫(kù)的內(nèi)容基本不會(huì)改變, 可以將其與業(yè)務(wù)代碼分離出來(lái), 這樣就可以最大化的利用瀏覽器的緩存機(jī)制, 減少請(qǐng)求.
  2. 按需加載:
    Webpack支持定義分割點(diǎn), 通過(guò)require.ensure進(jìn)行按需加載.

4.3 如何進(jìn)行Code Splitting?**

下面的代碼是基于vue-cliwebpack-simple模板生成的演示文檔

//cmd
vue init webpack-simple code_spliting_demo

(一) 第三方類庫(kù)單獨(dú)打包

我們假設(shè)項(xiàng)目中引入了jquery.jsrespond.js, 那么我們可以在webpack.config.js中配置多入口來(lái)進(jìn)行將這兩個(gè)第三方類庫(kù)單獨(dú)打包.

  • webpack.config.js進(jìn)行配置

    //webpack.config.js
    
    //在entry中添加相應(yīng)第三方類庫(kù)
    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.jsvendor.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ú)抽取出來(lái))

    //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è)人為的例子中看起來(lái)似乎并不多,但是當(dāng)處理具有復(fù)雜依賴樹的較大應(yīng)用程序時(shí),樹抖動(dòng)會(huì)導(dǎo)致束大小顯著減少。清除無(wú)用代碼,減少文件體積。

webpack scope hoisting 范圍提升

為了檢測(cè)這些導(dǎo)入鏈可以在哪里展平并轉(zhuǎn)換為一個(gè)內(nèi)聯(lián)函數(shù),而不會(huì)影響我們的代碼。我們不僅保存了額外的函數(shù)調(diào)用,還訪問(wèn)了模塊數(shù)組,因此我們的代碼運(yùn)行速度比以前更快。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 幼兒繪本《小老鼠亞歷山大》內(nèi)容簡(jiǎn)介: 《小老鼠亞歷山大》講的是一只小老鼠的故事。又是影子!冰箱的影子、蔥的影子、長(zhǎng)...
    修修寶媽閱讀 961評(píng)論 0 0
  • 回家過(guò)年,親自帶了九幾天,結(jié)果小家伙就粘上我了,睡覺(jué)必須我陪,反正你在他身邊他就聽(tīng)話,離開(kāi)就鬧。今天第一天開(kāi)工,跟...
    魚塘小八閱讀 189評(píng)論 0 0
  • 今天,結(jié)合上一篇文章的抽獎(jiǎng)小游戲,用canvas來(lái)寫一個(gè)小游戲——刮刮樂(lè)。首先,用canvas做一個(gè)畫布,寬高各為...
    單純的色狼閱讀 1,272評(píng)論 6 16
  • (2) 很小的時(shí)候,她就已經(jīng)失去了完整的家。在還沒(méi)有記憶的時(shí)候,父親就用棍子打斷了他們之間所有的聯(lián)系。對(duì)于父親這個(gè)...
    彩色的肥皂泡閱讀 252評(píng)論 0 0

友情鏈接更多精彩內(nèi)容