項目升級為react-router4後,就嘗試著根據官方文檔進行代碼分割。https://reacttraining.com/react-router/web/guides/code-splitting 在實際項目中,js,css文件預設通過webpack打包的話會很大,動不動就好幾兆。 在實際場景... ...
項目升級為react-router4後,就嘗試著根據官方文檔進行代碼分割。https://reacttraining.com/react-router/web/guides/code-splitting
在實際項目中,js,css文件預設通過webpack打包的話會很大,動不動就好幾兆。
在實際場景中,我們需要縮短首屏的時間展現時間,需要將 首屏沒有 涉及到 其他頁面的 業務和組件 進行代碼分離,按需載入。
通過按需載入,如果只是修改了某個頁面的邏輯,也不用整個項目文件載入,增加了瀏覽器緩存的利用
下麵就一步一步的介紹在我的項目中是怎麼實現Code Splitting的。
根據webpack文檔 https://webpack.js.org/guides/code-splitting/ 推薦的代碼分割的方式是 import(), 當然 require.ensure() 作為相容,還是支持的。
而react-router4 文檔上說到的 bundle-loader 組件就是通過 require.ensure 實現的。
實際項目中
a.太小的文件打個包也沒有太大的意義,最終考慮的是 每個一級菜單 作為分割點進行打包載入.
b.能夠通過參數配置 分割打包 還是 整體打包,儘可能的在webconfig中進行配置,可以參數化
c.打包文件名字要有意義,每次打包只是某個module文件修改的話,不會影響其他js文件hash值,提高緩存利用
下麵介紹下最終的結果,其中有些坎坷的心路歷程,只能簡單的略過了
1./router/moduleA.router.jsx,把需要打包在一起的文件整理到對用的文件下,方便統一管理
2.webpack.config.js
output: { //[chunkhash:8] 設置文件摘要,用於緩存 filename: '[name].[chunkhash:8].bundle.js', //entry 對用生成文件的文件名規則 chunkFilename : '[name].[chunkhash:8].js', //不是entry定義的文件(分離打包的模塊文件,提取的共同文件),對用生成文件的文件名規則 path: TARGET, publicPath: '/public/' }, plugins: [ //... //... //** 設置打包id生成規則,以文件地址 + hash 形式,是生成的 webpack 模塊id固定,參見參考文獻 new webpack.HashedModuleIdsPlugin(), //提取的共同文件插件配置 new webpack.optimize.CommonsChunkPlugin({ //在模塊中如果存在的功用的話,也進行提取設置 //如moduleA,moduleB 中都用了編輯器,entry中沒有,則會抽出公用打包在一個 數字.hash.js 中 async: true, minChunks: 2 //有2處使用的js文件就提取 }), //vendor: entry文件中用到的共用文件打包在vendor文件 //** manifest: 增加這個配置,則把一個載入的id信息統一到一個文件,這樣就可以實現每次打包未改的文件生成的hash不變,參見參考文獻 new webpack.optimize.CommonsChunkPlugin({ names: ['vendor', 'manifest'] }), //對應的 chunks 加上 'manifest', 'vendor' new HtmlWebpackPlugin({ filename: `${page.name}.html`, template: `${ROOT_PATH}/template.ejs`, chunks: ['manifest', 'vendor', page.name] } ], module: { rules: [{ test: /\.router\.jsx/, loader: [ //根據文件尾碼.router.jsx 設置規則,主要是name 和 regExp 的實現,這個可以查看bundle-loader源代碼就能發現相關的支持 //現在的邏輯是取文件名,/router/moduleA.router.jsx 則打包成 moduleA.hash.js 'bundle-loader?lazy&name=[1]®Exp=([^\\\\\\/]*)\\.router\\.jsx', 'babel-loader', ], exclude: /node_modules|assets/ }, { test: /\.jsx?$/, loader: 'babel-loader', exclude: /node_modules|assets/ } ] },
bundle-loader name參數,regExp參數的應用是查看的源代碼,一開始想看看是通過什麼實現非同步載入,就看見了相關插件的源代碼
require.ensure(),想要打包的文件打包在一起,只需要下麵代碼中的 chunkNameParam 設置成同一個值就可以了,
實際情況考慮到更好的管理文件,就通過取.router.jsx前面的文件名 進行命名
3.Bundle.jsx
1 import React, { Component } from 'react' 2 import { Route } from 'react-router-dom'; 3 const Loading = function () { 4 return <div></div> 5 } 6 //註意props的傳遞,在組件與Switch,嵌套的時候會有涉及 7 //增加lazyKey屬性,對應moduleA.router.jsx對應的key值 8 class Bundle extends Component { 9 state = { 10 mod: null 11 } 12 componentWillMount() { 13 this.load(this.props) 14 } 15 componentWillReceiveProps(nextProps) { 16 if (nextProps.load !== this.props.load) { 17 this.load(nextProps) 18 } 19 } 20 load(props) { 21 var key = props.lazyKey || 'default'; 22 this.setState({ 23 mod: null 24 }) 25 props.load(mod => { 26 this.setState({ 27 mod: mod[key] ? mod[key] : mod 28 }) 29 }) 30 } 31 render() { 32 return this.state.mod?<this.state.mod {...this.props} />:<Loading/> 33 } 34 } 35 //註意props的傳遞,在組件與Switch,嵌套的時候會有涉及 36 //通過isLazy進行是否分離打包配置,LazyRoute組件不是必要的,各自需求各自處理 37 class LazyRoute extends React.PureComponent{ 38 componentMap = {}; 39 render() { 40 let {menu, ...props} = this.props; 41 let {component, isLazy, componentKey, path} = menu; 42 componentKey = componentKey || 'default'; 43 if (!isLazy) { 44 return (<Route component={component[componentKey] || component} {...props}/>); 45 } else { 46 //通過this.componentMap進行緩存,防止不必要的組件重新載入 47 if (!this.componentMap[path]) { 48 this.componentMap[path] = function(props) { 49 return (<Bundle load={component} lazyKey={componentKey} {...props}></Bundle>) 50 } 51 } 52 return (<Route component={this.componentMap[path]} {...props}/>); 53 } 54 } 55 } 56 export {Bundle as default, LazyRoute}
實際的打包效果,還是可以的,代碼得到了分離。有些地方還可以優化,比如 第3方lib包不怎麼會變的,和 自己寫的組件 進行不同的提取合併。文獻中都有提及。
極致的按需載入 和 非同步載入,對代碼 組件瞭解的要求比較高,現在主要還是通過webpack 公共提取 做基本的優化
參考文件
http://geek.csdn.net/news/detail/135599 使用 Webpack 打包單頁應用的正確姿勢
https://sebastianblade.com/using-webpack-to-achieve-long-term-cache/ 用 webpack 實現持久化緩存