由来:
在软件开发过程中,模块化编程思想已经习以为常了,模块化编程不仅仅给开发团队带来效率方面上的好处,还能够让开发的项目或者产品维护成本大大降低。
那么,在WEB开发过程中JS脚本语言已经不可或缺了,通过JS脚本语言能够带来更加舒适的人机交互和用户体验。但是,JS脚本的使用过程中也会有出现引用依赖的混乱,那么JS脚本语言的模块化思想势必会得到大家广泛的认可,在这样的一个背景下,淘宝前端工程师玉伯带来了SeaJS脚本语言,让模块化编程思想进入到JS脚本的世界里。
特点:
SeaJS是一个遵循CommonJS规范的JavaScript模块加载框架,可以实现JavaScript的模块化开发及加载机制。与jQuery等JavaScript框架不同,SeaJS不会扩展封装语言特性,而只是实现JavaScript的模块化及按模块加载。SeaJS的主要目的是令JavaScript开发模块化并可以轻松愉悦进行加载,将前端工程师从繁重的JavaScript文件及对象依赖处理中解放出来,可以专注于代码本身的逻辑。SeaJS可以与jQuery这类框架完美集成。使用SeaJS可以提高JavaScript代码的可读性和清晰度,解决目前JavaScript编程中普遍存在的依赖关系混乱和代码纠缠等问题,方便代码的编写和维护。
SeaJS本身遵循KISS(Keep It Simple, Stupid)理念进行开发,其本身仅有个位数的API,因此学习起来毫无压力。在学习SeaJS的过程中,处处能感受到KISS原则的精髓——仅做一件事,做好一件事。
优势:从一个例子中来看SeaJS优势,
传统模式:
- var M1={
- run:function(){
- alert('M1');
- M2.run();
- }
- }
- var M2={
- run:function(){
- alert('M2');
- }
- }
- <script src="./M2.js"></script>
- <script src="./M1.js"></script>
var M1={ run:function(){ alert('M1'); M2.run(); } } var M2={ run:function(){ alert('M2'); } } <script src="./M2.js"></script> <script src="./M1.js"></script>
使用SeaJS之后:
- //init.js
- define(function(require, exports, module) = {
- var m1=require('M1');
- exports.init=function(){
- m1.run();
- }
- });
- //M1.js
- define(function(require,exports,module)={
- var m2=require('M2');
- exports.run=function(){
- alert('M1');
- m2.run();
- }
- });
- define(function(require,exports,module)={
- exports.run=function(){
- alert('M2');
- }
- });
- <script src="./sea.js"></script>
- <script>
- seajs.use('./init', function(init) {
- init.init();
- });
- </script>
//init.js define(function(require, exports, module) = { var m1=require('M1'); exports.init=function(){ m1.run(); } }); //M1.js define(function(require,exports,module)={ var m2=require('M2'); exports.run=function(){ alert('M1'); m2.run(); } }); define(function(require,exports,module)={ exports.run=function(){ alert('M2'); } }); <script src="./sea.js"></script> <script> seajs.use('./init', function(init) { init.init(); }); </script>
通过两个简单的实例能够看出使用SeaJS之后代码的模块化非常清晰,并且在HTML页面中仅仅引用一个./sea.js文件并且仅仅调用init即可,具体init后面实现的逻辑对用户是透明的。
通过这篇博客能够对SeaJS脚本语言有所了解,后面文章介绍利用SeaJS编写模块。
SeaJS简介二:模块编写
在介绍SeaJS模块编写和引用之前先介绍一下关于SeaJS模块化的使用原则。
使用SeaJS开发JavaScript的基本原则就是:一切皆为模块。引入SeaJS后,编写JavaScript代码就变成了编写一个又一个模块,SeaJS中模块的概念有点类似于面向对象中的类——模块可以拥有数据和方法,数据和方法可以定义为公共或私有,公共数据和方法可以供别的模块调用。另外,每个模块应该都定义在一个单独js文件中,即一个对应一个模块。
知道了这些之后我们再来学习模块的编写,载入和引用。
模块编写:
SeaJS中使用define函数定义一个模块。define可以接收三个参数:require, exports, module。
require——模块加载函数,用于记载依赖模块。
exports——接口点,将数据或方法定义在其上则将其暴露给外部调用。
module——模块的元数据。
关于模块的几种写法:
第一种是教科书式的写法,也是最常用的一种写法。
- define(function(require, exports, module) {
- var a = require('a'); //引入a模块
- var b = require('b'); //引入b模块
- var data1 = 1; //私有数据
- var func1 = function() { //私有方法
- return a.run(data1);
- }
- exports.data2 = 2; //公共数据
- exports.func2 = function() { //公共方法
- return 'hello';
- }
- });
define(function(require, exports, module) { var a = require('a'); //引入a模块 var b = require('b'); //引入b模块 var data1 = 1; //私有数据 var func1 = function() { //私有方法 return a.run(data1); } exports.data2 = 2; //公共数据 exports.func2 = function() { //公共方法 return 'hello'; } });
第二种方法是抛弃exports和module的方式:
- define(function(require) {
- var a = require('a'); //引入a模块
- var b = require('b'); //引入b模块
- var data1 = 1; //私有数据
- var func1 = function() { //私有方法
- return a.run(data1);
- }
- return {
- data2: 2,
- func2: function() {
- return 'hello';
- }
- };
- });
define(function(require) { var a = require('a'); //引入a模块 var b = require('b'); //引入b模块 var data1 = 1; //私有数据 var func1 = function() { //私有方法 return a.run(data1); } return { data2: 2, func2: function() { return 'hello'; } }; });
第三种方式:类似于JSON写法,其实就是没有方法的一种写法。
- define({
- data: 1,
- func: function() {
- return 'hello';
- }
- });
define({ data: 1, func: function() { return 'hello'; } });
通过这三种方式应该对模块的编写有所了解了,估计你已经迫不及待的想去写几个模块尝试一下了,那就快点来写吧。
SeaJS简介三:模块载入和引用
之前对模块有过介绍,一个模块对应一个js文件,而载入模块时一般都是提供一个字符串参数告诉载入函数需要的模块,所以就需要有一套从字符串标识到实际模块所在文件路径的解析算法。
SeaJS支持如下几种方式:
第一种:绝对路径--给出js文件的绝对路径
- require("http://example/js/a");
require("http://example/js/a");
第二种:相对路径--用相对调用载入函数所在js文件的相对地址寻找模块
- require("./c");
require("./c");
第三种:通过全局变量来匹配相对路径:相对SeaJS全局配置中的“base”来寻址(后面文章会介绍到)
SeaJS提供了三种载入的方式:seajs.use,require和require.async
第一种:seajs.use
seajs.use主要用于载入入口模块。入口模块相当于JAVA程序的main函数,同时也是整个模块依赖树的根。
这种方式有几种写法:
- //单一模式
- seajs.use('./a');
- //回调模式
- seajs.use('./a', function(a) {
- a.run();
- });
- //多模块模式
- seajs.use(['./a', './b'], function(a, b) {
- a.run();
- b.run();
- });
//单一模式 seajs.use('./a'); //回调模式 seajs.use('./a', function(a) { a.run(); }); //多模块模式 seajs.use(['./a', './b'], function(a, b) { a.run(); b.run(); });
一般seajs.use只用在页面载入入口模块,SeaJS会顺着入口模块解析所有依赖模块并将它们加载。如果入口模块只有一个,也可以通过给引入sea.js的script标签加入”data-main”属性来省略seajs.use。
来个小实例来说明一下:
- <!DOCTYPE HTML>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8">
- <title>TinyApp</title>
- </head>
- <body>
- <p class="content"></p>
- <script src="./sea.js" data-main="./init"></script>
- </body>
- </html>
<!DOCTYPE HTML> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>TinyApp</title> </head> <body> <p class="content"></p> <script src="./sea.js" data-main="./init"></script> </body> </html>
第二种:require
require是SeaJS主要的模块加载方法,当在一个模块中需要用到其它模块时一般用require加载:ar a = require('a'); //引入a模块
第三种:require.async
之前的require方式是一次把所有依赖的JS文件都加载进来,如果想什么时候用到什么时候加载的话就会用这种方式,这种方式效率比require高一些。
通过上面的介绍,应该对SeaJS的模块载入和引用有所了解了,其实者通过这几篇的介绍会发现SeaJS的模块化编程思想和它带给开发人员的简单易用的特点。下篇博客介绍一下SeaJS的全局配置。
SeaJS简介四:全局配置
SeaJS提供了一个seajs.config方法可以设置全局配置,接收一个表示全局配置的配置对象。具体使用方法如下:
- seajs.config({
- base: 'path/to/jslib/',
- alias: {
- 'app': 'path/to/app/'
- },
- charset: 'utf-8',
- timeout: 20000,
- debug: false
- });
seajs.config({ base: 'path/to/jslib/', alias: { 'app': 'path/to/app/' }, charset: 'utf-8', timeout: 20000, debug: false });
如果在JS脚本中引入模块jquery的话,var $ = require('jquery');那么这时候在上篇博客中说道的引入模块的方式之三就可以用全局配置来操作了。
在全局配置中base表示基址寻址时的基址路径。base设置为 http://example.com/js/3-party/ ,那么就会载入base路径下的juery模块。
另外的一些属性的用途为:
alias可以对较长的常用路径设置缩写。
charset表示下载js时script标签的charset属性。
timeout表示下载文件的最大时长,以毫秒为单位。
debug表示是否工作在调试模式下。
通过这样的一个全局配置,开发者能够很轻松的将一些很长很繁琐的公用的路径等来提取到全局配置中,减轻了开发者的繁琐的CV(复制、黏贴)操作。
到这里,关于SeaJS简介就介绍完了,我们会发现这个脚本语言模块化的特点非常突出,并且语言本身比较简单并且上手容易。