欢迎各位兄弟 发布技术文章

这里的技术是共享的

You are here

宁皓网 Grunt 自动化任务 有大用

在做前端开发的时候,有些重复的动作可以交给 Grunt 自动去执行,比如合并文件,最小化文件,编译 Sass 或者 Less,监视文件变化,刷新浏览器等等。

创建任务

创建的 Grunt 任务要放到项目根目录下面的一个叫 Gruntfile.js 的文件里。

执行任务

通过命令行工具,Mac 用户用终端,Windows 用户用命令提示符或者 PowerShell ,然后用 grunt 任务名 的形式去执行创建的 Grunt 任务。

插件

Grunt 有很多可用的插件,插件扩展了 Grunt 的功能,比如编译 Sass 的插件,创建本地开发服务器的插件。根据你要完成的任务,去搜索你的需求,应该可以找到合适的插件。

官方网站:http://gruntjs.com/

来自  https://ninghao.net/course/1974#info


安装好node.js之后(即包含了npm命令)

# sudo npm install -g grunt-cli        #全局安装  grunt-cli

# mkdir ninghao-grunt        #新建一个目录

# cd ninghao-grunt

# npm init             #会问我们一些问题,回答这些问题,来自动创建 package.json文件

# dir         #可以看到json文件

# sudo npm install grunt --save-dev            #安装grunt包,--save-dev表示把grunt 放到 package.json 文件的开发依赖的列表里面,这样别人下载下来使用 npm install就可以直接装上它了

# grunt              #Fatal error: Unable to find Gruntfile.,,,,提示 没找到 Gruntfile 这个文件,,,这个文件里面包含的东西就是我们设计好的让 grunt 执行的任务       




1)课程介绍

根据 Grunt 官方给自己的定义,Grunt 是 Task Runner ,执行任务的工具。使用 Grunt 就是为了自动化的去做一些事情。在平时开发项目的工作流程里面,一定会有一些事情,我们需要重复的做,最小化代码,编译输出 less 或者 sass ,合并文件等等 ...

这些动作都可以定义成 grunt 的任务,然后让 grunt 自动去执行。

http://gruntjs.com/

来自  https://ninghao.net/video/1975#info


2)安装 grunt


下面我们先去安装一下 grunt 的命令行工具 ... 确定在你的电脑上安装了 node 以后,我们就可以使用 npm 去安装 grunt 的命令行工具了 ...

输入 npm install -g grunt-cli ... 这里的 -g 参数的意思就是把东西安装到全局范围内 ... 这样在任何地方我们都可以使用 grunt 命令 ... 如果你用的是 unix 类型的操作系统,需要在这个命令的前面加上一个 sudo ,表示用超级管理员来执行这行命令 ... Windows 用户可以忽略掉这个 sudo ...

sudo npm install -g grunt-cli

完成以后,再去找个地方新建一个目录,这个目录就是项目的根目录 ... 先到 desktop 桌面上,新建一个叫 ninghao-grunt 的目录 ... 再执行一下 npm init ... 去在项目的根目录下面创建一个 package.json 文件 ...

这个命令会问一些问题,最后它会根据我们的回答来创建这个 package.json 文件 ... 有了这个文件以后,我们再在项目的目录里面,去安装 grunt ... sudo npm install grunt ... 后面加上一个 --save-dev ... 表示要把 grunt 放到 package.json 文件的开发依赖的列表里面 ...

sudo npm install grunt --save-dev

最后再执行一下 grunt 命令 ... 输入 grunt 回车 ... 会提示说 A valid Gruntfile could not be found ... 没找到 Gruntfile 这个文件,这个文件里面包含的东西就是我们设计好的让 grunt 执行的任务 ... 在下载的视频里我们再去创建一个这样文件,然后添加一个简单的任务 ...

来自 https://ninghao.net/video/1976#info


创建任务

3)创建任务:task.registerTask / registerTask


先用编辑器打开项目的根目录 ... 这里我用的是 atom 编辑器 ... command + o ,找到桌面上的 ninghao-grunt ... 然后先去创建一个 Gruntfile ... 新建文件 ... 文件的名字是 Gruntfile.js ....

文件一开始要用一个 module.exports ... 等于一个匿名函数 ... 这个函数授受一个叫 grunt 的参数 ... 这个 module.exports 是来自 node.js 的模块系统。

这样在这个花括号的里面,我们就可以使用 grunt 的一些方法去创建任务了 ... 注册一个任务可以使用 grunt 的 registerTask 这个方法 ... grunt.registerTask() ... 这个方法有两个参数,第一个参数就是任务的名字 ... 这里我们设置成 'default' ... 逗号分隔开 ... 它的第二个参数就是这个任务具体要做的事情 ...

用一个匿名函数 ... 在这里,我们可以使用 node.js 的 console.log 输出一些文字 ... Hello Grunt! ... 这样我们就定义好一个 grunt 任务 ... 名字是 default ... 要做的事就是输出 Hello Grunt!

image.png

保存 ... 回到命令行工具 ... 先进入到项目的根目录 ... 然后输入 grunt ... 后面加上要执行的任务的名字 default ... 这样会去执行我们在 Gruntfile.js 里定义好的 default 这个任务

 grunt  default 

 ... 在这里会显示 Hello Grunt! ...

因为 Grunt 默认执行的任务就是 default .. 所以我们可以直接输入 

grunt 

... 同样会去执行这个 default 任务 ... 在这个任务里用的是 node.js 的 console.log ... 如果你真的想在 Grunt 任务里输出内容的话, 我们可以使用 grunt 的 log 方法 ... 这样会更好一些 ...

回到 Gruntfile.js ... 把 console.log 换成 grunt.log ,使用 log 的 writeln ... 保存 ... 再回到命令行 ... 执行一下 default 这个任务 ... 同样会显示 Hello Grunt! ...

image.png




来自  https://ninghao.net/video/1977

4)在任务中使用参数


我们可以让创建的任务支持参数, 这样在执行这个任务的时候, 可以给这个参数一个具体的值 ... grunt 会根据这个传递进来的参数的值来执行任务。

打开 Gruntfile.js ... 这里我们修改一下这个任务的名字 ... default 换成 greet ... greet 是问候的意思 ... 这个任务支持一下参数 ... 参数的名字设置成 name ... 这样在这个任务里面,我们可以使用这个参数 ... 比如输出 Hello ... 再加上这个参数的具体的值 ...

grunt.log.writeln('Hello ' + name);

