使用 webpack4.0 從零搭建 React + TypeScript 應用

代碼地址 github.com/Maricaya/ad… , 有幫助請 star ~css

背景

剛接觸 react 的時候,你們都是經過 create-react-app 等腳手架來快速建立應用。當基本配置知足不了咱們的特殊需求時(好比使用 sass),咱們會修改 eject 出來的 webpack 配置文件。html

面對各類配置項一臉懵逼,不知道怎麼快速修改。其實,學習項目搭建與配置沒有什麼技巧,都是經驗(套路)。前端

這篇文章會介紹使用 webpack 手動搭建一個 react 項目的套路,來看看個人踩坑經歷吧。node

工具

  1. Yarn
  2. webpack 4
  3. 環境:webpack-dev-server 3
  4. 語言:TypeScript 3
  5. 編輯器:WebStorm / VScode 等

步驟

npm 初始化

npm init -y 
複製代碼

這一步建立 package.jsonreact

安裝 webpack、TypeScript、React

打開 webpack 官網,找到安裝命令。linux

yarn add webpack webpack-cli typescript --dev
yarn add react react-dom 
yarn add @types/react @types/react-dom --dev
複製代碼

新建 lib/index.tsx

在這個文件中寫入幾行 TypeScript 代碼。webpack

console.log(123);
複製代碼

index.tsx 在瀏覽器中是不能運行的,須要 webpack 幫咱們打包成瀏覽器可執行的 index.js。git

webpack.config.js

在根目錄新建 webpack.config.js。程序員

格式

如下配置都寫在 module.export 對象內部。github

module.export = {}
複製代碼

mode

mode 是 webpack 4 中新增長的選項, 有兩個可選值:production(生產環境)和 development(開發環境)。 mode 不可省略。 咱們先配置成開發環境,後面有須要再修改。

mode: 'development'
複製代碼

entry

打包的入口文件。

entry: { 
  <key>: <value> } 複製代碼

在這裏只介紹對象形式,是由於這個是最完整的entry配置,其餘形式只是它的簡化而已。

對象中的每一對屬性對,都表明着一個入口文件。所以多頁面配置時,確定是這種形式的 entry。

key 能夠是字符串變量,對應輸出文件的名稱;也能夠是路徑,好比 'a/b/c',對應着文件的輸出路徑。 詳細解釋能夠看這篇文章

entry: { 
  index: './lib/index.tsx' // ./ 當前目錄下
}
複製代碼

resolve

webpack在啓動後會從配置的入口模塊觸發找出全部依賴的模塊, resolve 配置 webpack 如何尋找模塊對應的文件。

在導入語句沒帶文件後綴時,webpack 會自動帶上後綴去嘗試訪問文件是否存在。 resolve.extensions 用於配置在嘗試過程當中用到的後綴列表。

resolve: {
  extensions: ['.ts', '.tsx', '.js', '.jsx']
}
複製代碼

module.rules

tsx

接下來配置解釋 tsx 的 loader。那麼問題來了,怎麼知道哪些是最合適的 loader?

答案是沒有標準 loader,只能是一個一個試。

把網上能找到的配置方式所有試一遍以後,我找到一個我認爲最好的方式 awesome-typescript-loader

安裝

yarn add awesome-typescript-loader --dev
複製代碼
module: {
   rules: [
      {
        test: /\.tsx?$/, 
        loader: 'awesome-typescript-loader'
      }
  ]
}
複製代碼

scss

再配置翻譯 scss 的模塊,須要使用三個 loader,style-loader, css-loader,sass-loader

yarn add --dev style-loader css-loader sass-loader
複製代碼

能夠看出 webpack 中 loader 的原則,一個loader只作一件事情

  • sass-loader 將 icon.scss 翻譯爲 icon.css
  • css-loader icon.css -> 對象
  • style-loader 對象 -> <style>css</style>
{
  test: /\.s([ac])ss$/,
  use: [
    'style-loader',
    'css-loader',
    'sass-loader'
  ]
}
複製代碼

配置完了輸入、中間 loader,接下來咱們來配置輸出 output。

output

  • path 輸出地址 我原本覺得這裏能夠直接寫絕對路徑__dirname + './dist',後來發如今 windows 上會報錯。 由於在不一樣的操做系統上,路徑表示方法不同。

    • windows: __dirname + '\\dist'
    • mac & linux: __dirname + './dist'

因此,咱們直接使用 node.js 提供的方法 path.resolve(__dirname, 'dist'), 這個 API 會根據操做系統的類型將兩個目錄鏈接起來。

  • library: 'adorable-react' 開發 npm 包的時候會用到這個命令,library 是指定用戶使用require時的模塊名。 這個模塊在使用時,命令是 require("adorable-react")

  • libraryTarget 表示庫的輸出格式是什麼,輸出模塊規範。有這麼幾種選擇 umd、amd、commonjs2。

