1. 程式人生 > 其它 >create react app配置多頁面應用

create react app配置多頁面應用

技術標籤:reactwebpackreact

最近在配置react的多頁面應用,在此記錄下來。

準備工作

首先根據create react app官網新建應用:

npm install -g create-react-app
npx create-react-app my-app
cd my-app
npm start

這時我們就初始新建了一個react基本應用了。

將應用配置開啟

由於我們需要自定義一些webpack配置,先把原來腳手架的配置提取出來。

npm run eject

這時我們的package.json以及目錄會有所改變 多出了scripts資料夾和config資料夾。

更改目錄

我們需要構建多頁面應用,首先將src目錄下,新建一個pages目錄,下面再新建你的多頁面檔案 例如:
在這裡插入圖片描述
這裡我把原來src下的首頁檔案拷貝轉進index和login下,這樣就成了兩個頁面應用入口。

開始配置

目錄更改後,我們清楚
1.pages目錄下以後我們新建的頁面都會在下面對應增加檔案。
2.且入口都為新建頁面下的index,js。
邏輯上我們應該需要在webpack下配置entry和output配置項。

  • 我們先來配置entry和output
    在config目錄下的webpack.config.js檔案找到entry選項,我們可以看到它只有一個入口為paths.appIndexJs,我們找到paths是在paths.js下定義的,所以我們需要將這個appIndexJs單項改為一個數組,包含我們src/pages目錄下的所有檔案下的index.js。
    這時我們要遍歷pages生成這個陣列。
    現在我們在paths.js做操作:
//這裡獲取所有的入口檔案生成物件對應所有的路徑
function getEntries(globPath) {
  const files = glob.sync(globPath),
    entries = {};
  files.forEach(function(filepath) {
    const split = filepath.split('/');
    const name = split[split.length - 2];
    entries[name] = './' + filepath;
}); return entries; } const entries = getEntries('src/pages/**/index.js'); //這裡將入口物件轉為路徑陣列 function getIndexJs() { const indexJsList = []; Object.keys(entries).forEach((name) => { const indexjs = resolveModule(resolveApp, `src/pages/${name}/index`) indexJsList.push({ name, path: indexjs }); }) return indexJsList; } const indexJsList = getIndexJs() // config after eject: we're in ./config/ module.exports = { dotenv: resolveApp('.env'), appPath: resolveApp('.'), appBuild: resolveApp('build'), appPublic: resolveApp('public'), appHtml: resolveApp('public/index.html'), appIndexJs: indexJsList, appPackageJson: resolveApp('package.json'), appSrc: resolveApp('src'), appTsConfig: resolveApp('tsconfig.json'), appJsConfig: resolveApp('jsconfig.json'), yarnLockFile: resolveApp('yarn.lock'), testsSetup: resolveModule(resolveApp, 'src/setupTests'), proxySetup: resolveApp('src/setupProxy.js'), appNodeModules: resolveApp('node_modules'), swSrc: resolveModule(resolveApp, 'src/service-worker'), publicUrlOrPath, entries };

這時paths.appIndexJs就是一個數組,現在回到webpack.config.js,修改entry和output:

const entry = {};
  paths.appIndexJs.forEach(e => {
    entry[e.name] = [
      isEnvDevelopment && !shouldUseReactRefresh && webpackDevClientEntry,
      e.path
    ].filter(Boolean);
  });
  return {
  	...
  	entry: entry,
  	output: {
  		...
  		filename: isEnvProduction
        ? 'static/js/[name]/[name].[contenthash:8].js'
        : isEnvDevelopment && 'static/js/[name]/[name].bundle.js',
 		...
 		chunkFilename: isEnvProduction
        ? 'static/js/[name]/[name].[contenthash:8].chunk.js'
        : isEnvDevelopment && 'static/js/[name]/[name].chunk.js',
  	}
  }

ouput裡的更改主要是在輸出檔案時的目錄結構做調整,也可以在webpack.config.js這個檔案下搜尋static/更改css目錄等。

  • 增加HtmlWebpackPlugin
    複製編譯多個html
...
plugins: [
	// Generates an `index.html` file with the <script> injected.
      ...Object.keys(paths.entries).map((name) => {
        return new HtmlWebpackPlugin(
          Object.assign(
            {},
            {
              inject: true,
              chunks: [name],
              template: paths.appHtml,
              filename: name + '.html',
            },
            isEnvProduction
              ? {
                  minify: {
                    removeComments: true,
                    collapseWhitespace: true,
                    removeRedundantAttributes: true,
                    useShortDoctype: true,
                    removeEmptyAttributes: true,
                    removeStyleLinkTypeAttributes: true,
                    keepClosingSlash: true,
                    minifyJS: true,
                    minifyCSS: true,
                    minifyURLs: true,
                  },
                }
              : undefined
          )
        );
      }),
]
  • 接下來更改ManifestPlugin的plugin配置
    由於原來是單頁面入口,所以manifest也是生成的一個頁面的。更改為多頁面後要就要多加一層遍歷了。程式碼如下:
...
new ManifestPlugin({
	...,
	generate: (seed, files, entrypoints) => {
	},
	...
	const manifestFiles = files.reduce((manifest, file) => {
     manifest[file.name] = file.path;
       return manifest;
     }, seed);
     // const entrypointFiles = entrypoints.main.filter(
     //   fileName => !fileName.endsWith('.map')
     // );
     const entrypointFiles = {};
     Object.keys(entrypoints).forEach((entry)=>{
       const files = entrypoints[entry].filter(
         fileName => !fileName.endsWith('.map')
       );
       entrypointFiles[entry]=files;
     });
     
     return {
        files: manifestFiles,
        entrypoints: entrypointFiles,
      };
}),
...
  • 最後
    到這一步webpack配置邏輯上應該是沒有問題了。
    但當我們執行start或者build的時候會出現一個報錯,是在start.js或者是在build.js裡的報錯。這是因為裡面有這樣一行程式碼作為檢驗必要檔案的函式判斷:
// Warn and crash if required files are missing
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
  process.exit(1);
}

這裡的意思是當不存在必要檔案時會退出。
由於我們把paths.appIndexJs更改成了陣列,所以這個函式導致了錯誤,原本應該是傳入單入口路徑的string。
在這裡我是直接註釋了這段程式碼,你也可以參照它的原始碼自己動手寫個這樣一個的檢查函式。
至此,一個多頁面react就配置好了。

總結

對於webpack的修改我們總認為配置項繁雜,所以看到報錯時都會心急改不好。需要大概知道哪些地方配置什麼功能,再結合需求思考邏輯下應該要修改的配置項,就會有思路了,問題也會慢慢得到解決。
雖然也總是會有在怪異問題上卡很久也搞不懂問題出在哪裡的時候,但嘗試解決問題這個過程也會學到很多。