image.png

保存 ... 回到命令行 ... 去执行一下 greet 这个任务 .. 输入 

# grunt greet ... 这里会显示 Hello 后面还有一个 undefined ... 这是因为我们没有给 greet 这个任务的 name 参数指定一个具体的值 ...


再执行一下这个任务 ... grunt greet ... 任务的名字的后面加上一个冒号 ... 然后是传递的参数的值 ... 输入 ninghao ... 如果这个任务支持其它参数,可以在这里再加上一个冒号,然后为其它的参数设置具体的值 ...


# grunt greet:ninghao

image.png

回车 ... 这里会显示 Hello ninghao ... ninghao 是我们在执行 greet 这个任务的时候传递给 name 参数的值 ...

来自  https://ninghao.net/video/1978


5)错误提示


在创建任务的时候,你可能需要在任务里根据一些条件来触发执行任务的时候的错误 ... 这样 grunt 会停止继续执行任务 ... 触发这个错误,可以使用 grunt 的 warn 还有 fatal 方法 .. 这两个方法是 grunt 的 fail api 里面的 warn 还有 fatal 这两个方法的别名。

在这个 Gruntfile 里面,修改一下之前定义的 greet 这个任务 ... 在这个任务里,可以去检查一下在执行任务的时候,传递给 name 参数的值的长度 ... 用一个 if ... 判断的条件是 name.length < 2 ... 如果 name 值的长度小于 2 的话,我们就用 grunt 的 warn 这个方法 ... 显示一个警告的信息 ... 显示的提示是 名字太短了 :(

if (name.length < 2) {
grunt.warn('名字太短了:(');
}

保存 ... 

image.png

打开命令行工具 ... 执行一下 greet 这个任务 ... 给它的 name 参数设置一下值,一个字母 n ... 回车 ... 因为我们给 name 的值的长度小于 2 ,所以就会触发这个错误 ... 用黄色显示的文字 ... Warning:警告 ... 名字太短了 ... grunt 会停止继续执行任务 ...

image.png

在执行命令的时候,如果加上 --force 参数,grunt 会不管这个警告输入执行任务 ...

grunt greet:n --force



会显示 Hello n ... 说明 grunt 继续执行了任务 ... 最后会提示,任务完成,but with warnings ,不过有警告 ...

image.png

比较重要的错误,可以使用 fatal ... 回到 Gruntfile ... 把 warn 换成 fatal ... 

image.png


回到命令行... 再执行一下 ... 虽然在命令里加了 --force 选项,不过因为是 fatal 触发的错误,所以 grunt 不会继续去执行任务。

image.png




http://gruntjs.com/api/grunt.fail

来自  https://ninghao.net/video/1979#info


6)链接多个任务


我们可以定义一个任务,这个任务的任务就去执行一些指定的其它的任务。在这个 Gruntfile 里面,我已经定义好了三个任务 ... greet-1.. greet-2.. greet-3... 这几个任务的作用就是输出各种语言的问候 ...

下面, 我们创建一个任务,在执行这个任务的时候,让 grunt 分别去执行 greet-1,greet-2,还有 greet-3 这几个任务 ..

定义任务用的是 grunt 的 registerTask ... 先给这个任务起个名字 ... greetAll ... 然后它的第二个参数就是要执行的任务的列表 ... 用一个数组 ... 数组里的项目就是要执行的任务的名字 ... 中间用逗号分隔开 ...

grunt.registerTask('greetAll', ['greet-1', 'greet-2', 'greet-3']);

保存 ... 回到命令行 ... 输入 grunt greetAll ... 去执行一下 greetAll 这个任务 ... 这样 grunt 会分别去执行 greet-1 ... greet-2 ... 还有 greet-3 这几个任务 

image.png

image.png



来自 https://ninghao.net/video/1980


7)初始化配置:conifg.init / initConfig


我们可以在 Grunt 项目的一开始,使用 grunt 的 config api 里面的 init 这个方法去初始化一些数据,这些数据可以用到不同的任务里面。

打开 Gruntfile.js ... 在这里输入 grunt.config.init() ... 或者也可以使用这个方法的另一个名字 ... initConfig ... 这个 initConfig 直接是在 grunt 对象的上面 ... 给这个方法一个对象参数 ...

grunt.initConfig({});

然后在这里,我们可以为任务去定义一些属性,还有对应的值 ... 比如我们去给下面的 greet 这个任务准备一些东西, 这些东西可以放到一个跟任务名字一样的属性里面 ... 这里就是 greet ... 这个属性的值是一个对象 ... 在这个对象里,我们可以继续去添加想要的属性 ... 比如添加一个叫 english 的属性 ... 它的值设置成 'Hello'

grunt.initConfig({

        greet:{

            english: 'Hello'

        }

    });


然后在到 greet 这个任务里面 ... 在这里可以使用上面的 greet 这个对象里的数据 ... 比如我们在控制台上输出 greet 里面的 english 这个属性的值 ... 得到在 config.init 里面定义的属性的值,可以使用 grunt.config.get ... 把想要得到的属性作为这个方法的参数 ... 这个属性就是 greet.english ...

在控制台输出这个属性的值,可以用 grunt.log.writeln() 这个方法 ...

grunt.registerTask('greet', function () {
grunt.log.writeln(grunt.config.get('greet.english'));
});

保存 ... 打开命令行工具 ... 进入到项目所在的目录 ... 然后去执行一下 greet 这个任务 ... 输入 grunt greet ... 这里会显示出在配置对象里面的 greet 下面的 english 这个属性的值 .. 也就是 Hello ...

module.exports = function (grunt) {

  grunt.initConfig({

    greet: {

      english: 'Hello'

    }

  });

  grunt.registerTask( 'greet', function () {

    grunt.log.writeln(grunt.config.get( 'greet.english'));

  });

};

image.png


来自  https://ninghao.net/video/1981#toc


8)多任务:multiTask


multiTask,多任务,就是可能会执行多次的任务。你可以配置好一组属性,然后 Grunt 每次执行任务的时候都会用到这组属性里面的其中的一个。

下面, 我们把这个 greet 改造成一个多任务 ... 多任务自动到配置对象里面去找跟自己同名的属性 ... 然后根据这个属性对象里的其它的属性去执行任务 ... 现在这个 greet 里面已经有了一个属性,就是 english ... 下面再添加两个属性 ... 最后用一下逗号 .. 另起一行 ... 再添加一个 spanish ... 值是 Hola ... 然后再添加一个 chinese ... 对应的值是 您好 ...

