React-CRA 多頁面配置(react-app-rewired)

更新時間:2019-01-11
版本信息:CRA v2.1.1 + Webpack v4.19.1 + react-app-rewired v1.6.2

1、前言

爲何要進行多頁面配置

在使用 React 進行開發的過程當中,咱們一般會使用 create-react-app 腳手架命令來搭建項目,避免要本身配置 webpack,提升咱們的開發效率。可是使用 create-react-app 搭建的項目是單頁面應用,若是咱們是作中後臺管理頁面或 SPA,這樣是知足要求的,但若是項目有多入口的需求,就須要咱們進行一些配置方面的修改。javascript

通常如今有兩種方式將腳手架搭建的項目修改成多入口編譯:css

  1. 執行 npm run eject 命令,彈出配置文件,進行自定義配置。請參見:React-CRA 多頁面配置(npm run eject)
  2. 使用 react-app-rewired 修改腳手架配置,在項目中安裝 react-app-rewired 後,能夠經過建立一個 config-overrides.js 文件來對 webpack 配置進行擴展。

本文對第 2 種方法給出具體配置方案,第 1 種方案詳見:React-CRA 多頁面配置(npm run eject)。在閱讀本文以前,能夠先了解一下第 1 種方案,有助於更好的理解本文內容。html

webpack 基礎

本文對 React 多頁面應用配置的探討是基於使用 create-react-app 腳手架命令構建的項目,並非 webpack 多頁面配置教程。但上述兩種方案都應該具備必定的 webpack 的基礎,實際上當你決定要改造或加強項目的構建打包配置的時候,你應該先對 webpack 進行必定的瞭解。java

若是你以前使用 webpack v3.x 版本,這裏附上 webpack v4.0.0 更新日誌 以及 webpack4升級徹底指南node

方案說明

經過使用 react-app-rewired 能夠修改腳手架配置,從而實現擴展 webpack 配置,如增長對 less 文件的支持、增長 antd 組件的按需加載、處理 html 文檔中的圖片路徑問題等,甚至能夠將單頁面入口編譯修改成多頁面入口編譯的方式。react

咱們能夠經過 react-app-rewired 在不暴露配置文件的狀況下達到擴展項目配置的目的,一樣咱們也能夠經過 react-app-rewired 來實現多頁面入口編譯的配置,可是這須要對腳手架原來的配置具備必定了解的,相較於 npm run eject 暴露配置文件的方式來講,這種方式是不太具備透明度的,後面維護的難度較大。本文的意義更多的是記錄對這種方案的嘗試,若是須要進行多頁面配置,最好仍是先了解一下經過執行 npm run eject 暴露配置文件的方案,具備了必定的多頁面配置經驗和 webpack 使用經驗以後再使用這種方案。webpack

本文方案完成的基礎是,我以前已經經過 npm run eject 這種方式改造過一次多頁面的配置,至關於說已經拆過這個箱子了,基本上了解了這個箱子裏有什麼,所以本文方案的配置實際上是對照着 npm run eject 這個方案中的配置文件來逐步進行的。git

版本的變更

使用 CRA 腳手架命令生成的項目免去了咱們本身配置 webpack 的麻煩,其內置的各類配置所須要的插件和依賴包的版本都是肯定的,是通過了檢驗的成熟配置,不會由於其中某個依賴包版本的問題形成構建出錯。但當咱們決定要本身動手配置 webpack 的時候,就意味着咱們要本身根據須要安裝一些 plugin 或 npm 依賴包,而這些插件和依賴包的版本可能不適用於當前 CRA 的版本,從而形成構建過程當中出現一些不可預期的錯誤。github

所以,咱們須要查看 CRA 腳手架項目的 package.json 文件,對於其中已經列出的 dependencies 依賴包,咱們不該該改變這些依賴包的版本,而對於未列出的 npm 包,咱們在使用的過程當中須要逐個驗證,不要一次性安裝不少個 npm 包,不然執行構建命令的時候若是出錯就會很難排查,最好是根據咱們須要的功能逐個的安裝相應的 npm 包,肯定沒有問題,再進行下一個功能的擴展。web

