使用Node.js 开发前端打包程序深度解析
沉沙 2018-06-13 来源 : 阅读 651 评论 0

摘要:我们在做前端开发的时候经常会在部署上线的时候做程序的打包和合并,我们接下来就会对如何使用Node.js 开发前端打包程序做非常深入的讲解,希望能够帮到有需要的同学。希望阅读本篇文章以后大家有所收获,帮助大家对Node.js的理解更加深入。

我们在做前端开发的时候经常会在部署上线的时候做程序的打包和合并,我们接下来就会对如何使用 node.js 开发前端打包程序做非常深入的讲解,希望能够帮到有需要的同学。

我们现在做前端开发更多的是多人共同协作开发,每个人负责不同的模块,便于开发和调试。这样就导致我们最后部署上线的时候需要把所有人开发的模块进行合并,生成单个或多个文件上线。如果手动合并的话肯定是费时又费力,而且非常容易出错,所以我们一般都是通过一些工具来实现自动合并的功能。

 

打包程序的原理非常简单,入口文件->寻找依赖关系->替换依赖关系->生成文件,其中中间的两个步骤是递归执行的。
我们先来看一下使用 node.js 如何完成一个简单的文件合并功能:


// 打包文件内容
var contentList = [];
// 排重列表
var loadedFileList = {};
 
// 打包主程序
function combine(filePath){
// 这里获取入口文件的内容
var fileContent = fs.readFileSync(filePath);
// 遍历文件内容
fileContent.forEach(function(value){
// 这里的findImport是需要你来实现的方法,用正则来匹配依赖关系
var matchFile = findImport(value);
if(matchFile){
//如果匹配到依赖关系
If(!loadedFileList[matchFile]){
//如果依赖关系不在排重列表中,递归调用combine
combine(matchFile);
contentList.push(‘\n’);
}
}else{
contentList.push(value);
}
});
}

   

最后只要根据 contentList 里面的内容来生成文件就可以了,怎么样,是不是很简单呢?下面我们就要介绍另外一种方式,使用流来完成我们的打包程序。

在 node.js 中,流(Stream)是一个由不同对象实现的抽象接口。流可以是可读的、可写的、或者既可读又可写的。所有的流都是 EventEmitter 的实例。我们可以通过继承接口来构造我们自己所需要的流。在我们的打包程序里面需要两个流,一个负责按行输出文件内容,另外一个负责处理依赖关系。所有的文件内容都在这两个流里面循环流动,当所有的依赖关系都处理完毕之后就结束流动并生成对应的文件,这样就达到我们的目的了。

让我们先来看一下负责按行输出文件内容的流是怎么样的:


var Stream = require('stream').Stream,
    util = require('util'),
    path = require('path'),
    fs = require('fs');
 
// 构造函数
function LineStream() {
    this.writable = true;
    this.readable = true;
    this.buffer = '';
}
 
module.exports = LineStream;
// 继承流接口
util.inherits(LineStream, Stream);
 
// 重写write方法,所有pipe过来的数据都会调用此方法
LineStream.prototype.write = function(data, encoding) {
    var that = this;
    // 把buffer转换为string类型
    if (Buffer.isBuffer(data)) {
        data = data.toString(encoding || 'utf8');
    }
 
    var parts = data.split(/\n/g);
 
    // 如果有上一次的buffer存在就添加到最前面
    if (this.buffer.length > 0) {
        parts[0] = this.buffer + parts[0];
    }
 
    // 遍历并发送数据
    for (var i = 0; i < parts.length - 1; i++) {
        this.emit('data', parts[i]);
    }
    // 把最后一行数据保存到buffer,使传递过来的数据保持连续和完整。
    this.buffer = parts[parts.length - 1];
};
// end方法,在流结束时调用
LineStream.prototype.end = function() {
    // 如果还有buffer,发送出去
    if(this.buffer.length > 0){
        this.emit('data',this.buffer);
        this.buffer = '';
    }
    this.emit('end');
};

   

这样我们的 lineStream 就完成了,我们看到在 write 方法里面就做了一件事,分解传递过来的数据并按行发送出去,然后我们看下处理依赖关系的流 DepsStream。


var stream = require('stream').Stream;
var util = require('util');
var fs = require('fs');
var path = require('path');
 
module.exports = DepsStream;
util.inherits(DepsStream,stream);
 
function DepsStream(){
    this.writable = true;
    this.readable = true;
    this.buffer = '';
    this.depsList = [];
};
 
// 这里的write方法只发送数据,不对数据做任何的处理
DepsStream.prototype.write = function(data){
    this.emit('data',data);
};
 
// 我们在这里重新pipe方法,使其能够处理依赖关系和生成最终文件
DepsStream.prototype.pipe = function(dest,opt){
    var that = this;
    function ondata(chunk){
        var matches = findImport(chunk);
        if(matches){
            if(this.depsList.indexOf(matches) >= 0){
                // 我们在这里把处理过后的数据pipe回lineStream
                dest.write('\n');
            }else{
                this.depsList.push(matches);
                var code = getFileContent(matches);
                // 我们在这里把处理过后的数据pipe回lineStream
                dest.write('\n' + code);
            }
        }else{
            this.buffer += chunk + '\n';
        }
    }
    function onend(){
        // 生成最终文件
        var code = this.buffer;
        fs.writeFileSync(filePublishUrl,code);
        console.log(filePublishUrl + ' combine done.');
    }
    // 监听end事件
    that.on('end',onend);
    // 监听data事件
    that.on('data',ondata);
};
 
// end方法
DepsStream.prototype.end = function(){
    this.emit('end');
};

   

我们看到上面的程序里面我们在 pipe 方法里面监听了 end 事件和 data 事件,ondata 方法主要用来对数据进行处理,发现有依赖关系的话就获取对应依赖关系的文件并重新发回给 LineStream 进行处理。onend 方法用来生成最终的文件,我们来看一下最终的调用方法:


var fileStream = fs.createReadStream(filepath);
var lineStream = new LineStream();
var depsStream = new DepsStream();
 
fileStream.pipe(lineStream);
lineStream.pipe(depsStream);
depsStream.pipe(lineStream);

   

怎么样,是不是非常简单,感兴趣的同学赶快尝试一下吧。

 

本文由职坐标整理并发布,了解更多内容,请关注职坐标WEB前端Node.js频道!

本文由 @沉沙 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程