代碼自動化打包系統【原創】

1、 需求背景

XX項目須要開發一套前端組件打包系統,來處理用戶的請求:php

1.       用戶經過平臺申請應用;css

2.       選擇所須要的組件;html

3.       把組件的相關前端文件如js、css、html進行抽取、合併、壓縮、打包;前端

4.       把壓縮包連接地址返回給用戶,用戶下載壓縮包。node

針對上面的需求,咱們選擇一個比較流行的前端代碼打包工具grunt。linux

2、 grunt簡介

什麼是grunt?shell

官網給它的解釋是The JavaScript Task Runner。Grunt是基於Node.js的項目構建工具,能夠對項目文件壓縮、編譯、單元測試等任務經過Gruntfile配置用grunt命令自動執行,節省大部分無聊的工做和時間。數據庫

爲何選grunt?npm

由於grunt有豐富的插件,能知足打包所須要的合併、壓縮、打zip包等功能。json

3、 系統運行環境

打包系統的運行須要下面的環境提供支持,在開發前須要對其一一安裝。

Node:Javascript運行環境(runtime)。實際上它是對Google V8引擎進行了封裝

npm:全稱Node Package Manager,是一個NodeJS包管理和分發工具

pm2:帶有負載均衡功能的Node應用的進程管理器

Dnode:實現php與node之間的通訊,提供雙向遠程方法調用類庫

Grunt及其插件grunt-contrib-clean,grunt-contrib-concat,grunt-contrib-copy,grunt-contrib-cssmin,grunt-contrib-less,grunt-contrib-uglify,grunt-contrib-watch,grunt-zip,load-grunt-tasks,gurnt依靠這些插件完成了代碼的合併、壓縮、打zip包等功能

4、 打包系統實現

先看一下流程圖

上圖的流程是通過無數次修改後的方案,期間遇到不少問題,主要集中在php和grunt之間的調用和參數傳遞上。須要解決的問題:

1.       怎樣從源代碼文件中根據所選擇的組件來抽取對應的文件;

2.       怎樣用php程序調用grunt命令

3.       dnode能夠做爲php調用grunt命令的橋樑,php怎樣同步調用grunt命令

1.       Gruntfile文件使用

用戶首先要選擇組件進行下載壓縮包,經過grunt命令給Gruntfile傳遞須要打包的組件列表,Gruntfile包含grunt的所有處理邏輯。

加載grunt插件

加載所須要的插件,寫在package.json文件裏:

"devDependencies": { "grunt": "~0.4.0", "grunt-contrib-clean": "^0.6.0", "grunt-contrib-concat": "^0.1.3", "grunt-contrib-copy": "^0.4.0", "grunt-contrib-cssmin": "^0.6.1", "grunt-contrib-uglify": "^0.9.1", "grunt-contrib-watch": "^0.3.1", "grunt-contrib-less": "^1.0.0", "load-grunt-tasks": "^3.2.0", "time-grunt": "^1.2.1", "grunt-zip": "^0.16.2" }

源文件配置文件:

下面是loading組件的配置文件,html和一些通用js,css (less) 直接寫在GruntFile文件內

{
  "app": "loading", "less": ["src/app/loading/loading.less"], "js": ["src/vendor/common/pxloader.js", "src/app/loading/loading.js"] }

Gruntfile參數接收:

var appArr = grunt.option('app').split(','); (app爲組件參數字符串,是組件名稱組合,名稱之間用逗號分隔)例如:’register,login,slider’。

Gruntfile根據獲取的組件列表讀取組件配置文件。

for (var i in appArr) { var confName = 'grunt_conf/' + appArr[i] + '.json'; confArr[i] = grunt.file.readJSON(confName); };

Gruntfile根據參數app獲取組件名稱,而後又根據組件名稱獲取組件配置文件,經過配置文件,能夠獲取源文件的文件列表,而後對這些組件的源文件進行組合、合併、壓縮,最後生成壓縮包

2.       php調用grunt

因爲生產環境php.ini的disable_functions把exec、shell_exec、system這樣能夠執行linux命令的函數禁用,可是Node能夠調用,DNode能夠實現php和Node之間的通訊。這樣咱們就能夠實現php程序調用grunt命令。

Node打包Server

var PORT = 7083; var dnode = require('dnode'); var cp = require('child_process'); var server = dnode({ pack: function (params, callback) { var ls = cp.exec("grunt  --app=" + params.coms, [], function(error, stdout, stderr){ if(error != null || stdout.indexOf('without errors') < 0){ callback('error'); }else{ callback('success'); } }); } }); server.listen(PORT);

 

Php同步調用

爲何同步調用而不是異步?由於打包後要對壓縮包上傳,上傳前必須保證壓縮包存在,因此咱們使用了回調函數,而且能夠根據回調函數的返回值判斷打包是否正常,同時也保證下一步上傳的正常進行。

/** * 源文件打包 * @param string $components * @param string $path * @param string $fileName * @throws \H5EException */ private static function sourcePack($components){ $loop = new \React\EventLoop\StreamSelectLoop();  $dnode = new \DNode\DNode($loop);  $port = 7083;  self::$params = array('coms' => $components);  $dnode->connect($port, function($remote, $connection) { $remote->pack(PackService::$params, function ($ret) use ($connection){ if ("success" != $ret) { throw new \H5EException("pack service error", Constants::SYSTEM_CODE); } $connection->end(); }); });  $loop->run(); }

 

3.       維持Node打包server的持續運行

node啓動打包server進程,一段時間後會莫名其妙的掛掉,pm2做爲node的守護進程很好的解決了這個問題。

4.       壓縮包上傳

壓縮包是根據版原本規劃的,當版本改變後,用戶下載壓縮包就須要從新打包,可是同一版本的源文件不必從新打壓縮包,因此咱們把壓縮包上傳資源服務器上,而後把資源連接保存在數據庫中,下一次只需從數據庫中查詢到該連接便可,而無須重複打包,這樣既提升了用戶下載的速度,又節省了服務器資源。

5、 後期改進

增長Node日誌,目前缺乏node日誌,若是出現異常,很難定位的問題;

Node打包server接收參數時進行嚴格校驗;

6、 安全問題思考

A. 控制訪問頻率,和普通數據接口相比,打包接口耗時較長,消耗服務器資源較多,若是出現接口被惡意頻繁請求,可能會影響服務器性能,同時形成正常的打包失敗,有必要對訪問頻率作限制;

B. 在php層和node層都要進行嚴格校驗參數,能夠有效的防止因參數問題而帶來的意外;

C. Node 代碼打包server運行的端口不能對外,阻止用戶經過外網直接訪問該端口。

相關文章
相關標籤/搜索