标签:
XX项目需要开发一套前端组件打包系统,来处理用户的请求:
1. 用户通过平台申请应用;
2. 选择所需要的组件;
3. 把组件的相关前端文件如js、css、html进行抽取、合并、压缩、打包;
4. 把压缩包链接地址返回给用户,用户下载压缩包。
针对上面的需求,我们选择一个比较流行的前端代码打包工具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运行的端口不能对外,阻止用户通过外网直接访问该端口。
标签:
原文地址:http://www.cnblogs.com/Felixdh/p/4986830.html