深刻淺出 Create React App

本文差點難產而死。由於總結的過程當中,屢次懷疑本文是對官方文檔的直接翻譯和簡單諾列;同時官方文檔很全面,全範圍的介紹無疑加深了寫做的心智負擔。但在最終的梳理中,發現走出了一條不同凡響的路,因而堅持分享出來。javascript

但願本文除了能帶領咱們再次瞭解 Create React App(後文簡稱 CRA) 外,還能提供一種不一樣的知識組織結構和技術視角,加深咱們對整個 React 技術生態的理解。css

本文多是多篇博客的綜合體,整理和寫做時間 15h+,仔細閱讀時間 30min+,請慢用。

本文面向的讀者是:html

  • 前端開發初學者或 React 初學者;
  • 使用過 CRA 搭建 React 項目但想拓展相關知識面的人;
  • 但願經過一篇文章快速複習 CRA 的人;
  • 英文初學者,想要經過一篇中文技術文章來讓本身接下來讀英文文檔再也不困難的人;
  • 以及就想點進來支持一下的人。

其次,本文在對官方文檔進行必定的從新編排下,加上了以下創新點以完善總體的閱讀學習體驗:前端

  • 添加了實戰 1:使用單個 HTML 文件構建 React App;
  • 添加了實戰 2:使用 Webpack 手動構建 React App;
  • 添加了實戰 3:使用 CRA 一站式構建 React App;
  • 添加了實戰 4:使用 Source Map Explorer 分析打包文件;
  • 添加了實戰 5:在已有的 React 項目中引入/升級 CRA;
  • 添加了實戰 6:使用 React App Rewired 注入新配置;
  • 添加了:對 CRA 將來版本的簡單展望;
  • 添加了:一個 Dan 十年回顧文章的導讀。

最終,本文不涉及源碼的解讀,想要閱讀源碼的同窗能夠移步官方源碼倉庫,總體設計思路並非很難,具體實現原理能夠細細品嚼;且本文對與 CRA 不直接相關的技術點會略略而過,歡迎從點到面主動學習更多。如下是官方源碼倉庫以及官方文檔地址:java

初始化 React App 的多種方式

常見的初始化 React App 的方式有:node

  • 不使用構建工具構建 React App;
  • 使用 Webpack 手動構建 React App;
  • 使用 Create React App 一站式構建 React App;
  • 在在線沙箱平臺直接構建 React App(通常用於 Demo 預演,本文不涉及)。

下面咱們分別進行介紹與實戰練習。react

實戰 1:使用單個 HTML 文件構建 React App

React 自己專一於構建用戶界面,並不依賴於某個構建工具,所以咱們能夠用傳統的方式引入 React 並書寫第一個「Hello World!」 App。這種方式是快速嘗試 React 的好方法,但並不適用於正式開發。webpack

如下 HTML 代碼段是一種實現方式,使用了可選的 Babel 編譯和 JSX 語法,基於非構建工具的更多初始化頁面的方法(如不使用 JSX 等)能夠自行探索。git

<html>
  <head>
    <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> <!-- 不須要用於生產環境 -->
  </head>
  <body>
    <div id="root"></div>
    <script type="text/babel">
      ReactDOM.render(
        <h1>Hello, world!</h1>,
        document.getElementById('root')
      )
    </script>
  </body>
</html>

實戰 2:使用 Webpack 手動構建 React App

構建工具備不少種,目前最爲主流的構建工具當屬 Webpack。如何使用 Webpack 逐步構建 React App?github

果不其然,爲了證實 CRA 的便捷性而引出的本節 Webpack 實戰,耗費了一小時多的時間進行了親自踩坑,搜索了較多的博文都因爲發佈時間性而不能和最新的版本進行融合,最終根據 Github 中 react-webpack-babel 庫的 package.json 文件裏的相關信息才獲得實現。
# 建立一個項目並進入該項目
$ mkdir react-webpack-steper & cd react-webpack-steper
# 使用默認選項直接生成一個初始化的 package.json
$ npm init -y
# 安裝 React 基礎包
$ sudo npm install --save react react-dom
# 安裝 Webpack 相關工具 - 打包、本地啓動支持、本地異步請求模擬以及熱更新等
$ sudo npm install --save webpack webpack-cli webpack-dev-server
# 安裝 Babel 相關工具 - 提供 ES6+ 新功能支持
$ sudo npm install --save-dev @babel/cli @babel-core @babel/preset-env @babel/preset-react
$ sudo npm install --save-dev babel-loader babel-plugin-module-resolver html-webpack-plugin
# 新建打包、編譯配置文件並準備編寫
$ touch webpack.config.js
$ touch .babelrc
# 新建 React 文件
$ mkdir src
$ touch src/index.js
$ touch src/index.html

