帶你擼個屬於本身的react項目|webpack+babel+typescript+eslint

大家都不看的總集篇: 從零開始的大前端築基之旅(深刻淺出,持續更新~)
以爲不錯就順手點個贊吧~javascript

寫前端一年多了,用的都是大佬建好的架子,還沒本身從頭建個項目,如今開始踩坑。css

項目大致包括如下幾部分html

  • 目錄規範
  • react
  • webpack
  • less
  • typescript
  • eslint
  • axios封裝

初始化項目

首先,新建個文件夾basic-react-app。名字不是重點,你高興就好。 而後,使用 npm init 初始化項目,依據提示,一路回車下去前端

$basic-react-app npm init

This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.

// 從這裏開始,使用默認的話回車便可
package name: (basic-react-app)
version: (1.0.0)
description: basic react demo
entry point: (index.js)
test command:
git repository:
keywords:
author: suil
license: (ISC)
About to write to /Users/zhangpengcheng15/Documents/code/temp/basic-react-app/package.json:

{
  "name": "basic-react-app",
  "version": "1.0.0",
  "description": "basic react demo",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "suil",
  "license": "ISC"
}


Is this OK? (yes)
複製代碼

如今,你獲得了一個package.json文件java

而後,使用git init 初始化倉庫,能夠考慮先在遠端初始化倉庫,再拉去到本地。node

$basic-react-app git init

Initialized empty Git repository in /Users/zhangpengcheng15/Documents/code/temp/basic-react-app/.git/

$basic-react-app git:(master) ✗
複製代碼

若是你使用了特殊的命令窗或zsh,應該就能夠看到目錄後面顯示git:(master),表明本地倉庫已經建好了。react

彆着急下一步,不要忘記配置gitignore,不然電腦配置很差會卡頓一陣子。
在根目錄下新建.gitignore文件,使用編輯器打開,輸入webpack

node_modules
dist
複製代碼

並保存。好了,如今倉庫初始化結束。ios

目錄規範

最後,打開你喜歡的編輯器,好比 vscode,來建立以下文件結構。git

.
+ |- /src
+   |- /assets
+     |- /less
+     |- /icons
+   |- /components
+   |- /constants
+   |- /static
+     |- /imgs
+   |- /utils 
複製代碼

使用 vscode的同窗,必定要下 vscode-icons插件呀

其中,

  • assets 放置資源文件,包括各類類型的icon、全局使用的樣式表等
  • components 抽取的組件,下屬每一個文件夾都是一個系列的組件
  • constants 常量資源,好比抽取出的配置常量,公共的接口定義、統一放置請求映射等
  • static 靜態資源,包括多語言資源、圖片資源、字體資源、第三方lib資源等
  • utils 工具類包,項目中抽取出的共用方法之類的

好了,文件結構建完成。下面開始安裝 react

React

React 是一個聲明式,高效且靈活的用於構建用戶界面的 JavaScript 庫。使用 React 能夠將一些簡短、獨立的代碼片斷組合成複雜的 UI 界面,這些代碼片斷被稱做「組件」。

  • React 認爲渲染邏輯本質上與其餘 UI 邏輯內在耦合,好比,在 UI 中須要綁定處理事件、在某些時刻狀態發生變化時須要通知到 UI,以及須要在 UI 中展現準備好的數據。

  • React 並無採用將標記與邏輯進行分離到不一樣文件這種人爲地分離方式,而是經過將兩者共同存放在稱之爲「組件」的鬆散耦合單元之中,來實現關注點分離。

使用命令行安裝 reactreact-dom

npm install react react-dom
或者 yarn add react react-dom
複製代碼

在開始下一步以前,咱們先在目錄中添加一些文件。

在src目錄下,新建index.tsxindex.less,先不用糾結可否被識別或者運行的問題,下一步咱們再來解決它。

// src/index.tsx

import React from 'react';
import ReactDOM from 'react-dom';
import './index.less';  // 必定要加 './' 表示當前目錄下

