Vue2実装APIの原理を解析する

青灯夜游
リリース: 2023-01-13 20:23:23
転載
2563 人が閲覧しました

Vue2実装APIの原理を解析する

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# と同じように実行します。 ## 戻り値はマージされます。 [関連する推奨事項: vuejs ビデオ チュートリアル Web フロントエンド開発 ]

  • vue2

    は、 data 内のデータは応答的に処理されますか? refreactive はどのように行うのでしょうか?

  • vue2

    レスポンシブ データによって定義された制約 (元のオブジェクトに割り当てられていない属性の追加、配列の添字の変更など)、ref# を使用します。 ## 代わりに reactive でよろしいでしょうか?

    もちろん、このプラグインは非常に多くの
  • API
を提供し、

Vue3## のほとんどをカバーしているため、まだ多くの疑問があります。 # has, here 主にこれらの質問からそれがどのように行われるかを分析してみましょう。 原理分析

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 が呼び出され、定義されたデータを取得します。 data. , したがって、関数はここで再ラップされます。これが、beforeCreate フック インジェクションが選択される理由の 1 つです。フック インジェクションは、関数が呼び出される前にラップする必要があります。 次に、initSetup

function initSetup(vm: ComponentInstance, props: Record = {}) {
  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> | 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
) {
  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、## で使用できる理由が理解できたと思います。 # メソッド など。返されたものは 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(
  options: RefOption,
  isReadonly = false,
  isComputed = false
): RefImpl {
  const r = new RefImpl(options)

  const sealed = Object.seal(r)
  if (isReadonly) readonlySet.set(sealed, true)
  return sealed
}

export class RefImpl implements Ref {
  readonly [_refBrand]!: true
  public value!: T
  constructor({ get, set }: RefOption) {
    proxy(this, 'value', {
      get,
      set,
    })
  }
}
ログイン後にコピー

通过 new RefImpl 实例,该实例上有一个 value 的属性,对 value 做代理,当取值的时候返回 value[RefKey],赋值的时候赋值给 value[RefKey], 这就是为什么 ref 可以用在基本类型,然后对返回值的 .value 进行操作。调用 object.seal 是把对象密封起来(会让这个对象变的不能添加新属性,且所有已有属性会变的不可配置。属性不可配置的效果就是属性变的不可删除,以及一个数据属性不能被重新定义成为访问器属性,或者反之。但属性的值仍然可以修改。)

我们主要看下 reactive 的实现

export function reactive(obj: T): UnwrapRef {
    const observed = observe(obj)
    setupAccessControl(observed)
    return observed as UnwrapRef
}


export function observe(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 文档最后的限制章节,里面讲了一些使用限制。

(学习视频分享:vuejs入门教程编程基础视频

以上がVue2実装APIの原理を解析するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:juejin.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!