其中,webpack.config.js 源碼以下:

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: path.resolve(__dirname, './src/index.js'),
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader'
      },
    ]
  },
  resolve: {
    extensions: ['*', '.js', '.jsx']
  },
  output: {
    path: path.resolve(__dirname, './build'),
    filename: 'bundle.js',
    publicPath: '/'
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, './src/index.html')
    })
  ]
}

.babelrc 源碼以下:

{
  "presets": ["@babel/preset-env", "@babel/preset-react"],
  "plugins": [
    ["module-resolver", {
      "root": ["./src"]
    }]
  ]
}

src/index.html 源碼以下:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Hello World!</title>
</head>
<body>
  <div id="root">Loading...</div>
</body>
</html>

src/index.js 源碼以下:

import React from 'react'
import ReactDOM from 'react-dom'

ReactDOM.render(
  <h1>Hello, world!</h1>,
  document.getElementById('root')
)

此時,一個基於 Webpack 手動搭建的簡易型「Hello World」App 開發完成,能夠經過以下命令本地運行。

$ webpack-dev-server --mode development --open --hot

更多自定義內容如添加 devServer 支持、添加多頁應用支持...等各類各樣新技術棧的支持,也能夠引伸實戰。

所以,咱們須要 CRA

可見,不使用構建工具編寫不切實際,使用構建工具手動搭建 React App 又很繁瑣。所以咱們須要一個能初始化一個可直接運行項目的工具,並提供各類簡易的插件,Create React App 應運而生。

CRA 適用於中小型 React 項目。

CRA 的設計哲學

  • 一種依賴關係。儘管 CRA 使用了 Webpack,Babel,ESLint 等各類出色的項目,但咱們只須要 CRA 一種依賴;從 CRA 生成項目的 package.json 中也能夠看到並無 Webpack、Babel 的痕跡。
  • 無需配置。咱們無需進行任何額外配置即可以直接運行代碼,專一業務開發;同時 CRA 還提供了開發和生產版本的合理配置。
  • 非鎖定配置。只須要運行一個命令, 全部的配置項和構建依賴項都將直接「彈出」到項目中,交由咱們來修改。

CRA 包含了什麼?

CRA 將具備構建現代單頁 React 應用所需的一切:

  • React,JSX,ES6,TypeScript 和 Flow 語法支持;
  • ES6+ 標準的支持,例如對象傳播運算符;
  • CSS 自動添加前綴的支持,所以咱們不須要 -webkit- 或其餘前綴;
  • 快速的交互式單元測試運行程序,內置對覆蓋率報告的支持;
  • 實時開發服務器,警告常見錯誤;
  • 一個構建腳本,用於將 JS,CSS 和圖像與哈希和源映射捆綁在一塊兒進行生產;
  • 知足全部漸進式 Web 應用程序標準的 ServiceWorkerWeb 應用程序清單 支持 (注意:從react-scripts@2.0.0及更高版本開始支持 ServiceWorker);
  • 單一依賴項便可輕鬆更新上述工具。

CRA 的使用場景

Create React App 很是適合:

  • 在方便且功能豐富的開發環境中學習 React;
  • 快速啓動新的單頁 React 應用程序;
  • 快速使用 React 爲庫和組件建立示例。

若是咱們想在沒有數百個傳遞構建工具依賴的狀況下嘗試 React,請考慮使用單個 HTML 文件構建或使用在線沙箱平臺構建。

  • 若是須要將 React 代碼與服務器端模板框架(如 Rails,Django 或 Symfony)集成,或者若是不構建單頁應用,請考慮使用更靈活的 nwbNeutrino。特別是對於 Rails,可使用 Rails Webpacker。對於 Symfony,請嘗試 Symfony's Webpack Encore
  • 若是須要發佈 React 組件,nwb 以及 Neutrino 的 react-components preset 也能夠這樣作;
  • 若是要使用 React 和 Node.js 進行服務器渲染,請查看 Next.jsRazzle。 Create React App 與後端無關,僅生成靜態HTML / JS / CSS包;
  • 若是網站大部分是靜態的(例如,投資組合或博客),請考慮改用 Gatsby。與 Create React App 不一樣,它在構建時會將網站預先渲染爲 HTML;
  • 最後,若是須要更多自定義設置,請查看 Neutrino 及其 React preset

CRA 支持的瀏覽器

