CcbeanBlog CcbeanBlog
首页
  • 前端文章

    • JavaScript
    • HTML+CSS
    • Vue
    • React
  • 系列笔记

    • React使用学习
    • Vue2源码探究
  • Node文章

    • 基础
    • 问题
    • 框架
  • 系列笔记

    • 数据结构与算法
  • 构建工具文章

    • webpack
  • 系列笔记

    • Webpack5使用学习
  • MySQL
  • Linux
  • 网络
  • 小技巧
  • 杂记
  • 系列笔记

    • Protobuf Buffers
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Ccbean

靡不有初,鲜克有终
首页
  • 前端文章

    • JavaScript
    • HTML+CSS
    • Vue
    • React
  • 系列笔记

    • React使用学习
    • Vue2源码探究
  • Node文章

    • 基础
    • 问题
    • 框架
  • 系列笔记

    • 数据结构与算法
  • 构建工具文章

    • webpack
  • 系列笔记

    • Webpack5使用学习
  • MySQL
  • Linux
  • 网络
  • 小技巧
  • 杂记
  • 系列笔记

    • Protobuf Buffers
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • JavaScript

  • HTML+CSS

  • Vue

  • React

    • Redux原理学习
    • Apollo-Client简介
    • React零碎点总结
    • react实现按需加载-代码分割(上)
      • react实现按需加载-代码分割(下)
      • 构建自己的redux
    • TypeScript

    • 系列笔记

    • 前端
    • React
    ccbean
    2017-12-29
    目录

    react实现按需加载-代码分割(上)

    # react实现按需加载-代码分割(上)

    # 前言

    我们直接使用webpack配置的react环境,在编译打包之后,页面只生成了一个文件bundle.js,这样在首屏加载时就会很慢,因为加载一个页面的时候也同时加载了其他页面的js,首次进入页面时,只加载自己的JS,这样首屏加载就会快很多。

    # 一、按需加载

    1.首屏加载需要使用bundle-loader这个插件,所以先安装这个插件

    npm install bundle-loader --save-dev
    

    2.新建Bundle.js

    import React, {Component} from 'react'
    
    class Bundle extends Component {
        constructor(props){
            super(props);
            this.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
            });
            props.load((mod) => {
                this.setState({
                    // handle both es imports and cjs
                    mod: mod.default ? mod.default : mod
                })
            })
        }
    
        render() {
            return this.props.children(this.state.mod)
        }
    }
    
    export default Bundle;
    

    此文件是官方提供,参考文档

    3.改造路由器

    // 原路由文件
    import React from 'react';
    import {Route,Switch,Redirect} from 'react-router-dom';
    import Login from './container/login/login'
    import Register from "./container/register/register";
    import Bossinfo from "./container/bossinfo/bossinfo";
    import Geniusinfo from "./container/geniusinfo/geniusinfo";
    import AuthRoute from "./components/authroute/authroute";
    import Dashboard from "./components/dashboard/dashboard";
    import Chat from './components/chat/chat';
    
    
    class App extends React.Component{
        constructor(props){
            super(props);
            this.state = {
                hasError:false
            }
        }
    
        componentDidCatch(err,info){
            console.log(err,info);
            this.setState({
                hasError:true
            })
        }
    
        render(){
            return(
                this.state.hasError ? <Redirect to='/msg'></Redirect>
                    :
                <div>
                    <AuthRoute/>
                    <Switch>
                        <Route path='/login' component={Login}></Route>
                        <Route path='/register' component={Register}></Route>
                        <Route path='/bossinfo' component={Bossinfo}></Route>
                        <Route path='/geniusinfo' component={Geniusinfo}></Route>
                        <Route path='/chat/:user' component={Chat}></Route>
                        <Route component={Dashboard}></Route>
                    </Switch>
                </div>
            )
        }
    }
    
    export default App;
    

    改造:

    import React from 'react';
    import {Route,Switch,Redirect} from 'react-router-dom';
    import Bundle from './Bundle';
    import Login from 'bundle-loader?lazy&name=login!./container/login/login'
    import Register from "bundle-loader?lazy&name=register!./container/register/register";
    import Bossinfo from "bundle-loader?lazy&name=bossinfo!./container/bossinfo/bossinfo";
    import Geniusinfo from "bundle-loader?lazy&name=geniusinfo!./container/geniusinfo/geniusinfo";
    import AuthRoute from "bundle-loader?lazy&name=authroute!./components/authroute/authroute";
    import Dashboard from "bundle-loader?lazy&name=dashboard!./components/dashboard/dashboard";
    import Chat from 'bundle-loader?lazy&name=chat!./components/chat/chat';
    
    
    const Loading = function () {
        return <div>Loading...</div>
    };
    
    // 调用bundle高阶组件,包裹原始组件
    const createComponent = (component) => (props) => (
        <Bundle load={component}>
            {
                (Component) => Component ? <Component {...props}/> : <Loading/>
            }
        </Bundle>
    );
    
    
    
    class App extends React.Component{
        constructor(props){
            super(props);
            this.state = {
                hasError:false
            }
        }
    
        componentDidCatch(err,info){
            console.log(err,info);
            this.setState({
                hasError:true
            })
        }
    
        render(){
            return(
                this.state.hasError ? <Redirect to='/msg'></Redirect>
                    :
                <div>
                    <AuthRoute/>
                    <Switch>
                        <Route path='/login' component={createComponent(Login)}></Route>
                        <Route path='/register' component={createComponent(Register)}></Route>
                        <Route path='/bossinfo' component={createComponent(Bossinfo)}></Route>
                        <Route path='/geniusinfo' component={createComponent(Geniusinfo)}></Route>
                        <Route path='/chat/:user' component={createComponent(Chat)}></Route>
                        <Route component={createComponent(Dashboard)}></Route>
                    </Switch>
                </div>
            )
        }
    }
    
    export default App;
    

    现在你可以npm start,打开浏览器,看是不是进入新的页面,都会加载自己的JS的~

    但是发现,名字都是0.bundle.js这样子的,分不清楚是哪个页面的js。

    修改下webpack.dev.config.js,加个chunkFilename。chunkFilename是除了entry定义的入口js之外的js~

        output: {
            path: path.join(__dirname, './dist'),
            filename: 'bundle.js',
            chunkFilename: '[name].js'
        }
    

    现在运行发现名字变成home.js,这样的了。

    那么问题来了home是在哪里设置的?webpack怎么知道他叫home?

    其实在这里我们定义了,router.js里面

    import Home from 'bundle-loader?lazy&name=home!pages/Home/Home';

    这里有个name=home。

    # 二、缓存

    想象一下这个场景~

    我们网站上线了,用户第一次访问首页,下载了home.js,第二次访问又下载了home.js~

    这肯定不行呀,所以我们一般都会做一个缓存,用户下载一次home.js后,第二次就不下载了。

    有一天,我们更新了home.js,但是用户不知道呀,用户还是使用本地旧的home.js。出问题了~

    怎么解决?每次代码更新后,打包生成的名字不一样。比如第一次叫home.a.js,第二次叫home.b.js。

    文档看这里 (opens new window)

    我们照着文档来

    webpack.dev.config.js

        output: {
            path: path.join(__dirname, './dist'),
            filename: '[name].[hash].js',
            chunkFilename: '[name].[chunkhash].js'
        }
    

    每次打包都用增加hash~

    现在我们试试,是不是修改了文件,打包后相应的文件名字就变啦?

    package (opens new window)

    但是你可能发现了,网页打开报错了~因为你dist/index.html里面引用js名字还是bundle.js老名字啊,改成新的名字就可以啦。

    这样每次都去改名字是不现实的。

    编辑 (opens new window)
    上次更新: 2021/11/10, 12:11:50
    React零碎点总结
    react实现按需加载-代码分割(下)

    ← React零碎点总结 react实现按需加载-代码分割(下)→

    最近更新
    01
    阅读精通正则表达式总结
    09-29
    02
    项目搭建规范的配置
    07-15
    03
    Vite的使用
    07-03
    更多文章>
    Theme by Vdoing | Copyright © 2018-2023 Ccbeango
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式