源码我放在码云上了: yjGeek/react-many-page 需要的可以拉下来

前言

node 版本很重要,>=8.10, 因为这将决定create-react-app用的依赖是否是最新的(第一次遇到要根据node版本来下载依赖的),如果你小于8.10的话,你下载下来的依赖都是很旧的(如: webpack: v1.4),这个是我踩的最深的一个坑了, 所以要着重说明。

我搭建这个框架的环境:
nodev11.12.0
create-react-appv3.0.1

tip: node推荐采用macOS/LinuxnvmWindowsnvm-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、各种自动化(拒绝手动引入)、异步加载等!