从0到1搭建webpack4.0+react全家桶(下)

本篇写的是优化方案,查看基础配置的话能够前往这里juejin.im/post/5e6e01…javascript

webpack优化

缓存配置

  • babel-loader自带的缓存

{
    test:/\.js$/,
    use:[
            'babel-loader?cacheDirectory=true'
        ]
}复制代码

  • 使用cache-loader

下载包 cnpm install cache-loader --savejava

{
    test:/\.js$/,
    use:[
        'cache-loader'
    ]
}复制代码

多进程打包(1)happypck

下载包 cnpm install happypack os --save
node

happy启用进程池,设置数量通常为设备cup总数,在loader处指定id做为一个实例,在plugin处进行单独处理react

const HappyPack = require('happypack');
const os = require('os');

module.exports = {

module:{
    rules:[{
        test:/\.js$/,
        use:['happypack/loader?id=js']
    }]
},

plugins:[
    new HappyPack({
        id:'js',
        //这里loaders配置与以前的loader一致
        loaders:['babel-loader'],
        //构建共享进程池,进程池里有多个子进程去处理任务
        ThreadPool: HappyPack.ThreadPool({size: os.cups().length })
    })
]

}复制代码

多进程打包(2)thread-loader(推荐)

下载包 cnpm install thread-loader --savewebpack

把这个loader放在其余loader前面,放置在这个loader以后的loader就会在一个单独的worker池中运行,尽可能只在耗时的loader上面使用,若是项目体积较小打包时间可能还会延长。es6

能够经过预热worker池将制定的loader模块载入node模块缓存里防止启动worker时的高延时。web

const ThreadLoader = require('thread-loader');
const os = require('os');

const JSWorkPool  = {
    //产生work数量
    workers: os.cups().length,
    //闲置时定时删除worker进程
    poolTimeout: 2000,
}

//预热babel-loader模块
ThreadLoader.warmup(JSWorkPool,['babel-loader']);

module.exports = {
    module:{
        rules:[
            {
                test:/\.js$/,
                use:[
                    'thread-loader',
                     'babel-loader'
              ]
            }
        ]
    }
}复制代码

缩小文件寻找路径

在resolve里使用路径重定向、include、exclude和loader的匹配规则npm

const path = require('path');

module.export = {
    module:{
        rules:[
            {
                test:/\.js$/,
                exclude: /node_modules/,
                include: path.join(__dirname,'./src'),
                use:['babel-loader']
            }
        ]
    },
    resolve:{
        modules:['node_modules'],
        extensions:['js','jsx','json'],
        alias:{
            '@src': path.join(__dirname,'./src')
        }
    }
}复制代码

打包压缩

使用官方推荐的terser-webpack-plugin插件,实际使用能极大提高打包速度json

const TerserWebpackPlugin = require('terser-webpack-plugin');

module.export = {
    optimization:{
        minimizer:[
            new TerserWebpackPlugin({
                parallel:true
            })
        ]
    }
}复制代码

React优化

路由懒加载

使用动态import()、React.lazy、React.Suspense加载路由缓存

在用lazy加载组件时,使用Suspense包裹加载组件,正在等待加载组件时能够用Suspense的fallback返回一个Loading组件

//Loading组件
import React, { Component } from 'react';
import { Spin } from 'antd';

class Loading extends Component {
    render() {
        return (
            <div style={{ width: '100%', height: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                <Spin size='large' />
            </div>
        );
    }
}

export default Loading;

//index.js
import React, {lazy,Suspense} from 'react';
import ReactDOM from 'react-dom';
import {Router,Route,Switch} from 'react-router-dom';
import Loading from './Loading'

const Page1 = lazy(()=>import('@src/page1'));
const Page2 = lazy(()=>import('@src/page2'));
const Page3 = lazy(()=>import('@src/page3'));

ReactDOM.render(
    <Router>
        <Suspense fallback = {<Loading/>}>
            <Switch>
                <Route path='/1' component={Page1} />
                <Route path='/2' component={Page2} />
                <Route path='/' component={Page3} />
            </Switch>
        </Suspense>
    </Router>
)复制代码

避免调停

使用React.PureComponent代替shouldComponentUpdate做浅层对比避免重复渲染

使用shouldComponentUpdate状况,对比父组件传来的color值和当前组件的num值变化来判断是否从新渲染组件。

class Child extends React.Component{
    constructor(props){
        super(props);
        this.state={
            num: 0
        }
    }

    shouldComponentUpdate(nextProps,nextState){
        if(this.props.color !== nextProps.color){
            return true;
        }
        
        if(this.state.num !== nextState.num){
            return true;
        }

        return false;
    }

    render(){
        return(
            <div>
                {this.state.num}
                <button 
                    color={this.props.color}
                    onClick={()=>this.setState(state => {num: state.num + 1})}
                >
                点击+1
                </button>
            </div>
        )
    }
}复制代码

换用PureComponent浅层渲染,它会对比全部的prop和state的值来判断是否更新组件,上面例子换做PureComponent来写

class Child extends React.PureComponent{
    constructor(props){
        super(props);
        this.state={
            num: 0
        }
    }

    render(){
        return(
            <div>
                {this.state.num}
                <button 
                    color={this.props.color}
                    onClick={()=>this.setState(state => ({num: state.num + 1}))}
                >
                点击+1
                </button>
            </div>
        )
    }
}复制代码

但这只是浅层对比,上面例子里state.num和props.color都是基本数据类型,举个例子

 1 === 1’red‘ === ’red‘结果都是true

[1,2,3] === [1,2,3] 、{a:1} === {a:1} 结果都是false

由于后面例子属于引用数据类型里的Array和Object,对比的是引用地址而不是值,因此遇到props或者state的值是引用数据类型时,PureComponent就会失去对比意义。

官方提出了一个解决方案就是使用浅拷贝,使用Array.concat、Array.slice、Object.assign来拷贝原数据

handleClick(n){
    this.setState(state=>({
        arr: state.arr.concat(['n'])
        //使用es6扩展符
        //arr: [state.arr, 'n']
    }))
}

handleClick2(obj){
    return Object.assign({},obj,{a:1})
    //使用扩展符
    return {...obj,{a:1}};
}复制代码

参考文档

www.webpackjs.com/    webpack官方文档
react.docschina.org/   react官方文档
相关文章
相关标签/搜索