v3-mini v1.0.0-Alpha.2
参考 Vue3.x ,尝试使用 composition-api 开发小程序
下载
npm install v3-mini --save
注意
- 参考 @vue/reactivity 开发小程序版 composition api ,使用 Proxy 而不用 Object.defineProperty 是因为解决属性添加删除上的问题,但小程序对的 Proxy 的适应性并不好
- 采用 westore 的 json diff 进行数据对比,减少了 setData 的内容
解决什么
- 使用 composition-api 开发,实现全局状态管理
- 添加了 reactive, ref, computed, watchEffect 方法
- 兼容原生小程序的写法,在原写法上新增写法
- 新的写法,data, methods 皆可在 setup 方法中一起定义返回
- 钩子函数也可直接在 setup 方法中直接定义
TODO
- 自定义组件的生命周期应该是按小程序原写法来还是与 Page 页面统一?
- setup的方法更新属于直接覆盖,是否需要对某些保护属性进行保护?
- setup返回的ref/reactive对象监听,采用异步setData的方式,数据改变后只执行一次setData,减少了setData方法的执行率
- 当页面中存在自定义组件时,是先执行自定义组件的setup后执行页面的setup
缺点
- Deps 依赖不同于 Vue3.x 存储于变量中,该版本存储于闭包中,通过js的机制不再引用时进行垃圾回收,使用不当容易造成内容泄露
- 采用 Proxy 才能实现 reactive 的数据监听(为了完成 属性添加删除上的问题),但小程序对 Proxy 的适应性并非很好
- setup返回的对象和方法,是在onLoad期间绑定在组件实例上的,依次不太适用于大量静态内容, 建议提前定义好data
- Provide / Inject 功能基础,有待提升
之后版本更新内容
- 内存回收机制完善(Deps、event)
- 自定义组件的 特殊的生命周期 暂未实现
setup
setup 函数是一个新的组件选项。作为在组件内使用 Composition API 的入口点。
调用时机 在onLoad/attached时候被执行
参数 该函数接收 props 作为其第一个参数, context 第二个参数
VPage({
... ,
setup(props, context) {
context.event.on('load', () => {});
context.event.emit('load');
context.setData({
age: ref(16)
})
}
})
props
reactive对象,代理小程序原始 onLoad 的参数 options,可通过 toRefs 函数对其进行分解context
作为上下文对象, 暴露了一些apievent
是事件通知模块, 包含常用的emit, on, once, off, context注册的事件, 在页面/组件被销毁时也会主动offsetData
是封装后的setData, 支持对reactive/ref对象
的解析, 其他用法和原生一致this
指向当前页面/组件实例
包装对象ref
ref
ref接受参数并将其包装在具有value属性的对象中,然后将其用于访问或更改反应变量的值
参数
1. val
{Any} 任意值,赋值于ref.value
返回值
{Object} 返回代理后的ref对象
import { ref } from 'v3-mini'
const count = ref(0)
console.log(count.value) // 0
count.value = 1
console.log(count.value) // 1
在视图层中读取
当该值被setup
返回, 将进入data值, 可在模板中被读取到, 会自动解套,无需在模板中额外书写.value
<view>{{ count }}</view>
<view bindtap="updateCount">+</view>
import { VPage, ref } from 'v3-mini'
VPage({
setup(props, context) {
const count = ref(0)
return {
count,
updateCount() {
count.value += 1
}
}
}
})
拆解包装对象
toRefs
对 reactive 对象进行拆解,得到多个 ref 对象
参数
1. reactive
{Reactive} reactive 对象
返回值
{Object} ref 对象集
import { VPage, reactive, toRefs } from 'v3-mini'
VPage({
setup(props, context) {
const person = reactive({ name: 'Jack', age: 25 })
const { name, age } = toRefs(person)
return {
name, age,
updatePerson() {
name.value = 'Rose'
person.age = 18
}
}
}
})
包装对象reactive
reactive
参数
1. val
{Object|Array} 创建该对象/数组的代理,监听数据变化
返回值
{Proxy} 返回代理后的reactive对象
将数据包装成一种可观测的类型,当数据产生变更的时候,监听并执行响应操作
import { reactive, watchEffect } from 'v3-mini'
const person = reactive({ name: 'Jack', age: 25 })
watchEffect(() => console.log(person.name)) // Jack
person.name = 'Rose' // Rose
reactive 对象中可使用 ref 对象
计算属性
computed
返回一个 不可手动修改的 ref 对象,收集监听当ref/reactive发送变化时随之更新值
参数
1. setter
{Function} 监听变化的回调, 返回任意值
返回值
{Object} 返回计算后的ref对象
import { ref, computed } from 'v3-mini'
const count = ref(1)
const plusOne = computed(() => count.value + 1)
console.log(plusOne.value) // 2
count.value = 5
console.log(plusOne.value) // 6
计算属性总是最少会执行一次,为了第一次赋值
监听ref/reactive值更新
watchEffect
当被监听的ref/reactive对象变化时, 将触发
参数
1. callback
监听变化的回调
返回值
{Function} 返回停止监听的方法
import { ref, watchEffect } from 'v3-mini'
const count = ref(1)
const stop = watchEffect(() => console.log(`count: ${count.value}`)) // count: 1
count.value++ // count: 2
stop()
count.value++ // 无输出
watchEffect 总是最少会执行一次,为了收集依赖 注意:第一次执行 watchEffect 时并没有生成 stop ,在 callback 中使用 stop 会产生错误
生命周期函数
可以直接导入 onXXX
一族的函数来注册生命周期钩子:
import { VPage, onLoad, onHide, onShow } from 'v3-mini'
VPage({
setup() {
onLoad(() => {
console.log('onLoad!')
})
onHide(() => {
console.log('onHide!')
})
onShow(() => {
console.log('onShow!')
})
},
}
VPage使用
<view>name: {{name}}</view>
<view>count1: {{count1}}</view>
<view>count2: {{count2}}</view>
<view>allCount.count1: {{allCount.count1}}</view>
<view>allCount.count2: {{allCount.count2}}</view>
<view>allCount.count3: {{allCount.count3}}</view>
<view bindtap="changeName">changeName</view>
<view bindtap="addCount1">addCount1</view>
<view bindtap="addCount3">addCount3</view>
<view bindtap="delCount3">delCount3</view>
<view bindtap="stopWatch">stopWatch</view>
import { VPage, ref, toRefs, reactive, watchEffect, computed, onLoad } from 'v3-mini';
VPage({
setup(props) {
let { name } = toRefs(props)
let count1 = ref(1)
let count2 = computed(() => count1.value * 2)
let allCount = reactive({ count1, count2 })
const stop = watchEffect(() => {
console.log(`监听allCount.${'count2'}: ${allCount['count2']}`);
})
function changeName() {
name.value = 'Rose'
}
function addCount1() {
count1.value++
}
function addCount3() {
allCount.count3 = (allCount.count3 || 0) + 3
}
function delCount3() {
delete allCount.count3
}
function stopWatch() {
console.log(`停止监听allCount.${'count2'}`);
stop()
}
onLoad(() => {
console.log('onLoad!')
})
return { name, count1, count2, allCount, changeName, addCount1, addCount3, delCount3, stopWatch }
}
})
VComponent使用
<view>name: {{name}}</view>
<view bindtap="changeName">changeName</view>
<myView name="Jack" />
import { VComponent, toRefs, attached } from 'v3-mini';
VComponent({
properties:{
name: String
},
setup(props) {
let { name } = toRefs(props)
function changeName() {
name.value = 'Rose'
}
attached(() => {
console.log('attached!')
})
return { name, changeName }
}
})
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago