亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

造輪子之 npm i -g creatshare-app-init 源碼淺析

adie / 1854人閱讀

摘要:剛剛在里說(shuō)明的回調(diào)函數(shù)綁定在命令下。使用開源協(xié)議源代碼都放在目錄下目錄要對(duì)不同的代碼進(jìn)行合理的分層。,我是韓亦樂(lè),現(xiàn)任本科軟工男一枚。

以我的小經(jīng)驗(yàn)來(lái)看,軟件萌新寫出來(lái)的代碼大多“無(wú)法直視”。具體現(xiàn)象包括空格和換行符亂用、文件夾和變量的命名多使用拼音等。坐不住的我,便想到了通過(guò) ESLint 配置文件來(lái)規(guī)范實(shí)驗(yàn)室的 JavaScript 代碼規(guī)范的 Idea。

于是巧遇前實(shí)驗(yàn)室畢業(yè)學(xué)長(zhǎng)曾經(jīng)發(fā)布的 npm 包——creatshare-project-quick-init。安裝好這個(gè)包,我們便可以在空文件夾下生成一個(gè)項(xiàng)目的基礎(chǔ)骨架。

dist  //發(fā)布目錄,用于生產(chǎn)環(huán)境
src   //開發(fā)目錄,開發(fā)時(shí)所需資源
|----dist  //測(cè)試環(huán)境目錄
|     |----static
|             |----css  //編譯打包后的css資源
|             |----js   //打包壓縮后的js資源
|             |----imgs //測(cè)試環(huán)境圖片資源
|----less  //開發(fā)所需less代碼
|----js    //開發(fā)所需js代碼
|    |----lib //庫(kù)或框架資源
|----imgs  //開發(fā)所需圖片資源
index.html    //開發(fā)頁(yè)面
gulpfile.js
package.json
README.md

What a good idea~!

在學(xué)長(zhǎng)的這個(gè)包中,主要構(gòu)建了 gulp 配置,less 和測(cè)試文件的骨架。雖然再無(wú)更多內(nèi)容,但這份構(gòu)建基礎(chǔ)骨架的靈感還是被我愉快的收走了——學(xué)前端的人很多,但大多都太缺工程化意識(shí)了。于是,這個(gè)靈感成為了不錯(cuò)突破口。

creatshare-app-init 腳手架孕育而生。

0

通過(guò)這篇文章,你能了解到:

如何用 NodeJS 編寫命令行工具?

如何發(fā)布自己的 npm 包?

筆者與 creatshare-app-init 的故事?

在本文中,或多或少出現(xiàn)過(guò)以下關(guān)鍵字,我的解釋是:

輪子:該詞在前端開發(fā)日常用語(yǔ)中,表示一個(gè)基于原生代碼實(shí)現(xiàn),但并沒有對(duì)前端行業(yè)產(chǎn)生積極意義的模塊。雖然它的出現(xiàn)方便了一些人的使用,但更多的加大了我們的學(xué)習(xí)成本。

項(xiàng)目:該詞在前端領(lǐng)域常指一個(gè)服務(wù)于用戶的軟件立項(xiàng)。

模塊:creatshare-app-init 就是一個(gè)模塊,是開發(fā)前端項(xiàng)目中的一個(gè)子集。正如汽車的各個(gè)部件一樣,多個(gè)模塊合理組裝起來(lái)才是一輛汽車。

1

嘗試解析源碼,第一步,從模塊根目錄下的 package.json 來(lái)看。

"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 聲明了模塊上線時(shí)的依賴,devDependencies 聲明了模塊開發(fā)時(shí)的依賴。該模塊在上線時(shí),即 npm 包被用戶用到時(shí),只需要 commander 庫(kù)。commander 庫(kù)是 NodeJS 命令行接口開發(fā)的優(yōu)選解決方案,受啟發(fā)于 Ruby 的 commander。在解析 bin/index.js 源碼時(shí)將詳細(xì)拓展。

