背景
創(chuàng)新互聯(lián)是一家集網(wǎng)站建設(shè),南票企業(yè)網(wǎng)站建設(shè),南票品牌網(wǎng)站建設(shè),網(wǎng)站定制,南票網(wǎng)站建設(shè)報價,網(wǎng)絡(luò)營銷,網(wǎng)絡(luò)優(yōu)化,南票網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強企業(yè)競爭力。可充分滿足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時我們時刻保持專業(yè)、時尚、前沿,時刻以成就客戶成長自我,堅持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實用型網(wǎng)站。
在平時工作中會有遇到許多以相同模板定制的小程序,因此想自己建立一個生成模板的腳手架工具,以模板為基礎(chǔ)構(gòu)建對應(yīng)的小程序,而平時的小程序都是用mpvue框架來寫的,因此首先先參考一下Vue-cli的原理。知道原理之后,再定制自己的模板腳手架肯定是事半功倍的。
在說代碼之前我們首先回顧一下Vue-cli的使用,我們通常使用的是webpack模板包,輸入的是以下代碼。
vue?init?webpack?[project-name]復(fù)制代碼
在執(zhí)行這段代碼之后,系統(tǒng)會自動下載模板包,隨后會詢問我們一些問題,比如模板名稱,作者,是否需要使用eslint,使用npm或者yarn進行構(gòu)建等等,當所有問題我們回答之后,就開始生成腳手架項目。
我們將源碼下來,源碼倉庫點擊這里,平時用的腳手架還是2.0版本,要注意,默認的分支是在dev上,dev上是3.0版本。
我們首先看一下package.json,在文件當中有這么一段話
{?"bin":?{?"vue":?"bin/vue",?"vue-init":?"bin/vue-init",?"vue-list":?"bin/vue-list" ?} } 復(fù)制代碼
由此可見,我們使用的命令 vue init,應(yīng)該是來自bin/vue-init這個文件,我們接下來看一下這個文件中的內(nèi)容
bin/vue-init
const?download?=?require('download-git-repo')const?program?=?require('commander')const?exists?=?require('fs').existsSyncconst?path?=?require('path')const?ora?=?require('ora')const?home?=?require('user-home')const?tildify?=?require('tildify')const?chalk?=?require('chalk')const?inquirer?=?require('inquirer')const?rm?=?require('rimraf').syncconst?logger?=?require('../lib/logger')const?generate?=?require('../lib/generate')const?checkVersion?=?require('../lib/check-version')const?warnings?=?require('../lib/warnings')const?localPath?=?require('../lib/local-path') 復(fù)制代碼
download-git-repo 一個用于下載git倉庫的項目的模塊 commander 可以將文字輸出到終端當中 fs 是node的文件讀寫的模塊 path 模塊提供了一些工具函數(shù),用于處理文件與目錄的路徑 ora 這個模塊用于在終端里有顯示載入動畫 user-home 獲取用戶主目錄的路徑 tildify 將絕對路徑轉(zhuǎn)換為波形路徑 比如/Users/sindresorhus/dev → ~/dev inquirer 是一個命令行的回答的模塊,你可以自己設(shè)定終端的問題,然后對這些回答給出相應(yīng)的處理 rimraf 是一個可以使用 UNIX 命令 rm -rf的模塊 剩下的本地路徑的模塊其實都是一些工具類,等用到的時候我們再來講
//?是否為本地路徑的方法?主要是判斷模板路徑當中是否存在?`./`const?isLocalPath?=?localPath.isLocalPath//?獲取模板路徑的方法?如果路徑參數(shù)是絕對路徑?則直接返回?如果是相對的?則根據(jù)當前路徑拼接const?getTemplatePath?=?localPath.getTemplatePath 復(fù)制代碼/** ?*?Usage. ?*/program ?.usage('<template-name>?[project-name]') ?.option('-c,?--clone',?'use?git?clone') ?.option('--offline',?'use?cached?template')/** ?*?Help. ?*/program.on('--help',?()?=>?{ ?console.log('?Examples:') ?console.log() ?console.log(chalk.gray('?#?create?a?new?project?with?an?official?template')) ?console.log('?$?vue?init?webpack?my-project') ?console.log() ?console.log(chalk.gray('?#?create?a?new?project?straight?from?a?github?template')) ?console.log('?$?vue?init?username/repo?my-project') ?console.log() })/** ?*?Help. ?*/function?help?()?{?program.parse(process.argv)?if?(program.args.length?<?1)?return?program.help() }help() 復(fù)制代碼
這部分代碼聲明了vue init用法,如果在終端當中 輸入 vue init --help或者跟在vue init 后面的參數(shù)長度小于1,也會輸出下面的描述
?Usage:?vue-init?<template-name>?[project-name] ?Options: ?-c,?--clone?use?git?clone ?--offline?use?cached?template ?-h,?--help?output?usage?information ?Examples:?#?create?a?new?project?with?an?official?template ?$?vue?init?webpack?my-project?#?create?a?new?project?straight?from?a?github?template ?$?vue?init?username/repo?my-project 復(fù)制代碼
接下來是一些變量的獲取
/** ?*?Settings. ?*///?模板路徑let?template?=?program.args[0]const?hasSlash?=?template.indexOf('/')?>?-1//?項目名稱const?rawName?=?program.args[1]const?inPlace?=?!rawName?||?rawName?===?'.'//?如果不存在項目名稱或項目名稱輸入的'.'?則name取的是?當前文件夾的名稱const?name?=?inPlace???path.relative('../',?process.cwd())?:?rawName//?輸出路徑const?to?=?path.resolve(rawName?||?'.')//?是否需要用到?git?cloneconst?clone?=?program.clone?||?false//?tmp為本地模板路徑?如果?是離線狀態(tài)?那么模板路徑取本地的const?tmp?=?path.join(home,?'.vue-templates',?template.replace(/[\/:]/g,?'-'))if?(program.offline)?{ ?console.log(`>?Use?cached?template?at?${chalk.yellow(tildify(tmp))}`)?template?=?tmp } 復(fù)制代碼
接下來主要是根據(jù)模板名稱,來下載并生產(chǎn)模板,如果是本地的模板路徑,就直接生成。
/** ?*?Check,?download?and?generate?the?project. ?*/function?run?()?{?//?判斷是否是本地模板路徑 ?if?(isLocalPath(template))?{?//?獲取模板地址 ?const?templatePath?=?getTemplatePath(template)?//?如果本地模板路徑存在?則開始生成模板 ?if?(exists(templatePath))?{ ?generate(name,?templatePath,?to,?err?=>?{?if?(err)?logger.fatal(err) ?console.log() ?logger.success('Generated?"%s".',?name) ?}) ?}?else?{ ?logger.fatal('Local?template?"%s"?not?found.',?template) ?} ?}?else?{?//?非本地模板路徑?則先檢查版本 ?checkVersion(()?=>?{?//?路徑中是否?包含'/' ?//?如果沒有?則進入這個邏輯 ?if?(!hasSlash)?{?//?拼接路徑?'vuejs-tempalte'下的都是官方的模板包 ?const?officialTemplate?=?'vuejs-templates/'?+?template ?//?如果路徑當中存在?'#'則直接下載 ?if?(template.indexOf('#')?!==?-1)?{ ?downloadAndGenerate(officialTemplate) ?}?else?{?//?如果不存在?-2.0的字符串?則會輸出?模板廢棄的相關(guān)提示 ?if?(template.indexOf('-2.0')?!==?-1)?{ ?warnings.v2SuffixTemplatesDeprecated(template,?inPlace???''?:?name)?return ?}?//?下載并生產(chǎn)模板 ?downloadAndGenerate(officialTemplate) ?} ?}?else?{?//?下載并生生成模板 ?downloadAndGenerate(template) ?} ?}) ?} } 復(fù)制代碼
我們來看下 downloadAndGenerate這個方法
/** ?*?Download?a?generate?from?a?template?repo. ?* ?*?@param?{String}?template ?*/function?downloadAndGenerate?(template)?{?//?執(zhí)行加載動畫 ?const?spinner?=?ora('downloading?template') ?spinner.start()?//?Remove?if?local?template?exists ?//?刪除本地存在的模板 ?if?(exists(tmp))?rm(tmp)?//?template參數(shù)為目標地址?tmp為下載地址?clone參數(shù)代表是否需要clone ?download(template,?tmp,?{?clone?},?err?=>?{?//?結(jié)束加載動畫 ?spinner.stop()?//?如果下載出錯?輸出日志 ?if?(err)?logger.fatal('Failed?to?download?repo?'?+?template?+?':?'?+?err.message.trim())?//?模板下載成功之后進入生產(chǎn)模板的方法中?這里我們再進一步講 ?generate(name,?tmp,?to,?err?=>?{?if?(err)?logger.fatal(err) ?console.log() ?logger.success('Generated?"%s".',?name) ?}) ?}) } 復(fù)制代碼
到這里為止,bin/vue-init就講完了,該文件做的最主要的一件事情,就是根據(jù)模板名稱,來下載生成模板,但是具體下載和生成的模板的方法并不在里面。
下載模板
下載模板用的download方法是屬于download-git-repo模塊的。
最基礎(chǔ)的用法為如下用法,這里的參數(shù)很好理解,第一個參數(shù)為倉庫地址,第二個為輸出地址,第三個是否需要 git clone,帶四個為回調(diào)參數(shù)
download('flipxfx/download-git-repo-fixture',?'test/tmp',{?clone:?true?},?function?(err)?{?console.log(err???'Error'?:?'Success') }) 復(fù)制代碼
在上面的run方法中有提到一個#的字符串實際就是這個模塊下載分支模塊的用法
download('bitbucket:flipxfx/download-git-repo-fixture#my-branch',?'test/tmp',?{?clone:?true?},?function?(err)?{?console.log(err???'Error'?:?'Success') }) 復(fù)制代碼
生成模板
模板生成generate方法在generate.js當中,我們繼續(xù)來看一下
generate.js
const?chalk?=?require('chalk')const?Metalsmith?=?require('metalsmith')const?Handlebars?=?require('handlebars')const?async?=?require('async')const?render?=?require('consolidate').handlebars.renderconst?path?=?require('path')const?multimatch?=?require('multimatch')const?getOptions?=?require('./options')const?ask?=?require('./ask')const?filter?=?require('./filter')const?logger?=?require('./logger') 復(fù)制代碼
chalk 是一個可以讓終端輸出內(nèi)容變色的模塊 Metalsmith是一個靜態(tài)網(wǎng)站(博客,項目)的生成庫 handlerbars 是一個模板編譯器,通過template和json,輸出一個html async 異步處理模塊,有點類似讓方法變成一個線程 consolidate 模板引擎整合庫 multimatch 一個字符串數(shù)組匹配的庫 options 是一個自己定義的配置項文件
隨后注冊了2個渲染器,類似于vue中的 vif velse的條件渲染
//?register?handlebars?helperHandlebars.registerHelper('if_eq',?function?(a,?b,?opts)?{?return?a?===?b ???opts.fn(this) ?:?opts.inverse(this) }) Handlebars.registerHelper('unless_eq',?function?(a,?b,?opts)?{?return?a?===?b ???opts.inverse(this) ?:?opts.fn(this) }) 復(fù)制代碼
接下來看關(guān)鍵的generate方法
module.exports?=?function?generate?(name,?src,?dest,?done)?{?//?讀取了src目錄下的?配置文件信息,?同時將?name?auther(當前git用戶)?賦值到了?opts?當中 ?const?opts?=?getOptions(name,?src)?//?拼接了目錄?src/{template}?要在這個目錄下生產(chǎn)靜態(tài)文件 ?const?metalsmith?=?Metalsmith(path.join(src,?'template'))?//?將metalsmitch中的meta?與?三個屬性合并起來?形成?data ?const?data?=?Object.assign(metalsmith.metadata(),?{?destDirName:?name,?inPlace:?dest?===?process.cwd(),?noEscape:?true ?})?//?遍歷?meta.js元數(shù)據(jù)中的helpers對象,注冊渲染模板數(shù)據(jù) ?//?分別指定了?if_or?和?template_version內(nèi)容 ?opts.helpers?&&?Object.keys(opts.helpers).map(key?=>?{ ?Handlebars.registerHelper(key,?opts.helpers[key]) ?})?const?helpers?=?{?chalk,?logger?}?//?將metalsmith?metadata?數(shù)據(jù)?和?{?isNotTest,?isTest?合并?} ?if?(opts.metalsmith?&&?typeof?opts.metalsmith.before?===?'function')?{ ?opts.metalsmith.before(metalsmith,?opts,?helpers) ?}?//?askQuestions是會在終端里詢問一些問題 ?//?名稱?描述?作者?是要什么構(gòu)建?在meta.js?的opts.prompts當中 ?//?filterFiles?是用來過濾文件 ?//?renderTemplateFiles?是一個渲染插件 ?metalsmith.use(askQuestions(opts.prompts)) ?.use(filterFiles(opts.filters)) ?.use(renderTemplateFiles(opts.skipInterpolation))?if?(typeof?opts.metalsmith?===?'function')?{ ?opts.metalsmith(metalsmith,?opts,?helpers) ?}?else?if?(opts.metalsmith?&&?typeof?opts.metalsmith.after?===?'function')?{ ?opts.metalsmith.after(metalsmith,?opts,?helpers) ?}?//?clean方法是設(shè)置在寫入之前是否刪除原先目標目錄?默認為true ?//?source方法是設(shè)置原路徑 ?//?destination方法就是設(shè)置輸出的目錄 ?//?build方法執(zhí)行構(gòu)建 ?metalsmith.clean(false) ?.source('.')?//?start?from?template?root?instead?of?`./src`?which?is?Metalsmith's?default?for?`source` ?.destination(dest) ?.build((err,?files)?=>?{ ?done(err)?if?(typeof?opts.complete?===?'function')?{?//?當生成完畢之后執(zhí)行?meta.js當中的?opts.complete方法 ?const?helpers?=?{?chalk,?logger,?files?} ?opts.complete(data,?helpers) ?}?else?{ ?logMessage(opts.completeMessage,?data) ?} ?})?return?data } 復(fù)制代碼
meta.js
接下來看以下complete方法
complete:?function(data,?{?chalk?})?{?const?green?=?chalk.green?//?會將已有的packagejoson?依賴聲明重新排序 ?sortDependencies(data,?green)?const?cwd?=?path.join(process.cwd(),?data.inPlace???''?:?data.destDirName)?//?是否需要自動安裝?這個在之前構(gòu)建前的詢問當中?是我們自己選擇的 ?if?(data.autoInstall)?{?//?在終端中執(zhí)行?install?命令 ?installDependencies(cwd,?data.autoInstall,?green) ?.then(()?=>?{?return?runLintFix(cwd,?data,?green) ?}) ?.then(()?=>?{ ?printMessage(data,?green) ?}) ?.catch(e?=>?{?console.log(chalk.red('Error:'),?e) ?}) ?}?else?{ ?printMessage(data,?chalk) ?} ?} 復(fù)制代碼
構(gòu)建自定義模板
在看完vue-init命令的原理之后,其實定制自定義的模板是很簡單的事情,我們只要做2件事
首先我們需要有一個自己模板項目
如果需要自定義一些變量,就需要在模板的meta.js當中定制
由于下載模塊使用的是download-git-repo模塊,它本身是支持在github,gitlab,bitucket上下載的,到時候我們只需要將定制好的模板項目放到git遠程倉庫上即可。
由于我需要定義的是小程序的開發(fā)模板,mpvue本身也有一個quickstart的模板,那么我們就在它的基礎(chǔ)上進行定制,首先我們將它fork下來,新建一個custom分支,在這個分支上進行定制。
我們需要定制的地方有用到的依賴庫,需要額外用到less以及wxparse 因此我們在 template/package.json當中進行添加
{?//?...?部分省略?"dependencies":?{?"mpvue":?"^1.0.11"{{#vuex}}, ?"vuex":?"^3.0.1"{{/vuex}} ?},?"devDependencies":?{?//?...?省略?//?這是添加的包?"less":?"^3.0.4",?"less-loader":?"^4.1.0",?"mpvue-wxparse":?"^0.6.5" ?} } 復(fù)制代碼
除此之外,我們還需要定制一下eslint規(guī)則,由于只用到standard,因此我們在meta.js當中 可以將 airbnb風(fēng)格的提問刪除
"lintConfig":?{?"when":?"lint",?"type":?"list",?"message":?"Pick?an?ESLint?preset",?"choices":?[ ?{?"name":?"Standard?(https://github.com/feross/standard)",?"value":?"standard",?"short":?"Standard" ?}, ?{?"name":?"none?(configure?it?yourself)",?"value":?"none",?"short":?"none" ?} ?] } 復(fù)制代碼
.eslinttrc.js
'rules':?{ ?{{#if_eq?lintConfig?"standard"}}?"camelcase":?0,?//?allow?paren-less?arrow?functions ?"arrow-parens":?0,?"space-before-function-paren":?0,?//?allow?async-await ?"generator-star-spacing":?0, ?{{/if_eq}} ?{{#if_eq?lintConfig?"airbnb"}}?//?don't?require?.vue?extension?when?importing ?'import/extensions':?['error',?'always',?{?'js':?'never',?'vue':?'never' ?}],?//?allow?optionalDependencies ?'import/no-extraneous-dependencies':?['error',?{?'optionalDependencies':?['test/unit/index.js'] ?}], ?{{/if_eq}}?//?allow?debugger?during?development ?'no-debugger':?process.env.NODE_ENV?===?'production'???2?:?0 ?} 復(fù)制代碼
最后我們在構(gòu)建時的提問當中,再設(shè)置一個小程序名稱的提問,而這個名稱會設(shè)置到導(dǎo)航的標題當中。 提問是在meta.js當中添加
"prompts":?{?"name":?{?"type":?"string",?"required":?true,?"message":?"Project?name" ?},?//?新增提問 ?"appName":?{?"type":?"string",?"required":?true,?"message":?"App?name" ?} } 復(fù)制代碼
main.json
{?"pages":?[?"pages/index/main",?"pages/counter/main",?"pages/logs/main" ?],?"window":?{?"backgroundTextStyle":?"light",?"navigationBarBackgroundColor":?"#fff", ?//?根據(jù)提問設(shè)置標題?"navigationBarTitleText":?"{{appName}}",?"navigationBarTextStyle":?"black" ?} } 復(fù)制代碼
最后我們來嘗試一下我們自己的模板
vue?init?Baifann/mpvue-quickstart#custom?min-app-project復(fù)制代碼
總結(jié)
以上模板的定制是十分簡單的,在實際項目上肯定更為復(fù)雜,但是按照這個思路應(yīng)該都是可行的。比如說將一些自行封裝的組件也放置到項目當中等等,這里就不再細說。原理解析都是基于vue-cli 2.0的,但實際上 3.0也已經(jīng)整裝待發(fā),如果后續(xù)有機會,深入了解之后,再和大家分享,謝謝大家。
標題名稱:Vue-cli原理分析
網(wǎng)頁地址:http://www.chinadenli.net/article26/gepdcg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站導(dǎo)航、企業(yè)建站、響應(yīng)式網(wǎng)站、網(wǎng)站制作、網(wǎng)站設(shè)計公司、云服務(wù)器
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)