w3ctech

thinkjs使用小结

这是俺这几天使用thinkjs的一些心得,由于没接触过nodejs和异步编程,代码很ZHA,如果有误人子弟请说明。。。谢谢

首先你要对她(thinkjs)有部分的了解,什么?不太熟?请移步: http://www.thinkjs.org/doc/

俺对MVC的定义

  • M层要直接接触“库”类,所有的查询要在这里进行,当然要对外暴露好点的API让C层调用更方便。遵循一个表一个M层的规则
  • V是视图,我喜欢在视图里直接循环/输出,不在做一些数据纠正等问题了,该做的都要在C里做好
  • C是控制层,一些逻辑代码将在这里写,比如:分页,判断,条件的筛选,修正一些值,输出模板,报错。经过这一层的处理,参数应该都是自己理想的,要考虑到数据空,错,非法等问题

定义module

数据库已知表 前缀_list, 在 Module 目录里新建文件 ListModule.js, 代码如下:

/**
 * 学习吧列表表模块
 * @author xieliang
 */
module.exports = Model(function() {
    'use strict';
    return {

        /**
         * 获取列表数据
         * @param {object} options 配置参数
         * @return {Promise}
         *
         * @description
         *     1, 成功的回调参数为数组 [{},{}]
         *        错误的回调参数为空数组 []
         *     2, one 成功的回调参数为对象 {a:1,b:2}
         *        one 错误的回调参数为空对象 {}
         */
        get_list_data: function(options) {
            var self = this,
                sql;

            //合并参数
            options = extend(false, {
                page: 1, //分页
                limit: 'all', //偏移,如果为all则为全部
                order: 'id DESC', //排序
                cache: false, //是否加cache
                field: 'id, title, url, description, keywords', //要查的字段
                where: null, //条件
                one: false //是否只查一个
            }, options || {});

            // 查字段
            sql = self.field(options.field);

            //如果不是一个才查排序
            if (!options.one) {
                if (options.limit !== 'all') {
                    sql.page(options.page, options.limit);
                }

                sql.order(options.order);
            }


            //如果有附加条件
            if (options.where) {
                sql.where(options.where);
            }


            //是否开启缓存
            if (options.cache) {
                sql.cache(true);
            }

            //如果是一个则用find查单个,否则查数组
            return options.one ? sql.find() : sql.select();
        },



        /**
         * 获取单条数据
         * @param  {object} options 配置
         * @return {Promise}
         */
        get_item_data: function(options) {
            options = options || {};
            options.one = 1;
            return this.get_list_data(options);
        }
    }
});

以上两个方法为获取数据,一个是获取多条支持分页,一个是获取单条,来看代码如何获取吧:(代码写在控制器里)

//根据id查单个内容
testAction: function(){
    var self = this;

    return D('List').get_item_data({
        where: {
            id: 1
        }
    }).then(function(data){
        //data = {}

        if(isEmpty(data)){
            console.log('空数据');
        } else {
            console.log('有数据');
        }

        console.log(data);
    });
},

是不是觉得很单调? 好吧, 来个参数多的:

//like查, 并分页
test1Action: function(){
    var self = this;

    return D('List').get_item_data({
        isPage: 1,
        page: page变量,
        where: {
            class_id: 123,
            title: ['like', '%'+ key +'%']
        },
        order: 'id DESC',
        limit: 20
    }).then(function(data){
        //这里的data为数组,如果为空则是空数组
    });
},

你会不会想说这只是单个查,有时候在列表里是需要查别的表数据的,比如循环列表的每行里都有作者信息,分类信息等, 那么来看下C层吧。。。

Controller的定义

首先我强制让自己遵循一个小约定:

  • 在C层里始终把最后的代码 return 出去
  • 把最后渲染数据放在最后的一个 then 里,即使多套一个 then 也要这样做,如:
testAction: function(){
    var self = this;

    return D('List').get_list_data().then(function(data){
        // data = []
        //这里是逻辑层1,要处理得到的列表循环数组

        // data.forEach()....

        return data;
    }).then(function(data){
        //data = 列表循环数组
        //这里是逻辑层2, 要处理每行的分类+用户信息
        //var arr = [];
        //data.forEach(function(val){
        //  arr.push()....
        //});
        //return Promise.all(arr)
        //
        //if(data为空则) return []

        return data;
    }).then(function(data){
        //data = 列表循环数组, 且已经处理过分类+用户信息了
        //这里是逻辑层3, 要处理一些判断,或者渲染的东东

        return data;
    }).then(function(data){
        //data = 列表循环数组, 且已经处理过分类+用户信息了, 且处理过渲染
        //这里是逻辑4,渲染模板

        self.assign("list", data....)
        self.assign....
        return self.display();//把这个return 出去 ,以便捕获异常
    });

    //在除了逻辑4以为的then,如果数据为空则直接return []交给逻辑4渲染模板为空, 如果数据错误则直接return 404的一个fn
}