"name": "creatshare-app-init",
"version": "2.1.0",
"description": "CreatShare 實(shí)驗(yàn)室前端項(xiàng)目初始化工具",
"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 模塊的唯一標(biāo)識(shí)符。

version 字段:聲明模塊當(dāng)前版本號(hào)。這里每當(dāng)使用 npm publish 將模塊發(fā)布到 npm 倉(cāng)庫(kù)中時(shí),版本號(hào)都需要手動(dòng)自增。

description 字段:對(duì)模塊進(jìn)行描述,同時(shí)有助于被檢索。

bin 字段:npm 本身是通過(guò) bin 屬性配置一個(gè)或多個(gè)可解析到 PATH 路徑下的可執(zhí)行模塊。模塊若被全局安裝,則 npm 會(huì)為 bin 中配置的文件在 bin 目錄下創(chuàng)建一個(gè)軟連接;模塊若被局部安裝,軟連接會(huì)配置在項(xiàng)目?jī)?nèi)的 ./node_modules/.bin/目錄下。

script 字段:定義模塊的腳本配置。如,當(dāng)我們?cè)谀K目錄下使用 npm run compile 時(shí),將自動(dòng)執(zhí)行 babel src/ -d lib/ 命令,進(jìn)行 ECMAScript6 代碼的轉(zhuǎn)譯。

2

剛剛提到 package.json 配置文件下的 bin 字段聲明了 npm 在生成軟連接時(shí)的配置。這就便是用戶在安裝好這個(gè)目錄后,可以隨時(shí)使用 cs 命令的出處。

我們又提到了該模塊在非開發(fā)環(huán)境下只需用到 commander 模塊,這個(gè)模塊是 NodeJS 命令行接口開發(fā)的優(yōu)選解決方案。

基于這倆點(diǎn),我們就從 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)實(shí)驗(yàn)室前端 Web App 項(xiàng)目腳手架")
  .option("-e, --enjoy")

program.
  .command("create ")
  .description("創(chuàng)建一個(gè)新的 Web App 項(xiàng)目骨架")
  .action(function (rootDir) {
    cs.create(rootDir)
})

program.parse(process.argv)

就這么二十來(lái)行。因?yàn)槲覀円獙懙哪K是要運(yùn)行在命令行下的,就需要 #!/usr/bin/env node 語(yǔ)句來(lái)告訴系統(tǒng)使用 node 環(huán)境來(lái)運(yùn)行我們的文件,必不可少。

在引入 commander 并將其賦值給 program 變量后,我們對(duì)其使用了如下方法:

.allowUnknownOption() 方法:

.version() 方法:用于設(shè)置命令程序的版本號(hào)。

.description() 方法:用于設(shè)置命令的描述??梢越壎ㄔ诟钕拢@里是 cs 命令;或綁定在子命令下,如 cs create

命令。

.option() 方法:定義命令的具體選項(xiàng)。

.command() 方法:定義命令的子命令,這里是 cs create

命令。

.action() 方法:用于設(shè)置命令執(zhí)行的相關(guān)回調(diào)。這里綁定在 cs create

命令上,在使用該命令時(shí)觸發(fā)執(zhí)行回調(diào)函數(shù)。

代碼最后的 process 為進(jìn)程對(duì)象,是 NodeJS 運(yùn)行時(shí)存在的眾多全局變量之一。process 對(duì)象中的 argv 屬性用來(lái)捕獲命令行參數(shù)。

3

剛剛在 bin/index.js 里說(shuō)明的 .action 回調(diào)函數(shù)綁定在 cs create

命令下。當(dāng)我們使用該命令時(shí),會(huì)觸發(fā) cs.create() 語(yǔ)句的執(zhí)行,這就要提及我們引入的 lib/cs.js 文件了。

