clientDB简介
clientDB:在客户端直接使用uniCloud.database()方式获取数据库引用,即在前端直接操作数据库
使用clientDB的好处:不用写服务器代码了!
如果需要数据库操作之前或之后,云端执行关联逻辑(比如获取文章详情后,文章阅读量+1),clientDB提供了数据库触发器(从HBuilderX 3.6.11开始)。 在不支持数据库触发器的低版本,使用action云函数
clientDB的前端,有两种用法,可以用js API操作云数据库,也可以使用<unicloud-db>组件。
注意:clientDB依赖uni-id(1.1.10+版本)提供用户身份和权限校验,存在uni-id-common时clientDB会优先依赖uni-id-common(相关内容请参考:https://uniapp.dcloud.net.cn/uniCloud/uni-id-common.html#)
使用js操作数据库(jql语法)
// 获取db引用
const db = uniCloud.database() //代码块为cdb
db.collection('list')
.where('name=="hello-uni-app"')
.get()
.then((res)=>{
// res 为数据库查询结果
}).catch((err)=>{
console.log(err.code); // 打印错误码
console.log(err.message); // 打印错误内容
})
前端操作数据库的语法与云函数一致,但有以下限制(使用jql语法时也一样):
- 上传时会对query进行序列化,除Date类型、RegExp之外的所有不可序列化的参数类型均不支持(例如:undefined)
- 为方便控制权限,禁止前端使用set方法,一般情况下也不需要前端使用set
- 更新数据库时不可使用更新操作符
db.command.inc
等 - 更新数据时键值不可使用{'a.b.c': 1}的形式,需要写成{a:{b:{c:1}}}形式
clientDB有两种方式获取数据库引用uniCloud.database()
和uniCloud.databaseForJQL()
(新增于HBuilderX 3.6.7)。推荐在支持databaseForJQL接口的版本使用databaseForJQL接口,和云端jql扩展库返回结构一致,方便代码复用
返回结果database接口返回的数据结构多了一层result,即为
{
result: {
data: [{
xx: xx
}]
}
}
使用databaseForJQL接口,res结构如下
{
data: [{
xx: xx
}]
}
使用unicloud-db组件
<unicloud-db> 组件是一个数据库查询组件,它是对clientDB的js库的再封装。
<unicloud-db> 由原 <uni-clientdb>插件 升级而来,从 HBuilderX 3.0 起<unicloud-db>内置到框架,与小程序基础库版本无关。
特点:
<unicloud-db> 组件尤其适用于列表、详情等展示类页面。开发效率可以大幅度的提升。
<unicloud-db> 组件的查询语法是jql,这是一种比sql语句和nosql语法更简洁、更符合js开发者习惯的查询语法。没学过sql或nosql的前端,也可以轻松掌握
<unicloud-db> 组件不仅支持查询。还自带了add、remove、update方法
组件属性:
属性:v-slot:default
示例:
<unicloud-db v-slot:default="{data, loading, error, options}" collection="user">
<view v-if="error">{{error.message}}</view>
<view v-else-if="loading">正在加载...</view>
<view v-else>
{{data}}
</view>
</unicloud-db>
属性:collection
多个临时表组成的数组
<template>
<unicloud-db v-slot:default="{data, loading, error, options}" :collection="colList">
</unicloud-db>
</template>
<script>
const db = uniCloud.database()
export default {
data() {
return {
colList: [
db.collection('book').where('name == "水浒传"').getTemp(),
db.collection('author').getTemp()
]
}
},
onReady() {},
methods: {}
}
</script>
属性:where
组件与js API有一个差别,就是组件的属性中若使用js中的变量,需额外注意
方式1. 使用模板字符串,用${}包裹变量
<template>
<view>
<unicloud-db collection="uni-id-users" :where="`username=='${tempstr}'`"></unicloud-db>
</view>
</template>
<script>
export default {
data() {
return {
tempstr: '123'
}
}
}
</script>
方式2. 在js中拼接字符串
<template>
<view>
<unicloud-db ref="udb" collection="uni-id-users" :where="sWhere" loadtime="manual"></unicloud-db>
</view>
</template>
<script>
export default {
data() {
return {
tempstr: '123',
sWhere: ''
}
}
onLoad() {
this.sWhere = "id=='" + this.tempstr + "'"
// 组件上配置了 loadtime = "manual", 这里需要手动加载数据
this.$nextTick(() => {
this.$refs.udb.loadData()
})
}
}
</script>
使用正则模糊查询示例:
<template>
<view class="content">
<input @input="onKeyInput" placeholder="请输入搜索值" />
<unicloud-db v-slot:default="{data, loading, error, options}" collection="goods" :where="where">
<view v-if="error">{{error.message}}</view>
<view v-else></view>
</unicloud-db>
</view>
</template>
<script>
export default {
data() {
return {
searchVal: ''
}
},
computed: {
where() {
return `${new RegExp(this.searchVal, 'i')}.test(name)` // 使用计算属性得到完整where
}
},
methods: {
onKeyInput(e) {
// 实际开发中这里应该还有防抖或者节流操作,这里不做演示
this.searchVal = e.target.value
}
}
}
</script>
属性:orderby
格式为: 字段名 空格 asc(升序)/desc(降序),多个字段用 , 分割,优先级为字段顺序
属性:loadtime
事件:load
load事件在查询执行后、渲染前触发,一般用于查询数据的二次加工。比如查库结果不能直接渲染时,可以在load事件里先对data进行预处理。
...
<unicloud-db @load="handleLoad" />
...
handleLoad(data, ended, pagination) {
// `data` 当前查询结果
// `ended` 是否有更多数据
// `pagination` 分页信息 HBuilderX 3.1.5+ 支持
}
数据库里的时间一般是时间戳,不能直接渲染。虽然可以在load事件中对时间格式化,但更简单的方式是使用<uni-dateformat>组件,无需写js处理。
事件:error
error事件在查询报错时触发,比如联网失败。
...
<unicloud-db @error="handleError" />
...
handleError(e) {
// {message}
}
ssr-key
发行 H5 ssr 时有效,用于保证服务器端渲染和前端加载的数据对应
默认值:根据 (页面路径 + unicloud-db 组件代码中的行号)生成的唯一值
注意:页面同时出现2个及以上 unicloud-db 组件需要配置此属性,且要保证值整个应用唯一
方法:loadData
当 <unicloud-db> 组件的 manual 属性设为 true 时,不会在页面初始化时联网查询数据,此时需要通过本方法在需要的时候手动加载数据。
this.$refs.udb.loadData()
//udb为unicloud-db组件的ref属性值
一般onLoad因时机太早取不到this.$refs.udb,在onReady里可以取到。
<template>
<view>
<unicloud-db ref="udb" collection="table1" :where="where" v-slot:default="{data,pagination,loading,error,options}" :options="options" manual>
{{data}}
</unicloud-db>
</view>
</template>
<script>
export default {
data() {
return {
_id:'',
where: ''
}
},
onLoad(e) {
const id = e.id
if (id) {
this._id = id
this.where = "_id == '" + this._id + "'"
}
else {
uni.showModal({
content:"页面参数错误",
showCancel:false
})
}
},
onReady() {
if (this._id) {
this.$refs.udb.loadData()
}
}
}
</script>
方法:loadMore
在列表的加载下一页场景下,使用ref方式访问组件方法,加载更多数据,每加载成功一次,当前页 +1
this.$refs.udb.loadMore()
//udb为unicloud-db组件的ref属性值
方法:clear
清空已加载的数据,但不会重置当前分页信息
this.$refs.udb.clear()
//udb为unicloud-db组件的ref属性值
方法:reset
重置当前分页信息,但不会清空已加载的数据
this.$refs.udb.reset()
//udb为unicloud-db组件的ref属性值
方法:refresh
清空并重新加载当前页面数据
this.$refs.udb.refresh()
//udb为unicloud-db组件的ref属性值
方法:remove
语法:this.$refs.udb.remove(id, options)
必选参数 id string|Array (传入数据库的_id)
id为数组时:this.$refs.udb.remove(["5f921826cf447a000151b16d", "5f9dee1ff10d2400016f01a4"])
可选参数 options
示例:
<button @click="confirmDelete(item._id)" type="warn">删除</button>
confirmDelete(id) {
this.$refs.udb.remove(id)
}
//删除多个
var ids = ["5f921826cf447a000151b16d", "5f9dee1ff10d2400016f01a4"]
this.$refs.udb.remove(ids, {
action: '', // 删除前后的动作
confirmTitle: '提示', // 确认框标题
confirmContent: '是否删除该数据', // 确认框内容
success: (res) => { // 删除成功后的回调
const { code, message } = res
},
fail: (err) => { // 删除失败后的回调
const { message } = err
},
complete: () => { // 完成后的回调
}
})
方法:add
语法:this.$refs.udb.add(value, options)
value 参数 必填 object类型
参数options 选填
示例:
this.$refs.udb.add(value, {
toastTitle: '新增成功', // toast提示语
success: (res) => { // 新增成功后的回调
const { code, message } = res
},
fail: (err) => { // 新增失败后的回调
const { message } = err
},
complete: () => { // 完成后的回调
}
})
方法:update
语法:this.$refs.udb.update(id, value, options)
参数id 必填 string类型
参数value 必填 object类型
options 选填
示例:
this.$refs.udb.update(id, value, {
toastTitle: '修改成功', // toast提示语
success: (res) => { // 更新成功后的回调
const { code, message } = res
},
fail: (err) => { // 更新失败后的回调
const { message } = err
},
complete: () => { // 完成后的回调
}
})
dataList
在js中,获取<unicloud-db> 组件的data的方法如下:
console.log(this.$refs.udb.dataList);
如果修改了dataList的值,组件渲染的界面也会同步变化。
但是在浏览器控制台里无法使用this来打印查看数据,为此特别新增了unidev.clientDB.data方法以优化调试体验。
H5平台,开发模式下浏览器控制台输入 unidev.clientDB.data,可查看组件内部数据,多个组件通过索引查看 unidev.clientDB.data[0]
示例:分页
PC端常见分页,点击第二页,第一页内容清空,显示第二页内容
<template>
<view class="content">
<unicloud-db ref="udb" v-slot:default="{data, pagination, loading, error, options}"
:options="options"
collection="table1"
orderby="createTime desc"
field="name,subType,createTime"
:getcount="true"
@load="onqueryload" @error="onqueryerror">
<view>{{pagination}}</view>
<view v-if="error" class="error">{{error.errMsg}}</view>
<view v-else class="list">
<view v-for="(item, index) in data" :key="index" class="list-item">
{{item.name}}
<!-- 使用日期格式化组件,详情见插件 https://ext.dcloud.net.cn/search?q=date-format -->
<uni-dateformat :date="item.createTime" />
</view>
</view>
<view class="loading" v-if="loading">加载中...</view>
<!-- 分页组件 -->
<uni-pagination show-icon :page-size="pagination.size" :total="pagination.count" @change="onpagination" />
</unicloud-db>
</view>
</template>
<script>
export default {
data() {
return {
options: {}, // 插槽不能访问外面的数据,通过此参数传递, 不支持传递函数
}
},
onPullDownRefresh() { //下拉刷新(一般此场景下不使用下拉刷新)
this.$refs.udb.loadData({
clear: true
}, () => {
uni.stopPullDownRefresh()
})
},
methods: {
onqueryload(data, ended) {
// 可在此处预处理数据,然后再渲染界面
},
onqueryerror(e) {
// 加载数据失败
},
onpagination(e) {
this.$refs.udb.loadData({
current: e.current
})
}
}
}
</script>
组件嵌套
子组件中访问父组件 data 时,需options传递数据
<unicloud-db v-slot:default="{data, loading, error, options}" :options="options" collection="table1"
orderby="createTime desc" field="name,createTime">
<view v-if="error" class="error">{{error.errMsg}}</view>
<view v-else class="list">
<!-- table1 返回的数据 -->
<view v-for="(item, index) in options" :key="index" class="list-item">
{{ item.name }}
</view>
</view>
<!-- 嵌套 -->
<!-- :options="data",将 父组件返回的 data 通过 options 传递到组件,子组件通过 options 访问 -->
<unicloud-db ref="dataQuery1" v-slot:default="{loading, data, error, options}" :options="data" collection="table2"
orderby="createTime desc" field="name,createTime" @load="onqueryload" @error="onqueryerror">
<!-- 父组件 table1 返回的数据 -->
<view v-for="(item, index) in options" :key="index" class="list-item">
{{ item.name }}
</view>
<!-- 子组件 table2 返回的数据 -->
<view v-for="(item, index) in data" :key="index" class="list-item">
{{ item.name }}
</view>
</unicloud-db>
</unicloud-db>
客户端事件
刷新token
const db = uniCloud.database()
function refreshToken({
token,
tokenExpired
}) {
uni.setStorageSync('uni_id_token', token)
uni.setStorageSync('uni_id_token_expired', tokenExpired)
}
// 绑定刷新token事件
db.on('refreshToken', refreshToken)
// 解绑刷新token事件
db.off('refreshToken', refreshToken)
全局错误处理
const db = uniCloud.database()
function onDBError({
code, // 错误码详见https://uniapp.dcloud.net.cn/uniCloud/clientdb?id=returnvalue
message
}) {
// 处理错误
}
// 绑定clientDB错误事件
db.on('error', onDBError)
// 解绑clientDB错误事件
db.off('error', onDBError)