书接上回 React 多页面应用 - 路由配置
这次我们来实现一些不一样的需求,这是在日常工作中遇到的
首先我们来描述一下我们想要实现的需求
首先在这个项目中有多个项目(a,b,c,d…),假如我只改了a项目的代码,按照我们之前的配置,发版的时候会把所有的项目都一起发上去,这样一个问题是会造成资源浪费(CDN 需要重新缓存),另一个问题是如果有修改到公共方法,会对未修改的项目造成影响,这个时候的需求是只发a项目相关的代码,其他项目的代码不会发上去。
接下来我们来看实现这个需求
实现项目单独打包
在上一个项目的基础上,我们可以发现多页面打包的基础是在 打包的时候配置的多入口,那么只需要把原来的多入口变成单入口,就可以实现按需打包了
所以在上一个项目的基础上,把 config.entry
和 config.plugins
的内容单独抽离出来,然后在 overrideConfig 方法内动态传入需要打包的组件入口名称,然后根据传入的组件名称动态获取 entry
和 plugins
的内容。具体实现代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
|
const { override } = require("customize-cra"); const htmlWebpackPlugin = require("html-webpack-plugin")
const entry = { main: "./src/index.js", page1: "./src/pages/page1/index.js", page2: "./src/pages/page2/index.js" }
const computedPluginList = (types) => { let pluginList = [] for(let i = 0; i < types.length; i++) { switch (types[i]) { case "main": pluginList.push(new htmlWebpackPlugin({ title: "main", template: "./public/index.html", filename: "main.html", chunks: ["main"] })) break; case "page1": pluginList.push(new htmlWebpackPlugin({ title: "Page1", template: "./public/index.html", filename: "page1.html", chunks: ["page1"] })) break; case "page2": pluginList.push(new htmlWebpackPlugin({ title: "Page2", template: "./public/index.html", filename: "page2.html", chunks: ["page2"] })) break; default: break; } } return pluginList } const overrideConfig = (paths) => (config) => { const innerEntry = {} if(paths) { for(let i = 0; i < paths.length; i++) { innerEntry[paths[i]] = entry[paths[i]] } } else { innerEntry.entry = entry }
config.output = { filename: "[name].[fullhash].js", path: __dirname + '/dist', }
config.plugins.push(...computedPluginList(paths)) return config } module.exports = override(overrideConfig(["page1", "main"]))
|
通过上述操作,我们可以实现根据传入的项目包的名称进行按需运行及按需打包
但是,这并不是最终的解决方案,我们不可能每次发版或者每次进行开发都要修改这个文件,这个操作太过繁琐,一旦忘了修改,就会发生比较严重的问题,这个时候需要对这个功能进行优化
项目单独打包优化
在经过诸多讨论后,最后敲定的优化方案是 本地开发时,运行所有的项目,在进行发版的时候根据 tag 名进行各个项目发版
package.json 文件修改
首先在 script 内新增一个命令 set:env
,这个命令会在 发版的时候用到,和 tag 一起用来设置发版的项目
1 2 3 4 5
| { "script": { "set:env": "REACT_APP_PROJECT=" } }
|
执行 set:env
时会向 process.env 新增变量 REACT_APP_PROJECT
可以用来判断当前发版项目,可以在项目全局进行访问,这里主要在config-overrides 文件里进行访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
|
const { override } = require("customize-cra"); const htmlWebpackPlugin = require("html-webpack-plugin")
const entry = { main: "./src/index.js", page1: "./src/pages/page1/index.js", page2: "./src/pages/page2/index.js" }
const computedPluginList = (types) => { let pluginList = [] for(let i = 0; i < types.length; i++) { switch (types[i]) { case "main": pluginList.push(new htmlWebpackPlugin({ title: "main", template: "./public/index.html", filename: "main.html", chunks: ["main"] })) break; case "page1": pluginList.push(new htmlWebpackPlugin({ title: "Page1", template: "./public/index.html", filename: "page1.html", chunks: ["page1"] })) break; case "page2": pluginList.push(new htmlWebpackPlugin({ title: "Page2", template: "./public/index.html", filename: "page2.html", chunks: ["page2"] })) break; default: break; } } return pluginList } const overrideConfig = () => (config) => { const innerEntry = {} let paths = ["main", "page1", "page2"] if(process.env.REACT_APP_PROJECT) { paths = [process.env.REACT_APP_PROJECT, "main"] } if(paths) { for(let i = 0; i < paths.length; i++) { innerEntry[paths[i]] = entry[paths[i]] } } else { innerEntry.entry = entry }
config.output = { filename: "[name].[fullhash].js", path: __dirname + '/dist', }
config.plugins.push(...computedPluginList(paths)) return config } module.exports = override(overrideConfig())
|
gitlab-ci.yml 文件修改
首先需要修改 gitlab-ci 文件内容,使其由原先的通过判断分支来发版,更改为判断 tag 名称发版,tag 需要定一个规则,用于区分正式,测试环境,我这边定义的规则 V-[环境名称]-[version]-[project]
在 gitlab CI/CD
进行发版时,首先需要获取到 tag 名,gitlab-ci 提供了 $CI_COMMIT_REF_NAME
可以访问到 tag 名
1 2 3 4 5 6 7 8 9
| test:sync-to-s3: only: - /V-T-US-.*/ script: - echo ${CI_COMMIT_REF_NAME} - echo ${CI_COMMIT_REF_NAME##*-}
|
修改了上述代码后,当我们打好以 V-T-US-
开头的 tag 后,会自动触发gitlab 的发版流程,执行过程中,会输出 当前tag 名称以及 project 名称。
获取完成之后,就需要根据获取到的工程名称,编写 node 代码,修改 package.json 与发包相关的 代码
首先创建 updatePackage.sh
1 2 3 4 5 6 7
| PROJECT_NAME=default if [ "$1" ]; then PROJECT_NAME=$1 fi node ./update.js $PROJECT_NAME ./package.json
|
在执行 updatePackage.sh
文件的时候,如果传入的有参数,则会 自动复制 PROJECT_NAME 变量,然后通过 node 执行 node 文件,并将传入的内容以及需要修改的文件路径传过去
需要注意的是,这里的路径都是基于执行 updatePackage.sh
的位置来说的,而不是updatePackage.sh
文件的位置
然后创建 update.js
文件,用来读取和修改 package.json
1 2 3 4 5 6 7 8 9 10
| const fs = require('fs'); const arg = process.argv.splice(2); const r = arg[arg.length - 1] const c = fs.readFileSync(r); const json = JSON.parse(c); const set_env = json.scripts['set:env'] const build_env = json.scripts['build:test'] json.scripts["build:test"] = set_env + arg[0] + " " + build_env fs.writeFileSync(r, JSON.stringify(json, null, 2));
|
修改 gitlab-ci.yml
文件 执行 updatePackage.sh
文件
1 2 3 4 5 6 7 8 9 10
| test:sync-to-s3: only: - /V-T-US-.*/ script: - echo ${CI_COMMIT_REF_NAME} - projectName=${CI_COMMIT_REF_NAME##*-} - sh ./script/updatePackage.sh $projectName
|
执行完上述命令后,gitlab 发版服务上拉取到的tag 代码,就会自动修改package.json 的内容,然后执行 打包命令的时候就会按照我们想要的发版的内容进行发版
扩展
如果有预渲染页面的,也可以同 updatePackage.sh
文件来动态的修改需要预渲染的页面,修改 gitlab-ci.yml
文件来更新对应文件
1 2 3 4 5 6 7 8 9 10 11
| test:sync-to-s3: only: - /V-T-US-.*/ script: - echo ${CI_COMMIT_REF_NAME} - projectName=${CI_COMMIT_REF_NAME##*-} - sh ./script/updatePackage.sh $projectName - aws s3 cp build/${projectName}.html s3://bucket_path/${projectName}.html
|
参考文章