打住,第一節(jié)里展示的 package.json 中,script字段里有這么一條語(yǔ)句:"compile": "babel src/ -d lib/"。這是說(shuō)明 lib/ 文件夾下的代碼是通過(guò) src/ 文件夾下的代碼轉(zhuǎn)譯過(guò)來(lái)的,真正我們需要去關(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() + "/"

/**
* [運(yùn)行 create 命令]
* @return {[type]} [description]
*/
exports.create = (rootDir) => {
  console.log("
項(xiàng)目目錄開始創(chuàng)建
")
  create.init(distPath, dist, rootDir)
  helpGuide()
}

不難理解,create 變量指向 cs create

所要執(zhí)行的源代碼;path 是 NodeJS 自帶模塊,提供文件目錄解析功能。

最終 src/index.js 使用 exports.create 語(yǔ)句向外部暴露出 create 方法。bin/index.js 便可以將該方法通過(guò) .action() 綁定到 cs create

命令上了。

4

精彩的來(lái)了。都說(shuō) ECMAScript6 的指定振奮人心,JavaScript 的魅力越來(lái)越大,這里便是一次體驗(yàn) JavaScript 在 NodeJS 上的新玩法有趣之旅。

src/create.js 文件中,主要用到了 NodeJS 自帶的 fs 文件模塊,來(lái)生成新項(xiàng)目的基礎(chǔ)架構(gòu)。文件最后暴露出的 init 方法源碼如下。

exports.init = (path, dist, rootDir) => {
  createRootDir(rootDir)
  // 從新目錄開始新建項(xiàng)目
  dist = dist + rootDir
  copyDir(path, dist)
}

init 方法獲取了 path 參數(shù)、dist 參數(shù)和 rootDir 參數(shù)。在該方法中,我們先將 rootDir 參數(shù)傳入 createRootDir() 函數(shù)中創(chuàng)建項(xiàng)目根目錄。

在哪里創(chuàng)建項(xiàng)目根目錄呢?就在執(zhí)行 cs 命令時(shí)的當(dāng)前目錄下:

const createRootDir = (rootDir) => {
  fs.access(process.cwd(), function (err) {
    if (err) {
      // 目錄不存在時(shí)創(chuàng)建目錄
      fs.mkdirSync(rootDir)
    }
  })
}

有了項(xiàng)目根目錄,就要將模塊下 dist/ 文件夾里的所有文件遞歸拷貝到根目錄下。一個(gè)參數(shù)用來(lái)指向 dist/ 文件夾,另一個(gè)參數(shù)用來(lái)指向根目錄,便可以開始遞歸復(fù)制。

/**
 * [初始化靜態(tài)資源]
 * @param  {[type]} src  [初始化資源路徑]
 * @param  {[type]} dist [當(dāng)前終端所在目錄]
 * @return {[type]}      [description]
 */
const copyDir = (src, dist) => {
  fs.access(dist, function (err) {
    if (err) {
      // 目錄不存在時(shí)創(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 }
      // 過(guò)濾不生成的文件
      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()) {
            // 當(dāng)是目錄是,遞歸復(fù)制
            copyDir(_src, _dist)
          }
        })
      })
    })
  }
}

fs 文件模塊的具體內(nèi)容推薦閱讀阮一峰的開源電子書——《JavaScript 標(biāo)準(zhǔn)參考教程》中的“NodeJS”章節(jié),來(lái)深入淺出 fs 模塊的用法。

完美,這時(shí)我們就可以發(fā)布我們的腳手架包了。

5

如何發(fā)布一個(gè) npm 包到 npm 倉(cāng)庫(kù)中,供其他人使用?當(dāng)我們照著第一步,將 package.json 配置好后,其實(shí)模塊的準(zhǔn)備工作已經(jīng)做好了。

還沒有做的就是在域名為 npmjs.com 的官網(wǎng)上注冊(cè)一個(gè)賬號(hào)。這樣,當(dāng)我們直接在模塊根目錄使用 npm publish 命令的時(shí)候,輸入正確的 npmjs.com 賬號(hào)、密碼,就能成功發(fā)布你的開源包了!