在这个 greet 里面,english ,spanish ,还有 chinese .... 在多任务里面,叫做 target ... 就是要执行任务的目标 ... 在多任务里面,可以使用 this.target 输出它们 ... 要使用这些 target 对应的值 ... 用的是 this.data ...

先回到 greet 这个任务 ... 现在创建这个任务用的是 registerTask 这个方法 .. 也就是 task api 的 registerTask 这个方法的别名 ... 要注册多任务 ... 用的是 task api 的 registerMultiTask 这个方法 ... 或者也可以使用这个方法的别名,就是 grunt 下面的 registerMultiTask ...

在这个任务里面,我们还是简单的在控制台上输出点东西 .. 可以先输出所有的 target .... 输入 this.target ... 它表示的就是这个任务在配置对象里面的同名属性下面的属性 ... 中间用一个冒号 ... 然后再加上 this.data ...

保存 ... 打开命令行 ... 输入 grunt greet .... 你会看一这个任务会执行三次 ... 第一次执行的是 greet 的 english ... 输出的 english: Hello ... 然后是 greet:spanish ... 输出的 spanish: Hola ... 最后是 greet 的 chinese ... 输出的 chinese: 您好 ...

english,spanish,chinese 就是 greet 这个任务的 target ... 我们也可以分别去执行这些任务 ... 输入 grunt greet:chinese ... 这样只会运行 greet:chinese ...

module.export..functio.(grunt.{

  grunt.initConfig({

    greet: {

      english:'Hello',

      spanish:'Hola'

      chinese:'您好'

    }

  });

  grunt.registerMultiTask('greet'.functio.(.{

    grunt.log.writeln(this.targe+ ':' +this.data);

  });

};

image.png



image.png



来自  https://ninghao.net/video/1982#info


文件与目录

9)创建与删除目录

grunt 很多插件都会用到 grunt 的 file api ,也就是跟文件和目录相关的东西,这个视频我们介绍几个在 file api 里方法 ... 比如你打算在 grunt 任务里面创建一个目录,可以使用 file api 的 mkdir 这个方法 ..

先创建一个任务 ... 这个任务可以叫作 createFolders .... 因为我们要用它去创建目录 ... 在这个任务里,可以使用 grunt 的 file api 里面的 mkdir 这个方法 ... 然后把要创建的目录作为这个方法的一个参数 ... 这里我们去创建一个叫 dist 的目录 ... 在这个目录的下面,再创建一个 stylesheets 目录 ...

grunt.registerTask('createFolders', function () {
grunt.file.mkdir('dist/stylesheets');
});

image.png


保存 ... 打开命令行 ... 输入 grunt createFolders ... 

image.png

完成以后,查看一个项目的目录结构 ... 

# tree -I node_modules ...   (#tree 以树状显示文件夹的结构  宁皓说 -I 表示排除,但是我在windows当中感觉 -I 不起作用)

你会发现,这里有一个 dist 目录,在这个目录的下面,包含一下 stylesheets 子目录 ... fila api 的 delete 方法,可以删除掉指定的目录还有这个目录包含的所有的东西。

回到 Gruntfile.js .. 再去注册一个任务 ... 这个任务叫做 clean ... 在这个任务里面,我们可以用一下 grunt 的 file api 的 delete 方法 ... 要把删除的目录或者文件交给这个方法 .. 要删除的是 dist ...

grunt.registerTask('clean', function () {
grunt.file.delete('dist');
});

image.png


保存 ... 再到命令行下面去执行这个 grunt 任务 ... 输入 grunt clean ... 

image.png

完成以后,再查看一下当前的目录 .... 在这里已经看不到 dist 这个目录了,因为我们用 file 的 delete 方法把它给删除掉了。

来自  https://ninghao.net/video/1983#info

10)读取与写入文件


这个视频我们用 grunt file 的 readJSON 方法去读取一个 JSON 文件,然后使用返回来的内容,写入到一个指定的文件里面。 这里我们要读取的文件就是项目根目录下的 package.json ... readJSON 会返回读取出来的结果 ... 这个结果我们可以交给一个属性 ... 放到配置对象里 ...

先用一个 grunt.initConfig .... 给这个方法一个对象参数 ... 添加一个属性 ... 比如叫它 pkg ,表示 package ... 它的值,就是使用 grunt 的 file api 里面的 readJSON 方法返回来的东西 ... 读取的文件是 package.json ..

grunt.initConfig({
pkg: grunt.file.readJSON('package.json')
});

这样配置对象里面的 pkg 属性包含的就是返回来的结果 ... pkg.name ,输出的就是项目的名称 ... pkg.version 输出的就是项目的版本号 ... 这些内容是在 package.json 里面定义的 ...

下面再去创建一个任务... 这个任务可以叫做 copyright ... 得到配置对象里的属性的值,可以使用 config.get 这个方法 ... 不过这里我们用一个其它的方法 ... 先添加一个变量,这个变量里的内容就是要写入到指定文件的内容 ... 叫它 content ...

然后把要用到的属性放到一组模板标签里面 ... <%= pkg.name %> ... 这里的 pkg.name 就是项目的名字 ... 再输入点静态的文字 ... 这个项目是由 ... 后面加上作者的信息 ... <%= pkg.author %> ... 再加点静态的文字 ... 创建的 ... 现在的版本是 <%= pkg.version %> ...

var content = '<%= pkg.name %> 这个项目是由
<%= pkg.author %> 创建的,现在的版本是 <%= pkg.version %>。';

这个内容里面用到了模板标签 ... 要把标签它里的内容替换成它表示的真正的内容,需要再用 grunt template 的 process 这个方法去处理一下 ...

var content = grunt.template.process('<%= pkg.name %> 这个项目是由 <%= pkg.author %> 创建的,现在的版本是 <%= pkg.version %>。');

这个 template 的 process 默认会根据配置对象里的内容去把使用了模板标签的地方,替换成它真正的表示的内容 ... 这个内容默认就是配置对象里的东西,也就是我们在 initConfig 这个方法里定义的那个对象 ...

下面,我们再用 file 的 write,把这个内容写入到一个文件里 ... 这个方法的第一个参数是文件的位置 ... 设置成 copyright.txt ... 第二个参数就是要写入文件里的内容 ... 这里用 content 来表示 ...

grunt.file.write('copyright.txt', content);

保存,

image.png