通常都選 'umd',一種在全部模塊定義下均可運行的方案。

下面問題來了,umd 究竟是什麼呢?

要知道 umd 是什麼,咱們須要先了解下前端打包的歷史。

前端最開始是沒有包管理系統的,全部的js代碼都經過 script 標籤一個一個加載。 這個階段 js 包內重名的全局變量會互相影響。這時就須要打包工具,把變量做用域限制在局部。

前端程序員提出了 AMD 打包方案,Node.js 社區也提出了另外一套打包規則 commonJS。

  1. AMD 比較著名的方案 require.js,咱們的變量都定義到 define 函數中。
define (function () { 
	// 這裏是咱們的變量
	var a = 1 ... 
}) 
複製代碼

這個標準叫作 amd 異步模塊定義。

  1. commonJS 差很少是同一時間,Node.js 社區也提出了另外一套打包規則 commonJS。
var a = 1 // 只在當前文件有效
module.exports = { ... }
複製代碼

咱們正在寫的 webpack.config.js 也遵循這個規範。

commonJS 只在 Node.js 中使用,AMD 只在瀏覽器中使用。 致使你在使用時,必須區分代碼是運行在客戶端仍是服務端。

這時,有人發明了一套統一的規則 UMD 統一模塊定義。 UMD 會判斷當前運行環境是什麼,根據運行環境選擇使用 commonJS 或者 AMD。

因此在這裏,咱們選擇最完整的 UMD。

最後,完整的 output 代碼以下:

const path = require('path')
// mo
output: {
  path: path.resolve(__dirname, 'dist/lib'),
-  library: 'adorable-react',
  libraryTarget: 'umd'
}
複製代碼

配置 tsconfig.json

在根目錄下建立 tsconfig.json,它是 TypeScript 的配置文件。

主要包含兩塊內容:

  1. 指定待編譯的文件
  2. 定義編譯選項

咱們這篇文章主要講 webpack 4.0 的配置,ts 的配置文件先略過,能夠直接拷貝個人倉庫裏 tsconfig.json 文件。

想知道詳細解釋的小夥伴能夠看官方文檔,和這篇博客。

嘗試第一次打包

配置完這些基本規則以後,咱們能夠嘗試用 npx webpack 打包啦。

打包後的文件是這樣的:

根目錄/dist/lib/index.js — 這是 index.tsx 翻譯以後的文件

根目錄/lib/index.d.ts — 包含了聲明的全部類型

index.d.ts 放置的目錄不對,生成的 *.d.ts 類型文件都應該放入dist目錄中。

一頓搜索後,我在 tsconfig.json "compilerOptions" 中配置 "outDir": "dist"

再次打包,全部文件都放進了 dist 目錄,第一次用 webpack 打包就完成啦。

目前 webpack 就作了一件事,把 ts、tsx 翻譯成 js。

webpack-dev-server

接下來咱們安裝 webpack-dev-server,讓代碼可以自動編譯,實現熱更新。

yarn add webpack-dev-server --dev
npx webpack-dev-server 
複製代碼

此時,訪問 http://localhost:8080/index.js 咱們能夠看到編譯後的文件。

隨意改動 lib/index.tsx 中的文件,http://localhost:8080/index.js 會自動更新。

那麼問題來了,webpack-dev-server 爲何編譯這麼快?什麼是熱更新?

速度快

整個的過程咱們能夠簡化一下,webpack-dev-server 會啓動一個小型服務器(稱爲 Bundle Server)。

Bundle Server就是一個服務器,會執行這些編譯後的文件,讓瀏覽器能夠訪問到。

webpack 打包好的文件 index.js 會傳輸給 Bundle Server,放在內存中,

等用戶訪問 index.tsx 時,服務器直接從內存中拋出index.js,因此編譯很快。

熱更新

在應用程序的開發環境,方便開發人員在不刷新頁面的狀況下,就能修改代碼, 而且直觀地在頁面上看到變化的機制。

html-webpack-plugin

到目前爲止,我尚未 html 文件,在根目錄新建 html 入口文件 index.html。

下面問題來了,引入 js 的文件路徑是什麼?

只要 entry 下的文件名稱修改,咱們就須要手動修改 js 的路徑。那爲何不自動生成路徑呢?

安裝插件 html-webpack-plugin,根目錄建立 index.html 文件,把 webpack 打包後的靜態文件自動插入到 html 中。

yarn add --dev html-webpack-plugin
複製代碼

webpack.config.js 配置

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

plugins: [
  new HtmlWebpackPlugin({
    title: "adorable-react", // 頁面 title
    template: "index.html"
  })
],
複製代碼

把 index.tsx 的 ReactDom 掛載到 index.html 上。 index.tsx 中:

const div = document.createElement('div');

div.innerText = 'div';
document.body.appendChild(div);
複製代碼

externals

若是咱們想引用一個庫,可是又不想讓webpack打包, 能夠經過配置 externals 實現。

