代码自动化打包系统【原创】

一、 需求背景

XX项目需要开发一套前端组件打包系统,来处理用户的请求:

1.       用户通过平台申请应用;

2.       选择所需要的组件;

3.       把组件的相关前端文件如js、css、html进行抽取、合并、压缩、打包;

4.       把压缩包链接地址返回给用户,用户下载压缩包。

针对上面的需求,我们选择一个比较流行的前端代码打包工具grunt。

二、 grunt简介

什么是grunt?

官网给它的解释是The JavaScript Task Runner。Grunt是基于Node.js的项目构建工具,可以对项目文件压缩、编译、单元测试等任务通过Gruntfile配置用grunt命令自动执行,节省大部分无聊的工作和时间。

为什么选grunt?

因为grunt有丰富的插件,能满足打包所需要的合并、压缩、打zip包等功能。

三、 系统运行环境

打包系统的运行需要下面的环境提供支持,在开发前需要对其一一安装。

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包等功能

四、 打包系统实现

先看一下流程图

上图的流程是经过无数次修改后的方案,期间遇到很多问题,主要集中在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.       压缩包上传

压缩包是根据版本来规划的,当版本改变后,用户下载压缩包就需要重新打包,但是同一版本的源文件没必要重新打压缩包,所以我们把压缩包上传资源服务器上,然后把资源链接保存在数据库中,下一次只需从数据库中查询到该链接即可,而无须重复打包,这样既提高了用户下载的速度,又节省了服务器资源。

五、 后期改进

增加Node日志,目前缺少node日志,如果出现异常,很难定位的问题;

Node打包server接收参数时进行严格校验;

六、 安全问题思考

A. 控制访问频率,和普通数据接口相比,打包接口耗时较长,消耗服务器资源较多,如果出现接口被恶意频繁请求,可能会影响服务器性能,同时造成正常的打包失败,有必要对访问频率做限制;

B. 在php层和node层都要进行严格校验参数,可以有效的防止因参数问题而带来的意外;

C. Node 代码打包server运行的端口不能对外,阻止用户通过外网直接访问该端口。

posted on 2015-11-22 21:35  Felixdh  阅读(1048)  评论(0编辑  收藏  举报

导航