微信小程序中使用npm
- 在微信开发者工具中的设置菜单下找到“项目设置”,勾选“使用npm模块”。
- 在微信开发者工具中找到page文件,然后右键选择-在终端打开。在终端打开后,因为是在page文件下右键打开的终端,所以当前的文件是page,使用"cd .."命令回到上一层,也就是当前项目的根目录层,使用" npm init -y "命令创建项目,其实创建的是package.json这个文件。
- 这时可以使用npm安装需要的组件。注意的是安装完需要的组件后,微信开发者工具的目录文件中并没有node_modules这个目录。这时需要选择微信开发者工具栏菜单中的工具-构建npm。
- 当前项目的根目录下会显示一个miniprogram_npm的目录,这个目录代替了node_modules这个目录。
使用lin-ui
lin-ui只是一个ui组件库。 lin-ui在小程序中的使用。 lin-ui在小程序中的注册是如下,是在index.json的文件里边进行注册的,如下代码:
{
"usingComponents": {
"li-button":"/miniprogram_npm/lin-ui/button/index"
}
}
注册组件后就可以直接使用这个<lin-button>的这个组件了。
lin-ui的点击事件
小程序正常的点击事件是bind的,但是我们使用的是lin-ui这个框架,所以点击事件是bindtap。如下代码显示:
<li-button size="long" bindtap="onLogin">
获取token按钮
</li-button>
小程序登录的代码
success(res){
if (res.code) {//表示微信登录小程序没问题
//发起网络请求,使用的方式与jq调用ajax特别的像
wx.request({
url: 'http://localhost:3000/v1/token',//设置-->项目设置--https证书的勾选需要勾上,否则访问的协议是https协议。
method:"post",
data:{
account:res.code,
type:101
},
//请求成功返回的结果,失败也会返回结果
success(r){
//r.data是返回来的数据,返回来的数据必须是2开头才是正确的token
if(r.statusCode.toString().startsWith("2")){
wx.setStorageSync({
key: 'token',
data: r.data.token
});
}
}
})
}else{
console.log("登录失败:"+res.errMsg);
}
}
小程序中本地保存的方法
wx.setStorageSync({key:value,data:value}); wx.getStorageSync(key);//获取本地储存的方法
sequelize创建表
在创建表的时候如果携带了tableName的字段,表示的是表的名字。
生成了3个表,分别是:movie music setence 还有 flow表,这些表都是需要我自己创建的。手动创建,哭死。
sequelize中排序的使用
如下代码:
//取出最新的一条数据,是不需要用where条件的默认就是取出第一条数据
const resOneFlow=await Flow.findOne({
//是可以对很多的字段进行多次排序所以会是多个数组的形式
order:[
["index_id","desc"]
]
});
sql语句
创建数据表的语句:
小程序的Authorization的实现
在微信小程序中使用header携带令牌
Authorization是Basic:+base64处理token的格式,所以我们需要在小程序端实现这个格式。然后通过header中的Authorization传递给服务端进行验证。 在小程序中使用第三方插件js-base64。 如下代码:
//在微信小程序中使用header携带令牌
verifyAuthToken(){
wx.request({
url: 'http://localhost:3000/v1/classic/auth',
method:"post",
header:{
Authorization:this._encode()
},
success(res){
console.log(res);
}
})
},
//auth验证中的一个方法
_encode(){
const token=wx.getStorageSync("token");
//这里是固定的写法,只有这样才是和其他的authorization的请求方式是一样的。
return "Basic "+Base64.encode(token+":");
}
mysql中的第3个表的作用
在mysql数据表中有一种现象是,第三个表来存储两个表之间的关系。 在这里的第三个表是存储其他表的索引信息,然后通过第三个表来获取其他两表的关系。
点赞功能的逻辑与model的实现
首先确定点赞的模型Favor,如下代码:
/**
* 这个表示是存储用户是否对这个期刊进行了点赞,没点赞一次就往这里边添加一条数据,否则删除一条数据。
* 所以是用户字典
* type 和 art_id 才能够确定是哪个期刊
*/
Favor.init({
uid:Sequelize.INTEGER,
art_id:Sequelize.INTEGER,
type:Sequelize.INTEGER
},{
sequelize,
tableName:"favor"
});
点赞功能的实现代码和注释
这个大概是点赞表的逻辑。点赞表的表名是favor。
表的结构是uid,type,art_id; 判断uid、type、art_id这样的数据是否存在。如果存在 还有就是添加时,那么会往相对应的在期刊表里边的fav_nums字段增加1。 如果不存在则是取消点赞,则虚拟删除该记录,同时相对应的期刊也减少fav_nums数字的值。
使用了sequelize的事务transaction。
点赞,如果没点添加到favor数据到表
并且还要往该期刊中的fav_num属性中增加个数
这两个需要同时进行,所以需要用到数据库的事务
sequelize.transation(t=>{
//这个里边的方法要同时执行完毕才可以
});
sequelize的模型的increment("字段",{by:num,transaction:t});表示的是某个字段加num
如果是减少是decrement
看下具体的代码:
//返回的变量
let resArtData=null;
if(fav){//如果存在则是取消操作
console.log("favId:"+fav.id);
await sequelize.transaction(async t=>{
await fav.destroy(
{
"where":{
id:fav.id
}
},
{transaction:t});
});
const artData=await Art.getData(art_id,type);
await sequelize.transaction(async t=>{
resArtData=
await artData.decrement("fav_nums",{by:1,transaction:t});
});
return resArtData;
}else{//否则是增加操作
console.log("no exit");
await sequelize.transaction(async t=>{
//往favor表里边添加数据
await Favor.create({
uid,
art_id,
type
},{transaction:t});
});
//同时往对应的期刊里边增加一条数据,返回的是sequelize的model数据表模型
const artData=await Art.getData(art_id,type);
//记住transation和transaction t的区别。单词是不一样的。
await sequelize.transaction(async t=>{
resArtData=
await artData.increment("fav_nums",{by:1,transaction:t});
});
return resArtData;
}
}
like的路由接口:
router.post("/",new Auth().m,async ctx=>{
let tokenData=ctx.auth.uid;
//验证参数是否符合标准
let v=await new LikeValidate().validate(ctx);
const type=v.get("body.type");
const art_id=v.get("body.art_id");
//通过用户表获取用户id
const resUser=await User.findOpenId(tokenData.uid);
// console.log("::::"+typeof resUser.id,typeof art_id,typeof type);
let resFavor=await Favor.Makefavor(resUser.id,art_id,type);
ctx.body={
data:resFavor
}
});
用户查询所有点过赞的数据
逻辑: 首先是两个表,一个是fvaor表,通过这个表可以查询出用户点过赞的所有的期刊,然后通过期刊表,查询到具体被点赞的期刊。
期刊只包括,music movie sentence 不包括book。所以要把type等于400的book去掉。
切记:不能够使用for循环,在数据库进行查询,如果使用for循环进行数据库的连接和查询,对数据库来说效率是非常低的。
问题:squelize物理删除
desctroy不是物理删除,只是在deletedAt上填充时间而已,在虚拟删除之前是NULL; 目前没有找到物理删除。
找到的是虚拟删除。一旦deletedAt有时间则表示已经删除,则查询语句是查不出的。
数据库的效率
1,循环查询数据库是非常危险的行为,对于数据库的性能是特别不好的,一定要避免。
关于json的key和switch的case的数据类型
json的key,切记不管填写的是什么,它的类型都是字符串类型。 switch进行判断是===号判断,所以判断前最好做一个类型的转换。
获取当前用户所有点过赞的期刊
这里要涉及到3个表。
当点赞表里获取到的所有数据和期刊表是一条一条对应的,如果不用for循环一条一套查询。那么这里有一个更好的办法,就是通过type分类进行查询比较好。 type分成3类,那么通过找到它,然后用in,先满足type,然后art_id在in里边符合的就查找出来。因为type只有3类,所以只要向数据库查询3次。
重要代码如下:
//获取用户点赞过的所有数据的方法
static async getUserAllLike(uid){
//获取用户所有点过赞的所有的数据的art_id 和 type
const resAllFavor=await Favor.findAll({
where:{
//关于Op和model相对应的使用可以参考文档,表示的是type不等于400的所有值。book是不属于期刊的。Op是需要从sequelize里边引入的。
type:{
[Op.not]:400
},
uid
}
});
//通过type把数据进行分类,组成一个集合
let arts=await Favor._classifycationLike(resAllFavor);
//分类好的数据进行遍历查询数据库
let artData=[];
for(let key in arts){
let type=parseInt(key);
console.log("arts[key]",arts[key]);
let where_={
where:{
type:type,
id:{
[Op.in]:arts[key]
}
}
};
let r=null;
if(arts[key].length!=0){
switch(type){
case 100:
r=await Music.findAll(where_);
artData.push(r);
break;
case 101:
r=await Movie.findAll(where_);
// artData.push(r);
// console.log(r);
if(r.id==undefined){
artData.push(r);
}
break;
case 102:
r=await Sentence.findAll(where_);
artData.push(r);
break;
default:
new Error("没有符合的type类型");
break;
}
}else{
continue;
}
}
// console.log(resAllFavor);
return artData;
}
//数据分类的内部函数
static async _classifycationLike(allLikeFavor){
//定义一个已经分类好的数据结构
let arts={
"100":[],
"101":[],
"102":[]
}
//通过for循环遍历每天数据然后进行分类
for(let key in allLikeFavor){
// console.log(allLikeFavor[key].type);
switch(allLikeFavor[key].type){
case 100:
arts["100"].push(allLikeFavor[key].art_id);
break;
case 101:
console.log("000");
arts["101"].push(allLikeFavor[key].art_id);
break;
case 102:
arts["102"].push(allLikeFavor[key].art_id);
break;
default:
break;
}
}
return arts;
}
技巧 :如果数据多并且数据是一一对应的,那么分类,先满足某个分类,然后用in集合一次性获取出来。 当然也可以用分类和where条件配合使用。
JavaScript循环导入的坑
在文件的头部进行引用其他文件,如果A.js使用require引入B.js,而B.js又引入了A.js很容易会出现其中一个文件引入后出现undefined的效果。所以尽量避免在文件头部进行引用,而是哪里使用这个文件里的返回结果就在哪里引入。
为什么会出现这种情况呢?
是因为A.js在引入B.js时,要执行B.js里边的代码,但是B.js里边又引入了A.js,但是是A.js先引入B.js的,那么B.js再引入就不合逻辑,因为A.js正在执行,所以就会出现坑。
如下是A.js代码:
const{B}=require("./b");
class A{
}
console.log(B);
module.exports={
A
}
如下是B.js的代码:
const{A}=require("./A");
class B{
}
console.log("A",A);
module.exports={B}
热门书籍的相关的接口设计
热门书籍的modle的设置
const {Sequelize,Model}=require("sequelize");
const {sequelize}=require("./../core/db");
class HotBook extends Model{
}
HotBook.init({
index:Sequelize.INTEGER,
title:Sequelize.STRING,
author:Sequelize.STRING,
img:Sequelize.STRING
},{
sequelize,
tableName:"hotbook"
});
sequelize.sync();//自动创建表
module.exports={
HotBook
}
forEach在遍历异步操作的坑
数组的forEach在遍历异步的时候我们使用async和await也不会如愿的操作。所以只能是尽量去避免。
并行和并发
并行:一个时间执行多个进程。真正的多线程。
并发:看起来像并行,但是其实是cpu足够快,并不是真正的一个时间内执行多个进程。nodejs之所以高并发是因为他模拟并行的很好(宏观任务和围观任务)和事件池有关的。
nodejs的优势,对服务器cpu的要求低。单线程解决并发的问题。
热门书的查询结果
先条件查询再分组。
然后将统计好的数据给这个对象做属性。
具体的代码:
//返回热门书籍表里边的所有的书籍并且进行排序。将热门书籍里边的书籍的id取出组成一个数组。
let resOrder=await HotBook.findAll({
order:["index_"]
});
//把所有的id都放入到一个数组里边,方便进行查询
let arrId=[];
for(let key in resOrder){
arrId.push(resOrder[key].id);
}
//原生mysql解决这查询结构后分组的问题
let resFavor=await sequelize.query(`select art_id,count(id) as count from favor where type=104 and art_id in(${arrId}) group by art_id`,{model:Favor,mapToModel:true});
//把返回的每本书对象添加一个count的属性
resOrder.forEach(b=>{
//每本书添加属性的方法
HotBook.setBookToArr(b,resFavor);
})
//
另外一个方法:
//把循环数组是否将数组中的某个元素的某个值放入到对对象中
//将数组中与book相等的元素的count增加属性给book这个对象
static setBookToArr(book,favors){
favors.forEach(favor=>{
if(book.id==favor.art_id){
book.setDataValue("count",favor.get("count"));
}
})
return book;
}
两个获取数据的网站
使用util.format(xxx,id);即可以进行格式化。
使用axios向服务器获取详细的数据
async getDetail(){
//第三房服务器获取数据的网址是配置到config的文件里边的
let url=util.format(config.yushu.detailUrl,this.id);
let resData=await axios.get(url);
return resData.data;
}
前端+后端 叫做大前端。但是我们的数据一般是放在服务器上的。 所以叫服务器端。
微服务其实就是把服务器分成一个小的服务,这个服务器与其他的服务存在的不一样的地方,这样去处理的话比人性化些。如有些业务就是访问量很高,那么我们优化负载均衡等都可以对这个服务操作就可以。
服务端其实把数据抛出来,但是如何返回给前端,就是我们后端操作。这样也就减少服务端的工作。 例如后端,把各个服务器上查询到的数据进行综合返回给前端,或者是筛选后返回给前端。
搜索的接口
借用第三方的接口实现的。
用户的所有点赞的一个接口
//用户获取图书点赞情况的接口
static async getBookFavorNumber(userid){
//通过用户登录的账号名获取用户的id
let user=await User.findOne({
where:{
openid:userid.uid
}
});
//通过用户的id来查询出用户的点赞情况
let count=await Favor.count({
where:{
uid:user.id,
type:104
}
});
return {user,count};
}
图书点赞情况和用户是否点赞的一个接口
这个图书被点赞的次数和是否被当前用户点赞。
//这个图书被点赞的次数,和是否被当前用户点赞
static async getBookFavor(userid,bookid){
console.log(userid);
//通过用户登录的账号名获取用户的id
let user=await User.findOne({
where:{
openid:userid.uid
}
});
//查询book被点赞的次数
let count=await Favor.count({
where:{
type:104,
art_id:bookid
}
});
//是否当前用户也点过赞
let favor=await Favor.findOne({
where:{
art_id:bookid,
type:104,
uid:user.id
}
})
//如果存在则是点过赞,否则是没有点赞的
let like_status=true;
if(favor.id===undefined){
like_status=false;
}
return {count,like_status}
}
数据序列化
就是在返回给前端数据的时候,在父亲的原生连或者是公用的地方写上一个方法,这个方法是用来返回给前端数据时,只显示某些数据或者不显示某些数据。
sequealize中Model使用构造函数的坑
在sequelize中尽量不要使用construcotr,否则的话会出现有些字段是无法查询出来的。