webpack4 中的最新 React全家桶實戰使用配置指南!

前端小智發表於2018-11-17

圖片描述

最新React全家桶實戰使用配置指南

這篇文件 是呂小明老師結合以往的專案經驗 加上自己本身對react webpack redux理解寫下的總結文件,總共耗時一週總結下來的,希望能對讀者能夠有收穫, 我是在這基礎多些加工!

目錄

1.版本說明

2.目錄結構

3.初始化專案

4.webpack

5.react

6.配置loader(sass,jsx))

7.引入babel

8.使用HtmlWebpackPlugin

9.redux

10.使用webpack-dev-server

11.多入口頁面配置

12.如何理解entry point(bundle),chunk,module

13.多入口頁面html配置

14.模組熱替換(Hot Module Replacement)

15.使用ESLint

16.使用react-router

17.使用redux-thunk

18.使用axios和async/await

19.Code Splitting

20.使用CommonsChunkPlugin

版本說明

由於構建相關例如webpack,babel等更新的較快,所以本教程以下面各種模組的版本號為主,切勿輕易修改或更新版本。

"dependencies": {
    "babel-core": "^6.26.3",
    "babel-eslint": "^8.2.3",
    "babel-loader": "^7.1.4",
    "babel-plugin-transform-async-to-generator": "^6.24.1",
    "babel-plugin-transform-runtime": "^6.23.0",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-react": "^6.24.1",
    "babel-preset-stage-0": "^6.24.1",
    "babel-preset-stage-3": "^6.24.1",
    "css-loader": "^0.28.11",
    "eslint": "^4.19.1",
    "eslint-loader": "^2.0.0",
    "eslint-plugin-react": "^7.9.1",
    "file-loader": "^1.1.11",
    "history": "^4.7.2",
    "html-webpack-plugin": "^3.2.0",
    "react": "^16.4.0",
    "react-dom": "^16.4.0",
    "react-hot-loader": "^4.0.0",
    "react-redux": "^5.0.7",
    "react-router-dom": "^4.3.1",
    "react-router-redux": "^5.0.0-alpha.9",
    "redux": "^4.0.0",
    "sass-loader": "^7.0.3",
    "style-loader": "^0.21.0",
    "url-loader": "^1.0.1",
    "webpack": "^4.12.0",
    "webpack-cli": "^3.0.3",
    "webpack-dev-server": "^3.1.1"
}

目錄結構

開發和釋出版本的配置檔案是分開的,多入口頁面的目錄結構。

react-family/
    |
    |──dist/                                    * 釋出版本構建輸出路徑
    |
    |──dev/                                     * 除錯版本構建輸出路徑
    |
    |──src/                                     * 工具函式
    |     |
    |     |—— component/                        * 各頁面公用元件
    |     |
    |     |—— page/                             * 頁面程式碼
    |     |      |—— index/                     * 頁面程式碼
    |     |      |        |—— Main/             * 元件程式碼
    |     |      |        |       |—— Main.jsx  * 元件jsx
    |     |      |        |       |—— Main.scss * 元件css
    |     |      |
    |     |      |—— detail/                    * 頁面程式碼
    |     |
    |     |—— static/                           * 靜態檔案js,css
    |
    |
    |──webpack.config.build.js                  * 釋出版本使用的webpack配置檔案
    |──webpack.config.dev.js                    * 除錯版本使用的webpack配置檔案
    |──.eslint                                  * eslint配置檔案
    |__.babelrc                                 * babel配置檔案
    
    
    

初始化專案

1.建立資料夾

mkdir react-family-bucket

2.初始化npm

cd react-family-bucket
npm init

如果有特殊需要,可以填入自己的配置,一路回車下來,會生成一個package.json,裡面是你專案的基本資訊,後面的npm依賴安裝也會配置在這裡。

webpack

1.安裝webpack

npm install webpack@4.12.0 --save
or
npm install webpack@4.12.0 --g

--save 是將當前webpack安裝到react-family-bucket下的/node_modules。

--g 是將當前webpack安裝到全域性下面,可以在node的安裝目錄下找到全域性的/node_modules。

2.配置webopack配置檔案

touch webpack.config.dev.js

