看着公司的另外一個同事寫了一個靜態資源服務器進行打包后的預覽, 但是個人覺得他定義的指令實在是太長, 每次都得打一大串, 個人實在受不了. 於是做了個夢, 再夢里自己就寫了這么一個東西, 第二天正好公司空閑沒啥事干就開始琢磨着去寫.
需要准備的: node, npm賬號, github倉庫, 足夠的耐性
開干 :
1.聲明npm package.json
npm init
2.隨后鍵入package基本信息
name: vsv // 模塊名稱 version: (1.0.0) 1.0.1 // 基礎版本號 description: 描述 // 模塊描述 entry point: (index.js) index.js // 默認入口 test command: none // 測試指令 git repository: https://github.com/vok123.... // 個人git倉庫地址 keywords: server // 關鍵字 author: vok // 作者 license: (ISC)
最后輸入 y 完成package.json編輯, 隨后會生成一個package.json文件
3.在目錄下新建lib文件夾和readme.md並往里面塞點說明
4.在lib下面新建lib.js
module.exports = { // 暴露模塊 fun: function () { console.log('我的第一個測試包'); } };
5.在跟目錄新建index.js
var lib = require( './lib/lib.js' ); module.exports = lib; //暴露模塊
6.注冊npm賬號:
方法1: npm 注冊賬號
方法2: npm adduser 依次輸入用戶名, 密碼, 郵箱就注冊成功了, 注冊成功會自動登錄
注意: (注意當前鏡像源, 如果不是原版鏡像源的需要求換原鏡像源 , 可以使用 nrm 進行管理鏡像源) 1.安裝npm install -g nrm 2. 查看鏡像源 nrm ls 3. 切換 nrm use npm
如果有npm賬號
npm login 輸入用戶名, 密碼, 郵箱
7. 發布
npm publish
8. 使用包
npm installl --save vsv
新建test.js
var funs = require('vsv'); funs.fun(); // 調用我們定義模塊的方法
當然這僅僅是應用的模塊, 貌似並不是我想要的結果 , 我想要的vsv -p 8989就啟動一個靜態資源服務進行瀏覽呢! 這時候會想到是否把這個包全局安裝 npm install -g vsv 就可以使用呢? 顯然不行 , 光是解決vsv不是一個指令就是一個大問題, 還有定義的端口號也是一個問題. 接下來我們一步步來解決問題.
首先我要借助 commander 命令行來解決指令問題
npm install --save commander //這里需要使用--save 進行安裝 不然下面require('commander')會找不到模塊
把根目錄新建src目錄新建一個沒有后綴的 vsv 文件並鍵入代碼
const program = require('commander'); program .version(require('../package').version) // 設置版本 .option('-p, --port [port]', 'Input port !') // 自定義形參[port] .parse(process.argv); // 參數數組 console.log(program.port); // 使用自定義的port參數
然后我們可以發布npm試試了 執行指令
npm publish
發布完成后
npm install -g vsv
安裝完成后執行 vsv -p 8999 卻發現報錯了 (找不到指令)
'vsv' 不是內部或外部命令,也不是可運行的程序或批處理文件。
這時候需要我們在package.json里面配置 bin 節點 (跟 "scripts"同級)
"bin": {
"vsv": './src/vsv'
}
保存后修改package.json下version節點的版本號為1.0.1, 每次發布的時候要求版本號都要比上一個大
然后 npm publish 進行npm包發布
發布完成后npm install -g vsv 再次安裝
執行指令 vsv -p 8999
發現還是報了錯 (指令錯誤)
'"C:\Users\Administrator\AppData\Roaming\npm\\node_modules\vsv\src\vsv"' 不是內部或外部命令,也不是可運行的程序或批處理文件。
到這步的時候離成功已經不遠了, 證明我們輸入vsv 指令的時候能夠找到對應的需要執行的文件了. 只差執行所編寫的代碼了, 代碼是js 所以應該指定這個指令由node去執行.
這時候我們需要告訴這個文件需要用node執行 , 在vsv文件的頭部加上 #!/usr/bin/env node
#!/usr/bin/env node const program = require('commander'); program .version(require('../package').version) // 設置版本 .option('-p, --port [port]', 'Input port !') // 自定義形參[port] .parse(process.argv); // 參數數組 console.log(program.port); // 使用自定義的port參數
再次更改package.json的版本號進行發布, 安裝
在次執行vsv -p 8999
發現這時候終於輸出了 8999
接下來就是配置打開瀏覽器和啟動靜態資源服務器了
打開瀏覽器使用 open 這個npm包 npm install --save open 以及控制台顏色colors控制 npm install --save colors
// 使用自定義的port參數以及默認端口處理 #!/usr/bin/env node const program = require('commander'), main = require('../lib/main.js'), open = require('open'), colors = require('colors'); program .version(require('../package').version) .usage('[entry]') .option('-p, --port [port]', 'Input port !') .parse(process.argv); var ports = (program.port > 0 && program.port != true) ? program.port : 8080; // 監測端口是否被占用 main.checkPort(ports).then(res => { main.createServer(ports); open(`http://localhost:${ports}`); }).catch((err) => { console.log(err); });
新增/lib/main.js處理端口檢查以及靜態資源處理服務
const net = require('net'), colors = require('colors'), fs = require('fs'), url = require('url'), mime = require('./mime'), config = require('./config'), http = require('http'), path = require('path'); module.exports = { checkPort (port) { // 監測端口 return new Promise((resolve, reject) => { let server = net.createServer().listen(port); server.on('listening', () => { // 執行這塊代碼說明端口未被占用 server.close(); resolve(true, port); }); server.on('error', (err) => { if (err.code === 'EADDRINUSE') { // 端口已經被使用 console.log(`${port}端口已經被占用 !`.underline.red); reject(); } }); }); }, createServer (port) { // 創建靜態資源服務 http.createServer((req, res) => { let pathName = url.parse(req.url).pathname; // 獲取文件名'/xxx' // 對中文進行解碼,防止亂碼 pathName = decodeURI(pathName); // 重定向: 考慮定義標准的url,以'/'結尾. if (path.extname(pathName) === '') { // 沒有擴展名 if (pathName.charAt(pathName.length - 1) !== '/') { pathName += '/'; let redirect = encodeURI('http://' + req.headers.host + pathName); // 記得encodeURI,不然中文目錄報錯 res.writeHead(301, { location: redirect }); } } // 獲取資源的絕對路徑 let realFilePath = path.resolve(process.cwd() + pathName); // 獲取對應文件的文檔類型 let ext = path.extname(pathName); // 獲取后綴名,如'.html' ext = ext ? ext.slice(1) : 'notKnow'; // 取掉.符號 if (ext.match(config.Expires.fileMatch)) { let expires = new Date(); expires.setTime(expires.getTime() + config.Expires.maxAge * 1000); // 設置響應頭 res.setHeader('Expires', expires.toUTCString()); res.setHeader('Cache-Control', 'max-age=' + config.Expires.maxAge); } // 定義未知文檔的類型MIME let contentType = mime[ext] || 'text/plain'; // 后綴名存在就進行映射,不存在就是'text/plain' // 判斷文件是否存在 fs.stat(realFilePath, (err, stats) => { // err if (err) { // 也可以定制自己的404頁面 res.writeHead(404, { 'content-type': 'text/html' }); res.end('<h3>404 Not Found</h3>'); } if (!stats) return; // 存在 if (stats.isFile()) { res.writeHead(200, { 'content-type': contentType }); // 開始讀取文件 let stream = fs.createReadStream(realFilePath, { encoding: 'utf8' }); // 讀取時候錯誤處理 stream.on('error', () => { res.writeHead(500, { 'content-type': contentType }); }); // 返回文件內容 stream.pipe(res); } // 路徑是目錄的情況,列出當前目錄下文件 if (stats.isDirectory()) { let html = '<head><meta charset="utf-8"></head>'; // 讀寫該目錄下的內容 files是文件數組 fs.readdir(realFilePath, (err, files) => { if (err) { console.log('目錄文件讀取失敗'.red); } else { let tpath = ''; for (let i = 0; i < files.length; i++) { tpath = realFilePath.replace(process.cwd(), ''); // html += '<div><a href=''+ tpath + '/' + files[i] + ''>' + files[i] + '</a></div>'; html += `<div><a href='${tpath}/${files[i]}'>${files[i]}</a></div>`; } } res.writeHead(200, { 'content-type': 'text/html' }); res.end(html); }); } }); }).listen(port, () => { console.log(`開啟服務成功 >>>>> 端口: ${port}`.blue); }); } }
如果文章對你有幫助的話請留下你的足跡 , 拒絕做伸手黨 ^_^ ~
代碼 github