zoukankan      html  css  js  c++  java
  • Grunt学习笔记【3】---- filter使用方式和实现原理详解

    本文主要讲配置任务中的filter,包括使用默认fs.Stats方法名和自定义过滤函数,以及filter的实现原理。

    通过设置filter属性可以实现一些特殊处理逻辑。例如:要清理某个文件夹下的所有空文件夹,这时使用clean任务时,需要判断该文件夹下的哪些是文件,哪些是文件夹,只对空文件执行clean任务。

    具体使用方法分为如下两种:

    一 使用fs.Stats方法作为过滤函数

    fs.stats是NodeJS的一个类,上边提供了很多对文件判断的方法,具体可以参考这里

    如下将filter设置为'isFile'时,如果发现当前文件不是文件时,就不进行clean处理。

    1 grunt.initConfig({
    2   clean: {
    3     foo: {
    4       src: ['tmp/**/*'],
    5       filter: 'isFile',
    6     },
    7   },
    8 });

    二 自定义过滤函数

    自定义过滤函数,如果过滤函数返回true,就对当前文件进行处理;返回值是false时,就不处理当前文件。

    下边示例是清理tmp下边所有的空文件夹:

     1 grunt.initConfig({
     2   clean: {
     3     foo: {
     4       src: ['tmp/**/*'],
     5       filter: function(filepath) {
     6         return (grunt.file.isDir(filepath) && require('fs').readdirSync(filepath).length === 0);
     7       },
     8     },
     9   },
    10 });

     三 filter实现原理

    在分析filter的实现原理之前,首先给出一个使用filter时的注意事项:

    filter配置属性只能用于通过grunt.task.registerMultiTask()方法注册的复合任务。

    如果使用的某个Grunt插件注册任务时,没有使用grunt.task.registerMultiTask 注册任务,则配置filter是不生效的。

    而大多数的contrib任务,包括 jshint taskconcat task 和 uglify task 都是复合任务。

    下边说明filter在Grunt的实现原理:

    通过grunt.task.registerMultiTask()方法注册复合任务时,会拿到配置信息中对应任务的配置对象,对配置对象中的filter属性进行判断,如果设置了filter,就对要处理的文件进行filter过滤,根据过滤结果将新的文件列表存到files属性中,各个任务函数中拿到的files属性实际上是已经过滤后的复合filter要求的文件。

    下边以上边的clean任务为例,分析几个关键点的源码:

    3.1 首先分析clean的源码:

    在npm安装包node_module目录下grunt-contrib-clean中找到clean.js文件,该文件实际就是一个标准的nodeJS模块文件,找到其中通过grunt.registerMultiTask()方法注册clean任务代码,在第三个参数的处理函数中,我们可以看到这里实际是对this.filesSrc进行的处理,下边是部分代码:

     1 module.exports = function(grunt) {
     2 
     3     function clean(filepath, options, done) {
     4         // 省略的代码...
     5     }
     6 
     7     grunt.registerMultiTask('clean', 'Clean files and folders.', function() {
     8         // 省略的代码...
     9 
    10         // 要处理的文件
    11         var files = this.filesSrc;
    12         
    13         async.eachSeries(files, function (filepath, cb) {
    14             clean(filepath, options, cb);
    15         }, function (err) {
    16             grunt.log.ok(files.length + ' ' + grunt.util.pluralize(files.length, 'path/paths') + ' cleaned.');
    17             done(err);
    18         });
    19     });
    20 
    21 };

    3.2 接下来分析task.normalizeMultiTaskFiles()函数的源码:

    在该函数源码中,我们要找到传给上边clean函数的this.filesSrc来自哪里。

    在grunt的npm源码包中,找到task.js,其中定义了task.registerMultiTask函数,从该函数中我们可以发现this.filesSrc实际是对this.files.src进行处理后得到的。

    而this.files又是通过task.normalizeMultiTaskFiles(this.data, target)得到的,其中this.data和target就是源自于Grunt的配置信息。

    在最后fn.apply(this, this.args)可以知道,这个fn实际就是注册任务时的回调函数。

    下边是部分关键代码:

    task.registerMultiTask = function(name, info, fn) {
        //  省略的代码...
        
        task.registerTask(name, info, function(target) {
            // 省略的代码...
            
            this.data = grunt.config([name, target]);
            // 获取到files文件
            this.files = task.normalizeMultiTaskFiles(this.data, target);
            // 定义this.filesSrc文件
            Object.defineProperty(this, 'filesSrc', {
                enumerable: true,
                get: function() {
                    return grunt.util._(this.files).chain().pluck('src').flatten().uniq().value();
                }.bind(this)
            });
            // 调用任务的回调函数,并将指定this
            return fn.apply(this, this.args);
        });
    
        //  省略的代码...
    };

    3.3 分析task.normalizeMultiTaskFiles()源码:

    在这个源码中找到this.files.src是如何来的。从源码中可以发现来源于grunt.file.expand(expandOptions, src)函数。

    下边是其中部分代码:

     1 task.normalizeMultiTaskFiles = function(data, target) {
     2     // 省略的代码...
     3     
     4     if ('src' in result) {
     5         Object.defineProperty(result, 'src', {
     6             enumerable: true,
     7             get: function fn() {
     8                 var src;
     9                 if (!('result' in fn)) {
    10                     src = obj.src;
    11                     // If src is an array, flatten it. Otherwise, make it into an array.
    12                     src = Array.isArray(src) ? grunt.util._.flatten(src) : [src];
    13                     // Expand src files, memoizing result.
    14                     fn.result = grunt.file.expand(expandOptions, src);
    15                 }
    16                 return fn.result;
    17             }
    18         });
    19     }
    20 
    21     // 省略的代码...
    22 
    23     return result;
    24 };

    3.4 分析grunt.file.expand源码:

    在grunt的npm包中,找到file.js文件,在里边可以找到file.expand函数的定义,在该函数的定义中我们终于可以看到对filter的判断和处理,可以看到对filter是函数和字符时不同的处理。

    下边是部分关键代码:

     1 file.expand = function() {
     2     // 省略的代码...
     3     
     4     if (options.filter) {
     5         matches = matches.filter(function(filepath) {
     6             filepath = path.join(options.cwd || '', filepath);
     7             try {
     8                 if (typeof options.filter === 'function') {
     9                     return options.filter(filepath);
    10                 } else {
    11                     // If the file is of the right type and exists, this should work.
    12                     return fs.statSync(filepath)[options.filter]();
    13                 }
    14             } catch(e) {
    15                 // Otherwise, it's probably not the right type.
    16                 return false;
    17             }
    18         });
    19     }
    20     return matches;
    21 };

     说明:本人实际分析源码过程是倒着来的,首先在grunt包中搜到filter判断的地方,然后一步一步查找方法的调用链。

    参考资料&内容来源:

    Grunt官网:https://www.gruntjs.net/configuring-tasks

  • 相关阅读:
    MySQL之视图
    C# 学习笔记(二) 时间格式化字符串
    C# 学习笔记(一) Winform利用Assembly反射动态创建窗体
    puppet 4.4 System Requirements
    Linux下MySql启动时报错
    Linux Iptables
    Nginx Configure时配置
    Wireshark 使用教程
    Linux 下安装oracle数据库
    CaseFile
  • 原文地址:https://www.cnblogs.com/zhaoweikai/p/9713528.html
Copyright © 2011-2022 走看看