從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官方文檔
相關文章
相關標籤/搜索