正是因爲版本的變更會對配置產生很大的影響,所以當咱們肯定了一個配置方案以後,不要再輕易去改動其中涉及到的 npm 包的版本,經過 package-lock.json 文件鎖定版本,防止配置方案錯亂。

另外,在本文編輯的時候,CRA 的最新版本爲 v2.1.2,這個版本的 CRA 生成的項目,當前版本的 react-app-rewired v1.6.2 已經沒法使用,具體信息能夠參見 CRA >=2.1.2 breaking issue 2.1.2。至少在本文編輯的時候(2019.01.05),是沒法適用於 CRA >=2.1.2 版本的,所以本文方案是基於最後一個能夠適用的 CRA v2.1.1 版原本作的,package.json 文件中其餘的 version 信息會附在方案後面。

2019-01-11 補充:上面提到的版本基本都是指 package.json 文件中列出的依賴包的版本,可是嚴格來說還應包含一些構建配置中使用的 node.js 工具包,好比 globbydir-glob等,這些工具包的版本更新也有可能會形成構建出錯。這種狀況的出現每每是沒法預期的,它們形成的影響通常比較普遍,而不只僅是出如今咱們方案配置的過程當中,這種錯誤基本上都會在 Github 上有相應的 issue 以及解決方法或修復措施,本文中也列出了遇到的一個這種類型的錯誤,詳見 4、錯誤排查 章節中的 其餘錯誤 一節。

2、準備工做

建立一個 CRA v2.1.1 項目

咱們的配置方案是基於 CRA v2.1.1 腳手架項目進行改造的,所以首先咱們要先建立一個 CRA 項目,詳見官方文檔:Create React App

可是這樣建立出來的項目默認是最新版本的 CRA 項目,咱們在上文中已經說明,咱們的配置方案是要求特定版本的 CRA 項目的,那麼如何建立特定的 CRA v2.1.1 版本項目?

CRA 項目版本的核心其實就是 react-scripts 的版本,咱們能夠先建立一個最新版本的腳手架項目,而後更改成 v2.1.1 版本,具體以下:

  1. 建立一個最新版本的 CRA 項目,參見官方文檔:Create React App
  2. 刪除 node_modules 文件夾,若是有 package-lock.json 文件或 yarn.lock 文件,也要一併刪除,不然從新安裝 node_modules 依賴包仍然會被鎖定爲原來的版本;
  3. 修改 package.json 文件,從新指定 react-scripts 的版本:

    "dependencies": {
      - "react": "^16.7.0",
      + "react": "^16.6.3",
      - "react-dom": "^16.7.0",
      + "react-dom": "^16.6.3",
      - "react-scripts": "2.1.3"
      + "react-scripts": "2.1.1"
    }
  4. 執行 yarn installnpm install 從新安裝項目依賴。

至此,咱們已經建立了一個 CRA v2.1.1 項目,這將做爲咱們進行多頁面入口編譯改造的基礎。

react-app-rewired 的用法

首先要學習 react-app-rewired 的用法,參照官方文檔:How to rewire your create-react-app project
主要是三點:

  1. 安裝 react-app-rewired
  2. 建立一個 config-overrides.js 文件
  3. 修改 package.json 文件中的腳本命令

查看 CRA 項目原有的配置文件

上面咱們提到在進行多頁面配置以前,須要對腳手架原有的配置文件具備必定了解,那麼如何查看原有的配置文件?

  • 方法1:將使用 CRA 腳手架命令生成的項目拷貝一份,執行 npm run eject 暴露配置文件,eject 以後文件組織結構以下:

    my-app
    ├── config
    │   ├── jest
    │   ├── env.js
    │   ├── paths.js
    │   ├── webpack.config.dev.js
    │   ├── webpack.config.prod.js
    │   └── webpackDevServer.config.js
    ├── node_modules
    ├── public
    ├── scripts
    │   ├── build.js
    │   ├── start.js
    │   └── test.js
    ├── package.json
    ├── README.md
    └── src

    其中 config 文件夾下就是腳手架原有的配置文件。

  • 方法2:使用 CRA 腳手架命令生成項目,在 my-app/node_modules/react-scripts/config 路徑下能夠看到原有的配置文件
  • 方法3:在 config-overrides.js 文件中將 webpack 配置對象輸出在控制檯,查看其結構

