摘要:剛剛在里說明的回調(diào)函數(shù)綁定在命令下。使用開源協(xié)議源代碼都放在目錄下目錄要對不同的代碼進行合理的分層。,我是韓亦樂,現(xiàn)任本科軟工男一枚。
以我的小經(jīng)驗來看,軟件萌新寫出來的代碼大多“無法直視”。具體現(xiàn)象包括空格和換行符亂用、文件夾和變量的命名多使用拼音等。坐不住的我,便想到了通過 ESLint 配置文件來規(guī)范實驗室的 JavaScript 代碼規(guī)范的 Idea。
于是巧遇前實驗室畢業(yè)學(xué)長曾經(jīng)發(fā)布的 npm 包——creatshare-project-quick-init。安裝好這個包,我們便可以在空文件夾下生成一個項目的基礎(chǔ)骨架。
dist //發(fā)布目錄,用于生產(chǎn)環(huán)境 src //開發(fā)目錄,開發(fā)時所需資源 |----dist //測試環(huán)境目錄 | |----static | |----css //編譯打包后的css資源 | |----js //打包壓縮后的js資源 | |----imgs //測試環(huán)境圖片資源 |----less //開發(fā)所需less代碼 |----js //開發(fā)所需js代碼 | |----lib //庫或框架資源 |----imgs //開發(fā)所需圖片資源 index.html //開發(fā)頁面 gulpfile.js package.json README.md
What a good idea~!
在學(xué)長的這個包中,主要構(gòu)建了 gulp 配置,less 和測試文件的骨架。雖然再無更多內(nèi)容,但這份構(gòu)建基礎(chǔ)骨架的靈感還是被我愉快的收走了——學(xué)前端的人很多,但大多都太缺工程化意識了。于是,這個靈感成為了不錯突破口。
creatshare-app-init 腳手架孕育而生。
0通過這篇文章,你能了解到:
如何用 NodeJS 編寫命令行工具?
如何發(fā)布自己的 npm 包?
筆者與 creatshare-app-init 的故事?
在本文中,或多或少出現(xiàn)過以下關(guān)鍵字,我的解釋是:
輪子:該詞在前端開發(fā)日常用語中,表示一個基于原生代碼實現(xiàn),但并沒有對前端行業(yè)產(chǎn)生積極意義的模塊。雖然它的出現(xiàn)方便了一些人的使用,但更多的加大了我們的學(xué)習(xí)成本。
項目:該詞在前端領(lǐng)域常指一個服務(wù)于用戶的軟件立項。
模塊:creatshare-app-init 就是一個模塊,是開發(fā)前端項目中的一個子集。正如汽車的各個部件一樣,多個模塊合理組裝起來才是一輛汽車。
1嘗試解析源碼,第一步,從模塊根目錄下的 package.json 來看。
"dependencies": { "commander": "^2.11.0" }, "devDependencies": { "babel-plugin-transform-runtime": "^6.23.0", "babel-preset-es2015": "^6.24.1", "babel-preset-stage-2": "^6.24.1", "babel-runtime": "^6.26.0", "eslint-config-standard": "^10.2.1", "eslint-plugin-import": "^2.8.0", "eslint-plugin-node": "^5.2.1", "eslint-plugin-promise": "^3.6.0", "eslint-plugin-standard": "^3.0.1" }
如上,dependencies 聲明了模塊上線時的依賴,devDependencies 聲明了模塊開發(fā)時的依賴。該模塊在上線時,即 npm 包被用戶用到時,只需要 commander 庫。commander 庫是 NodeJS 命令行接口開發(fā)的優(yōu)選解決方案,受啟發(fā)于 Ruby 的 commander。在解析 bin/index.js 源碼時將詳細拓展。
"name": "creatshare-app-init", "version": "2.1.0", "description": "CreatShare 實驗室前端項目初始化工具", "bin": { "cs": "bin/index.js" }, "scripts": { "compile": "babel src/ -d lib/", "prepublish": "npm run compile", "eslint": "eslint src bin", "test": "echo "Error: no test specified" && exit 1" },
上面一段是 package.json 最開頭的內(nèi)容,字段詳情如下:
name 字段:聲明模塊名稱。特殊注意該字段不允許大寫字母及空格的出現(xiàn),且其與 version 字段形成了 npm 模塊的唯一標識符。
version 字段:聲明模塊當前版本號。這里每當使用 npm publish 將模塊發(fā)布到 npm 倉庫中時,版本號都需要手動自增。
description 字段:對模塊進行描述,同時有助于被檢索。
bin 字段:npm 本身是通過 bin 屬性配置一個或多個可解析到 PATH 路徑下的可執(zhí)行模塊。模塊若被全局安裝,則 npm 會為 bin 中配置的文件在 bin 目錄下創(chuàng)建一個軟連接;模塊若被局部安裝,軟連接會配置在項目內(nèi)的 ./node_modules/.bin/目錄下。
script 字段:定義模塊的腳本配置。如,當我們在模塊目錄下使用 npm run compile 時,將自動執(zhí)行 babel src/ -d lib/ 命令,進行 ECMAScript6 代碼的轉(zhuǎn)譯。
2剛剛提到 package.json 配置文件下的 bin 字段聲明了 npm 在生成軟連接時的配置。這就便是用戶在安裝好這個目錄后,可以隨時使用 cs 命令的出處。
我們又提到了該模塊在非開發(fā)環(huán)境下只需用到 commander 模塊,這個模塊是 NodeJS 命令行接口開發(fā)的優(yōu)選解決方案。
基于這倆點,我們就從 bin 字段所指向的 bin/index.js 聊起。
#!/usr/bin/env node var program = require("commander") var cs = require("../lib/cs") program .allowUnknownOption() .version("2.1.1") .description("CreatShare 互聯(lián)網(wǎng)實驗室前端 Web App 項目腳手架") .option("-e, --enjoy") program. .command("create") .description("創(chuàng)建一個新的 Web App 項目骨架") .action(function (rootDir) { cs.create(rootDir) }) program.parse(process.argv)
就這么二十來行。因為我們要寫的模塊是要運行在命令行下的,就需要 #!/usr/bin/env node 語句來告訴系統(tǒng)使用 node 環(huán)境來運行我們的文件,必不可少。
在引入 commander 并將其賦值給 program 變量后,我們對其使用了如下方法:
.allowUnknownOption() 方法:
.version() 方法:用于設(shè)置命令程序的版本號。
.description() 方法:用于設(shè)置命令的描述。可以綁定在跟命令下,這里是 cs 命令;或綁定在子命令下,如 cs create
.option() 方法:定義命令的具體選項。
.command() 方法:定義命令的子命令,這里是 cs create
.action() 方法:用于設(shè)置命令執(zhí)行的相關(guān)回調(diào)。這里綁定在 cs create
代碼最后的 process 為進程對象,是 NodeJS 運行時存在的眾多全局變量之一。process 對象中的 argv 屬性用來捕獲命令行參數(shù)。
3剛剛在 bin/index.js 里說明的 .action 回調(diào)函數(shù)綁定在 cs create
打住,第一節(jié)里展示的 package.json 中,script字段里有這么一條語句:"compile": "babel src/ -d lib/"。這是說明 lib/ 文件夾下的代碼是通過 src/ 文件夾下的代碼轉(zhuǎn)譯過來的,真正我們需要去關(guān)注的是 src/cs.js 文件。
為什么需要轉(zhuǎn)譯?src 里的 JavaScript 代碼或多或少的使用到了 ECMAScript6 新特性,有些用戶的 Node 環(huán)境并不一定能得到較好的解析。
src/cs.js 主要代碼片段為:
let create = require("./create") let path = require("path") let distPath = path.join(__dirname, "/../dist") let dist = process.cwd() + "/" /** * [運行 create 命令] * @return {[type]} [description] */ exports.create = (rootDir) => { console.log(" 項目目錄開始創(chuàng)建 ") create.init(distPath, dist, rootDir) helpGuide() }
不難理解,create 變量指向 cs create
最終 src/index.js 使用 exports.create 語句向外部暴露出 create 方法。bin/index.js 便可以將該方法通過 .action() 綁定到 cs create
精彩的來了。都說 ECMAScript6 的指定振奮人心,JavaScript 的魅力越來越大,這里便是一次體驗 JavaScript 在 NodeJS 上的新玩法有趣之旅。
在 src/create.js 文件中,主要用到了 NodeJS 自帶的 fs 文件模塊,來生成新項目的基礎(chǔ)架構(gòu)。文件最后暴露出的 init 方法源碼如下。
exports.init = (path, dist, rootDir) => { createRootDir(rootDir) // 從新目錄開始新建項目 dist = dist + rootDir copyDir(path, dist) }
init 方法獲取了 path 參數(shù)、dist 參數(shù)和 rootDir 參數(shù)。在該方法中,我們先將 rootDir 參數(shù)傳入 createRootDir() 函數(shù)中創(chuàng)建項目根目錄。
在哪里創(chuàng)建項目根目錄呢?就在執(zhí)行 cs 命令時的當前目錄下:
const createRootDir = (rootDir) => { fs.access(process.cwd(), function (err) { if (err) { // 目錄不存在時創(chuàng)建目錄 fs.mkdirSync(rootDir) } }) }
有了項目根目錄,就要將模塊下 dist/ 文件夾里的所有文件遞歸拷貝到根目錄下。一個參數(shù)用來指向 dist/ 文件夾,另一個參數(shù)用來指向根目錄,便可以開始遞歸復(fù)制。
/** * [初始化靜態(tài)資源] * @param {[type]} src [初始化資源路徑] * @param {[type]} dist [當前終端所在目錄] * @return {[type]} [description] */ const copyDir = (src, dist) => { fs.access(dist, function (err) { if (err) { // 目錄不存在時創(chuàng)建目錄 fs.mkdirSync(dist) } _copy(null, src, dist) }) function _copy (err, src, dist) { if (err) { throw err } fs.readdir(src, function (err, files) { if (err) { throw err } // 過濾不生成的文件 miscFiles.forEach(function (v) { if (!files.includes(v)) return files = files.filter(function (k) { return k !== v }) }) // 遍歷目錄中的文件 files.forEach(function (path) { var _src = src + "/" + path var _dist = dist + "/" + path fs.stat(_src, function (err, st) { if (err) { throw err } // 判斷是文件還是目錄 if (st.isFile()) { fs.writeFileSync(_dist, fs.readFileSync(_src)) } else if (st.isDirectory()) { // 當是目錄是,遞歸復(fù)制 copyDir(_src, _dist) } }) }) }) } }
fs 文件模塊的具體內(nèi)容推薦閱讀阮一峰的開源電子書——《JavaScript 標準參考教程》中的“NodeJS”章節(jié),來深入淺出 fs 模塊的用法。
完美,這時我們就可以發(fā)布我們的腳手架包了。
5如何發(fā)布一個 npm 包到 npm 倉庫中,供其他人使用?當我們照著第一步,將 package.json 配置好后,其實模塊的準備工作已經(jīng)做好了。
還沒有做的就是在域名為 npmjs.com 的官網(wǎng)上注冊一個賬號。這樣,當我們直接在模塊根目錄使用 npm publish 命令的時候,輸入正確的 npmjs.com 賬號、密碼,就能成功發(fā)布你的開源包了!
縱然讀博文是一個有趣的體驗,但也可以親自動手試一試哦。
6也就是說,酷炫的生成新項目骨架的來源,只是簡單的遞歸復(fù)制該模塊下的 dist/ 文件夾到新項目中。但我們需要關(guān)注的重點在于,dist/ 文件夾下,到底裝了什么?
“初級 Web App 項目初始化工具”一說,也就名歸有主了。dist/ 模板,也就是新項目的骨架如下。
. ├── .babelrc # ES6 代碼轉(zhuǎn)義規(guī)則配置 ├── .eslint.js # JavaScript 代碼規(guī)范 ├── .gitignore # Git 不跟蹤的特殊文件 ├── LICENSE # 開源協(xié)議 ├── README.md # 項目介紹 ├── material # README.md 引用的圖片庫 ├── package.json # 項目配置文件 ├── src # 源碼開發(fā)目錄 │?? ├── favicon.ico # 網(wǎng)頁標題小圖標 │?? ├── html # HTML 頁面模板目錄 │?? ├── image # 圖片資源目錄 │?? ├── manifest.json # 網(wǎng)絡(luò)應(yīng)用清單 │?? ├── script # 腳本文件資源目錄 │?? └── style # 樣式文件資源目錄 ├── webpack.config.js # Webpack 多文件打包基礎(chǔ)配置 ├── webpack.dev.js # Webpack 開發(fā)環(huán)境配置 ├── webpack.prod.js # Webpack 發(fā)布上線配置 └── yarn.lock # yarn 包管理器的依賴說明
新項目骨架中默認推薦了:
使用 Webpack 來打包多頁面;
使用 ESLint 來規(guī)范自己項目的 JavaScript 代碼;
使用 Babel 來編譯使用 ECMAScript 新特性的 JavaScript 代碼。
使用 MIT 開源協(xié)議;
源代碼都放在 src/ 目錄下;
src/ 目錄要對不同的代碼進行合理的分層。
End現(xiàn)在的不足,是未來的暢想。
這個模塊并不完美,一個健壯的命令還應(yīng)該能支持足夠多的參數(shù),運行足夠有意義的子命令。比如我們常用 man 命令來看另一個命令的使用手冊,那要讓用戶能用到 man cs 命令,還需要我們在代碼中加入 man 字段等等。。
我又為什么,這么熱衷于分享這個輪子?
記得有一個前端群里曾有人問過:
“怎么沒有 VueJS 的源碼解析?”
時,我說過:
“大牛很忙,關(guān)注的是前端前沿,不寫這些源碼解析博文是個好事。“當我們想有一個源碼解析教程的時候,這是一個打開新世界的契機——未嘗不使我們親自來寫,通過分享走向?qū)W習(xí)效率金字塔的最高層?”
這樣的能力并不是人人都能具備,也不必要讓人人都具備。我曾在大一傲氣的說過“做最好的自己,影響該影響的人”,現(xiàn)在想起來除了有立刻找地洞鉆進去的沖動外,反而還是覺得有一定的道理(笑。這時候允許我自稱為一次“教主”,我們的理念是:
讀文檔,讀文檔,讀文檔。
寫博客,寫博客,寫博客。
Hello,我是韓亦樂,現(xiàn)任本科軟工男一枚。軟件工程專業(yè)的一路學(xué)習(xí)中,我有很多感悟,也享受持續(xù)分享的過程。如果想了解更多或能及時收到我的最新文章,歡迎訂閱我的個人微信號:韓亦樂。我的簡書個人主頁中,有我的訂閱號二維碼和 Github 主頁地址;我的知乎主頁 中也會堅持產(chǎn)出,歡迎關(guān)注。
本文內(nèi)部編號經(jīng)由我的 Github 相關(guān)倉庫統(tǒng)一管理;本文可能發(fā)布在多個平臺但僅在上述倉庫中長期維護;本文同時采用【知識共享署名-非商業(yè)性使用-禁止演繹 4.0 國際許可協(xié)議】進行許可。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/89479.html
摘要:接下來我看看一下函數(shù)我們先按照分支走為讀取是里的對象,饒了這大的一個圈子,那么接下來一起來看一看對你的輸入配置做了怎么樣的處理吧 打開webpeck-cli下的convert-argv.js文件 // 定義options為空數(shù)組 const options = []; // webpack -d 檢查 -d指令 if (argv.d) { //... } ...
為什么讀webpack源碼 因為前端框架離不開webpack,天天都在用的東西啊,怎能不研究 讀源碼能學(xué)到很多做項目看書學(xué)不到的東西,比如說架構(gòu),構(gòu)造函數(shù),es6很邊緣的用法,甚至給函數(shù)命名也會潛移默化的影響等 想寫源碼,不看源碼怎么行,雖然現(xiàn)在還不知道寫什么,就算不寫什么,看看別人寫的總可以吧 知道世界的廣闊,那么多插件,那么多軟件開發(fā)師,他們在做什么,同樣是寫js的,怎么他們能這么偉大 好奇...
摘要:時間選擇的表盤其實有兩個,一個是小時的選擇,另一個則是分鐘的選擇。也就是說,第一步選擇小時,第二部選擇分鐘它是一個小時制的時間選擇器。而則用于處理拖拽事件,標記著當前是否處于被拖拽狀態(tài)。 本文的源碼全部位于github項目倉庫react-times,如果有差異請以github為準。最終線上DEMO可見react-times github page 文章記錄了一次創(chuàng)建獨立React組件...
摘要:最近學(xué)習(xí),學(xué)習(xí)過程中使用官方推薦的庫,感覺官方庫不太好用,基礎(chǔ)的沒問題。介紹這個輪子其實是很早以前就造好的,主要參考的數(shù)據(jù)庫操作方式。將設(shè)置表名設(shè)置查詢字段聯(lián)表等操作進行鏈式操作,給人一種語義化操作數(shù)據(jù)庫的感覺。 最近學(xué)習(xí)eggjs,學(xué)習(xí)過程中使用官方推薦的MySQL庫,感覺官方庫不太好用,基礎(chǔ)的CURD沒問題。但是復(fù)雜點的操作就不行了,雖然官方還有一個egg-sequelize,但是...
閱讀 3525·2023-04-26 00:16
閱讀 1361·2021-11-25 09:43
閱讀 3824·2021-11-23 09:51
閱讀 2964·2021-09-24 09:55
閱讀 713·2021-09-22 15:45
閱讀 1387·2021-07-30 15:30
閱讀 3064·2019-08-30 14:04
閱讀 2237·2019-08-26 13:46