Test 3rd-party API connectivity

content :None

Web.js MVC between client and server

Web.js 是一个 为简化 HTTP 开发而设计的 Web Framework,它致力于以最简单的语法进行开发高性能的应用。

Web.js between client and server 是指 web.js 这个文件可以同时在客户端和服务端使用吗?

不是的。。这个先不管。。先来看看其他“无关”的。。

以下是 Wikipedia 对 MVC 的解释:

MVC模式(Model-View-Controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。

 


先来说说前端 MVC

现在国内来说,相对优秀的前端 MVC 架构是来自豆瓣说的 [(Backbone.js + Underscore.js) + Mustache.js + jQuery]

M – Model

Backbone.js 和 Underscore.js 是暂时来说国内公认的最好的一组 Model 框架

  1. 高性能
  2. 强自定义度
  3. 高灵活性
  4. …………
Backbone.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//Model of Backbone
var Person = Backbone.Model.extent({
    sayHello: function () {
        alert('Hey, I`m ' + this.get('name') + '.');
        return this;
    },
    setName: function (name) {
        this.set({'name': name});
        alert('My name is ' + this.get('name') + '.');
        return this;
    }
});

var Will = new Person;

Will.setName('Will Wen Gunn')    // --> My name is Will Wen Gunn.
    .sayHello();                // --> Hey, I`m Will Wen Gunn.

其中 Backbone.Model 可以理解为经过封装的 Class 类型,然后 Person 是一个经过 extend 拓展的自定义 Class 类型,

Will 则是继承了 Person 的一个 Model 对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//Model of Underscore
$.getJSON(location.origin + '/persons')
    .done(function (data) {
        sessionStorage.persons = data;
        var persons = JSON.parse(sessionStorage.persons);
        // --> [{name: 'Will Wen Gunn'}, {name: 'Foo'}, {name: 'Bar'}]
        $('body').append(
            JSON.stringify(
                _.map(persons, function (person) {
                    if (person.name == 'Will Wen Gunn' || person.name !== 'Bar') {
                        return true;
                    }
                })
            )
        );
    });
// --> [{"name": "Will Wen Gunn"}, {"name": "Foo"}]

与其说 Underscore.js 是 Model 框架,还不如说是 Model 操作库,Underscore.js 并没有封装 Class ,而是使用 JavaScript 原有的 Array, Object, Function 类型进行操作 (其中人们比较常用 Object 和 Function 来模拟 Class)

这种 Model 会比 利用 Object 或者 Function 来定义 Class 要实际,因为这样可以很好地对对象和对象集 (Collection) 进行管理和操作。


V – View

View 更直接点地说就是我们平时所构建的 HTML ,而 MVC 其实源自于 DHTML ,W3C 的解释是动态页面(Dynamic HTML),而我更认为是 Data to HTML。

而这个正是Mustache.js诞生的原因

{{Mustache}}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
var person,
    tmpl-persons,
    proxy = new Eventproxy(),
    persons = function (data, tmpl) {
        $('body').append(
            Mustache.to_html(tmpl, data)
        );
    };
proxy.assign('data', 'tmpl', persons);
$.getJSON(location.origin + '/persons')
    .done(function (data) {
        persons = JSON.parse(data);
        /*
         * [ {Name: "Will Wen Gunn", Age: 15, Sex: "Man"},
         *   {Name: "Foo", Age: 15, Sex: "Man"},
         *   {Name: "Bar", Age: 15, Sex: "Man"} ]
         */
        proxy.trigger('data', data);
    });
$.get(location.origin + '/tmpls/persons.html')
    .done(function (data) {
        tmpl-persons = data;
        /*
         * {{#peoples}}
         *     {{Name}}
         *     {{Age}}
         *     {{Sex}}
         * {{/peoples}}
         */
        proxy.trigger('tmpl', tmpl);
    });
/*
 * Will Wen Gunn
 * 15
 * Man
 * Foo
 * 15
 * Man
 * Bar
 * 15
 * Woman
 */

这里使用到了朴灵小田同学的 EventProxy.js,相当好玩的一个小工具,但是发挥出来的作用很强阿。

它能让几个异步请求并行处理,最后集中处理。

https://github.com/JacksonTian/eventproxy

另外也还有一个来自国外的,类似的东西,叫Step,它也让异步函数分开,但是是串列的队列式,所以会产生阻塞,小问并不建议使用。


C – Controller

这个恐怕是争议最大的一块了,有人推崇jQuery,有人推崇YUI,有人推崇MooTools……

其实这个并没有什么太大关系,只要是有这样的能力的,哪种 JavaScript Library 都没所谓的。

我这里用jQuery把上面的这些M,V整合起来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
var Person = Backbone.Model.extent({
        sayHello: function () {
            alert('Hey, I`m ' + this.get('name') + '.');
            return this;
        },
        setName: function (name) {
            this.set({'name': name});
            alert('My name is ' + this.get('name') + '.');
            return this;
        }
    }),
    Persons  = Backbone.Collection.extend({
        model: Person,
        sayHello: function () {
            this.each(function (Person) {
                $('body').append('Hey, I`m ' + Person.get('name') + '.');
            });
        },
        sayName: function () {
            this.each(function (Person) {
                $('body').append('My name is ' + Person.get('name') + '.');
            });
        }
    });
var CNodejs = new Persons;
var persons,
    tmpl-persons;
$.get(location.origin + '/persons')
    .done(function (data) {
        persons = JSON.parse(data);
    })
    .get(location.origin + '/tmpls/persons.html')
    .done(function () {
        tmpl-persons = JSON.parse(data);
    });
CNodejs.add(persons.peoples);
$('body').append(
    Mustache.to_html(
        tmpl-persons,
        { peoples: CNodejs.toJSON() }
    )
);

Node.js MVC

M – Model, V – View

其实 Backbone.js, Underscore.js 和 Mustache.js 在 Node.js 上的用法是和前端一模一样的,所以我就不多介绍了。

C – Controller

来看看 Web.js 的 Router :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
var urlRouter = {
        '^(\d{4})\/(\d{2})\/(\d{2})\/(.*)\.jpg': '$1-$2-$3-$4.jpg',
        'google': 'http://www.google.com',
        'iwillwen': 'http://www.iwillwen.com'
    },
    getRouter = {
        '^getsomthing': function(req, res, qs) {
            res.sendJSON(qs);
        },
        '^car': function(req, res, qs) {
            switch (qs.action) {
                case 'foo':
                    res.send('Your action is foo');
                    break;
                case 'bar':
                    res.send('Your action is bar');
                    break;
            }
        }
    },
    postRouter = {
        '^postsomthing': function(req, res, data) {
            res.sendJSON(qs);
        },
        '^car': function(req, res, data) {
            switch (data.action) {
                case 'foo':
                    res.send('Your action is foo');
                    break;
                case 'bar':
                    res.send('Your action is bar');
                    break;
            }
        }
    };

web.run(urlRouter, 80)
    .get(getRouter)
    .post(postRouter);

console.log('The app is running on http://localhost');

这个和Express有点区别。

如果结合数据库的 Node.js MVC,Web.js该怎么写呢

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
var web =require('webjs'),
    mongo = require('mongoskin'),
    Backbone = require('backbone'),
    Underscore = require('underscore'),
    eventproxy = require('EventProxy.js'),
    db = mongo.db('localhost:27017/blog'),
    posts = db.collection('posts'),
    metas = db.collection('metas'),

var urlRouter = {
        '^page/(\d)': 'page.html',
        '^(.*)': 'post.html'
    },
    getRouter = {
        'init': function (req, res, qs) {
            var proxy = new eventproxy.EventProxy(),
            init = function (title, description, posts) {
                    var obj = {
                        title: title,
                        description: description,
                        posts: posts
                    };
                    res.sendJSON(obj);
                };
            proxy.assign('title', 'description', 'posts', init);
            metas.findOne({type: 'title'}, function (err, title) {
                proxy.trigger('title', title);
            });
            metas.findOne({type: 'description'}, function (err, description) {
                proxy.trigger('description', description);
            });
            posts.findTop10(function (err, posts) {
                proxy.trigger('posts', posts);
            });
        },
        'getPost': function (req, res, qs) {
            posts.findOne(qs, function (err, post) {
                res.sendJSON(post);
            });
        }
    },
    postRouter = {
        'setMeta': function (req, res, data) {
            metas.update(
                {type: data.type},
                data,
                {upsert: true},
                function (err) {
                    if (err) return res.send('Set failed.
' + err);
                    res.send('Set successed.');
                }
            );
        },
        'post': function (req, res, data) {
            posts.update(
                {title: data.title},
                data,
                {upsert: true},
                function (err) {
                    if (err) return res.send('Post failed.
' + err);
                    res.send('Post successed');
                }
            );
        }
    };
web.run(urlRouter, 80)
    .get(getRouter)
    .post(postRouter);

最后来讲讲

Web.js between client and server

来看看 Web.js for client (仍在编写中) 的用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
(function ($) {
    //loaded /js/web-client.js
    var getRouter = {
        'getsomething' : function () {
            var proxy = new Eventproxy(),
                post = function (data, tmpl) {
                    $('body').append(
                        Mustache.to_html(tmpl, data)
                    );
                };
            proxy.assign('data', 'tmpl', init);
            web.getData('post', {}, function (data) {
                proxy.trigger('data', data);
            });
            web.getTmpl('post', function (tmpl) {
                proxy.trigger('tmpl', tmpl);
            });
        }
    };
    web.conn(location.origin)
        .get(getRouter);
})(jQuery);

这时候大家可能会问了,Web.js 的 MVC 究竟异样在哪里呢?

前端 MVC 的 M 其实和后端 MVC 的 M 本来是不可能一起使用的,因为如果由后端进行模板渲染,前端就没有渲染的必要,那么前端 MVC就不成立了。同理,如果让前端进行模板渲染,后端也就不存在 MVC 的概念。

如果要前后端的 MVC 同时存在要怎么做呢,其实很简单,就是让客户端进行性能·评估。

比如说 IE6、7 这样的低性能浏览器,Web.js for client 会让服务器先进行 Data to HTML 渲染,然后传输再到客户端,如果是Chrome,FireFox,Safari 和 Opera 等高性能浏览器,则选择在客户端进行渲染,减轻传输荷载。

另外 Web.js 默认会开启缓存应用加速机制 (DOM Storage,Cookies,Buffer,Object……),让一部分数据先存入缓存,让短时间内再次发出的请求从缓存中获取。减少 LAN 资源和数据库资源的损耗。

JavaScript 是一门建立在静态页面上的动态脚本语言,Ajax的普及和发展,使它完全可以完成一些静态页面做不到的事情,比如像PHP自身的文件响应机制 (相比这也是 PHP 吸引人的一个终于优点)。

上面的这一段代码就演示了一个静态页面通过 Web.js 进行 URL action router 识别,并向服务器请求数据和模板,然后在 DOM 中插入渲染得到的 HTML。

两个 Web.js 之间是可以无缝对接的,开发者无须设置太多。

当然 Web.js for client 也是支持 Express 等其他 Server-side 开发框架的。


好,最后来放一个完整的 Web.js MVC Router 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
//server.js
var web =require('webjs'),
    mongo = require('mongoskin'),
    Backbone = require('backbone'),
    Underscore = require('underscore'),
    eventproxy = require('EventProxy.js'),
    db = mongo.db('localhost:27017/blog'),
    posts = db.collection('posts'),
    metas = db.collection('metas'),

var urlRouter = {
        '^(.*)': 'page.html'
    },
    getRouter = {
        'init': function (req, res, qs) {
            var proxy = new eventproxy.EventProxy(),
                init = function (title, description, posts) {
                    var obj = {
                        title: title,
                        description: description,
                        posts: posts
                    };
                    if (qs.render) {
                        res.send(
                            web.render('init', obj)
                        );
                    } else {
                        res.sendJSON(obj);
                    }
                };
            proxy.assign('title', 'description', 'posts', init);
            metas.findOne({type: 'title'}, function (err, title) {
                proxy.trigger('title', title);
            });
            metas.findOne({type: 'description'}, function (err, description) {
                proxy.trigger('description', description);
            });
            posts.findTop10(function (err, posts) {
                proxy.trigger('posts', posts);
            });
        },
        'getpost': function (req, res, qs) {
            posts.findOne(qs ,function (err, post) {
                if (qs.render) {
                    res.send(
                        web.render('post', post)
                    );
                } else {
                    res.sendJSON(post);
                }
            });
        }
    };
web.run(urlRouter, 80)
    .get(getRouter)
    .set('tmplDir', 'tmpls');

//page.html - client.js
(function ($) {
    var getRouter = {
        '/' : function () {    //init
            var proxy = new Eventproxy(),
                init = function (data, tmpl) {
                    $('body').append(
                        Mustache.to_html(tmpl, data)
                    );
                };
            proxy.assign('data', 'tmpl', init);
            web.getData('init', {}, function (data) {
                proxy.trigger('data', data);
            });
            web.getTmpl('init', function (tmpl) {
                proxy.trigger('tmpl', tmpl);
            });
        }
        '^(.*)' : function (action) {    // action --> (.*)
            var proxy = new Eventproxy(),
                post = function (data, tmpl) {
                    $('body').append(
                        Mustache.to_html(tmpl, data)
                    );
                };
            proxy.assign('data', 'tmpl', init);
            web.getData('post', {title: action}, function (data) {
                proxy.trigger('data', data);
            });
            web.getTmpl('post', function (tmpl) {
                proxy.trigger('tmpl', tmpl);
            });
        }
    };
    web.conn(location.origin)
        .get(getRouter)
})(jQuery);

你确信你了解时间吗?

你还记得“软件真的好难做”中的那个有意思的例子吗?那个例子告诉我们软件开发中假设可能会是致命的事。今天,我又在StackOverflow上看到一个关于时间的问题——为什么1927年12月31日的午夜时间这么奇怪?提问题的这个人给了下面的一段java代码(我做一些修改,保证让你可以copy过去就可以编译运行)

我在其中高亮了几行,这个程序就是想比较一下“1927-12-31 23:54:07”  和  “1927-12-31 23:54:08” 差几秒,很明显,是差一秒。但是程序的输出却不是这样的。

  1. import java.text.SimpleDateFormat;
  2. import java.text.ParseException;
  3. import java.util.Date;
  4. import java.util.TimeZone;
  5. class time{
  6.     public static void main(String[] args) throws ParseException {
  7.         SimpleDateFormat sf = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);
  8.         sf.setTimeZone(TimeZone.getTimeZone(“Asia/shanghai”));
  9.         String str3 = “1927-12-31 23:54:07”;
  10.         String str4 = “1927-12-31 23:54:08”;
  11.         Date sDt3 = sf.parse(str3);
  12.         Date sDt4 = sf.parse(str4);
  13.         long ld3 = sDt3.getTime() /1000;
  14.         long ld4 = sDt4.getTime() /1000;
  15.         System.out.println(ld3);
  16.         System.out.println(ld4);
  17.         System.out.println(ld4-ld3);
  18.     }
  19. }

复制代码

下面,让我们来看看程序的输出:(是的,差出353秒钟来)

-1325491905
-1325491552
353

产品管理:用流程规避风险

最近一直在跟的一些项目,过程中出现了一些反复,原因有很多。深入分析一下问题根结,希望以后能避免。

  关键词:产品负责制、确认层级机制、产品脊梁、靠谱

  问题:
  • 1、项目初期,产品原则已经开会统一、皆大欢喜,但中期却在产品原则上出现分歧。甚至在产品面临上线时,还有人为该产品赋予新的定位。
  • 2、产品设计环节中,在产品需求和定位已确认的情况下,纠结在“不符合需求、偏离定位”的设计细节,僵持不下、浪费时间、拖垮精力。
  • 3、跨部门合作的项目,设计部门提交的工作结果,经常被他们自己的上层或左右推翻重来,即使对产品有利,却伤害整个项目,伤害自己人。
  根据这些问题,需要问很多为什么。

  为什么初期统一过产品原则,中期却出现原则上的分歧?“发起分歧者”所具备的必要条件是什么?

  首先需要问公司:产品负责制,到底有没有明确和落实?其实,产品人员已经也应该,把对产品的长期责任,承担在自己身上。但是公司层面和总监层面如果没有强有力的确认产品负责制的归属,我们这种自我承担的产品负责制,永远不会在关键时刻发挥作用,而只会左顾右盼。

  在产品负责制尚不明晰的情况下,即使团队核心成员开会确认了产品原则,也永远不会有一个强有力的角色把产品原则贯彻到每一个阶段,初期产品定位和产品原则的会议确认,是没有力度的,只是表面上的统一而已。

  原因很简单,当一切顺风顺水、条条大路通罗马的时候,旅游团众大佬必然跟着美女导游就OK了。当路遇挫折时,导游就算熟悉路途中的险恶,也仍然不是无法把自己伪装成团队的菩萨,左右不了各位大佬的想法。(默契,可以克服这一问题。但在大公司的营盘里面,想培养一个默契而完整的项目团队,一般是可遇不可求的。但默契的产品小组还是有的。)

  在以上这种情况下,发起分歧者有什么必要条件?一是长期跟进产品,了解最初的产品原则和定位;二是了解整个团队的工作重心和产品市场情况,而不是只熟悉自己所管部门的情况。可悲的是,熟悉了以上两点的人,往往不会做出不靠谱的决策,而去伤害整个项目。

  既然已经确定了产品需求,为什么会出现“不符合需求、偏离定位”的设计细节?为什么会在这种问题上浪费时间?

  这种问题出现非常正常,当设计人员没有做充足的竞品分析,也没有充分理解产品的情况下,总会在设计时出现偏差。只要产品人员说明需求和修改原因,说服设计人员回归需求即可。可怕的是“僵持不下”,设计人员坚持己见,认为设计细节是正确的、靠谱的、符合产品需求的,产品人员却无法认同。僵持不下时,应该搁置问题,继续推进项目,而不是在这种问题上纠结一个下午,还把各种用户调研、应用实例搬出来说理。

  最终做决定时,如果依旧僵持不下,那么谁对产品长期负责,谁做决定。谁对产品长期负责?回归的上一个问题。

  为什么“工作结果”可以推翻重来?谁赋予了提交者“提交的权利”,又是谁赋予了推翻者“推翻的权利”?

  首先要问的问题是:是不是根本没有明确“确认层级”的工作流程,或者只是口头明确、表面默契而已?

  比如一个设计稿,当然要由直接负责人A确认,没有问题了,再交到下一步去执行。那么这个负责人A需要评估:自己是否能够保证这一个确认结果是最后结果。如果不能保证,那么再提交上一层的负责人,让他确认。但如果上层负责人曾经授权了负责人A的确认权限,就不应该在A已经确认之后,轻率的推翻确认结果。

  在这种确认过程中,应该在每一个项目实施之前明确“相应的设计工作,需要有相应的确认层级”,这一点至关重要!“确认层级”都不明晰的情况下,首先应该检讨的不是设计质量没有达到要求,而是为什么会出现这种流程上的漏洞,让不符合设计质量的东西推进到下一步骤。漏洞不堵住,就没有资格反复插手,这样既伤害团队,也拖垮自己。

  相反,当指定了项目设计工作的确认层级之后,再出现提交设计稿不合格、推翻设计工作不符合流程的情况,就可以明确的质疑“提交的权利”和“推翻的权利”,是否符合“确认层级”的流程。这个时候,是能力问题的就调整确认层级或更换负责人、是流程问题的就贯彻流程,比大家互相装无辜更加务实。

  最后感叹一下,产品经理是妥妥的炮灰,哪个环节出问题,都可以揭出产品规划和项目管理的漏洞和伤疤

  一个产品项目,从立项、交互、视觉、开发、测试等各个阶段,总会因为各种问题而反复伤害。尤其是跨部门合作、多环节配合的流程,伤害问题发生的隐患比比皆是。这个过程期间,产品经理的职责,就是想尽办法,避免各种伤害的发生、避免各种伤害打击到核心成员、伤及团队重心。

  可悲的是,在大部分环境下,卑微的产品人员不仅无法预估各种隐患问题,对于已经发生的伤害问题也没有足够的防备资源和职权。这个时候,产品经理,就是妥妥的炮灰,众口一铄,灰飞烟灭。最后,产品经理仍然必须站起来,拖着被各种问题所累的核心成员,继续把项目推进下去。

  不知道这种情况,在其他公司是否常见。当没有固化的产品负责制和确认层级管理机制的时候,深埋问题的跨部门团队开发产品,然后暴露出一堆问题,一遍又一遍的伤害每一个产品。所以,这种公司总是出现一堆一堆的打折产品,承担着一堆又一堆不给力的数据和市场境况,最后担责到产品人员身上,该调走的调走、该跳槽的跳槽,再招进下一批未知轻重的欣欣向荣者,如此反复、以至无穷。

  关键角色位置的靠谱牛人,是产品的脊梁

  产品团队,碰到这种复杂的情况,唯一的救命稻草,就是产品各环节的关键位置,有靠谱的关键角色把持,然后这类关键角色需要保持持续的头脑清醒、精神集中,在任何阶段都能回溯最初的产品原则、评估当前的团队重心和高层要求,统一麾下执行者的行动,并说服高层。从这个角度讲,没有那么多流程管理和部门配合的小团队产品和创业公司产品,要更加安全、更加靠谱。近期组里的一个小众产品,没有大资源投入、没有多部门合作、没有高层压力、没有战略意义,虽然选择了一条难路,但整个产品从设计到运营,让外人看来,显得张弛有度、节奏明确,这就是一个明显的例子。

  只要人靠谱,做什么都是靠谱的。如果人本身不靠谱,在任何角色上,都会出问题。至于什么问题:细节不到位、文档及版本控制逻辑不清楚、决策前后矛盾、协调成员配合乏力、工作重心聚焦不正确、沟通不主动……比比皆是。

  希望自己靠谱一点。

杜旬华你是怎么看到这个网站的?

没了,rt