3.新建一個app.js

touch app.js

寫入基本的webpack配置,可以參考這裡

const path = require('path');
const srcRoot = './src';
module.exports = {

    // 輸入配置
    entry: [
      './app.js'
    ],,

    // 輸出配置
    output: {
        path: path.resolve(__dirname, './dev'),

        filename: 'bundle.min.js'
    },
};

3.執行webpack命令

如果是全域性安裝:

webpack --config webpack.config.dev.js

如果是當前目錄安裝:

./node_modules/.bin/webpack --config webpack.config.dev.js

為了方便我們使用,可以在package.json中 scripts 新增執行命令:

"scripts": {
  "dev": "./node_modules/.bin/webpack --config webpack.config.dev.js",
},

執行npm run dev命令之後,會發現需要安裝webpack-cli,(webpack4之後需要安裝這個)

npm install webpack-cli --save

安裝後,執行 npm run dev 會發現控制檯有個警告 WARNING in configuration ,去除WARNING in configuration 警告,在webpack.config.dev.js 增加一個配置即可:

...
mode: 'development'
...

成功之後會在dev下面生成bundle.min.js代表正常。

如果想要動態監聽檔案變化需要在命令後面新增 --watch。

react

1.安裝react

npm install react react-dom --save

2.建立page目錄和index頁面檔案:

mkdir src
mkdir page
cd page

3.建立index


mkdir index
cd index & touch index.js & touch index.html

index.js

import ReactDom from 'react-dom';
import Main from './Main/Main.jsx';

ReactDom.render(<Main />, document.getElementById('root'));

index.html

<!DOCTYPE html>
<html>
<head>
    <title>index</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">

</head>
<body>
<div id="root"></div>
</body>
</html>

4.建立Main元件

import React from 'react';

class Main extends React.Component {

    constructor(props) {
        super(props);
    }

    render() {
        return (<div>Main</div>);
    }
}
export default Main;

5.修改webpack配置入口檔案

entry: [
    path.resolve(srcRoot,'./page/index/index.js')
],

配置loader

1.處理樣式檔案需要這些loader:

npm install css-loader sass-loader style-loader file-loader --save

配置:

