前端自动化 模块化开发

前端自动化 模块化开发

Grunt


为什么Grunt

在前端开发的过程中,我们经常会做一些跟功能、代码无关的重复的事情,比如复制、粘贴、压缩等等,我们需要有一个前端自动化的工具。


什么是Grunt

免费的小秘书 Grunt官网


怎么用Grunt

  • 前提:node npm
  • 安装grunt命令行工具 npm install -g grunt-cli
  • 在工作目录安装grunt npm install grunt --save-dev
  • 编辑Gruntfile.js文件
  • 在命令行执行grunt命令

关于Gruntfile.js的内容

格式如下,三步上篮:

module.exports=function(grunt){//包裹函数,所有代码都在这里
    grunt.initConfig({});//1配置任务内容
    grunt.loadNpmTasks();//2加载插件
    grunt.registerTask();//3注册任务
}

initconfig——配置
可以有多个插件名,每个的插件名-加载插件名-注册任务中执行的插件名字要一致。
每个插件名可以有多个任务描述,默认全部执行,也可以指定执行某些任务。

grunt.initConfig({
	插件名1: {
		任务描述1: {
			//描述xxx
		},
		任务描述2: {
			//描述xxx2
		}
	},
	插件名2: {
		任务描述3: {
			//描述xxx3
		}
	}
});
比如:
uglify: {
	main: {
		//描述
		src:	'1.js',//源文件
		dest:	'1.min.js'//目标文件
	}
}

loadNpmTasks——引入插件

grunt.loadNpmTasks('grunt-contrib-uglify');
关于grunt的插件。。。

registerTask——注册任务

grunt.registerTask('default', ['uglify']);

关于执行grunt命令

执行的是registerTask注册的任务

Tips:

  • 插件基本都能自动创建文件夹
  • 重复执行grunt的话,已经存在的文件会被覆盖,不报错,不在后面添加。
  • 通配符 *号 代指所有
  • 通配符 **号 代指所有,包括没有
  • cwd 描述想要作为当前文件夹的文件夹路径
  • expand,分开成一个个的文件,uglify可以压缩文件到一个文件,cssmin不可以
  • ext参数,设置后缀名
  • grunt中的模板 <%= grunt.template.today("yyyy-mm-dd HH-MM-ss") %>, 也可以放一个常量json进来可以在模板中使用,避免重复写,也便于更改
    结合<%= %>使用模板
  • grunt输出 grunt > log.txt
  • concat 连接起来,不压缩,生成的是一个文件【注意: 顺序未知】
  • grunt.file上面有基于nodeJs的fs包装好的操作文件的方法,比如:grunt.file.readJSON()通常用来读取json文件,直接转化为可用的json
  • copy 带文件夹层级复制,复制多个文件时,要有expand,否则会报错
  • watch —— 监测,(自动化)

AMD和CMD

WHY——模块化编程 vs 原始代码

转自阮一峰的博客:最早的时候,所有Javascript代码都写在一个文件里面,只要加载这一个文件就够了。后来,代码越来越多,一个文件不够了,必须分成多个文件,依次加载。下面的网页代码,相信很多人都见过。

<script src="1.js"></script>
<script src="2.js"></script>
<script src="3.js"></script>
<script src="4.js"></script>
<script src="5.js"></script>
<script src="6.js"></script>

这段代码依次加载多个js文件。随着代码越来越多,当依赖关系很复杂的时候,代码的编写和维护都会变得困难。就算是代码作者本人,在后期维护起来也会越来越吃力,更不敢想多人合作了。

在前端开发过程中,经常会出现一个单文件几千行甚至近万行的情况。这种大文件,对协作开发、后续维护、性能调优等都不利。模块化开发初衷是帮助前端开发工程师将大文件拆分成小文件,能保持小颗粒度的模块化开发,同时不需要过多考虑依赖关系,让依赖管理轻松自如,将更多精力聚焦在代码本身的逻辑上。

HOW——AMD和CMD之争

先分开介绍,再对比
CMD seaJs 玉伯
玉伯(王保平),淘宝前端类库 KISSY、前端模块化开发框架SeaJS、前端基础类库Arale的创始人。
玉伯


CMD规范的定义

  • 懒加载——Execution must be lazy.
  • 一个模块就是一个文件,代码的书写格式如下:define(factory);
  • define 是一个全局函数,用来定义模块。
  • define 接受 factory 参数,factory 可以是一个函数,也可以是一个对象或字符串。
  • factory 为对象、字符串时,表示模块的接口就是该对象、字符串
  • factory 为函数时,表示是模块的构造方法。执行该构造方法,可以得到模块向外提供的接口。factory 方法在执行时,默认会传入三个参数:require、exports 和 module:
```
define(function(require, exports, module) {//参数的顺序必须是这样的
  // 模块代码
  //require是获取其他模块的函数
  //exports用来输出当前模块
  //module用来输出当前模块
  //exports 仅仅是 module.exports 的一个引用。在 factory 内部给 exports 重新赋值时,并不会改变 module.exports 的值。因此给 exports 赋值是无效的,不能用来更改模块接口。
});
```
  • 经常使用的 API 只有 define, require, exports, module.exports而已,简单明了

seaJs的用法

最简单的seaJs案例

//主html
<!doctype html>
<html>
    <head>
        <script src="../sea-modules/seajs/seajs/2.2.0/sea.js"></script>
        <script>
            seajs.use("main");//main是main.js,是用define定义的模块
        </script>
    </head>
    <body>
    页面内容
    </body>