推薦使用第 1 種方式,便於咱們在配置過程當中查看和操做,第 3 種方式做爲一種輔助手段,主要是用於改造過程當中的調試和驗證配置對象的改造結果的。

項目文件組織結構

在開始進行多入口配置以前,須要先明確項目的文件組織結構,這關係到咱們如何準確獲取全部的入口文件,這裏再也不詳述,請參見React-CRA 多頁面配置(npm run eject)中的項目文件組織結構一節。

3、具體方案

  1. 安裝 react-app-rewired,建立 config-overrides.js 文件,修改 package.json 文件中的腳本命令。詳見官方文檔 How to rewire your create-react-app project

    執行 yarn start 命令,查看 http://localhost:3000,確保安裝 react-app-rewired 操做沒有問題。

  2. 修改文件組織結構。這裏再也不詳述,參見上文 項目文件組織結構 一節。
    CRA項目原文件組織結構爲:

    my-app
      ├── README.md
      ├── node_modules
      ├── package.json
      ├── config-overrides.js // 安裝 react-app-rewired 時建立的
      ├── .gitignore
      ├── public
      │   ├── favicon.ico
      │   ├── index.html
      │   └── manifest.json
      └── src
          ├── App.css
          ├── App.js
          ├── App.test.js
          ├── index.css
          ├── index.js
          ├── logo.svg
          └── serviceWorker.js

    修改成多頁面入口編譯的文件組織結構:

    // 本方案示例項目有兩個頁面 index.html & admin.html
      // 這裏給出的文件組織結構是配置完成後的完整結構, 有些文件(如 src/setupProxy.js)的具體做用後面會給出說明
      my-app
      ├── README.md
      ├── node_modules
      ├── package.json
      ├── config-overrides.js // 安裝 react-app-rewired 時建立的
      ├── .gitignore
      ├── public
      │   ├── favicon.ico
      │   ├── index.html // 做爲全部頁面的 html 模板文件
      │   └── manifest.json
      └── src
          ├── index.js // 空白文件, 爲了不構建報錯, 詳見下文
          ├── setupProxy.js // proxy 設置, 詳見下文(在當前操做步驟中能夠缺失)
          ├── index // index.html 頁面對應的文件夾
          │   ├── App.less
          │   ├── App.js
          │   ├── App.test.js
          │   ├── index.less // 使用 less 編寫樣式文件
          │   ├── index.js
          │   ├── logo.svg
          │   └── serviceWorker.js
          └── admin // admin.html 頁面對應的文件夾
              ├── App.less
              ├── App.js
              ├── App.test.js
              ├── index.less // 使用 less 編寫樣式文件
              ├── index.js
              ├── logo.svg
              └── serviceWorker.js

    執行 yarn start 命令,查看 http://localhost:3000/index.html 和 http://localhost:3000/admin.html,確保修改文件組織結構操做沒有問題。

    這個示例項目是以 my-app/public/index.html 做爲全部頁面的 html 模板文件的,固然也能夠分別指定不一樣的 html 模板文件,這是根據項目須要和項目文件組織結構決定的。在這個示例項目中,因爲做爲模板的 html 文件只須要有個根元素便可,所以將其做爲全部入口的 html 模板文件。這樣的話,每一個頁面的 <title></title> 就須要在各自頁面中分別指定,通常能夠在頁面掛載以後進行操做,好比:

    class App extends Component {
        componentDidMount() {
          document.title = 'xxx';
        }
        render() {
          return (
            ...
          );
        }
      }
  3. 修改 config-overrides.js 文件,進行具體配置。實際上咱們以後全部的操做都是在這個文件中進行的。
    咱們的測試方案是一個使用了 Ant Design、Redux、Less、Echarts 的多頁面項目,須要達到如下要求:

    • 指定頁面的多入口文件路徑以及入口文件對應的 html 模板
    • 實現 antd 組件的按需加載
    • 增長對 less 文件的支持
    • 更改輸出的文件名
    • 增長對 html 文檔中圖片路徑的處理
    • 更改代碼拆分的配置
    • 設置別名路徑

上述每個功能的實現,都須要執行 yarn startyarn build 進行驗證,確保操做成功後再進行下一項的配置。這裏再也不逐步說明配置的步驟,詳見下面的代碼及註釋。