module: {
    // 載入器配置
    rules: [
        { test: /\.css$/, use: ['style-loader', 'css-loader'], include: path.resolve(srcRoot)},
        { test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'], include: path.resolve(srcRoot)}
    ]
},

2.url-loader 處理處理靜態檔案

npm install url-loader --save

配置:

module: {
    // 載入器配置
    rules: [
        { test: /\.(png|jpg|jpeg)$/, use: 'url-loader?limit=8192&name=images/[name].[hash].[ext]', include: path.resolve(srcRoot)}
    ]
},

limit:表示超過多少就使用base64來代替,單位是byte

name:可以設定圖片的路徑,名稱和是否使用hash 具體參考這裡

引入babel

bebel是用來解析es6語法或者是es7語法分解析器,讓開發者能夠使用新的es語法,同時支援jsx,vue等多種框架。

1.安裝babel

npm install babel-core babel-loader --save

配置:

module: {
    // 載入器配置
    rules: [
        { test: /\.(js|jsx)$/, use: [{loader:'babel-loader'}] ,include: path.resolve(srcRoot)},
    ]
},

2.babel配置檔案:.babelrc

touch .babelrc

配置:

{
    "presets": [
        "es2015",
        "react",
        "stage-0"
    ],
    "plugins": []
}

babel支援自定義的預設(presets)或外掛(plugins),只有配置了這兩個才能讓babel生效,單獨的安裝babel是無意義的。

presets:代表babel支援那種語法(就是你用那種語法寫),優先順序是從下往上,state-0|1|2|..代表有很多沒有列入標準的語法回已state-x表示,參考這裡

plugins:代表babel解析的時候使用哪些外掛,作用和presets類似,優先順序是從上往下。

依次安裝:

npm install babel-preset-es2015 babel-preset-react babel-preset-stage-0 --save

3.babel-polyfill是什麼?

我們之前使用的babel,babel-loader 預設只轉換新的 JavaScript 語法,而不轉換新的 API。例如,Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全域性物件,以及一些定義在全域性物件上的方法(比如 Object.assign)都不會轉譯。如果想使用這些新的物件和方法,必須使用 babel-polyfill,為當前環境提供一個墊片。

npm install --save babel-polyfill

4.transform-runtime 有什麼區別?

當使用babel-polyfill時有一些問題:

  • 預設會引入所有babel支援的新語法,這樣就會導致你的檔案程式碼非常龐大。
  • 通過向全域性物件和內建物件的prototype上新增方法來達成目的,造成全域性變數汙染。

這時就需要transform-runtime來幫我們有選擇性的引入:

npm install --save babel-plugin-transform-runtime

配置檔案:


{
  "plugins": [
    ["transform-runtime", {
      "helpers": false,
      "polyfill": false,
      "regenerator": true,
      "moduleName": "babel-runtime"
    }]
  ]
}


使用HtmlWebpackPlugin

記得我們之前新建的index.html麼 我們執行構建命令之後並沒有將index.html打包到dev目錄下 我們需要HtmlWebpackPlugin來將我們output的js和html結合起來:

npm install html-webpack-plugin --save

配置:

const HtmlWebpackPlugin = require('html-webpack-plugin');
...
plugins: [
    new HtmlWebpackPlugin({
        filename: path.resolve(devPath, 'index.html'),
        template: path.resolve(srcRoot, './page/index/index.html'),
    })
]

filename:可以設定html輸出的路徑和檔名
template:可以設定已哪個html檔案為模版

更多引數配置可以參考這裡

redux

關於redux的使用可以參考阮一峰老師的入門教程

1.安裝redux

npm install redux react-redux --save

1.新建reducers,actions目錄和檔案

|—— index/                          
|—— Main/                   * 元件程式碼
|       |—— Main.jsx        * 元件jsx
|       |—— Main.scss       * 元件css
|
|—— actions/ 
|       |—— actionTypes.js  * action常量
|       |—— todoAction.js   * action
|
|—— reducers/ 
|       |—— todoReducer.js  * reducer
|
|—— store.js
|
|—— index.js

2.修改程式碼,引入redux,這裡以一個redux todo為demo例子:

index.js

import ReactDom from 'react-dom';
import React from 'react';
import Main from './Main/Main.jsx';
import store from './store.js';
import { Provider } from 'react-redux';

ReactDom.render(
    <Provider store={store}>
        <Main />
    </Provider>
, document.getElementById('root'));  

store.js

import { createStore } from 'redux';
import todoReducer from './reducers/todoReducer.js';

const store = createStore(todoReducer);

export default store;  

tabReducer.js

    import { ADD_TODO } from '../actions/actionTypes.js';
    
    const initialState = {
          todoList: []
    };
    
    const addTodo = (state, action) => {
    
      return { ...state, todoList: state.todoList.concat(action.obj) }
    }
    
    const todoReducer = (state = initialState, action) => {
      switch(action.type) {
        case ADD_TODO: return addTodo(state, action);
        default: return state;
      }
    };
    export default todoReducer;

Main.jsx

import React from 'react';
import { connect } from 'react-redux';
import { addTodo } from '../actions/todoAction.js';

class Main extends React.Component {

    onClick(){
        let text = this.refs.input;

        this.props.dispatch(addTodo({
            text: text.value
        }))
    }
    render() {
        return (
            <div>
                <input ref="input" type="text"></input>
                <button onClick={()=>this.onClick()}>提交</button>
                <ul>
                {this.props.todoList.map((item, index)=>{
                    return <li key={index}>{item.text}</li>
                })}
                </ul>
            </div>
        );
    }
}

export default connect(
    state => ({
        todoList: state.todoList
    })
)(Main);

todoAction.js

import { ADD_TODO } from './actionTypes.js';

export const addTodo = (obj) => {
  return {
    type: ADD_TODO,
    obj: obj
  };
};

使用webpack-dev-server

webpack-dev-server是一個小型的Node.js Express伺服器,它使用webpack-dev-middleware來服務於webpack的包。

1.安裝

npm install webpack-dev-server --save

修改在package.json中新增的執行命令:

"scripts": {
  "dev": "./node_modules/.bin/webpack-dev-server --config webpack.config.dev.js",
},

2.配置webpack配置檔案:

devServer: {
    "contentBase": devPath,
    "compress": true,
},

contentBase: 表示server檔案的根目錄
compress: 表示開啟gzip

更多的配置文件參考這裡

  • webpack-dev-server預設情況下會將output的內容放在記憶體中,是看不到物理的檔案的,如果想要看到物理的dev下面的檔案可以安裝write-file-webpack-plugin這個外掛。
  • webpack-dev-server預設會開啟livereload功能

3.devtool功能:

具體來說新增了devtool: 'inline-source-map'之後,利用source-map你在chrome控制檯看到的source原始碼都是真正的原始碼,未壓縮,未編譯前的程式碼,沒有新增,你看到的程式碼是真實的壓縮過,編譯過的程式碼,更多devtool的配置可以參考這裡

多入口檔案配置

在之前的配置中,都是基於單入口頁面配置的,entry和output只有一個檔案,但是實際專案很多情況下是多頁面的,在配置多頁面時,有2中方法可以選擇:

1.在entry入口配置時,傳入物件而不是單獨陣列,output時利用[name]關鍵字來區分輸出檔案例如:

entry: {
    index: [path.resolve(srcRoot,'./page/index/index1.js'),path.resolve(srcRoot,'./page/index/index2.js')],
    detail: path.resolve(srcRoot,'./page/detail/detail.js'),
    home: path.resolve(srcRoot,'./page/home/home.js'),
},
output: {
    path: path.resolve(__dirname, './dev'),

    filename: '[name].min.js'
},

2.通過node動態遍歷需要entry point的目錄,來動態生成entry:

const pageDir = path.resolve(srcRoot, 'page');
function getEntry() {
    let entryMap = {};

    fs.readdirSync(pageDir).forEach((pathname)=>{
        let fullPathName = path.resolve(pageDir, pathname);
        let stat = fs.statSync(fullPathName);
        let fileName = path.resolve(fullPathName, 'index.js');

        if (stat.isDirectory() && fs.existsSync(fileName)) {
            entryMap[pathname] = fileName;
        }

    });

    return entryMap;
}
{
    ...
    entry: getEntry()
    ...
}

本demo採用的是第二中寫法,能夠更加靈活。

如何理解entry point(bundle),chunk,module

在webpack中,如何理解entry point(bundle),chunk,module?

根據圖上的表述,我這裡簡單說一下便於理解的結論:

  • 配置中每個檔案例如index1.js,index2.js,detail.js,home.js都屬於entry point.
  • entry這個配置中,每個key值,index,detail,home都相當於chunk。
  • 我們在程式碼中的require或者import的都屬於module,這點很好理解。
  • chunk的分類比較特別,有entry chunk,initial chunk,normal chunk,參考這個文章
  • 正常情況下,一個chunk對應一個output,在使用了CommonsChunkPlugin或者require.ensure之後,chunk就變成了initial chunk,normal chunk,這時,一個chunk對應多個output。

理解這些概念對於後續使用webpack外掛有很大的幫助。

多入口頁面html配置

之前我們配置HtmlWebpackPlugin時,同樣採用的是但頁面的配置,這裡我們將進行多頁面改造,entryMap是上一步得到的entry:

function htmlAarray(entryMap) {
    let htmlAarray = [];

    Object.keys(entryMap).forEach(function(key){
        let fullPathName = path.resolve(pageDir, key);
        let fileName = path.resolve(fullPathName, key + '.html')
        if (fs.existsSync(fileName)) {
            htmlAarray.push(new HtmlWebpackPlugin({
                chunks: key, // 注意這裡的key就是chunk
                filename: key + '.html',
                template: fileName,
                inlineSource:  '.(js|css)'
            }))
        }
    });

    return htmlAarray;

}
修改plugin配置:plugins: [
     ...
].concat(htmlMap)

模組熱替換(Hot Module Replacement)

模組熱替換 (Hot Module Replacement 或 HMR)是 webpack 提供的最有用的功能之一。它允許在執行時更新各種模組,而無需進行完全重新整理,很高大上有木有!

下面說一下配置方法,它需要結合devServer使用:

devServer: {
    hot: true // 開啟HMR
},

開啟plugin:

const webpack = require('webpack');
plugins: [
    new webpack.NamedModulesPlugin(),
    new webpack.HotModuleReplacementPlugin(),
].concat(htmlMap)

結合React一起使用:

1.安裝react-hot-loader

npm install react-hot-loader --save

並新建一個Container.jsx:

import React from 'react';
import Main from './Main.jsx';
import { hot } from 'react-hot-loader'

class Container extends React.Component {

    render() {
        return <Main />
    }
        
}
export default hot(module)(Container);

結合redux:如果專案沒有使用redux,可以無需配置後面2步

2.修改store.js新增下面程式碼,為了讓reducer也能實時熱替換

if (module.hot) {
    module.hot.accept('./reducers/todoReducer.js', () => {
      const nextRootReducer = require('./reducers/todoReducer.js').default;
      store.replaceReducer(nextRootReducer);
    });
}

3.修改index.js

import ReactDom from 'react-dom';
import React from 'react';
import Container from './Main/Container.jsx';
import store from './store.js';

import { Provider } from 'react-redux';

ReactDom.render(
    <Provider store={store}>
        <Container />
    </Provider>
, document.getElementById('root'));

當控制檯看到[WDS] Hot Module Replacement enabled.代表開啟成功

使用ESLint

ESLint 是眾多 Javascript Linter 中的其中一種,其他比較常見的還有 JSLintJSHint,之所以用 ESLint 是因為他可以自由選擇要使用哪些規則,也有很多現成的 plugin 可以使用,另外他對 ES6 還有 JSX 的支援程度跟其他 linter 相比之下也是最高的。

1.安裝ESLint

npm install eslint eslint-loader babel-eslint --save

其中eslint-loader是將webpack和eslint結合起來在webpack的配置檔案中新增一個eslint-loader種,修改如下:

{ test: /\.(js|jsx)$/, use: [{loader:'babel-loader'},{loader:'eslint-loader'}] ,include: path.resolve(srcRoot)},   


2.新建.eslintrc配置檔案,將parser配置成babel-eslint

{
    "extends": ["eslint:recommended"],
    
    "parser": "babel-eslint",

    "globals": {
    },
    "rules": {
    }
} 

3.安裝eslint-plugin-react:

npm install eslint-plugin-react --save
  • 說明一下,正常情況下每個eslint規則都是需要在rule下面配置,如果什麼都不配置,其實本身eslint是不生效的。
  • eslint本身有很多預設的規則模版,可以通過extends來配置,預設可以使用eslint:recommended。
  • 在使用react開發時可以安裝eslint-plugin-react來告知使用react專用的規則來lint。

修改.eslintrc配置檔案,增加rules,更多rules配置可以參考這裡

{
    "extends": ["eslint:recommended","plugin:react/recommended"],
    
    "parser": "babel-eslint",

    "globals": {
        "window": true,
        "document": true,
        "module": true,
        "require": true
    },
    "rules": {
        "react/prop-types" : "off",
        "no-console" : "off"
    }
}

使用react-router

react-router強大指出在於方便程式碼管理,結合redux使用更加強大,同時支援web,native更多參考這裡

1.安裝react-router-dom

npm install react-router-dom --save

2.如果專案中用了redux,可以安裝react-router-redux

npm install react-router-redux@next history --save

3.修改程式碼:

index.js

import ReactDom from 'react-dom';
import React from 'react';
import Container from './Main/Container.jsx';
import { store, history } from './store.js';

import { Provider } from 'react-redux';

import createHistory from 'history/createHashHistory';
import { ConnectedRouter } from 'react-router-redux';

const history = createHistory();

ReactDom.render(
    <Provider store={store}>
        <ConnectedRouter history={history}>
            <Container />
        </ConnectedRouter>
    </Provider>
, document.getElementById('root'));

結合history,react-router一共有3中不同的router:

  • BrowserRouter通過history/createBrowserHistory引入:當切換時,url會動態更新,底層使用的時html5的pushState
  • HashRouter通過history/createHashHistory引入:當切換時,動態修改hash,利用hashchange事件。
  • MemoryRouter 通過history/createMemoryHistory引入:將路徑,路由相關資料存入記憶體中,不涉及url相關更新,相容性好。

更多配置可以參考這裡

4.如果想要在程式碼邏輯中獲取當前的route路徑需要引入router-reducer:

新建main.js:

import { combineReducers } from 'redux';
import { routerReducer } from "react-router-redux";
import todoReducer from './todoReducer.js';

const reducers = combineReducers({
  todoReducer,
  router: routerReducer
});
export default reducers;

修改store.js:

import { createStore } from 'redux';
import mainReducer from './reducers/main.js';

const store = createStore(mainReducer);

export default store;

然後就可以在this.props.router裡面獲取單相關的路徑資訊

5.如果需要自己通過action來觸發router的跳轉,需要引入routerMiddleware:

import { createStore,applyMiddleware } from 'redux';
import { routerMiddleware } from "react-router-redux";
const middleware = routerMiddleware(history);
const store = createStore(mainReducer,applyMiddleware(middleware));

6.使用Route和Link和withRouter:

先說說都是幹嘛的:

<Route exact path="/" component={Div1}></Route>
<Route path="/2" component={Div2}></Route>
export default withRouter(connect(
   state => ({
      todoList: state.todoReducer.todoList
   })
)(Main));

如果你在使用hash時遇到Warning: Hash history cannot PUSH the same path; a new entry will not be added to the history stack錯誤,可以將push改為replace即:

<NavLink
    replace={true}
    to="/2"
    activeClassName="selected"
    >切換到2號</NavLink>

7.設定初始化路由:

BrowserRouter和HashRouter:

const history = createHistory();
history.push('2');

MemoryRouter:

const history = createMemoryHistory({
    initialEntries: ['/2']
});


使用redux-thunk

redux-thunk 是一個比較流行的 redux 非同步 action 中介軟體,比如 action 中有 setTimeout 或者通過 fetch通用遠端 API 這些場景,那麼久應該使用 redux-thunk 了。redux-thunk 幫助你統一了非同步和同步 action 的呼叫方式,把非同步過程放在 action 級別解決,對 component 沒有影響。

1.安裝redux-thunk:

npm install redux-thunk --save

2.修改store.js:

import { createStore,applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import mainReducer from './reducers/main';
...
const store = createStore(mainReducer, applyMiddleware(thunk));
...
export default store;

3.在action.js使用redux-thunk:

export const getData = (obj) => (dispatch, getState) => {
  setTimeout(()=>{
    dispatch({
        type: GET_DATA,
        obj: obj
    });
  },1000);
};

使用axios和async/await

axios 是一個基於Promise 用於瀏覽器和 nodejs 的 HTTP 客戶端:

  • 從瀏覽器中建立 XMLHttpRequest
  • 從 node.js 發出 http 請求
  • 支援 Promise API
  • 自動轉換JSON資料

1.安裝axios:

npm install axios --save

2.在action中使用axios:

import axios from 'axios';
export const getData = (obj) => (dispatch, getState) => {
    axios.get('/json/comments.json').then((resp)=>{
        dispatch({
            type: GET_DATA,
            obj: resp
        });
    });
};

async/await:

Javascript的回撥地獄,相信很多人都知道,尤其是在node端,近些年比較流行的是Promise的解決方案,但是隨著 Node 7 的釋出,程式設計終級解決方案的 async/await應聲而出。

function resolveAfter2Seconds() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('resolved');
    }, 2000);
  });
}

