zoukankan      html  css  js  c++  java
  • 转:Why SeaJS

    原文地址:http://chaoskeh.com/blog/why-seajs.html

    Why SeaJS

    前言

    本文主要面向刚接触 SeaJS 的同学。
    文章会先提出传统 Javascript 开发上遇到的一些难以解决的问题(即“冲突”与“依赖”两节),然后介绍如何使用 SeaJS 来解决这些难点(即 “Why SeaJS” 一节)。
    实际上,如果你想了解 RequireJS 等其他模块加载器,也可以阅读本文

    冲突

    我们从一个最简单的例子开始
    以前我做项目时,常常会将一些通用的、底层的功能抽出来,独立成一个函数,比如

    function print(str) {
        // 代码!
    };
    

    然后像模像样的将这个函数丢到 util.js 里面,最后告诉大家:你们想用 xx 功能时候,引入 util.js 就行啦
    这是一个很好的习惯(嘻嘻),但是项目做久了,难免会遇到问题,比如常有人问我:

    • 你这个函数为啥叫 print 啊!正好我也刚写了个函数叫 print 啊!要不我改名叫 print2 吧!(我靠……)
    • 你这个函数为啥叫 print 啊!我引入了个开源的模块,里面也有个函数叫 print 啊!改人家的怕有问题,要不改你的吧!(我再靠……)

    在没有命名空间的 Javascript 中,发生这样的情况一点都不奇怪,幸亏有补救方案,可以用对象模拟命名空间

    var FocusTech = {};
    FocusTech.print = function(str){
        // 代码!
    }
    

    这样需要调用这个函数时,得带上命名空间 FocusTech.print(‘我是字符串啊’) 
    虽然麻烦一点,不过能降低函数命名冲突的概率,可惜也只是降低而已
    比如这里我选择了用公司名字作为命名空间,但是我们公司几百号研发人员,跟我一样想法的人肯定大有人在
    所以命名空间依然有可能会冲突。那么为了继续降低冲突概率,只好拉长命名空间,比如学 Java 用项目的网址做命名空间
    下面这段代码节选自 Yahoo! 的一个开源项目

    if (org.cometd.Utils.isString(response)) {
        return org.cometd.JSON.fromJSON(response);
    }
    if (org.cometd.Utils.isArray(response)) {
        return response;
    }
    

    看到这里我其实还是挺同情他们的,为了避免冲突用了那么长一个命名空间,对记忆和书写的负担实在太重了
    好了,冲突的问题暂且放着,我们继续往下看

    依赖

    继续讲简单的例子,我开始编写一个通用的展示组件,提供给项目组使用,这样其他同学就不用重复造轮子了
    我告诉大家:组件写在 componet_one.js 中,你们要用的时候引入一下就行啦
    于是另一位同学就这么做了:

    <script src="componet_one.js"></script>
    <script>
      FocusTech.ComponetOne.init();
      // 代码!
    </script>
    

    看上去很好,可是报错了!
    我赶紧查找原因,发现原来是我的组件中,调用了上面一个例子中提供的 print 方法,而页面中没有引入 util.js ,赶紧加上!

    <script src="util.js"></script>
    <script src="componet_one.js"></script>
    

    很快就修复了,看上去好像挺简单的,不过让我们看看这个例子的后续发展:

    • 某天,我扩充了组件的功能,除了需要 util.js 之外,还需要 ooxx.js

    这时候,项目中已经有 N 个地方用到了我的组件……
    于是我只好全局搜索每一个调用的地方,都给页面加上对 ooxx.js 的引用

    • 某天,需求砍掉了组件上的一个交互效果

    我修改了 componet_one.js 的代码,然后发现我不再需要 util.js 中提供的 print 方法了
    于是我再次全局搜索每一个调用的地方,去掉对 util.js 的引用

    • 测试同学告诉我,改完之后,好多个页面报错!

    我赶紧检查,发现有些页面上是这样的

    <script src="ooxx.js"></script>
    <script src="componet_one.js"></script>
    <script src="componet_two.js"></script>
    

    页面还引入了 componet_two.js ,而且 componet_two.js 中用到了 util.js 的 print 方法!
    所以 BUG 原因找到了,我不能武断地把 util.js 全都去掉,而是需要仔细检查页面上的其他的 JS 文件或代码是否需要它!
    崩溃……

    • 一段时间后,测试同学又来找我,说页面又报错了!

    再次检查,发现原来是某人动过了 ooxx.js ,在里面调用了 util.js 的方法……害死人不偿命啊!
    好吧,我只好再次全局搜索所有使用到 componet_one 组件的地方,给页面加上 util.js 的引用

    小结

    我已经不忍心再讲下去了,正所谓:看到哪句你哭了,不顶不是中国人!
    相信做过大一点项目的同学,应该都遇到过上面的这种破事。
    为什么会这么费神呢?因为 javascript 的语法中天生缺少引入其他 JS 文件的语法。
    相比之下,我们的好战友设计师们就轻松很多了,比如:

    @import url("base.css");
    
    #test {...}
    .classA {...}
    .classB {...}
    

    看到吧,人家只要用一个 @import 就解决了,页面中引入 css 时,只需要引入这个 css ,浏览器会自动去下载 base.css
    css 文件的依赖能够实现自动管理,而不是像 js 一样,需要手动地去编写

    OK,下面终于可以进入重点了

    Why SeaJS

    先不谈理论,直接来看看上面的这两个例子中如果引入 SeaJS 该怎么写
    首先是 util.js ,我们改用 SeaJS 的 CMD 规范来书写

    define(function (require, exports) {
        function print(str) {
            // ...代码
        }
    
        exports.print = print;
    });
    

    可以看到,其实改变并不大,主要是外部包裹了一层,再加最后多写了一行
    最后这行很重要,通过它,文件对外提供一个叫做 print 方法的接口
    另外大家发现没,这里我没有使用命名空间,为什么呢?看下面的 componet_one.js

    define(function (require, exports) {
        var util = require('./util.js');
    
        var ComponetOne = {
            doSth : function() {
                util.print('我是字符串');
                // ...代码
            }
        }
    
        return ComponetOne;
    });
    

    高潮终于到了!
    首先是 var util = require('./util.js'); 这句,有没有觉得很熟悉?是不是很像上面提到的 @import url("base.css"); ?
    没错!这里的 require 可以认为是 SeaJS 给 Javascript 增加的一个关键词语法,就像 @import 这个关键词一样!

    通过这个函数,SeaJS 赋予了 Javascript 直接加载 js 文件的功能,并且这个函数是 同步 的!
    函数直接就有一个返回值,那么返回值是什么呢?相信你已经猜到了,就是 util.js 里面的 exports 对象
    所以下面我们就可以使用 util.print() 来调用 util.js 提供的对外接口了

    刚才那个问题有答案了:因为 require 函数的返回值赋值给哪个变量完全由 componet_one 决定,变量名可以随便起,与 util.js 毫无关系(这里为了方便变量也叫 util ),所以 util.js 里面自然就不需要命名空间了!

    那么页面中该怎么引入呢?很简单

    <script src="sea.js"></script>
    <script>
    seajs.use('./componet_one.js')
    </script>
    

    我们需要先引入 SeaJS 这个加载器,然后通过它提供的 API 来加载文件
    而且最重要的是:因为有了 require ,这里我们只需要加载 componet_one.js 即可,util.js 会由 SeaJS 自动加载!
    也就是说,因为有了 require ,我们获得了类似 css 的文件依赖自动管理机制!

    小结

    仔细琢磨一下这几行代码,我想大家应该能看出 SeaJS 带给 Javascript 开发的巨大好处:

    • 一般来说,不再需要冗长的命名空间了,也不再需要担心命名的冲突
    • 不需要手动管理 js 文件的依赖,依赖关系写在代码中,SeaJS 会自动处理

    别小看这两点,对于稍微有些规模的项目来说它们非常重要,我想这也是 SeaJS 备受青睐的原因。
    最后,SeaJS 的 CMD 规范非常简单易懂,SeaJS 的 API 也非常简洁优雅,相信有一定开发经验的同学很快就能上手
    更多信息可以移步 SeaJS官网

  • 相关阅读:
    多测师讲解html _伪类选择器17_高级讲师肖sir
    多测师讲解html _后代选择器16_高级讲师肖sir
    多测师讲解html _组合选择器_高级讲师肖sir
    多测师讲解html _标签选择器14_高级讲师肖sir
    前端 HTML form表单标签 input标签 type属性 重置按钮 reset
    前端 HTML form表单标签 textarea标签 多行文本
    前端 HTML form表单标签 input标签 type属性 file 上传文件
    前端 HTML form表单标签 input标签 type属性 radio 单选框
    前端 HTML form表单标签 input标签 type属性 checkbox 多选框
    前端 HTML form表单目录
  • 原文地址:https://www.cnblogs.com/front-Thinking/p/3363874.html
Copyright © 2011-2022 走看看