Vue2如何支持composition API示例详解

目录
  • 前言
  • 如何使用
    • 原理解析
  • 响应式( ref reactive 的实现)
  • 总结

前言

自从 Vue3 发布之后,composition API 这个词走入写 Vue 同学的视野之中,相信大家也一直听到 composition API 比之前的 options API 有多好多强,如今由于 @vue/composition-api 插件的发布,Vue2 的同学也可以上车咯,接下来我们主要以响应式的 refreactive 来深入分析一下,这个插件是怎么实现此功能的。

如何使用

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

怎么样,看完是不是感觉和 vue3 一模一样,你可能会想:

  • 这是 vue2 啊,我之前的 datamethods 里面也有变量和方法,怎么做到跟 setup 返回值打通合并在一起的。
  • vue2 不是只有定义在 data 里面的数据才会被处理成响应式的吗? refreactive 是怎么做到的呢?
  • vue2 响应式数据定义的约束(添加赋值原对象没有的属性,数组下标修改等),改用 refreactive 就没问题吗?

当然还有很多的疑惑,因为插件提供的 API 相当多,覆盖了绝大部分 Vue3 所拥有的,这里主要从这几个问题来分析一下是如何做到的。

原理解析

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

// 这里只贴跟本章要讲的相关代码
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 || {}
  }

因为 VuebeforeCreatedcreated 生命周期之间,会 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 中定义返回的为什么能够在 templatedatamethods 等之中去使用了,因为返回的东西都已经被代理到 vm 之上了。

响应式( ref reactive 的实现)

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

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 接受的必须是一个对象,所有这里使用了一个常量作为 refkey, 也就是

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 -> proxysetup 里面的 foo -> observedfoo ,完成取值的流程,这会比直接在 data 上多代理了一层,因此整个过程也会有额外的性能开销。

因此使用该 API 也不会让你可以直接规避掉 vue2 响应式数据定义的约束,因为最终还是用 Object.defineProperty 去做对象拦截,插件同样也提供了 set API 让你去操作对象新增属性等操作。

总结

通过上面的了解,相信你一定对于 Vue2 如何使用 composition API 有了一定的了解,因为 API 相当多, 响应式相关的就还有 toRefs、toRef、unref、shallowRef、triggerRef 等等,这里就不一一分析,有兴趣的可以继续看源码的实现。

Vue2 的同学也可以不用羡慕写 Vue3 的同学了,直接引入到项目就可以使用起来,虽然没有 vue3 那么好的体验,但是绝大部分场景还是相同的,使用时注意 README 文档最后的限制章节,里面讲了一些使用限制。

以上就是Vue2如何支持composition API示例详解的详细内容,更多关于Vue 支持composition API的资料请关注我们其它相关文章!

(0)

