因爲使用了React直出,頁面各項性能指標令人悅目。本篇將深刻探討目前PC部落所採用webpack打包優化策略,以及探討PC部落並未使用的 webpack Code Splitting 代碼分包、異步模塊加載特性。看看它們又是如何對PC部落的性能起到進一步的催化做用。javascript
若是你曾經使用過 Broserify, RequireJS 或相似的打包工具,並注重:代碼分包、異步加載、靜態資源打包(圖片/CSS)。那麼 webpack 就是幫你構建項目的利器!簡單一句話:在webpack中,全部資源都被看成是模塊,js能夠引用 css , css 中能夠嵌入圖片 dataUrl。php
對應不一樣文件類型的資源,webpack有對應的模塊 loader ,好比對於 less, 使用的是 less-loader
,你能夠在這裏找到 全部loader. webpack 具備requireJS 和 browserify 的功能,但仍有本身的新特性: 一、對 CommonJS、AMD、ES6的語法作了兼容; 二、對js、css、圖片等資源文件都支持打包; 三、串聯式模塊加載器以及插件機制讓其具備更好的靈活性和拓展性,例如對 coffeeScript、ES6的支持; 四、有獨立的配置文件 webpack.config.js; 五、能夠將代碼切割成不一樣 chunk,實現按需加載,下降了初始化時間; 六、支持 SourceUrls 和 SourceMaps,易於調試; 七、具備強大的 Plugin 接口,大可能是內部插件,使用起來比較靈活; 八、webpack 使用異步 IO 並具備多級緩存,使得 webpack 在增量編譯上更快!css
自React誕生以來,耳熟能詳的是 React+webpack 開發大法,並且在大多數 React 網絡教程中也不多說起同時採用了 Grunt 聯合構建項目。html
Grunt 能夠對整個項目文件作複製、刪除、合併、壓縮等等。而Webpack 的優點在於對靜態文件(js/jsx/coffeeScript/css/less/sass/iamges)按不一樣模塊加載(包括按需加載)——這正是咱們對webpack感興趣的地方,各個模塊組建化(能夠將一個組建的圖片、樣式、腳本、頁面放在同一個文件夾中)。因此,在項目中兩者分工不一樣,各司其職。java
注:使用 gulp 替換 grunt 固然也是沒有問題。node
webpack 有多種配置方式,因爲PC部落中靜態資源文件較多,使用配置文件進行打包會方便不少。react
一般狀況下,若是咱們只使用 webpack 構建項目,那麼配置 webpack.config.js 便可。因爲在PC部落中使用了 grunt,並在 grunt 組合任務中調用 webpack 任務,所以須要在 grunt 的任務配置中添加 webpack.js(使用了load-grunt-config
插件) 進行配置。android
var taskConfig = { dev: { entry: { // 入口文件,考慮到多頁面資源緩存,咱們打成多個包 "index": path.resolve(config.srcPath, "pages/index/index.jsx"), "detail": path.resolve(config.srcPath, "pages/detail/detail.jsx"), ... }, resolve: { // 請求重定向,顯示指出依賴查找路徑 alias: { img: path.resolve(config.srcPath + 'img'), comps: path.resolve(config.srcPath + 'pages/components') ... } }, output: { // 輸出文件 path: config.devPath + '/js', // 文件絕對路徑 filename: "[name].min.js", // 輸出文件名 publicPath: "http://s.url.cn/qqun/xiaoqu/buluo/p/js/", // 公共訪問路徑,替換CDN chunkFilename: "[name].chunk.min.js" // 異步加載時須要被打包的文件名 }, module: { // 各種文件 loader noParse: [], // 忽略解析的文件 preLoaders: [{ // 預加載的模塊 test: /\.jsx$/, exclude: /node_modules/, loader: 'jsxhint-loader' }], loaders: [{ // 各式加載器 test: /\.jsx$/, loader: 'jsx-loader', include: path.resolve(config.srcPath) }, { test: /\.less$/, // 使用「!」鏈式loader,從右向左依次執行 loader: ExtractTextPlugin.extract("style-loader", "css-loader!less-loader"), include: path.resolve(config.srcPath) }, { test: /\.(jpe?g|png|gif|svg)$/i, // inline base64url for <=1500 images loader: 'url-loader?limit=1500&name=images/[name].[hash].[ext]', include: path.resolve(config.srcPath) }] }, externals: { // 指定採用外部 CDN 依賴的資源,不被webpack打包 "react": "React", "react-dom": "ReactDOM" }, plugins: [ ... // 公共模塊獨立打包配置 new CommonsChunkPlugin("common", "common.min.js", ["index", "detail", "barindex", "search"]), // 獨立打包css文件之外鍊形式加載 new ExtractTextPlugin("../css/[name].min.css") ], watch: true, keepalive: true, lessLoader: { lessPlugins: [ new LessPluginAutoPrefix() ] } },
resolve.alias
是webpack 的一個配置項,它的做用是把用戶的一個請求重定向到另外一個路徑。 好比:webpack
resolve: { // 顯示指出依賴查找路徑 alias: { comps: 'src/pages/components' } }
這樣咱們在要打包的腳本中的使用 require('comps/Loading.jsx');
其實就等價於require('src/pages/components/Loading.jsx')
。這猶如《高性能javascript》中給查詢壓力較大的對象給了一個別名,經過使用別名能夠將本例減小几乎一半的時間。ios
module.noParse
,若是你肯定一個模塊中沒有其它新的依賴,就能夠配置這項,webpack 將再也不掃描這個文件中的依賴。 好比咱們在入口文件 entry.js
中檢測到對資源src/pages/components/ueditor.min.js
資源的請求,若是咱們配置:
module: { noParse: [/ueditor/] }
noParse
規則中的/ueditor/
一條生效,因此 webpack 直接把依賴打包進了 entry.js
。增長這樣的配置會讓 webpack 編譯時間更短。
考慮到web上有不少的公用 CDN 服務,那麼咱們能夠將 react
從 bundle 中分離出來,進而不會被 webpack 打包, 做爲外部依賴引用 CDN 。 方法是使用 externals
聲明一個外部依賴。 如:
module:{ externals: { // 方式一:申明爲外部依賴並指定別名 "react": "React", "react-dom": "ReactDOM" // 方式二:true 爲外部依賴,false 則不是 a: false, // a is not external b: true // b is external }, }
並在 HTML 代碼中加上一行
<script src="//cdn.bootcss.com/react/0.14.2/react.js"> <script src="//cdn.bootcss.com/react/0.14.2/react-dom.js">
這樣咱們在js中引入React = require('react')
, webpack 就不會把 react 打包進來而直接引用CDN,這樣作可讓 webpack 編譯時間縮減一大半!
開發中須要將多個頁面的公用模塊獨立打包,從而能夠利用瀏覽器緩存機制來提升頁面加載效率,減小頁面初次加載時間,只有當某功能被用到時纔去動態加載。這就要使用到 webpack 中的 CommonsChunkPlugin 插件。
var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin"); module.exports = { ... /* * @param 1 將公共模塊提取,生成名爲 common 的chunk * @param 2 最終生成的公共模塊的 js 文件名 * @param 3 公共模塊提取的資源列表 */ new CommonsChunkPlugin("common", "common.min.js", ["index", "detail", "barindex", "search"]) }
webpack 中編寫js文件時,能夠經過 require
的方式引入其餘靜態資源,可經過loader對文件自動解析並打包文件。 一般咱們會將 js 文件打包合併,css 文件在頁面header中嵌入 style 的方式載入頁面。但在開發過程當中咱們並不想將樣式打包在腳本中(最好能夠獨立生成css文件,之外鍊形式加載)。 ExtractTextPlugin 插件能夠幫咱們達到這樣的效果。
npm install extract-text-webpack-plugin –save-dev
var ExtractTextPlugin = require("extract-text-webpack-plugin"); module.exports = { ... plugins: [ new ExtractTextPlugin("../css/[name].min.css") ] }
這樣配置就能夠將 js 中的 css 文件提取,並以指定的文件名來進行加載。
顧名思義,就是autoPrefix插件,用來補全CSS的廠商前綴(-webkit-, -moz-, -o-);
var LessPluginAutoPrefix = require('less-plugin-autoprefix'); var taskConfig = { dev: { ... lessLoader: { lessPlugins: [ new LessPluginAutoPrefix() ] } }
對於一個大型的web app,咱們把全部的 js 文件合成一個顯然是很是低效的,由於有些 js 模塊並非咱們當前頁面所須要的(這會大大增長頁面首屏渲染時間)。Webpack 就是這樣一種神器,爲您提供優質的代碼分包服務,今後「媽媽不再用擔憂頁面按需加載的問題了」!
require(dependencies, callback)
聽從 AMD 規範定義的異步方法。使用該方法時,全部的依賴被異步加載並從左至右當即執行,依賴都被執行後,執行callback
。
require.ensure(dependencies, callback)
聽從 CommonJS 規範,在須要的時候才下載依賴的模塊。當全部的依賴都被加載完畢,便執行 callback
(注:require做爲callback的參數)。 細心的同窗可能還記得 output
配置中有
output: { ... chunkFilename: "[name].chunk.min.js" }
chunk
究竟是什麼? chunk
又是怎麼生成的呢? 爲了實現部分資源的異步加載,有些資源是不打包到入口文件裏面的。因而咱們使用 require.ensure
做爲代碼分割的標識。require.ensure
會建立一個 chunk ,且能夠指定該 chunk 的名稱(注:若是這個chunk已經存在了,則將本次依賴的模塊合併到已經存在的chunk中),最後這個 chunk 在 webpack 構建時會單獨生成一個文件。 好比咱們要根據當前運行平臺,加載兩個不一樣的UI組建,那麼:
var platform = Util.getPlatform(); if( platform === "ios"){ require.ensure(['./components/dialog'], function(require){ ... }, 'popup'); // 最後一個參數是 chunk 名 } if( platform === "android"){ require.ensure(["./components/toast"], function(require){ ... }, 'popup'); }
經過webpack打包以後,會生成一個 popup.chunk.min.js
文件。在不一樣的運行平臺上,咱們會發現 popup.chuck.min.js 文件的內容是相同的(由於咱們配置的 chunk 名都是 popup)。 若是咱們想讓按需加載的模塊再次拆分紅 dialog 和 toast,兩個文件,僅僅須要將 require.ensure
中配置的chunk 名改不一樣,便可在代碼被執行時加載單一文件。
一、require
:加載模塊,並當即執行; 二、require.ensure
:僅僅加載模塊,但不會執行; 三、不用在 html 中顯示調用生成的 chunk 文件,按需加載時會自動調用; 四、不用擔憂第三方庫被反覆打包的問題,由於咱們已經使用 CommonsChunkPlugin
對公共部分進行提取。