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