网站离线浏览器 怎么做,WordPress工具主题,源码网站python免费,广东seo网站设计多少钱大家好#xff0c;我是若川。本文是读者NewName 投稿#xff0c;看了我推荐的vuejs如何发布的源码#xff08;200余行#xff09;#xff0c;并成功写了一个小工具。推荐的当晚看到挺晚#xff0c;这执行力这努力程度超过很多人啊。我本来是打算自己写一篇这个文章的我是若川。本文是读者NewName 投稿看了我推荐的vuejs如何发布的源码200余行并成功写了一个小工具。推荐的当晚看到挺晚这执行力这努力程度超过很多人啊。我本来是打算自己写一篇这个文章的没想到他写完了。前言第一次看源码感谢我川哥提供合适题材给予细心指导。感觉学习源码真的很有帮助我第一次就品尝到甜头了可以借鉴抄袭源码的思想解决实际的问题真好啊。本人着实菜鸟一枚也不会写作把学习的笔记记录一下而已。1. 准备工作和主要精神1.源码地址https://github1s.com/vuejs/vue-next/blob/HEAD/scripts/release.js2.源码主旨内容vuejs 是如何发布3.要思考学完了可以应用的地方1.比如学完这个源码优化自己项目的发布流程
————真有点想法耶
我们公司目前的前端发版本流程中有许多的git命令 这些git命令我可以用脚本的方式运行借鉴源码中的run方法
const run (bin, args, opts {}) execa(bin, args, { stdio: inherit, ...opts })
目前已经写了CV一个工具脚本优化了我们公司前端项目发预发布版本的流程详见第六部分。
4.读源码特别要注意先看懂大局呀 主线是main函数 先看懂大概 然后不懂的再查
2. 从main函数开始看将 main 函数中主要代码梳理一下总体流程如下图所示先简单看下 vuejs 发布总体流程2.1 版本号验证相关流程 40 - 77行40行获取版本号44 -49行 如果版本号不存在则问是要升级大版本、小版本、 补丁版本 还是自定义52-59 行 如果用户选择的自定义则获取自定义的版本65-67行检查版本号是否合法69-73行问是否确定要发布版本55-77: 如果选择否 main函数执行结束 返回2.2 发布之前的测试80-86行2.3 更新依赖的版本号88-90行2.4 运行build命令 93-101行2.5 运行日志命令 104行2.6 提交代码106-113行2.7 运行发布命令 115-119 行2.8 新的版本push到git121-125行2.9 提示跳过更新的包131-139行3. 一些变量和函数的定义细节3.1 发布之前测试80-86行//如果没有skipTests 跳过测试 并且没有 isDryRun空跑则运行测试
step(\nRunning tests...)if (!skipTests !isDryRun) {//使用jest , 清除缓存await run(bin(jest), [--clearCache])//yarn test await run(yarn, [test, --bail])} else {console.log((skipped))}
3.2 构建所有的包93—101行//其实是运行yarn build 命令
await run(yarn, [build, --release])
3.3 运行日志命令104行运行 yarn 的log命令await run(yarn, [changelog])
//changelog定义在package.json 的 npm scripts 中
changelog: conventional-changelog -p angular -i CHANGELOG.md -s,//这里使用到了conventional-changelog,见第五部分使用的依赖
3.4 提交代码106-113行git版本管理相关//判断文件是否有变化如果有变化则提交到git
const { stdout } await run(git, [diff], { stdio: pipe })if (stdout) {step(\nCommitting changes...)await runIfNotDry(git, [add, -A])await runIfNotDry(git, [commit, -m, release: v${targetVersion}])} else {console.log(No changes to commit.)}
3.5 运行发布命令115-119行: 发布// publish packagesstep(\nPublishing packages...)for (const pkg of packages) {await publishPackage(pkg, targetVersion, runIfNotDry)}
//publishPackage的定义在175-230行
async function publishPackage(pkgName, version, runIfNotDry) {// 如果某个包要跳过则直接返回if (skippedPackages.includes(pkgName)) {return}//获取路径和内容const pkgRoot getPkgRoot(pkgName)const pkgPath path.resolve(pkgRoot, package.json)const pkg JSON.parse(fs.readFileSync(pkgPath, utf-8))//如果是私有的则返回if (pkg.private) {return}// For now, all 3.x packages except vue can be published as// latest, whereas vue will be published under the next tag.// 定义一下发布的标记 (打tag)let releaseTag nullif (args.tag) {releaseTag args.tag} else if (version.includes(alpha)) {releaseTag alpha} else if (version.includes(beta)) {releaseTag beta} else if (version.includes(rc)) {releaseTag rc} else if (pkgName vue) {// TODO remove when 3.x becomes defaultreleaseTag next}// TODO use inferred release channel after official 3.0 release// const releaseTag semver.prerelease(version)[0] || null// 执行发布 运行yarn的发布命令step(Publishing ${pkgName}...)try {await runIfNotDry(yarn,[publish,--new-version,version,...(releaseTag ? [--tag, releaseTag] : []),--access,public],{cwd: pkgRoot,stdio: pipe})console.log(chalk.green(Successfully published ${pkgName}${version}))} catch (e) {if (e.stderr.match(/previously published/)) {console.log(chalk.red(Skipping already published: ${pkgName}))} else {throw e}}
}
3.6 更新依赖的细节88-90: 更新所有的依赖updateVersions(targetVersion)
//updateVersions 是定义的一个更新版本的函数里面调用了 updatePackage函数
function updateVersions(version) {// 1. update root package.jsonupdatePackage(path.resolve(__dirname, ..), version)// 2. update all packagespackages.forEach(p updatePackage(getPkgRoot(p), version))
}
//packages的定义在第16行
//updatePackag在157行大概意思是找到路径下的package.json文件然后读取文件内容转成对象更新版本再写回文件。
function updatePackage(pkgRoot, version) {const pkgPath path.resolve(pkgRoot, package.json)const pkg JSON.parse(fs.readFileSync(pkgPath, utf-8))pkg.version versionupdateDeps(pkg, dependencies, version)updateDeps(pkg, peerDependencies, version)fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) \n)
}
//这里用到的updateDeps在 159-173行定义
function updateDeps(pkg, depType, version) {const deps pkg[depType]if (!deps) returnObject.keys(deps).forEach(dep {//大概含义是检查依赖如果其中有vue则提示并且替换版本。if (dep vue ||(dep.startsWith(vue) packages.includes(dep.replace(/^vue\//, )))) {console.log(chalk.yellow(${pkg.name} - ${depType} - ${dep}${version}))deps[dep] version}})
}
4. 总体的流程总结回顾将 main 函数中主要代码梳理一下总体流程如下图所示先简单看下 vuejs 发布总体流程5. 使用的依赖5.1 minimistconst args require(minimist)(process.argv.slice(2))
minimist轻量级的命令行参数解析引擎https://blog.csdn.net/fangxuan1509/article/details/107469465https://www.npmjs.com/package/minimist5.2 chalkconst chalk require(chalk)
chalk是一个颜色的插件https://blog.csdn.net/sqrtsix/article/details/76615630https://www.npmjs.com/package/chalk5.3 semverconst semver require(semver)
Semver是一个专门分析Semantic Version语义化版本的工具“semver”其实就是这两个单词的缩写。Npm使用了该工具来处理版本相关的工作。语义化版本扫盲 https://segmentfault.com/a/1190000014405355https://www.npmjs.cn/misc/semver/5.4 enquirerconst { prompt } require(enquirer)
命令行提示的https://www.npmjs.com/package/enquirer5.5 execaconst execa require(execa)
execa是可以调用shell和本地外部程序的javascript封装。会启动子进程执行。支持多操作系统包括windows。如果父进程退出则生成的全部子进程都被杀死。http://abloz.com/tech/2018/08/21/nodejs-execa/https://www.npmjs.com/package/execa5.6 yarn的相关命令https://yarn.bootcss.com/docs/getting-started/5.7 conventional-changelog在约定式提交的基础上来自动生成changeloghttps://blog.csdn.net/weixin_34326179/article/details/91382865https://www.npmjs.com/package/conventional-changelog6. 应用优化发“预发布版本”的流程6.1 问题描述“预发布版本”是我们发正式版本之前的一个验证版本目前我们公司前端项目发“预发布版本”的时候要通过如下图所示的流程这里面有很多操作git的命令受阅读源码启发我想写一个工具脚本把这些命令变成自动执行的方式需要的信息只需要开发者输入或者选择即可。其中生成tag的时候需要根据tag命名规范来生成。下图中 12 为固定值 20代表年份30代表是一年中的第几周01代表第几次发版。6.2 解决方案所以我需要写的脚本中就需要做这几件事情1要获取当前年份当前第几周这两个值用JS基本代码就ok2需要和开发者交互获取第几次发版和服务名可以使用 release.js 中使用的 依赖enquirer3需要运行git命令release.js 中使用的 依赖execa以及定义的 run()方法。4如果控制台中要高亮提示信息则使用依赖chalk 当然这个是可选的6.3 具体实现6.3.1 首先项目根目录下新建scripts文件夹然后新建 release.js文件6.3.2 安装依赖npm install chalk --save-dev
npm install execa --save-dev
npm install enquirer --save-dev
6.3.3 写代码const chalk require(chalk)
const execa require(execa)
const {prompt
} require(enquirer)const step msg console.log(chalk.cyan(msg))const run (bin, args, opts {}) execa(bin, args, {stdio: inherit,...opts})async function main() {console.log(欢迎使用发版小助手脚本)step(\n验证是否提交代码...)const { yes } await prompt({type: confirm,name: yes,message: 确定已经提交代码并将代码push到develop分支了吗?})if (!yes) {return}// 切换到 develop 分支拉取最新代码await run(git, [checkout, develop])await run(git, [pull, origin, develop])// 切换到 release 分支拉取最新代码await run(git, [checkout, release])await run(git, [pull, origin, release])// 本地 release 分支合并 develop 分支代码await run(git, [merge, develop])// 合并完成后推送到远程 release 分支await run(git, [push, origin, release])// 以下逻辑为拼写tag号打tag用// 获取当前年份后两位const yearLastTwoBit getLastTwoBitYear()console.log(yearLastTwoBit)// 获取当前是一年中的第几周const currentWeek theWeek()console.log(currentWeek)// 本次的版本号const targetVersion (await prompt({type: input,name: version,message: 这次是本周第几次发版请输入数字,})).versionconsole.log(targetVersion)// 前端服务名字const serviceName (await prompt({type: input,name: serviceName,message: 请输入这个前端服务名字例如 fe_beg,})).serviceNameconsole.log(serviceName)// 拼接tag名 const tagName Cloud_R-12.${yearLastTwoBit}.${currentWeek}.${targetVersion 10 ? targetVersion : 0${targetVersion}}-${serviceName}// 拼接注释const comment ${serviceName}服务${currentWeek}周/迭代第${targetVersion}次版本发布// 本地打 Tag生成版本await run(git, [tag, tagName, -m, comment])// 推送 Tag 到远程代码库触发构建await run(git, [push, --tags])console.log(稍等片刻即可完成预发布环境版本发布详细查看Jenkins)// 重新切换到develop分支await run(git, [checkout, develop])
}// 获取当前年份后两位
function getLastTwoBitYear() {const date new Date()const currentYear date.getFullYear().toString()return currentYear.substr(currentYear.length - 2, 2)
}function theWeek() {var totalDays 0;now new Date();var days new Array(12);days[0] 31;days[2] 31;days[3] 30;days[4] 31;days[5] 30;days[6] 31;days[7] 31;days[8] 30;days[9] 31;days[10] 30;days[11] 31;//判断是否为闰年针对2月的天数进行计算if (Math.round(now.getYear() / 4) now.getYear() / 4) {days[1] 29} else {days[1] 28}if (now.getMonth() 0) {totalDays totalDays now.getDate();} else {var curMonth now.getMonth();for (var count 1; count curMonth; count) {totalDays totalDays days[count - 1];}totalDays totalDays now.getDate();}//得到第几周var week Math.round(totalDays / 7);return week;
}
main().catch(err {console.log(err)
})
6.3.4 然后在package.json 中增加一个npm scriptrelease: node scripts/release.js,
6.3.5 测试运行命令npm run release
下图为脚本运行效果小工具脚本运行还算顺利只需要运行1次命令确认1次输入2次避免了输入很多git命令。还有就是发完“预发布”之后我们很容易忘了切回develop分支这个小工具脚本最后把代码切到了develop分支可以减少不必要的麻烦。当然这里我没有考虑许多的异常case , 健壮性不太好有待优化。最后我非常感谢川哥细心耐心给予我指导解答我的问题。川哥人好又心善勤勉又聪慧实在是我学习的楷模最近组建了一个江西人的前端交流群如果你是江西人可以加我微信 ruochuan12 私信 江西 拉你进群。推荐阅读我在阿里招前端该怎么帮你可进面试群我读源码的经历在字节做前端一年后有啥收获~老姚浅谈怎么学JavaScript················· 若川简介 ·················你好我是若川毕业于江西高校。现在是一名前端开发“工程师”。写有《学习源码整体架构系列》多篇在知乎、掘金收获超百万阅读。从2014年起每年都会写一篇年度总结已经写了7篇点击查看年度总结。同时活跃在知乎若川掘金若川。致力于分享前端开发经验愿景帮助5年内前端人走向前列。点击上方卡片关注我、加个星标今日话题付费课程一定程度上可以帮助到一些自律爱学习的人同时也确实会节省不少时间。打个可能不是那么恰当的比方。如果说学习是爬山那么付费学习可以理解为是适当的花钱坐缆车坐上缆车节省了时间和体力更容易攀登到更高的山峰但前提是体力较好。自己走山路上山当然也很好但如果爬更高的山峰后续就有可能到达不了最顶峰。欢迎分享、收藏、点赞、在看我的公众号文章~