最新React全家桶實戰使用配置指南
這篇文件 是呂小明老師結合以往的專案經驗 加上自己本身對react webpack redux理解寫下的總結文件,總共耗時一週總結下來的,希望能對讀者能夠有收穫, 我是在這基礎多些加工!
目錄
12.如何理解entry point(bundle),chunk,module
14.模組熱替換(Hot Module Replacement)
版本說明
由於構建相關例如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
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 中的其中一種,其他比較常見的還有 JSLint 跟 JSHint,之所以用 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
});
});
};
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
你的點贊是我持續分享好東西的動力,歡迎點贊!
歡迎加入前端大家庭,裡面會經常分享一些技術資源。