Skip to content

Latest commit

 

History

History
99 lines (67 loc) · 7.83 KB

技术分享.md

File metadata and controls

99 lines (67 loc) · 7.83 KB

##一、为什么要用前端构建工具

####1. JavaScript和CSS的版本问题
由于javascript和css是静态文件,如果地址不变的话,浏览器会缓存这些文件,那就意味着当我们去修改javascript或者css文件的时候,可能客户端是看不到的,因此很有可能在开发过程中,项目部署的时候即使有本地修改过,但是客户端并不会体现出来,所以需要给javascript和css添加一个版本,但是如果这个版本是手动的,那么很有可能被遗忘

####2. javascript和css依赖问题 说的通俗一点就是关于javascript和css在页面中引用的顺序问题,很可能出现css不起作用,javascript报异常,虽然我们可以使用requireJS来进行模块管理,但是依然很多情况下需要引入不同文件,尤其是css没有一个好的模块管理工具

####3. 性能优化 浏览器请求文件越多越耗时,请求文件越大越耗时,尤其是使用目前有MVC,MV*的框架时候,为了代码结构清晰,结构合理,需要很多js文件, 拖慢了网页的速度。为了解决这两件事,我们需要做两件事:

  1. 文件合并:浏览器可能需要下载多个js文件,但是浏览器是有并发限制的,也就是同时并发只能下载指定数量的文件,所以文件合并是弥补了浏览器并发限制的缺陷
  2. 文件压缩:文件越大,下载越慢,对于javascript文件中的空格 换行只是为了开发一目了然,对于机器来说并没有影响,所以通过去掉空格和换行实现文件压缩

####4. 效率提升

  1. vendor前缀: 自动给不同浏览器加上vendor前缀
  2. 单元测试: 构建工具跑一遍我们的单元测试时很重要的
  3. 代码分析: grunt-jshint,帮我们检查javascript代码中一些潜在的bug

##二、前端构建工具的相关介绍

####Grunt/Gulp介绍

  1. Grunt: 所有实际工作是由任务运行器来做的。命令行接口仅解析参数和传递任务运行器,通过需要安装任务运行器才能够实现Grunt构建项目。 工作方式:通过配置任务和加载任务来实现代码的构建(如:压缩,合并,代码检测等),在使用Grunt的I/O过程中会产生一些中间态的临时文件,一些任务生成临时文件,其它任务可能会基于临时文件再做处理并生成最终的构建后文件。

  2. Gulp: 同样也是通过任务运行器来完成代码构建的,需要对任务运行器进行安装。工作方式:在配置和加载任务的过程中,利用流的方式进行文件的处理,通过管道将多个任务和操作连接起来,因此只有一次I/O的过程,流程更清晰,更纯粹。

首先来看一下Grunt/Gulp的配置文件:

Grunt中被广泛运用的一些插件:

官方插件:

  1. grunt-contrib-concat:文件合并(介绍例子,其他的就不介绍)
  2. grunt-contrib-uglify:js文件压缩
  3. grunt-contrib-jshint:js代码检测
  4. grunt-contrib-watch:代码监控
  5. grunt-contrib-htmlmin:html代码压缩
  6. grunt-contrib-cssmin:css代码压缩
  7. grunt-contrib-uncss: 比较适合使用了bootstrap等框架开发,可以删除未使用的css

创建插件:

  方式一:按照官网的步骤(比较繁琐) http://www.gruntjs.net/creating-plugins

  方式二:如果只是想自己使用这个插件更方便的解决办法

  1. 阅读官方插件中的源码,知道grunt.registerMultiTask来注册自己的插件名
  2. 通过Grunt中loadTasks(相对于Gruntfile所有在目录)加载任务相关文件
  3. 再去使用Grunt中的API进行自定义任务的实现(演示自己的插件)

Grunt源码分析:摘自于http://bin-playground.top/#/

首先通过一张图来了解Grunt工作方式:

在谈Grunt工作方式之前,我们先了解一下grunt-cli是什么?

  grunt-cli是建立在grunt基础上的命令行工具,通过它可以很方便的使用grunt进行一些自动化任务,grunt-cli的处理过程分为以下几步:

  1. 加载必须的模块,包括grunt-cli内部模块和第三方模块
  2. 获取命令行参数执行相应的操作
  3. 查找Gruntfile.js文件并执行任务

了解完grunt-cli之后,接着我们看看从grunt输入命令到任务运行完毕整个过程都经过了哪些步骤?

  1. 首先,我们输入命令行之后调用require(gruntpath).cli()方法,在cli方法中会初始化命令行的默认参数列表,解析输入命令行的参数以及任务名称
  2. 然后调用grunt.tasks方法,将任务参数和名称传入。在grunt.tasks方法中,会进一步对参数进行解析,初始化log功能,如果参数带有version或者help选项那么直接执行相应的函数,否则就解析任务名称。开始调用init方法对Gruntfile.js进行解析,同时调用run,start方法对
  3. 接着调用task.init方法。加载Gruntfile.js文件,注册任务信息以及配置信息。
  4. 接着调用task.run方法。task.run方法并不会运行任务,而是把任务相关信息添加到任务队列中。
  5. 最后才是调用task.start方法来依次运行任务队列中的任务。

P.S. 在task.start方法中定义了一个nextTask方法,方法的作用是依次执行任务队列中的任务,从任务队列中取出任务对象,利用任务对象构建一个上下文对象,然后在这个上下文中执行任务的注册函数,执行完注册函数之后执行队列中的下一个任务。执行注册函数的功能有task.runTaskFn方法实现。在这个方法中定义了一个complele方法,会在任务注册函数执行完成后备调用,进行错误处理工作。同时在task.runTaskFn方法中还向上下文对象context中添加了一个async方法,这个方法就是当我们需要在任务中进行一些异步操作是首先需要调用的方法,调用这个方法之后会返回一个函数,这个函数会异步执行complete方法,如果没有async方法,那么在我们任务中的异步操作还未返回时,grunt内部就会调用complete方法,这样就会造成错误。有了async方法,我们就可以确保complete方法是在我们任务完成之后才被调用。

由于Grunt与Gulp在代码构建方面类似,在这里就不多的进行赘述。只要掌握四个API的用法就能够很快的写出gulpfile.js实现代码构建

Gulp源码分析:

gulp的四个接口分别来源于orchestrator,vinyl-fs模块,所以gulp特性都来自于这两个模块,orchestrator负责任务管理以及发布一些事件,vinyl-fs提供gulp的流式文件系统。

Orchestrator模块

var Orchestrator = function() {
	EventEmitter.call(this); //继承了EventEmitter对象
    this.doneCallback = undefined; // 当task里所有的任务完成时调用这个函数
    this.seq = []; // task以及task里依赖的执行顺序,(start里会有多个task,每个task又有可能有多个依赖,每个依赖又可能有多个依赖,所以需要保存其执行顺序)
    this.tasks = {}; // 任务对象,包括任务名,依赖,回调函数
    this.isRunning = false; // 表示当前是否在执行任务
}

Orchestrator利用seq这个队列数组存储需要执行的task,这样如果计算机有能力执行,它就从队列里取走一个,如果还有能力就再取走一个。就是以最大的并发能力来执行

同时通过继承EventEmitter对象,Orchestrator发布一些可订阅的事件

vinyl-fs模块

主要依赖于vinyl与glob-watcher。后者提供监视文件变化的watch接口, 前者则在file的基础上封装一些属性与方法,构造出独特的vinyl文件对象。 Gulp使用的是Stream,但却不是普通的Node Stream,而是基于vinyl对象的vinyl File Object Stream。