最終 config-overrides.js 文件內容以下:

/* config-overrides.js */
  /*
  * @Author: mzhang.eric 
  * @Last Modified time: 2019-01-10 18:37:17
  * @Description: 使用 react-app-rewired 擴展和改造 CRA v2.1.1 項目, 基於 webpack v4.19.1  + react-app-rewired v1.6.2 版本
  */

  const rewireLess = require('react-app-rewire-less');
  const { injectBabelPlugin, paths } = require('react-app-rewired');
  const HtmlWebpackPlugin = require('html-webpack-plugin');
  const path = require('path');
  const fs = require('fs');
  const globby = require('globby');
  const appDirectory = fs.realpathSync(process.cwd());
  const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
  // const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

  module.exports = function override(config, env) {

    // 使用 babel-plugin-import 按需加載組件
    config = injectBabelPlugin(
      ['import', { libraryName: 'antd', libraryDirectory: 'es', style: true }],
      config,
    );

    // 增長 less 支持
    config = rewireLess.withLoaderOptions({
      // 解決報錯: Inline JavaScript is not enabled. Is it set in your options?
      javascriptEnabled: true,
    })(config, env);

    // 入口文件路徑
    // const entriesPath = globby.sync([resolveApp('src') + '/*/index.js']);
    const entriesPath = globby.sync([resolveApp('src') + '/*/index.js'], {cwd: process.cwd()});
    paths.entriesPath = entriesPath;

    // 獲取指定路徑下的入口文件
    function getEntries(){
      const entries = {};
      const files = paths.entriesPath;
      files.forEach(filePath => {
        let tmp = filePath.split('/');
        let name = tmp[tmp.length - 2];
        if(env === 'production'){
          entries[name] = [
            require.resolve('./node_modules/react-scripts/config/polyfills.js'),
            filePath,
          ];
        } else {
          entries[name] = [
            require.resolve('./node_modules/react-scripts/config/polyfills.js'),
            require.resolve('react-dev-utils/webpackHotDevClient'),
            filePath,
          ];
        }
      });
      return entries;
    }

    // 入口文件對象
    const entries = getEntries();

    // 配置 HtmlWebpackPlugin 插件, 指定入口文件生成對應的 html 文件
    let htmlPlugin;
    if(env === 'production'){
      htmlPlugin = Object.keys(entries).map(item => {
        return new HtmlWebpackPlugin({
          inject: true,
          template: paths.appHtml,
          filename: item + '.html',
          chunks: [item],
          minify: {
            removeComments: true,
            collapseWhitespace: true,
            removeRedundantAttributes: true,
            useShortDoctype: true,
            removeEmptyAttributes: true,
            removeStyleLinkTypeAttributes: true,
            keepClosingSlash: true,
            minifyJS: true,
            minifyCSS: true,
            minifyURLs: true,
          },
        });
      });
    } else {
      htmlPlugin = Object.keys(entries).map(item => {
        return new HtmlWebpackPlugin({
          inject: true,
          template: paths.appHtml,
          filename: item + '.html',
          chunks: [item],
        });
      });
    }

    if (env === 'production') {
      for (let i = 0; i < config.plugins.length; i++) {
        let item = config.plugins[i];

        // 更改輸出的樣式文件名
        if (item.constructor.toString().indexOf('class MiniCssExtractPlugin') > -1) {
          item.options.filename = 'static/css/[name].css?_v=[contenthash:8]';
          item.options.chunkFilename = 'static/css/[name].chunk.css?_v=[contenthash:8]';
        }

        // SWPrecacheWebpackPlugin: 使用 service workers 緩存項目依賴
        if(item.constructor.toString().indexOf('function GenerateSW') > -1){
          // 更改輸出的文件名
          item.config.precacheManifestFilename = 'precache-manifest.js?_v=[manifestHash]';
        }
      }

      // 更改生產模式輸出的文件名
      config.output.filename = 'static/js/[name].js?_v=[chunkhash:8]';
      config.output.chunkFilename = 'static/js/[name].chunk.js?_v=[chunkhash:8]';

    } else {
      // 更改開發模式輸出的文件名
      config.output.filename = 'static/js/[name].js';
      config.output.chunkFilename = 'static/js/[name].chunk.js';
    }

    // 修改入口
    config.entry = entries;

    // 修改 HtmlWebpackPlugin 插件
    for (let i = 0; i < config.plugins.length; i++) {
      let item = config.plugins[i];
      if (item.constructor.toString().indexOf('class HtmlWebpackPlugin') > -1) {
        config.plugins.splice(i, 1);
      }
    }

    config.plugins.push(...htmlPlugin);

    // 分析打包內容
    // config.plugins.push(new BundleAnalyzerPlugin());

    // 設置別名路徑
    config.resolve.alias = {
      ...config.resolve.alias,
      '@src': paths.appSrc, // 在使用中有些 Eslint 規則會報錯, 禁用這部分代碼的 Eslint 檢測便可
    };

    // 處理 html 文檔中圖片路徑問題
    config.module.rules[2].oneOf.push({
      test: /\.html$/,
      loader: 'html-withimg-loader'
    });

    // 修改 build/static/media/ 路徑下的文件名
    for (let i = 0; i < config.module.rules[2].oneOf.length; i++) {
      const item = config.module.rules[2].oneOf[i];
      if(!item.options || !item.options.name){ 
        continue;
      }
      let str = item.options.name.toString();
      if(str.indexOf('static/media/[name].[hash:8].[ext]') > -1){
        item.options.name = 'static/media/[name].[ext]?_v=[hash:8]';
      }
    }

    // 修改代碼拆分規則,詳見 webpack 文檔:https://webpack.js.org/plugins/split-chunks-plugin/#optimization-splitchunks
    config.optimization = {
      splitChunks: {
        // 將全部入口點共同使用到的、次數超過 2 次的模塊,建立爲一個名爲 commons 的代碼塊
        // 這種配置方式可能會增大初始的捆綁包,好比有些公共模塊在首頁其實並未用到,但也會打包進來,會下降首頁的加載性能
        // 建議將非必需模塊使用 import() 的方式動態加載,提高頁面的加載速度
        // cacheGroups: {
        //   commons: {
        //     name: 'commons',
        //     chunks: 'initial',
        //     minChunks: 2
        //   }
        // }

        // 將全部使用到的 node_modules 中的模塊包打包爲 vendors 代碼塊。(不推薦)
        // 這種方式可能會產生一個包含全部外部依賴包的較大代碼塊,建議只包含核心框架和工具函數代碼,其餘依賴項動態加載
        // cacheGroups: {
        //   commons: {
        //     test: /[\\/]node_modules[\\/]/,
        //     name: 'vendors',
        //     chunks: 'all'
        //   }
        // }

        cacheGroups: {
          // 經過正則匹配,將 react react-dom echarts-for-react 等公共模塊拆分爲 vendor
          // 這裏僅做爲示例,具體須要拆分哪些模塊須要根據項目須要進行配置
          // 能夠經過 BundleAnalyzerPlugin 幫助肯定拆分哪些模塊包
          vendor: {
            test: /[\\/]node_modules[\\/](react|react-dom|echarts-for-react)[\\/]/,
            name: 'vendor',
            chunks: 'all', // all, async, and initial
          },

          // 將 css|less 文件合併成一個文件, mini-css-extract-plugin 的用法請參見文檔:https://www.npmjs.com/package/mini-css-extract-plugin
          // MiniCssExtractPlugin 會將動態 import 引入的模塊的樣式文件也分離出去,將這些樣式文件合併成一個文件能夠提升渲染速度
          // 其實若是能夠不使用 mini-css-extract-plugin 這個插件,即不分離樣式文件,可能更適合本方案,可是我沒有找到方法去除這個插件
          styles: {            
            name: 'styles',
            test: /\.css|less$/,
            chunks: 'all',    // merge all the css chunk to one file
            enforce: true
          }
        },
      },
    };

    return config;
  };

