按需載入(code spliting)

鬧鬧不愛鬧發表於2018-12-19

前言:專案載入的時候,不載入全部程式碼,只載入對應路由需要的元件,屬於前端優化範疇。

原理:利用打包工具將程式碼拆分成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} />複製程式碼


相關文章