縱然讀博文是一個(gè)有趣的體驗(yàn),但也可以親自動(dòng)手試一試哦。

6

也就是說(shuō),酷炫的生成新項(xiàng)目骨架的來(lái)源,只是簡(jiǎn)單的遞歸復(fù)制該模塊下的 dist/ 文件夾到新項(xiàng)目中。但我們需要關(guān)注的重點(diǎn)在于,dist/ 文件夾下,到底裝了什么?

“初級(jí) Web App 項(xiàng)目初始化工具”一說(shuō),也就名歸有主了。dist/ 模板,也就是新項(xiàng)目的骨架如下。

.
├── .babelrc             # ES6 代碼轉(zhuǎn)義規(guī)則配置
├── .eslint.js           # JavaScript 代碼規(guī)范
├── .gitignore           # Git 不跟蹤的特殊文件
├── LICENSE              # 開源協(xié)議
├── README.md            # 項(xiàng)目介紹
├── material             # README.md 引用的圖片庫(kù)
├── package.json         # 項(xiàng)目配置文件
├── src                  # 源碼開發(fā)目錄
│?? ├── favicon.ico      # 網(wǎng)頁(yè)標(biāo)題小圖標(biāo)
│?? ├── html             # HTML 頁(yè)面模板目錄
│?? ├── 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 包管理器的依賴說(shuō)明

新項(xiàng)目骨架中默認(rèn)推薦了:

使用 Webpack 來(lái)打包多頁(yè)面;

使用 ESLint 來(lái)規(guī)范自己項(xiàng)目的 JavaScript 代碼;

使用 Babel 來(lái)編譯使用 ECMAScript 新特性的 JavaScript 代碼。

使用 MIT 開源協(xié)議;

源代碼都放在 src/ 目錄下;

src/ 目錄要對(duì)不同的代碼進(jìn)行合理的分層。

End

現(xiàn)在的不足,是未來(lái)的暢想。

這個(gè)模塊并不完美,一個(gè)健壯的命令還應(yīng)該能支持足夠多的參數(shù),運(yùn)行足夠有意義的子命令。比如我們常用 man 命令來(lái)看另一個(gè)命令的使用手冊(cè),那要讓用戶能用到 man cs 命令,還需要我們?cè)诖a中加入 man 字段等等。。

我又為什么,這么熱衷于分享這個(gè)輪子?

記得有一個(gè)前端群里曾有人問(wèn)過(guò):

“怎么沒有 VueJS 的源碼解析?”

時(shí),我說(shuō)過(guò):

“大牛很忙,關(guān)注的是前端前沿,不寫這些源碼解析博文是個(gè)好事。

“當(dāng)我們想有一個(gè)源碼解析教程的時(shí)候,這是一個(gè)打開新世界的契機(jī)——未嘗不使我們親自來(lái)寫,通過(guò)分享走向?qū)W習(xí)效率金字塔的最高層?”