我应该怎么在循环里查表呢?

/**
 * 内部调用列表
 * @description 处理分类名, 作者, 发表时间等内容
 * @param {object} options 配置
 * @return {Promise}
 */
_get_list: function(options) {
    var self = this;


    options = options || {};

    return D('Content').get_list_data(options).then(function(data) { //查分类名 + 修改url + 发布时间  + 内容
        var arr = [];
        var List = D("List");

        //如果为空则返回空数组
        if(isEmpty(data)){
            return arr;
        }

        data.forEach(function(val) {
            //查分类名
            arr.push(List.get_item_data({
                where: {
                    id: val.class_id
                }
            }));

            //修正链接
            val.uri = Url.get_detail(val.id, val.url);

            //发布时间
            val.add_time = Date.elapsedDate(val.add_time * 1000, 'yyyy-M-d');

            //去html+截取
            val.article = quickHtml(val.article);
        });


        //数组异步处理
        return Promise.all(arr).then(function(list_data) {

            data.forEach(function(val, index) { //把分类的数据叠加到主数据上
                if (list_data[index]) {
                    val.list_name = list_data[index].title;
                }
            });
            return data;
        });

    }).then(function(data) {
        //查作者信息 
        var arr = [];
        var User = D("User");

        //如果为空
        if(isEmpty(data)){
            return arr;
        }

        data.forEach(function(val) {
            arr.push(User.get_item_data({
                where: {
                    id: val.add_uid
                }
            }));
        });

        //数组异步处理
        return Promise.all(arr).then(function(list_data) {
            data.forEach(function(val, index) {
                if (list_data[index]) {
                    val.user_name = list_data[index].user_name;
                }
            });
            return data;
        });
    }).then(function(data){
        //这里是渲染模板, 如果要写成公用方法这里则删除,写在你调用之后,如:
        //  公用方法名().then(function(data){});
        self.assign('list', data);

        return self.display();
    });
},

这里循环查只是利用了数组异步处理+回调,上次听 @welefen 这种形式有个很高端的方法解决,目前俺也不会,只能这样先处理了,我相信不久将来 thinkjs 官网会出相应demo的。。。啦啦啦

此外 thinkjs 还支持base控制器,可以把一些常用的方法定义到里面,比如错误的,私有内部查询等。。。

V层就不说了,多看看模板引擎就啥也有了。。。

使用nginx做代理映射

大家都知道一般是通过域名访问web的,但nodejs好像只能绑定端口(俺才书学浅),那么我们就需要一个http server了,俺选的是nginx。。。

首先安装nginx, 然后修改nginx配置文件:

server {
    listen     80;//http端口
    server_name  new.xuexb.com;//host name

    root /ssd/wwwroot/www.xuexb.com;//目录

    index index.js index.html;//默认主页

    if ( -f $request_filename/index.html ){//如果请求的路径下有index.html,则重写到这个文件
        rewrite (.*) $1/index.html break;
    }

    if ( !-f $request_filename ){//如果请求的文件真实路径不存在则重写到thinkjs上
        rewrite (.*) /index.js;
    }


    location = /index.js {//配置thinkjs
        proxy_set_header Connection "";
        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;
        proxy_pass http://127.0.0.1:81$request_uri;//这里配置thinkjs的监听端口
        proxy_redirect off;
    }
}

然后把thinkjs的端口改为已知的,比如81,然后使用提供的 sh 命令来进行后台运行服务(linux系统下),然后不出意外的话访问就会成功,这个配置感谢 @welefen,这个优势在于如果是真实路径则不启用thinkjs作服务,更快速,也可以扩展自己的 真静态 文件了,对,思路我已经想好了,只待写了。。。_

最后感谢 thinkjs,让前端也可以写后端(虽说写的很烂)。。。

w3ctech微信

扫码关注w3ctech微信公众号

共收到9条回复

  • 赞,欢迎多发thinkjs相关的文章

    回复此楼
  • 亮神好屌,哪都能见到你。。。

    回复此楼
  • 我是跟着你过来的。。。

    回复此楼
  • <h1>不错</h1><p>真好</p>

    回复此楼
  • 亮神带我飞

    回复此楼
  • 亮了

    回复此楼
  • 要求不复杂的话,nodejs 本身就能当WEB服务器,thinkjs里面把配置项port: 8360,改成port: 80,就行了,访问的时候用个http://localhost/index/index 是OK的

    回复此楼
  • @老六 uncaughtexception 有什么好的处理方法?

    回复此楼
  • @Doerme 用domain模块。 如果用thinkjs,框架已经帮你做了,应用里不用处理了。

    回复此楼