从属于笔者的 ,更多前端思考参阅笔者的 。
Chunks 是 webpack 的基本概念之一,最直观的概念是在多入口配置中,诶个单独的入口会生成单独的 Chunk。而在添加额外的插件配置之后,Webpack 会输出譬如独立的 CSS 包体这样独立的块。Webpack 内置有如三种类型的 Chunk:
bundle-loader 是 Webpack 官方出品的 之一,bundle-loader 可以用来加载异步代码块,基本的用法如下:
- // 当请求某个Bundle时,Webpack会为我们自动加载
- var waitForChunk = require("bundle-loader!./file.js");
- //我们需要等待Chunk加载完成才能获取到文件详情
- waitForChunk(function(file) {
- // use file like is was required with
- // var file = require("./file.js");
- });
- // wraps the require in a require.ensure block
我们同样可以自定义 Chunk 名:
- require("bundle-loader?lazy&name=my-chunk!./file.js");
我们可以很方便地利用 bundle-loader 实现 React Router 中模块的懒加载,譬如如果我们的路由设置如下:
- import HomePage from "./pages/HomePage";
- import AdminPage from "./pages/admin/AdminPage";
- import AdminPageSettings from "./pages/admin/AdminPageSettings";
- export
- default
- function routes(fromServer) {
- return ( < Router history = {
- browserHistory
- } > <Route path = "/"component = {
- HomePage
- }
- />
- <Route path="/admin " component={AdminPage}/>
- <Route path=" / admin / settings " component={AdminSettingsPage}/>
- <Router/>
- )
- }"
其中 AdminPage 可能非常笨重,我们希望只有当用户真实请求到
这个地址时才会加载相关组件,此时我们就可以在 Webpack 配置中添加 bundle-loader 的支持:
- /admin
- {
- ...
- module: {
- loaders: [{
- // use `test` to split a single file
- // or `include` to split a whole folder
- test: /.*/,
- include: [path.resolve(__dirname, 'pages/admin')],
- loader: 'bundle?lazy&name=admin'
- }]
- }
- ...
- }
该配置会自动帮我们从主文件中移除 admin 相关的组件代码,然后将其移动到
文件中,然后在 React Router 中,我们同样需要冲定义组件加载函数:
- 1.admin.js
- import HomePage from "./pages/HomePage";
- import AdminPage from "./pages/admin/AdminPage";
- import AdminPageSettings from "./pages/admin/AdminPageSettings";
- const isReactComponent = (obj) = >Boolean(obj && obj.prototype && Boolean(obj.prototype.isReactComponent));
- const component = (component) = >{
- return isReactComponent(component) ? {
- component
- }: {
- getComponent: (loc, cb) = >component(comp = >cb(null, comp.
- default || comp))
- }
- };
- export
- default
- function routes(fromServer) {
- return ( < Router history = {
- browserHistory
- } > <Route path = "/" {...component(HomePage)
- }
- />
- <Route path="/admin " {...component(AdminPage)}/>
- <Route path=" / admin / settings "
- {...component(AdminSettingsPage)}/>
- <Router/>
- )
- }"
有时候我们需要将某个厚重的组件设置为异步加载,这里我们将常见的懒加载操作封装为某个组件及其高阶组件接口,源代码参考 :
- import React from 'react';
- /**
- * @function 支持异步加载的封装组件
- */
- class LazilyLoad extends React.Component {
- constructor() {
- super(...arguments);
- this.state = {
- isLoaded: false,
- };
- }
- componentWillMount() {
- this.load(this.props);
- }
- componentDidMount() {
- this._isMounted = true;
- }
- componentWillReceiveProps(next) {
- if (next.modules === this.props.modules) return null;
- this.load(next);
- }
- componentWillUnmount() {
- this._isMounted = false;
- }
- load(props) {
- this.setState({
- isLoaded: false,
- });
- const {
- modules
- } = props;
- const keys = Object.keys(modules);
- Promise.all(keys.map((key) = >modules[key]())).then((values) = >(keys.reduce((agg, key, index) = >{
- agg[key] = values[index];
- return agg;
- },
- {}))).then((result) = >{
- if (!this._isMounted) return null;
- this.setState({
- modules: result,
- isLoaded: true
- });
- });
- }
- render() {
- if (!this.state.isLoaded) return null;
- return React.Children.only(this.props.children(this.state.modules));
- }
- }
- LazilyLoad.propTypes = {
- children: React.PropTypes.func.isRequired,
- };
- export const LazilyLoadFactory = (Component, modules) = >{
- return (props) = >( < LazilyLoad modules = {
- modules
- } > { (mods) = ><Component {...mods
- } {...props
- }
- />}
- </LazilyLoad > );
- };
- export const importLazy = (promise) = >(promise.then((result) = >result.
- default));
- export
- default LazilyLoad;
这里我们使用类似于 bundle-loader 中的回调方式进行懒加载,不过将其封装为了组件形式。其中的
主要是为了兼容 Babel/ES2015,其只是单纯的返回默认属性值,实例代码参考 。
- importLazy
- render(){
- return ...
- <LazilyLoad modules={{
- LoadedLate: () => importLazy(System.import('../lazy/loaded_late.js'))
- }}>
- {
- ({LoadedLate}) => {
- return <LoadedLate />
- }
- }
- </LazilyLoad>
- ...
- }
在入门介绍中我们讲过可以利用 external 属性来配置引入 jQuery,而这里我们也可以使用高阶组件方式进行异步加载:
- // @flow
- import React,
- {
- Component,
- PropTypes
- }
- from 'react';
- import {
- LazilyLoadFactory
- }
- from '../../../common/utils/load/lazily_load';
- /**
- * 组件LoadedJquery
- */
- export
- default class LoadedJQuery extends Component {
- /**
- * @function 默认渲染函数
- */
- render() {
- return ( < div ref = { (ref) = >this.props.$(ref).css('background-color', 'red')
- } > jQuery加载完毕 < /div>
- );
- }
- }
- export default LazilyLoadFactory(
- LoadedJQuery,
- {
- $: () => System.import('jquery'),
- }
- );/
这里我们将加载完毕的 jQuery 作为组件的 Props 参数传入到组件中使用,同样我们也可以使用这种方式加载我们自定义的函数或者组件。上述两种的效果如下所示:
来源: