[譯] 如何利用 Webpack4 提升你的 React.js 開發效率

圖片來源: www.instagram.com/p/BiaH379hr…
在現實生活的開發中,我們經常需要對新功能進行快速迭代。在本教程中,我將向你展示一些你可以採取的措施,以提升大約 20% 的開發速度。
為什麼要這樣,你可能會問?
因為在程式設計時進行人工操作往往會非常適得其反,我們希望儘可能將流程自動化。因此,我將向你展示使用 Webpack v4.6.0 提升 React 的開發過程中的哪些部分。
我不會介紹如何初始化配置 webpack,因為我已經在 之前的帖子 裡講過它。在那篇文章裡,我詳細介紹瞭如何配置 Webpack。我假設在閱讀本文之前你已經熟悉 Webpack 配置的基礎知識,這樣我們就可以從準備好了基本配置之後開始。
配置 Webpack
在你的 webpack.config.js
檔案中,新增以下程式碼:
// webpack v4 const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const WebpackMd5Hash = require('webpack-md5-hash'); const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { entry: { main: './src/index.js' }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].[chunkhash].js' }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: "babel-loader" } } ] }, plugins: [ new CleanWebpackPlugin('dist', {} ), new HtmlWebpackPlugin({ inject: false, hash: true, template: './src/index.html', filename: 'index.html' }), new WebpackMd5Hash() ] }; 複製程式碼
並在你的 package.json
檔案中新增這些依賴:
{ "name": "post", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "build": "webpack --mode production", "dev": "webpack --mode development" }, "author": "", "license": "ISC", "devDependencies": { "babel-cli": "^6.26.0", "babel-core": "^6.26.0", "babel-loader": "^7.1.4", "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.24.1", "babel-runtime": "^6.26.0", "clean-webpack-plugin": "^0.1.19", "html-webpack-plugin": "^3.2.0", "react": "^16.3.2", "react-dom": "^16.3.2", "webpack": "^4.6.0", "webpack-cli": "^2.0.13", "webpack-md5-hash": "0.0.6" } } 複製程式碼
接下來你可以安裝你的專案所需依賴:
npm i 複製程式碼
並將 index.html
和 index.js
兩個檔案新增進專案的 src/
目錄下
首先在 src/index.html
檔案中新增如下程式碼:
<html> <head> </head> <body> <div id="app"></div> <script src="<%= htmlWebpackPlugin.files.chunks.main.entry %>"></script> </body> </html> 複製程式碼
接著在 src/index.js
中新增:
console.log("hello, world"); 複製程式碼
執行 dev 指令碼:
npm run dev 複製程式碼
接下來你就會發現:專案完成編譯了!現在讓我們繼續為它配置 React。
配置 React 專案
由於 React 使用了名為 JSX 的特殊語法,我們需要轉換程式碼。如果我們去 babel 的官網,就可以看到它為我們提供了React 的 preset.
npm install --save-dev babel-cli babel-preset-react 複製程式碼
我們的 .babelrc
檔案應該長這樣:
{ "presets": ["env", "react"] } 複製程式碼
在你的 index.js
檔案中新增一些專案的初始化程式碼:
import React from 'react'; import { render } from 'react-dom'; class App extends React.Component { render() { return ( <div> 'Hello world!' </div> ); } } render(<App />, document.getElementById('app')); 複製程式碼
接著執行 dev 指令碼:
npm run dev 複製程式碼
如果在你的 ./dist
目錄下能夠看到一個 index.html
檔案和一個帶有 hash 值的 main.js
檔案, 那麼說明你做得很棒!專案完成了編譯!
配置 web-dev-server
嚴格來說,我們並不是必須要使用它,因為社群裡有很多為前端服務的 node.js 服務端程式。但我之所以建議使用 webpack-dev-server 因為本就是為 Webpack 而設計的,它支援一些很好的功能,如 熱模組替換 、**Source Maps(原始檔對映)**等。
正如他們在 官方文件 中提到的那樣:
使用webpack 和後端開發服務配合能夠實現 live reloading(熱重啟),但這隻應當被用於開發環境下。
這可能會讓人感到有些困惑:怎樣使 webpack-dev-server 僅在開發模式下生效?
npm i webpack-dev-server --save-dev 複製程式碼
在你的 package.json
檔案中,調整:
"scripts": { "dev": "webpack-dev-server --mode development --open", "build": "webpack --mode production" } 複製程式碼
現在它應該能夠啟動一個本地伺服器並使用你的應用程式自動開啟瀏覽器選項卡。
你的 package.json
現在看起來應該像這樣:
{ “name”: “post”, “version”: “1.0.0”, “description”: “”, “main”: “index.js”, “scripts”: { "dev": "webpack-dev-server --mode development --open", "build": "webpack --mode production" }, “author”: “”, “license”: “ISC”, “devDependencies”: { “babel-cli”: “6.26.0”, “babel-core”: “6.26.0”, “babel-loader”: “7.1.4”, “babel-preset-env”: “1.6.1”, “babel-preset-react”: “6.24.1”, “babel-runtime”: “6.26.0”, “clean-webpack-plugin”: “0.1.19”, “html-webpack-plugin”: “3.2.0”, “react”: “16.3.2”, “react-dom”: “16.3.2”, “webpack”: “4.6.0”, “webpack-cli”: “2.0.13”, “webpack-dev-server”: “3.1.3”, “webpack-md5-hash”: “0.0.6” } } 複製程式碼
現在,如果你嘗試修改應用中的某些程式碼,瀏覽器就會自動重新整理頁面。

接下來,你需要將 React devtools 新增到 Chrome 擴充套件程式。

這樣,你就可以更輕鬆地使用 Chrome 控制檯除錯應用。
ESLint 配置
我們為什麼需要它?好吧,通常來講我們不是必須使用它,但 ESLint 是一個方便的工具。在我們的例子中,它將呈現並突出顯示(在編輯器和終端中以及在瀏覽器上)我們程式碼中的錯誤,包括拼寫錯誤等等(如果有的話),這稱為 linting 。
ESLint 是一個開源的 JavaScript linting 實用程式,最初由 Nicholas C. Zakas 於 2013 年 6 月開發完成。它有其替代品,但到目前為止,它與 ES6 和 React 配合使用效果特別好,能夠發現常見問題,並能與專案的生態系統其他部分整合。
現在,讓我們在本地為我們自己的新專案安裝它。當然,此時 ESLint 會有很多設定。你可以在官方網站閱讀更多相關資訊。
npm install eslint --save-dev ./node_modules/.bin/eslint --init 複製程式碼
最後一個命令將建立一個配置檔案。系統將提示你選擇以下三個選項:

在本教程中,我選擇了第一個:回答問題。以下是我的答案:

這會將一個 .eslintrc.js
檔案新增到專案目錄中。我生成的檔案如下所示:
module.exports = { "env": { "browser": true, "commonjs": true, "es6": true }, "extends": "eslint:recommended", "parserOptions": { "ecmaFeatures": { "experimentalObjectRestSpread": true, "jsx": true }, "sourceType": "module" }, "plugins": [ "react" ], "rules": { "indent": [ "error", 4 ], "linebreak-style": [ "error", "unix" ], "quotes": [ "error", "single" ], "semi": [ "error", "always" ] } }; 複製程式碼
到目前為止什麼都沒發生。雖然這是一個完全有效的配置,但這還不夠,我們必須將它與 Webpack 和我們的文字編輯器整合才能工作。正如我所提到的,我們可以在程式碼編輯器、終端(作為 linter)或 git 的 precommit 鉤子中使用它。我們現在將為我們的編輯器配置它:
Visual Studio Code 中安裝
如果你想要,幾乎每個常用的程式碼編輯器都有 ESLint 外掛,包括 Visual Studio Code、Visual Studio、SublimeText、Atom、WebStorm 甚至是 vim 。所以,下載你自己的文字編輯器的對應版本。在本次示例中我會使用 VS Code 。

現在我們可以看到出現了一些程式碼錯誤提示。這是因為專案有一個 Lint 配置檔案,它會在沒有遵守某些規則時標記程式碼並提示警告。

你可以通過檢查錯誤訊息手動除錯它,或者你可以使用它只需執行儲存便自動修復問題的功能。

你現在也可以調整 ESLint 設定:
module.exports = { "env": { "browser": true, "commonjs": true, "es6": true }, "extends": ["eslint:recommended", "plugin:react/recommended"], "parserOptions": { "ecmaFeatures": { "experimentalObjectRestSpread": true, "jsx": true }, "sourceType": "module" }, "plugins": [ "react" ], "rules": { "indent": [ "error", 2 ], "linebreak-style": [ "error", "unix" ], "quotes": [ "warn", "single" ], "semi": [ "error", "always" ] } }; 複製程式碼
更改了配置之後,如果你錯誤地使用了雙引號而不是單引號,ESLint 不會中斷構建。它還將為 JSX 新增一些檢查。
新增 Prettier

Prettier 是當今最流行的格式化程式之一,它已被編碼社群廣泛使用。它可以新增到 ESLint、你的編輯器,也可以被掛載在 git 的 pre-commit 鉤子上。

我會在這裡將它安裝到我的 VS Code 中
安裝後,你可以嘗試再次檢查程式碼。如果我們寫一些奇怪的縮排並執行儲存,它應該會自動格式化程式碼。

但這還不夠。為了使其與 ESLint 同步工作並且不會兩次發出相同的錯誤,甚至發生規則衝突,你需要將它與 ESLint 整合。
npm i --save-dev prettier eslint-plugin-prettier 複製程式碼
在官方文件中,他們建議你使用 yarn,但 npm 現在同樣也能安裝。在你的 .eslintrc.json
檔案中新增:
... sourceType: "module" }, plugins: ["react", "prettier"], extends: ["eslint:recommended", "plugin:react/recommended"], rules: { indent: ["error", 2], "linebreak-style": ["error", "unix"], quotes: ["warn", "single"], semi: ["error", "always"], "prettier/prettier": "error" } ... 複製程式碼
現在我們想擴充套件我們的 ESLint 規則以包含 prettier 的規則:
npm i --save-dev eslint-config-prettier 複製程式碼
併為你的 eslint 配置新增一些 extends:
... extends: [ "eslint:recommended", "plugin:react/recommended", "prettier", "plugin:prettier/recommended" ] ... 複製程式碼

讓我們為它新增更多配置。為了避免預設的 Prettier 規則和你的 ESLint 規則之間的不匹配,你應該像我現在這樣做:

Prettier 借用了 ESLint 的override 格式,這允許你將配置應用於特定的檔案。
你現在可以以 .js
檔案的形式為其建立配置檔案。
nano prettier.config.js 複製程式碼
現在,貼上到該檔案中:
module.exports = { printWidth: 80, tabWidth: 2, semi: true, singleQuote: true, bracketSpacing: true }; 複製程式碼

現在,當你執行儲存時,你會看到程式碼自動格式化。那不是很漂亮(prettier)嗎?雙關語很有意思。
我的 package.json
檔案現在看起來是這樣:
{ "name": "post", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "build": "webpack --mode production", "dev": "webpack-dev-server --mode development --open" }, "author": "", "license": "ISC", "devDependencies": { "babel-cli": "^6.26.0", "babel-core": "^6.26.0", "babel-loader": "^7.1.4", "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.24.1", "babel-runtime": "^6.26.0", "clean-webpack-plugin": "^0.1.19", "eslint": "^4.19.1", "eslint-config-prettier": "^2.9.0", "eslint-plugin-prettier": "^2.6.0", "eslint-plugin-react": "^7.7.0", "html-webpack-plugin": "^3.2.0", "prettier": "^1.12.1", "react": "^16.3.2", "react-dom": "^16.3.2", "webpack": "^4.6.0", "webpack-cli": "^2.0.13", "webpack-dev-server": "^3.1.4", "webpack-md5-hash": "0.0.6" } } 複製程式碼
現在我們已經完成了很多工作,讓我們快速回顧一下:ESLint 會監視程式碼中的錯誤,而 Prettier 是一種樣式格式化工具。ESLint 有許多方法可以捕獲錯誤,而 Prettier 可以很好地格式化你的程式碼。
// webpack v4 const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const WebpackMd5Hash = require('webpack-md5-hash'); const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { entry: { main: './src/index.js' }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].[chunkhash].js' }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: "babel-loader" } } ] }, plugins: [ new CleanWebpackPlugin('dist', {} ), new HtmlWebpackPlugin({ inject: false, hash: true, template: './src/index.html', filename: 'index.html' }), new WebpackMd5Hash() ] }; 複製程式碼
問題:Prettier 不會自動格式化 Visual Studio Code 中的程式碼
有些人指出 VS Code 無法使用 Prettier。
如果你的 Prettier 外掛在儲存時沒有自動格式化程式碼,你可以通過將下面的程式碼新增到 VS Code 設定來修復它:
"[javascript]": { "editor.formatOnSave": true } 複製程式碼
問題描述在 這裡 。
新增 ESLint loader 到你的 pipeline 中
由於 ESLint 是在專案中配置的,因此一旦執行 dev 伺服器,它也會在終端中提示警告。