ReactDOM.render(
  <h1>Hello, world!</h1>,
  document.getElementById('root')
);
複製代碼
h1{
	color: blue;
}
複製代碼

webpack

webpack 是什麼

webpack 是一個現代 JavaScript 應用程序的靜態模塊打包器,當 webpack 處理應用程序時,會遞歸構建一個依賴關係圖,其中包含應用程序須要的每一個模塊,而後將這些模塊打包成一個或多個 bundle

webpack 的核心概念

  • entry: 入口
  • output: 輸出
  • loader: 模塊轉換器,用於把模塊原內容按照需求轉換成新內容
  • 插件(plugins): 擴展插件,在webpack構建流程中的特定時機注入擴展邏輯來改變構建結果或作你想要作的事情

在本地安裝 webpack,接着安裝 webpack-cli(此工具用於在命令行中運行 webpack):

npm install webpack webpack-cli --save-dev
複製代碼

或者 yarn add webpack webpack-cli --dev

當前webpack基於最新的版本

"webpack": "^4.43.0",
"webpack-cli": "^3.3.11"
複製代碼

在 webpack 4 中,能夠無須任何配置使用,然而大多數項目會須要很複雜的設置,這就是爲何 webpack 仍然要支持 配置文件。

本文目的在於配置一個可用的react項目,所以有些配置會一步到位,如需更多webpack知識,請移步 webpack中文網

在根目錄建立一個 webpack.config.js,寫入以下內容

const path = require('path');

module.exports = {
  entry: './src/index.tsx',
  mode: "development",
  devtool: 'cheap-module-eval-source-map',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};
複製代碼

若是 webpack.config.js 存在,則 webpack 命令將默認選擇使用它。使用 --config 選項能夠傳遞任何名稱的配置文件。

對於多入口文件,可使用以下配置

entry: {
  index: './src/index.js',
  index2: './src/index2.js',
},

output: {
  path: path.resolve(__dirname,'dist'),      //此處若非絕對路徑,可能報錯
  filename: '[name].bundle.js',
},
複製代碼

在上面的配置文件中,咱們使用了入口 entry和出口 output兩個概念,相信你也理解這兩個配置的含義。後面咱們會用到另外兩個概念。

mode

mode 配置項,告知 webpack 使用相應模式的內置優化。

mode 支持如下兩個配置:

  • development:將 process.env.NODE_ENV 的值設置爲 development,啓用 NamedChunksPlugin 和 NamedModulesPlugin

  • production:將 process.env.NODE_ENV 的值設置爲 production,啓用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 UglifyJsPlugin

簡單來講,*開發環境(development)生產環境(production)*的構建目標差別很大。將 mode設置爲development 會啓用一些webpack默認的優化。這裏咱們先設置爲 mode: "development",

devtool

devtool 中的一些設置,能夠幫助咱們將編譯後的代碼映射回原始源代碼。不一樣的值會明顯影響到構建和從新構建的速度。

對咱們而言,可以定位到源碼的行便可,所以,綜合構建速度,在開發模式下,devtool 的值設置爲cheap-module-eval-source-map

其餘的配置參數可參考devtool

Babel

Babel 是一個 JavaScript 編譯器

ES2015 中的 import 和 export 語句已經被標準化。雖然大多數瀏覽器還沒法支持它們,可是 webpack 卻可以提供開箱即用般的支持。

事實上,webpack 在幕後會將代碼「轉譯」,以便舊版本瀏覽器能夠執行。可是注意的是,webpack 不會更改代碼中除 import 和 export 語句之外的部分。若是咱們須要使用其它 ES2015 特性,須要在 webpack 的 loader 系統中使用了一個像是 Babel 或 Bublé 的轉譯器。

