webpack生成html檔案,用於後端渲染的研究
不適用後端渲染的原因
webpack的打包方式是把所有的資源都打包成bundle.js,並用一個沒有內容的html引入生成的bundle.js,不太熟悉的同學可以參看慕課網的視訊教程。但是如果公司的建站方式是後端渲染的話(如jsp),那就不能使用webpack了,因為webpack會把html也打包在bundle.js中。本文就是介紹如何用webpack生成我們需要的html,以及其中的問題和優化。
主要思路
webpack的html-webpack-plugin外掛,可以設定一個template,我們可以在這個template上做文章,配合上相應的loader,就可以生成我們需要的html。
專案結構
下面是我的webpack目錄結構
HTML部分
如何生成我們需要的html檔案呢?
html-webpack-plugin的使用
我們採用曲線救國的方式生成我們需要的html,用於後端渲染。這就要使用到html-webpack-plugin的template屬性。
module.exports = {
entry: 'index.js',
output: {
path: __dirname + '/dist',
filename: 'index_bundle.js'
},
plugins: [
new HtmlWebpackPlugin(), // 生成一個空的html,用於引入webpack打包好的js檔案
new HtmlWebpackPlugin({ // 再生成一個html
filename: 'test.html',
template: 'src/assets/test.html' //注意這裡可以使用一個template作為要生成的html的模板
})
]
}
上面程式碼中的template這裡是一個html,官方的介紹是,你可以使用jade或ejs等模板引擎,也就是說webpack不關心你使用什麼作為模板,只要輸出一串字串就行,於是我大膽的使用一個js檔案作為模板,輸出一段字串,結果完全可行。
這樣就產生了很多種方案,比如用js檔案作為模板,在這個js檔案中require其他的html檔案,進行字串拼接,最後輸出,但是這樣的話,每個需要輸出的頁面都需要配置也個這樣的js檔案,懶惰的我不允許這種事情的發生…..
再比如,直接使用一個html作為模板,配合使用html-loader,這個loader有一種引入其他檔案的語法支援:
<div> ${require('./components/gallery.html')} </div>
這樣我們就能引入其他的檔案模組…..
但是有的時候我們需要一些模板語法,例如我們需要迴圈生成li
標籤,最後我採用的方案是使用 ejs
作為模板檔案,採用 underscore-template-loader
作為我的loader,而沒有采用 ejs-loader
,因為 ejs-loader
不會處理檔案html結構中的圖片路徑問題,而且 ejs-loader
也沒有require其他ejs檔案的語法支援,雖然 ejs-loader
官方推薦使用 ejs-compiled-loader
用它來引入其他的ejs模組,但是這樣顯得很麻煩,而且圖片路徑問題還是沒有解決。然後我就找到了神器 underscore-template-loader
,首先圖片路徑問題loader會幫你解決,其次該loader支援兩種require其他檔案的語法:
<div class="top-section">
@require('header.ejs', {"title": "First Section"})
</div>
這裡引入一個ejs檔案,並向其中傳入對應的值。(如果你看不懂上面的程式碼,可以先熟悉一下ejs語法)這樣就能很輕鬆的引入一個component檔案,並傳入值,此外,如果你只想引入一段不帶語法的html結構(即純字串),也可以採用下面的寫法
<div class="wiki">
<h3>Introduction</h3>
@include('intro.htm')
<h3>Authors</h3>
@include('authors.htm')
</div>
include只會將檔案轉換成字串,並引入,所以確保你要引入的檔案沒有被loader處理過,不然很可能引入的是一個函式,而不是一串html結構。我在layout目錄下設定不同的資料夾,一個資料夾代表一個頁面,其中的ejs檔案作為html-webpack-plugin的template,用這個ejs檔案去require其他的component,如下圖:
到這裡我,我們基本都已經解決了html結構的部分,那css應該寫在哪裡呢?另外我不想把css也打包在bundle.js中,我想要生成單獨的css檔案,怎麼辦?
生成CSS檔案
extract-text-webpack-plugin的使用
下面我們介紹另外一個webpack的外掛:extract-text-wepack-plugin
,這個外掛用於提取出css檔案。
我把一個頁面的css放在layout下(這裡我用的是sass),用這個css去require其他的component的css,如下圖:
這個總的css是有了,可是我們把它放在哪呢?任何資源只有被入口的js檔案require,才能被webpack處理,所以我們當然是用layout下的入口js檔案去require這個css,但是這樣css也就會被打包到bundle.js中,於是我們可以在這裡使用extract-text-wepack-plugin
怎麼用呢?主要是兩步:
首先在rule中,對所有的scss檔案使用extract-text-wepack-plugin
rules: [{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: ["css-loader?importLoaders=2","postcss-loader","sass-loader"]
})
}]
其次在plugin中使用extract-text-wepack-plugin
:
plugin: [new ExtractTextPlugin('[name]/[name].css')]
特別注意,我們還需要在webpack.config.js檔案的頭部引入extract-text-wepack-plugin
這個模組,不然ExtractTextPlugin 就會沒有定義,從而報錯:
const ExtractTextPlugin = require("extract-text-webpack-plugin");
打包一下,在dist檔案下,就能看到我們需要的css檔案了。並且生成好的html也自動引入了這個css檔案。
如何熱更新?
當我們開啟webpack的webpack-dev-server後,發現改動html和css都不能產生熱更新,只有改動js才能熱更新,我們可以在github官方頁面上找到答案,如下圖:
也就是說 extract-text-webpack-plugin
不支援熱更新,於是我們可以這樣來改進它,使用兩種環境,一種是開發環境,一種是生產環境,在開發環境中,我們用入口的js去 require
layout
檔案下的ejs模板,而不再使用html-webpack-plugin生成。也就是說,在開發的時候,所有的資源都是打包在bundle.js檔案中的,只有在生產的時候,才像上面我們說的那樣生成html和css,大概思路就是這樣,那我們可以寫兩份不同的webpack的config檔案,一個是開發另一個用作生產。
但是我只用了一個webpack.config.js,我們可以使用node提供給我們的一個API,來設定一個全域性的值,使用方法如下:
//package.json
"scripts": {
"build": "set NODE_ENV=production&& webpack -p --color"
}
使用set NODE_ENV=production
就可以設定這個全域性的值了,這裡設定的是production,注意: production
和&&
之間不能有空格,不然這個全域性的值就設定成'production '
,production後面多了一個空格,怎麼獲取這個值呢?我們可以在程式的任意位置,通過process.env.NODE_ENV
來拿到這個值,做一個 if
判斷就可以知道是開發環境還是生產環境了。
以css為例,配置如下:
rules: [{
test: /\.scss$/,
use: process.env.NODE_ENV == 'production'
? ExtractTextPlugin.extract({ fallback: "style-loader", use: [
"css-loader?importLoaders=2",
"postcss-loader",
"sass-loader"
]
})
: ['style-loader','css-loader?importLoaders=2',"postcss-loader",'sass-loader']
}]
這樣就可以用一份配置實現不同的需求,當然,我們還可以使用webpack提供的一個外掛:DefinePlugin
把這個值暴露給整個webpack
plugin: [new webpack.DefinePlugin({
'ENV': JSON.stringify(process.env.NODE_ENV)//獲取到NODE_ENV的值,並暴露為全域性變數
})
]
這樣我們在任意的地方都可以直接使用ENV
這個值了。
到這裡就結束了,拋磚引玉說了一下大致的思路,沒有展開談具體細節,我把這個腳手架開源在github上,(https://github.com/cwj0130/webpack-cli),大家可以clone參看具體細節,我也在完善部分功能,例如單元測試等,如果此教程對您有幫助,麻煩在github上給個star,謝謝。
此文為原創,可以任意轉載,但請標明出處。