一. 先了解一下package.json
每個專案的根目錄都有一個package.json
檔案,定義了這個專案所需要的各種模組,以及專案的配置資訊,下面是一個比較完整的package.json檔案
{
"name": "vue-cli",
"version": "2.9.3",
"description": "A simple CLI for scaffolding Vue.js projects.",
"preferGlobal": true,
"bin": {
"vue": "bin/vue",
"vue-init": "bin/vue-init",
"vue-list": "bin/vue-list"
},
"repository": {
"type": "",
"url": ""
},
"keywords": [
],
"author": "litongqian",
"license": "MIT",
"bugs": {
"url": ""
},
"homepage": "",
"scripts": {
"test": "npm run lint && npm run e2e",
"start": "node index.js"
},
"dependencies": {
"async": "^2.4.0",
"chalk": "^2.1.0",
},
"devDependencies": {
"chai": "^4.1.2",
"eslint": "^3.19.0",
},
"engines": {
"node": ">=6.0.0"
}
}
複製程式碼
1. 其中scripts欄位指定了執行指令碼命令的npm命令列縮寫,比如start指定了執行npm run start
時,所要執行的命令。
2. bin欄位
bin項用來指定各個內部命令對應的可執行檔案的位置
"bin": {
"vue": "bin/vue",
"vue-init": "bin/vue-init",
"vue-list": "bin/vue-list"
},複製程式碼
上面程式碼指定,vue 命令對應的可執行檔案為 bin 子目錄下的vue。
3. npm link
專案目錄:
自己開發模組的時候,比如上面的mymodule模組,如果想在hello中使用,我們可以採用相對路徑require('./mymodule')
載入模組。
也可以使用自定義模組標識require('mymodule'),
Node載入自定義模組需要將其安裝到全域性的或專案的node_modules
目錄之中。對於上述模組,解決方法就是在全域性的node_modules
目錄之中,生成一個符號連結,指向模組的本地目錄。
npm link
就能起到這個作用,會自動建立這個符號連結。
- 請設想這樣一個場景,你開發了一個模組
mymodule
,目錄為hello/mymodule
,你自己的專案要用到這個模組,首先,在模組目錄(src/mymodule
)下執行npm link
命令。
hello/mymodule$ npm link
複製程式碼
上面的命令會在NPM的全域性模組目錄內,生成一個符號連結檔案,該檔案的名字就是package.json
檔案中指定的模組名。
/usr/local/lib/node_modules/mymodule -> /Users/tongqianli/Desktop/work/hello/mymodule複製程式碼
這個時候,已經可以使用mymodule
模組了。
這時可以通過新增path解決:
作業系統中都會有一個PATH
環境變數,系統呼叫一個命令的時候,就會在PATH變數中註冊的路徑中尋找,如果註冊的路徑中有就呼叫,否則就提示命令沒找到。
-> export PATH=$PATH: # 將 /usr/bin 追加到 PATH 變數中
-> export NODE_PATH="/usr/lib/node_modules;/usr/local/lib/node_modules" #指定 NODE_PATH 變數
複製程式碼
那 NODE_PATH
就是NODE
中用來尋找模組所提供的路徑註冊環境變數
。我們可以使用上面的方法指定NODE_PATH環境變數。
更好的辦法:
切換到專案目錄,再次執行npm link
命令,並指定模組名。
hello tongqianli$ npm link mymodule複製程式碼
上面命令生成了本地node_modules/mymodule模組對映到全域性的node_modules/mymodule ,全域性,全域性的對映到本地hello/mymodule。
/Users/tongqianli/Desktop/work/hello/node_modules/mymodule
-> /usr/local/lib/node_modules/mymodule
-> /Users/tongqianli/Desktop/work/hello/mymodule複製程式碼
然後,就可以在你的專案中,載入該模組了。
var myModule = require('myModule');
複製程式碼
這樣一來,mymodule
的任何變化,都可以直接反映在專案之中。但是,這樣也出現了風險,任何在目錄中對mymodule
的修改,都會反映到模組的原始碼中。
如果你的專案不再需要該模組,可以在專案目錄內使用npm unlink
命令,刪除符號連結。
hello tongqianli$ npm unlink mymodule複製程式碼
二. 可執行指令碼
寫一個簡單的指令碼hello
$ mkdir
hello
#建立一個資料夾
$
cd
hello && touch
hello #建立命令檔案
#!/usr/bin/env node
console.log('hello world');
複製程式碼
檔案的頭部加入#!/usr/bin/env node這行程式碼,這裡表示使用node作為指令碼的解釋程式,可以省略node ./hello,直接執行./hello, node的路徑通過env來查詢,可以避免node安裝的路徑不一樣帶來找不到的問題。
開啟/usr/bin/env,可以檢視到PATH,作業系統通過路徑找到node
然後,修改 hello 的許可權。
$ chmod 755 hello
$./hello複製程式碼
如果想把 hello 前面的路徑去除,可以將 hello 的路徑加入環境變數 PATH。但是,另一種更好的做法,是在當前目錄下新建 package.json ,寫入下面的內容。
{
"name": "hello",
"bin": {
"hello": "./hello"
}
}複製程式碼
然後執行 npm link 命令。不明白的看上面
$ npm link複製程式碼
執行後會產生一個全域性的對映關係,可執行指令碼bin/hello對映到全域性node_modules/hello/hello,全域性的node_modules/hello對映到本地hello,這樣就可以使用hello了
三.命令列引數
命令列引數可以用系統變數 process.argv 獲取。
修改hello指令碼
#!/usr/bin/env node
console.log('hello ', process.argv);複製程式碼
其中process為node程式中的全域性變數,process.argv為一陣列,陣列記憶體儲著命令列的各個部分,argv[0]為node的安裝路徑,argv[1]為主模組檔案路勁,剩下為子命令或引數,如下:
$ hello a b c # process.argv的值為[ '/usr/local/bin/node', '/usr/local/bin/hello', 'a', 'b', 'c' ] |
指令碼可以通過 child_process 模組新建子程式,從而執行 Unix 系統命令,修改hello
exec
方法用於執行bash命令,exec
方法最多可以接受兩個引數,第一個引數是所要執行的shell命令,第二個引數是回撥函式,該函式接受三個引數,分別是發生的錯誤、標準輸出的顯示結果、標準錯誤的顯示結果。
#!/usr/bin/env node
var name = process.argv[2];
var exec = require('child_process').exec;
var child = exec('echo hello ' + name, function(err, stdout, stderr) {
if (err) throw err;
console.log(stdout);
});複製程式碼
執行$ hello litongqian
如果我們想檢視所有檔案,修改hello
var name = process.argv[2];
var exec = require('child_process').exec;
var child = exec(name, function(err, stdout, stderr) {
if (err) throw err;
console.log(stdout);
});複製程式碼
執行$ hello ls
hello目錄下有三個檔案
四、shelljs 模組
shell.js 模組重新包裝了 child_process,呼叫系統命令更加方便。它需要安裝後使用。
npm install --save shelljs 複製程式碼
然後,改寫指令碼。
#!/usr/bin/env node
var name = process.argv[2];
var shell = require("shelljs");
shell.exec("echo hello " + name);複製程式碼
五、yargs 模組
shelljs 只解決了如何呼叫 shell 命令,而 yargs 模組能夠解決如何處理命令列引數。它也需要安裝。
$ npm install --save yargs 複製程式碼
yargs 模組提供 argv 物件,用來讀取命令列引數。請看改寫後的 hello 。
#!/usr/bin/env node
var argv = require('yargs').argv;
console.log('hello ', argv.name);
複製程式碼
使用時,下面兩種用法都可以。
$ hello --name=tom hello tom $ hello --name tom hello tom 複製程式碼
也就是說,process.argv 的原始返回值如下。
$ node hello --name=tom
[ 'node',
'/usr/local/bin/hell',
'--name=tom' ]
複製程式碼
yargs 可以上面的結果改為一個物件,每個引數項就是一個鍵值對。
六.釋出命令包
通過npm publish進行釋出,前提是有npm帳號。如何操作可以檢視npm 官方文件。
本文是通過原生node.js來開發命令工具,而vue-cli是採用commander.js來簡化命令工具開發,
瞭解了執行流程,學習對應的模組,就可以了!,本文拋個磚頭
最後:有時我們用到的命令列不是全域性安裝的,而是本地安裝的
1. package.json bin欄位
bin項用來指定各個內部命令對應的可執行檔案的位置。
"name":"someTool",
"bin": {
"someTool": "./bin/someTool.js"
}
複製程式碼
上面程式碼指定,someTool 命令對應的可執行檔案為 bin 子目錄下的 someTool.js。
當一個專案依賴上面的someTool工具時,同時只是本地安裝
{
"name": "myproject",
"devDependencies": {
"someTool": "latest"
},
"scripts": {
start: 'someTool build' //等同於start: './node_modules/someTool/someTool.js build'
}
}複製程式碼
npm會尋找這個檔案,在node_modules/.bin/
目錄下建立符號連結。在上面的例子中,someTool.js會建立符號連結npm_modules/.bin/someTool
。由於node_modules/.bin/
目錄會在執行時加入系統的PATH變數,因此在執行npm時,就可以不帶路徑,直接通過命令來呼叫這些指令碼。
因此,像上面這樣的寫法可以採用簡寫。
scripts: {
start: './node_modules/someTool/someTool.js build'
}
// 簡寫為
scripts: {
start: 'someTool build'
}
複製程式碼
所有node_modules/.bin/
目錄下的命令,都可以用npm run [命令]
的格式執行。在命令列下,鍵入npm run
,然後按tab鍵,就會顯示所有可以使用的命令。
1. npm run
上面程式碼中,scripts
欄位指定了兩項命令
npm run-script start
或者npm run start
,就會執行 someTool build。npm run
是npm run-script
的縮寫,一般都使用前者,但是後者可以更好地反應這個命令的本質。npm run
命令會自動在環境變數$PATH
新增node_modules/.bin
目錄,所以scripts
欄位裡面呼叫命令時不用加上路徑,這就避免了全域性安裝NPM模組。
npm run
如果不加任何引數,直接執行,會列出package.json
裡面所有可以執行的指令碼命令。
npm內建了兩個命令簡寫,npm test
等同於執行npm run test
,npm start
等同於執行npm run start
。
npm run
會建立一個Shell,執行指定的命令,並臨時將node_modules/.bin
加入PATH變數,這意味著本地模組可以直接執行。
舉例來說,你執行ESLint的安裝命令。
$ npm i eslint --save-dev
複製程式碼
執行上面的命令以後,會產生兩個結果。首先,ESLint被安裝到當前目錄的node_modules
子目錄;其次,node_modules/.bin
目錄會生成一個符號連結node_modules/.bin/eslint
,指向ESLint模組的可執行指令碼。
然後,你就可以在package.json
的script
屬性裡面,不帶路徑的引用eslint
這個指令碼。
{
"name": "Test Project",
"devDependencies": {
"eslint": "^1.10.3"
},
"scripts": {
"lint": "eslint ."
}
}
複製程式碼
等到執行npm run lint
的時候,它會自動執行./node_modules/.bin/eslint .
。
如果直接執行npm run
不給出任何引數,就會列出scripts
屬性下所有命令。
$ npm run
Available scripts in the user-service package:
lint
jshint **.js
test
mocha test/複製程式碼