我们要做一个node作中间层的前端项目,首先要有一个架构,如果是自己一步步写出来那就很耗时间,而这种事情别人都已经做过了,所以我们也可以坐享其成,脚手架是大家在开发时常用的东西也是大大提高我们效率的东西,我们的脚手架是fx.express-generator(使用方法在内网npm上),因为是放在内网npm上所以需要配置nrm,具体的配置就不赘述了,大家去npm上看相关教程吧。看到这里我们都知道我们在写node的时候主要是基于express这个node界最负盛名的框架了,虽然脚手架和express都帮我们解决了大部分的问题,但是还是有很多东西需要我们自己来解决!
由于现在使用node的项目普遍是一个独立子站或网页,那么经常会出现调用接口跨域的问题,例如需要用到繁星主站的一些接口,而事实上也确实是需要用到很多主站的接口,通过这些接口获取一些必要的信息,最普遍的就是获取繁星用户信息,那么就出问题了,因为这个接口是不支持jsonp的。
解决办法:我的解决办法比较简单粗暴,就是在nodejs中写一个方法,这个方法对路由进行过滤一下,如果进来的是繁星UService接口的时候对其进行转发,然后将数据给前端使用,不过这个只能解一时之渴,为的是一开始后端没有接口时使用,最后还是需要后端写一个接口来转发这些数据信息,我的代码如下:
// 此为开发时使用的接口请求转发适配器,可以自动帮你转发所有向services发起的请求 router.get('/*', function(req, res, next) { if (req.params[0].match(/api[.php]?/)) { var url = host + '/' + req.params[0]; } else if (req.params[0].match('UServices')) { var url = '[你的开发服地址]' + req.params[0]; } else { return false; } var cookie = req.headers.cookie; var referer = req.headers.referer; var options = { uri: url, headers: { 'User-Agent': 'Request-Promise', 'Cookie': cookie, 'Referer': referer, 'Content-Type': 'application/json; charset=utf-8' }, json: true }; options.qs = {}; if (req.query.args) { for (var name in req.query) { options.qs[name] = req.query[name]; } } rp(options) .then(function(json) { res.send(json); }) .catch(function(err) { console.log(err) res.send(err); }); });
由于我们使用handlebars的模板引擎,一开始是为了降低学习成本,但是这个模板引擎并没有想象中的好用,而且教程的缺失,文档的不给力也造成了刚开始使用时的一些难度,例如在一般的模板中我们是可以这样用的 {{#if !value}}hello node{{/if}}
,但是在handlebars中这样是会报错的,你没有听错,直接就报错了,我百思不得其姐,就去百度了一下,然后度娘告诉我,这个东西是弱逻辑模板引擎,不能这样用,只能使用unless在做与if相反的判断,但是如果你想使用 {{#if value===0}}hello node{{/if}}
那么很抱歉,handlebars并没有这样的内置作用块给你使用。
看到这里如果你看过handlebars的文档或教程相信你也知道该怎么办了,请使用handlebars引以为豪的helper,这个东西很强大可以做各种各样的东西,但是很小的逻辑也需要用他来写,这就尴尬了,例如你想写一个两值相加,你需要这样写 hbs.registerHelper("sum", function() { return arguments[0] + arguments[1]; }); <div class="listCase rank{{sum @index 1}}"></div>
如果你想要在模板中写一个时间格式化那么需要这样写 hbs.registerHelper('timeFormat', function(leftTime) { if (leftTime <= 0) return '剩余0天00时00分00秒'; var seconds = leftTime % 60; if (seconds < 10) seconds = '0' + seconds; var minutes = Math.floor(leftTime % 3600 / 60); if (minutes < 10) minutes = '0' + minutes; var hours = Math.floor(leftTime % 86400 / 3600); if (hours < 10) hours = '0' + hours; var days = Math.floor(leftTime / 86400); return '剩余' + days + '天' + hours + '时' + minutes + '分' + seconds + '秒'; }); <div>{{timeFormat 10000}}</div>
根据代码来看helper是既当爹又当妈把全部需要用到逻辑代码的地方都囊括了,所以这也就造成了在写模板时经常会发现没有这个逻辑,然后就跳过去写个helper再继续写模板,只能说handlebars这个引擎模板可强可弱,如果写一些比较复杂的模板页面那么很好很强大,写简单的自然很弱很鸡肋,还不如一般的模板引擎好用。 上面这些是行级helper的代码,如果你想写一个像if的块级helper的话可以这样写 // 实现单一参数大于1时,渲染代码块 hbs.registerHelper("more", function(num, options) { if (num > 1) { return options.fn(this); } }); {{#more 2}}hello node{{/more}}
众所周知在nodejs中写js有个很爽的地方,那就是可以尽情的使用ES6标准来写代码,甚至可以使用ES7的新特性来写,真是让jser爽到不行,但是传说中号称解决回调地狱的ES6异步解决方案Promise真的有那么好用吗?这里将带你认识一下promise的一些坑的地方!
由于我们是使用nodejs来做中间层首屏渲染的,经常会遇到这两种情况:1、同时去获取几个接口(部分有依赖关系)一起将数据渲染出来,那么这个时候我们就需要用到Promise来解决了,否则就很可能会陷入回调地狱。2、如果这些接口 没有依赖关系,那么这个时候你可以使用正常的ajax请求然后通过计数等待的回调函数进行统一渲染视图,例如接口有三个那么伪代码如下: //此处使用jq的ajax来模拟,不要当真这么写。 var a = 0, b = 0, c = 0, data = []; // 存储数据 // 全部数据返回后的回调 var callback = function(data){data.forEach(function(){...})} $.ajax({ url: '/node', type: 'GET', success: function(json){ a = 1; json.status === 0 ? data.push(json) : data.push([]); if(b===1&&c===1) { callback(data); } } }) //另外两个请求也是类似写法
但是这么写很不优雅而且要自己计数的话代码可读性也不好,当数据越大的时候越恶心还不如用Promise,那么要怎么做呢?
解决方法:1、如果N个接口部分有依赖的情况下有两种解决办法,一是使用Promise然后不可避免的陷入了疯狂的thenthenthen之中;二是要么直接跳过ES6使用史上最强的异步解决方案ES7新特性async await进行处理,可是这个方案在旧的node版本中支持不咋地,为了兼容性,只能放弃或者是全体升级node!
其实这个情况下也并没有那么绝望,因为接口不可能每个都要依赖于另一个接口,那么如果4个接口只有一个接口是依赖于另一个接口的,那么这个情况下该怎么做呢,其实就是问题1和问题2糅合起来之后的解决办法,方法如下:
Promise有个方法叫all,all有个数组作为参数,这个数组就是由各种Promise对象组成的,Promise.all会等到所有异步都请求回来后进入then方法,但是有个问题,数组中任何一个Promise对象进入catch后执行了reject而不是resolve,那么Promise.all就不会进入then而是catch,然后整个页面都没有数据了,哪怕你其他的接口是成功的返回了数据,因此我们需要在各自的Promise对象中特殊处理一下,我的整个方法是写在request.js中的,所有的接口都会经过该js统一去请求,代码如下: // 统一封装好的请求方法 var url = config.hostname + path; console.log(url); var options = { uri: url, port: config.port, headers: { 'User-Agent': 'Request-Promise', 'Content-Type': 'application/json; charset=utf-8' }, json: true }; if(headers) { headers.cookie ? (options.headers.Cookie = headers.cookie) : ''; // 判断是否需要cookie,可省略 } var promise = new Promise(function(resolve, reject) { rp(options) // rp即request-promise库 .then(function(data) { if(data.ret === 0) { console.log(path + '数据成功返回:' + JSON.stringify(data)); resolve(data); } else { console.log(path + '数据返回错误码:' + JSON.stringify(data)); resolve(data); // 不管是否报错都resolve数据 } }) .catch(function(error) { console.log('请求' + path + '报错:' + JSON.stringify(error)); resolve(error); // 不管是否报错都resolve数据 }); }); return promise;
其实在这次的独立项目开发中还是遇到了不少问题,例如nginx的配置,使用seajs后sdk的压缩和node的一些问题等等,至于其他的非node的问题下次另写一篇文章或者是流程规范讲解,请大家敬请关注wiki和ued,错过了或许你就要踩多几个坑了!
大家在遇到这些问题的时候,第一时间先是自己解决一下,如果实在是解决不了不要钻牛角尖赶紧问人,毕竟时间是有限的,在产品开发中根本不会给太多的时间让你去解决一些莫名其妙的问题,多交流对于团队来说很重要共同学习共同进步。
看完这几点,如果还有一些是这里没有解决的可以直接来问我,或者是问白大神,苍大神和周大神!