這個功能主要用在,建立一個 npm 庫的時候。

好比:咱們開發了一個 React 的組件庫,裏面引用了 react 和 react-dom。 可是咱們不必把 react 和 react-dom 打包起來,由於用戶會在使用時會本身下載。

那麼咱們就能夠 externals 的方式,將 react/react-dom 從咱們的源代碼中排出去。

externals: {
// 配置如何引入
      react: {
// 運行在 Node.js 環境中,import * as React from 'react' 等價於 const react = require('react')
        commonjs: 'react', 
        commonjs2: 'react',
// 使用require.js等加載,等價於 define(["react"], factory);
        amd: 'react',
//在瀏覽器中使用,須要提供一個全局的變量'React',等價於 var React = (window.React) or (React);
        root: 'React'
      },
      'react-dom': {
        commonjs: 'react-dom',
        commonjs2: 'react-dom',
        amd: 'react-dom',
        root: 'ReactDom'
      }
    }
複製代碼

webpack 配置區分開發、生產模式

看到這裏,你已經完成了 webpack 全部基本配置!給本身鼓鼓掌吧!

不過目前 develop 模式和 production 模式都寫在一塊兒,難道每次打包都須要手動修改嗎?

別急,webpack 爲咱們提供了自動切換方式。

將 webpack 文件分爲 develop 模式和 production 模式,公共部分寫入 webpack.config.js

develop 寫入 webpack.config.prod.js,production 寫入 webpack.config.dev.js

完整寫法能夠點擊連接查看,這裏這給出核心代碼: webpack.config.prod.js

const base = require('./webpack.config')
// 使用 Object.assign
module.exports = Object.assign({}, base,{
  mode: 'production'
})
複製代碼

配置 package.json scripts

咱們在 package.json scripts 中配置好命令,直接使用 yarn xxx 就能夠運行項目。

直接看最後的命令:

{
    "script": {
	"start": "cross-env NODE_ENV=development webpack-dev-server --config webpack.config.dev.js",
	"build": "cross-env NODE_ENV=production webpack --config webpack.config.prod.js"
    }
}
複製代碼

開發模式命令 webpack-dev-server,webpack 的配置是 webpack.config.dev.js。 生產模式命令 webpack,webpack 的配置是 webpack.config.prod.js

cross-env NODE_ENV=xxx

NODE_ENV=development 是咱們寫入的環境變量。

你可能也有這個疑問,webpack.dev.config.js 中不是有 mode 嗎? 爲何要手動寫入環境變量?

由於 webpack 處理的這個入口腳本文件及其引用的腳本文件都沒法訪問 webpack.dev.config.js 的屬性, 因此手動寫入環境變量,讓全部文件能夠訪問到 process.env.NODE_ENV。

cross-env 插件的做用是:保證環境變量在各個平臺都能添加成功。(在 unix、windows 上環境變量寫法不同)

安裝方式

yarn add --dev cross-env 
複製代碼

引入測試(選看)

引入 react 社區最流行的測試框架 jest,官網

測試配置不是重點,你們能夠直接看倉庫

這裏簡單說下配置:

yarn add --dev jest babel-jest @babel/preset-env @babel/preset-react react-test-renderer
yarn add --dev ts-jest
複製代碼

建立 .babelrc

{ "presets": ["react-app"] }
複製代碼

配置 scripts test

"test": "cross-env NODE_ENV=test jest --config=jest.config.js --runInBand"
複製代碼

建立 jest.config.js

配置 Enzyme Enzyme 是用於 React 的 JavaScript 測試實用程序,能夠更輕鬆地測試 React 組件。

yarn add --dev enzyme enzyme-adapter-react-16
複製代碼

建立 test 文件夾,寫入配置,這裏不贅述了,有興趣直接看源碼吧~

總結每一個目錄做用

終於完成了項目搭建,最後來看看每一個目錄的做用吧!

如今每一個文件的功能咱們都很是清楚,出了問題,咱們也知道應該在哪裏修改。

.
├── .babelrc                      // babel 配置
├── README.md
├── index.html                    // 首頁
├── jest.config.js                // jest 配置
├── dist                          // 最終代碼
├── lib                           // 源代碼         
│   ├── __tests__
│   │   └── index.unit.tsx
│   ├── index.scss
│   └── index.tsx
├── package.json
├── test                          // test 配置
│   ├── __mocks__
│   │   ├── file-mock.js
│   │   └── object-mock.js
│   └── setupTests.js
├── tsconfig.json                 // ts 配置
├── tsconfig.test.json            // ts 測試配置
├── tslint.json                   // 代碼檢測配置
├── webpack.config.dev.js         // webpack 配置
├── webpack.config.js
├── webpack.config.prod.js
├── webpack.config.wheel.js
└── yarn.lock
複製代碼

恭喜你完成了項目搭建,接下來咱們能夠愉快地 coding 了!

相關文章
相關標籤/搜索