uniCloud clientDB和unicloud-db组件

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方法

组件属性:

image.png

属性:v-slot:default
image.png

示例:

<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

image.png

事件: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

image.png

示例:

<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 选填

image.png

示例:

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 选填

image.png

示例:

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)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,470评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,393评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,577评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,176评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,189评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,155评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,041评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,903评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,319评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,539评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,703评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,417评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,013评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,664评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,818评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,711评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,601评论 2 353

推荐阅读更多精彩内容