4、錯誤排查

這裏對方案造成過程當中遇到的錯誤進行記錄,這些錯誤有些是配置過程當中一定會出現的,是須要咱們進行改造的,也有些錯誤多是操做不當主觀形成的。

客觀錯誤

這類錯誤是進行多頁面入口編譯改造過程當中一定會遇到的,有些是因爲項目文件組織結構的改變形成的,也有些是爲了擴展項目配置而使用了源 CRA 腳手架項目未提供的 npm 包形成的,前者會出現哪些錯誤咱們是能夠明確知道的,後者相對來講是不確切的,但大多都是版本不適用形成的。

  1. Could not find a required file.

    Could not find a required file.
    Name: index.js
    Searched in: C:xxxxxxmy-appsrc

    錯誤描述:經過 create-react-app 腳手架搭建的項目以 my-app/src/index.js 做爲應用入口,當執行構建腳本時若是檢測到缺失了這個必要文件,node 進程會退出。可是在咱們進行多頁面入口改造的時候,src 直接路徑下沒有 index.js 文件,根據咱們的文件組織結構,index.js 文件存在於每一個獨立頁面的子文件夾下,如 my-app/src/index/index.js my-app/src/admin/index.js
    解決方法:在 src 直接路徑下建立一個空的 index.js 文件。

  2. Inline JavaScript is not enabled. Is it set in your options?

    // https://github.com/ant-design...
    .bezierEasingMixin();^ Inline JavaScript is not enabled. Is it set in your options?
    in C:xxxsrcmy-appnode_modulesantdesstylecolorbezierEasing.less (line 110, column 0)

    錯誤描述:less、less-loader 配置問題,提示須要容許行內 js 的執行,好比當咱們在項目中使用 antd 組件時,若是引入的樣式文件是 less 文件,構建時就會報上述錯誤。
    解決方法:在 config-overrides.js 文件中增長 less 文件支持時,設置 javascriptEnabled 爲 true。

    module.exports = function override(config, env) {
        ...
        // 增長 less 支持
        config = rewireLess.withLoaderOptions({
          // 解決報錯: Inline JavaScript is not enabled. Is it set in your options?
          javascriptEnabled: true,
        })(config, env);
        ...
        return config;
      };
  3. Failed to load resource: net::ERR_FILE_NOT_FOUND(build 版本)

    錯誤描述:執行 yarn buildnpm run build 構建生產版本時,構建出的頁面未能正確加載樣式和腳本文件,chrome 檢查工具報路徑錯誤。
    解決方法:修改 package.json 文件,指定 homepage 字段的值,本項目這裏指定爲相對路徑。

    "homepage": "./",
  4. When specified, "proxy" in package.json must be a string.

    When specified, "proxy" in package.json must be a string.
    Instead, the type of "proxy" was "object".
    Either remove "proxy" from package.json, or make it a string.

    錯誤描述:咱們在開發過程當中通常會在 package.json 文件中配置 proxy 代理服務器,可是在 CRA 2.x 升級之後對 proxy 的設置作了修改,具體請參見官方升級文檔:Move advanced proxy configuration to src/setupProxy.js
    解決方法:移除 package.json 文件中有關 proxy 的設置,使用 http-proxy-middleware,在 src 目錄下建立 setupProxy.js 文件。詳細方法請參見上述文檔。