打开命令行工具 .. 进入到项目所在的目录 ... 然后去执行一下 copyright 这个任务 ... 输入 

# grunt copyright ...

image.png


完成以后,查看一下当前的目录 ... 你会发现在项目的根目录下面会多了一个叫 copyright.txt 的文件 ... 再查看一下这个文件里的内容 ...

你会看到我们使用 file 的 write 方法写入到这个文件里的东西。

image.png

来自  https://ninghao.net/video/1984#info


11)复制文件:grunt-contrib-copy


grunt-contrib-copy ,这个插件可以把我们指定的文件复制到指定的位置上。先去安装一下这个插件,打开命令行 ... 使用 npm 的 install 命令去安装这个插件 ... 加上一个 --save-dev ,把这个插件放到 package.json 的开发依赖的列表里 ...

# npm install grunt-contrib-copy --save-dev

image.png

完成以后,回到我们的项目 ... 打开 Gruntfile.js ... 想要使用这个插件,需要先去把它载入进来 ... 用 grunt 的 loadNpmTasks 这个方法 ... 要载入的是 grunt-contrib-copy ...

grunt.loadNpmTasks('grunt-contrib-copy');

保存 ... 

image.png

现在我们就可以使用 grunt-contrib-copy 这个插件了 ... 这个插件的命令是

(这个插件的任务名是 copy )     copy ... 回到命令行 ... 输入 grunt copy ...


会提示,No "copy" targets found ... 意思就是,copy 这个命令没有指定要执行的 targets ... 在下面的视频里,我们再去配置一下这个插件 ...

image.png

https://github.com/gruntjs/grunt-contrib-copy

来自  https://ninghao.net/video/1985


12)配置要复制文件


我们先在项目里面添加几个要复制的文件 ... 在项目的根目录下,新建一个 index.html ... 在文件里添加一个基本的 html 结构 ...

image.png

打开 Gruntfile.js ... 下面我们去配置一下 grunt-contrib-copy 的 copy 这个任务 ... 让这个任务去把刚才创建的这些文件复制到一个指定的地方 ...

任务的配置,可以放到配置对象里,这里用一个 grunt 的 initConfig 这个方法 .. 给它一个对象参数 ... 然后先去添加一个 copy 属性 ... 因为 grunt-contrib-copy 这个插件定义的任务叫做 copy ,,,所以给它添加一个同名的属性

它的值是一个对象 ... 现在我们打算先把项目根目录下的 index.html 这个文件复制到 dist 这个子目录里面 ... dist 表示 distribution ...

在这个 copy 对象里面,添加一个叫 html 的属性,属性的名字你可以随便定义 ... 这个属性就是 copy 这个任务的 target ... 它的值是一个对象 ... 在这个对象里 ... 可以添加两个属性,src ... 还有 dest ... src 属性的值,就是你想要处理的文件 ... 这里就是你要复制的文件 ... src 表示 source ... 中文是源的意思 ... dest 表示 destination .. 目的地的意思 ... 也就是你想要把处理好的文件放到的地方 ...

添加一个 src ... 它的值设置成 index.html ... 然后再添加一个 dest ... 它的值设置成 dist/ ...

保存 ... 

image.png

打开命令行工具 ... 进入到项目所在的目录 ... 然后执行一下 copy 这个任务 ... 可以输入 grunt copy ... 在 copy 的后面我们也可以加上具体的目标 ... 也就是在配置对象里面跟任务同名属性下面定义的 ... 输入一个 :html ... 这样执行 copy 任务的时候,会根据 html 这个目标里的配置,去把 index.html 这个文件,复制到 dist 这个目录的下面 ...

# grunt copy:html

image.png

回到编辑器,你会发现,项目的根目录下面会多了一个 dist 目录 ... 在这个目录的下面有一个 index.html ... 这个文件就是从项目的根目录下面复制过来的 ...

来自  https://ninghao.net/video/1986#info

13)复制多个文件


然后再去创建一个目录 ... 

命名为 stylesheets ... 在这个目录的下面,再新建一个样式表 ... 可以叫它 style.css .. 在这个样式表里,添加一个简单的样式 ..

image.png

再去创建一个目录 .. 命名为 javascripts ... 在这个目录的下面,新建一个叫 app.js 的文件 ... 添加点内容 

image.png

... 然后在这个 javascripts 的目录里面,再添加一个子目录,叫它 modules ... 在这个目录的下面,创建一个叫 module.js 的文件 ...

image.png

下面,我们在这个 copy 任务里面, 再去添加两个目标 ... 先添加一个 style ... 在这个目标里,去把 stylesheets 这个目录下面的所有的文件复制到 dist 这个目录的下面 ...

这里要先用一个逗号分隔一下 ... 另起一行,再输入 style ... 它的值也是一个对象 ... 在这个对象里,用 src 属性去指定要处理的文件 ... 就是在 stylesheets 目录下面的所有的 css 文件 ... 在这个用一个 * 号,表示任意的字符,但不包含 / ... 后面加上一个 .css 。意思就是,直接在 stylesheets 这个目录下面的所有的扩展名是 .css 的文件 ...

逗号分隔一下 .. 再添加一个 dest 属性,指定一下存储这些文件的目标 ... 把它们放到 dist 这个目录的下面 ... grunt 会在 dist 这个目录下面,创建一个 stylesheets 目录,然后把复制过来的文件放到这里 ...

再去添加一个目标 ... 逗号分隔开 ... 这个目标可以叫做 js ... src 设置一下要复制的文件 ... 它们都在 javascripts 这个目录的下面,因为这个目录里还包含其它的子目录,这些子目录里的所有的文件我们也想复制一下 ... 这里用两个 ** ... 表示所有的东西 ... 后面再加上一个斜线... 再用一个 *.js ... 意思就是,在 javascripts 这个目录下面还有这个目录里所有的层级的子目录下面的所有的扩展名是 .js 的文件 ...

逗号分隔 ... 另起一行,再添加一个 dest ... 指定一下这些文件复制到的目标 ... 把它们也都放到 dist 这个目录的下面 ...

image.png

保存 ... 打开命令行 ... 输入 

#grunt copy 

... 执行一下 copy 这个任务 ... 

image.png

你会看到分别会去执行 copy:html ,copy:style,copy:js ...

完成以后,再查看一下项目的目录结构 ... 可以使用 tree 这个命令 ... 加上一个大写的 I ... 后面再加上一个要排除显示的目录 ...