Babel 是一個工具鏈,主要用於將 ECMAScript 2015+ 版本的代碼轉換爲向後兼容的 JavaScript 語法,以便可以運行在當前和舊版本的瀏覽器或其餘環境中。下面列出的是 Babel 能爲你作的事情:

  • 語法轉換
  • 經過 Polyfill 方式在目標環境中添加缺失的特性 (經過 @babel/polyfill 模塊)
  • 源碼轉換 (codemods)

執行下面的指令安裝 babel系列

yarn add @babel/core @babel/cli @babel/preset-env @babel/preset-react babel-loader --dev
複製代碼

其中,

  • @babel/preset-env 是一個智能預置,容許咱們使用最新的JavaScript,而不須要管理在目標環境中哪些語法須要轉換(以及可選的瀏覽器填充)。這既使咱們的工做更輕鬆,也使JavaScript包更小!
  • @babel/preset-react 可以轉換 JSX 語法

webpack 最出色的功能之一就是,除了 JavaScript,還能夠經過 loader 引入任何其餘類型的文件。也就是說,以上列出的那些 JavaScript 的優勢(例如顯式依賴),一樣能夠用來構建網站或 web 應用程序中的全部非 JavaScript 內容。

如今,在 webpack 配置對象中,添加 babel-loader 到 module 的 loaders 列表中,,當前配置文件以下

module.exports = {
  entry: './src/index.tsx',
  mode: "development",
  devtool: 'cheap-module-eval-source-map',
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /(node_modules|bower_components)/,
        loader: "babel-loader",
      },
    ],
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};
複製代碼

我下載了那麼多包,你就用了這一個?你專門解釋的那兩個包呢?

不着急,其餘的包咱們經過設置babel配置文件來使用。在根目錄下新建 .babelrc文件,輸入以下內容

{
  "presets": [
    "@babel/env",
    "@babel/preset-react"
  ]
}
複製代碼

既然說到這裏,咱們先還個債,還記得上面新建的 index.tsx文件麼?webpack不認識tsx文件,瀏覽器也不認識 tsx 文件,那誰來認呢?

固然是babel了,在7以前的版本中,咱們須要專門的 ts-loader來轉義 tstsx類型的文件,可是,如今只須要有babel就夠了

不管代碼是否具備 ES2015 特性,JSX,TypeScript,仍是其餘瘋狂的自定義————編譯器都知道要作什麼。向 ts-loader、ts-jest、ts-karma、create-react-app-typescript 等等說再見就好啦,使用 Babel 代替它們。

yarn add @babel/preset-typescript --dev
複製代碼

.babelrc文件中補充

{
  "presets": [
    "@babel/env",
    "@babel/preset-react",
+   "@babel/preset-typescript"
  ],
}
複製代碼

好了,到此,ts | tsx 文件可使用babel-loader編譯了,記得修改webpack.config.js文件,補充 ts | tsx使用babel-loader編譯

rules: [
      {
        test: /\.(js|jsx|ts|tsx)$/,
        exclude: /(node_modules|bower_components)/,
        loader: "babel-loader",
      },
    ],
複製代碼

Loader

對於loader的用法,

test 字段是匹配規則,針對符合規則的文件進行處理。

use 字段有幾種寫法

  • 能夠是一個字符串,例如上面的 use: 'babel-loader'
  • 能夠是一個數組,例以下面處理CSS文件時,use: ['style-loader', 'css-loader']
  • use 數組的每一項既能夠是字符串也能夠是一個對象,當咱們須要在webpack 的配置文件中對 loader 進行配置,就須要將其編寫爲一個對象,而且在此對象的 options 字段中進行配置,例如上面 babel-loader 可使用另外一種配置形式
rules: [
    {
        test: /\.jsx?$/,
        use: {
            loader: 'babel-loader',
            options: {
                presets: ["@babel/preset-env"]
            }
        },
        exclude: /node_modules/
    }
]
複製代碼

css

如今,去還另外一個債,less文件。要讓webpack識別 less文件,天然是 less-loader了。
固然,咱們順手也補充 css文件的識別。

