模块代码Javascript模块化编程(三):模块化编程实战,试用SeaJS

新手发帖,很多方面都是刚入门,有错误的地方请大家见谅,欢迎批评指正

    看了阮一峰教师的关于JavaScript模块化的文章后,解答了我思考良久的问题,然突有种朗开然豁的感到。后来解了到SeaJS,就想写篇文章,践实一下模块化编程。天今把文章写出来了。发出来,望希对大家有效。

    

本系列目录

    

    1. “JavaScript模块化编程(一):模块原型和论理观点详解”
    2. Javascript模块化编程(二):模块化编程实战,require.js详解

    第三篇文章的两个“引子”

    

    1. 给哥三十五次机遇,哥能就料中你的手机号
    2. '猜手机号游戏'的源码分析:二分查找+面向象对
  1. Javascript模块化编程(三):模块化编程实战,试用SeaJS

 

    前段时光转载了阮一峰教师的两篇解讲Javascript模块化编程的文章: “JavaScript模块化编程(一):模块原型和论理观点详解”,分析了Javascript模块原型和论理观点;Javascript模块化编程(二):模块化编程实战,require.js详解,分析了在实战中,如何利用RequireJS库,行进模块化编程。

    在这两篇文章布发出来后之,在和网友的交流探讨中,解了到了SeaJS,这个由国人玉伯自己建创的模块化编程库。然后,我就想习学习学, 再写篇文章给大家分析一下。

    

背景分析

    官网的料资是最靠谱的。在SeaJS的官网上现发,有一个“5分钟上手SeaJS”的例子,然后就从这个例子的开始习学。不过,只看白了明个六六七七。我没看白明和我平常的JavaScript编程有啥别区。另外,我没有着手践实,心里头不实踏。所以,着手写个序程,玩味一下。后来,就想到了“猜手机号游戏”!

    由于官网经已有应用SeaJS的教程,我就不重复这方面的务任了,而且我也得觉我我定肯没有官网写的好。由于我不清楚,应用SeaJS行进“模块化编程”和我平常不行进“模块化编程”的别区。所以,我预备从另外一个角度来分析SeaJS:将一个没有行进模块化编程的序程,改革成应用SeaJS行进“模块化编程”的序程。由于这个设法的跨度比大较,信息量也较比多。所以我把我的设法织组成了三篇文章:第一篇文章,“给哥三十五次机遇,哥能就料中你的手机号”,通过一个小游戏,来吸引大家的趣兴;第二篇,“‘猜手机号游戏’的源码分析:二分查找+面向象对”,来解讲在没有行进模块化编程时,序程的实现细节;然后,这是第三篇,在没有行进模块化编程的基本上,将来原的序程改革成一个应用SeaJS行进模块化编程的例子。

    在浏览这篇文章之前,请浏览前两篇,尤其是“'猜手机号游戏'的源码分析:二分查找+面向象对”。同时,还提议浏览一下“JavaScript模块化编程(一):模块原型和论理观点详解”Javascript模块化编程(二):模块化编程实战,require.js详解,范规、系统一下关于Javascript模块化编程的知识。

    