一些支持的瀏覽器規則以下:

  • 默認狀況下,生成的項目支持全部現代瀏覽器。 對 Internet Explorer 九、10 和 11 的支持須要 polyfill。 對於一組支持舊版瀏覽器的 polyfill,請使用 react-app-polyfill
  • 默認狀況下,生成的項目在 package.json 文件中包含一個 browserslist 配置,以針對基於全球使用狀況(> 0.2%)的普遍瀏覽器(用於生產構建)和用於開發的現代瀏覽器。 這提供了良好的開發體驗,尤爲是在使用異步/等待等語言功能時,但仍與生產中的許多瀏覽器保持高度兼容性;
  • browserslist 配置控制輸出的 JavaScript,以使注入的代碼與指定的瀏覽器兼容。 經過運行構建腳原本建立生產構建時,將使用生產列表,而在運行啓動腳本時,將使用開發列表。 可使用 https://browserl.ist 查看配置的瀏覽器列表支持的瀏覽器;
  • 請注意,這不會自動包括 polyfills,仍然須要根據所支持的瀏覽器來添加語言功能(見上文);
  • 在編輯 browserslist 配置時,咱們的更改可能不會當即被獲取。 這是因爲 babel-loader 中的一個未檢測到 package.json 中更改的問題。 一種快速的解決方案是刪除 node_modules/.cache 文件夾,而後重試。

這裏的重點是 BrowsersList,一個「在不一樣的前端工具之間共用目標瀏覽器和 node 版本的配置工具」。簡而言之,就是 Babel 等轉移工具經過咱們設置的 BrowsersList 中想要支持的瀏覽器版原本決定哪些語法須要被編譯。

CRA 支持的 ES 標準

一些支持的 ES 標準規則以下:

CRA 的兩個核心庫

Create React App 有兩個核心庫,以下:

  • create-react-app 是全局命令,用於建立初始化的 React 項目;
  • react-scripts 是所生成的項目中的開發依賴項,包括運行項目、測試項目、打包項目等多種命令。因爲 CRA 的一種依賴性原則,react-scripts 便開放了全部內部其它依賴的使用方式。

實戰 3:使用 CRA 構建 React App

到這裏,咱們終於須要經過命令行來安裝和使用 CRA,來構建咱們的第三個「Hello World」App。

全局安裝 CRA

爲保證每個新項目都能使用到 CRA 最新最全的功能,請確保 CRA 爲最新版本。

# 再已安裝 CRA 的狀況下,能夠先卸載 CRA
$ npm uninstall -g create-react-app
# 正式安裝 CRA
$ npm install -g create-react-app

初始化 CRA 項目

根據咱們的 npm 版本,選擇相應命令來安裝最新版的 CRA 並初始化第一個項目。同時檢查本身的 node 版本,須要在本地開發計算機上安裝 Node 8.16.0 或 Node 10.16.0 或更高版本(但服務器上不須要)。 咱們可使用nvm(macOS / Linux)或 nvm-windows 在不一樣項目之間切換Node版本。

# 查看本身的 npm 版本
$ npm --version

# 第一種新建項目方式——npm 5.2+ 時,如下命令會安裝最新版 CRA
$ npx create-react-app my-app
# 第一種新建項目方式——npm 版本小於等於 5.1 時
$ create-react-app my-app

# 第二種新建項目方式
# npm 6+ 開始支持 npm init <initializer> 
$ npm init react-app my-app

# 第三種新建項目方式
$ yarn create react-app my-app

項目的文件結構

經過命令行的構建,咱們初始化了第一個 CRA 項目,其中幫咱們生成的項目目錄結構以下(只有 src 下的文件纔會被 Webpack 處理,只有 public 下的文件才能被 public/index.html 使用):

my-app
├── .git # 隱藏文件夾,會初始化第一個 Commit 記錄
├── README.md
├── node_modules
├── package.json # 依賴配置文件
├── .gitignore
├── [floder_name] # 根目錄下能夠創建其餘文件夾,但不會被用在生產環境中
├── public # 只有 public 下的文件才能被 public/index.html 使用
│   ├── favicon.ico
│   ├── index.html # public/index.html 頁面模板
│   └── manifest.json
└── src # 只有 src 下的文件纔會被 Webpack 處理
    ├── App.css
    ├── App.js
    ├── App.test.js
    ├── [floder_name] # 能夠創建其餘文件夾,以被 Webpack 成功導入
    ├── index.css
    ├── index.js # JavaScript 打包入口文件
    ├── logo.svg
    └── serviceWorker.js

