標籤:

bb學習 Vue 你需要知道的 webpack 知識(三)

在使用 vue-cli 的 webpack 項目模板創建的項目中,Vue 的非同步組件是一個很有用的特性,可以幫助減少初始載入文件的體積,從而提升首屏載入的速度。

有人在使用 Vue 非同步組件時,提出這樣兩個問題,⑴在設置了使用 ExtractTextPlugin 的打包配置中,其他 Vue 組件中的 css 內容都被提取出來生成了一個單獨的文件,為什麼非同步組件的內容還在打包生成的 [chunckId].[hash].js 文件中,⑵有沒有辦法把非同步組件里的 css 提取到初始打包生成的 app.[contenthash].css 中去。

這要從這個項目 webpack 打包過程和結果來解釋。

首先看打包過程,通過研讀項目的 webpack 配置,可以知道,在項目模板默認配置中,webpack 打包時會將各種 css 代碼提取出來並保存為一個單獨的 app.[contenthash].css 文件,相關的配置內容如下:

utils.js 文件中使用 ExtractTextPlugin.extract 對相應的載入器進行提取文本內容設置

wepback 生產配置中設置 extract 為 true 調用 utils 中的函數

webpack 生產配置中使用 ExtractTextPlugin 插件生成單獨的 css 文件

css 提取出來之後,根據 webpack 的配置,還會將生成的 css 文件路徑信息插入到 html 文件中,這是通過使用 HtmlWebpackPlugin 來實現的,這部分配置內容如下:

webpack 生產配置中生成打包目錄下 html 文件的配置

在打包過程中,webpack 知道 css 提取出來的文件的存在,因此可以生成 link 標籤插入到打包的 index.html 中,從而在頁面載入時 css 可以成功載入到瀏覽器。

再來看打包結果,對於使用了一個非同步組件的項目打包結果大致如下:

打包結果目錄結構

其中 manifest.[chunkhash].js 文件中包含了 webpack 的 bootstrap 代碼,webpack 的核心思想是「一切皆模塊」,因此生成的其他 js 文件中均包含了若干個 js 模塊,webpack 的 bootstrap 代碼中包含用於模塊載入函數,項目初始化時,該模塊載入函數(從入口模塊開始)載入 vender.[chunkhash].js 文件和 app.[chunkhash].js 文件中的模塊,從而啟動項目。

如果打包生成的文件中包含非同步組件 chunk,在需要載入非同步組件時,模塊載入函數採用 jsonp 的方式來載入其中的模塊,在這個項目中,在需要時載入 0.[chunkhash].js 文件中的模塊。

包含非同步組件的 chunk 頭部

從 0.[chunkhash].js 文件的頭部可以看到整個文件內容是一個 jsonp 函數調用(vender 和 app 文件的內容同樣也是 jsonp 函數調用)。

從該文件中還可以看到,css 相關的內容被打包成了一個導出字元串的模塊:

chunk 中與 css 相關的模塊

webpack 的「一切皆模塊」核心思想中的「模塊」指的是 js 模塊,因此在非同步載入組件的時候,webpack bootstrap 引導的模塊載入函數不能載入 css 文件,這就回答了第一個問題,如果單獨提取非同步組件中的 css 內容並保存為一個 css 文件,則不但有違 webpack 只處理 js 模塊的思想,webpack 還要單獨實現一個非同步載入 css 文件的方法。

此外,還可以進一步了解一下 ExtractTextPlugin 的作用機制,ExtractTextPlugin 提取出 css 後,對應的模塊變成一個空模塊,在使用 ExtractTextPlugin 處理過的生成文件中沒有留下任何 css 文件的信息,webpack 模塊載入函數也不知道該文件的存在,從而也不可能知道如何單獨載入這個 css 文件。

簡言之,webpack 打包過程中,webpack 知道所生成的 css 文件相關信息並能將相應的 link 標籤正確插入到所生成的 html 文件中,非同步組件的載入是運行階段,這時候 webpack 的運行時代碼只能載入包含 js 模塊的 js 文件,不能載入 css 文件,即使能夠載入 css 文件,也無從得知相關 css 文件信息,因此非同步載入的組件不應該提取組件文件中的 css 內容生成單獨的文件,事實上 webpack 及 ExtractTextPlugin 確實將 css 內容留在了生成的 chunk 中。

關於第二個問題,可以將相關的 css 寫到一個單獨的 async-component.css 文件中,然後使用 <style src=async-component.css> 放到 app.vue 里(注意不要加 scoped),這樣就會在 app.[contenthash].css 中包含這部分 css 了。單獨寫一個 .css 文件便於代碼維護。

為了防止 css 類名衝突,可以在 css 的每個選擇子前面加一個 「[v-async-component] 」(含空格)屬性選擇子前綴,然後將 test.vue 的根元素寫成 <div v-async-component></div>。使用 less 或者 sass 統一加前綴可能更為簡潔。

[v-async-component] {n .red-text { color: red; }n .green-textt { color: green; }n /* other styles */n}n

推薦閱讀:

webpack之loader和plugin簡介
Start React with Webpack

TAG:Vuejs | webpack |