源码我放在码云上了: yjGeek/react-many-page 需要的可以拉下来
前言
node 版本很重要,>=8.10, 因为这将决定create-react-app用的依赖是否是最新的(第一次遇到要根据node版本来下载依赖的),如果你小于8.10的话,你下载下来的依赖都是很旧的(如: webpack: v1.4),这个是我踩的最深的一个坑了, 所以要着重说明。
我搭建这个框架的环境:
node:v11.12.0
create-react-app: v3.0.1
tip: node推荐采用macOS/Linux用nvm, Windows 用nvm-windows
要更改的文件
- 在src目录添加entrances目录,作为我们的入口文件,一个js文件代表一个入口
- 在config目录添加
getEntrance.js
文件来自动读取上面的入口文件夹 - 更改config目录的
webpack.config.js
path.js
的内容 - 更改scripts目录的
start.js
build.js
的内容
下面就按照上面的顺序来依次讲下
1.添加entrances目录
在里面新建两个入口文件 admin.js
user.js
// 经典开场 hello word
import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(
<div>hello word admin </div>,
document.getElementById('root')
);
2.添加getEntrance.js
文件
遍历src/entrances
下的所有js文件
const path = require('path');
const fs = require('fs');
const entrancePaths = path.resolve(__dirname, '../src/entrances')
const files = fs.readdirSync(entrancePaths);
let entrances = [];
files.forEach(item => {
const { ext, name} = path.parse(item);
// 只有js文件才算是入口文件,如果有用jsx、ts的自行修改
if (ext === '.js') {
entrances.push(name)
}
})
module.exports = entrances;
3.更改config目录的 webpack.config.js
path.js
webpack.config.js
// 导入入口文件
const entrances = require('./getEntrance');
module.exports = function (webpackEnv) {
// 在return 前面插入
// 配置多个入口
let entrys = {};
// 生成多个入口文件
let pluginsHtmls = [];
entrances.forEach(name => {
// 如果是生产环境 不等于当前打包的应用不给他进去
const dirName = env.raw.BUILD_ENV;
// 如果是生产环境 每次只能一个入口
if (isEnvProduction && name !== dirName) {
return false;
}
// 配置入口文件
entrys[name] = [
isEnvDevelopment &&
require.resolve('react-dev-utils/webpackHotDevClient'),
path.resolve(paths.appEntranceJs, name + '.js')
].filter(Boolean)
pluginsHtmls.push(
new HtmlWebpackPlugin(
Object.assign(
{},
{
inject: true,
chunks: [name],
template: paths.appHtml,
// 如果是开发环境区分入口就用每个入口的文件名来区分 如: admin.html、user.html
filename: isEnvProduction ? `${dirName}/index.html` : 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
)
)
)
});
// 修改return的返回内容 以下是要修改的代码
return {
// 入口改成上面的
entry: entrys,
output: {
filename: isEnvProduction ? 'static/js/[name].[contenthash:8].js' : isEnvDevelopment && 'static/js/[name].[hash].js',
chunkFilename: isEnvProduction ? 'static/js/[name].[contenthash:8].chunk.js' : isEnvDevelopment && 'static/js/[name].[hash].chunk.js',
},
plugins: [
// 把new HtmlWebpackPlugin()删除了 改成上面的pluginsHtmls定义的变量
...pluginsHtmls,
// 然后把下面的来跟实例注释掉 new ManifestPlugin new WorkboxWebpackPlugin.GenerateSW 这个好像是用来做pwa的,不要他,如果你有需要自行去配置
]
}
}
path.js
module.exports = {
// 添加这个文件路径
appEntranceJs: resolveApp('src/entrances'),
};
4.更改scripts目录的 start.js
build.js
start.js
// 导入各种东西
const entrances = require('../config/getEntrance');
const getClientEnvironment = require('../config/env');
const env = getClientEnvironment();
// 把下面检查入口去掉 大概55行
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
process.exit(1);
}
// 添加入口来判断
if (!entrances[0]) {
process.exit(1);
}
// 把checkBrowsers的第二个then 加入以下代码
let isEntrance = false;
// 如果有指定某个入口,怎么指定入口? 运行这条命令: set START_ENV=user&& node scripts/start.js
entrances.forEach(name => {
if (env.raw.START_ENV === name) {
isEntrance = true;
urls.localUrlForBrowser += name + '.html'
}
})
// 防止没有指定入口 默认第一个
if (!isEntrance && entrances[0]) {
urls.localUrlForBrowser += entrances[0] + '.html'
}
build.js
// 声明各种变量
const entrances = require('../config/getEntrance');
const getClientEnvironment = require('../config/env');
const env = getClientEnvironment();
const appName = env.raw.BUILD_ENV;
if (!entrances[0]) {
printErrors('没有检测到要打包的应用');
process.exit(1);
}
if (!appName && !entrances.includes(appName)) {
printErrors('应用不能为空');
process.exit(1);
}
const buildPath = path.resolve(paths.appBuild, appName)
// 把checkBrowsers的第一个then 修改成
then(previousFileSizes => {
return measureFileSizesBeforeBuild(buildPath);
})
// 把checkBrowsers的第二个then 修改成
then(previousFileSizes => {
fs.emptyDirSync(buildPath);
copyPublicFolder();
return build(previousFileSizes);
})
// 修改下build方法
compiler.run((err, stats) => {
if (err) {
// ...
} else {
// 把static剪切到对应的应用下
let newModules = path.join(buildPath, 'static')
fs.rename(path.join(paths.appBuild, 'static'), newModules, function (err) {
if (err) {
printErrors('Failed to compile.', err);
process.exit(1);
}
})
}
})
// 修改下这个方法
function copyPublicFolder() {
// 复制路径
fs.copySync(paths.appPublic, buildPath, {
dereference: true,
filter: file => file !== paths.appHtml
});
}
ok 以上就完成多页面的一个配置
然后来试下开发环境下
$ npm start
打开的应该是: http://localhost:3000/user.html
你可以把上面的地址改成: http://localhost:3000/admin.html 来测试下
再来试下生产环境
生产环境必须要一个一个来打包,你可以在package.json 的script的build命令改成:
set BUILD_ENV=admin&& node scripts/build.js && set BUILD_ENV=user&& node scripts/build.js
$ npm run build
看下效果图:
打包出来的目录
以上是我针对create-react-app 多页面的一次采坑,后面还会出一个完整的项目框架,包括 封装请求、mock、各种自动化(拒绝手动引入)、异步加载等!