特別提示:儘管可以這樣做,但此時我不建議將 ESLint 用作 Webpack 的 loader。它將破壞 source map 的生成,我在我的前一篇文章 《如何解決 Webpack 中的問題 —— 一些實際案例》 中有更詳細的描述。我將展示如何在這裡設定它,以防這些人已經修復了他們的錯誤。
Webpack 有它自己的ESLint loader.
npm install eslint-loader --save-dev 複製程式碼
你必須將 ESLint 新增到 rules 配置中。當使用了使用了編譯類的 loader(如 babel-loader)時,請確保它們的執行順序正確(從下到上)。否則,Webpack 將檢查檔案經過 babel-loader 編譯後的檔案。
... module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: [{ loader: "babel-loader" }, { loader: "eslint-loader" }] } ] }, ... 複製程式碼

以下是你可能遇到的一些問題:
- 將未使用的變數新增到 index 檔案中

如果你偶然發現了這個錯誤(no-unused-vars),那麼在 GitHub 和 這裡 的 這個 issue 中很好地解釋了這個錯誤。
我們可以通過新增一些規則來解決這個問題, 這裡 和 這裡 都有解答。
你可能已經注意到,這裡會出現no-unused-vars 錯誤,你需要將其設為警告而不是錯誤,因為這樣可以更輕鬆地進行快速開發。你需要向 ESLint 新增新規則,以便不會收到預設錯誤。
你可以在此處和此處更詳細地瞭解這個配置。
... semi: ['error', 'always'], 'no-unused-vars': [ 'warn', { vars: 'all', args: 'none', ignoreRestSiblings: false } ], 'prettier/prettier': 'error' } ... 複製程式碼
這樣我們就會得到漂亮的錯誤和警告資訊。
我喜歡使用自動修復功能,但我們必須明確一點:我並不是特別想讓事情神奇地改變。為了避免這種情況,我們現在可以提交 autofix。
Pre commit 鉤子
在使用 Git 工具時,人們都會非常小心。但我向你保證,這個東西非常簡單而且直截了當。掛載了 Prettier 的 Pre commit 鉤子之後,團隊在每個專案檔案中將有一致的程式碼風格,並且沒有人可以提交不規範的程式碼。要為你的專案設定 Git 整合,如下所示:
git init git add . nano .gitignore (add your node_modules there) git commit -m "First commit" git remote add origin your origin git push -u origin master 複製程式碼
這裡有一些關於git 鉤子和使用 Prettier 的精彩文章。
對於那些說你只能在本地做這些操作的人說:不,那不是真的!
你可以使用Andrey Okonetchnikov 開源的 lint-staged 工具執行此操作。
新增 propTypes
讓我們在我們的應用程式中建立一個新元件。到目前為止,我們的 index.js
看起來像這樣:
import React from 'react'; import { render } from 'react-dom'; class App extends React.Component { render() { return <div>Hello</div>; } } render(<App />, document.getElementById('app')); 複製程式碼
我們將建立一個名為 Hello.js 的新元件用於演示。
import React from 'react'; class Hello extends React.Component { render() { return <div>{this.props.hello}</div>; } } export default Hello; 複製程式碼
現在在 index.js
檔案中引入:
import React from 'react'; import { render } from 'react-dom'; import Hello from './Hello'; class App extends React.Component { render() { return ( <div> <Hello hello={'Hello, world! And the people of the world!'} /> </div> ); } } render(<App />, document.getElementById('app')); 複製程式碼
我們應該看到這個元素,但 ESLint 提示警告:

