1. 程式人生 > >【webpack系列】從零搭建 webpack4+react 腳手架(一)

【webpack系列】從零搭建 webpack4+react 腳手架(一)

搭建一個React工程的方式有很多,官方也有自己的腳手架,如果你和我一樣,喜歡刨根究底,從零開始自己一行一行程式碼建立一個React腳手架專案,那你就來對地方了。本教程是針對React新手,以及對webpack還不熟悉的使用者,或者是想了解當前前端工程化方案的使用者。我會在整個系列通過webpack4的配置,從生產和開發環境分別入手,包含程式碼壓縮,大檔案gz壓縮,webpack4的code split,postcss等外掛如何引入,css編譯,如何讓環境支援各種css前處理器,cssModule配置化,bundle結果分析,本地代理配置,React相關技術棧等等都有一個講解。本系列教材由淺入深,將會從零開始一步步完善整個腳手架。

前提條件

在開始之前,請確保安裝了 Node.js 的最新版本。建議使用 Node.js 最新的長期支援版本(LTS - Long Term Support)。如果你使用舊版本,你可能遇到各種問題,因為它們可能缺少 本教程用到的相關的 package 包。

執行以下命令可以檢視你本機安裝的node版本:
node -v && npm -v
如果你本機的node版本不是最新的,建議升級到新的node版本。node版本建議通過nvm進行管理,瞭解可以檢視 http://nvm.sh 這裡不再闡述。

 

通過本課程,你會學到什麼? 學習完本系列教程,你將會了解(持續更新):
  1. 前端開發的框架,工程化的方式
  2. webpack4的基礎配置以及常用的一些外掛
  3. postcss的使用,以及常用的postcss外掛
  4. 開發環境和生產環境的指令碼的不同
  5. 如何讓控制檯輸出編譯結果更加高大上
  6. 大檔案如何進行gz壓縮
  7. 讓css Module可配置化
  8. 介面轉發
  9. 如何進行bundle分析
  10. 如何規範程式碼,如何更好地整合eslint。
  11. 如何測試React程式碼
1.從空資料夾開始   現在建立一個空的資料夾,用你的開發工具開啟它。
開啟終端,確保你已經進入剛剛建立的目錄,接下去執行:
npm init

終端會讓你輸入一些資訊,比如名字,版本,描述,入口檔案等等。 完成後,npm會自動在你的根目錄生成 package.json 檔案。package.json 檔案不僅僅包含專案的配置資訊(比如名稱、版本、許可證等元資料),還會記錄專案所需要的各種模組,以及專案執行的指令碼等等。

 

2.安裝webpack4   本教程使用webpack來管理react工程。
執行以下命令,安裝webpack
npm install --save-dev webpack

npm安裝的包會存放在一個叫node_modules資料夾。如果你用的npm版本是npm 5版本,你還會發現多了package-lock.json檔案。package-lock.json檔案是當 node_modules 或 package.json 發生變化時自動生成的檔案,它的主要功能是確定當前安裝的包的依賴,以便後續重新安裝的時候生成相同的依賴,而忽略專案開發過程中有些依賴已經發生的更新(翻譯自官方說明)。

開啟package.json,你會發現多了一個名為devDependencies的屬性,通過--save-dev安裝的包會記錄在此屬性下。後續我們還會使用--save去安裝一個模組包,區別是通過--save安裝的模組會記錄在dependencies屬性內,而不是devDependencies屬性。通過字面意思,你應該也可以知道,一個是dev開發時候的依賴,一個是執行時的依賴。

新建webpack配置檔案。webpack 的配置檔案,是匯出一個物件的 JavaScript 檔案。在根目錄建立build資料夾,新增一個js檔案,命名為webpack.base.conf.js。
手動敲入以下程式碼:

        const path = require('path');
        const DIST_PATH = path.resolve(__dirname, '../dist');

        module.exports = {
            entry: {
                app: './app/index.js'
            },
            output: {
                filename: "js/bundle.js",
                path: DIST_PATH
            }
        };

webpack 配置是標準的 Node.js CommonJS 模組,它通過require來引入其他模組,通過module.exports匯出模組,由 webpack 根據物件定義的屬性進行解析。

這裡定義了2個屬性。當然,webpack的配置屬性遠遠不止這2個,之後我會深入講解更多屬性的配置。回到我們定義的2個屬性,entry定義了入口檔案,output定義了編譯後的輸出。按照對這段程式碼的理解,它應該是告訴webpack,我這個專案入口檔案是app目錄下的index.js檔案,你編譯後,在根目錄下建立dist資料夾(如果不存在),最後把編譯後的檔案輸出到dist檔案下,命名為bundle.js。

webpack4.0還增加了mode屬性,用來表示不同的環境。
我們使用merge的方式來組織webpack基礎配置和不同環境的配置。
先安裝webpack-merge:

npm install --save-dev webpack-merge

在build資料夾中再新增一個js檔案,命名為webpack.prod.conf.js,輸入以下程式碼。

    const merge = require('webpack-merge');
    const baseWebpackConfig = require('./webpack.base.conf');
    module.exports = merge(baseWebpackConfig, {
        mode: 'production'
    });

在根目錄下建立app目錄,然後建立index.js檔案。

    var element =document.getElementById('root');
    element.innerHTML = 'hello, world!';

