最新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上個月自己花了 1300 買了a li的伺服器來學習 node 及對應的架構,在 13 号之前它們有做活動,1300 的配置現在一年隻要 86 元,真心覺得很劃算了,
可以此連結進行了解:
版本說明
由于建構相關例如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 [email protected] --save
or
npm install [email protected] --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例子:
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一起使用:
react-hot-loadernpm 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更多
react-router-domnpm install react-router-dom --save
2.如果項目中用了redux,可以安裝
react-router-reduxnpm install react-router-redux@next history --save
3.修改代碼:
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-generatornpm 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-runtimenpm 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等等。
Prefetching/Preloading moduleswebpack 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
編輯中可能存在的bug沒法實時知道,事後為了解決這些bug,花了大量的時間進行log 調試,這邊順便給大家推薦一個好用的BUG監控工具
Fundebug交流
幹貨系列文章彙總如下,覺得不錯點個Star,歡迎 加群 互相學習。
https://github.com/qq449245884/xiaozhi
我是小智,公衆号「大遷世界」作者,對前端技術保持學習愛好者。我會經常分享自己所學所看的幹貨,在進階的路上,共勉!
關注公衆号,背景回複福利,即可看到福利,你懂的。