Vue 3.2 发布了,那尤雨溪是怎么发布 Vue.js 的?
1. 前言
大家好,我是若川。最近組織了源碼共讀活動(dòng),感興趣的可以加我微信 ruochuan12,長期交流學(xué)習(xí)。
之前寫的《學(xué)習(xí)源碼整體架構(gòu)系列》 包含jQuery、underscore、lodash、vuex、sentry、axios、redux、koa、vue-devtools、vuex4十篇源碼文章。
寫相對(duì)很難的源碼,耗費(fèi)了自己的時(shí)間和精力,也沒收獲多少閱讀點(diǎn)贊,其實(shí)是一件挺受打擊的事情。從閱讀量和讀者受益方面來看,不能促進(jìn)作者持續(xù)輸出文章。
所以轉(zhuǎn)變思路,寫一些相對(duì)通俗易懂的文章。其實(shí)源碼也不是想象的那么難,至少有很多看得懂。
最近尤雨溪發(fā)布了3.2版本。小版本已經(jīng)是3.2.4了。本文來學(xué)習(xí)下尤大是怎么發(fā)布vuejs的,學(xué)習(xí)源碼為自己所用。
本文涉及到的 vue-next/scripts/release.js文件,整個(gè)文件代碼行數(shù)雖然只有 200 余行,但非常值得我們學(xué)習(xí)。
歌德曾說:讀一本好書,就是在和高尚的人談話。同理可得:讀源碼,也算是和作者的一種學(xué)習(xí)交流的方式。
閱讀本文,你將學(xué)到:
1.?熟悉?vuejs?發(fā)布流程 2.?學(xué)會(huì)調(diào)試?nodejs?代碼 3.?動(dòng)手優(yōu)化公司項(xiàng)目發(fā)布流程環(huán)境準(zhǔn)備之前,我們先預(yù)覽下vuejs的發(fā)布流程。
vue 發(fā)布流程2. 環(huán)境準(zhǔn)備
打開 vue-next[1], 開源項(xiàng)目一般都能在 README.md 或者 .github/contributing.md[2] 找到貢獻(xiàn)指南。
而貢獻(xiàn)指南寫了很多關(guān)于參與項(xiàng)目開發(fā)的信息。比如怎么跑起來,項(xiàng)目目錄結(jié)構(gòu)是怎樣的。怎么投入開發(fā),需要哪些知識(shí)儲(chǔ)備等。
你需要確保 Node.js[3] 版本是 10+, 而且 yarn 的版本是 1.x Yarn 1.x[4]。
你安裝的 Node.js 版本很可能是低于 10。最簡單的辦法就是去官網(wǎng)重新安裝。也可以使用 nvm等管理Node.js版本。
node?-v #?v14.16.0 #?全局安裝?yarn #?克隆項(xiàng)目 git?clone?https://github.com/vuejs/vue-next.git cd?vue-next#?或者克隆我的項(xiàng)目 git?clone?https://github.com/lxchuan12/vue-next-analysis.git cd?vue-next-analysis/vue-next#?安裝?yarn npm?install?--global?yarn #?安裝依賴 yarn?#?install?the?dependencies?of?the?project #?yarn?release2.1 嚴(yán)格校驗(yàn)使用 yarn 安裝依賴
接著我們來看下 vue-next/package.json 文件。
//?vue-next/package.json {"private":?true,"version":?"3.2.4","workspaces":?["packages/*"],"scripts":?{//?--dry?參數(shù)是我加的,如果你是調(diào)試?代碼也建議加//?不執(zhí)行測試和編譯?、不執(zhí)行?推送git等操作//?也就是說空跑,只是打印,后文再詳細(xì)講述"release":?"node?scripts/release.js?--dry","preinstall":?"node?./scripts/checkYarn.js",} }如果你嘗試使用 npm 安裝依賴,應(yīng)該是會(huì)報(bào)錯(cuò)的。為啥會(huì)報(bào)錯(cuò)呢。因?yàn)?package.json 有個(gè)前置 preinstall ?node ./scripts/checkYarn.js 判斷強(qiáng)制要求是使用yarn安裝。
scripts/checkYarn.js文件如下,也就是在process.env環(huán)境變量中找執(zhí)行路徑npm_execpath,如果不是yarn就輸出警告,且進(jìn)程結(jié)束。
//?scripts/checkYarn.js if?(!/yarn\.js$/.test(process.env.npm_execpath?||?''))?{console.warn('\u001b[33mThis?repository?requires?Yarn?1.x?for?scripts?to?work?properly.\u001b[39m\n')process.exit(1) }如果你想忽略這個(gè)前置的鉤子判斷,可以使用yarn --ignore-scripts 命令。也有后置的鉤子post。更多詳細(xì)的可以查看 npm 文檔[5]
2.2 調(diào)試 ?vue-next/scripts/release.js 文件
接著我們來學(xué)習(xí)如何調(diào)試 vue-next/scripts/release.js文件。
這里聲明下我的 VSCode 版本 是 1.59.0 應(yīng)該 1.50.0 起就可以按以下步驟調(diào)試了。
code?-v #?1.59.0找到 vue-next/package.json 文件打開,然后在 scripts 上方,會(huì)有debug(調(diào)試)按鈕,點(diǎn)擊后,選擇 release。即可進(jìn)入調(diào)試模式。
debugger這時(shí)終端會(huì)如下圖所示,有 Debugger attached. 輸出。這時(shí)放張圖。
terminal更多 nodejs 調(diào)試相關(guān) ?可以查看官方文檔[6]
學(xué)會(huì)調(diào)試后,先大致走一遍流程,在關(guān)鍵地方多打上幾個(gè)斷點(diǎn)多走幾遍,就能猜測到源碼意圖了。
3 文件開頭的一些依賴引入和函數(shù)聲明
我們可以跟著斷點(diǎn)來,先看文件開頭的一些依賴引入和函數(shù)聲明
3.1 第一部分
//?vue-next/scripts/release.js const?args?=?require('minimist')(process.argv.slice(2)) //?文件模塊 const?fs?=?require('fs') //?路徑 const?path?=?require('path') //?控制臺(tái) const?chalk?=?require('chalk') const?semver?=?require('semver') const?currentVersion?=?require('../package.json').version const?{?prompt?}?=?require('enquirer')//?執(zhí)行子進(jìn)程命令???簡單說?就是在終端命令行執(zhí)行?命令 const?execa?=?require('execa')通過依賴,我們可以在 node_modules 找到對(duì)應(yīng)安裝的依賴。也可以找到其README和github倉庫。
3.1.1 minimist ?命令行參數(shù)解析
minimist[7]
簡單說,這個(gè)庫,就是解析命令行參數(shù)的。看例子,我們比較容易看懂傳參和解析結(jié)果。
$?node?example/parse.js?-a?beep?-b?boop {?_:?[],?a:?'beep',?b:?'boop'?}$?node?example/parse.js?-x?3?-y?4?-n5?-abc?--beep=boop?foo?bar?baz {?_:?[?'foo',?'bar',?'baz'?],x:?3,y:?4,n:?5,a:?true,b:?true,c:?true,beep:?'boop'?} const?args?=?require('minimist')(process.argv.slice(2))其中process.argv的第一和第二個(gè)元素是Node可執(zhí)行文件和被執(zhí)行JavaScript文件的完全限定的文件系統(tǒng)路徑,無論你是否這樣輸入他們。
3.1.2 chalk 終端多色彩輸出
chalk[8]
簡單說,這個(gè)是用于終端顯示多色彩輸出。
3.1.3 semver ?語義化版本
semver[9]
語義化版本的nodejs實(shí)現(xiàn),用于版本校驗(yàn)比較等。關(guān)于語義化版本可以看這個(gè)語義化版本 2.0.0 文檔[10]
版本格式:主版本號(hào).次版本號(hào).修訂號(hào),版本號(hào)遞增規(guī)則如下:
主版本號(hào):當(dāng)你做了不兼容的 API 修改,
次版本號(hào):當(dāng)你做了向下兼容的功能性新增,
修訂號(hào):當(dāng)你做了向下兼容的問題修正。
先行版本號(hào)及版本編譯信息可以加到“主版本號(hào).次版本號(hào).修訂號(hào)”的后面,作為延伸。
3.1.4 enquirer 交互式詢問 CLI
簡單說就是交互式詢問用戶輸入。
enquirer[11]
3.1.5 execa 執(zhí)行命令
簡單說就是執(zhí)行命令的,類似我們自己在終端輸入命令,比如 echo 若川。
execa[12]
//?例子 const?execa?=?require('execa');(async?()?=>?{const?{stdout}?=?await?execa('echo',?['unicorns']);console.log(stdout);//=>?'unicorns' })();看完了第一部分,接著我們來看第二部分。
3.2 第二部分
//?vue-next/scripts/release.js//?對(duì)應(yīng)?yarn?run?release?--preid=beta //?beta const?preId?=args.preid?||(semver.prerelease(currentVersion)?&&?semver.prerelease(currentVersion)[0]) //?對(duì)應(yīng)?yarn?run?release?--dry //?true const?isDryRun?=?args.dry //?對(duì)應(yīng)?yarn?run?release?--skipTests //?true?跳過測試 const?skipTests?=?args.skipTests //?對(duì)應(yīng)?yarn?run?release?--skipBuild? //?true const?skipBuild?=?args.skipBuild//?讀取?packages?文件夾,過濾掉?不是?.ts文件?結(jié)尾?并且不是?.?開頭的文件夾 const?packages?=?fs.readdirSync(path.resolve(__dirname,?'../packages')).filter(p?=>?!p.endsWith('.ts')?&&?!p.startsWith('.'))第二部分相對(duì)簡單,繼續(xù)看第三部分。
3.3 第三部分
//?vue-next/scripts/release.js//?跳過的包 const?skippedPackages?=?[]//?版本遞增 const?versionIncrements?=?['patch','minor','major',...(preId???['prepatch',?'preminor',?'premajor',?'prerelease']?:?[]) ]const?inc?=?i?=>?semver.inc(currentVersion,?i,?preId)這一塊可能不是很好理解。inc是生成一個(gè)版本。更多可以查看semver文檔[13]
semver.inc('3.2.4',?'prerelease',?'beta') //?3.2.5-beta.03.4 第四部分
第四部分聲明了一些執(zhí)行腳本函數(shù)等
//?vue-next/scripts/release.js//?獲取?bin?命令 const?bin?=?name?=>?path.resolve(__dirname,?'../node_modules/.bin/'?+?name) const?run?=?(bin,?args,?opts?=?{})?=>execa(bin,?args,?{?stdio:?'inherit',?...opts?}) const?dryRun?=?(bin,?args,?opts?=?{})?=>console.log(chalk.blue(`[dryrun]?${bin}?${args.join('?')}`),?opts) const?runIfNotDry?=?isDryRun???dryRun?:?run//?獲取包的路徑 const?getPkgRoot?=?pkg?=>?path.resolve(__dirname,?'../packages/'?+?pkg)//?控制臺(tái)輸出 const?step?=?msg?=>?console.log(chalk.cyan(msg))3.4.1 bin 函數(shù)
獲取 node_modules/.bin/ 目錄下的命令,整個(gè)文件就用了一次。
bin('jest')相當(dāng)于在命令終端,項(xiàng)目根目錄 運(yùn)行 ./node_modules/.bin/jest 命令。
3.4.2 run、dryRun、runIfNotDry
const?run?=?(bin,?args,?opts?=?{})?=>execa(bin,?args,?{?stdio:?'inherit',?...opts?}) const?dryRun?=?(bin,?args,?opts?=?{})?=>console.log(chalk.blue(`[dryrun]?${bin}?${args.join('?')}`),?opts) const?runIfNotDry?=?isDryRun???dryRun?:?runrun 真實(shí)在終端跑命令,比如 yarn build --release
dryRun 則是不跑,只是 console.log(); 打印 'yarn build --release'
runIfNotDry 如果不是空跑就執(zhí)行命令。isDryRun 參數(shù)是通過控制臺(tái)輸入的。yarn run release --dry這樣就是true。runIfNotDry就是只是打印,不執(zhí)行命令。這樣設(shè)計(jì)的好處在于,可以有時(shí)不想直接提交,要先看看執(zhí)行命令的結(jié)果。不得不說,尤大就是會(huì)玩。
在 main 函數(shù)末尾,也可以看到類似的提示。可以用git diff先看看文件修改。
if?(isDryRun)?{console.log(`\nDry?run?finished?-?run?git?diff?to?see?package?changes.`) }看完了文件開頭的一些依賴引入和函數(shù)聲明等,我們接著來看main主入口函數(shù)。
4 main 主流程
第4節(jié),主要都是main 函數(shù)拆解分析。
4.1 流程梳理 main 函數(shù)
const?chalk?=?require('chalk') const?step?=?msg?=>?console.log(chalk.cyan(msg)) //?前面一堆依賴引入和函數(shù)定義等 async?function?main(){//?版本校驗(yàn)//?run?tests?before?releasestep('\nRunning?tests...')//?update?all?package?versions?and?inter-dependenciesstep('\nUpdating?cross?dependencies...')//?build?all?packages?with?typesstep('\nBuilding?all?packages...')//?generate?changelogstep('\nCommitting?changes...')//?publish?packagesstep('\nPublishing?packages...')//?push?to?GitHubstep('\nPushing?to?GitHub...') }main().catch(err?=>?{console.error(err) })上面的main函數(shù)省略了很多具體函數(shù)實(shí)現(xiàn)。接下來我們拆解 main 函數(shù)。
4.2 確認(rèn)要發(fā)布的版本
第一段代碼雖然比較長,但是還好理解。主要就是確認(rèn)要發(fā)布的版本。
調(diào)試時(shí),我們看下這段的兩張截圖,就好理解啦。
終端輸出選擇版本號(hào)終端輸入確認(rèn)版本號(hào)//?根據(jù)上文?mini?這句代碼意思是?yarn?run?release?3.2.4? //?取到參數(shù)?3.2.4 let?targetVersion?=?args._[0]if?(!targetVersion)?{//?no?explicit?version,?offer?suggestionsconst?{?release?}?=?await?prompt({type:?'select',name:?'release',message:?'Select?release?type',choices:?versionIncrements.map(i?=>?`${i}?(${inc(i)})`).concat(['custom'])})//?選自定義if?(release?===?'custom')?{targetVersion?=?(await?prompt({type:?'input',name:?'version',message:?'Input?custom?version',initial:?currentVersion})).version}?else?{//?取到括號(hào)里的版本號(hào)targetVersion?=?release.match(/\((.*)\)/)[1]} }//?校驗(yàn)?版本是否符合?規(guī)范 if?(!semver.valid(targetVersion))?{throw?new?Error(`invalid?target?version:?${targetVersion}`) }//?確認(rèn)要?release const?{?yes?}?=?await?prompt({type:?'confirm',name:?'yes',message:?`Releasing?v${targetVersion}.?Confirm?` })//?false?直接返回 if?(!yes)?{return }4.3 執(zhí)行測試用例
//?run?tests?before?release step('\nRunning?tests...') if?(!skipTests?&&?!isDryRun)?{await?run(bin('jest'),?['--clearCache'])await?run('yarn',?['test',?'--bail']) }?else?{console.log(`(skipped)`) }4.4 更新所有包的版本號(hào)和內(nèi)部 vue 相關(guān)依賴版本號(hào)
這一部分,就是更新根目錄下package.json 的版本號(hào)和所有 packages 的版本號(hào)。
//?update?all?package?versions?and?inter-dependencies step('\nUpdating?cross?dependencies...') updateVersions(targetVersion) function?updateVersions(version)?{//?1.?update?root?package.jsonupdatePackage(path.resolve(__dirname,?'..'),?version)//?2.?update?all?packagespackages.forEach(p?=>?updatePackage(getPkgRoot(p),?version)) }4.4.1 updatePackage 更新包的版本號(hào)
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') }主要就是三種修改。
1.?自己本身?package.json?的版本號(hào) 2.?packages.json?中?dependencies?中?vue?相關(guān)的依賴修改 3.?packages.json?中?peerDependencies?中?vue?相關(guān)的依賴修改一圖勝千言。我們執(zhí)行yarn release --dry 后 git diff 查看的 git 修改,部分截圖如下。
更新的版本號(hào)舉例4.4.2 updateDeps 更新內(nèi)部 vue 相關(guān)依賴的版本號(hào)
function?updateDeps(pkg,?depType,?version)?{const?deps?=?pkg[depType]if?(!deps)?returnObject.keys(deps).forEach(dep?=>?{if?(dep?===?'vue'?||(dep.startsWith('@vue')?&&?packages.includes(dep.replace(/^@vue\//,?''))))?{console.log(chalk.yellow(`${pkg.name}?->?${depType}?->?${dep}@${version}`))deps[dep]?=?version}}) }一圖勝千言。我們?cè)诮K端執(zhí)行yarn release --dry。會(huì)看到這樣是輸出。
更新 Vue 相關(guān)依賴的終端輸出也就是這句代碼輸出的。
console.log(chalk.yellow(`${pkg.name}?->?${depType}?->?${dep}@${version}`) )4.5 打包編譯所有包
//?build?all?packages?with?types step('\nBuilding?all?packages...') if?(!skipBuild?&&?!isDryRun)?{await?run('yarn',?['build',?'--release'])//?test?generated?dts?filesstep('\nVerifying?type?declarations...')await?run('yarn',?['test-dts-only']) }?else?{console.log(`(skipped)`) }4.6 生成 changelog
//?generate?changelog await?run(`yarn`,?['changelog'])yarn changelog 對(duì)應(yīng)的腳本是conventional-changelog -p angular -i CHANGELOG.md -s。
4.7 提交代碼
經(jīng)過更新版本號(hào)后,有文件改動(dòng),于是git diff。是否有文件改動(dòng),如果有提交。
git add -Agit commit -m 'release: v${targetVersion}'
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.') }4.8 發(fā)布包
//?publish?packages step('\nPublishing?packages...') for?(const?pkg?of?packages)?{await?publishPackage(pkg,?targetVersion,?runIfNotDry) }這段函數(shù)比較長,可以不用細(xì)看,簡單說就是 yarn publish 發(fā)布包。我們 yarn release --dry后,這塊函數(shù)在終端輸出的如下:
發(fā)布終端輸出命令值得一提的是,如果是 vue 默認(rèn)有個(gè) tag 為 next。當(dāng) Vue 3.x 是默認(rèn)時(shí)刪除。
}?else?if?(pkgName?===?'vue')?{//?TODO?remove?when?3.x?becomes?defaultreleaseTag?=?'next' }也就是為什么我們現(xiàn)在安裝 vue3 還是 npm i vue@next命令。
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.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]?||?nullstep(`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}} }4.9 推送到 github
//?push?to?GitHub step('\nPushing?to?GitHub...') //?打?tag await?runIfNotDry('git',?['tag',?`v${targetVersion}`]) //?推送?tag await?runIfNotDry('git',?['push',?'origin',?`refs/tags/v${targetVersion}`]) //?git?push?所有改動(dòng)到?遠(yuǎn)程??-?github await?runIfNotDry('git',?['push']) //?yarn?run?release?--dry//?如果傳了這個(gè)參數(shù)則輸出?可以用?git?diff?看看更改//?const?isDryRun?=?args.dry if?(isDryRun)?{console.log(`\nDry?run?finished?-?run?git?diff?to?see?package?changes.`) }//?如果?跳過的包,則輸出以下這些包沒有發(fā)布。不過代碼?`skippedPackages`?里是沒有包。 //?所以這段代碼也不會(huì)執(zhí)行。 //?我們習(xí)慣寫 arr.length !==?0?其實(shí)?0?就是 false ??梢圆粚?。 if?(skippedPackages.length)?{console.log(chalk.yellow(`The?following?packages?are?skipped?and?NOT?published:\n-?${skippedPackages.join('\n-?')}`)) } console.log()我們 yarn release --dry后,這塊函數(shù)在終端輸出的如下:
發(fā)布到github到這里我們就拆解分析完 main 函數(shù)了。
整個(gè)流程很清晰。
1.?確認(rèn)要發(fā)布的版本 2.?執(zhí)行測試用例 3.?更新所有包的版本號(hào)和內(nèi)部?vue?相關(guān)依賴版本號(hào)3.1?updatePackage?更新包的版本號(hào)3.2?updateDeps?更新內(nèi)部?vue?相關(guān)依賴的版本號(hào) 4.?打包編譯所有包 5.?生成?changelog 6.?提交代碼 7.?發(fā)布包 8.?推送到?github用一張圖總結(jié)則是:
vue 發(fā)布流程看完vue-next/scripts/release.js,感興趣還可以看vue-next/scripts文件夾下其他代碼,相對(duì)行數(shù)不多,但收益較大。
5. 總結(jié)
通過本文學(xué)習(xí),我們學(xué)會(huì)了這些。
1.?熟悉?vuejs?發(fā)布流程 2.?學(xué)會(huì)調(diào)試?nodejs?代碼 3.?動(dòng)手優(yōu)化公司項(xiàng)目發(fā)布流程同時(shí)建議自己動(dòng)手用 VSCode 多調(diào)試,在終端多執(zhí)行幾次,多理解消化。
vuejs發(fā)布的文件很多代碼我們可以直接復(fù)制粘貼修改,優(yōu)化我們自己發(fā)布的流程。比如寫小程序,相對(duì)可能發(fā)布頻繁,完全可以使用這套代碼,配合miniprogram-ci[14],再加上一些自定義,加以優(yōu)化。
當(dāng)然也可以用開源的 release-it[15]。
同時(shí),我們可以:
引入 git flow[16],管理git分支。估計(jì)很多人不知道windows git bash已經(jīng)默認(rèn)支持 git flow命令。
引入 husky[17] 和 lint-staged[18] 提交commit時(shí)用ESLint等校驗(yàn)代碼提交是否能夠通過檢測。
引入 單元測試 jest[19],測試關(guān)鍵的工具函數(shù)等。
引入 conventional-changelog[20]
引入 git-cz[21] 交互式git commit。
等等規(guī)范自己項(xiàng)目的流程。如果一個(gè)候選人,通過看vuejs發(fā)布的源碼,積極主動(dòng)優(yōu)化自己項(xiàng)目。我覺得面試官會(huì)認(rèn)為這個(gè)候選人比較加分。
看開源項(xiàng)目源碼的好處在于:一方面可以拓展視野,另外一方面可以為自己所用,收益相對(duì)較高。
參考資料
[1]
vue-next: https://github.com/vuejs/vue-next
[2]更多可點(diǎn)擊 閱讀原文 查看
最近組建了一個(gè)江西人的前端交流群,如果你是江西人可以加我微信?ruochuan12?私信 江西?拉你進(jìn)群。
推薦閱讀
我在阿里招前端,該怎么幫你(可進(jìn)面試群)
我讀源碼的經(jīng)歷
初學(xué)者也能看懂的 Vue3 源碼中那些實(shí)用的基礎(chǔ)工具函數(shù)
老姚淺談:怎么學(xué)JavaScript?
·················?若川簡介?·················
你好,我是若川,畢業(yè)于江西高?!,F(xiàn)在是一名前端開發(fā)“工程師”。寫有《學(xué)習(xí)源碼整體架構(gòu)系列》多篇,在知乎、掘金收獲超百萬閱讀。
從2014年起,每年都會(huì)寫一篇年度總結(jié),已經(jīng)寫了7篇,點(diǎn)擊查看年度總結(jié)。
同時(shí),活躍在知乎@若川,掘金@若川。致力于分享前端開發(fā)經(jīng)驗(yàn),愿景:幫助5年內(nèi)前端人走向前列。
識(shí)別上方二維碼加我微信、拉你進(jìn)源碼共讀群
今日話題
略。歡迎分享、收藏、點(diǎn)贊、在看我的公眾號(hào)文章~
總結(jié)
以上是生活随笔為你收集整理的Vue 3.2 发布了,那尤雨溪是怎么发布 Vue.js 的?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: DirectX详解
- 下一篇: 读完 Vue 发布源码,小姐姐回答了 l