這樣的能力并不是人人都能具備,也不必要讓人人都具備。我曾在大一傲氣的說(shuō)過(guò)“做最好的自己,影響該影響的人”,現(xiàn)在想起來(lái)除了有立刻找地洞鉆進(jìn)去的沖動(dòng)外,反而還是覺得有一定的道理(笑。這時(shí)候允許我自稱為一次“教主”,我們的理念是:

讀文檔,讀文檔,讀文檔。
寫博客,寫博客,寫博客。


Hello,我是韓亦樂(lè),現(xiàn)任本科軟工男一枚。軟件工程專業(yè)的一路學(xué)習(xí)中,我有很多感悟,也享受持續(xù)分享的過(guò)程。如果想了解更多或能及時(shí)收到我的最新文章,歡迎訂閱我的個(gè)人微信號(hào):韓亦樂(lè)。我的簡(jiǎn)書個(gè)人主頁(yè)中,有我的訂閱號(hào)二維碼和 Github 主頁(yè)地址;我的知乎主頁(yè) 中也會(huì)堅(jiān)持產(chǎn)出,歡迎關(guān)注。

本文內(nèi)部編號(hào)經(jīng)由我的 Github 相關(guān)倉(cāng)庫(kù)統(tǒng)一管理;本文可能發(fā)布在多個(gè)平臺(tái)但僅在上述倉(cāng)庫(kù)中長(zhǎng)期維護(hù);本文同時(shí)采用【知識(shí)共享署名-非商業(yè)性使用-禁止演繹 4.0 國(guó)際許可協(xié)議】進(jìn)行許可。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/89479.html

相關(guān)文章

  • 淺析webpack源碼convert-argv模塊(二)

    摘要:接下來(lái)我看看一下函數(shù)我們先按照分支走為讀取是里的對(duì)象,饒了這大的一個(gè)圈子,那么接下來(lái)一起來(lái)看一看對(duì)你的輸入配置做了怎么樣的處理吧 打開webpeck-cli下的convert-argv.js文件 // 定義options為空數(shù)組 const options = []; // webpack -d 檢查 -d指令 if (argv.d) { //... } ...

    lemon 評(píng)論0 收藏0
  • 淺析webpack源碼前言(一)

    為什么讀webpack源碼 因?yàn)榍岸丝蚣茈x不開webpack,天天都在用的東西啊,怎能不研究 讀源碼能學(xué)到很多做項(xiàng)目看書學(xué)不到的東西,比如說(shuō)架構(gòu),構(gòu)造函數(shù),es6很邊緣的用法,甚至給函數(shù)命名也會(huì)潛移默化的影響等 想寫源碼,不看源碼怎么行,雖然現(xiàn)在還不知道寫什么,就算不寫什么,看看別人寫的總可以吧 知道世界的廣闊,那么多插件,那么多軟件開發(fā)師,他們?cè)谧鍪裁?,同樣是寫js的,怎么他們能這么偉大 好奇...

    suosuopuo 評(píng)論0 收藏0
  • 一言不合輪子--擼一個(gè)ReactTimePicker

    摘要:時(shí)間選擇的表盤其實(shí)有兩個(gè),一個(gè)是小時(shí)的選擇,另一個(gè)則是分鐘的選擇。也就是說(shuō),第一步選擇小時(shí),第二部選擇分鐘它是一個(gè)小時(shí)制的時(shí)間選擇器。而則用于處理拖拽事件,標(biāo)記著當(dāng)前是否處于被拖拽狀態(tài)。 本文的源碼全部位于github項(xiàng)目倉(cāng)庫(kù)react-times,如果有差異請(qǐng)以github為準(zhǔn)。最終線上DEMO可見react-times github page 文章記錄了一次創(chuàng)建獨(dú)立React組件...

    lifesimple 評(píng)論0 收藏0
  • 輪子 - EGGJS的MySQL操作庫(kù)

    摘要:最近學(xué)習(xí),學(xué)習(xí)過(guò)程中使用官方推薦的庫(kù),感覺官方庫(kù)不太好用,基礎(chǔ)的沒問(wèn)題。介紹這個(gè)輪子其實(shí)是很早以前就造好的,主要參考的數(shù)據(jù)庫(kù)操作方式。將設(shè)置表名設(shè)置查詢字段聯(lián)表等操作進(jìn)行鏈?zhǔn)讲僮?,給人一種語(yǔ)義化操作數(shù)據(jù)庫(kù)的感覺。 最近學(xué)習(xí)eggjs,學(xué)習(xí)過(guò)程中使用官方推薦的MySQL庫(kù),感覺官方庫(kù)不太好用,基礎(chǔ)的CURD沒問(wèn)題。但是復(fù)雜點(diǎn)的操作就不行了,雖然官方還有一個(gè)egg-sequelize,但是...

    Alex 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<