主觀錯誤

這類錯誤應該是能夠避免的,可是在配置過程當中可能會因爲開發人員的不當操做形成出錯,最主要的就是安裝了錯誤版本的 plugin 或 npm 包,咱們上面已經說過,對於項目本來的配置中在 package.json 文件中已經列出的 plugin 和 npm 包,不要再重複進行安裝,不然可能從新安裝的 plugin 或 npm 包版本是不適用的,從而形成出錯。

例如,CRA v2.1.1 腳手架項目原有的 html-webpack-plugin 版本是 v4.0.0-alpha.2,是不須要再另外進行安裝的。若是此時開發人員本身執行了 yarn add html-webpack-plugin,從而將 html-webpack-plugin 的版本更換爲了 v3.2.0,這就會形成構建報錯:URIError: Failed to decode param '/%PUBLIC_URL%/favicon.ico' URIError: Failed to decode param '/%PUBLIC_URL%/manifest.json',其餘主觀錯誤相似。

  1. html-webpack-plugin 版本錯誤

    URIError: Failed to decode param '/%PUBLIC_URL%/favicon.ico'
    URIError: Failed to decode param '/%PUBLIC_URL%/manifest.json'

    錯誤描述:在執行 yarn start 構建命令時報錯,頁面空白。
    解決方法:html-webpack-plugin 版本錯誤,在本文的配置方案中,應當使用 "html-webpack-plugin": "4.0.0-alpha.2", 版本。

  2. TypeError: Cannot read property 'state' of undefined(頁面報錯)

    錯誤描述:編譯構建過程沒有報錯,但頁面報錯:TypeError: Cannot read property 'state' of undefined。
    解決方法:redux 版本錯誤,在本文的配置方案中,應當使用 redux <=3.7.2 版本。

