3.6 為單頁應(yīng)用生成 HTML
問題一:單頁面常見問題彤避?
一個頁面常常有很多資源要加載
舉例說明
要求如下:
項目采用 ES6 語言加 React 框架焰扳。
給頁面加入Google Analytics,這部分代碼需要內(nèi)嵌進(jìn) HEAD 標(biāo)簽里去。
給頁面加入Disqus用戶評論禁熏,這部分代碼需要異步加載以提升首屏加載速度壤巷。
壓縮和分離 JavaScript 和 CSS 代碼邑彪,提升加載速度。
在開始前先來看看該應(yīng)用最終發(fā)布到線上的代碼:
<html><head><meta charset="UTF-8"><!--注入 Chunk app 依賴的 CSS--><style rel="stylesheet">h1{color:red}</style><!--內(nèi)嵌 google_analytics 中的 JavaScript 代碼--><script>(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*newDate();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');ga('create','UA-XXXXX-Y','auto');ga('send','pageview');</script><!--異步加載 Disqus 評論--><script async=""src="https://dive-into-webpack.disqus.com/embed.js"></script></head><body><div id="app"></div><!--導(dǎo)入 app 依賴的 JS--><script src="app_746f32b2.js"></script><!--Disqus 評論容器--><div id="disqus_thread"></div></body></html>
HTML 應(yīng)該是被壓縮過的胧华,這里為了方便大家閱讀而格式化了 HTML寄症,并且加入了注釋。
構(gòu)建出的目錄結(jié)構(gòu)為:
dist├── app_792b446e.js└── index.html
可以看到部分代碼被內(nèi)嵌進(jìn)了 HTML 的 HEAD 標(biāo)簽中矩动,部分文件的文件名稱被打上根據(jù)文件內(nèi)容算出的 Hash 值有巧,并且加載這些文件的 URL 地址也被正常的注入到了 HTML 中。 如果你還采用手寫index.html文件去完成以上要求悲没,這就會使工作變得復(fù)雜篮迎、易錯,項目難以維護(hù)示姿。 本節(jié)教你如何自動化的生成這個符合要求的index.html甜橱。
問題二:如何解決上述問題?
推薦一個用于方便的解決以上問題的 Webpack 插件web-webpack-plugin
下面具體介紹如何用它來解決上面的問題栈戳。
首先岂傲,修改 Webpack 配置為如下:
const path=require('path');const UglifyJsPlugin=require('webpack/lib/optimize/UglifyJsPlugin');const ExtractTextPlugin=require('extract-text-webpack-plugin');const DefinePlugin=require('webpack/lib/DefinePlugin');const{WebPlugin}=require('web-webpack-plugin');module.exports={entry:{app:'./main.js'// app 的 JavaScript 執(zhí)行入口文件},output:{filename:'[name]_[chunkhash:8].js',// 給輸出的文件名稱加上 Hash 值path:path.resolve(__dirname,'./dist'),},module:{rules:[{test:/\.js$/,use:['babel-loader'],// 排除 node_modules 目錄下的文件,// 該目錄下的文件都是采用的 ES5 語法子檀,沒必要再通過 Babel 去轉(zhuǎn)換exclude:path.resolve(__dirname,'node_modules'),},{test:/\.css$/,// 增加對 CSS 文件的支持// 提取出 Chunk 中的 CSS 代碼到單獨(dú)的文件中use:ExtractTextPlugin.extract({use:['css-loader?minimize']// 壓縮 CSS 代碼}),},]},plugins:[// 使用本文的主角 WebPlugin镊掖,一個 WebPlugin 對應(yīng)一個 HTML 文件newWebPlugin({template:'./template.html',// HTML 模版文件所在的文件路徑filename:'index.html'// 輸出的 HTML 的文件名稱}),newExtractTextPlugin({filename:`[name]_[contenthash:8].css`,// 給輸出的 CSS 文件名稱加上 Hash 值}),newDefinePlugin({// 定義 NODE_ENV 環(huán)境變量為 production,以去除源碼中只有開發(fā)時才需要的部分'process.env':{NODE_ENV:JSON.stringify('production')}}),// 壓縮輸出的 JavaScript 代碼newUglifyJsPlugin({// 最緊湊的輸出beautify:false,// 刪除所有的注釋comments:false,compress:{// 在UglifyJs刪除沒有用到的代碼時不輸出警告warnings:false,// 刪除所有的 `console` 語句褂痰,可以兼容ie瀏覽器drop_console:true,// 內(nèi)嵌定義了但是只用到一次的變量collapse_vars:true,// 提取出出現(xiàn)多次但是沒有定義成變量去引用的靜態(tài)值reduce_vars:true,}}),],};
以上配置中亩进,大多數(shù)都是按照前面已經(jīng)講過的內(nèi)容增加的配置,例如:
增加對 CSS 文件的支持缩歪,提取出 Chunk 中的 CSS 代碼到單獨(dú)的文件中归薛,壓縮 CSS 文件;
定義NODE_ENV環(huán)境變量為production,以去除源碼中只有開發(fā)時才需要的部分苟翻;
給輸出的文件名稱加上 Hash 值韵卤;
壓縮輸出的 JavaScript 代碼。
但最核心的部分在于plugins里的:
newWebPlugin({template:'./template.html',// HTML 模版文件所在的文件路徑filename:'index.html'// 輸出的 HTML 的文件名稱})
其中template: './template.html'所指的模版文件template.html的內(nèi)容是:
<html><head><meta charset="UTF-8"><!--注入 Chunk app 中的 CSS--><link rel="stylesheet"href="app?_inline"><!--注入 google_analytics 中的 JavaScript 代碼--><script src="./google_analytics.js?_inline"></script><!--異步加載 Disqus 評論--><script src="https://dive-into-webpack.disqus.com/embed.js"async></script></head><body><div id="app"></div><!--導(dǎo)入 Chunk app 中的 JS--><script src="app"></script><!--Disqus 評論容器--><div id="disqus_thread"></div></body></html>
該文件描述了哪些資源需要被以何種方式加入到輸出的 HTML 文件中崇猫。
以<link rel="stylesheet" href="app?_inline">為例沈条,按照正常引入 CSS 文件一樣的語法來引入 Webpack 生產(chǎn)的代碼。href屬性中的app?_inline可以分為兩部分,前面的app表示 CSS 代碼來自名叫app的 Chunk 中,后面的_inline表示這些代碼需要被內(nèi)嵌到這個標(biāo)簽所在的位置笔横。
同樣的<script src="./google_analytics.js?_inline"></script>表示 JavaScript 代碼來自相對于當(dāng)前模版文件template.html的本地文件./google_analytics.js嫌拣, 而且文件中的 JavaScript 代碼也需要被內(nèi)嵌到這個標(biāo)簽所在的位置。
也就是說資源鏈接 URL 字符串里問號前面的部分表示資源內(nèi)容來自哪里郁副,后面的 querystring 表示這些資源注入的方式。
除了_inline表示內(nèi)嵌外,還支持以下屬性:
_dist只有在生產(chǎn)環(huán)境下才引入該資源
_dev只有在開發(fā)環(huán)境下才引入該資源
_ie只有IE瀏覽器才需要引入的資源父款,通過[if IE]>resource<![endif]注釋實現(xiàn)
這些屬性之間可以搭配使用,互不沖突瞻凤。例如app?_inline&_dist表示只在生產(chǎn)環(huán)境下才引入該資源憨攒,并且需要內(nèi)嵌到 HTML 里去。