[置顶] React-router-v4 - Webpack 实现按需加载(code-splitting)
2018-01-18 12:20
1026 查看
React-router-v4 - Webpack 实现按需加载(code-splitting)
方法一、结合 bundle-loader
实现按需加载
1. 首先创建一个包装组件 Bundle
一下是 react-router4.0 官方文档中给出的例子import React from 'react'; export default class Bundle extends React.Component { state = { mod: null } componentWillMount() { this.load(this.props); } componentWillReceiveProps(nextProps) { if (nextProps.load !== this.props.load) { this.load(nextProps); } } // load 方法,用于更新 mod 状态 load(props) { // 初始化 this.setState({ mod: null }); /* 调用传入的 load 方法,并传入一个回调函数 这个回调函数接收 在 load 方法内部异步获取到的组件,并将其更新为 mod */ props.load(mod => { this.setState({ mod: mod.default ? mod.default : mod }); }); } render() { /* 将存在状态中的 mod 组件作为参数传递给当前包装组件的'子' */ return this.state.mod ? this.props.children(this.state.mod) : null; } }
Bundle 组件会接受一个名为
load的
props
load 值一是一个组件异步加载的方法
load -> function (cb) {...cb(/* 异步加载的组件 */)}
这个方法需要传入一个回调函数作为参数
回调函数会在在方法内异步接收加载完的组件
2. 创建包装组件的方法
import React from 'react'; import Bundle from './Bundle'; // 默认加载组件,可以直接返回 null const Loading = () => <div>Loading...</div>; /* 包装方法,第一次调用后会返回一个组件(函数式组件) 由于要将其作为路由下的组件,所以需要将 props 传入 */ const lazyLoad = loadComponent => props => ( <Bundle load={loadComponent}> {Comp => (Comp ? <Comp {...props} /> : <Loading />)} </Bundle> ); export default lazyLoad;
结合第一步中
this.props.children(this.state.mod)加载后的组件会被传入
{Comp => (Comp ? <Comp {...props} /> : <Loading />)}中渲染
3. 在 Router 配置中使用
import React from 'react'; import { Route, Switch, Redirect } from 'react-router-dom'; import { hot } from 'react-hot-loader'; // 引入包装函数 import lazyLoad from './lazyLoad'; import App from '../containers/App'; /* 使用 bundle-loader 插件引用组件,返回的实际上是包装后的组件异步加载的函数 */ import Home from 'bundle-loader?lazy&name=home!../containers/Home'; import Test from 'bundle-loader?lazy&name=test!../containers/Test'; const Root = () => ( <div> <Switch> <Route path="/" render={props => ( <App> <Switch> <!-- 包装后作为路由映射的组件 --> <Route path="/" exact component={lazyLoad(Home)} /> <Route path="/home" component={lazyLoad(Home)} /> <Route path="/test" component={lazyLoad(Test)} /> <Route render={() => <Redirect to="/" />} /> </Switch> </App> )} /> <Route render={() => <Redirect to="/" />} /> </Switch> </div> ); export default hot(module)(Root);
方法二、bundle-loader
的另一种使用方式 - 在 webpack 中配置
1. 创建一个包装组件 Bundle
创建的组件与 方法一 中完全一致2. 创建包装组件的函数
函数与 方法一 中完全一致3. webpack 中配置 loader
由于不是所有js文件都是需要包装的组件,因此这里要做一个区分,就是 需要包装的组件要写为
[name].bundle.js
module.exports = { module: { rules: [ { test: /\.bundle\.js$/, loader: 'bundle-loader', include: path.join(__dirname, 'src'), // 源码目录 options: { lazy: true, name: '[name]' } } ] } }
4. Router 配置中使用
import lazyLoad from './lazyLoad'; import App from '../containers/App'; /* 其他配置使用与 方法一 一致 只是引用组件的时候不在需要在路径中配置 bundle-loader 需要按需加载的组件也要文件名变为 [name].bundle.js 的格式 */ // import Home from 'bundle-loader?lazy&name=home!../containers/Home'; // import Test from 'bundle-loader?lazy&name=test!../containers/Test'; import Home from '../containers/Home.bundle'; import Test from '../containers/Test.bundle'; ...
方法三、使用 import()
方法代替 bundle-loader 实现
import('../xxx.js')返回的是一个 promise,因此需要改写 Bundle 组件,此外不在需要 bundle-loader ,其在 webpack 中的配置应该删除
1. 创建一个包装组件 Bundle
import React from 'react'; export default class Bundle extends React.Component { state = { mod: null } componentWillMount() { this.load(this.props); } componentWillReceiveProps(nextProps) { if (nextProps.load !== this.props.load) { this.load(nextProps); } } // 更改 load 方法为异步函数 async load(props) { this.setState({ mod: null }); /* 使用 props.load() 返回的是一个 promise */ const mod = await props.load(); this.setState({ mod: mod.default ? mod.default : mod }); } render() { return this.state.mod ? this.props.children(this.state.mod) : null; } }
2. 创建包装组件的函数
函数与 方法一 中完全一致3. Router 配置中使用
import React from 'react'; import { Route, Switch, Redirect } from 'react-router-dom'; import { hot } from 'react-hot-loader'; import lazyLoad from './lazyLoad'; import App from '../containers/App'; // 动态引入 const Home = lazyLoad(() => import('../containers/Home')); const Test = lazyLoad(() => import('../containers/Test')); const Root = () => ( <div> <Switch> <Route path="/" render={props => ( <App> <Switch> <Route path="/" exact com c8bd ponent={Home} /> <Route path="/home" component={Home} /> <Route path="/test" component={Test} /> <Route render={() => <Redirect to="/" />} /> </Switch> </App> )} /> <Route render={() => <Redirect to="/" />} /> </Switch> </div> ); export default hot(module)(Root);
问题
1. 虽然在配置 webpack 时设置了 output: {chunkFilename: 'chunk/[name].[chunkhash].js'}
但是使用 ‘方法三’ 拆分后的包名还是以数字开始的,如下
Time: 9556ms Asset Size Chunks Chunk Names chunk/0.7429253c8b.js 5.86 kB 0 [emitted] chunk/1.e5cc1c11d3.js 1.32 kB 1 [emitted] static/js/app.f61c516b85.bundle.js 40 kB 2 [emitted] app static/js/vendor.9f5f812985.bundle.js 209 kB 3 [emitted] vendor static/js/runtime.1bbb4b5baf.bundle.js 1.44 kB 4 [emitted] runtime static/css/app.ec6e1795a1.css 2.54 kB 2 [emitted] app index.html 2.16 kB [emitted]
可以使用 webpack 提供的一个特殊的注释语法
/* webpackChunkName: "Home" */来提供 chunk name (需要 Webpack > 2.4),这样生成的模块就能与 bundle-loader 一致的命名
const Home = lazyLoad(() => import(/* webpackChunkName: "Home" */'../containers/Home')); const Test = lazyLoad(() => import(/* webpackChunkName: "Test" */'../containers/Test'));
2. 而使用 bundle-loader 的方法,由于配置了参数 name ,所以打包后会有模块命名如下
Time: 9190ms Asset Size Chunks Chunk Names chunk/Home.4a8c77815d.js 21.6 kB 0 [emitted] Home chunk/Test.283681f60c.js 1.32 kB 1 [emitted] Test static/js/app.3edc8970d4.js 24.2 kB 2 [emitted] app static/js/vendor.9f5f812985.js 209 kB 3 [emitted] vendor static/js/runtime.0f777aeb81.js 1.46 kB 4 [emitted] runtime static/css/app.ec6e1795a1.css 2.54 kB 2 [emitted] app index.html 2.14 kB [emitted]
参考
react-routerCreate React App
相关文章推荐
- react-router4 配合webpack require.ensure 实现异步加载的示例
- react-router4 + webpack Code Splitting
- React-router中结合webpack实现按需加载实例
- [转] react-router4 + webpack Code Splitting
- React router+ webpack实现JS按需加载
- React-router4 使用bundle-loader实现按需加载(code-splitting)
- React-router中,结合webpack实现按需加载
- webpack+react-router实现代码拆分按需加载(下)
- 详解webpack + react + react-router 如何实现懒加载
- webpack+react-router实现代码拆分按需加载(上)
- Webpack + react-router 按需加载
- webpack/webpack+bundle-loader/webpack+redux code splitting(按需加载)
- 基于react16 webpack3 搭建前端spa基础框架 react-router的4种异步加载方式
- 利用 React/Redux/React-Router 4/webpack 开发大型 web 项目时如何按需加载
- webpack2+node+react+babel实现热加载(hmr)
- 基于 Webpack 2 的 React Router 懒加载路由配置
- Webpack懒加载React Router的页面组件
- EasyDSS高性能流媒体服务器前端重构(五)- webpack + vue-router 开发单页面前端实现按需加载
- Webpack2 + Vue2 + Vue-Router2 如何实现懒加载?
- EasyDSS高性能流媒体服务器前端重构(五): webpack + vue-router 开发单页面前端实现按需加载