讓NodeJS在項目中發光發熱系列文章, 沒看過這個的朋友建議先看一下,否則直接這個可能會有點吃力。html
最近離職了,閒着也是閒着。就想起來了以前Node
相關的文章還有一部分沒寫,恰好有時間,今天給續上。現現在的前端開發,經過Node
能夠高度自定義的爲咱們的項目打造一條龍服務。既然一條龍那麼不只僅是開發階段,打包以後的事情,咱們也要處理。這篇文章就聊一聊打包以後的一些用得上的hooks
。一樣的這篇文章也是拋磚引玉的做用,文章裏涉及的都是一些簡單的部分,深層次的騷操做還要各位朋友本身去挖掘。前端
首先來看一下大體的效果vue
準備一個項目,Vue/React/Angular
的均可以,咱們這裏以vue-base-template爲項目模板node
由於我使用的是Vue-cli3.0
版本的腳手架,它打包的命令是經過vue-cli-service build
命令來執行。npm
直接運行的話咱們就無法加hooks
了, 全部咱們本身寫一個Node
腳原本執行打包命令, 在scripts
文件夾下新建build.js
,這個腳本用來組織咱們全部的打包相關東西。git
build.js
的職責其實很簡單:github
hooks
有了需求再去寫功能,對你們來講都是小case,下面就來實現這三個需求。vue-cli
這個參數就是咱們在命令行中輸入的參數,也就是package.json scripts
中寫好的參數,例如npm
"scripts": { "build": "node scripts/build.js --mode production" }
這裏咱們暫時不對參數作特殊的處理,僅僅取出來給打包命令用,若是你須要複雜的參數建議你使用commander.js,我這個項目如今還沒涉及到複雜的打包參數。因此直接使用process.argv
取出來就好了,關於process
具體使用感興趣能夠本身看看文檔json
const scriptArgv = process.argv.slice(2) // 刪除node scripts/build.js const args = scriptArgv.join(' ') // 組裝成正常格式
這個對Node
來講,這個很簡單。經過child_process
就能完美實現,我比較懶,直接用了tasksfile這個庫。你也能夠本身採用原生Node
寫,不過要注意異步的問題。segmentfault
const { sh } = require('tasksfile') // 同步執行打包命令 sh(`vue-cli-service build ${args}`, { silent: false })
Hooks
,這就是執行個方法。下面是build.js
中所有的代碼。
const ora = require('ora') const { sh } = require('tasksfile') const { Notify } = require('./util') const builtHooks = require('./build-hooks') const scriptArgv = process.argv.slice(2) const args = scriptArgv.join(' ') const spinner = ora(`building for ${process.env.NODE_ENV}...\n`) spinner.start() // real pack command sh(`vue-cli-service build ${args}`, { silent: false }) // build success spinner.succeed("打包完成") // notify Notify.showNotify("打包完成", "即將進行下一步操做") // delay 2s setTimeout(() =>{ // run hooks builtHooks() },2000)
這個發揮的空間有點大,大部分都是爲了提高咱們的效率的,我就寫幾個我本身認爲經常使用一點的。
在執行完build.js
的前兩步以後,接着就會運行hooks
。由於hooks
腳本存在的目的是爲開發者最大可能性的節約時間。因此就設計爲了非強制性的選項。
首先新建文件build-hooks.js
第一步設計hooks
選項,一樣的使用咱們的老朋友inquirer
const builtHooks = () => { inquirer.prompt([ { type: 'list', message: `檢測到production環境打包完成,請選擇下一步操做`, name: 'next', choices: [ { name: '退出腳本', value: 0 }, { name: '發佈到服務器', value: 1 }, { name: '本地預覽', value: 2 }, { name: '生成Zip文件', value: 3 }, { name: '備份Zip文件到本地', value: 4 } ] } ]).then(answers => { afterHooks.get(answers.next)() }) }
第二步設計不一樣選項對應的行爲
const afterHooks = new Map([ [0, () => { Log.logger('退出程序') process.exit(0) }], [1, () => { Log.logger('即將進行發佈🎈') require('./deploy') }], [2, () => { Log.logger('開始本地預覽💻') require('./server') }], [3, async () => { Log.logger('開始壓縮zip文件👜') await FileUtil.zipDir() }], [4, async () => { Log.logger('開始備份Zip文件到本地📦') await Backup.doBackup() }] ])
是否是很是簡單,簡潔易懂哈哈哈。 下面咱們來看一下具體的操做實現細節
這個在這裏就不說了, 能夠直接看這裏。
簡單問題了,無非是在本地搭建一個輕量級的Web
服務器, 用第三方庫實現的話就更簡單了。代碼以下server.js
const http = require('http') const fs = require('fs') const path = require('path') const { Log } = require('./util') const httpPort = 8088 const filePath = path.resolve(__dirname, '../dist/index.html') const open = require('open') // create current http server http.createServer((req, res) => { Log.logger(req.url) try { const content = fs.readFileSync(filePath, 'utf-8') // deal resource if (req.url.indexOf('static') !== -1 || req.url.indexOf('vendor') !== -1 || req.url == "/favicon.ico") { const data = fs.readFileSync(path.resolve(__dirname, `../dist${req.url}`)) return res.end(data) } // index.html res.setHeader('Content-Type','text/html;charset=utf-8') res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }) res.end(content) } catch (error) { Log.error('We cannot open "index.htm" file.') } }).listen(httpPort, async () => { const location = `http://localhost:${httpPort}` Log.success(`Server listening on: ${location}, Open in the browser after 3 seconds`) // open default browser setTimeout(async ()=> { // 自動打開默認瀏覽器 await open(location) }, 3000) })
效果以下:
這個也沒什麼好說的😂, 就是經過Node
壓縮dist
文件夾,在這裏我用了[zip-local]()來實現需求(你也可使用Node
實現, 可是我比較懶哈哈哈)代碼以下
/** * 壓縮文件夾 * @param {*} dir 要壓縮的文件夾 默認 ROOTPATH.distDir * @param {*} zipedPath 壓縮以後 的zip存放路徑 */ static async zipDir (dir = ROOTPATH.distDir, zipedPath = ROOTPATH.distZipPath) { try { if(fs.existsSync(zipedPath)) { Log.logger('zip已經存在, 即將刪除壓縮包') fs.unlinkSync(zipedPath) } else { Log.logger('即將開始壓縮zip文件') } await zipper.sync.zip(dir).compress().save(zipedPath); Log.success('文件夾壓縮成功') } catch (error) { Log.error(error) Log.error('壓縮dist文件夾失敗') } }
壓縮+備份(就是複製一份文件到指定文件夾)
我在深思熟慮以後仍是決定把備份給加上,我反正是吃過沒備份的虧😂, 這個選項會爲咱們在backups
文件目錄下生成一個新的以當前日期命名的壓縮文件。
熟悉Node
的文件系統的話這個對你們來講就很簡單了。backup.js
代碼以下:
const path = require('path') const { FileUtil, StringUtil, DateUtil, ROOTPATH, Log, Notify } = require('./util') class Backup { /** * @author: etongfu * @description: 清空全部備份 */ static clearBackups () { } /** * @author: etongfu * @description: 執行備份 * @param {type} {*} * @returns: {*} */ static async doBackup () { try { // 文件名是當前日期 精確到秒 let date = StringUtil.trim(DateUtil.getCurrentDate("YYYY-MM-DD hh:mm:ss"), 1) date = StringUtil.replaceAll(date,"-", "") date = StringUtil.replaceAll(date,":", "") let targetPath = path.resolve(__dirname, `../backups/${date}.backup.zip`) if(FileUtil.fileExist(targetPath)) { return Log.warning(`${targetPath}已存在,已放棄備份`) } // Zip File await FileUtil.zipDir(ROOTPATH.distDir, targetPath) Log.success(`本地備份完成, 文件:${targetPath}`) Notify.showNotify("本地備份", `本次備份完成, 文件地址:${targetPath}`) } catch (error) { Log.error(`備份文件失敗:${error}`) } } } module.exports = Backup
效果以下:
其實hooks徹底沒必要和打包耦合在一塊兒,徹底能夠拆出來使用,不過這個是下一個版本的需求哈哈哈😀。寫完這些腳本再親身使用了一段時間以後,真的感受能夠節省下來很多時間。免去了一些繁瑣的手工操做。推薦你們嘗試一下,畢竟自定義程度賊高,爲團隊造福。
原文地址 若是以爲有用得話給個⭐吧