最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
当前位置: 首页 - 科技 - 知识百科 - 正文

gulp进阶-自定义gulp插件_html/css_WEB-ITnose

来源:动视网 责编:小采 时间:2020-11-27 16:36:50
文档

gulp进阶-自定义gulp插件_html/css_WEB-ITnose

gulp进阶-自定义gulp插件_html/css_WEB-ITnose:gulp已经成为很多项目的标配了,gulp的插件生态也十分繁荣,截至2015.1.5,npm上已经有10190款gulp插件供我们使用。我们完全可以傻瓜式地搭起一套构建。 然而,我们经常会遇到一种情况,我们好不容易按照文档传入对应的参数调用了插件,却发现结果不如预期,
推荐度:
导读gulp进阶-自定义gulp插件_html/css_WEB-ITnose:gulp已经成为很多项目的标配了,gulp的插件生态也十分繁荣,截至2015.1.5,npm上已经有10190款gulp插件供我们使用。我们完全可以傻瓜式地搭起一套构建。 然而,我们经常会遇到一种情况,我们好不容易按照文档传入对应的参数调用了插件,却发现结果不如预期,


gulp已经成为很多项目的标配了,gulp的插件生态也十分繁荣,截至2015.1.5,npm上已经有10190款gulp插件供我们使用。我们完全可以傻瓜式地搭起一套构建。

然而,我们经常会遇到一种情况,我们好不容易按照文档传入对应的参数调用了插件,却发现结果不如预期,这时候我们就要一点点去排错,这就要求我们对gulp插件的工作原理有一定的了解。本文以实现一个gulp插件为例,讲解一下gulp插件是如何工作的。

需求描述

通常,我们的构建资源为js/css/html以及其它的一些资源文件,在开发或发布阶段,js/css会经过合并,压缩,重命名等处理步骤。

有些场景下,我们不能确定经过构建后生成js/css的名称或者数量,如此就不能在HTML文件中写死资源的引用地址,那么该如何实现一个Gulp的插件用以将最终生成的资源文件/地址注入到HTML中呢?

假设我们需要实现的插件是这样使用方式:

  

我们通过一个HTML注释用以声明需要依赖的资源,InlineResource 是匹配的关键词,":"做为分割,/*.css$/,/*.js$/ 是声明要依赖的文件的正则匹配。

在gulpfile.js我们需要这边配置:

gulp.task('dist', function () { return gulp.src('index.html') .pipe(InjectResources( gulp.src(['*.js', '*.css']) .pipe(hash(/*添加MD5作为文件名*/)) )) .pipe(gulp.dest('dist'))})

