使用
定义页面
//index.js
import { definePage, ref } from '@vue-mini/wechat'
definePage({
setup() {
const count = ref(0)
function increment() {
count.value++
}
return {
count,increment
}
},
})
<!-- index.wxml -->
<button bindtap="increment">
Count is: {{ count }}
</button>
跟写vue3一样,可以轻松的使用vue3响应式相关Api。
无需繁琐的在Page.data里定义数据,更新数据时在setData进行更新。
实现
vue-mini在实现方面并没有做编译相关的工作,底层直接依赖于 @vue/reactivity,那么接下来就看看他是如何实现相关功能的
初始化
如上,我们在使用definePage定义页面时,调用definePage方法,与vue3里defineComponent一样,可以接收对象或函数
通过判断,最终的目的依旧是获取setup和options
function definePage(optionsOrSetup: any, config?: Config): void {
let setup: PageSetup
let options: Options
if (isFunction(optionsOrSetup)) {
setup = optionsOrSetup
options = {}
} else {
const { setup: setupOption, ...restOptions } = optionsOrSetup
setup = setupOption
options = restOptions
}
...
并创建onLoad函数,等待Page调用onLoad函数
//用户如果有写onLoad方法,就保存该函数,并创建一个新的onLoad函数,等待page调用
const originOnLoad = options[PageLifecycle.ON_LOAD]
options['onLoad'] = function (query) {
//setup执行就在这里
...
}
//创建其他相关生命周期相关函数
...
在拿到setup和创建生命函数后,最后会执行Page方法
function definePage(optionsOrSetup: any, config?: Config): void {
...
...
...
Page(options)
}
Page是小程序的方法,当我们正常写小程序时,会这么写
Page({
data:{
count:0
},
increment(){
let count = ++this.data.count
this.setData({count:count})
}
})
将options传入,等待小程序执行onLoad函数
options['onLoad'] = function (query) {
//将currentPage指向当前Page
setCurrentPage(this)
//创建上下文
const context: PageContext = {
is: this.is,
route: this.route,
options: this.options,
...
}
//执行setup函数,并拿到return后的返回值
const bindings = setup(query, context)
if (bindings !== undefined) {
Object.keys(bindings).forEach((key) => {
const value = bindings[key]
//判断值是否是函数,小程序中函数是与page.data同级的
//而响应式数据是在data内的
if (isFunction(value)) {
this[key] = value
return
}
//非函数的数据会执行setData更新,并等待视图刷新
this.setData({ [key]: deepToRaw(value) })
//并监听bindings内数据的变化,更新就触发相对应的effect
deepWatch.call(this, key, value)
})
}
setCurrentPage(null)
//对用户的onLoad方法进行调用
if (originOnLoad !== undefined) {
originOnLoad.call(this, query)
}
}
数据更新
deepWatch实现
function deepWatch( key,value) {
if (!isObject(value)) {
return
}
//监听数据,一旦变化,就调用setData进行数据变更
watch(
isRef(value) ? value : () => value,
() => {
this.setData({ [key]: deepToRaw(value) })
},
{
deep: true,
}
)
}
watch并非@vue/reactivity提供的而是内部实现的,实现如下
function watch(source,cb,options) {
return doWatch(source, cb, options)
}
调用doWatch函数
代码比较长,实现进行了简化
doWatch主要的目的就是创建effect函数,并执行自己的scheduler更新调度,最终目的还是执行cb函数
function doWatch(source,cb,{ immediate, deep, flush, onTrack, onTrigger } = {}) {
let getter
//通过source类型 对getter进行赋值
if (isRef(source)) {
...
} else if (isReactive(source)) {
...
} else if (isArray(source)) {
...
} else if (isFunction(source)) {
...
} else {
...
}
//不采用effect默认的执行方式
//使用自身的scheduler调度
let scheduler
scheduler = () => {
queueJob(job)
}
const runner = effect(getter, {
lazy: true,
onTrack,
onTrigger,
scheduler,
})
// Initial runner
if (cb) {
oldValue = runner()
} else {
runner()
}
}
结语
可以看到,vue-mini主要依赖于@vue/reactivity和小程序
在使用上,与vue3一样
在实现上,并没有对小程序做相关修改,而是执行vue-mini内方法后,在特定的时机,调用小程序的原生方法