對於先後端分離,如何把一個頁面的公共部分好比head, header, footer, content等組合成一個完整的html 是一個值得考慮的地方。php
對於php,咱們能夠利用include加載其餘頁面,像yii框架,能夠利用render將輸出的內容嵌入到父模板,從而造成一個完整的頁面。css
那對於純靜態的html咱們如何拼接呢?html
能夠想到市面上的多種模板引擎,好比artTemplate, doT, ejs等,他們能夠使用require或include等特殊標記的語法來引入其餘模塊。但若是每一個頁面咱們都去寫若干個require,好比:vue
require('head.html') require('header.html') ... require('side-bar.html') require('footer.html')
是否是略顯麻煩?另外head內的title如何自定義?對於要求head內根據不一樣頁面有不一樣引用的icon或者css甚至js,該如何配置呢?webpack
這時咱們就想着去尋找一套自動化的拼接和可配置的靈活方案,html-webpack-plugin 就能夠幫咱們完成這些。git
var HtmlWebpackPlugin = require('html-webpack-plugin'); var webpackConfig = { entry: 'index.js', output: { path: 'dist', filename: 'bundle.js' }, plugins: [ new HtmlWebpackPlugin( title: 'this is mytitle a', _html: 'this is content a.', filename: 'index.html', template: 'index.ejs', ) ], };
html-webpack-plugin的基本使用方法如上,它能夠把模板template index.ejs轉化成html,命名爲index.html,並把bundle.js引入index.html。github
html-webpack-plugin默認集成了ejs模板引擎,因此咱們能夠直接使用ejs模板。固然咱們也能夠引入其餘模板,包括handlebars等均可以使用。web
title, _html爲自定義的一些屬性,你還能夠增長好比content, data等等你想要的數據傳到模板。傳到模板後,ejs能夠直接獲取到傳過來的值,獲取方法以下:後端
<!-- index.ejs --> <!doctype html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0"> <title><%= htmlWebpackPlugin.options.title %></title> </head> <body> <header style="text-align:center;color:#fff;font-size:20px;background:#333;"> this is header. </header> <%= htmlWebpackPlugin.options._html %> <section id="app"></section> <footer style="text-align:center;color:#fff;font-size:20px;background:#333;"> this is footer. </footer> <script src="./dist/build.js"></script> </body> </html>
<%= %> 用來引入變量app
<% %> 用來執行js判斷語句
這樣咱們就能夠自定義一些內容輸入到模板頁面中,但咱們若是有不少個模板,如何挨個生成呢?
官網給出的解決方案就是有幾個模板就聲明幾回插件:
plugins: [
new HtmlWebpackPlugin(
title: 'this is mytitle a',
_html: 'this is content a.',
filename: 'index_a.html',
template: 'index_a.ejs',
),
new HtmlWebpackPlugin(
title: 'this is mytitle b',
_html: 'this is content b.',
filename: 'index_b.html',
template: 'index_b.ejs',
),
new HtmlWebpackPlugin(
title: 'this is mytitle c',
_html: 'this is content c.',
filename: 'index_c.html',
template: 'index_c.ejs',
)
],
多個模板的問題解決了,但對於每一個模板內部,如何抽離出公共部分(head等),咱們每次寫頁面只關注內容部分呢?
既然html-webpack-plugin的template能夠接受多個模板,那咱們也能夠傳給它一個js,只要js返回一個模板文件就能夠,這樣咱們拼接的工做均可以用js和ejs完成。
在此以前咱們說下html-webpack-plugin的 chunks 屬性
{ entry: { a: './a.js', b: './b.js', c: './c.js' }, output: { path: 'dist', filename: 'js/[name].js' }, plugins: [ new HtmlWebpackPlugin({ title: 'My App', filename: 'assets/admin.html', chunks: ['a','b'] }) ] }
chunks: 規定須要引入的模塊。在這裏例子中,只有a和b被插入到html中,c並不會被引入。
ok,接下來咱們就能夠針對不一樣的模板指定引入不一樣的js了。
先看下流程(以下示意圖),假如如今要作income.html頁面,咱們只關注income.ejs,它是body中的內容部分,html-webpack-plugin 的 template 爲 html/income.js,它會把咱們的頁面內容 income.ejs 傳給 html/layout.js,在 layout.js 內,咱們會引入html的各個公共部分,並把html/income.js 中定義的各類參數傳給頁面的各個部分,而後把這些公共部分傳給 html/layout.ejs 組合並返回。html-webpack-plugin 就把返回的完整的模板轉化爲 目標html
代碼以下:
/***** 生成組合後的html *****/ var pages = getEntry('./html/src/**/*.ejs') for (var pathname in pages) { var conf = { filename: path.resolve(__dirname, './html/dist/' + pathname + '.html'), // html文件輸出路徑 template: path.resolve(__dirname, './html/src/' + pathname + '/' + pathname + '.js'), // 模板路徑 inject: true, cache: true, //只改動變更的文件 minify: { removeComments: true, collapseWhitespace: false } } if (pathname in module.exports.entry) { conf.chunks = [pathname, 'vendors', 'manifest'] } module.exports.plugins.push(new htmlWebpackPlugin(conf)) }
inject: ture/body 將js引用插入到body內,head將js引用插入到head內,false爲不插入
cache:是否值編譯改動的文件
minify: 壓縮html
removeComments: 去除註釋
collapseWhitespace: 去除空格
chunks: 自動引入公共模塊 js 以及 當前 pathname 對應的 js 文件
template: 爲入口js文件對應的用於拼接模板的js
這個js就有點像php的controller,能夠定義當前頁面的title等信息,並規定使用哪一個ejs模板進行拼接
/* html/income/income.js */
const content = require('./income.ejs') //使用income.ejs模板進行拼接 const layout = require('../layouts/layout.js') const pageTitle = '消息通知' //自定義頁面title並傳給 layoutjs 分發給頁面的公共模塊 module.exports = layout.init(pageTitle).run(content({ pageTitle }))
layout.js則引入各個公共模塊,給他們傳入須要的參數,並返回layout.ejs拼接後的結果
/* html/layout/layout.js */ const layout = require('./layout.ejs') const header = require( './header.ejs') // 頁頭的模板 const footer = require('./footer.ejs') // 頁腳的模板 const topNav = require('./top-nav.ejs') // 頂部欄的模板 const sideMenu = require('./side-menu.ejs') // 側邊欄的模板 /* 整理渲染公共部分所用到的模板變量 */ const pf= { pageTitle: '' } const moduleExports = { /* 處理各個頁面傳入而又須要在公共區域用到的參數 */ init(pageTitle) { pf.pageTitle = pageTitle // 好比說頁面名稱,會在<title>或麪包屑裏用到 //console.log('pf.pageTitle'+pf.pageTitle) return this }, /* 整合各公共組件和頁面實際內容,最後生成完整的HTML文檔 */ run(content) { const renderData = { header: header(), footer: footer(), topNav: topNav(pf), sideMenu: sideMenu(), content: content, } return layout(renderData) }, } module.exports = moduleExports
layout.ejs 爲終極模板,引入各個公共模塊變量
<!-- html/layout/layout.ejs --> <!DOCTYPE html> <html> <head> <title>vue</title> </head> <body> <%= header %> <div id="wrapper"> <%= topNav %> <%= sideMenu %> <%= content %> </div> <%= footer %> </body> </html>
OK,到這裏咱們基本能夠完成功能了,對每一個新頁面咱們只須要關注這個頁面的body部分,以及一個頁面控制器 js 便可。無需在頁面引入 css 和 js ,html-webpack-plugin會自動根據模板命名找到對應的js文件引入到html中,而css就只須要在相應的js文件中引入便可。
先後端分離的html拼接也就完成了。
目錄結構參考:
源碼:https://github.com/saysmy/vue2-webpack2-demo
若有錯誤請指正,有更好的構建方式期待留言交流