有關 less 及 sass 的選擇,可移步 Sass.vs.Less | 簡介與比較

爲了從 JavaScript 模塊中 import 一個 CSS 文件,你須要在 module 配置中 安裝並添加 style-loader 和 css-loader:

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

webpack.config.js

rules: [
      {
        test: /\.(js|jsx|ts|tsx)$/,
        exclude: /(node_modules|bower_components)/,
        loader: "babel-loader",
      },
+     {
+       test: /\.css$/,
+       use: [
+         'style-loader',
+         'css-loader'
+       ]
+     },
+     {
+     test: /\.less$/,
+       use: [
+         'style-loader',
+         'css-loader',
+         'less-loader'
+       ]
+     }
  ],
複製代碼

小提示:vscode中,選中 +號,而後按 win + D,就能夠連續選中加號,最後按刪除就能夠刪除全部的+

postcss-loader

PostCss是一個樣式處理工具,它經過自定義的插件和工具生態體系來從新定義css。它鼓勵開發者使用規範的css原生語法編寫代碼,而後配置編譯器轉換須要兼容的瀏覽器版本,最後經過編譯將源碼轉換爲目標瀏覽器可用的css代碼。

yarn add postcss-loader autoprefixer --dev
複製代碼

webpack.config.js

rules: [
     {
       test: /\.css$/,
       use: [
         'style-loader',
         'css-loader',
+        'postcss-loader',
       ]
     },
     {
     test: /\.less$/,
       use: [
         'style-loader',
         'css-loader',
+        'postcss-loader',
         'less-loader'
       ]
     }
  ],
複製代碼

postcss-loader 是專門用來加瀏覽器前綴的,可是它本身也就只能加個前綴而已,由於它本身不知道哪一個該加,哪一個不應加。因此咱們須要 autoprefixer 來告訴它,哪一個加,哪一個不加。

在根目錄新增postcss.config.js

module.exports = {
  plugins: [
    require('autoprefixer')
  ]
}
複製代碼

package.json中補充下列字段,來肯定咱們要支持到哪一步

"browserslist": [
    "defaults",
    "not ie < 11",
    "last 2 versions",
    "> 1%"
  ]
複製代碼

未雨綢繆,咱們在此安裝另外兩個會用到的loader

file-loader 和 url-loader

  • 使用 file-loader,咱們能夠輕鬆地將圖片、字體等內容混合到 打包文件中。
  • url-loader 功能相似於 file-loader,可是在文件大小(單位 byte)低於指定的限制時,能夠返回一個 DataURL。
yarn add url-loader file-loader --dev
複製代碼

webpack.config.js中補充rules規則

{
   test: /\.(png|svg|jpg|gif)$/,
     use: [
       {
         loader: 'url-loader',
         options: {
           limit: 2048,
         }
       },
       'file-loader',
     ]
 },
{
  test: /\.(woff|woff2|eot|ttf|otf)$/,
  use: [
  	'file-loader'
  ]
},
複製代碼

resolve

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

resolve.alias

建立 import 或 require 的別名,來確保模塊引入變得更簡單。例如,一些位於 src/ 文件夾下的經常使用模塊:

resolve: {
   alias: {
        '@': path.resolve(__dirname, "src/")
      }
}
複製代碼

如此,引用本身封裝的組件或函數就能夠以@做爲起始,例如

import BaseButton from "./components/Button/BaseButton";
複製代碼

這樣,避免了無止盡的../../../,並且萬一某個組件切換了目錄,組件裏的外部引用也不須要從新調整,由於它們老是相對於src路徑開始尋找。

請注意,此處只是容許webpack打包時識別,要開啓路徑提示,須要配置tsconfig.json中的path,文末有附配置代碼

Plugin

clean-webpack-plugin

每次文件修改後,從新打包,致使 dist 目錄下的文件愈來愈多怎麼辦?
快使用 clean-webpack-plugin,每次構建自動爲你清理 /dist/目錄,留你想留,清你想清,一鍵配置,呵護懶癌晚期的你

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