</html>
//main.js
define(function(require) {
    alert(1);
});
  • 主文件配置参数 seajs.config(); 官方介绍
    seajs.config({//paths 配置可以结合 alias 配置一起使用,让模块引用非常方便。 // 设置路径,方便跨目录调用 paths: { 'arale': 'https://a.alipayobjects.com/arale', 'jquery': 'https://a.alipayobjects.com/jquery' }, // 设置别名,方便调用 alias: { 'class': 'arale/class/1.0.0/class', 'jquery': 'jquery/jquery/1.10.1/jquery' } });
  • 主文件使用模块:seajs.use();——头(or衣领)API介绍
    // 加载一个模块 seajs.use('./a'); // 加载一个模块,在加载完成时,执行回调 seajs.use('./a', function(a) { a.doSomething(); }); // 加载多个模块,在加载完成时,执行回调 seajs.use(['./a', './b'], function(a, b) { a.doSomething(); b.doSomething(); });
  • 如何定义模块
    用define来定义模块。Sea.js 推崇一个模块一个文件,遵循统一的写法:
    define(function(require, exports, module) { // 模块代码 // exports 被require的对象上的属性或方法 // 对外提供 foo 属性 // 在外部使用:require(xx).foo exports.foo = 'bar'; // 对外提供 doSomething 方法 // 在外部使用:require(xx).dosomething exports.doSomething = function() {}; //********************// // module.exports 就是被require的对象 module.exports = { name: 'a', doSomething: function() {}; }; });
  • 如何引用模块
    require 用来获取指定模块的接口。
    define(function(require, exports, module) { // 获取模块 a 的接口 var a = require('./a'); // 调用模块 a 的方法 a.doSomething(); });
  • 总结:用define来定义模块,一个模块就是一个文件,在模块里用require来引用其他模块,在主html文件里引入seajs后用seajs.use()来开启js文件。

AMD requireJs

AMD规范的定义

  • 全称 Asynchronous Module Definition——异步模块定义规范。
  • 模块和模块的依赖可以被异步加载。
  • 模块的依赖被异步加载完成以后,工厂函数才会执行。
  • define 是一个全局函数,用来定义模块。
  • define 可以接受多种格式的参数,不过通常可以是:
  • define(依赖的数组,工厂函数);
  • define(工厂函数);
  • define(一个对象);
  • 一个模块就是一个文件

requireJs的用法

最简单的requireJs案例

//主html
<!doctype html>
<html>
    <head>
        <script src="js/require.js"></script>
        <script>
            require(['jquery'], function($){
                alert('jquery loaded');
            });
        </script>
    </head>
    <body>
    页面内容
    </body>
</html>

or:

//主html
<!doctype html>
<html>
    <head>
        <script src="js/require.js" data-main="js/main.js"></script>
    </head>
    <body>
    // 这样写,会异步加载data-main指定的js文件,并把data-main所在的路径作为config配置的baseUrl。如果不想异步,就像上面一样写。
    页面内容
    </body>
</html>
//main.js
require(['jquery'], function($){//会在jquery模块执行完成后,把jquery的输出做为参数传入后面的工厂函数,并执行工厂函数
    alert('jquery loaded');
});
  • 配置: 可以使用require.config来设置依赖的模块的名称和对应的路径,也可以在引入require.js之前定义一个名字为require的json。两种方式都可以进行配置。; requireJs配置官方介绍
    <script src="scripts/require.js"></script> <script> require.config({ baseUrl: "/another/path", paths: { "some": "some/v1.0" } }); require( ["some/module", "my/module"], function(someModule, myModule) { //..... } ); </script> 或者是: <script> var require = { baseUrl: "/another/path", paths: { "some": "some/v1.0" } }; </script> <script src="scripts/require.js"></script>
  • 主文件如何使用模块:从上面已经可以看到,不同于seaJs的使用seajs.use()函数,requireJs使用require()函数来开始执行。
  • 如何定义模块
    用define来定义模块:常见的3种情况
  • define(依赖的数组,工厂函数);
```
define(['aModule','bModule'],function(a,b) {
  // 模块代码
  // 这个定义的模块在被调用时,aModule和bModule模块加载完成之后,才会以他们的返回值作为参数执行工厂函数
});
```
>> * define(工厂函数); [语法糖](http://requirejs.org/docs/whyamd.html#sugar)
```
define(function(require, exports, module) {
  // 模块代码
  // 这种方式其实是requireJs的语法糖,可以在函数里面用变量接受require过来的模块(就像seaJs做的那样),并不真的是同步require那个代码,而是用的Function.prototype.toString()转换成数组里面的格式(怎么转换的咱们不管)后异步下载后执行回调。
});
```
>> * define(一个对象);
```
define({
    color: "black",
    size: "12"
});
```
  • 如何引用模块
    用require来获取指定模块的接口(常见有2种):
  • require(依赖的数组,工厂函数);
```
require(['aModule','bModule'],function(a,b) {
  // 模块代码
  // aModule和bModule模块加载完成之后,才会以他们的返回值作为参数执行工厂函数
});
```
>> * require(依赖的数组);
```
//只是用来做一个头,异步引入js后自己不用再做其他事情
require(['aModule','bModule']) ;
```
  • 总结:
    用define来定义模块,一个模块就是一个文件。
    define新模块时用数组提前声明依赖的模块。
    在主html文件里引入requirejs后用require()来开启js文件。

RequireJS 与 SeaJs 的异同

共同点:

  • 都是模块加载器,倡导模块化开发理念
  • 核心价值是让 JavaScript 的模块化开发变得简单自然

不同点:

  • RequireJS是异步加载模块,SeaJs是同步加载模块
  • RequireJS是提前声明并加载依赖,SeaJs是按需加载依赖模块

我们目前使用的是 RequireJS。

posted @ 2015-07-30 10:36  刘henry  阅读(651)  评论(0编辑  收藏  举报