应该每个前端开发者都有一颗全干全栈的心💗吧。 那就让云开发知足你javascript
云开发一出来就开始玩,云数据库,云函数,全栈的体验和开发速度,真的不是通常的爽。 接下来工做中要开发一款新闻类小程序,因而就开始了对头条君的调研,此篇文章,是个人我的总结和分析,欢迎大佬拍砖。css
看看效果图:(没有展现全,下面有更多配图以供学习这个项目)html
小程序云开发是什么???前端
开发者可使用云开发开发微信小程序、小游戏,无需搭建服务器,便可使用云端能力。java
云开发为开发者提供完整的云端支持,弱化后端和运维概念,无需搭建服务器,使用平台提供的 API 进行核心业务开发,便可实现快速上线和迭代,同时这一能力,同开发者已经使用的云服务相互兼容 > ,并不互斥。android
目前提供三大基础能力支持:git
- 云函数:在云端运行的代码,微信私有协议自然鉴权,开发者只需编写自身业务逻辑代码
- 数据库:一个既可在小程序前端操做,也能在云函数中读写的 JSON 数据库
- 存储:在小程序前端直接上传/下载云端文件,在云开发控制台可视化管理
这是官方文档的描述github
其实简单的来讲小程序云开发是一款Serverless服务,开发者可使用它开发微信小程序、小游戏,无需搭建服务器,便可使用云端能力。目前提供云函数、数据库、存储三大基础能力支持。,而且将这些能力封装成特定的接口,以wx.cloud.xxx来进行调用。数据库
根据需求创建了三个集合json
三个集合经过id字段“链接”,要是个新手这时会想那么麻烦干吗,直接所有放一块儿,梭哈一下,全给拿过来,直接用。这确定是不行的,想一想要是这个数据多怎么办,你要让用户等你多久? 做为前端工程师确定是但愿给用户带来极佳的使用体验,因此你想在页面上展现什么,就设置一个对应的数据去关联,要什么取什么。后台数据无非就是一对一,一对多,你要啥数据就用传相关字段进云函数,在云函数里进行简单的增删改查。
千万记住,要考虑你的集合数据的使用范围进行权限设置,好比我添加的是articles文章,那这是公开的。那我就应该在权限设置中修改成全部用户可读、仅管理员可写,默认的是仅建立者及管理员可读写。
如何建表,表和表之间的联系,在动手项目以前要考虑好,避免表里的内容重复致使内存浪费,在能实行其功能的基础上作到不浪费内存。我上面的表建的就有点问题,图片URL的地址存错了地方,并且在两表里都存储了,你们作的时候能够吸收个人教训。
头条功能那么可能是不可能写的完,咱们在这里就选择其首页、新闻详情、登陆界面这部分来实现一下。
首页推荐:头条里面有不一样的新闻样式,有无图的,一张图的还有三张图的,因此咱们确定要分离出不一样的模板,根据后台传过来的数据去判断新闻的样式最后在页面中显示出来 数据驱动页面
新闻详情:打开新闻详情页,分析以后发现大致分为三个部分 头部的做者信息 新闻内容 底部的用户评论 这些信息都是在云开发数据库中不一样的集合里面,这些数据的提取操做封装在云函数里面以便调用 这三部分都是重复的结构抽离出来做为组件,组件很是的灵活,这样作的好处是页面结构将会更加的清晰,增长代码的复用性,而且耦合度下降,后续程序的维护也更为方便。
登陆界面:直接使用wx.getUserInfo得到用户信息 后面会贴出js代码
该页面采用顶部的固定搜索栏、scroll-view和swiper内容区三个模块,三个模块都可采用绝对定位,搜索栏flex布局,swiper内容区内swiper-item有关注,推荐,热点,南昌和视频
<scroll-view class="navBar-box" scroll-x="true" style="white-space: nowrap; display:flex ">
<view class="cate-list {{curIndex==index?'on':''}}" wx:for="{{category}}"
wx:key="{{item.id}}" data-id="{{item.id}}" data-index="{{index}}" bindtap="switchCategory">
{{item.name}}
</view>
</scroll-view>
复制代码
在jsdata里面 设置curIndex, toView,用以监控不一样版块的状态 实现了将上面的scroll-view 和下面的文章swiper创建关系
<swiper class="notes" current="{{curIndex}}" bindchange="swiperchange">
<swiper-item class="focus">
...
</swiper-item>
<swiper-item class="category" wx:key="{{item.id}}">
<scroll-view class="cate-box" scroll-y="true" bindscrolltolower="loadarticles">
<view class="note" wx:for="{{notes}}" wx:for-item="note" wx:key="{{index}}">
<block wx:if="{{note.image.length < 3 }}">
<template is="templateone" data="{{...note}}"></template>
</block>
<block wx:elif="{{note.image.length >= 3}} ">
<template is="templatetwo" data="{{...note}}"></template>
</block>
</view>
</scroll-view>
</swiper-item>
</swiper>
复制代码
不一样的新闻显示模板 使用组件化的概念,建立一个template文件夹
<template name="templateone">
<view class="newList">
...
</view>
</template>
<template name="templatetwo">
<view class="newList">
...
</view>
</template>
复制代码
在须要用到模板的地方就能够直接使用
<import src="../../components/template/template.wxml"/>
复制代码
既然要用到模板那接下来咱们就把模板给写出来。分析一下模板里面的内容
里面的数据除时间之外都是能够直接从后台调取在页面上显示出来的数据,但时间不同,它是变化的 在数据库中time字段以时间戳的形式保存。在后面的详情页中也要用到时间的格式化,so写一个js封装起来
在小程序中咱们时时刻刻须要去请求数据,数据的调取封装起来是极好的,存在util里想用的时候拿一下就ok
wx.cloud.init();
const db = wx.cloud.database();
const notes = db.collection('articles');
// 加载notes,page=1默认形参 ,limit = 4 ,fn
const loadNotes = (fn,page = 1,limit = 4) =>{
//return 数据集 异步
const skip = (page -1) * limit;
notes
.count()
.then(() =>{
return notes
.limit(limit)
.skip(skip)
.get()
})
.then(res =>{
//console.log(res.data)
fn({
data:res.data
})
})
// fn(data);
};
module.exports = {
loadNotes,
}
复制代码
WXS 是小程序的一套脚本语言,结合WXML,能够构建出页面的结构。了该一哈
新建一个timeapi.wxs文件,在template.wxml引用,定义模块名便可引用:
<wxs src="../../utils/timeapi.wxs" module="timeapi" ></wxs>
.....
<text class="newList-item-time">{{timeapi.formatTime(time)}}</text>
复制代码
timeapi.wxs文件和时间格式实现方法:
var formatTime = function (time) {
// 获取当前时间
var getUnix = function () {
var date = getDate()
return date.getTime()
}
// 获取今天零点时间
var getTodayUnix = function () {
var date = getDate()
date.setHours(0)
date.setMinutes(0)
date.setSeconds(0)
date.setMilliseconds(0)
return date.getTime()
}
// 获取今年的1月1日零点时间
var getYearUnix = function () {
var date = getDate()
date.setMonth(0)
date.setDate(1)
date.setHours(0)
date.setMinutes(0)
date.setSeconds(0)
date.setMilliseconds(0)
return date.getTime()
}
// 获取标准时间
var getLastDate = function (time) {
var date = getDate(time)
var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
var day = date.getDay() < 10 ? '0' + (date.getDay()) : date.getDay()
return date.getFullYear() + '-' + month + '-' + day
}
// 转换时间
var getFormatTime = function (timestamp) {
var now = getUnix()
var today = getTodayUnix()
var year = getYearUnix()
var timer = (now - timestamp) / 1000
var tip = ''
if (timer <= 0) {
tip = '刚刚'
} else if (Math.floor(timer / 60) <= 0) {
tip = '刚刚'
} else if (timer < 3600) {
tip = Math.floor(timer / 60) + '分钟前'
} else if (timer >= 3600 && (timestamp - today >= 0)) {
tip = Math.floor(timer / 3600) + '小时前'
} else if (timer / 86400 <= 31) {
tip = Math.ceil(timer / 86400) + '天前'
} else {
tip = getLastDate(timestamp)
}
return tip
}
return getFormatTime(time)
}
module.exports = {
formatTime: formatTime
}
复制代码
wxs有优势有缺点,用的时候要考虑好。注意如下三点。
详情页涉及到三个表的内容。那就在云函数里完成表的查询组装输出,多方便啊。 这个代码就不贴了,要参考的话,底部会把GitHub贴出来,目录在上面的图片。 使用云函数的话有个技巧。在开发工具里看是否成功,且把数据传输过来了。 如图。
wxParse-微信小程序富文本解析自定义组件,支持HTML及markdown解析本
githup 上有使用方法,这里我就不作重复的描述 点着 我这里使用这个工具彻底是为项目实现的而去作,若是真正作一个小程序新闻类项目确定是不太好的,先不说wxParse解析可能会出现乱码,再就是它所占的内存也不小。真正开发的时候就别用了,数据库里的数据也不会是html格式的。
在手机上显示时(坑)
wxparse 代码的一个 bug,在一些特殊的手机里面,在 wxparse/html2json.js 中的第 112 和 119 行,都有一个 console.dir 这个函数的使用,把这个函数注释掉,内容就能够正常显示出来
评论组件
点赞的变化是由用户产生的一个交互,传统的观点就是用户点赞->后端更新数据->前端拉取数据->数据驱动视图的变化。 真实的体验就是,很是的慢,慢到点击后2秒才能看到点赞的效果,这种差劲的交互简直就是一场灾难。
那咱们就先把样式给用户表现出来,数据交给后台慢慢异步处理
页面布局就没啥好说的,
getUserInfo
button type="bindUserInfo"
auth 受权登陆
高阶函数
异步的处理
html部分
<view wx:if="{{auth === 0}}" > //判断登陆状况
<button open-type="getUserInfo"
bindgetuserinfo="getUserInfo">
登陆
</button>
</view>
<view wx:else>
<view class="top">
<image class="avatar" src="{{avatarUrl}}"/>
<view class="name">
<text class="nickname">{{nickname}}</text>
<button>申请认证</button>
</view>
<icon type="zhixiang" color="#848484"></icon> //封装的icon
</view>
复制代码
//获取应用实例
const app = getApp()
const globalData= app.globalData;
Page({
data: {
auth:-1,
nickname:'',
avatarUrl:''
},
onLoad: function () {
this.getScope(this.getUserInfo,()=>{
this.setData({
auth:0
})
});
},
//高阶函数 success 参数也是一个函数
getScope(success,fail,name='scope.userInfo'){
wx.getSetting({
success:(res) => {
// console.log(res);
if(res.authSetting[name]){
success();
}else{
fail();
}
}
})
},
getUserInfo (){
// console.log('userinfo')
if(!globalData.nickname||!globalData.avatarUrl){
// 1. wx.getUserInfo(nickname,avatar)函数
// 2. 放到全局 函数
this._getUserInfo((res) =>{
// console.log(res);
this.setData({
nickname:res.nickName,
avatarUrl:res.avatarUrl,
auth:-1
});
globalData.nickname=res.nickName;
globalData.avatarUrl=res.avatarUrl;
});
}
},
_getUserInfo(cb = () =>{}){
wx.getUserInfo({
success:(res) =>{
cb(res.userInfo);
}
})
}
})
复制代码
将平凡的事坚持下去,就会变的不平凡,路漫漫其修远兮,吾将上下而求索。
您的鼓励是我前行最大的动力,欢迎点赞,欢迎送小星星✨ ~