您的位置: 翼速应用 > 业内知识 > web前端 > 正文

如何在Vue2中实现composition API

大家最近都听说了composition API的功能之强,最近由于 @vue/composition-api 插件的发布,它已经不再是Xue3的专属了,也可以在Vue2中使用,这篇文章和大家一起聊聊以响应式的 ref 和 reactive 来深入分析一下,这个插件是怎么实现此功能的。


如何在Vue2中实现composition API


如何在Vue2中实现composition API


// 入口文件引入并注册
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
     }
  }
})


原理解析


得益于 Vue 的插件系统,@vue/composition-api 像 vue-router 、vuex 一样也是通过官方提供的插件式来注入。


// 这里只贴跟本章要讲的相关代码
 
funciton mixin (Vue) {
    Vue.mixin({
      beforeCreate: functionApiInit
   }
}
 
function install (Vue) {
    mixin(Vue);
}
 
export const Plugin = {
  install: (Vue: VueConstructor) => install(Vue),
}


Vue 插件就是向外面暴露一个 install 的方法,当调用 use 的时候会调用该方法,并把 Vue 构造函数作为参数传入,然后调用 Vue.mixin 混入对应钩子时要处理的函数。


接下来主要看下 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 || {}
  }


因为 Vue 在 beforeCreated 和 created 生命周期之间,会 initState 对数据进行处理,其中对 data的处理时就会调用 $options.data拿到定义的数据,所以这里重新对该函数其包裹一层,这也是为什么要选择 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
  }
}

这个函数比较长,不在本次要讲解的主线上代码逻辑都删除了,这个函数主要是创建了 ctx 和把 vm 实例转换成 Vue3 数据类型定义的 instance ,然后执行 setup 函数得到返回值,然后遍历每个属性,调用 asVmProperty 挂载到 vm 上面,当然这里的挂载不是直接通过把属性和值添加到 vm 上面,这么做会有一个问题,就是后续对该属性的修改不能同步到 vm 中,这里采用的还是 Vue 最常见的数据代理。


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
        },
      })
    }
}


看到这里,相信你已经明白了在 setup 中定义返回的为什么能够在 template 、 data 、 methods 等之中去使用了,因为返回的东西都已经被代理到 vm 之上了。


响应式( ref reactive 的实现)


接下来我们来说说响应式相关的,为什么 ref 和 reactive 也可以让数据成为响应式的。


ref 的实现其实是对 reactive 再次封装,主要用来给基本类型使用。


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),
  })
}

因为 reactive 接受的必须是一个对象,所有这里使用了一个常量作为 ref 的 key, 也就是:


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
}


以上就是所有关于在Vue2中实现composition API的教程,翼速应用平台内有更多相关资讯,欢迎查阅!


我来说两句

0 条评论

推荐阅读

  • 响应式布局CSS媒体查询设备像素比介绍

    构建响应式网站布局最常见的是流体网格,灵活调整大小的站点布局技术,确保用户在使用的幕上获得完整的体验。响应式设计如何展示富媒体图像,可以通过以下几种方法。

    admin
  • 提升网站的性能快速加载的实用技巧

    网站速度很重要,快速加载的网站会带来更好的用户体验、更高的转化率、更多的参与度,而且在搜索引擎排名中也扮演重要角色,做SEO,网站硬件是起跑线,如果输在了起跑线,又怎么跟同行竞争。有许多方法可提升网站的性能,有一些技巧可以避免踩坑。

    admin
  • 织梦CMS TAG页找不到标签和实现彩色标签解决方法

    织梦cms是我们常见的网站程序系统的一款,在TAG标签中常常遇到的问题也很多。当我们点击 tags.php 页的某个标签的时候,有时会提示:“系统无此标签,可 能已经移除!” 但是我们检查程序后台,以及前台显示页面。这个标签确实存在,如果解决这个问题那?

    admin
  • HTML关于fieldset标签主要的作用

    在前端开发html页面中常用的标签很多,今天为大家带来的是关于HTML中fieldset标签主要的作用说明,根据技术分析HTML

    admin

精选专题