從零搭建webpack4+react+typescript+eslint腳手架(一)

引言

項目github倉庫地址: https://github.com/mecoepcoo/ts-react-boilerplate

這個系列的文章主要講述如何從一個空目錄創建webpack+react+typescript+eslint腳手架,書寫此文時各主要工具的版本爲:javascript

  • webpack v4
  • react v16.9
  • typescript v3.5
  • babel v7
  • eslint v6.2

本文涉及的內容大體包含:css

  • webpack的配置
  • 對靜態資源(圖片,模板等)的處理
  • 使react項目支持typescript,eslint,prettier等工具
  • 優化webpack配置,減少代碼的體積
  • 支持不一樣的css預處理器(less,sass等)
  • 一套好用的樣式方案
  • 使項目支持多個環境切換(開發,測試,預發佈,生產等)
  • 使用規則來自動約束代碼規範
  • 優化開發體驗
  • 一些優化項目性能的建議

閱讀這個系列的文章須要具有的條件:html

  • 你使用過vuereactangular等任意一種前端框架
  • 你瞭解過vue-clicreate-react-appangular-cli等任意一種腳手架生成工具
  • 你瞭解webpack的基本原理或用法
  • 你有生產環境代碼的開發經驗,瞭解生產環境中的代碼與自娛自樂代碼的區別
Why not create-react-app?

筆者使用CRA新建項目時,以爲自定義程度不夠。嘗試過 react-app-rewired + customize-cra 的方案,仍是以爲異常繁瑣,並且會消耗額外的維護精力,魯迅說:青年應當有朝氣,敢做爲,遂自行搭建一個 boilerplate 。前端

初始化目錄

咱們要從一個空目錄開始,先新建這個目錄,作一些必要的初始化工做:vue

$ mkdir my-react
$ cd my-react

$ git init
$ npm init

新建以下目錄結構:java

react-projectnode

  • config 打包配置
  • public 靜態文件夾react

    • index.html
    • favicon.ico
  • src 源碼目錄

規範git提交

協做開發時,git提交的內容若是沒有規範,就很差管理項目,咱們用 husky + commitlint 來規範git提交。webpack

咱們先在根目錄下創建 .gitignore 文件,忽略不須要要的文件。git

而後安裝工具:

$ npm i -D husky
$ npm i -D @commitlint/cli
husky 會爲 git 增長鉤子,在 commit 時執行一系列操做,commitlint 能夠檢查 git message 是否符合規則。

package.json 中增長配置以下:

"husky": {
  "hooks": {
    "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
  }
},

在根目錄新建文件 .commitlintrc.js,根據具體狀況配置:

module.exports = {
  parserPreset: {
    parserOpts: {
      headerPattern: /^(\w*)(?:\((.*)\))?:\s(.*)$/,
      headerCorrespondence: ['type', 'scope', 'subject']
    }
  },
  rules: {
    'type-empty': [2, 'never'],
    'type-case': [2, 'always', 'lower-case'],
    'subject-empty': [2, 'never'],
    'type-enum': [
      2,
      'always',
      ['feat', 'fix', 'update', 'docs', 'style', 'refactor', 'test', 'chore', 'release', 'revert']
    ]
  }
}

這樣便可完成配置,具體的使用方法參考 commitlint文檔

React hello, world

安裝react,寫一個react hello, world

如今讓主角 React 登場:

$ npm i react react-dom

新建一個 hello, world 結構,這裏直接用ts書寫:

// src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './style.css';

ReactDOM.render(<App />, document.getElementById('root'));
// src/App.tsx
import React from 'react';
import './app.css';

const App: React.FC = () => {
  return (<div>hello, world</div>);
};

export default App;

咱們還須要一個html模板:

<!-- public/index.html -->
<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8" />
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
    <title>react-app</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>

完整的結構參考 代碼示例

webpack的基本配置

安裝webpack相關工具:

$ npm i -D webpack webpack-cli webpack-dev-server webpack-merge

在 config 目錄下新建幾個文件:config.js, webpack.base.js, webpack.prod.js, webpack.dev.js, build.js

先抽取一些通用的配置:

// config/config.js
const path = require('path');

module.exports = {
  assetsRoot: path.resolve(__dirname, '../dist'),
  assetsDirectory: 'static',
  publicPath: '/',
  indexPath: path.resolve(__dirname, '../public/index.html'),
};
// config/webpack.base.js
const path = require('path');
const webpack = require('webpack');
const config = require('./config');

module.exports = {
  entry: {
    app: './src/index.tsx',
  },
  output: {
    filename: 'js/[name].bundle.js',
    path: config.assetsRoot,
    publicPath: config.publicPath
  },
  module: {
    rules: [
      {
        oneOf: []
      }
    ]
  },
  resolve: {
    extensions: ['.js', '.json', '.jsx', '.ts', '.tsx'] // 自動判斷後綴名,引入時能夠不帶後綴
  },
  plugins: []
};

babel和typescript,路徑別名

接下來咱們須要讓webpack支持typescript,而且將代碼轉換爲es5,這樣才能在低版本的瀏覽器上運行。

依然是先安裝工具:

$ npm i -D babel-loader @babel/core @babel/preset-env @babel/preset-react @babel/polyfill
$ npm i core-js@2 # babel的按需引入依賴
$ npm i -D @babel/plugin-proposal-class-properties # 可以在class中自動綁定this的指向
$ npm i -D typescript awesome-typescript-loader # 處理ts,主要就靠它
$ npm i -D html-loader html-webpack-plugin # 順便把html的支持作好

用了ts,就要有一個tsconfig配置,在根目錄新建 tsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "typeRoots": [
      "src/types" // 指定 d.ts 文件的位置,根據具體狀況修改
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react",
    "baseUrl": ".",
  },
  "include": [
    "src"
  ],
  "exclude": [
    "./node_modules"
  ]
}

來配一下webpack:

// webpack.base.js
const APP_PATH = path.resolve(__dirname, '../src');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module: {
  rules: [
    {
      oneOf: [
        {
          test: /\.(html)$/,
          loader: 'html-loader'
        },
        {
          test: /\.(j|t)sx?$/,
          include: APP_PATH,
          use: [
            {
              loader: 'babel-loader',
              options: {
                presets: [
                  '@babel/preset-react',  // jsx支持
                  ['@babel/preset-env', { useBuiltIns: 'usage', corejs: 2 }] // 按需使用polyfill
                ],
                plugins: [
                  ['@babel/plugin-proposal-class-properties', { 'loose': true }] // class中的箭頭函數中的this指向組件
                ],
                cacheDirectory: true // 加快編譯速度
              }
            },
            {
              loader: 'awesome-typescript-loader'
            }
          ]
        },
      ]
    }
  ]
},
plugins: [
  new HtmlWebpackPlugin({
    inject: true,
    template: config.indexPath,
    showErrors: true
  }),
],
optimization: {}

爲了之後開發時引入路徑方便,咱們加個路徑別名的配置,須要改webpack配置和tsconfig兩處:

// webpack.base.js
resolve: {
  extensions: ['.js', '.json', '.jsx', '.ts', '.tsx'],
  alias: {
   '@': path.resolve(__dirname, '../src/') // 以 @ 表示src目錄
  }
 },
{
  "compilerOptions": {
    // ...
    "paths": {
      "@/*": ["src/*"]
    }
    // ...
  }
}

至此,咱們完成了最最基本的webpack配置,但暫時還不能打包。


相關文章
相關標籤/搜索