CMD模块义定范规分析

    想享用模块化编程带来的精良封装,就必须遵守模块化编程的范规。在 SeaJS 中,有所 JavaScript 模块都遵守 CMD(Common Module Definition) 范规。该范规定确了模块的基本誊写式格和基本交互式方。所以,应用SeaJS之前,必须浏览一下SeaJS所要求遵守的范规。

    鉴于范规盖覆的货色较比多,看多了头大。所以,我把这个范规提炼简化一下,只存眷我们须要用到的。至于,更细详的CMD模块义定范规,等先把例子跑通,晓得了全部程流,然后再回头看范规,梳理、范规这分部知识。

    在分析简化版范规之前,D瓜哥提两个或许大家都回“疑惑”的问题:

    

  1. 如何义定模块?
  2. 如何获得外部依附的模块?

    CMD模块义定范规中的主要内容恰是答复这两个问题。面下请看经D瓜哥简化的范规如下:

    

    1. 义定、封装模块的法方。(CMD模块义定范规中有多好义定法方。单简起见,现在只虑考应用如下这一种式方。)如下:

    define(function(require, exports, module) {// The module code goes here});

    这里须要别特说明一下,向参数传递的三个参数名必须按照代码所示中那样写,不能简写,或者应用其他字符串替代;同时,在数函内,exports不能被改写成其他值;可以把exports成看象对加添性属,如exports.key,然后对其复制,又如exports.key = "dValue"。

    

    1. 对外供给模块接口。在上一步中,我们在数函内义定了模块,但是这是在数函内义定的,在数函外部不容易问访到。该怎么向外供给模块接口呢?义定法方如下:

    define(function(require, exports, module) { // 适用这类式方向外供给模块接口 module.exports = { foo: 'bar', doSomething: function() {}; }; // 或者。由于,D瓜哥将模块封装成了一个象对,所以,本例中,应用这个式方。 module.exports = yourFunctionName; });

    传给 factory 构造法方(就是define(function(){})法方参数中那个数函,称为factory。数函只是factory的一种情势,其他情势后以再弥补。)的 exports 参数是 module.exports 象对的一个引用。只通过 exports 参数来供给接口,时有没法足满开发者的有所求需。 比如当模块的接口是某个类的例实时,须要通过 module.exports 来实现。D瓜哥这里就是一个象对,所以只能应用module.exports 。

    

    1. 获得外部依附模块。模块义定要了,须要应用的时候,以可就应用require数函获得外部依附。体具代码如下:

    define(function(require, exports, module) { // 应用require数函获得外部依附 var a = require('./a'); a.doSomething();});

    require数函的参数是a.js件文的相对路径。后缀名可以省略,在SeaJS加载模块的时候会主动加上的。另外,这里可以执行回调数函。不过,我们的务任是跑起来。因为不须要回调数函,所以这分部先略过了。

    结总一下:define数函,义定模块;module象对,保存模块信息;require数函,获得外部依附模块。

    看到这里,估计大家还是一头雾水。系关没,渐渐往下看,面下的例子跑起来的时候,你再回头看就会白明的。

    

模块化改革

    先声明一下,面下的改革进程会参考“5分钟入门”的说明。所以,提议大家先看看。当然,起一看也可以。

    通过看"5分钟入门"的例子可以看出,SeaJS的目录结构还是有点庞杂的。所以,最单简的法方就是,把她的例子载下上去,在她的基本之上修改:"5分钟入门"例子载下

    

目录结构

    载下实现后,解压到意任目录下。请看一下目录,

    

  1. hello-seajs/下放我们的html件文;
  2. hello-seajs/assets/sea-modules下放存的是我们须要用到的第三方模块块;
  3. hello-seajs/assets/main,这个目录可以说最主要,是放存我们自己编写的JavaScript和CSS件文的地方。面下还有四个子目录及一个件文:
    1. src放存畸形的代码;
    2. test放存测试代码;
    3. docs放存文档;
    4. examples放存示例代码;
    5. package.json是打包的配置件文;

    

“改革”模块代码

    面下,我们开始改革我们的模块。

    首先,把我GuessNumber.js放到hello-seajs/assets/main/src/下。然后,按照“第1条范规”的要求改革这个件文中代码。由于全部件文就是GuessNumber象对的义定。同时,这个JavaScript件文又没有引用其他模块。所以,只须要在件文的第一行加增define,在最后一行加增括号分号就行。体具代码如下:

    define(function(require, exports, module){/*** numberScope 须要猜想的字数范围*/function GuessNumber(numberScope){ // 为了凸起修改的代码,我把一些同相的代码省略了, // 完全代码请看:http://www.diguage.com/archives/80.html }GuessNumber.prototype = { constructor: GuessNumber,// 完全代码请看:http://www.diguage.com/archives/80.html}});

    其次,现在我们经已义定为一个模块。但是外部如何问访这个GuessNumber?所以,我们要向外部供给一个接口,供给式方参考“第2条范规”。体具代码见第18行:

    define(function(require, exports, module){/*** numberScope 须要猜想的字数范围*/function GuessNumber(numberScope){ // 完全代码请看:http://www.diguage.com/archives/80.html }GuessNumber.prototype = { constructor: GuessNumber,// 完全代码请看:http://www.diguage.com/archives/80.html}module.exports = GuessNumber; });

    这时,一个接口经已全体义定实现。面下,我们誊写调用这个模块的例子。

    在“范规”的第三条中,我们说了明加载外部依附模块的法方,我们只须要按说明照做就行。另外,还须要弥补一下模块加载时须要意注的地方。体具请看代码注释:

    define(function(require) {// 这是引入jQuery库类,我们面下说明为什么这样下。 var $ = require('jquery');// 引入GuessNumber模块,也就是GuessNumber.js件文。// 参数中传递的是GuessNumber.js件文的相对路径。// .js的后缀名可以省略,SeaJS在加载的时候会主动加上。var GuessNumber = require("./GuessNumber"); // 完全代码请看:http://www.diguage.com/archives/80.html//式格化示显结果function formatResult(num, type) {//……}// ……$("#initButton").click(function(){guess.start(scopeArr[type].min, scopeArr[type].max);showResult();});});

    从面上的代码中,可以看出,main.js件文的改革,只是把来原的

    $(document).ready(function(){// 主要的业务代码});

    改革成了,

    define(function(require) {// 这是引入jQuery库类,我们面下说明为什么这样下。 var $ = require('jquery');// 引入GuessNumber模块,也就是GuessNumber.js件文。// 参数中传递的是GuessNumber.js件文的相对路径。// .js的后缀名可以省略,SeaJS在加载的时候会主动加上。var GuessNumber = require("./GuessNumber"); // 和原件文同相的业务代码});

    每日一道理
只有启程,才会到达理想和目的地,只有拼搏,才会获得辉煌的胜利,只有播种,才会有收获。只有追求,才会品味堂堂正正的人。

    另外,加了两行倒入必要关联模块的代码。仅此而已。

    main.js与GuessNumber.js不同的还有一点,main.js不须要向外供给问访接口。这点也要意注一下。

    到这里有所的JavaScript都经已修改终了了。面下,我们修改一下如在何HTML中的引入式方。

    

在页面中加载模块

    来原的写法是,按序顺应用<scrip>签标把jQuery、GuessNumber.js以及main.js件文引入到HTML页面中便可。如果应用SeaJS,则须要先加载SeaJS的库类,然后应用JavaScript通过SeaJS的接口来加载所需的模块,也就是模块对应的JavaScript件文。体具代码如下:

    <!-- 首先,首先我们须要引入 sea.js -->

    <script src="assets/sea-modules/seajs/1.3.1/sea.js"></script>

    <script type="text/javascript">

    seajs.config({

    alias: {

    // 指定应用的jQuery本版以及说明jQuery的路径

    // 请意注:这里名知了jQuery的路径,所以,我们

    // 在引入jQuery库时,只须要填写jquery便可。

    'jquery': 'gallery/jquery/1.8.2/jquery'

    }

    });

    // 然后SeaJS通过 use 法方来加载模块,后以打包后也是修改这里

    // 或许你会问疑为什么不加载GuessNumber.js件文,

    // 这个在应用require引入依附时,SeaJS主动加载须要的外部件文

    // 另外,这里的.js后缀名也可以省略,SeaJS会主动补全。

    seajs.use('./assets/main/src/main');

    </script>

    <!-- 这里只展示了和JavaScript引入相干的代码 -->

    <!-- 完全代码请看:http://www.diguage.com/archives/80.html 中的HTML代码 -->

    到此,改革务任就全体实现了。你可以打开一下inde.html件文,看看效果了。

    

打包署部

    根据“高性能网站的十四条黄金则法”中的践实,我们在现实目项上线时,为了进步页面的加载速度,必定要压缩一下JavaScript件文。这些,SeaJS也虑考到了,甚至做得更好:还做了件文合并。

    这里,须要先分析一下,SPM,一个基于命令行的前端目项管理具工。 SPM 和 SeaJS 关系密切,你甚至可以以为SPM是为SeaJS专门打造的具工。首先,请“安装教程”安装好这个具工。按照进程可能会有一个问题,请参考面下的“涌现的问题”。

    应用SPM打包,须要修改一下打包的配置件文。配置件文是:hello-seajs/assets/main/package.json。打开后内容如下:

    { "name": "main", "version": "1.0.0", "dependencies": { "jquery": "gallery/jquery/1.8.2/jquery" }, "root": "hello-seajs", "output": { "main.js": ".", "main.css": "." }, "spmConfig": { "build": { "to": "../sea-modules/{{root}}/{{name}}/{{version}}" } }}

    不过,这个须要根据我们的现实情况来修改。root性属,由于我们的模块是“猜数”,所以将其修改成GuessNumber;output性属,我们只须要输出JS,所以删除main.css。另外,须要意注,第十四行,这个是打包后的输出路径。好了,开始打包。打包须要执行如下令指:

    $ cd hello-seajs/assets/main$ spm build ...BUILD SUCCESS!$

    打包结束后,在hello-seajs/assets/中就会现发多了一个GuessNumber件文夹,那个就是打包输出出来。

    这里说明一下:D瓜哥只在Linux下执行了这么命令。不知在Windows否是好使。为了便方大家测试运行,打包结果已提交,载下的代码中包括打包结果。

    察观这个结果,大家会现发只有一个main.js和main-debug.js;望文生义,main.js是用于产生署部的,经过压缩的件文;main-debug.js是为测试应用的,只是合并了代码并没有压缩,应用的时候直接引用这个两个件文中的一个就行,直接把seajs.use()中的路径改一下就OK。GuessNumber.js哪里去了啊?大家可以打开main-debug.js看看(main.js也行,只是压缩过去,可读性好不),来原,GuessNumber.js经已合并到了main.js中了。SPM把两个件文合并成一个件文了,这样在浏览器问访网页时,以可就少减一个HTTP求请,进步网页的加载速度。

    另外,大家也可能会意注到在来原main.js中义定的define()数函,在新的main.js有了一些化变,多了两个参数:第一个参数模块的ID,主要是为了便方别区一个件文中的各个模块;第二个参数是模块依附的外部模块的路径,因为依附的模块可能有多个,所以这个参数是一个数组。第三个参数是来原的function,也就是factory。更细详的解释请看:为什么要用 spm 来压缩 CMD 模块?

    懒人要把懒行进到底!打包后还要修改SeaJS的加载路径,这点其实还可以应用如下代码来防止:

    // 这个路径只有在署部到服务器上才行,直接打开件文好不使。seajs.use(location.host === 'localhost' ? './assets/main/src/main' : 'GuessNumber/main/1.0.0/main');

    如果长短态静页面,也可以应用量变来配置。

    

折腾中涌现的问题

    折腾这么个玩意,免难涌现一些问题,D瓜哥碰到了三个问题。这些问题主要中集在SPM境环搭建进程中。给大家分享一下。

    第一个问题:按照seajs时,示提info.json不存在的错误。端终示显如下:

    d@dPC:~/Dev/hello-seajs/assets$ spm install seajsStart installing ...success create global config.json to /home/d/.spmDownloading: http://modules.spmjs.org/info.json[ERROR] Caught exception: Error: not found config http://modules.spmjs.org/info.json

    大家可以在浏览器地址中打开http://modules.spmjs.org/info.json,会现发可以打开。这是怎么回事呢?

    我查阅了一下SeaJS论坛,里头有类似的问题。其中的一个复兴,我拿过去作当解答吧:这段时光是举国同庆的日子,网络不稳定。至于原因,你晓得。估计等过了这段时光就没事了。所以,既然浏览器可以问访,则内容以可就问访到。碰到这个问题,多试两次以可就了。

    第二个问题:按照jquery库时,示提Error: ALREADY_EXISTS。端终示显如下:

    d@dPC:~/Dev/hello-seajs/assets$ spm install gallery.jqueryStart installing ...Downloading: http://modules.spmjs.org/gallery/info.jsonDownloaded: http://modules.spmjs.org/gallery/info.jsonDownloading: http://modules.spmjs.org/gallery/jquery/1.8.2/jquery.tgzDownloaded: http://modules.spmjs.org/gallery/jquery/1.8.2/jquery.tgz** This module already exists: /home/d/Dev/hello-seajs/assets/sea-modules/gallery/jquery/1.8.2Turn on --force option if you want to override it.[ERROR] Caught exception: Error: ALREADY_EXISTS

    其实,问题正如反馈信息所示,jQuery库经已存在,不须要再次载下了。我们在hello-sea这里例子的源代码中构建,这个源代码中经已包括了jQuery了,在这里这步可以疏忽。

    第三个问题:修改了package.json后,重新编译报错。端终示显如下:

    [WARN] http://modules.spmjs.org/GuessNumber/config.json null

    这个不影响编译,直接疏忽了行就。另外说明一下,在第一次打包时,没见这个错误;第二次会涌现。

    

代码载下

    为了便方大家载下代码,我把代码托管到了Github上,大家可以去Github上载下、提交您的修改。Github页面:GuessNumber;不想去Github上载下的,也可以直接点击载下:点击载下

    

入深习学

    面上的例子只是简要把一个例子跑起来了,给大家一个较比抽象的认知。但是,这个例子实在是太单简了。我还需弥补我们刚才为了易于晓得而简化的一些知识。为了更入深的解了SeaJS,请续继浏览“SeaJS 应用文档”。另外,这里有几个须要重点浏览,体具如下:

    

  1. CMD模块义定范规
  2. require 誊写定约
  3. 模块标识
  4. API 速快参考
  5. 模块的加载动启,重点看里头的"最佳践实"
  6. 模块系统

    把这个列表中的货色看完,SeaJS的习学该应以可就师出了。有好的料资请给我推荐,我再弥补上来。

    

遗留问题

    经过面上这些折腾,我们经已胜利运行起来一个应用SeaJS行进模块化编程的例子。但是,我们还是有很多的问疑。体具问疑如下:

    

    1. D瓜哥在main.js中,并没有应用$(document).ready();等DOM加载完再运行,并也没有讲JS放到HTML件文的最后,为啥还能序顺执行呢?莫非SeaJS有什么部内机制,保障在DOM加载实现后再执行我们自己编写的JavaScript代码?
    2. 这里例子很小,并没有很多很多的模块。在模块很多的情况下,如果织组模块?这个还须要写更多的例子,试验一下。
    3. 样同,在很多模块的情况下,难道要建很多目录预备很多的main.js,让多众的HTML别分加载吗?

    刚刚D瓜哥开窍了一下,main.js只是一个例子,可以根据自己的件组名称定名,然后在件组中加载相对应的JavaScript件文便可。另外,在配置package.json时,然突得觉,在/assets/main/src/下每一个目录该应算是一个模块,都有一个打包的配置件文package.json,用于配置该模块的必要信息。不知这样晓得否是确正?这个还有待证考。

    

未完待续

    这篇文章只是初略地让大家认知一下SeaJS。要想更入深地解了SeaJS的道理,D瓜哥得觉最靠谱的法方就是自己实现一个SeaJS。所以,下一篇,D瓜哥预备自己着手实现一个简化版的SeaJS。当然,为了便于晓得SeaJS,D瓜哥的实现会参考“CMD模块义定范规”来编写代码。敬请期待!

    PS:

    这篇文章代码较比多,排版整的好不。看着不是很爽,若有好的提议,请留言提出,D瓜哥立马改良。感谢!

 

    参考料资:

    参考料资在文章都涌现了,这里就不再赘述了。

    吐槽一下:

    我费老大劲用SyntaxHighlighter给代码排出来的、很漂亮的版,到“博客园”子下一都好不使了。只能应用pre块替代了。望希“博客园”能持支SyntaxHighlighter,SyntaxHighlighter真的很好很大强!

    本文章,表发在博客园的同时,也布发到我的个人博客瓜地哥上。转载请注明作者和原文网址。
瓜地哥:http://www.diguage.com/archives/82.html

文章结束给大家分享下程序员的一些笑话语录: 一个合格的程序员是不会写出 诸如 “摧毁地球” 这样的程序的,他们会写一个函数叫 “摧毁行星”而把地球当一个参数传进去。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值