async function asyncCall() {
  var result = await resolveAfter2Seconds();
}

asyncCall();

async/await的用途是簡化使用 promises 非同步呼叫的操作,並對一組 Promises執行某些操作。await前提是方法返回的是一個Promise物件,正如Promises類似於結構化回撥,async/await類似於組合生成器和 promises。

1.async/await需要安裝babel-plugin-transform-async-to-generator

npm install babel-plugin-transform-async-to-generator --save

2.在.babelrc中增加配置:

"plugins": [
    "transform-async-to-generator"
]

這樣做僅僅是將async轉換generator,如果你當前的瀏覽器不支援generator,你將會收到一個Uncaught ReferenceError: regeneratorRuntime is not defined的錯誤,你需要:

3.安裝babel-plugin-transform-runtime:

npm install babel-plugin-transform-async-to-generator --save

4.修改.babelrc中的配置(可以去掉之前配置的transform-async-to-generator):

"plugins": [
    "transform-runtime"
]

5.如果不想引入所有的polyfill(參考上面對babel的解釋),可以增加配置:

"plugins": [
    "transform-runtime",
        {
            "polyfill": false,

            "regenerator": true,
        }
]

6.結合axios使用

import axios from 'axios';
export const getData = (obj) => async (dispatch, getState) => {
    let resp = axios.get('/json/comments.json');
    dispatch({
        type: GET_DATA,
        obj: resp
    });
};