關於 package.json、index.js 和 public/index.html 文件夾,咱們經過「實戰 2」已經有所瞭解。前者是 JavaScript 打包入口文件,一般連接整個業務代碼;後者是頁面模板,是打包後整個靜態頁面的總入口。

這裏對如下兩個文件的出現進行簡要的意義歸納。
_

  • src/serviceWorker.js:提供漸進式 Web 應用的核心功能,不論網絡情況如何都能當即加載,而且在不須要網絡請求(離線時)的狀況下也能展現 UI ;
  • public/manifest.json:是漸進式 Web 應用將自身添加至桌面的功能依賴文件,也能夠對圖標、名稱等信息進行配置。

運行 CRA 項目

CRA 默認提供了運行、測試、打包、部署以及彈出項目的命令。其中的一些貼士:

  • npm start 內置熱更新機制,代碼改動時頁面自動刷新;
  • npm test 以交互方式運行測試觀察程序,默認狀況下運行與自上次提交以來更改的文件相關的測試;
  • npm run build 將要生產的應用程序生成到生成文件夾。它在生產模式下正確捆綁了React,並優化了構建以得到最佳性能。生成文件被壓縮,而且文件名包含哈希;
  • npm run eject 將內置的各類 Webpack 配置彈出到項目中,讓咱們能夠進行自定義。同時此操做不可逆,意味着咱們承擔了彈出配置後的風險。一般不推薦彈出,能夠經過 React App Rewired 庫進行配置注入。
# ---- 運行 ----
$ npm start
$ open http://localhost:3000
# ---- 測試 ----
$ npm test
# ---- 打包 ----
$ npm run builds
# ---- 彈出配置 ----
$ npm run eject

搭建 CRA 生態

根據官方文檔的思路,咱們還能從更多角度拓展 CRA 的使用邊界,下面進行概要介紹。

  • 爲開發環境添加額外功能:包括「配置編輯器風格」、「開發隔離組件」、「分析打包文件」和「添加 HTTPS 支持」;
  • 添加樣式與靜態資源支持:包括「添加樣式表文件」、「添加 CSS Modules 支持」、「添加 Sass 支持」、「添加 PostCSS 支持」、「添加圖片文字和字體支持」、「添加 GraphQL 支持」、「使用 public 文件夾」、「進行代碼拆分」;
  • 添加業務驅動支持:包括安裝各類依賴項如「BootStrap」、「Flow」、「TypeScript」、「Delay」、「Router」,以及「導出組件」、「使用全局變量」、「配置環境變量」、「製做漸進式 Web 應用」和「建立生產環境」;
  • 添加測試支持:包括「運行測試」和「調試測試」;
  • 添加後端集成支持:包括「在開發環境中代理 API 請求」、「使用 AJAX 請求獲取數據」、「集成後端 API」和「使用 Title & Meta 標籤」;
  • 部署進階:包括「靜態服務器」、「Azure」、「Firebase」、「Github Pages」等平臺的部署等。

這裏沒法深刻展開,每個點均可以是一個新的實戰,當咱們須要某個功能時即可以查閱相關文檔來主動探索。其中「分析打包文件」的解讀見「實戰 4」。

實戰 4:使用 Source Map Explorer 分析打包文件

# 安裝文件分析工具 source-map-explorer
$ sudo npm install --save source-map-explorer
# 打包項目
$ npm run build
# 將以下命令放入 package.json 中並生成快捷方式 npm run analyze
# $ source-map-explorer 'build/static/js/*.js'
# 注意此命令直接在命令行輸入會提示找不到相關命令
$ npm run analyze

對於一個剛被 CRA 生成的 React App 來講,分析的結果以下,包大總計 129.38k。

實戰 5:在已有的 React 項目中引入/升級 CRA

回到剛纔「實戰 2」創建的 react-webpack-steper 項目中,當咱們已經編寫了一部分業務時,可否直接在當前項目中無痛引入 CRA?

解決思路即是:在大多數狀況下,更改 package.json 中的 react-scripts 版本並刪除沒必要要文依賴配置,接着在此文件夾中運行 npm install 就足夠了,但最好參考更改日誌以瞭解潛在的重大更改。CRA 致力於將重大更改保持在最低限度,以即可以輕鬆升級 React 腳本。

# 卸載 CRA 自己已經提供的依賴
$ sudo npm uninstall --save webpack webpack-cli webpack-dev-server
$ sudo npm uninstall --save-dev @babel/cli @babel-core @babel/preset-env @babel/preset-react
$ sudo npm uninstall --save-dev babel-loader babel-plugin-module-resolver html-webpack-plugin
# 刪除 CRA 不須要使用的文件
$ rm webpack.config.js .babelrc
# 刪除 node_modules
$ rm -rf node_modules
# 手動安裝 React Script
$ sudo npm install --save react-scripts@latest
# 因爲 CRA 默認規則,將 src/index.html 移至 public/index.html
$ mkdir public
$ mv src/index.html public
# 在 package.json 中添加 React Script 啓動命令
$ vim package.json