Error: [eslint] ‘hello’ is missing in props validation (react/prop-types)
在 React v16 中,必須新增prop 型別以避免型別混淆。你可以在這裡閱讀更多相關資訊。
import React from 'react'; import PropTypes from 'prop-types'; class Hello extends React.Component { render() { return <div>{this.props.hello}</div>; } } Hello.propTypes = { hello: PropTypes.string }; export default Hello; 複製程式碼

熱模組替換
現在你已經檢查了程式碼,現在是時候向 React 應用新增更多元件了。到目前為止,你只有兩個,但在大多數情況下,你會有幾十個。
當然,每次更改專案中的某些內容時,重新編譯整個應用程式都不是一種好的選擇,你需要一種更快的方法來優化它。
所以讓我們新增熱模組替換,即 HMR。在文件中,它被描述為:
熱模組更換(HMR)在應用程式執行時變更、新增或刪除模組無需完全重新載入。可以通過以下幾種方式顯著提升開發速度:
保留在完全重新載入期間丟失的應用程式狀態。
只更新已變更的內容,即可節省寶貴的開發時間。
更快地調整樣式 —— 幾乎可以與在瀏覽器控制檯中進行樣式更改相媲美。
我不會在這裡討論它的工作原理:這足夠寫成一篇單獨的文章,但我會告訴你該如何配置它:
... output: { path: path.resolve(__dirname, 'dist'), filename: '[name].[chunkhash].js' }, devServer: { contentBase: './dist', hot: true }, module: { rules: [ ... 複製程式碼
解決 HMR 的小問題

我們必須使用 hash 來替換 chunkhash,因為很明顯 webpack 已經修復了自上次以來的問題,現在我們終於讓熱模組替換開始正常工作了!
... module.exports = { entry: { main: './src/index.js' }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].[hash].js' }, devServer: { contentBase: './dist', ... 複製程式碼
解決 bugs
如果我們在這裡執行 dev 指令碼:

然後使用 這個 issue 提到的方案來解決它:
接下來,在 package.json
中新增 --hot 引數到 dev 指令碼中:
... "scripts": { "build": "webpack --mode production", "dev": "webpack-dev-server --hot" } ... 複製程式碼
Source maps:
我之前提到過, source maps 不能和 ESLint loader 一起使用 ,我在 這裡 提了一個 issue。
通常,你無論如何都不希望它們出現在你的專案中(因為你想從 ESLint 錯誤訊息中除錯專案),總所周知,他們會使 HMR 變慢。

如果你希望生成 source maps,最簡單的方法就是通過devtools 選項。
... module.exports = { entry: { main: './src/index.js' }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].[hash].js' }, devtool: 'inline-source-map', devServer: { contentBase: './dist', hot: true }, ... 複製程式碼
注意:你必須以正確的方式配置環境,否則 source maps 將不起作用。你可以在這裡閱讀我的除錯過程。下面我將為你梳理一個流程並解釋我如何解決該問題。
如果我們現在在程式碼中建立一個錯誤,它將顯示在控制檯中並指向正確的位置:

但現實好像不太盡人意…

這是錯誤的做法
你需要更改環境變數,如下所示:
... "main": "index.js", "scripts": { "build": "webpack --mode=production", "start": "NODE_ENV=development webpack-dev-server --mode=development --hot" }, "author": "" ... 複製程式碼
webpack.config.js
... devtool: 'inline-source-map', devServer: { contentBase: './dist', open: true } ... 複製程式碼
現在它就有效了!

如你所見,我們得到了發生錯誤的確切檔案!
現在專案的開發環境已經搭建成功!
讓我們回顧一下:
- 我們配置了 webpack
- 我們建立了第一個 React 元件
- 我們引入 ESLint 來檢查程式碼是否存在錯誤
- 我們配置了熱模組替換
- 我們(可能)添加了 source maps 功能
特別提醒:由於許多 npm 依賴項可能會在你閱讀此內容時發生更改,因此相同的配置可能對你無效。我懇請你將錯誤留在下面的評論中,以便我以後編輯。
如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。
掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為掘金 上的英文分享文章。內容覆蓋 Android 、 iOS 、 前端 、 後端 、 區塊鏈 、 產品 、 設計 、 人工智慧 等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃 、官方微博、 知乎專欄 。