相关推荐

  • 详解vue3.0 的 Composition API 的一种使用方法

    网上讨论的文章已经很多了,这里举一个简单的例子来讨论一下 Composition API 的用法,具体问题才好具体讨论嘛. 假如我们要做一个论坛的讨论列表和分页,以前是把需要的数据都放在data里面,事件都放在methods 里面,代码少的话还好分辨,如果代码多了,看着就头疼了. 现在vue3.0可以按照业务关系分成多个管理类来管理这些代码. 比如帖子列表+发帖功能,我们先做一个模板: (简单表示一下,不做美化处理了) <template> <div> 论坛列表 <div v

  • Vue3 Composition API的使用简介

    vue3.0在7月发布了rc版本,vue-cli4.5后也支持选择vue3作为备选版本可以体验了,vue3的正式版本相必也不远了.学不动了呀!!!! 相比vue2.0版本(Option API),Composition API(组合API)算是3.0的重大变更之一了. 概述 Composition API 主要灵感来源于React Hooks,目的是通过一组低侵入式的.函数式的 API,使得我们能够更灵活地「组合」组件的逻辑. 示例 <template> <div>{{count}

  • 动画详解Vue3的Composition Api

    目录 回顾Option Api Option Api的缺陷 Composition Api 众所周知,Vue3.0带来了一个全新的特性——Composition API. 字面意思就是“组合API”,它是为了实现基于函数的逻辑复用机制而产生的. 回顾Option Api 在了解Composition Api之前,首先回顾下我们使用Option Api遇到的问题,我们在Vue2中常常会需要在特定的区域(data,methods,watch,computed...)编写负责相同功能的代码. Opti

  • vue3.2 Composition API项目依赖升级

    目录 第一次Composition API 思考setup 最常见的问题,忘记写.value style v-bind 的优缺点 pinia or not 第一次Composition API 在vue3.2中,正式支持了script setup的写法,这样可以大大简化组件的代码量,减少一些重复操作,我认为当你写vue3时,应该把这当作默认写法. 在vue3.2之前,一般会这样写. <script> export default { setup(props,ctx){ const a = re

  • Vue3 Composition API优雅封装第三方组件实例

    目录 前言 一.对于第三方组件的属性props.事件events 二.对于第三方组件的插槽slots 三.对于第三方组件的方法methods 前言 对于第三方组件,如何在保持第三方组件原有功能(属性props.事件events.插槽slots.方法methods)的基础上,优雅地进行功能的扩展了? 以Element Plus的el-input为例: 很有可能你以前是这样玩的,封装一个MyInput组件,把要使用的属性props.事件events和插槽slots.方法methods根据自己的需要再

  • Vue3 composition API实现逻辑复用的方法

    Composition API实现逻辑复用的步骤: 抽离逻辑代码到一个函数,这个函数命令约定为useXXX格式(这点同React Hooks) 在setup中引用函数useXXX 举下例子,定义一个获取当前鼠标位置的方法 第一种,直接使用ref定义的useMousePosition: 这种方式,导出和导入都可以随意解构 // useMousePosition.js import { ref, onMounted, onUnmounted } from 'vue' // 1. 定义一个函数,抽离逻

  • Vue2如何支持composition API示例详解

    目录 前言 如何使用 原理解析 响应式( ref reactive 的实现) 总结 前言 自从 Vue3 发布之后,composition API 这个词走入写 Vue 同学的视野之中,相信大家也一直听到 composition API 比之前的 options API 有多好多强,如今由于 @vue/composition-api 插件的发布,Vue2 的同学也可以上车咯,接下来我们主要以响应式的 ref 和 reactive 来深入分析一下,这个插件是怎么实现此功能的. 如何使用 // 入口

  • Vue3进阶主题Composition API使用详解

    目录 什么是Composition API 为什么Vue3推荐使用Composition API 总结 什么是Composition API Composition API 是 Vue3 中引入的一种新的 API 风格,旨在提高代码的可读性.可维护性和可重用性.Composition API 不同于 Vue2 中的 Options API,它采用了一种基于函数的编程方式,将组件内的逻辑分解成更小的可组合函数单元,以实现更加灵活和高效的代码组织方式. 为什么Vue3推荐使用Composition

  • javascrip高级前端开发常用的几个API示例详解

    目录 MutationObserver API 特点 IntersectionObserver API 举个例子 图片懒加载 无限滚动 getComputedStyle() API 与style的异同 getBoundingClientRect API 应用场景 1.获取 dom 元素相对于网页左上角定位的距离 2.判断元素是否在可视区域内 MutationObserver MutationObserver 是一个可以监听 DOM 结构变化的接口. 当 DOM 对象树发生任何变动时,Mutati

  • Java8新特性Optional类及新时间日期API示例详解

    目录 Optional类 以前对null的处理 Optional类介绍 Optional的基本使用 Optional的常用方法 新时间日期API 旧版日期时间的问题 新日期时间API介绍 日期时间的常见操作 日期时间的修改和比较 格式化和解析操作 Instant类 计算日期时间差 时间校正器 日期时间的时区 JDK新的日期和时间API的优势 Optional类 面试官:Optional类了解过吗? 这个Optional类主要是解决空指针的问题. 以前对null的处理 @Test public v

  • vue整合项目中百度API示例详解

    目录 官网介绍 申请密钥 官方示例 项目实战 创建地图 获取经纬度 创建Map实例 两个坐标点之间的距离 查询地点信息 Vue项目中整合百度API获取地理位置的方法 组件中使用 vue-baidu-map 百度地图官方vue组件 官网介绍 百度地图 JavaScript API 是一套由 JavaScript 语言编写的应用程序接口 可帮助您在网站中,构建功能丰富交互性强的地图应用 支持PC端和移动端,基于浏览器的地图应用开发,且支持HTML5特性的地图开发 官网传送门 百度地图JavaScri

  • Vue3中的组合式 API示例详解

    目录 为什么要有组合式 API? 更好的逻辑复用 更灵活的代码组织 更好的类型推导 生产包体积更小 与选项式 API 的关系 组合式 API 是否覆盖了所有场景? 可以同时使用两种 API 吗? 选项式 API 会被废弃吗? 组合式 API 是一系列 API 的集合,使我们可以使用函数而不是声明选项的方式书写 Vue 组件.它是一个概括性的术语,涵盖了以下方面的 API: 响应性 API:例如ref()和reactive(),使我们可以直接创建响应式状态.计算属性和侦听器. 生命周期钩子:例如o

  • Gin与Mysql实现简单Restful风格API实战示例详解

    目录 It works main.go 编译运行 数据库 CURD 增删改查 增 查 查询列表 Query 查询单条记录 QueryRow 改 删 组织代码 封装模型方法 Handler函数 组织项目 数据库处理 数据model封装 handler 路由 分组路由 app入口 总结 我们已经了解了Golang的Gin框架.对于Webservice服务,restful风格几乎一统天下.Gin也天然的支持restful.下面就使用gin写一个简单的服务,麻雀虽小,五脏俱全.我们先以一个单文件开始,然

  • Composition API思想封装NProgress示例详解

    目录 正文 安装和基本使用 自己实现一个 正文 最近在用vue3封装一套后台管理模版,自然会用到NProgress.如果你没有用过,你可以看一下instagram,youtube这些网站,它们都有一个顶部加载条,这也是现在最流行的网页加载条. 于是我发现了@vueuse/integrations中的useNProgress,我们先看一下Anthony Fu大神如何去封装的,然后咱们再去试着自己实现一下. 安装和基本使用 npm i nprogress @vueuse/integrations 基

  • vue组合式API浅显入门示例详解

    目录 正文 组合式API setup 变量声明 目前比起选项式API的优点 生命周期 正文 在react推出了hook之后,vue也在vue3里面添加了组合式API,鉴于这个时间间隔,我有理由认为组合式api和hook还是有一些关系的.不过在我具体的使用中,我并没发现这两个方法太多的相同点,不过鉴于vue自动发布之后就与react之间那剪不断理还乱的量子力学关系,估计会有很多人会问及这俩之间的异同.我还没到能分辨出异同的地步,就简单的介绍一下vue的组合式API吧. 组合式API 在vue3.0

  • 使用java8 API遍历过滤文件目录及子目录和隐藏文件示例详解

    1. 使用Files.list()迭代目录及其子目录文件 Files.list()可以迭代目录及其子目录文件 Files.list(Paths.get(".")) //当前目录 .forEach(System.out::println); 输出: .\filename1.txt .\directory1 .\filename2.txt .\Employee.java 2. 使用 filter表达式过滤文件 过滤器函数引用,isRegularFile表示普通文件 Files.list(P

随机推荐