webpack.config.js

+ const CleanWebpackPlugin = require('clean-webpack-plugin');

 module.exports = {
   
    plugins: [
+     new CleanWebpackPlugin({
+           cleanOnceBeforeBuildPatterns:['**/*', '!store', '!store/**'] // 不刪除store目錄下的文件
+      }),

    ],
  };
複製代碼

注意:本文沒有拆分不一樣環境的配置文件,在本地調試時,請註釋掉這個插件

更多配置移步clean-webpack-plugin

HtmlWebpackPlugin

你可能須要一個HtmlWebpackPlugin,不然,你可能會想,react組件渲染了掛在哪裏呢?找不到的話它會本身造一個Html模版麼?

並非,我只是由於懶沒有建立模版文件,

當沒有這個插件的時候,每次webpack打包生成的[name].bundle.js須要手動配置,如今,它解放了你的雙手,你能夠快樂的乾點別的事了。

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

不論你以前有沒有建立,HtmlWebpackPlugin 會默認在輸出目錄生成 index.html 文件,全部的 bundle 會自動添加到 html 中。

+  const HtmlWebpackPlugin = require('html-webpack-plugin');
   
   
   plugins: [
     new CleanWebpackPlugin({
         cleanOnceBeforeBuildPatterns:['**/*', '!store', '!store/**'] // 不刪除store目錄下的文件
     }),
+     new HtmlWebpackPlugin({
+       title: 'Basic react app'
+     })
    ],
複製代碼

固然,通常來講,咱們有時須要在public目錄下新建一個index.html 的模版文件,裏面定義了一些咱們定製的內容,但我仍是不想佔用個人雙手在裏面引入打包生成的bundle.js文件。

new HtmlWebpackPlugin({
    template: './public/index.html',
    filename: 'index.html', //打包後的文件名
    config: config.template
 })
複製代碼

更多功能移步html-webpack-plugin

webpack-dev-server

說了這麼多,終於到了碰見Hello world的時候了。不然我擔憂你會看不下去。

webpack-dev-server 提供了一個簡單的 web 服務器,而且可以實時從新加載(live reloading)。讓咱們運行如下命令:

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

修改配置文件

+   devServer: {
+		  port: '3000', //默認是8080
+		  publicPath: "http://localhost:3000/dist/",
+     hotOnly: true,
+     contentBase: './dist'
+   },
複製代碼

在package.json中補充腳本

"scripts": {
      "watch": "webpack --watch",
      "start": "webpack-dev-server --open",
      "build": "webpack"
    },
複製代碼

在命令行先執行yarn build,再執行yarn start,頁面自動打開 -> 碰見Hello World

Typescript

eslint

雖然咱們使用babel來編譯 ts|tsx文件,可是,有時候咱們還會懷念ts的類型錯誤檢查,好比

「不!我不會編譯這玩意兒的!你的代碼在42個不一樣的文件中出現異常!」

如何去檢查類型錯誤呢?添加一段 lint 腳原本喚起 TypeScript 編譯器。例如,將 npm test 命令調整爲先檢查類型,而後再繼續運行單元測試。

因爲性能問題,TypeScript 官方決定全面採用 ESLint,甚至把倉庫(Repository)做爲測試平臺,而 ESLint 的 TypeScript 解析器也成爲獨立項目,專一解決雙方兼容性問題。

yarn add eslint typescript @typescript-eslint/parser @typescript-eslint/eslint-plugin --dev
複製代碼

同時,咱們是一個react app 項目,因此,你懂的

yarn add eslint-plugin-react eslint-plugin-react-hooks --dev
複製代碼

在根目錄下新建.eslintrc.js文件,填入以下內容

