我從過年開始寫自己的前端工具 coodev,目前已經寫出一個基本的架子,大多數預想的功能都能跑通,還剩一些需要解決的問題也都已經列出來了。本來這一周打算解決對不同后端模板的前后端渲染支持的問題。但是看了一下代碼,當初很多功能寫的還是有很大優化空間的。我打算先停下腳步,多看一些優秀的開源框架的源碼,然后優化整理一下我的前端工具。
在選擇閱讀源碼時,我首先想到的就是vue-cli的源碼。畢竟我最初的靈感就是來源於vue-cli。閑話少說,我們先下載一份vue-cli,打開源碼先看package.json熟悉整體的代碼結構,不過這里我建議直接閱讀/bin 文件夾下的幾個文件:vue、 vue-init、 vue-build、 vue-list
引用的比較多的比如vue-init就在一開始引用了一大堆的文件
1 #!/usr/bin/env node 2 3 var download = require('download-git-repo') 4 var program = require('commander') 5 var exists = require('fs').existsSync 6 var path = require('path') 7 var ora = require('ora') 8 var home = require('user-home') 9 var tildify = require('tildify') 10 var chalk = require('chalk') 11 var inquirer = require('inquirer') 12 var logger = require('../lib/logger') 13 var generate = require('../lib/generate') 14 var checkVersion = require('../lib/check-version') 15 var warnings = require('../lib/warnings')
它們都是干嘛用的,我們調一些比較重要的說一下。
一、優化參數處理
先看vue:
1 #!/usr/bin/env node 2 3 require('commander') 4 .version(require('../package').version) 5 .usage('<command> [options]') 6 .command('init', 'generate a new project from a template') 7 .command('list', 'list available official templates') 8 .command('build', 'prototype a new project') 9 .parse(process.argv)
可以看出這個文件主要是對 ‘commander’的使用。那么‘commander’這個npm包又是什么呢?字面意思‘指揮官’,我們在 https://www.npmjs.com/ 網站上可以搜一下,會有一些簡單的文檔用例。其實‘commander’就是命令控制器,vue-cli用這個庫來管理用戶輸入的命令,來完成在終端上用戶命令行的交互。
回到vue-cli能看到一個很大的鏈試調用,完成了一些列的任務,我們要先了解這些api,就能明白這個文件在干什么了。關於 ‘commander’的API,可以看npm上的官方例子,后續我也會寫一篇有關於使用 ‘commander’的文章。我這里先介紹幾個我用到的:
我原先是通過一個map定義命令,用到的地方require一下,通過 process.argv.splice(2) 拿到參數。后來改用‘commander’,就比較省心直接注冊參數:
1 var program = require('commander'); 2 3 4 program 5 .version(require('./package').version) 6 .option('-i, --init', '初始化項目文件夾') 7 .option('-d, --dev', '以開發模式啟動coodev 即時編譯 不壓縮') 8 .option('-b, --build', '以生產模式啟動coodev 編譯一次 壓縮') 9 .option('-s, --server', '本地server調試') 10 .option('-p, --publish', '提交到發布倉庫') 11 .parse(process.argv); 12 13 /** init */ 14 if (program.init) { 15 require('./src/tasks/task-init.js').render(); 16 } 17 18 /** dev */ 19 if (program.dev) { 20 require('./src/tasks/task-dev.js').render(); 21 } 22 23 /** build */ 24 if (program.build) { 25 require('./src/tasks/task-build.js').render(); 26 } 27 28 /** start */ 29 if (program.start) { 30 require('./src/tasks/task-server.js').render(); 31 } 32 33 /** publish */ 34 if (program.publish) { 35 require('./src/tasks/task-publish.js').render(); 36 } 37 38 program.on('--help', function () { 39 console.log(' Examples:') 40 console.log() 41 console.log(chalk.gray(' # create a new project with an official template')) 42 console.log(' $ coodev --init') 43 console.log() 44 console.log(chalk.gray(' # develop project')) 45 console.log(' $ coodev --dev') 46 console.log() 47 console.log(chalk.gray(' # start local server')) 48 console.log(' $ coodev --start') 49 console.log() 50 });
其中version和我原來的方式差不多,版本號就直接引用package.json,自己覺得預設的命令就用option定義(簡寫,全名,描述),簡寫和全名是調用的時候用的,描述是在輸入命令'--help'的時候會自動抓取的。最后的‘on’,就像是jquery中的事件綁定一樣,給命令追加額外的處理邏輯。
這里只是一些常用的,我后期還要加上一些交互性質的參數處理,比如引導用戶配置工程等等,雖然原來自己寫的也能滿足目前的需求,但為了后期的功能,還是先改成‘commander’好一些。
二、按需拉取github上的模板
這事我最想學習的功能。用過vue-cli的朋友都知道有一個這樣的命令 $ vue init webpack-simple yourproject-name 的初始化命令,他會作為幾個腳手架給我們初始化模板,那么模板都放哪了?如果放在vue-cli里面,隨着后期維護,就會越來越大,這不就毀了嗎。我看了一下官方的描述說模板放在了另一個git倉庫里,我也在github里找到了。我起初的時候為了獨立維護模板,也是這樣做的放在了別處,每次coodev發布時,用腳本同步過來,但我的方式雖然快,但是也就是現在模板數少還可以。因此我打算按需要拉取模板。
這個功能無疑是在vue-init文件里面,我發現有一個 var download = require('download-git-repo') ,在npm上查了一下,描述是這樣“從節點下載並提取一個git存儲庫”,那么應該就是它了。
我的模板放在了github上的另一個倉庫,https://github.com/grARM/coodev-temp-normal。現在想拉到coodev中只需要調用如下代碼:
1 /** get code from github */ 2 var loadNormalTmpl = function(cb){ 3 download('grARM/coodev-temp-normal', proPath, function (err) { 4 if(err){ 5 console.log('err', err); 6 } 7 cb && cb(err); 8 }); 9 }
其中proPath是我的工程目錄,這樣就比我之前的遞歸文件拷貝高端了不少。
三、交互式命令
既然可以從別處拉模板,那么我的coodev一下子就可以精簡很多,我以后可以擁有一大堆模板,都放在github上,我想拉哪一個,就拉哪一個。我還需要給用戶提供交互式的命令。在vue-init中也是有這個例子的。 var inquirer = require('inquirer')
'inquirer'是一個提供交互式命令的npm包,地址: https://www.npmjs.com/package/inquirer。這個庫在Github的examples文件夾里有詳細的例子。每一個問答都會被封裝到answers里。
1 inquirer.prompt([{ 2 type: 'list', 3 message: 'which template do you need:', 4 name: 'template', 5 choices: ['normal', 'wap', 'h5'] 6 }]).then(function (answers) { 7 console.log(answers) 8 done(); 9 })
如圖:
總結:
就這樣,我就在vue-cli中學習了3招:命令管理、按需拉取模板、交互式命令。
接下來就是處理不同的主流后端模板引擎的支持了。