package.json 中添加/覆蓋以下指令。

"scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",

再次執行便可。因爲每一個人的具體配置不必定一致,可根據自身所遇問題進行搜索。升級原理相似。

# 當沒有 BrowsersList 時,CRA 會進行詢問並幫助咱們生成
$ npm start

實戰 6:使用 React App Rewired 注入新配置

CRA 官方並不推薦使用 npm run eject 彈出配置,這會增長更多的 Webpack 維護工做。對於實在想改的 Webpack 配置來講,咱們可使用 React App Rewired 庫進行配置注入,這裏來作個小例子。

此工具能夠在不 'eject' 也不建立額外 react-scripts 的狀況下修改 create-react-app 內置的 webpack 配置,而後你將擁有 create-react-app 的一切特性,且能夠根據你的須要去配置 webpack 的 plugins, loaders 等。

繼續使用 react-webpack-steper 項目,咱們的簡易目標是增長 devServer 本地代理。

第一步:安裝依賴並進行基礎配置

# 安裝依賴
$ sudo npm install --save-dev react-app-rewired customize-cra
# 根目錄創建 config-overrides.js
$ touch config-overrides.js
# 修改 package.json
$ vim package.json
# 運行項目
$ npm start

其中,config-overrides.js 的初始代碼爲:

/* config-overrides.js */
module.exports = function override(config, env) {
  //do stuff with the webpack config...
  return config;
}

package.json 的修改思路爲:

/* package.json */
"scripts": {
- "start": "react-scripts start",
+ "start": "react-app-rewired start",
- "build": "react-scripts build",
+ "build": "react-app-rewired build",
- "test": "react-scripts test --env=jsdom",
+ "test": "react-app-rewired test --env=jsdom",
  "eject": "react-scripts eject"
}

第二步:編寫配置,進行代理

# 新增配置文件
$ mkdir config
$ touch config/proxy.js
# 修改 config-overrides.js
$ vim config-overrides.js

其中,config/proxy.js 源碼是:

module.exports = {
  '/api/**': {
      target: 'http://110.114.120.120:8080',
      secure: false,
      changeOrigin: false,
  },
}

config-overrides.js 修改成:

const { overrideDevServer } = require('customize-cra')
const proxy = require('./config/proxy')

module.exports = {
  devServer: overrideDevServer((config) => {
      config.proxy = proxy
      return config
  }),
}

此時,本地的全部 api 開頭的接口請求都會被轉發到 http://110.114.120.120:8080 的模擬後端 IP 上。

對 CRA 將來版本的簡單展望

截止目前(2020-01-10),CRA 的最新版本是 v3.3.0,咱們能夠從 Github 的 MileStone 中看到將來可能會改善的功能,其中整理並以下所述。

  • v3.x:添加多入口文件支持(不僅是一個 index.js 入口);使用 worker-loader 添加對 WebWorker 的支持;更早地檢查 Node 的版本;添加對子資源完整性 SRI 支持;生產環境中預加載腳本和連接...
  • v4.0:支持 Webpack 5.0(Webpack 目前最新版 v4.41.5,v5 也推出了一年多內測版);在 tsconfig.json 和 jsconfig.json 裏新增對 baseUrl 和 paths 的支持(方便寫 @ 絕對路徑等);支持 Jest 配置中設置browser 爲 true(根據環境提供正確的 Node 或 Browser 模塊)...
  • v100.0:提供構建過程當中的監視模式;適用於 Hooks 的熱加載...

讓咱們一塊兒持續關注。

結語

回顧文章,咱們從初始化 React App 的多種方式,引出 CRA 的必要性再對其進行較爲充分的解釋,最後配上 6 個角度來從一些角度對 CRA 的使用方式進行了實戰,最後迴歸到 CRA 的版本展望之中。

感謝你的閱讀,若是你有什麼更多的疑惑,CRA 的官方文檔 + 開源倉庫必定會知足你的一切。

最後,一塊兒拜讀一下 CRA 和 Redux 做者、React 的核心貢獻者 Dan Abramov 發佈的這篇「個人十年回顧」文章。

如今咱們能夠開始正式深刻地學習 React 技術棧了。

相關文章
相關標籤/搜索