Sejak keluaran Vue3
, perkataan composition API
telah masuk ke ufuk pelajar yang menulis Vue
Saya percaya semua orang telah mendengar bahawa composition API
adalah lebih baik daripada options API
sebelumnya sangat berkuasa Kini, terima kasih kepada pemalam @vue/composition-api
, pelajar Vue2
juga boleh menaiki bas, terutamanya ref
dan reactive
untuk menganalisis secara mendalam cara pemalam ini melaksanakan fungsi ini.
// 入口文件引入并注册 import Vue from 'vue' import VueCompositionAPI from '@vue/composition-api' Vue.use(VueCompositionAPI)
// vue文件使用 import { defineComponent, ref, reactive } from '@vue/composition-api' export default defineComponent({ setup () { const foo = ref('foo'); const obj = reactive({ bar: 'bar' }); return { foo, obj } } })
Adakah anda merasakan sama seperti vue3
Anda mungkin berfikir:
Ini adalah vue2
saya sebelum ini data
dan methods
juga mempunyai pembolehubah dan kaedah. [Cadangan berkaitan: setup
tutorial video vuejs, pembangunan bahagian hadapan web]
Bukan sahaja data yang ditakrifkan dalam vue2
Adakah ia akan dibuat responsif? Bagaimanakah data
dan ref
melakukannya? reactive
Kekangan yang ditakrifkan oleh data responsif (menambah atribut yang tidak diberikan kepada objek asal, mengubah suai subskrip tatasusunan, dll.), adakah boleh menggunakan vue2
dan ref
sebaliknya ? reactive
, merangkumi kebanyakan perkara yang API
ada di sini isu ini. Begini caranya. Vue3
, Vue
seperti @vue/composition-api
dan vue-router
turut disediakan oleh palam rasmi -dalam sistem suntikan. vuex
// 这里只贴跟本章要讲的相关代码 funciton mixin (Vue) { Vue.mixin({ beforeCreate: functionApiInit } } function install (Vue) { mixin(Vue); } export const Plugin = { install: (Vue: VueConstructor) => install(Vue), }
Pemalam mendedahkan kaedah Vue
ke luar Apabila install
dipanggil, kaedah ini akan dipanggil dan pembina use
akan dihantar masuk sebagai. parameter, dan kemudian dipanggil Vue
Fungsi yang akan diproses apabila mencampurkan dalam cangkuk yang sepadan. Vue.mixin
lakukan functionApiInit
function functionApiInit(this: ComponentInstance) { const vm = this const $options = vm.$options const { setup, render } = $options // render 相关 const { data } = $options $options.data = function wrappedData() { initSetup(vm, vm.$props) return isFunction(data) ? ( data as (this: ComponentInstance, x: ComponentInstance) => object ).call(vm, vm) : data || {} }
akan Vue
melaksanakan pemprosesan data antara beforeCreated
dan created
kitaran hayat. Pemprosesan, apabila memproses initState
, data
akan dipanggil untuk mendapatkan data yang ditetapkan, jadi di sini kita bungkus semula fungsi Ini juga salah satu sebab mengapa suntikan cangkuk dipilih Balut sebelum panggilan fungsi.
Seterusnya, mari lihat apa yang $options.data
lakukan beforeCreate
initSetup
function initSetup(vm: ComponentInstance, props: Record<any, any> = {}) { const setup = vm.$options.setup! const ctx = createSetupContext(vm) const instance = toVue3ComponentInstance(vm) instance.setupContext = ctx def(props, '__ob__', createObserver()) resolveScopedSlots(vm, ctx.slots) let binding: ReturnType<SetupFunction<Data, Data>> | undefined | null activateCurrentInstance(instance, () => { binding = setup(props, ctx) }) // setup返回是函数的情况 需要重写render函数 const bindingObj = binding Object.keys(bindingObj).forEach((name) => { let bindingValue: any = bindingObj[name] // 数据处理 asVmProperty(vm, name, bindingValue) }) return } }
, dan kemudian fungsi ctx
dilaksanakan untuk mendapatkan nilai pulangan, dan kemudian setiap atribut dilalui, dan vm
dipanggil untuk dipasang pada Vue3
Sudah tentu, pelekap di sini tidak Menambah atribut dan nilai secara langsung ke instance
akan menyebabkan masalah, iaitu, pengubahsuaian seterusnya pada atribut tidak boleh disegerakkan ke setup
proksi data yang paling biasa asVmProperty
digunakan di sini. vm
vm
vm
Selepas melihat ini, saya percaya anda telah memahami mengapa pulangan yang ditakrifkan dalam Vue
boleh digunakan dalam
export function asVmProperty( vm: ComponentInstance, propName: string, propValue: Ref<unknown> ) { const props = vm.$options.props if (!(propName in vm) && !(props && hasOwn(props, propName))) { if (isRef(propValue)) { proxy(vm, propName, { get: () => propValue.value, set: (val: unknown) => { propValue.value = val }, }) } else { proxy(vm, propName, { get: () => { if (isReactive(propValue)) { ;(propValue as any).__ob__.dep.depend() } return propValue }, set: (val: any) => { propValue = val }, }) } }
, dsb., kerana semua yang dikembalikan mempunyai telah diproksikan kepada setup
. template
data
methods
Responsif (pelaksanaan vm
ref
Seterusnya mari kita bincangkan tentang responsif, mengapa reactive
dan juga Data boleh dibuat responsif . Pelaksanaan ref
sebenarnya merupakan pengkapsulan semula reactive
, terutamanya digunakan untuk jenis asas.
ref
Kerana reactive
mesti menerima objek, jadi pemalar digunakan di sini sebagai
function ref(raw?: unknown) { if (isRef(raw)) { return raw } const value = reactive({ [RefKey]: raw }) return createRef({ get: () => value[RefKey] as any, set: (v) => ((value[RefKey] as any) = v), }) }
const value = reactive({ "composition-api.refKey": row })
export function createRef<T>( options: RefOption<T>, isReadonly = false, isComputed = false ): RefImpl<T> { const r = new RefImpl<T>(options) const sealed = Object.seal(r) if (isReadonly) readonlySet.set(sealed, true) return sealed } export class RefImpl<T> implements Ref<T> { readonly [_refBrand]!: true public value!: T constructor({ get, set }: RefOption<T>) { proxy(this, 'value', { get, set, }) } }
通过 new RefImpl
实例,该实例上有一个 value
的属性,对 value
做代理,当取值的时候返回 value[RefKey]
,赋值的时候赋值给 value[RefKey]
, 这就是为什么 ref
可以用在基本类型,然后对返回值的 .value
进行操作。调用 object.seal
是把对象密封起来(会让这个对象变的不能添加新属性,且所有已有属性会变的不可配置。属性不可配置的效果就是属性变的不可删除,以及一个数据属性不能被重新定义成为访问器属性,或者反之。但属性的值仍然可以修改。)
我们主要看下 reactive
的实现
export function reactive<T extends object>(obj: T): UnwrapRef<T> { const observed = observe(obj) setupAccessControl(observed) return observed as UnwrapRef<T> } export function observe<T>(obj: T): T { const Vue = getRegisteredVueOrDefault() let observed: T if (Vue.observable) { observed = Vue.observable(obj) } else { const vm = defineComponentInstance(Vue, { data: { $$state: obj, }, }) observed = vm._data.$$state } return observed }
我们通过 ref
或者 reactive
定义的数据,最终还是通过了变成了一个 observed
实例对象,也就是 Vue2
在对 data
进行处理时,会调用 observe
返回的一样,这里在 Vue2.6+
把observe
函数向外暴露为 Vue.observable
,如果是低版本的话,可以通过重新 new
一个 vue
实例,借助 data
也可以返回一个 observed
实例,如上述代码。
因为在 reactive
中定义的数据,就如你在 data
中定义的数据一样,都是在操作返回的 observed
,当你取值的时候,会触发 getter
进行依赖收集,赋值时会调用 setter
去派发更新,
只是定义在 setup
中,结合之前讲到的 setup
部分,比如当我们在 template
中访问一个变量的值时,vm.foo
-> proxy
到 setup
里面的 foo
-> observed
的 foo
,完成取值的流程,这会比直接在 data
上多代理了一层,因此整个过程也会有额外的性能开销。
因此使用该 API
也不会让你可以直接规避掉 vue2
响应式数据定义的约束,因为最终还是用 Object.defineProperty
去做对象拦截,插件同样也提供了 set API
让你去操作对象新增属性等操作。
通过上面的了解,相信你一定对于 Vue2
如何使用 composition API
有了一定的了解,因为 API
相当多, 响应式相关的就还有 toRefs、toRef、unref、shallowRef、triggerRef
等等,这里就不一一分析,有兴趣的可以继续看源码的实现。
写 Vue2
的同学也可以不用羡慕写 Vue3
的同学了,直接引入到项目就可以使用起来,虽然没有 vue3
那么好的体验,但是绝大部分场景还是相同的,使用时注意 README
文档最后的限制章节,里面讲了一些使用限制。
Atas ialah kandungan terperinci Analisis prinsip Vue2 melaksanakan API komposisi. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!