其餘錯誤

咱們在上文 版本的變更 一節的補充中已經對此有所說起,這類錯誤主要是由 node.js 工具包版本的升級形成的,這些錯誤基本都是不可預期的,也沒法在這裏所有涵蓋,只能就當前遇到的問題進行簡要記錄,可能隨着時間的推移,還會出現其餘的相似問題,也可能這些錯誤已經在後續的版本中被修復了,所以請勿糾結於這裏記錄的錯誤,若是遇到了這類錯誤,就查閱資料進行修正,若是沒有遇到,則無須理會。

  1. TypeError: Expected cwd to be of type string but received type undefined

    C:xxxmy-appnode_modulesdir-globindex.js:59
    throw new TypeError( Expected \cwd` to be of type `string` but received type `${typeof opts.cwd}``);
    TypeError: Expected cwd to be of type string but received type undefined

    錯誤描述:本文的寫做開始於 2019-01-05,在 2019-01-11 從新審覈本文方案的時候,遇到了這個錯誤,主要是因爲 dir-glob 版本的升級形成的,咱們在配置腳本中使用了 globby 的 sync 方法,dir-glob 版本升級以後,這個方法的調用會使得 dir-glob 拋出上述錯誤。詳細信息參見:Broken build do to major change from 2.0 to 2.2 以及 globby will pass opts.cwd = undefined to dir-glob, which leads to TypeError.

    解決方法:這裏給出的解決方法是限定於當前時間的,由於在本文編輯的時候(2019-01-11)這個 issue 尚未給出最終的解決方案,我的以爲可能會由 globby 進行修復。

    /* config-overrides.js */
    // 修改獲取入口文件路徑的代碼
    // const entriesPath = globby.sync([resolveApp('src') + '/*/index.js']);
    const entriesPath = globby.sync([resolveApp('src') + '/*/index.js'], {cwd: process.cwd()});

5、package.json 信息

本文的多頁面配置方案是基於 CRA 腳手架項目的,項目的依賴包信息會包含在兩個 package.json 文件中,其中腳手架自帶的配置信息位於 my-app/node_modules/react-scripts/package.json 文件中,而咱們根據項目須要本身增長的配置信息在 my-app/package.json 文件中,這裏將本方案項目配置信息附錄以下:

/* package.json */
  {
    "name": "my-app",
    "version": "0.1.0",
    "private": true,
    "dependencies": {
      "antd": "^3.12.1",
      "babel-plugin-import": "^1.11.0",
      "echarts": "^4.2.0-rc.2",
      "echarts-for-react": "^2.0.15-beta.0",
      "html-withimg-loader": "^0.1.16",
      "http-proxy-middleware": "^0.19.1",
      "react": "^16.6.3",
      "react-app-rewire-less": "^2.1.3",
      "react-app-rewired": "^1.6.2",
      "react-dom": "^16.6.3",
      "react-intl": "^2.7.2",
      "react-lazyload": "^2.3.0",
      "react-loadable": "^5.5.0",
      "react-redux": "^6.0.0",
      "react-scripts": "2.1.1",
      "redux": "3.7.2",
      "redux-promise-middleware": "^5.1.1",
      "webpack-bundle-analyzer": "^3.0.3"
    },
    "scripts": {
      "start": "react-app-rewired start",
      "build": "react-app-rewired build",
      "test": "react-app-rewired test --env=jsdom",
      "eject": "react-scripts eject"
    },
    "eslintConfig": {
      "extends": "react-app"
    },
    "browserslist": [
      ">0.2%",
      "not dead",
      "not ie <= 11",
      "not op_mini all"
    ],
    "homepage": "./"
  }
相關文章
相關標籤/搜索