• 前端性能优化之按需加载(React-router+webpack)


    一、什么是按需加载

    和异步加载script的目的一样(异步加载script的方法),按需加载/代码切割也可以解决首屏加载的速度。

    • 什么时候需要按需加载

    如果是大文件,使用按需加载就十分合适。比如一个近1M的全国城市省市县的json文件,在我首屏加载的时候并不需要引入,而是当用户点击选项的时候才加载。如果不点击,则不会加载。就可以缩短首屏http请求的数量以及时间。

    如果是小文件,可以不必太在意按需加载。过多的http请求会导致性能问题。

    二、实现按需加载的方法

    1. Webpack打包模块工具实现
    2. RequireJs实现

    这里介绍React-router+Webpack实现按需加载的功能,效果如下:
    按需加载效果图

    三、实现过程(React-router4.0)

    注意!我这里使用的是最新版本的React-router-dom^4.3.1.如果是4.0以下的react-route可直接看四

    4.0相比以前实现方式要复杂。需要引入bundle-loader模块。并且自己创建bundle模型实现。

    1.创建包装组件模型bundle.js
    import React from 'react';
    
    class Bundle extends React.Component {
        constructor(arg){
            super(arg)
            this.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;
        }
    }
    
    export default Bundle ;
    
    2.创建包装组件的方法(函数)
    // 懒加载方法
    import React from 'react';
    import Bundle from './Bundle';
    
    console.log(Bundle);
    // 默认加载组件,可以直接返回 null
    const Loading = () => <div>Loading...</div>;
    
    /*
       包装方法,第一次调用后会返回一个组件(函数式组件)
       由于要将其作为路由下的组件,所以需要将 props 传入
    */
    
    const lazyLoad = loadComponent => props => (
       <Bundle load={loadComponent}>
          {Comp => (Comp ? <Comp {...props} /> : <Loading />)}
       </Bundle>
    );
    
    console.log(lazyLoad);
    export default lazyLoad;    //实际上lazyLoad就是一个函数,组件调用即可
    
    

    上面两个文件的关系:
    lazyLoad.js从名字上看,叫懒加载.实际上是一个中间件的作用。最后lazyLoad会暴露一个函数出来供组件调用。lazyLoad导出的内容:

    function lazyLoad(loadComponent) {
        return function(props) {
            return (
                <Bundle load={loadComponent}>
                    {Comp => (Comp ? <Comp {...props} /> : <Loading />)}
                </Bundle>
            )
        }
    }
    

    显而易见,loadComponent就是要加载的组件,在路由中调用,例如:异步调用page1组件

    <Route path="/page1" component={lazyLoad(Page1)}/> 
    

    Bundle.js作为按需加载的核心,在lazyLoad中间件就已经引入,并传入一个自定义的方法load,值为组件内容。以及动态的子内容children:

    {Comp => (Comp ? <Comp {...props} /> : <Loading />)}
    

    最终返回组件信息,并附带相应的props.如果不存在相关组件,则Loading
    bundle

    3. Route搭配使用
    import React from 'react';
    import { NavLink,Route,Switch,BrowserRouter as Router } from 'react-router-dom'
    import './style/style.css'
    import 'bundle-loader'
    // bundle模型用来异步加载组件
    import Bundle from '../routes/Bundle.js';
    import lazyLoad from '../routes/lazyLoad';
    
    import Page1 from 'bundle-loader?lazy&name=page1!../components/page1/index';
    import Page2 from 'bundle-loader?lazy&name=page2!../components/page2/index';
    import Page3 from 'bundle-loader?lazy&name=page3!../components/page3/index';
    
    class AppPage extends React.Component{
        constructor(arg){
            super(arg)
    
            this.state={}
        }
        render(){
            return(
                <Router  basename="/" >
                    <div className="appWried">
                        <div className="appBtn">
                            <NavLink to="/page1" className="button" activeClassName="active">
                                PAGE1
                            </NavLink>
                            <NavLink to="/page2" className="button" activeClassName="active">
                                PAGE2
                            </NavLink>
                            <NavLink to="/page3" className="button" activeClassName="active">
                                PAGE3
                            </NavLink>
                        </div>
                        <Route
                            path="/"
                            render={props => (
                                <Switch>
                                    <Route path="/page1" component={lazyLoad(Page1)}/> 
                                    <Route path="/page2" component={lazyLoad(Page2)}/> 
                                    <Route path="/page3" component={lazyLoad(Page3)}/> 
                                </Switch>
                            )}
                        />
                    </div>
                </Router>
            )
        }
    }
    
    export default AppPage;
    
    • 几个注意的点:

      1. import异步加载组件的时候,名字变更为'bundle-loader?lazy&name=page1!../components/page1/index'

      其中bundle-loader表示loader:'bunle-loader'(需加载bundle-loader模块)
      lazy表示lazy:true;懒加载
      name:表示异步生成的文件名字

      1. 去掉外层route,不用render渲染也是可行的
      <Switch>
          <Route path="/page1" component={lazyLoad(Page1)}/> 
          <Route path="/page2" component={lazyLoad(Page2)}/> 
          <Route path="/page3" component={lazyLoad(Page3)}/> 
      </Switch>
      
    //webpack.config.js
    ...
        module.exports = {
            ...
            output:{
                path:path.join(__dirname + '/dist'),    //打包地方
                filename:'bundle.js',    //打包名字
                publicPath: '/',    //自动生成html引入js的路径
                //按需加载
                chunkFilename:'[name]_[chunkhash:8].js'
            },
            ...
        }
    ...
    

    这里要注意一下publicPath这个参数.
    如果未设置publicPath参数,则默认打包生成的html引入bundle的时候为:

    <script type="text/javascript" src="bundle.js"></script>
    

    如果设置publicPath为publicPath: '/dist',则打包后html文件引入js格式为:

    <script type="text/javascript" src="/dist/bundle.js"></script>
    

    参考文章:React-router-v4 - Webpack 实现按需加载(code-splitting)

  • 相关阅读:
    CentOS7 安装Redis Cluster集群
    CentOS7 安装Redis 单机版
    CentOS7 配置免密登陆
    深入java虚拟机学习 -- 内存管理机制
    深入java虚拟机学习 -- 类的卸载
    深入java虚拟机学习 -- 类的加载机制(四)
    深入java虚拟机学习 -- 类的加载机制(三)
    深入java虚拟机学习 -- 类的加载机制(续)
    (原创)cocos lua 热更新从零开始(一)最简单demo
    lua 调用参数报错(a userdata value)
  • 原文地址:https://www.cnblogs.com/soyxiaobi/p/9535292.html
Copyright © 2020-2023  润新知