按需加载(code spliting)

981 阅读2分钟

前言:项目加载的时候,不加载全部代码,只加载对应路由需要的组件,属于前端优化范畴。

原理:利用打包工具将代码拆分成chunk,然后动态创建script标签,并将src属性指向对应的chunk的路径即可。

因为要单独打包代码块,所以webpack统一配置chunkFileName和publicPath字段,前者为打包后的chunk文件命名,后者为所有静态文件引入添加路径前缀。

方式一:(react-loadable)

import React from 'react';import ReactDOM from 'react-dom';import { BrowserRouter, Route, Link } from 'react-router-dom';import Loadable from 'react-loadable';
function LoadableAnotherComponent({ error }) {  if (error) {    return <div>Error!</div>;  } else {    return <div>Loading...</div>;  }}class MyComponent extends React.Component { // loading组件    render() {      return <LoadableAnotherComponent/>;    } }
const HomePage =() => <div>Home Page</div> // 业务组件const UsersPage = () => <div>Users Page</div>
const HomePage2 = Loadable({    loader: () => import(/* webpackChunkName: "home" */'./component/homePage'), // import()返回一个promise,then里面的data对应返回的module。    loading: MyComponent})const UsersPage2 = Loadable({    loader: () => import(/* webpackChunkName: "user" */'./component/userPage'),    loading: MyComponent})function Layout() {    return (        <BrowserRouter>            <div>            <Link exact to="/">首页</Link>            <Link to="/users">用户</Link>            <Route path="/" exact component={HomePage2} />            <Route path="/users" component={UsersPage2} />            </div>        </BrowserRouter>    );}
ReactDOM.render(<Layout />, document.getElementById('box'));

方式二:bundle-loader + Bundle包装组件

import React from 'react';import ReactDOM from 'react-dom';import { BrowserRouter, Route, Link } from 'react-router-dom';import HomePage from 'bundle-loader?lazy!./component/homePage'; // bundle-loader底层为require.ensure方法,返回一个方法lazy参数表示懒加载,否则引入时就会执行对应的module。import UserPage from 'bundle-loader?lazy!./component/userPage';
function LoadableAnotherComponent({ error }) {  console.log(error)  return (error ? <div>{error}</div> : <div>loading.............</div>);}class MyComponent extends React.Component {    render() {      return <LoadableAnotherComponent/>;    }}class Bundle extends React.Component { // 包装组件类似上面的react-loadable    state = {      // short for "module" but that's a keyword in js, so "mod"      mod: null    }      componentWillMount() {      // 加载初始状态      this.load(this.props);    }      componentWillReceiveProps(nextProps) {      if (nextProps.load !== this.props.load) {        this.load(nextProps);      }    }      load(props) {      // 重置状态      this.setState({        mod: null      });      // 传入组件的组件      console.log(props.load);      props.load((mod) => {        console.log(mod)        this.setState({          // handle both es imports and cjs          mod: mod.default ? mod.default : mod        });      });    }      render() {      // if state mode not undefined,The container will render children      return this.state.mod ? this.props.children(this.state.mod) : null;    }  }  const HomePageC = (props) => {    return (      <Bundle load={HomePage}>        {(Container) => <Container {...props} />}      </Bundle>    );}const UserPageC = (props) => {    return (      <Bundle load={UserPage}>        {(Container) => <Container {...props} />}      </Bundle>    );}
function Layout() {    return (        <BrowserRouter>            <div>            <Link to="/">首页</Link>            <Link to="/users">用户</Link>            <Route path="/" exact component={HomePageC} />            <Route path="/users" component={UserPageC} />            </div>        </BrowserRouter>    );}
ReactDOM.render(<Layout />, document.getElementById('box'));

方式三:require.ensure + (react-router3的getComponent)

const about = (location, cb) => {
    require.ensure([], require => { // require.ensure是CommonJs的唯一的动态加载模块方法。
        cb(null, require('../Component/about').default)
    },'about')
}

//配置route
<Route path="about" getComponent={about} />