[PyJs系列介绍]一、从commonjs和seajs说起

PyJs JavsScript Framework 项目地址:  https://github.com/demix/PyJs

文档地址:  http://demix.github.com/pyjs/

 

这里的PyJs,不是已有的Pyjamas暂不具体说PyJs是个神马东东,只说一句:PyJs是依赖于python的一个符合commonjs规范的浏览器端JavaScript Framework。所以,不喜欢写js有环境依赖的,可以点浏览器的x了,不好意思占用各位时间。 

 

继续看的童鞋门,我们先来说一下commonjs。

 

Commonjs模块

wiki地址 http://wiki.commonjs.org/

 

module 1.1.1规范 http://wiki.commonjs.org/wiki/Modules/1.1.1

 

commonjs 的目标是定义一套普通应用程序使用的API,从而填补原生JavaScript标准库过少的缺点。终极目标是实现一个像python,java中含有的标准库。现在非常火爆的nodejs实际上就是commonjs的一个实现。

 

commonjs的module 1.1.1基本设计思路如下:

Module上下文中,有一个require的函数。require接受一个module标志量,返回一个目标模块的api接口;

Module上下文中,有一个exports变量。module使用exports倒出自己对外开发的api接口。

Module上下文中,有一个module变量。有id & uri 属性。

 

定义一个普通的模块,可以只是如下的代码。

math.js 

exports.add = function() {
    var sum = 0, i = 0, args = arguments, l = args.length;
    while (i < l) {
        sum += args[i++];
    }
    return sum;

};

 

increment.js 依赖math.js,如下代码  

var add = require('math').add;
exports.increment = function(val) {
    return add(val, 1);

};

 

主程序program.js如下:

var inc = require('increment').increment;
var a = 1;
inc(a); // 2
 

module.id == "program"; 

 

Commonjs在浏览器环境运行的问题

Commonjs的API设计其实很像python等语言。如果使用在nodejs等服务器、客户端终端上,是不会有什么问题的。但是如果在js使用量最大的浏览器端使用commonjs,将会产生很大的局限性,根本在于require函数只能同步运行。

 

如increment.js文件所示,js解析器运行到 require('math')的时候,是需要向后端请求math.js的文件的。同时,这里没有使用js的异步回调,在require之后直接获取了math的add方法,必须使用一次同步的请求。那么program.js里面也会有同样的情况。如果程序运行在服务器,文件请求都是本地获取,那么这几个同步的请求其实对性能是没有什么影响的。但是在浏览器端就存在根本的差异了:一方面,浏览器必须请求increment.js,加载完成并执行到require('math')的时候再去串行的请求math.js,同时进程只能挂起等待请求的结束,再继续下面的脚本;另一方面,同步的请求不仅会住后面的请求队列,同时浏览器的渲染进程也同时进度了等待状态。这样我们的浏览器可能就得等所有的同步请求串行的加载结束之后才能开始渲染页面。这对于一个良好体验的因特网应用是不能容忍的。

 

Seajs的改变

seajs 地址: http://seajs.com/ 

 

seajs的解决方案是在module定义外边加上一套标准模板

 

define(function(require, exports, module) {

  // The module code goes here

});

 

然后可以在模块文件加载完成之后先静态分析其中的依赖关系,并在运行模块之前先将其依赖模块全部异步加载,已达到执行到require时所需模块已经加载到本地,而不需要同步加载的效果。同时,对于一个模块中出现的多个依赖模块,还可以并行加载这些依赖模块,以达到运行效果。 

 

Seajs的问题

模块对其依赖模块的加载还是串行的

也就是,如果a依赖b依赖c依赖d,那么加载的过程会是a->b->c->d。这样如果模块依赖关系很多,a的运行逻辑还是会长时间被暂停,这对于用户体验的伤害还是致命的。

 

seajs还提供了一个打包工具,可以将几个依赖文件打包到同一个文件中来规避上述问题。但这样对于需要增加依赖的情况,必须重新编译一次,require再也不是随需随取。而且如果两个应用逻辑很一致只有一个require不同,将编译出两份不同的文件,这样也产生了很多不必要的消耗。

另外,如果我完全希望在一个用户需要什么功能才require什么组件的地方,这个打包工具就没有用处了。 


还有我个人也不喜欢这种写法。所有的define其实在每个模块都是一样的,但是却要求我们手动去写这些没有意义的代码。重复coding在我来看是最大的浪费  XD

 

PyJs 的实现

说了那么多,也要说一下PyJs的实现了吧。

 

PyJs优点:

a.编写过程完全遵循commonjs module 1.1.1规范,不需要自行增加公共模板,没有各种自定义函数。nodejs的lib库文件(没有特殊接口)可以直接copy到PyJs运行目录下即可运行。

b.完全实现文件的并行异步加载,对于支持combo的服务器,只需要一个请求就能将所有依赖文件全部读取出来。

 

PyJs缺点:

a.依赖于python环境。

b.由于项目的需求,暂时只对pyjsdir目录下文件和包进行require,不能针对子文件夹。 这一块暂时也没想好最好的实现方式。

 

测试demo可以到 PyJs 的 github 项目中获取:https://github.com/demix/PyJs 

 

放到本地环境之后,运行python pyjs.py runserver

 

然后在浏览器输入 http://localhost:8150/demo-server.html,即可看到效果。

 

依赖的文件在src目录下,就是上面的math,increment文件,毫无修改。

 

这只是一个最简单的demo,而且请求也是同步的。不过不用担心,这只是本地调试才会出现的情况。PyJs已经实现了一套非常完善的异步加载机制,后面几篇文章将会对个中机制、适用场景做详细介绍。 

 

PS: PyJs还是0.1状态,而且也是commonjs规范还没有实现完全,这些将在我们开发中不断完善。

 

PPS: 第一次做这种东东,很多地方不完善,虚心接受大家指正 

 

posted @ 2011-08-24 15:14 demix 阅读(...) 评论(...) 编辑 收藏