創(chuàng)建項(xiàng)目及包管理yarn create vite源碼學(xué)習(xí)
1.引言
我們?cè)诰幊虒W(xué)習(xí)的過(guò)程中也會(huì)寫一些項(xiàng)目的模板,這樣的模板在后期其實(shí)并沒(méi)有進(jìn)行很好的管理,以至于下次再來(lái)回顧或使用的時(shí)候還需要從“零”開(kāi)始,在使用過(guò) Vite 來(lái)創(chuàng)建項(xiàng)目后順便拿看了一下倉(cāng)庫(kù)中create-vite
包中的源碼,得到了很好的啟發(fā)~
2.走進(jìn)“yarn create vite”的源碼
2.1 Vite 創(chuàng)建項(xiàng)目的方式:
- 終端交互方式創(chuàng)建項(xiàng)目;
- 終端指定模版創(chuàng)建項(xiàng)目;
2.1.1 終端交互方式創(chuàng)建項(xiàng)目:
相比于以往的 CLI 工具提供的創(chuàng)建項(xiàng)目都需要優(yōu)先手動(dòng)安裝 CLI 工具后再執(zhí)行對(duì)應(yīng)的創(chuàng)建命令,另一種就是 Vite 目前采用的直接通過(guò)包管理器內(nèi)置命令使用統(tǒng)一的規(guī)范來(lái)實(shí)現(xiàn)項(xiàng)目的快速創(chuàng)建;
如果你使用 YARN:
# yarn yarn create vite
接下來(lái)就可以按終端提示進(jìn)行項(xiàng)目名稱的錄入和項(xiàng)目模板的選擇了~
2.1.2 終端指定模版創(chuàng)建項(xiàng)目:
如果我們很明確內(nèi)置的模板選項(xiàng)的話我們可以在終端執(zhí)行時(shí)同時(shí)錄入項(xiàng)目名稱和模板名稱更快速的創(chuàng)建項(xiàng)目;
# yarn yarn create vite my-vue-app --template vue
備注:使用“.
”來(lái)在當(dāng)前目錄創(chuàng)建項(xiàng)目;
2.2 源碼分析:
- 終端參數(shù)解析;
- 交互收集數(shù)據(jù);
- 目錄初始化;
- 拷貝模板文件夾;
- 重寫
gitignore
名稱; - 重寫
package
字段; - 后續(xù)操作提示;
2.2.1 終端參數(shù)解析:
通過(guò) minimist 模塊將終端參數(shù)解析為對(duì)象形式:
const argv = minimist<{ t?: string template?: string }>(process.argv.slice(2), { string: ['_'] })
通過(guò)讀取對(duì)象屬性來(lái)得到 argTargetDir 和 argTemplate 兩個(gè)參數(shù):
const argTargetDir = formatTargetDir(argv._[0]) const argTemplate = argv.template || argv.t
2.2.2 交互收集數(shù)據(jù):
通過(guò)交互收集的數(shù)據(jù)包括:projectName、overwrite、packageName、framework、variant:
projectName:默認(rèn)值是 defaultTargetDir ,對(duì)應(yīng)的值是vite-project
,當(dāng)通過(guò)終端解析到 argTargetDir 后將跳過(guò)此步驟;
{ type: argTargetDir ? null : 'text', name: 'projectName', message: reset('Project name:'), initial: defaultTargetDir, onState: (state) => { targetDir = formatTargetDir(state.value) || defaultTargetDir } }
overwrite:在確認(rèn)好 targetDir 參數(shù)后,我們要看 targetDir 對(duì)應(yīng)的文件夾是否存在或是文件夾中是否有文件,要提示用戶是否需要情況或終端操作;
{ type: () => !fs.existsSync(targetDir) || isEmpty(targetDir) ? null : 'confirm', name: 'overwrite', message: () => (targetDir === '.' ? 'Current directory' : `Target directory "${targetDir}"`) + ` is not empty. Remove existing files and continue?` }, { type: (_, { overwrite }: { overwrite?: boolean }) => { if (overwrite === false) { throw new Error(red('?') + ' Operation cancelled') } return null }, name: 'overwriteChecker' }
packageName:packageName 的值通過(guò) 路徑 和 targetDir 來(lái)確定,在終端交互收集數(shù)據(jù)的時(shí)候需要對(duì)值做格式的校驗(yàn)來(lái)確定合法性;
// 確定項(xiàng)目名稱 const getProjectName = () => targetDir === '.' ? path.basename(path.resolve()) : targetDir // 校驗(yàn)項(xiàng)目名稱合法性 { type: () => (isValidPackageName(getProjectName()) ? null : 'text'), name: 'packageName', message: reset('Package name:'), initial: () => toValidPackageName(getProjectName()), validate: (dir) => isValidPackageName(dir) || 'Invalid package.json name' }
framework:如果終端已獲取到 argTemplate 參數(shù),且已內(nèi)置該模板將跳過(guò)這步,否則將進(jìn)行預(yù)置模板配置的解析并選擇;
{ type: argTemplate && TEMPLATES.includes(argTemplate) ? null : 'select', name: 'framework', message: typeof argTemplate === 'string' && !TEMPLATES.includes(argTemplate) ? reset( `"${argTemplate}" isn't a valid template. Please choose from below: ` ) : reset('Select a framework:'), initial: 0, choices: FRAMEWORKS.map((framework) => { const frameworkColor = framework.color return { title: frameworkColor(framework.display || framework.name), value: framework } }) }
variant:通過(guò)上一步得到的 framework 參數(shù)將確定這個(gè)步的配置,因?yàn)橥瑯拥?framework 看一配置多種 variant ;
{ type: (framework: Framework) => framework && framework.variants ? 'select' : null, name: 'variant', message: reset('Select a variant:'), choices: (framework: Framework) => framework.variants.map((variant) => { const variantColor = variant.color return { title: variantColor(variant.display || variant.name), value: variant.name } }) }
2.2.3 目錄初始化:
目錄如果不存在的話我們需要?jiǎng)?chuàng)建對(duì)應(yīng)的目錄,因?yàn)樵谇捌谑占瘏?shù)可能是個(gè)目錄,這兒創(chuàng)建的時(shí)候需要遞歸創(chuàng)建,但是當(dāng)目錄存在且有內(nèi)容的時(shí)候我們就需要清空掉里面的文件了,但是在清空的時(shí)候我們要考慮當(dāng)時(shí)目錄可能已經(jīng)被版本管理過(guò)了,所以需要對(duì).git
目錄過(guò)濾掉,這樣才更完整;
const root = path.join(cwd, targetDir) if (overwrite) { emptyDir(root) } else if (!fs.existsSync(root)) { fs.mkdirSync(root, { recursive: true }) }
2.2.4 拷貝模板文件夾:
模板拷貝的時(shí)候需要過(guò)濾掉package.json
,我們會(huì)在后面單獨(dú)進(jìn)行處理;
const files = fs.readdirSync(templateDir) for (const file of files.filter((f) => f !== 'package.json')) { write(file) }
2.2.5 重寫 gitignore 名稱:
在上一步的模板文件夾拷貝的時(shí)候已經(jīng)用到了這個(gè)函數(shù),我們這里關(guān)系第二行中的關(guān)鍵對(duì)象 renameFiles ,實(shí)際上就是要將 _gitignore 重命名為 .gitignore ,因?yàn)樵谀0逯兄苯邮褂?.gitignore 可能就導(dǎo)致關(guān)節(jié)的文件被丟失掉了;
const write = (file: string, content?: string) => { const targetPath = path.join(root, renameFiles[file] ?? file) if (content) { fs.writeFileSync(targetPath, content) } else { copy(path.join(templateDir, file), targetPath) } }
2.2.6 重寫 package 字段:
最后來(lái)輸出 package.json ,需要改變里面的內(nèi)容,尤其是重要的項(xiàng)目名稱,命名規(guī)范也是為了符合 package 中 name 的規(guī)則;
const pkg = JSON.parse( fs.readFileSync(path.join(templateDir, `package.json`), 'utf-8') ) pkg.name = packageName || getProjectName() write('package.json', JSON.stringify(pkg, null, 2))
2.2.7 后續(xù)操作提示:
在模板拷貝完畢后項(xiàng)目的創(chuàng)建階段也就結(jié)束了,接著就是判斷在終端執(zhí)行的包管理器來(lái)提示用戶下一步的操作了~
const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent) const pkgManager = pkgInfo ? pkgInfo.name : 'npm' switch (pkgManager) { case 'yarn': console.log(' yarn') console.log(' yarn dev') break default: console.log(` ${pkgManager} install`) console.log(` ${pkgManager} run dev`) break }
3. 總結(jié)
在源碼中還支持了第三方模板通過(guò)自定義命令來(lái)創(chuàng)建項(xiàng)目,關(guān)鍵詞可以搜索 customCommand
,整體源碼是很簡(jiǎn)單的,你準(zhǔn)備好為自己創(chuàng)建一套模板管理工具了嗎~
以上就是創(chuàng)建項(xiàng)目及包管理yarn create vite源碼學(xué)習(xí)的詳細(xì)內(nèi)容,更多關(guān)于創(chuàng)建項(xiàng)目包管理yarn create vit的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue實(shí)現(xiàn)會(huì)議室拖拽布局排座功能
vue-draggable-resizable-gorkys是一更強(qiáng)大的拖拽組件,可以隨意拖拽,有點(diǎn)坐標(biāo),會(huì)議室拖拽布局排座是vue-draggable結(jié)合vue-draggable-resizable-gorkys進(jìn)行開(kāi)發(fā)的,本文重點(diǎn)給大家介紹vue實(shí)現(xiàn)會(huì)議室拖拽布局排座,感興趣的朋友一起看看吧2023-11-11解決vue動(dòng)態(tài)為數(shù)據(jù)添加新屬性遇到的問(wèn)題
今天小編就為大家分享一篇解決vue動(dòng)態(tài)為數(shù)據(jù)添加新屬性遇到的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-09-09vue如何解決數(shù)據(jù)加載時(shí),插值表達(dá)式閃爍問(wèn)題
這篇文章主要介紹了vue如何解決數(shù)據(jù)加載時(shí),插值表達(dá)式閃爍問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01vue引入jquery時(shí)報(bào)錯(cuò) $ is not defined的問(wèn)題及解決
這篇文章主要介紹了vue引入jquery時(shí)報(bào)錯(cuò) $ is not defined的問(wèn)題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。2022-09-09Vue實(shí)現(xiàn)隨機(jī)驗(yàn)證碼功能
這篇文章主要為大家詳細(xì)介紹了Vue實(shí)現(xiàn)隨機(jī)驗(yàn)證碼功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-12-12Vue3通過(guò)ref操作Dom元素及hooks的使用方法
這篇文章主要介紹了Vue3通過(guò)ref操作Dom元素及hooks的使用方法,需要的朋友可以參考下2023-01-01前端設(shè)置cookie之vue-cookies使用及說(shuō)明
這篇文章主要介紹了前端設(shè)置cookie之vue-cookies使用及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05