在根目錄建立一個public資料夾,然後新建一個index.html檔案。

    <!DOCTYPE html>
    <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>從零開始搭建react工程</title>
      </head>
      <body>
      <div id="root"></div>
      <script src="../dist/js/bundle.js"></script>
      </body>
    </html>

你現在的檔案路徑應該看起來是這樣:(顯然我們引用的bundle.js並不存在)

      |- /app
        |- index.js
      |- /node_modules
      |- /public
        |- index.html
      |- /build
        |- webpack.base.conf.js
        |- webpack.prod.conf.js
      |- package.json
      |- package-lock.json

接下去我們要通過執行webpack命令,來編譯我們的程式碼,生成bundle.js。4.0版本之後的webpack,已經將webpack命令工具遷移到webpack-cli模組了。你需要安裝webpack-cli。

npm install --save-dev webpack-cli

安裝完成後,執行下面指令碼進行編譯:

webpack --config build/webpack.prod.conf.js

編譯完成後,重新整理根目錄,可以看到已經生成dist資料夾和bundle.js檔案。
用瀏覽器開啟html檔案,你會看到hello webpack。我們成功通過webpack編譯了js檔案,並且沒有出現問題。
webpack --config build/webpack.prod.conf.js命令,我們可以通過npm scripts管理起來。
在package.json檔案,我們為scripts屬性配置一個build命令,其值為:webpack --config build/webpack.prod.conf.js,以下是scripts的相關程式碼:

    "scripts": {
        "build": "webpack --config build/webpack.prod.conf.js",
        "test": "echo \"Error: no test specified\" && exit 1"
    },

 然後在命令列輸入:

npm run build

我們可以看到,webpack重新進行了編譯,這和執行
webpack --config build/webpack.prod.conf.js 是一樣的效果。

3.安裝react   在終端輸入以下命令:
npm install --save react react-dom

安裝成功後,我們在專案使用react,我們直接修改app目錄下的index.js的程式碼,我們用react來插入這句hello world!

    import React from "react";
    import ReactDom from "react-dom";

    ReactDom.render(
        <h1>hello, world!</h1>,
        document.getElementById("root")
    );

好了,我們再編譯試試看。

npm run build

失敗了?對不對!首先,我告訴你這段程式碼沒有任何問題,你需要思考下,它為什麼會編譯失敗。下一小節,我們一起來了解下原因。

 

4.整合babel 7.0   為什麼會失敗?因為webpack只識別JavaScript檔案,而且只能編譯es5版本的JavaScript語法。實際上,我們使用ES2015,以及jsx的語法糖,webpack它根本不認識啊。怎麼辦?webpack 可以使用 loader 來預處理檔案。它不僅僅可以處理JavaScript本身,還允許你打包任何的靜態資源。
其中,babel-loader,就是這樣一個預處理外掛,它載入 ES2015+ 程式碼,然後使用 Babel 轉譯為 ES5。我們來了解下如何在webpack配置babel-loader。
首先安裝babel相關的模組:
npm install --save-dev babel-loader @babel/preset-react @babel/preset-env @babel/core

除了babel-loader,我們還安裝了好多的包,其中@babel/core是babel的核心模組,@babel/preset-env是轉譯ES2015+的語法,@babel/preset-react是轉譯react的JSX以及FLOW。瞭解詳情可以移步 babel官方。
第二步,你需要在根目錄建立一個.babelrc的檔案,配置相關的presets:

    {
      "presets": [
        [
          "@babel/preset-env",
          {
            "targets": {
              "browsers": [
                "> 1%",
                "last 5 versions",
                "ie >= 8"
              ]
            }
          }
        ],
        "@babel/preset-react"
      ]
    }

更多關於babel的配置可以看下官方http://babeljs.io/docs/en的說明,這裡不再闡述。

第三步,修改webpack.base.conf.js檔案。

    const path = require('path');
    const APP_PATH = path.resolve(__dirname, '../app');
    const DIST_PATH = path.resolve(__dirname, '../dist');
    module.exports = {
        entry: {
            app: './app/index.js'
        },    
        output: {
            filename: 'js/bundle.js',
            path: DIST_PATH
        },
        module: {
            rules: [
                {
                    test: /\.js?$/,
                    use: "babel-loader",
                    include: APP_PATH
                }
            ]
        }
    };

在 webpack 配置中定義 loader 時,要定義在 module.rules 中。其中,test和use屬性是必須的。include屬性,定義了rules執行的範圍。這告訴 webpack 編譯器如下資訊:
嘿,webpack 編譯器,你在編譯檔案過程中,如果這個是在app目錄下的js檔案,在你對它打包之前,先使用 babel-loader 轉換一下。
重新執行本地編譯

npm run build

這次不再報錯,編譯成功。
好了,再次開啟public目錄的index.html,頁面成功顯示了hello world。
可能,你還有一些疑惑,html檔案如何也同步到dist目錄?bundle.js檔案修改了,萬一被瀏覽器快取了怎麼辦?如何通過script標籤把打包生成的js自動新增到html?react和業務程式碼如何分開打包?如何實現開發環境的熱更新?

下一章,我將針對這些問題,會一一進行講解,並且持續優化我們的工程。