tree -I node_modules

这样会显示排除了 node_modules 以后的当前目录的树形结构。你会看到,在 dist 这个目录的下面,会包含复制过来的文件 ...

来自 https://ninghao.net/video/1986#info


14)监视文件变化:grunt-contrib-watch


指定的文件发生变化以后,就去执行指定的任务,这个就是 grunt-contrib-watch 插件的功能。先去安装一下这个插件 ... 打开命令行工具 ... 输入 npm install ... 后面加上插件的名字 ... grunt-contrib-watch .. 再加上一个 --save-dev,把这个插件放到 package.json 文件的开发依赖列表里面 ...image.png

npm install grunt-contrib-watch --save-dev  

如果出现权限问题,在这个命令之前加上sudo ( sudo npm install grunt-contrib-watch --save-dev  )


完成以后,打开 Gruntfile.js ... 在这里,先去载入这个插件 ... grunt.loadNpmTasks ... 加载的插件是 grunt-contrib-watch ...

# grunt.loadNpmTasks('grunt-contrib-watch');

image.png

这样我们就可以使用这个插件定义的任务去做一些事情了 ... 先保存一下 ... 再回到命令行 ... 输入 

# grunt --help ... 回车

image.png

image.png

 ... 会显示一些帮助的信息,在这个 Available tasks 这里,会列出当前项目所有可用的任务 ... 这里有一个 watch ... 这个任务就是 grunt-contrib-watch 插件定义的任务的名字 ...

Available tasks
watch Run predefined tasks whenever watched files change.

在下面的视频里,我们再看一下怎么样使用这个插件。

https://www.npmjs.com/package/grunt-contrib-watch


来自  https://ninghao.net/video/1988#info


15)文件发生变化执行指定的任务


watch 这个任务的相关配置可以放到在 initConfig 方法里面定义的对象里,输入 grunt.initConfig({}); 然后要添加一个跟任务同名的属性 ... watch 任务的属性就是 watch ... 它的值是一个对象 ... 在这个对象里,我们可以为 watch 任务定义多个目标 ... 比如定义一个叫 html 的目标,去监视所有的 html 文件的变化,定义一个 style 目标,去监视所有样式表文件的变化 ...

这里我先添加一个 html ... 它的值又是一个对象 ... 在这个对象里,可以设置一下 files 属性去描述一下要监视的文件,这个属性的值就是你要监视的文件 ... 可以是一个文件,可以是多个文件,也可以是表示文件的模式 ... 比如 * 表示所有的东西,除了 / 以外 ... 用 ** 可以表示任何东西 ...

给这个 files 属性一个数组作为它的值 ... 然后把 index.html 这个文件作为这个数组的一个项目 ... 这个文件就是 html 这个目标要监视的文件 ... 后面用一个逗号 ...

另起一行 ... 再添加一个 tasks 属性 ... 这个属性的值,就是要执行的任务的列表 ... 也给它一个数组作为它的值 ... 数组里的每个项目就是要执行的任务 ... 添加一个 copy:html ... 现在我们执行watch这个任务以后,如果index.html这个文件发生了变化,就会执行任务copy:html这个目标,,,,,

image.png 

image.png

我们可以再去定义这个任务 ... 在前面的视频里已经介绍过了 grunt-contrib-copy 这个插件的用法 ...

先去加载这个插件 ..

grunt.loadNpmTasks('grunt-contrib-copy');

再配置一下 copy 任务 ... 同样在 initConfig 这个方法里面,先添加一个叫 copy 的属性 ... 然后给这个任务添加一个目标 ... 名字是 html ... 它的值是一个对象 ... 在这个对象里 设置一下 src 属性的值 ... 就是要处理的文件 ... 这里就是 index.html ... 再设置一下 dest 属性,设置一下这个文件复制到的位置 ... 把它放到 dist 这个目录的下面 ...

现在,我们在执行 watch 任务的以后,如果 index.html 这个文件发生了变化 ... 就会去执行 copy任务的 html 这个目标 ... 保存 ... 回到命令行 ... 输入 

# grunt watch ...

image.png

这个任务一直会在待机的状态 ... 因为它要实时的监视文件的变化 ... 回到项目 ... 打开 index.html ... 保存一下 ... 这样会触发执行 watch 任务的 html 这个目标任务 ... 也就会把 index.html 这个文件,复制一份到 dist 这个目录的下面 ...

image.png

https://github.com/isaacs/minimatch#options

来自  https://ninghao.net/video/1989#info


插件

16)创建服务器:grunt-contrib-connect


有时候在做前端开发也需要把项目放到 Web 服务器上去运行,这个服务器,可以用 Grunt 的 connect 插件去搭建 ... 你可以指定服务器的端口号,使用的协议,http 或者 https ,跟 watch 插件配置到一块儿,可以让服务有实时预览的功能,也就是当你指定的文件发生变化以后,浏览器会立即自动刷新,显示出修改之后的结果 ...

先打开命令行 ... 用 npm 去安装一下这个插件 ... 插件的名字是 grunt-contrib-connect ... 后面加上一个 --save-dev ... 把这个插件放到 package.json 的开发依赖的列表里 ...

# npm install grunt-contrib-connect --save-dev

image.png

完成以后,打开 Gruntfile.js ... 再去加载刚才安装的 connect 插件 ... 用 grunt 的 loadNpmTasks ... 载入的是 grunt-contrib-connect ..

# grunt.loadNpmTasks('grunt-contrib-connect');

image.png

image.png

现在我们就要以使用这个插件定义的任务去运行服务器了 ... 回到命令行 ... 输入

# grunt connect

image.png 

... 回车 ... 会显示没有找到 connect 任务的目标 ... 在下面的视频里我们再去配置一下这个 connect 任务 ...

https://www.npmjs.com/package/grunt-contrib-connect
https://github.com/gruntjs/grunt-contrib-connect

来自  https://ninghao.net/video/1990#info


17)配置服务器


connect 的相关的配置也要放到配置对象里,就是这个在 initConfig 方法里面定义的对象 ... 先添加一个跟任务同名的属性 ... connect ... 它的值是一个对象 ... 在这个对象里面,我们可以定义多个任务的目标 ... 每个目标都可以定义一台服务器 ... 先添加一个 server ... 它的值也是一个对象 ... 然后把相关的选项,放到一个 options 属性里面 ...