Code Splitting

1.對於webpack1,2之前,你可以使用require.ensure來控制一個元件的懶載入:

require.ensure([], _require => {
    let Component = _require('./Component.jsx');
},'lazyname')

2.在webpack4中,官方已經不再推薦使用require.ensure來使用懶載入功能Dynamic Imports,取而代之的是ES6的import()方法:

import(
  /* webpackChunkName: "my-chunk-name" */
  /* webpackMode: "lazy" */
  'module'
);

不小小看註釋裡的程式碼,webpack在打包時會動態識別這裡的程式碼來做相關的配置,例如chunk name等等。

3.Prefetching/Preloading modules:

webpack 4.6.0+支援了Prefetching/Preloading的寫法:

import(/* webpackPreload: true */ 'ChartingLibrary');

3.結合React-Router使用:

react-loadable對上述的功能做了封裝,豐富了一些功能,結合React-Router起來使用更加方便。

npm install react-loadable --save

在react-router裡使用:

function Loading() {
  return <div>Loading...</div>;
}

let Div2 = Loadable({
  loader: () => import('./Div2'), 
  loading: Loading,
});

<Route path="/2" component={Div2}></Route>

使用CommonsChunkPlugin

CommonsChunkPlugin 外掛,是一個可選的用於建立一個獨立檔案(又稱作 chunk)的功能,這個檔案包括多個入口 chunk 的公共模組。通過將公共模組拆出來,最終合成的檔案能夠在最開始的時候載入一次,便存起來到快取中供後續使用。

