好久沒有更新博客了。。。不知不覺做前端已過去一年有余了,漸漸發現現在入門前端的門檻越來越高了,需要學習的東西也多了不少,工作中效率也越來越慢,那么提高自己的打碼速度也就越發的重要了,而作為程序員的我們也應該把和業務無關的東西提取出來,讓它自動化,簡單化,這樣才能事半功倍。於是便有了我做工具鏈的想法,經過兩個版本的迭代,npm上也有3千多的下載了,也算是穩定了下來,這個系列我就來一步步的講述整個工具鏈的始末吧~
首先,先貼上github地址 nuts工具鏈,(求關注 (☆_☆))在文檔中記錄了如何使用,在這里我就不再贅述了,我們就從一個個的命令開始吧。
創建新項目
我的工具鏈是基於gulp開發的,所以在運行的時候需要gulp命令進行操作,今天就來介紹一下 create 這個命令是如何實現的。首先看這個命令的用法:
gulp create --name xxxx
一般來說我們的項目的目錄組成是由 html文件,js文件和css文件以及圖片字體等靜態資源組成的,那么我們在創建新項目的時候也就應該擁有這些內容了。那么這整個是怎么開始運行起來的呢?這就要從開始輸入命令行的時候說起來了,在這個命令行中,create 關鍵字是一個任務,而這個任務接收 -- 結尾的參數,並且拿到參數的內容已交給后續的程序處理,這其實就是整個庫的結構了,而最外層的gulpfile.js 文件就是我們項目的配置文件了,因為使用的是gulp3.X的版本,所以必須存在這個gulpfile文件才能正常運行gulp任務,而在這個文件中我也僅僅是引入了 controller 文件並且運行其中的 run 方法,剩下的僅僅是提供一個config的配置對象而已,那么這個 run 方法又是做什么的呢?
exports.run = ()=> { fs.readdirSync('./nuts/tasks/').forEach((files) => { if (/(\.(js)$)/i.test(path.extname(files))) { require('./tasks/' + files); } }); };
在這個方法中我循環加載了tasks文件夾下的所有文件,也就是說tasks文件夾下的每一個文件都是一個任務,同時為了支持ES6的代碼,nuts中使用了webpack作為打包工具,下面這個方法返回的就是一個最基礎的webpack配置:
// webpack的配置文件,一般情況下不需要修改 exports.webpackConfig = (dev)=> { return { watch: false, module: { loaders: [ { test: /\.js$/, loader: 'babel-loader', exclude: './node_modules/', query: { presets: ['es2015'] } } ] }, plugins: (dev == 'dev') ? [] : [new webpack.optimize.UglifyJsPlugin()] } };
而整個命令行會接收多少參數呢?端口號,項目名稱,打包版本等等,這么多的參數,我選擇采用一個對象來進行保存,這樣便於管理和替換:
/** * 通過命令行傳入的參數,在這里進行緩存,方便后面調用。 */ exports.arguments = ()=> { let inputArgv = minimist(process.argv.slice(2)); return { _name: inputArgv.name || inputArgv.dir, _port: inputArgv.port, _clean: inputArgv.clean, _dev: inputArgv.dev, _build: inputArgv.build, _create: inputArgv.create, _ver: inputArgv.ver, _include: inputArgv.include }; };
這段代碼中的 minmist 方法是一個第三方的庫,這是一個可以讓node接收命令行參數的庫,雖然功能簡單,但是夠用就好了~何況它還特別的小~
其實這個流程總結下來就是由 minmist 接收命令行中傳來的參數,然后交給 controller文件去處理,然后再將各個參數傳給不同的tasks去處理的流程,只不過,在這個當中,我做了一些針對自己的處理。下面我們看看這個 create 命令里面究竟做了些什么~
packages._core.task('create', () => { let proName = controller.arguments()._name || defaultName(), devDir = `${controller.config.sourceDir}/${proName}`; if (!!path.basename(proName)) { fs.exists(devDir, (exists) => { if (exists) { console.log('警告!!!您要創建的項目已經存在!'); } else { createProject('nuts/templets', devDir, path.basename(proName)); } }); } else { console.log('警告!!!這是一個需要完整路徑的項目!'); } });
在這個任務初始化的時候我們首先初始化兩個參數,一個是需要創建的項目名稱 proName,以及這個項目相對的路徑 devDir,然后檢查這個項目是否已經創建,如果不存在的話就調用 createProject 方法來進行創建。
/** * 創建新項目的函數 * @param templet * @param devDir 項目路徑 * @param name 項目名稱 */ function createProject(templet, devDir, name) { var letter_name = name.replace(/\_(\w)/g, (all, letter)=> { return letter.toUpperCase(); }); packages._core.src(`${templet}/index.html`) .pipe(packages._replace('@@main', name)) .pipe(packages._core.dest(`${devDir}/`)); packages._core.src(`${templet}/scss/main.scss`) .pipe(packages._rename(`${name}.scss`)) .pipe(packages._core.dest(`${devDir}/scss`)); packages._core.src(`${templet}/js/main.tmpl`) .pipe(packages._replace({ '@@project_name': name, '@@author': controller.config.author, '@@date': time, '@@project': letter_name.replace(/(\w)/, v=> v.toUpperCase()), '@@letter': letter_name })) .pipe(packages._rename(`${name}.js`)) .pipe(packages._core.dest(`${devDir}/js`)); // 創建圖片和字體文件夾 mkdirs(path.resolve(__dirname, `../../${devDir}/`), (err)=>{ if (err) { console.log(err); } fs.mkdirSync(path.resolve(__dirname, `../../${devDir}/images`)); fs.mkdirSync(path.resolve(__dirname, `../../${devDir}/font`)); }); console.log(name + '項目創建完成!!!'); }
這個方法接受3個參數,但是使用者只能傳入后兩個參數,我要做的就是創建html文件,js文件和scss文件以及images和font兩個文件夾,同時在輸出這些文件的時候我也會根據配置文件中的設置去替換模板中的關鍵字,其中需要注意的就是node在創建文件夾的時候需要如果父文件夾不存在時則會拋出錯誤,那么這個時候就需要遞歸的創建缺失的文件夾了,所以我寫了 mkdirs 方法來解決這個問題。
在tasks同級的目錄下有個util目錄,里面放着一些非任務的腳本,通常是用來處理諸如此類的問題的:
/** * 創建多層文件夾 異步 * Created by fuhuixiang on 16-9-1. */ const fs = require('fs'), path = require('path'); module.exports = (dirpath, callback)=> { mkdirs(dirpath, ()=> { callback(); }); }; // 異步文件夾創建 遞歸方法 function mkdirs(dirpath, _callback) { fs.exists(dirpath, (exists)=> { if (exists) { _callback(dirpath); } else { //嘗試創建父目錄,然后再創建當前目錄 mkdirs(path.dirname(dirpath), ()=> { fs.mkdir(dirpath, _callback); }); } }); }
通過這樣一個簡單的遞歸方式,就可以循環的創建文件夾了,到此create命令中的兩點,創建文件和替換關鍵字的功能點都實現了,下面就是真正的使用了~下篇我將介紹真正使用最多的dev命令~
gulp create ---name test1 gulp create ---name 2016/test1