module.exports = {
  root: true,
  parser: '@typescript-eslint/parser',
  parserOptions: {
    "ecmaFeatures": {
        "jsx": true
    },
    "ecmaVersion": 11,
    "sourceType": "module",
    project: './tsconfig.json',
},
  plugins: [
    "react",
    "react-hooks",
    '@typescript-eslint',
  ],
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:react/recommended',
    'plugin:react-hooks/recommended',
  ],
};
複製代碼

總有些文件是不須要檢查的,作人麼,能夠爲難別人,但不能爲難本身,因此,在根目錄建立一個.eslintignore文件

config/
scripts/
node_modules/
\.eslintrc.js // 幹掉它本身。。。
webpack.config.js
複製代碼

通過考慮,補充 airbnb規則

yarn add eslint-config-airbnb-typescript eslint-plugin-jsx-a11y eslint-plugin-import --dev
複製代碼

eslintrc.js文件替換爲

extends: [
    'airbnb-typescript',
    'plugin:@typescript-eslint/recommended',
    'plugin:react/recommended',
    'plugin:react-hooks/recommended',
  ],
複製代碼

不要忘了在vscode中下載eslint插件

雖然上述規則通過了檢驗,但總有些對於咱們本身來講是多餘或者缺失的,因此在.eslintrc.js文件補充 rules字段,用於開啓或關閉某些規則

"rules": {
   // 禁止使用 var
   'no-var': "error",
   // 優先使用 interface 而不是 type
   '@typescript-eslint/consistent-type-definitions': [
   "error",
   "interface"
   ],
   '@typescript-eslint/no-explicit-any': 'off',
   '@typescript-eslint/explicit-module-boundary-types': 'off',
   "react-hooks/rules-of-hooks": "error",
   "react-hooks/exhaustive-deps": "warn",
   "react/prop-types": "off",
 }
複製代碼

修改package.json中script腳本

"scripts": {
    "lint": "eslint --ext .ts --ext .tsx src/",
    "watch": "webpack --watch",
    "start": "webpack-dev-server --open",
    "build": "webpack"
  },
複製代碼

執行yarn lint,而後愉快的扇本身吧。。。。

我在src/index.tsx中留了三個錯誤,大概? 你能夠試着改一下
受不了檢查記得禁止一些規則

custom.d.ts

忽然發現我沒寫怎麼生成tsconfig.json,兩個方法,

  1. 找個現成的複製到根目錄,固然,不要亂複製
  2. 若是全局安裝typescript,執行tsc --init
"compilerOptions": {
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": false,
    "outDir": "./dist/",
    "forceConsistentCasingInFileNames": true,
    "module": "commonjs",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    // "isolatedModules": true,
    // "noEmit": true,
    "jsx": "react",
    "experimentalDecorators": true,
    "baseUrl": ".",
    "paths": {
      "@/*":["src/*"]
    }
  },
  "include": [
    "src","test",
  ]
}
複製代碼

配置項不是本文重點,主要是爲了下面的內容。
在typscript中是沒法識別非代碼資源的,因此若是你試圖import一個svg|png之類的文件就會提示你cannot find module '.png'

custom.d.ts

declare module "*.svg" {
  const content: any;
  export default content;
}
declare module "*.png" {
  const content: any;
  export default content;
}
複製代碼

在tsconfig.json中有一個include字段,裏面的內容表示ts須要轉義的內容,將custom.d.ts文件放到include字段包含的目錄中,就能夠解決類型不識別的問題。

最後,這裏附上

本篇教程到此結束,這只是一篇簡明的指導式教程,若是有什麼不對的地方,歡迎在評論中指出,我會及時修改

後續會針對webpack、eslint語法規範專門整理一期,工做較忙,時間就隨緣了~

若是你收穫了新知識,請點個贊吧~

本文收納於: 從零開始的大前端築基之旅(深刻淺出,持續更新~)

推薦閱讀:

參考文檔

  1. webpack中文網
  2. babel
  3. eslint
  4. typescript
相關文章
相關標籤/搜索