在这个 options 里面,就是 server 这个目标的相关的配置 ... 比如去设置服务器的端口号,根目录等等 ... 要设置端口号,先添加一个 port 属性 ... 默认它的值是 8000 ,你也可以设置成其它的端口号,在创建多个服务器的时候,每台服务器的端口号都应该不一样 ... 比如这台服务器的端口号是 8000 ,另一台服务器你可以使用 8080 ...

我们先设置成默认的 8000 ... 逗号分隔一下 ... 另起一行,再设置一下 base 属性的值,这个属性的值就是服务器的根目录 ... 设置成 dist ... 因为我想用服务器打开这个目录下面的文件 ...

image.png

image.png

保存 .. 打开命令行工具 ... 输入 

# grunt connect:server ... 

image.png

注意这个服务器只有在 grunt 运行的时候才会运行 ... 也就是 grunt 结束了任务,这个服务器也就会关掉了 .. 想让它一直运行下去,可以加上一个 keepalive ... 一般我们不会直接在 connect 任务上使用这个 keepalive ... 因为它会阻止其它的任务使用我们创建的服务器 ... 这里先用一下 ... 回车 ...

# grunt connect:server:keepalive

image.png


打开浏览器 ... 输入 localhost:8000  

image.png

... 这样会用创建的 Web 服务器,服务在 dist 这个目录下面的文件 ... 默认会打开这个目录下面的 index.html ...

来自  https://ninghao.net/video/1991#info


18)实时刷新:livereload


当被监视的文件发生变化以后,会自动的刷新浏览器查看文件的变化 ... 这就是 livereload ... 实时刷新的功能 ... 我们可以把 watch 还有 connect 这两个任务结合到一块儿用 ...

先打开 Gruntfile.js ... 找到 connect 相关的配置 ... 在这个 options 里面 ... 添加一个 livereload 属性 ... 它的值设置成 true ... 表示这台服务器允许使用实时刷新的功能 ..

connect: {
server: {
options: {
base: 'dist',
livereload: true
}
}
},

再找到 watch 任务的配置 ... 在它的目标里面,如果想要使用实时刷新的功能 ... 也得把 livereload 的值设置成 true .. 这个 livereload 属性要放到 options 里面 ... 先添加一个 options ... 在这个选项对象里面 ... 把 livereload 这个属性的值,设置成 true ...

watch: {
html: {
files: ['index.html'],
tasks: ['copy:html'],
options: {
livereload: true
}
}
},

下面再去创建一个任务,在执行这个任务的时候,同时去执行 watch 还有 connect ... grunt.registerTask ... 这个任务可以叫它 serve ... 它的第二个参数就是要执行的任务列表 .. 用一个数组 ... 先添加一个 connect ... 再添加一个 watch ...

image.pngimage.png

image.png


保存 ... 打开命令行工具 ... 输入 

# grunt serve  

image.png

 然后在浏览器上打开服务器  localhost:8000 ... 先回到项目 ... 打开项目根目录下面的 index.html ... 修改一下这个文件里的内容 ... 然后保存一下 ...

image.png

因为我们使用 watch 监视了这个文件变化,并且让它自动去执行 copy 任务 ... 这个任务会把 index.html 复制到 dist 的目录下面 ... 因为我们又使用了实时刷新的功能 .. 所以,不需要我们手工去刷新浏览器( 应该是不需要重启服务器吧   好像我的不能实时刷新 难道我的代码与宁皓的代码不一样 )  ... 它会自动刷新,显示出文件修改之后的结果 ..

来自 https://ninghao.net/video/1992#info



19)编译 sass:grunt-contrib-sass


把写好的 sass 编译成 css ,可以使用 grunt-contrib-sass 这个插件 .. 打开命令行 ... 先去安装一下这个插件 ...

# npm install grunt-contrib-sass --save-dev

除了这个插件,你的电脑上还需要安装 ruby 还有 sass ... 然后打开 Gruntfile.js ... 去加载一下安装的 sass 插件 ...

image.png

grunt.loadNpmTasks('grunt-contrib-sass');

image.png

这样我们就可以在项目里面使用 grunt sass 这个任务了。我们先在项目里添加一个 sass 文件 ... 在 stylesheets 这个目录的下面 ... 可以把这个 style.css 重命名为 scss ...

打开它,简单的修改一下这个文件里的东西 ... 先定义一个变量 ... 然后在这个样式里面,可以使用这个变量 ... 关于 sass ,你可以参考宁皓网的 sass 基础教程 ...

$base-color: #000;
body {
background-color: $base-color;
}

image.png


回到 Gruntfile.js ... 再去配置一下 sass 这个任务 .. 在这个 initConfig 里面定义的对象里 ... 添加一个跟任务同名的属性 ... 就是 sass ... 它的值是一个对象 ... 然后再去设置一下这个任务的目标 ... 比如我们添加一个叫做 dist 的目标 ... 表示 distribution ...

在这里,再添加一个 files 对象 ... 在这个对象里面,可以设置一下,把在哪里的 sass 文件,编译成 css ,然后放到哪里 ... 首先要指定一下编译后的目标 ... 比如我们要编译一下 stylesheets 这个目录下面的 style.scss ... 编译以后,放到 dist 这个目录下面的 stylesheets 里面,文件的名字是 style.css ...

这里添加这个编译之后的目标 ... 就是 'dist/stylesheets/style.css' ... 冒号 ... 再设置一下 sass 文件的位置 ... 'stylesheets/style.scss' ...

逗号分开 ... 可以继续去用这样的形式来指定其它的要编译的 sass ... 跟编译相关的选项,要放到一个 options 属性里面 ... 添加一个 options 属性 ... 值是一个对象 ... 这里我们设置一下编译之后的格式 ... 用一个 style 属性 ... 它的值,设置成 expanded ...

image.png

保存 ... 去执行一下这个任务 .. 回到命令行工具 .. 输入 

# grunt sass:dist 

image.png

完成以后 ... 回到项目 ... 在 dist 这个目录的下面 ... 打开 stylesheets . .. 找到 style.css ... 这个文件就是编译之后的在项目根目录下面的 stylesheets 下面的 style.scss ...

来自  https://ninghao.net/video/1993#info

20)编译 less:grunt-contrib-less


把 less 编译成 css,用的是 grunt-contrib-less 这个插件,它的用法跟我们之前介绍的 grunt-contrib-sass 差不多 ... 下面,我们还是先去安装一下这个插件 ... 打开命令行工具 ... 用 npm install ... 安装的是 grunt-contrib-less ...

