LeanCloud+Node.js学习笔记

// 数据存储篇

//include指针save注意点 因为offer.save的时候owner作为pointer已经save了,所以不用再单独save了,如果save,则会重复操作
offer.fetch({ include: ['owner'] }).then(function(offer){
var owner = offer.get("owner");
owner.increment(product, -amount);
offer.increment("amount", -amount);
offer.save(); //不要再单独save owner
}

怎么双重include
比如battle include country, country再include countryImage
解答: file类型的不需要include,直接get
对于其他类型的指针,battle.include('country.countryImage')

空的attriubte值为undefined
将attribute值设为undefined
city.unset('warPending');
return city.save().then(function(res){
alert("设为undefined成功!");
}, function(err){
alert("设为undefined失败");
});

新建对象
var Todo = AV.Object.extend("todo"); //在todo表创建Todo Object
保存对象
Todo.save().then(function(todo){ //保存Todo Object
});
获取对象
var query = new AV.Query("todo"); //query todo表
query.get("objectId").then(function(todo){ //设置查询条件 objectId
});
获取对象属性
var query = new AV.Query('Todo');
query.get('objectId').then(function (todo) {
//正常获取属性的方法
var location = todo.get('location');
或者
var location = todo.attributes.location;
//返回值的自带属性
var id = todo.id;
//一次性获取所有属性
console.log(todo.toJSON());
同步对象(确保本地数据和云端同步)
todo.fetch(
keys: 'priority,location' //可以指定刷新的属性或者不填
).then(function(todo)
});
更新对象(通过使用保存选项,query可以按照指定条件去更新对象——当条件满足时,执行更新;条件不满足时,不执行更新。)
var Account = AV.Object.extend('Account'); //获得Account表的object
new AV.Query(Account).first().then(function(account) { 查询对象的条件设定为 1.为Account表的object 2.第一个查询结果
var amount = -100;
account.increment('balance', amount); //原子操作的增加或减少,不怕并发
return account.save(null, { //保存经修改的查询对象,这里指定条件
query: new AV.Query(Account).greaterThanOrEqualTo('balance', -amount), //Save object only when the object matches the query
fetchWhenSave: true, //fetch and update object after save succeeded
});
}).then(function(account) {
// 保存成功
console.log('当前余额为:', account.get('balance'));
}).catch(function(error) {
if (error.code === 305) {
console.log('余额不足,操作失败!');}
});
删除对象
var todo = AV.Object.createWithoutData('Todo', '57328ca079bc44005c2472d0');
todo.destroy().then(function (success) {});

pointer存储
关联一个不存在的对象,例如将「广州市」添加至「广东省」:
var GuangZhou = new AV.Object('City');
GuangZhou.set('name', '广州');
var GuangDong = new AV.Object('Province');
GuangDong.set('name', '广东');
GuangZhou.set('dependent', GuangDong);// 为广州object 设置 dependent pointer 为广东
GuangZhou.save(); // 广东无需被单独保存,因为在保存广州的时候已经上传到云端。

关联一个已经存在的对象,例如将「东莞市」添加至「广东省」:
var GuangDong = AV.Object.createWithoutData('Province', '56545c5b00b09f857a603632'); // 假设 GuangDong 的 objectId 为 56545c5b00b09f857a603632
var DongGuan = new AV.Object('City');
DongGuan.set('name', '东莞');
DongGuan.set('dependent', GuangDong);
DongGuan.save();

pointer查询 ---记得query前先include pointer所指的表
假如已知一个城市,想知道它的上一级的省份
var DongGuan = AV.Object.createWithoutData('City', '568e743c00b09aa22162b11f');
DongGuan.fetch({ include: ['dependent'] }).then(function (city) {
var provinceName = city.get('dependent').get('name');

});

假如查询结果中包含了城市,并想通过一次查询同时把对应的省份也一并加载到本地:
var query = new AV.Query('City');
query.equalTo('name', '广州');
query.include(['dependent']);
query.find().then(function (result) {
if (result.length > 0) {
var GuangZhou = result[0];
var province = GuangZhou.get('dependent');
}
});

假如已知一个省份,要找出它的所有下辖城市:

var GuangDong = AV.Object.createWithoutData('Province', '56545c5b00b09f857a603632');
var query = new AV.Query('City');
query.equalTo('dependent', GuangDong);
query.find().then(function (cities) {
cities.forEach(function (city, i, a) {
console.log(city.id);
});
});

云引擎里定义云函数
AV.Cloud.define('averageStars', function(request) {
var query = new AV.Query('Review');
query.equalTo('movie', request.params.movie);
return query.find().then(function(results) {
var sum = 0;
for (var i = 0; i < results.length; i++ ) {
sum += results[i].get('stars');
}
return sum / results.length;
});
});

在SDK中调用云函数
// 在 JavaScript 中 AV.Cloud 提供了一系列方法来调用云函数
var paramsJson = {
movie: "夏洛特烦恼"
};
AV.Cloud.run('averageStars', paramsJson).then(function(data) {
// 调用成功,得到成功的应答 data
}, function(err) {
// 处理调用失败
});

package.json规定启动时启动server.js (启动服务器)
server.js中启动了app.js (服务器里启动app)
app.js启动了cloud.js (加载functions目录下和cloud.js中定义的所有云函数)

req 请求的数据
res 回应的数据
next 如果当前middleware的函数不会结束要求回应循环,必须呼叫next

将response render到todos.ejs里,
<%= title %> 设置成TODO列表
<%= todos %> 设置成results
res.render('todos', {
title: 'TODO 列表',
todos: results
});

双击highlight所有occurance
alt+j选中下一个occurance alt+shift+j取消选中下一个occurance
f3 navigate between hightlighted

// ..
node.js学习手册
两种风格API
第一种callback
callback的返回值参数列表第一个一定是err,如果有额外的返回值,那么就在第二个
例如一下代码(check stats) 第一个返回值是err,第二个才是stats
fs.stat('/tmp/world', (err, stats) => {
if (err) throw err;
console.log(stats: ${JSON.stringify(stats)});
});
第二种event(XMLHttpRequest)

// ..
第一期 node.js介绍和异步风格
event风格异步回调API
var redis = require("redis");
client = redis.createClient();
client.on("error", function (err)){
console.log("error" + err);
}
on event 有 ready, connect, reconnecting, error, end ...
callback 和 event怎么选择
如果进程非常快速,能很快得到结果,一般设计成callback
如果进程非常慢及持续时间长(比如下载4GB),那么使用callback(读完4GB再callback写给用户)不实际
用event风格设计成读1MB给用户写1MB ls.stdout.on('data', (data) =>)

处理异常
同步用try catch,异步用callback的rr对象
有时候觉得自己出了异常但是没有拿到,可能是同步的没有处理,例如

const fs = require("fs");
const x = y + z;
fs.rename('hello', 'world', (err) => {
if (err) throw err;
console.log('rename completed')
});

const x = y + z;导致的错误
异步异常处理了,但是同步没有
所以最保险是在异步外面用try catch拦住可能产生的同步异常

try {
const fs = require("fs");
const x = y + z;
fs.rename('hello', 'world', (err) => {
if (err) throw err;
console.log('rename completed')
});
}catch (err){
console.log (err);
}

使用uncaughtException助力开发期
如果代码中没有异常处理,程序会把error一直抛抛抛,最后抛到控台,打印到stderr并程序崩溃
进程运行时,遇到uncaughtException后将error写入
注:process是全局变量, 任何地方都可以使用
process.on('uncaughtException', (err, origin) => {
fs.writeSync(
process.stderr.fd,
Caught exception: ${err}\n +
Exception origin: ${origin}
);
});

setTimeout(() => {
console.log('This will still run.');
}, 500);

nonexistentFunc();
//call一个不存在的程序,没有异常处理,如果没有process.on('uncaughtException'),程序直接崩溃,堆栈信息打印不出来
因为有,所以不会崩溃,error被抛出并被拦截,程序因为异常不会执行异常函数之后的代码,所以最后的console log this will not run不会被打印
error被处理,程序回归正常,在setTimeout执行完毕后正常退出
console.log('This will not run.');

// ..
第二期 npm包管理器
npm init 创建package.json 项目描述文件(需要哪些依赖等)
下载包 npm install (package_name) 加包名就是下载指定包,不加就是下载package.json里描述的包
--save 自动更新依赖 -g 全局安装(安装到了本地全局 /usr/local/lib/node_modules)
下载完后会有node_modules的包文件夹
npm list 当前项目的依赖列表
npm shrinkwrap 版本固化-冻结依赖 生成npm-shrinkwrap.json(包含node_modules目录里面所有依赖包)下次用npm install安装的时候
会按照shrinkwrap.json里的版本号安装

依赖管理
生产环境依赖 dependencies 运行依赖,编译依赖(babel)
测试环境依赖 devDependencies 测试依赖(npm install mocha --save-dev),开发工具依赖
优化依赖包存储: npm dedupe重新打乱然后解决依赖,或者删了node_modules,重新npm install一下

package.json里的 scripts 运行脚本
"scripts": {
//hook函数
"start": "node server.js", (start时可以自己做一下初始化的事情等,比如设置全局变量)
"dev": "nodemon server.js",
"postinstall": "echo 1111"
},
npm run start run-执行script start-执行script里的start
自带的hook函数不用run 比如npm start可以直接执行
但是自定义的就需要run, 比如npm run dev
leancloud的命令行工具 执行lean up时 间接执行了dev的script
云引擎指定node.js版本 详见网站托管开发指南里的package.json

// ..
第三期 开发工具介绍 curl, Postman, lean-cli
curl 命令行的http client
常用参数:
-X method(GET,POST),-H header(cookie等信息),-d data(内容)
curl -v web_address 详细的http请求

本机发到服务器
< 服务器返回本机
{"foo", "bar"} 传入jason格式参数(request payload)
curl -X POST http://localhost:3000/todos -d 'content=test'
curl 'http://localhost:3000/1.1/functions/hello' -H 'appId' -H 'appKey' -H 'Content-Type: application/json' (--data-binary'{"foo", "bar"}') (-d @data.json)

开启debug日志 启动的时候设置一下debug的变量
env DEBUG=leancloud:request lean up
Postman-软件
lean-cli-leancloud命令行
可以在云引擎的控台配置环境变量(比如不想写在代码中的key)然后通过process.env.xxxKey拿到这个值

// ..
第四期 Promise(处理对象和处理规则规范化:有些包写的不规范,可能第一个返回的不是error,以及是否返回一个对象还是多个params)
并且解决node.js回调金字塔
var promise = getAsyncPromise("fileA.txt");
promise.then(function(result){
// 获取文件内容成功时的处理
}).catch(function(error){
// 获取文件内容失败时的处理
});

创建promise对象, 使用new来调用promise的构造器进行实例化
var promise = new Promise(function(resolve, reject)){
});
promise.then(onFulfilled, onRejected);

promise.then()是异步的,所以
function A(){
var value;
query.find.then(function(){
value = 1;
});
return value;
}
结果是undefined,因为.then还没有执行完
那我们就要chain promise以解决这个问题
function A(){
var value;
let promise = query.find.then(function(){
value = 1;
return value;
});
return promise;
}
function B(){
A().then(function(value){
var i = value; //value = 1
});
}