咱們的目標是利用webpack搭建一個基於react + react-router +dva + es6 + less + antd用於中後臺開發的腳手架,同窗們可能會說社區裏那麼多優秀的腳手架爲何還要本身搭,並且網絡上這類文章也很是的多,沒有再重複寫的必要,可是對我而言,分享也是再次學習的過程,只有本身動手實現一遍纔會使印象更加深入,總的來講基本秉持一個理念:在學習中分享,在分享中學習。 javascript
mkdir React-Whole-barrels // 創建項目文件夾,名稱隨意
cd React-Whole-barrels
mkdir src // 新建src文件夾
npm init -y //初始化項目
複製代碼
新建index.js和index.html文件:css
// index.js
console.log('hello world')
// index.html
<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8"> <title>React-Whole-barrels</title> </head> <body> <div id='root'></div> </body> </html> 複製代碼
安裝webpackhtml
npm add webpack webpack-cli --save-dev
複製代碼
新建一個build文件夾存放wbepack配置文件java
mkdir build
touch webpack.dev.js
複製代碼
webpack.dev.js,並書寫基本的配置node
const path = require('path');
module.exports = {
mode: 'development', // 模式,表示dev環境
entry: './src/index.js', // 入口文件
module: {}, // 讓 webpack 可以去處理那些非 JavaScript 文件
plugins: [], // 插件
output: {
filename: 'bundle.js', // 打包後文件名稱
path: path.resolve(__dirname, '../dist') // 打包後文件夾存放路徑
}
}
複製代碼
package.json更改react
"scripts": {
"start": "webpack --config ./build/webpack.dev.js"
},
複製代碼
既然要使用react和es6,babel的配置是必不可少的webpack
npm install @babel/polyfill core-js@2 --save
// @babel/polyfill: 模擬一個es6+的環境,提供es6方法和函數的墊片
// core-js@2:@babel/preset-env實現按需引入polyfill時,聲明core-js版本
npm install babel-loader @babel/core @babel/preset-env @babel/preset-react --save-dev
// babel-loader和@babel/core是核心模塊
// @babel/preset-env是一個智能預設,容許您使用最新的JavaScript
// @babel/preset-react 轉換JSX
複製代碼
擴展:若是是開發工具庫,想要實現按需替換,可使用下面下面兩個工具來實現 @babel/plugin-transform-runtime避免 polyfill 污染全局變量,減少打包體積,所以更適合做爲開發工具庫 @babel/runtime-corejs2做爲生產環境依賴,約等於@babel/runtime + babel-polyfill,使用了@babel/runtime-corejs2,就無需再使用@babel/runtime了git
.babelrc文件es6
{
"presets": [
[
"@babel/preset-env", { // 將es6的語法翻譯成es5語法
"targets": {
"chrome": "67",
},
"useBuiltIns": "usage", // 作@babel/polyfill補充時,按需補充,用到什麼才補充什麼,
"corejs": "2",
}
],
"@babel/preset-react",
],
"plugins": [
"@babel/plugin-proposal-class-properties"
]
}
複製代碼
1.解決:Support for the experimental syntax 'classProperties' isn't currently enabled 問題, npm i -D @babel/plugin-proposal-class-properties,並在plugins中配置。 2.useBuiltIns 和 transform-runtime 不能同時使用,若是使用transform-runtime就不要配useBuiltInsor,通常獨立的類庫項目才用transform-runtime,出處github
更改webpack.dev.js
...
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/, // 排除node_modules中的代碼
use: [{
loader: 'babel-loader'
}],
}]
},
複製代碼
安裝react、react-dom、react-router,並書寫代碼
npm install react react-dom react-router react-router-dom --save
複製代碼
書寫完成上述代碼運行npm start後,打開index.html你會發現沒有任何內容,此時咱們須要配置一個簡單的WEB服務器,指向index.html。
// 安裝webpack-dev-server
npm install webpack-dev-server --save-dev
// 配置webpack.dev.js
...
devServer: {
contentBase: path.join(__dirname, '../dist')
},
...
// 更改npm start 命令
"start": "webpack-dev-server --config ./build/webpack.dev.js"
複製代碼
運行npm start命令,既能夠看到咱們的代碼內容。dev更多配置請看這裏
到目前爲止,咱們會發現都須要手動都將index.html放到dist文件夾中,並手動引入bundle.js.這個問題能夠經過html-webpack-plugin解決。
引入html-webpack-plugin後,在plugins生成一個實例,HtmlWebpackPlugin能夠接受一個參數做爲模版文件,打包結束後自動生成一個以參數爲模版的html文件。並把打包生成的js文件自動引入到html文件中。clean-webpack-plugin能夠實如今每次打包以前都把上一次的打包文件清空,這樣避免了冗餘文件的存在,用法也是直接在plugins裏面生成一個實例.
// 安裝html-webpack-plugin和clean-webpack-plugin
npm install html-webpack-plugin clean-webpack-plugin --save-dev
複製代碼
更改webpack.dev.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
...
plugins: [ // 插件
new HtmlWebpackPlugin({ // 向dist文件中自動添加模版html
template: 'src/index.html',
}),
new CleanWebpackPlugin(), // 打包後先清除dist文件,先於HtmlWebpackPlugin運行
],
複製代碼
樣式使用less預處理器,那麼就須要使用less,less-loader,css-loader,style-loader等
npm install less less-loader css-loader style-loader postcss-loader --save-dev
// less-loader 編譯less
// css-loader // 編譯css
// style-loader建立style標籤,並將css添加進去
// postcss-loader提供自動添加廠商前綴的功能,可是須要配合autoprefixer插件來使用
npm install autoprefixer --save-dev
複製代碼
更改webpack.dev.js配置
rules: [
...
{
test: /\.less$/,
exclude: /node_modules/,
use: ['style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
}
}, 'less-loader', 'postcss-loader']
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
}
]
複製代碼
postcss.config.js
module.exports = { // 自動添加css廠商前綴
plugins: [
require('autoprefixer')
]
}
複製代碼
安裝file-loader和url-loader
npm install file-loader url-loader --save-dev
複製代碼
filr-loader幫助咱們作兩件事情:
1.當遇到圖片文件時會將其打包移動到dist目錄下
2.接下來會得到圖片模塊的地址,並將地址返回到引入模塊到變量之中
url-loader基本上能夠實現file-loader的功能,可是有一區別就是通過url-laoder打包後的dist文件下是不存在image文件的,這是由於url-loader會把圖片轉換成base64的字符串直接放在bundle.js裏面。
好處:直接將圖片打包到js裏,不用額外到請求圖片,省了http請求
壞處:若是遇到打包到文件很是大,那麼加載會加載很長時間,影響體驗
所以咱們能夠這樣配置webpack.dev.js
rules: [
...
{
test: /\.(png|jpg|gif|jpeg)$/,
use: {
loader: 'url-loader',
options: {
name: '[name]_[hash].[ext]', // placeholder 佔位符
outputPath: 'images/', // 打包文件名
limit: 204800, // 小於200kb則打包到js文件裏,大於則使用file-loader的打包方式打包到imgages裏
},
},
},
{
test: /\.(eot|woff2?|ttf|svg)$/,
use: {
loader: 'url-loader',
options: {
name: '[name]-[hash:5].min.[ext]', // 和上面同理
outputPath: 'fonts/',
limit: 5000,
}
},
}
]
複製代碼
模塊熱替換也稱爲HMR,代碼更新時只會更新被修改部分都顯示。有以下有點
HMR配置有兩種方式,一種cli方式,一種Node.js API方式,咱們這裏採用第二種方式,若是想了解兩種HMR的實現以及HMR實現原理能夠看這裏。
咱們經過在自定義開發服務下,使用插件webpack-dev-middleware和webpack-hot-middleware配合實現HMR
// 安裝webpack-dev-middleware webpack-Hot-middleware
npm install webpack-dev-middleware webpack-hot-middleware --save-dev
// 不要忘記安裝express,咱們是經過express來啓動本地服務
npm install express --save-dev
複製代碼
新建dev-server.js
const path = require('path');
const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require("webpack-hot-middleware")
const config = require('./webpack.dev.js');
const complier = webpack(config); // 編譯器,編譯器執行一次就會從新打包一下代碼
const app = express(); // 生成一個實例
const DIST_DIR = path.resolve(__dirname, '../', 'dist'); // 設置靜態訪問文件路徑
let devMiddleware = webpackDevMiddleware(complier, {
quiet: true,
noInfo: true,
stats: 'minimal'
})
let hotMiddleware = webpackHotMiddleware(complier,{
log: false,
heartbeat: 2000
})
app.use(devMiddleware)
app.use(hotMiddleware)
// 設置訪問靜態文件的路徑
app.use(express.static(DIST_DIR))
app.listen(8080, () => {
console.log("成功啓動:localhost:"+ 8080)
}) //監聽端口
複製代碼
更改webpack.dev.js,添加以下內容:
const webpack = require('webpack');
...
entry: {
//實現刷新瀏覽器webpack-hot-middleware/client?noInfo=true&reload=true 是必填的
main: ['webpack-hot-middleware/client?noInfo=true&reload=true', './src/index.js']
},
...
plugins: [
...
new webpack.NamedModulesPlugin(), //用於啓動HMR時能夠顯示模塊的相對路徑
new webpack.HotModuleReplacementPlugin(), // 開啓模塊熱更新,熱加載和模塊熱更新不一樣,熱加載是整個頁面刷新
]
複製代碼
webpack-hot-middleware更多配置在這裏
修改啓動命令:
"start": "node ./build/dev-server.js"
複製代碼
npm install antd babel-plugin-import --save
複製代碼
安裝antd後便可使用and-design裏面的ui組件。使用babel-plugin-import來實現按需加載的效果
.babelrc
"plugins": [
...
["import", {
"libraryName": "antd",
"libraryDirectory": "es",
"style": "css" // `style: true` 會加載 less 文件
}]
]
複製代碼
npm install dva --save
複製代碼
成功安裝後改寫index.js和並新建router.js
// index.js
import dva from 'dva';
// 1. Initialize
const app = dva({});
// app.use();
// 2. Model
// app.model();
// 3. Router
app.router(require('./router').default);
// 4. Start
app.start('#root');
複製代碼
這裏的代碼量有點多就不一一列出,能夠在github上面查看
在使用react-router的過程當中你可能會出現這樣的問題,點擊刷新後報錯 or Cannot GET,解決方案有兩個。 1.用的 BrowserRouter 改成 HashRouter 便可。2.devServer 中必須設置 historyApiFallback: true 因爲咱們使用的自定義服務,那麼咱們可使用connect-history-api-fallback來實現和historyApiFallback相同的功能。具體實現看代碼
CDN經過將資源部署到世界各地,使得用戶能夠就近訪問資源,加快訪問速度。若是咱們把網頁的靜態資源上傳到CDN服務上,在訪問這些資源時,publicPath填寫的就是CDN提供URL
咱們當前用/,相對於當前路徑,是由於咱們的資源在同一文件夾下。》》
webpack.dev.js
output: {
...
publicPath : '/'
}
複製代碼
sourceMap本質上是一種映射關係,打包出來的js文件中的代碼能夠映射到代碼文件的具體位置,這種映射關係會幫助咱們直接找到在源代碼中的錯誤。能夠直接在devtool中使用.合理的使用source-map能夠幫助咱們提升開發效率,更快的定位到錯誤位置。
生產環境和開發環境的devtool配置是不一樣的。咱們能夠在webpack.dev.js中添加devtool。
devtool:"cheap-module-eval-source-map",// 開發環境配置最佳實踐
devtool:"cheap-module-source-map", // 生產配置最佳實踐
複製代碼
到目前爲止咱們配置的都是開發環境的webpack,開發環境(development)和生產環境(production)的構建目標差別很大,而在生產環境中,咱們的目標則轉向於關注更小的 bundle,更輕量的 source map,以及更優化的資源,以改善加載時間.
新建webpack.prod.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: "production", // 只要在生產模式下, 代碼就會自動壓縮,自動啓用 tree shaking
devtool:"cheap-module-source-map",
entry: { // 入口文件
main: './src/index.js'
},
module: {
rules: ...省略 //代碼和dev中相同
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html',
}),
new CleanWebpackPlugin(),
],
output: {
publicPath: "/",
filename: 'bundle.js',
path: path.resolve(__dirname, '../dist')
}
}
複製代碼
添加打包腳本,
"build": "webpack --config ./build/webpack.prod.js"
複製代碼
執行npm run build後,你會發現dist文件夾下已經生成一系列文件。你會發現生產環境下的配置和開發環境下的配置有不少相同,接下來咱們會對webpack配置進行優化。
webpack.dev.js和webpack.prod.js中有不少相同對配置,咱們能夠將公共配置提取出來,再使用webpack-merge來將不一樣環境下的配置合併起來。
npm install webpack-merge --save
複製代碼
webpack配置文件更改
webpack.dev.js
...
const merge = require('webpack-merge');
const commonConfig = require('./webpack.common.js');
const devConfig = {
mode: 'development',
devtool:"cheap-module-eval-source-map",
entry: {
main: ['webpack-hot-middleware/client?noInfo=true&reload=true', './src/index.js']
},
devServer: {
contentBase: path.join(__dirname, '../dist')
},
plugins: [
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin(),
],
output: {}
}
module.exports = merge.smart(commonConfig, devConfig)
複製代碼
webpack.prod.js
const merge = require('webpack-merge');
const commonConfig = require('./webpack.common.js');
const prodConfig = {
mode: "production", // 只要在生產模式下, 代碼就會自動壓縮
devtool:"cheap-module-source-map",
entry: {
main: './src/index.js'
},
module: {},
plugins: [],
output: {}
}
module.exports = merge.smart(commonConfig, prodConfig)
複製代碼
webpack.common.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const commonConfig = {
module: {
...太多了省略吧
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html',
}),
new CleanWebpackPlugin(),
],
output: {
publicPath: "/",
filename: 'bundle.js',
path: path.resolve(__dirname, '../dist')
}
}
module.exports = commonConfig;
複製代碼
具體的能夠看這裏。
想要分開打包咱們的css文件,須要使用mini-css-extract-plugin這個插件,可是這個插件目前還不支持HMR,爲了避免影響開發效率,所以就在生成環境下使用該插件。
optimize-css-assets-webpack-plugin 這個插件能夠幫助咱們把相同的樣式合併。
css-split-webpack-plugin插件能夠幫咱們把過大的css文件拆分
npm install mini-css-extract-plugin optimize-css-assets-webpack-plugin css-split-webpack-plugin --save-dev
複製代碼
修改webpack.prod.js,並同步修改webpack.common.js、webpack.dev.js 看這裏
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const CSSSplitWebpackPlugin = require('css-split-webpack-plugin').default;
...
module: {
rules: [{
test: /\.less$/,
exclude: /node_modules/,
use: [MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
importLoaders: 2
}
}, 'less-loader', 'postcss-loader']
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader']
}]
},
optimization: {
minimizer: [new OptimizeCSSAssetsPlugin({})]
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[name].chunk.css'
}),
new CSSSplitWebpackPlugin({
size: 4000,
filename: '[name]-[part].[ext]'
})
]
複製代碼
爲了解決瀏覽器文件緩存問題,例如:代碼更新後,文件名稱未改變,瀏覽器非強制刷新後,瀏覽器去請求文件時認爲文件名稱未改變而直接從緩存中讀取不去從新請求。咱們能夠在webpack.prod.js輸出文件名稱中添加hash值.
使用HashedModuleIdsPlugin的緣由是能夠當更改某一個文件時,只改變這一個文件的hash值,而不是全部的文件都改變。
plugins: [
...
new webpack.HashedModuleIdsPlugin(), //根據模塊的相對路徑生成一個四位數的hash
new webpack.optimize.CommonsChunkPlugin({ // 配合上面的插件使用
name: 'runtime'
})
],
output: {
filename: '[name].[contenthash].js', // entry對應的key值
chunkFilename: '[name].[contenthash].js', // 間接引用的文件會走這個配置
},
複製代碼
運行npm run build命令後,會發現dist文件中js文件名中已經有了hash值
記得同步修改webpack.common.js、webpack.dev.js,若是你不知道如何修改,請看這裏
extension配置以後能夠不用在require或是import的時候加文件擴展名,會依次嘗試添加擴展名進行匹配
mainFiles配置後不用加入文件名,會依次嘗試添加的文件名進行匹配
alias經過配置別名能夠加快webpack查找模塊的速度。
webpack.common.js更改:
resolve: {
extensions: ['.js', '.jsx'], // 當經過import login from './login/index'形式引入文件時,會先去尋找.js爲後綴當文件,再去尋找.jsx爲後綴的文件
mainFiles: ['index', 'view'], // 若是是直接引用一個文件夾,那麼回去直接找index開頭的文件,若是不存在再去找view開頭的文件
// alias: { // 暫時沒用到,就註釋掉
// home: path.resolve(__dirname, '../src/home') // 配置別名能夠加快webpack查找模塊的速度
//}
}
複製代碼
Tree Shaking能夠剔除掉一個文件中未被引用掉部分,而且只支持ES Modules模塊的引入方式,不支持CommonJS的引入方式。緣由:ES Modules是靜態引入的方式,CommonJS是動態的引入方式,Tree Shaking只支持靜態引入方式。
// webpack.dev.js
optimization: { // 開發環境時使用
usedExports: true
},
// package.json
"sideEffects": [
"*.css"
],
// 若是在項目中使用相似 css-loader 並 import 一個 CSS 文件,則須要將其添加到 side effect 列表中,以避免在生產模式中無心中將它刪除
複製代碼
注意:mode 選項設置爲 production,能夠自動啓用 minification(代碼壓縮) 和 tree shaking
不論是同步代碼的分割仍是異步的代碼分割均可以使用SplitChunksPlugin這個插件,能夠將第三方庫從業務代碼中分割出來.
webpack.prod.js
...
optimization: {
splitChunks: {
chunks: "all", // 只對異步引入代碼起做用,設置all時並同時配置vendors纔對二者起做用
minSize: 30000, // 引入的庫大於30kb時纔會作代碼分割
minChunks: 1, // 一個模塊至少被用了1次纔會被分割
maxAsyncRequests: 5, // 同時異步加載的模塊數最可能是5個,若是超過5個則不作代碼分割
maxInitialRequests: 3, // 入口文件進行加載時,引入的庫最多分割出3個js文件
automaticNameDelimiter: '~', // 生成文件名的文件連接符
name: true, // 開啓自定義名稱效果
cacheGroups: { // 判斷分割出的代碼放到那裏去
vendors: { // 配合chunks: ‘all’使用,表示若是引入的庫是在node-modules中,那就會把這個庫分割出來並起名爲vendors.js
test: /[\\/]node_modules[\\/]/,
priority: -10,
filename: 'vendors.js'
},
default: { // 爲非node-modules庫中分割出的代碼設置默認存放名稱
priority: -20,
reuseExistingChunk: true, // 避免被重複打包分割
filename: 'common.js'
}
}
}
}
複製代碼
對於第三方庫,這些庫在很長的一段時間內,基本不會更新,打包的時候分開打包來提高打包速度, DllPlugin動態連接庫插件,其原理就是把網頁依賴的基礎模塊抽離出來打包到dll文件中,當須要導入的模塊存在於某個dll中時,這個模塊再也不被打包,而是去dll中獲取。
cnpm install add-asset-html-webpack-plugin fs --save
// add-asset-html-webpack-plugin: 將打包生產的dll.js文件自動引入html
// fs文件讀取
複製代碼
新建webpack.dll.js
const path = require('path');
const webpack = require('webpack');
module.exports = {
mode: 'production',
entry: {
vendors: ['lodash'],
react: ['react', 'react-dom'],
},
output: {
filename: '[name].dll.js',
path: path.resolve(__dirname, '../dll'),
library: '[name]',
},
plugins: [
new webpack.DllPlugin({
name: '[name]',
path: path.resolve(__dirname, '../dll/[name].manifest.json'),
}),
],
};
複製代碼
webpack.common.js更改
const fs = require('fs');
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
...
const plugins = [
new HtmlWebpackPlugin({
template: 'src/index.html',
}),
new CleanWebpackPlugin(),
];
const files = fs.readdirSync(path.resolve(__dirname, '../dll'));
files.forEach((file) => {
if (/.*\.dll.js/.test(file)) {
plugins.push(new AddAssetHtmlWebpackPlugin({ // 將dll.js文件自動引入html
filepath: path.resolve(__dirname, '../dll', file),
}));
}
if (/.*\.manifest.json/.test(file)) {
plugins.push(new webpack.DllReferencePlugin({ // 當打包第三方庫時,會去manifest.json文件中尋找映射關係,若是找到了那麼就直接從全局變量(即打包文件)中拿過來用就行,不用再進行第三方庫的分析,以此優化打包速度
manifest: path.resolve(__dirname, '../dll', file),
}));
}
});
複製代碼
package.json
"dll": "webpack --config ./build/webpack.dll.js"
複製代碼
PWA全稱progressive Web Application,PWA實現的功能是即使服務器掛掉,仍是能夠經過在本地的緩存來訪問到頁面。首先安裝workbox-webpack-plugin插件,而後在webpack.prod.js中Plugins中配置,由於該功能只在線上使用。
npm install workbox-webpack-plugin --save
複製代碼
webpack.prod.js更改
const WorkboxPlugin = require('workbox-webpack-plugin');
...
plugins: [
...
new WorkboxPlugin.GenerateSW({
clientsClaim: true,
skipWaiting: true
})
]
複製代碼
運行命令打包後會多處兩個文件precache-manifest.js和service-worker.js, service-worker這個文件就可讓咱們的頁面被緩存住
能夠經過指定環境,來使webpack進行選擇性編譯,擇性編譯是指根據打包是環境的不一樣,選擇性地讓特定的語句有效,讓特定的語句無效。這樣能夠對具體用戶的環境進行代碼優化,從而刪除或添加一些重要代碼。
最簡單的例子,在開發環境中,咱們打印日誌,但在生產環境中,咱們讓全部打印日誌的語句無效(讓程序不運行打印的語句,甚至讓打包出來的文件根本就不包含打印日誌的語句)
咱們可使用 webpack 內置的 DefinePlugin 來實現。
// webpack.dev.js
...
plugins: [
...
new Webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('development'),
}),
]
// webpack.prod.js
plugins: [
...
new Webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
]
複製代碼
也能夠經過mode:production來指定生產環境,設置mode:"production",webpack默認會設置"process.env.NODE_ENV": JSON.stringify("production")。記得不要重複設置哦!
寫到這裏,這樣一個基本的腳手架也就搭起來了,本文全部代碼React-Whole-barrels都在這裏了。你們能夠跟着代碼進行配置,每一步基本都有commit,若是不清楚能夠看commit。固然本文也有許多不完善的地方,例如mock、eslint、styleLint、ts等等都沒有添加上去,並且也還有許多優化點,因爲篇幅關係我也就不一一寫了,後續我會加上以及不斷完善優化該項目,感興趣的同窗能夠持續關注哦!但願能夠經過本文讓您對webpack工具備一個更加深刻和全面的認識,以便於應對之後項目中配置更改。若是要使用項目框架的話當熱是推薦creat-react-app或者umi了。本文若是有錯誤的地方,您發現了,歡迎告訴我!!謝謝!!若是以爲對您有幫助,歡迎給我一個star! ! 謝謝!!