本文目录:
- 1.单个页面的顶部文字设置
- 2.组件内调用字体图标的三种方法
- 3.slot插槽
- 4.判断授权情况并获取用户授权
- 5.上传图片到临时路径并实现本地预览
- 6.将博客存储到数据库
- 7.模糊查询
1.单个页面的顶部文字设置
在app.json的window属性中设置的navigationBarTitleText等内容都是全局生效的,可以在每个页面的json文件中再次设置进行覆盖.
在小程序中使用字体图标的时候可以使用专属的<i>标签,增强语义。
给placeholder设置样式,在标签中增加placeholder-class属性placeholder-class="in-bar"
,然后在in-bar中添加样式代码
.in-bar{
color: #999;
}
2.组件内调用字体图标的三种方法
在页面文件中可以自由调用字体图标样式,但是因为组件有样式隔离,所以在组件内部不能直接通过样式去使用字体图标。
解决方法一:
在组件文件夹里复制一个字体wxss文件,然后在组件内的wxss文件中通过import导入,这样就可以在组件内使用字体图标样式了
解决方法二:
调用组件的标签进行外部传入
<x-search iconfont="iconfont" icon-sousuo="icon-sousuo"></x-search>
组件中定义属性,是一个数组
externalClasses:[
'iconfont',
'icon-sousuo'
],
注意:外部传入的样式,组件内部无法进行修改
第三种方式:在组件的js文件中增加属性
options: {
styleIssolation: 'apply-shared'
},
这样做的好处就是可以直接对外部样式进行修改。
组件接收外部属性,如果定义了布尔类型的话,系统默认值就是false
3.slot插槽
小程序的组件也有slot插槽,如果是要设置多个slot,则要在options开启多个插槽的选项
options: {
styleIsolation: 'apply-shared',
multipleSlots: true
}
同时使用具名插槽,如<slot name="model-content"></slot>
引入这个组件的地方页面标签内部的元素也需要添加上slot属性,如<view slot="model-content"></view>
4.判断授权情况并获取用户授权
点击发布的时候,需要先判断用户是否已授权,下面这个会自动返回当前用户的授权信息,如果用户授权了,authSetting的scope.userInfo属性为true(未授权的话authSetting为空),然后再用微信提供的getUserInfo接口去获取到用户信息。
onPublish() {
wx.getSetting({
success: (res) => {
// 如果用户授权过,我们就去获取用户的信息
if (res.authSetting['scope.userInfo']) {
wx.getUserInfo({
success: (res) => {
this.onLoginSuccess({
detail: res.userInfo
})
}
})
} else {
this.setData({
modelShow: true
})
}
}
})
},
当还没有获得用户授权的时候,可以使用小程序button组件自带的开放能力open-type="getUserInfo"
来触发获取授权的弹窗,同时给按钮绑定获取用户的属性,bindgetUserinfo绑定的事件可以自定义用户允许或者拒绝后的处理事件,<button class="login" open-type="getUserInfo" bindgetUserinfo="onGotUserInfo">获取微信授权信息</button>
,在触发的事件中判定const userInfo = event.detail.userInfo
,如果userInfo存在,则代表用户点击的是同意授权,反之则是拒绝。拒绝之后以弹窗的形式提示用户
wx.showModal({
title: '授权用户才能发布博客',
content: '',
})
textarea是微信的原生组件,具有一些很特殊的属性,比如不能通过zindex改变层级,会始终覆盖在页面的最高层级,并且绑定事件bind后面不能写冒号(非原声组件bind后面的冒号可写可不写),maxlength设置成-1则可以突破textarea默认的140字限制。通过绑定bindinput事件,并且通过事件参数的detail里面的value可以获取到textarea输入的值
给textarea添加auto-focus属性可以自动获取焦点,这里有一个逻辑:底部的发布按钮所在footer部分本来的位置是fixed并且bootom为0定位在页面底部的。当textarea获取焦点的时候,键盘弹起,footer的bottom应该发生改变,失去焦点后,bottom应该恢复成0
给footer动态绑定样式style="bottom:{{footerBottom}}px"
这时候问题就变成了通过监听textarea的bindfocus和bindblur事件来动态的改变footerBottom数据就可以了
bindfocus的事件参数里面的detail里有一个属性height就是键盘的高度。注意:这个高度只有移动端真机才能看出来,模拟器中的height始终为0
每次重新编译小程序都会回到首页,如果是深层的页面则显得很麻烦,这时候可以增加模式并且自定义编译条件,让每次渲染都停留在指定的地方,其中启动参数每个参数的属性和值用等号链接,参数之间用逗号链接
循环数组的时候,通过wx:key="*this"
可以让数组循环快速绑定自身作为key值
5.上传图片到临时路径并实现本地预览
给选择图片的icon绑定tap事件bindtap="onChooseImage"
onChooseImage() {
// 这是微信提供的上传图片的接口
wx.chooseImage({
count: MAX_IMG_NUM - this.data.images.length,
// 初始值/压缩
sizeType: ['original', 'compressed'],
// 从手机相册选/拍照选择
sourceType: ['album', 'camera'],
success: (res) => {
// 把上传成功的图片链接赋值给data中预先定义好的变量
this.setData({
images: this.data.images.concat(res.tempFilePaths)
})
// 控制添加图片的元素是否显示
let max = MAX_IMG_NUM - this.data.images.length
this.setData({
selectPhoto: max <= 0 ? false : true
})
},
})
},
给删除图片的icon元素绑定删除图片的事件<i class="iconfont icon-shanchu1" bindtap="onDeleteImgs" data-index="{{index}}"></i>
data-index传递的index在事件处理函数的event参数中可以快速拿到event.target.dataset.index
this.data.images.splice(event.target.dataset.index, 1)
这样可以快速删除指定index的那条数据,但是要注意splice方法的返回值是删除的数组项。
微信提供的预览图片的接口
wx.previewImage({
urls: this.data.images,
current: event.target.dataset.imagesrc
})
第一个参数是预览的图片的数组,
第二个参数是当前要查看的第一张图片
6.将博客存储到数据库
上面实现了图片的上传和预览,接下来要实现的是点击“发布”按钮的功能。
1.将图片地址存放到云数据库中=>先将图片上传到云存储中,云存储中的每个文件都有一个独立的fileID,数据库中存储的是图片资源的fileID
云开发天然自带上传资源进云存储的接口,但是要注意这个接口每次只能上传一张图片
wx.cloud.uploadFile({
cloudPath:,
filePath:,
success:(res)=>{
},
fail:(err)=>{
}
})
cloudPath表示图片的存储地址,为了更好的管理,我们可以去云开发的“存储”中建立一个文件夹去专门存放指定位置的图片。
取文件拓展名的正则表达式:let suffix = /\.\w+$/.exec(item)[0]
filePath表示的文件的临时路径
2.储存博客数据:每条博客动态的数据库设计:
博客的文字内容
图片fileID:fileID是文件存储云储存之后,返给开发者的标识id
openid:在小程序当中,openid是用户的唯一标识
昵称,头像,发布时间
这时候还涉及到一个问题:当用户一条动态有多个图片的时候,每个图片上传到云储存中的时间肯定不同,储存博客数据到云数据库中的时间肯定是要等到所有图片上传到云存储完成之后再去执行,所以这里要用promise和promise.all对上传图片到云存储然后存储博客数据到云数据库进行优化,完整的点击“发布”按钮触发的事件代码如下
const db = wx.cloud.database()
先初始化数据库
send() {
if (content.trim() === '') {
wx.showModal({
title: '请输入内容',
content: '',
})
return
}
wx.showLoading({
title: '发布中',
mask: true
})
// 数据=>云数据库
let promiseArr = []
let fileIds = []
// 1.图片上传,将图片存到云存储中
for (let i = 0, len = this.data.images.length; i < len; i++) {
let p = new Promise((resolve, reject) => {
let item = this.data.images[i]
//文件扩展名
let suffix = /\.\w+$/.exec(item)[0]
wx.cloud.uploadFile({
cloudPath: 'blog/' + Date.now() + '-' + Math.random() * 10000000 + suffix,
filePath: item,
success: (res) => {
fileIds = fileIds.concat(res.fileID)
resolve()
},
fail: (err) => {
reject()
}
})
})
promiseArr.push(p)
}
// 存入到云数据库中
Promise.all(promiseArr).then((res) => {
db.collection('blog').add({
data: {
...userInfo,
content,
img: fileIds,
creatTime: db.serverDate() //服务端时间
}
}).then((res) => {
wx.hideLoading()
wx.showToast({
title: '发布成功',
})
// 返回blog页面,并且刷新
wx.navigateBack()
const pages = getCurrentPages()
const prevPage = pages[pages.length - 2]
prevPage.onPullDownRefresh()
})
}).catch((err) => {
wx.hideLoading()
wx.showToast({
title: '发布失败',
})
})
},
这里直接小程序端往云数据库中插入数据,一大好处就是自带openid
一个界面如何去调用另外一个界面的事件?
取到当前小程序的界面以及之前界面的信息
const pages = getCurrentPages()
取到上一个界面的
const prevPage = pages[pages.length-2]
刷新上一个界面
prevPage.onPullDownRefresh()
接下来要实现的查询数据库中的博客数据并显示到博客列表中:
查询功能在云函数中实现,在云函数中新建一个blog云函数,然后右键在终端中打开,执行npm install tcb-router --save
引入tcb-router并且初始化数据库并定义一个查询到指定数据库的变量(因为每次查询操作都需要先找到对应的数据库)
const TcbRouter = require('tcb-router')
const db = cloud.database()
const blogCollection = db.collection('blog')
查询博客列表的云函数代码如下:
exports.main = async(event, context) => {
const app = new TcbRouter({
event
})
app.router('list', async(ctx, next) => {
let bloglist = await blogCollection.where(w).skip(event.start).limit(event.count)
.orderBy('creatTime', 'desc').get().then((res) => {
return res.data
})
ctx.body = bloglist
})
return app.serve()
}
在博客列表中定义方法:
_loadBlogList(start = 0) {
wx.showLoading({
title: '拼命加载中',
})
wx.cloud.callFunction({
name: 'blog',
data: {
keyword,
start,
count: 10,
$url: 'list',
}
}).then((res) => {
this.setData({
blogList: this.data.blogList.concat(res.result)
})
wx.hideLoading()
wx.stopPullDownRefresh()
})
},
云数据库中拿到的时间显示不是自己期望的格式。
格式化时间的代码我们写在utils文件夹中,相当于是一个工具箱
导出:
module.exports=()=>{ }
在需要使用的地方用import进行引用
import formatTime from '../../utils/formatTime.js'
通过observers监听器去监听blog数据下的createTime字段
小程序的每个页面的HTML都是被包裹在page标签中,改变页面背景颜色等操作可以直接给page添加
定义页面的下拉刷新:
首先需要在页面的json文件中定义"enablePullDownRefresh":true
然后监听页面的下拉事件,并定义相应的事件
onPullDownRefresh: function() {
this.setData({
blogList: []
})
this._loadBlogList(0)
},
注意:微信不会自动监测到何时终止下拉事件,所以我们在加载数据完成后应该是通过代码主动的终止下拉刷新的动画
wx.stopPullDownRefresh()
如果打算在子组件的整体上面绑定一个事件,应该让调用者去定义事件,可以让事件不是耦合在组件中。如:<x-blog-card blog="{{item}}" data-blogid="{{item._id}}" bindtap="goComment" />
当给组件整体绑定了事件之后,组件内部的一些元素也会相应的事件绑定,但是不希望冒泡,这时候可以用catch:tap="xxx"
去绑定,效果和bind一样,但是会阻止事件冒泡。
7.模糊查询
搜索框组件的搜索功能应该让自己触发父组件的search事件,然后把自己拿到的value值传给父组件,这样可以提高组件的复用性。
如果通过云数据库实现模糊查询?
小程序里面支持正则表达式的写法,注意一下,正则表达式里面是不能写变量的,所以小程序提供可以用变量去进行匹配的写法。
引用了tcb-ruter,并且加入模糊查询之后的云函数写法:
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
const TcbRouter = require('tcb-router')
const db = cloud.database()
const blogCollection = db.collection('blog')
// 云函数入口函数
exports.main = async(event, context) => {
const app = new TcbRouter({
event
})
app.router('list', async(ctx, next) => {
const keyword = event.keyword
// 定义一个变量去存放查询条件
let w = {}
if (keyword.trim() != "") {
w = {
content: db.RegExp({
regexp: keyword,
//模式
options: 'i'
})
}
}
let bloglist = await blogCollection.where(w).skip(event.start).limit(event.count)
.orderBy('creatTime', 'desc').get().then((res) => {
return res.data
})
ctx.body = bloglist
})
return app.serve()
}
云函数如果只是改了单个的文件,可以只点击修改的那个文件,然后右键增量上传,如果修改的文件比较多,则右键整个云函数进行全部上传。
options:i 忽略大小写
最后在查询语句上添加上where(查询条件)
为了提高数据库查询的速度,我们可以在数据库中添加索引,索引会增大数据库的体积,对于我们来说,就相当于用空间去换时间。
小程序端进行数据操作和云函数进行数据操作的区别:
1.读取数量的区别,小程序端一次读20,云函数一次100
2.权限区别,小程序端受限制,可在云开发界面中进行改变
网友评论