这里简单介绍下其中的一些方法与步骤:

  • gulp.src('index.html')会读取文件系统中当前目录下的index.html,并生成一个可读的Stream,用于后续的步骤消费

  • InjectResources(stream)是我们将要实现的插件,它接受一个参数用以获取要注入到HTML中的JS/CSS,此参数应该是一个 Stream实例,用生成一个Stream实例,用于接收并处理上一步流进来的数据

  • hash(options)是一个第三方插件,用于往当前流中的文件名添加md5串,如: gulp-hash

  • gulp.dest('dist')用于将注入资源后的HTML文件生成到当前目录下

  • 我们要关心的是第2点:如何接所有的资源文件并完成注入?

    我们可以将该逻辑分成4个步骤

    1. 获取所有的js/css资源
    2. 获取所有的HTML文件
    3. 定位HTML中的依赖声明
    4. 匹配所依赖的资源
    5. 生成并注入依赖的资源标签

    在开编之前,我们需要依赖一个重要的第三方库: map-stream

    map-stream 用于获取当前流中的每一个文件数据,并且修改数据内容。

    步骤1 (JS/CSS资源)

    module.exports = function (resourcesStream) { // step 1: TODO => 这里要获取所有的js/css资源}

    资源流会作为参数的形式传给InjectResources方法,在此通过一个异步的实例方法获取所有的文件对象,放到一个资源列表:

    var resources = []function getResources(done) { if (resources) return done(resources) // 由于下面的操作是异步的,此处要有锁... resourcesStream.pipe(mapStream(function (data, cb) { resources.push(data) cb(null, data) })) .on('end', function () { done(resources) })}

  • mapStream的处理方法中获取到的data是由gulp.src生成的 vinyl对象,代表了一个文件
  • 每一个stream都会在接受后抛出end事件
  • Note: mapStream的处理方法中的cb方法,第二个参数可以用于替换当前处理的文件对象

    到此,我们就完成了第一步的封装啦!

    module.exports = function (resourcesStream) { // step 1: function getResources () { ... }}

    步骤2 (HTML文件)

    module.exports = function (resourcesStream) { // step 1: ?? // step 2: TODO => 获取当前流中的所有目标HTML文件 return mapStream(function (data, cb) { })}

    InjectResources插件方法会返回一个 Writable Stream实例,用于接收并处理流到InjectResources的HTML文件,mapStream的返回值就是一个writable stream。

    此时,mapStream的处理方法拿到的data就是一个HTML文件对象,接下来进行内容处理。

    步骤3 (定位依赖)

    module.exports = function (resourcesStream) { // step 1: ?? // step 2: ? return mapStream(function (data, cb) { var html = data.contents.toString() // step 3: TODO => 获取HTML中的资源依赖声明 })}

    我们拿到的data是一个 vinyl对象,contents属性是文件的内容,类型可能是Buffer也可能是String, 通过toStraing()后可以获取到字符串内容。

    所有的依赖声明都有InlineResource关键词,简单点的做法,可以通过正则来定位并替换HTML中的资源依赖:

    html.replace(//g, function (expr, fileRegexpStr){ // fileRegexp是用以匹配依赖资源的正则字符串})

    到此,我们完成了资源依赖的定位,下一步将是获取所依赖的资源用以替换。

    步骤4 (依赖匹配)

    我们将通过步骤1定义的 getResources 方法获取所需的资源文件:

    module.exports = function (resourcesStream) { // step 1: ?? // step 2: ? return mapStream(function (data, cb) { // step 3: ? getResources(function (list) { html.replace(depRegexp, function (expr, fileRegexpStr) { var fileRegexp = new RegExp(fileRegexpStr) // step 4: TODO => 获取匹配的依赖 }) }) })}

    由于 getResources 是异步方法,因此需要把替换处理逻辑包裹在 getResources 的回调方法中

    根据依赖声明中的正则表达式,对资源列表一一匹配:

    function matchingDependences(list, regexp) { var deps = [] list.forEach(function (file) { var fpath = file.path if (fileRegexp.test(fpath)) { deps.push(fpath) } }) return deps}

    到此只差最后一步,将资源转换为HTML标签并注入到HTML中

    步骤5 (资源转换/依赖注入)

    module.exports = function (resourcesStream) { // step 1: ?? // step 2: ? return mapStream(function (data, cb) { // step 3: ? // step 4: ? // ... html.replace(depRegexp, function (expr, fileRegexpStr) { var deps = matchingDependences(list, fileRegexpStr) // step 5: 文件对象转换为HTML标签 }) })}

    接下来的定义一个transform方法,用于将路径列表转换为HTML的资源标签列表,其中引入了 path模块用于解析获取文件路径的一些信息,该模块是node内置模块。

    var path = require('path') function transform(deps) { return deps.map(function (dep) { var ext = path.extname(dep) switch (ext) { case 'js': '

    最终,我们将标签列表拼接为一个字符串来HTML中的依赖声明(注入):

    html = html.replace(depRegexp, function (expr, fileRegexpStr) { var deps = matchingDependences(list, fileRegexpStr) // step 5: 文件对象转换为HTML标签 return transform(deps)})// html文件对象data.contents = new Buffer(html)// 把修改后的文件对象放回HTML流中cb(null, data)

    到此也就完整地实现了一个拥有基本注入功能的插件~~~~~~

    One More Thing

    通过上面实现的示例步骤,可以清楚了解到gulp插件的工作原理。 但要做一个易用/可定制性高的插件,我们还要继续完善一下,例如:

  • 比较资源的路径与HTML的路径,输出相对路径作为默认的标签资源路径
  • 提供 sort 选项方法用于修改资源的注入顺序
  • 提供 transform 选项方法用于定制标签中的资源路径
  • 在依赖声明中支持 inline 声明,用以将资源内容内联到HTML中,例如:

     

  • 支持命名空间,用于往同一个资源流中使用多次资源注入的区分,例如:

     gulp.src('index.html') .pipe( InjectResources(gulp.src('asserts/*.js'), { name: 'asserts'}) ) .pipe( InjectResources(gulp.src('components/*.js'), { name: 'components'}) ) ...

  • . . .

  • 文档

    gulp进阶-自定义gulp插件_html/css_WEB-ITnose

    gulp进阶-自定义gulp插件_html/css_WEB-ITnose:gulp已经成为很多项目的标配了,gulp的插件生态也十分繁荣,截至2015.1.5,npm上已经有10190款gulp插件供我们使用。我们完全可以傻瓜式地搭起一套构建。 然而,我们经常会遇到一种情况,我们好不容易按照文档传入对应的参数调用了插件,却发现结果不如预期,
    推荐度:
    标签: html css gu
    • 热门焦点

    最新推荐

    猜你喜欢

    热门推荐

    专题
    Top