Table des matières
需求描述
步骤1 (JS/CSS资源)
步骤2 (HTML文件)
步骤3 (定位依赖)
步骤4 (依赖匹配)
步骤5 (资源转换/依赖注入)
One More Thing
. . .
Maison interface Web tutoriel HTML gulp进阶-自定义gulp插件_html/css_WEB-ITnose

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

Jun 21, 2016 am 08:58 AM

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

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

需求描述

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

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

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

<html><head>    <!--InlineResource:\.css$--></head><body>    <!--InlineResource:\.js$--></body></html>
Copier après la connexion

我们通过一个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'))})
Copier après la connexion

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

  • 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资源}
Copier après la connexion

资源流会作为参数的形式传给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)        })}
Copier après la connexion

  • mapStream的处理方法中获取到的data是由gulp.src生成的 vinyl对象,代表了一个文件
  • 每一个stream都会在接受后抛出end事件

Note: mapStream的处理方法中的cb方法,第二个参数可以用于替换当前处理的文件对象

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

module.exports = function (resourcesStream) {    // step 1:    function getResources () {        ...    }}
Copier après la connexion

步骤2 (HTML文件)

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

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中的资源依赖声明     })}
Copier après la connexion

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

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

html.replace(/<!--InlineResource:(.*?)-->/g, function (expr, fileRegexpStr){    // fileRegexp是用以匹配依赖资源的正则字符串})
Copier après la connexion

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

步骤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 => 获取匹配的依赖            })        })    })}
Copier après la connexion

由于 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}
Copier après la connexion

到此只差最后一步,将资源转换为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标签            })    })}
Copier après la connexion

接下来的定义一个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':                     '<script>' + dep + '</script>'            break            case 'css':                return '<link rel="stylesheet" href="' + dep + '">'            break        }        return ''    }).join('')}
Copier après la connexion

最终,我们将标签列表拼接为一个字符串来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)
Copier après la connexion

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

One More Thing

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

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

      <!--InjectResources:*\.js$??inline-->
    Copier après la connexion

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

      gulp.src('index.html')      .pipe(          InjectResources(gulp.src('asserts/*.js'), { name: 'asserts'})      )      .pipe(          InjectResources(gulp.src('components/*.js'), { name: 'components'})      )      ...
    Copier après la connexion

  • . . .

Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn

Outils d'IA chauds

Undresser.AI Undress

Undresser.AI Undress

Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover

AI Clothes Remover

Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

AI Hentai Generator

AI Hentai Generator

Générez AI Hentai gratuitement.

Article chaud

R.E.P.O. Crystals d'énergie expliqués et ce qu'ils font (cristal jaune)
2 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
Repo: Comment relancer ses coéquipiers
4 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island Adventure: Comment obtenir des graines géantes
4 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
Combien de temps faut-il pour battre Split Fiction?
3 Il y a quelques semaines By DDD

Outils chauds

Bloc-notes++7.3.1

Bloc-notes++7.3.1

Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise

SublimeText3 version chinoise

Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1

Envoyer Studio 13.0.1

Puissant environnement de développement intégré PHP

Dreamweaver CS6

Dreamweaver CS6

Outils de développement Web visuel

SublimeText3 version Mac

SublimeText3 version Mac

Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Difficulté à mettre à jour la mise en cache des pages Web officielles du compte: comment éviter l'ancien cache affectant l'expérience utilisateur après la mise à jour de la version? Difficulté à mettre à jour la mise en cache des pages Web officielles du compte: comment éviter l'ancien cache affectant l'expérience utilisateur après la mise à jour de la version? Mar 04, 2025 pm 12:32 PM