1.在webpack4之前的用法:

new webpack.optimize.CommonsChunkPlugin({
    name: 'common',
    chunks: ['page1','page2'],
    minChunks: 3
})
  • name: string: 提出出的名稱
  • chunks: string[]: webpack會從傳入的chunk裡面提取公共程式碼,預設從所有entry裡提取
  • minChunks: number|infinity|function(module,count)->boolean: 如果傳入數字或infinity(預設值為3),就是告訴webpack,只有當模組重複的次數大於等於該數字時,這個模組才會被提取出來。當傳入為函式時,所有符合條件的chunk中的模組都會被傳入該函式做計算,返回true的模組會被提取到目標chunk。

更多的引數配置,可以參考這裡

2.在webpack4之後的用法:

module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'async',
      minSize: 30000,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: '~',
      name: true,
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    }
  }
};
  • splitChunks: 配置一個分離chunk(代替老版本的CommonsChunkPlugin)
  • cacheGroups: 自定義配置主要使用它來決定生成的檔案:
  • test: 限制範圍
  • name: 生成檔名
  • priority: 優先順序
  • minSize: number: 最小尺寸必須大於此值,預設30000B
  • minChunks: 其他entry引用次數大於此值,預設1
  • maxInitialRequests: entry檔案請求的chunks不應該超過此值(請求過多,耗時)
  • maxAsyncRequests: 非同步請求的chunks不應該超過此值
  • automaticNameDelimiter: 自動命名連線符
  • chunks: 值為”initial”, “async”(預設) 或 “all”:
  • initial: 入口chunk,對於非同步匯入的檔案不處理
  • async: 非同步chunk,只對非同步匯入的檔案處理
  • all: 全部chunk

你的點贊是我持續分享好東西的動力,歡迎點贊!

歡迎加入前端大家庭,裡面會經常分享一些技術資源。

圖片描述

相關文章