# npm install grunt-contrib-less --save-dev

image.png

完成以后,打开 Gruntfile.js ... 去加载一下这个插件 ... grunt.loadNpmTasks ... 要载入的是 grunt-contrib-less ... 现在我们的项目里就可以使用 less 这个任务去编译 less 文件了 ..

grunt.loadNpmTasks('grunt-contrib-less');

image.png

先去创建一个 less 文件 ... 这里我们可以去改造一下 stylesheets 里面的 style.scss ... 先把它重命名成 style.less ... 然后打开这个文件 ... 再用 less 的语法去修改一下这个文件里的内容 ... 在 less 里面变量名字的前面用的是 @ 符号 ...

保存 ... 

image.png

再回到 Gruntfile.js ... less 的用法跟 sass 差不多 .. 可以直接修改一下 ... 把 sass 换成 less ... 跟 less 任务相关的选项同样放到 options 这个属性里面 .. 这里我不打算使用自定义的选项 ... 先去掉这个 options 属性 ...

files 这个属性里面的东西,就是编译之后的 css 的路径 ,还有要编译的 less 的路径 ... 把这里的 style.scss 换成 style.less ... 意思就是,去把 stylesheets 目录下面的 style.less 这个文件编译成 css ,然后把编译好的 css 放到 dist 目录下面的 stylesheets 这个目录里面,文件的名字是 style.css ...

保存 ... 

image.png

打开命令行 .. 去执行一下 less 任务 .. 输入 

# grunt less

 ... 这样 grunt 会执行所有在 less 任务里定义的目标 ... 我们也可以在后面加上一个冒号 .. 再指定一下要执行的目标 ... 加上一个 dist ...

# grunt less:dist

image.png

完成以后,回到项目 ... 在 dist 里面,找到 stylesheets ... 这个目录下面的 style.css 就是编译好的 style.less ...

https://github.com/gruntjs/grunt-contrib-less

来自  https://ninghao.net/video/1994#info


21)合并文件:grunt-contrib-concat


在项目里有些文件你可能想要把它们合并成一个文件 ... 比如项目用的 css ... javascript ... 这个合并的任务,可能使用 grunt-contrib-concat 插件去做 ...

打开命令行 ... 先去安装一下这个插件 ...

# npm install grunt-contrib-concat --save-dev

image.png

然后再打开 Gruntfile.js ... 这里再去加载一下刚才安装的 concat 插件 ...

grunt.loadNpmTasks('grunt-contrib-concat');

image.png

这样我们就可以去执行 concat 任务去合并文件了 ... 比如在我的项目里, javascripts 这个目录下面,有一个 app.js ... 在 modules 目录还有一个 module.js ... 我们去配置一下,把这两个文件合并到一块儿 ... 然后把合并以后的 js 文件再放到 dist 目录下面的 javascripts 这个目录里面 ...

这些配置要在配置对象里去定义 ... 先添加一个跟任务同名的属性 ... 这里就是 concat ... 它的值是一个对象 ... 跟合并相关的选项可以放到一个 options 属性里面 ... 比如你可以设置合并文件的头部还有页脚部分的内容 ...

concat 是一个多任务 ... 所以我们可以定义多个目标 ... 比如这里我要做的是去合并 js 文件 ... 可以添加一个目标叫做 js ... 这个名字你可以随便定义,只要你认为合理就行 ...

它的值也是一个对象 ... 这里我们可以用一个 files 对象 ... 在这个对象里,你可以用目标: 源的形式,去设置合并以后的文件的位置,还有要合并的文件 ...

files:{

  "目标":"源"

}

或者,我们也可以使用 src 属性,来定义要合并的文件 ... 然后用 dest 属性来定义合并以后的文件的路径 ...

先添加一个 src 属性 ... 它的值是一个数组 ... 数组里的项目就是要合并到一块儿的文件 ... 先是 javascripts 里面的 app.js ... 然后是 javascripts 下面的 modules 目录里面的 module.js ...

逗号分隔开 ... 再设置一下 dest 属性 ... 用它来指定一下合并以后的文件的路径 ... 把合并之后的文件,放到 dist 目录下面的 javascripts 这个目录里面 ... 合并之后的文件的名字可以设置成 app.js ...

保存 ... 

image.png

再去执行一下这个任务 ... 回到命令行 ... 输入 

# grunt concat ... 


执行这行命令,会执行 concat 任务里面的所有的目标 ... 我们也可以手工指定一个任务的目标 ... 用一个冒号 ... 然后输入刚才定义的 js 这个目标 ...

grunt concat:js

image.png

回车 ... 完成以后,再回到项目 ... 找到 dist ... javascripts ... 在这个目录下面的 app.js 就是合并以后的 js 文件 ... 在这个文件里面,包含了项目根目录下的 javascripts 目录里面的 app.js 还有子目录 modules 里面的 module.js 这两个文件的内容 ...

https://github.com/gruntjs/grunt-contrib-concat

来自 https://ninghao.net/video/1995#info


22)选项:options


大部分插件定义的任务都支持一些配置的选项,选项会影响任务的一些行为 ... 具体都支持哪些选项,可以参考插件在 github 或者 npm 上面的使用说明 ... 这些选项一般都会放到一个 options 属性里面,比如在上一个视频里我们用的这个 concat 任务 ... 可以直接把 options 属性放到 concat 里面 ... 这样在这里配置的选项会影响到所有的在任务里面定义的目标 ...

如果在目标里,也添加了一个 options 属性,那么在目标里配置的选项,会覆盖任务总体的选项 ... 在这个 js 目标里,用一个 options 属性 ... 它的值是一个对象 ... 属性的名字就是选项的名字 ... 属性的值就是选项的值 ... 这个 concat 支持一下叫 banner 的选项 ... 这个选项的值,会出现在合并之后的文件的页头上 ...

设置一段注释的内容 ... 最后的 \n 是一个换行符 ... 另起一行,再添加一个 footer 选项 ... 它的值会出现在合并以后的文件的页脚部分 ... 在这里也添加一段注释的内容 ...

保存 ...

image.pngimage.png


 再回到命令行 ... 去执行一下 concat 任务 ... 

# grunt concat;js  

image.png

完成以后,回到项目 .. 再打开合并之后的文件 ... 你会看一下,在这个文件的一开始,会出现为 banner 选项设置的值 ... 在文件结束的地方 ... 是为 footer 这个选项设置的值 ...

image.png

来自  https://ninghao.net/video/1996#info