Le cache de mise à jour de la page Web du compte officiel, cette chose est simple et simple, et elle est suffisamment compliquée pour en boire un pot. Vous avez travaillé dur pour mettre à jour l'article officiel du compte, mais l'utilisateur a toujours ouvert l'ancienne version. Dans cet article, jetons un coup d'œil aux rebondissements derrière cela et comment résoudre ce problème gracieusement. Après l'avoir lu, vous pouvez facilement faire face à divers problèmes de mise en cache, permettant à vos utilisateurs de toujours ressentir le contenu le plus frais. Parlons d'abord des bases. Pour le dire franchement, afin d'améliorer la vitesse d'accès, le navigateur ou le serveur stocke des ressources statiques (telles que des images, CSS, JS) ou du contenu de la page. La prochaine fois que vous y accédez, vous pouvez le récupérer directement à partir du cache sans avoir à le télécharger à nouveau, et il est naturellement rapide. Mais cette chose est aussi une épée à double tranchant. La nouvelle version est en ligne,

Comment utiliser les attributs de validation du formulaire HTML5 pour valider l'entrée utilisateur? Comment utiliser les attributs de validation du formulaire HTML5 pour valider l'entrée utilisateur? Mar 17, 2025 pm 12:27 PM

L'article discute de l'utilisation des attributs de validation de formulaire HTML5 comme les limites requises, motifs, min, max et longueurs pour valider la saisie de l'utilisateur directement dans le navigateur.

Quelles sont les meilleures pratiques pour la compatibilité entre les navigateurs dans HTML5? Quelles sont les meilleures pratiques pour la compatibilité entre les navigateurs dans HTML5? Mar 17, 2025 pm 12:20 PM

L'article examine les meilleures pratiques pour assurer la compatibilité des navigateurs de HTML5, en se concentrant sur la détection des fonctionnalités, l'amélioration progressive et les méthodes de test.

Comment ajouter efficacement les effets de course aux images PNG sur les pages Web? Comment ajouter efficacement les effets de course aux images PNG sur les pages Web? Mar 04, 2025 pm 02:39 PM

Cet article démontre un ajout de bordure PNG efficace aux pages Web à l'aide de CSS. Il soutient que CSS offre des performances supérieures par rapport à JavaScript ou à des bibliothèques, détaillant comment ajuster la largeur, le style et la couleur des bordures pour un effet subtil ou proéminent

Quel est le but du & lt; datalist & gt; élément? Quel est le but du & lt; datalist & gt; élément? Mar 21, 2025 pm 12:33 PM

L'article traite du HTML & lt; Datalist & GT; élément, qui améliore les formulaires en fournissant des suggestions de saisie semi-automatique, en améliorant l'expérience utilisateur et en réduisant les erreurs. COMMANDE COMPRES: 159

Quel est le but du & lt; mètre & gt; élément? Quel est le but du & lt; mètre & gt; élément? Mar 21, 2025 pm 12:35 PM

L'article traite du HTML & lt; mètre & gt; élément, utilisé pour afficher des valeurs scalaires ou fractionnaires dans une plage, et ses applications courantes dans le développement Web. Il différencie & lt; mètre & gt; De & lt; Progress & gt; et ex

Comment utiliser le html5 & lt; time & gt; élément pour représenter les dates et les temps sémantiquement? Comment utiliser le html5 & lt; time & gt; élément pour représenter les dates et les temps sémantiquement? Mar 12, 2025 pm 04:05 PM

Cet article explique le html5 & lt; time & gt; élément de représentation sémantique de date / heure. Il souligne l'importance de l'attribut DateTime pour la lisibilité à la machine (format ISO 8601) à côté du texte lisible par l'homme, stimulant AccessIbilit

Quel est le but du & lt; Progress & gt; élément? Quel est le but du & lt; Progress & gt; élément? Mar 21, 2025 pm 12:34 PM

L'article traite du HTML & lt; Progress & GT; élément, son but, son style et ses différences par rapport au & lt; mètre & gt; élément. L'objectif principal est de l'utiliser & lt; Progress & gt; pour l'achèvement des tâches et & lt; mètre & gt; pour stati

See all articles