23)最小化 js:grunt-contrib-uglify


最小化 js,就是使用一些方法,让 js 文件的尺寸变得更小一些,比如去掉文件里的注释,空白,改变变量的名字等等 ... 使用 grunt-contrib-uglify 这个插件,我们可以完成最小化 js 文件的任务 ...

先去安装一下这个插件 ... 打开命令行 ... npm install ... grunt-contrib-uglify ... grunt 的大部分插件都是用 grunt-contrib 开头 ... 最后一部分是插件的名字,也是在这个插件里面定义的任务的名字 ...

# npm install grunt-contrib-uglify --save-dev

image.png

完成以后,再到 Gruntfile.js 里面去加载一下这个插件 ...

grunt.loadNpmTasks('grunt-contrib-uglify');

image.png

现在,我们就可以使用 uglify 这个任务了 ... 不过想让它真正去做事情,还得配置一下 ... 先在配置对象里面添加一个跟任务同名的属性 ... uglify ... 相关选项可以放在 options 属性里面 ... 我们先添加一个任务的目标 ... 比如叫它 dist ... 在这个目标里面,去描述一下要处理的文件,还有文件输出的位置 ... uglify 可以把多个文件合并到一块儿,再去最小化一下 ... 或者,我们也可以先用 concat 这个任务去把文件合并到一块,然后再把合并以后的文件用 uglify 去处理 ... 这样会更快一些 ...

先用 src 属性,去描述一下要处理的文件的位置 ... 这里我们用一个模板标签 ... 然后是 concat.js.dest ... 意思是使用 concat 的 js 这个目标里面的 dest 这个属性的值 ...

另起一行 ... 再用一个 dest 属性,设置一下最小化之后的文件的位置 ... 放到 dist 下面的 javascripts 这个目录里面,文件的名字是 app.min.js ...

我们再去定义一个任务 ... grunt.registerTask ... 任务的名字可以叫做 build ... 执行这个任务的时候,先去执行 concat ... 再执行 uglify ...

grunt.registerTask('build', ['concat', 'uglify']);

image.png

image.png


保存 ... 回到命令行 ... 执行一下 build 这个任务 ... 

# grunt build 

image.png

... 完成以后再回到项目 .. 在 dist 下面的 javascripts 里面,找到处理之后的 app.min.js ...

这个文件,就是合并并且最小化之后的 js 文件 ...


来自  https://ninghao.net/video/1997#info


24)最小化 css:grunt-contrib-cssmin


下面我再用 grunt-contrib-cssmin 这个插件去最小化一下指定的 css 文件... 先去安装一下这个插件 ...

# npm install grunt-contrib-cssmin --save-dev

image.png

完成以后,打开 Gruntfile.js ... 再把刚才安装的插件载入进来 ...

grunt.loadNpmTasks('grunt-contrib-cssmin');

然后再到配置对象里面去配置一下这个任务 ... 添加一个叫 cssmin 的属性,这个属性要跟任务的名字一样 ... 再定义一个任务的目标 ... 添加一个叫 dist 的目标 ... 它的值是一个对象 ... 在这个对象里,我们可以使用 files 这个对象,去描述一下文件处理之后的路径,还有对应的要处理的文件 ...

或者也可以使用 src 跟 dest 属性 ... 输入一个 src ... 它的值就是要处理的文件的源 ... 我们要把使用 less 或者 sass 任务编译之后的 css 文件最小化一下 ... 这个文件是在 dist/stylesheets 下面 ... 文件的名字是 style.css ...

逗号分隔开 ... 另起一行,再添加一个 dest ... 它的值就是处理之后的文件的位置 ... 放到 dist/stylesheets 下面,命名为 style.min.css ...

保存 ... 

image.png

image.png

image.png

回到命令行 . ... 输入 

# grunt cssmin 

image.png

... 回车 ... 完成以后,再回到项目 ... 在 dist ... stylesheets 下面,可以找到最小化之后的 css 文件,style.min.css ...

image.png

来自  https://ninghao.net/video/1998#info


25)最小化图像:grunt-contrib-imagemin


png,jpg,gif,svg,这些格式的图像文件,我们都可以使用 grunt-contrib-imagemin 这个插件去优化一下,让图像文件的体积变得更小一些...

先去安装一下 ... 这个插件需要很多依赖的东西,所以安装的时候可能会长一些 ... 另外我们可能需要使用管理员的身份去执行这个安装的命令 ... 加上一个 sudo ,Windows 用户可以忽略这个 sudo 命令,直接输入 ... npm install ...

# sudo npm install grunt-contrib-imagemin --save-dev

image.png

如果在git里面执行这个命令不行,就到 cmd 下执行这个命令

完成以后,打开 Gruntfile.js ... 去载入一下这个插件 ..

grunt.loadNpmTasks('grunt-contrib-imagemin');

再去配置一下这个插件里面定义的 imagemin 任务 ... 先添加一个 imagemin 属性 ... 这里我们也可以去定义多个目标 ... 这里我添加一个叫 dist 的目标 ... 相关的选项放到 options 属性里面 ... 然后再去描述一下要处理的文件,还要处理之后文件的存储位置 .

可以使用 files 对象,也可以使用 src 还有 dest 属性 ... 输入一个 src ... 这个属性的值就是要处理的图像文件 ... 是在 images 这个目录的下面 ... ** 两个星号表示所有的东西,包含 images 目录里面的子目录里的文件 ... 加上一个 /*{png, jpg} ... 意思就是,在 images 目录下面的所有的 png 还有 jpg 格式的文件 ...

逗号分隔一下 ... 另起一行,再添加一个 dest 属性,设置一下处理之后的文件的位置 ... 我们把处理好的文件放到 dist 这个目录的下面 ...

这里还需要把 expand 设置成 true ... 这样才会去处理所有的图像

imagemin:{

            dist:{

                expand: true,

                src:'images/**/*.{png,jpg}',

                dest: 'dist/'

            }

            

        },

保存 ... 

image.png

image.png

image.png

回到命令行工具 ... 输入 

# grunt imagemin 

image.png

... 在它后面也可以加上具体的任务的目标 ... 不加的话,会执行这个任务里面的所有的目标 ...

完成以后 ... 会显示出最小化的图像数量(由上图3个) ... 还有节省的空间(由上图 831.72kB) ...

https://github.com/gruntjs/grunt-contrib-imagemin


来自  https://ninghao.net/video/1999#info


普通分类: