李恒道 发表于 2024-10-18 08:07:40

【油猴开发指南】Vue3的常见数据位置与触发响应

Vue3的开发方式不再使用Vue2的选项式
这个时候数据来源以及修改就成了问题
我们可以创建一个最简单的实例来作为分析例子
```js
    const { createApp, ref } = Vue;
createApp({
setup() {
    debugger;
    const message = ref("Hello vue!");
    return {
      message,
    };
},
created() {
      console.log("created")
},
}).mount("#app");
```
针对setup函数打端点,通过堆栈回溯可以找到`setupStatefulComponent`函数进行调用
其中可以看到首先从Component组件的模板中获得了Setup,然后通过`callWithErrorHandling`函数来防止setup出错进行调用
```js
      const { setup } = Component;
      if (setup) {
      pauseTracking();
      const setupContext = instance.setupContext = setup.length > 1 ? createSetupContext(instance) : null;
      const reset = setCurrentInstance(instance);
      const setupResult = callWithErrorHandling(
          setup,
          instance,
          0,
          [
            shallowReadonly(instance.props) ,
            setupContext
          ]
      );
    }
```
其中最后一部分是setup传入的参数,我们可以发现
其中组件的setup函数的props数据来自于实例的`instance.props`通过一层`shallowReadonly`做浅包裹
而contexnt来自于instance.setupContext包含了emit,expose,attrs,slots四个参数,但是如果setup的参数没有进行使用不会进行创建

我们继续往下走可以走到`handleSetupResult`函数中
其中可以发现如果是函数则将作为render模板,而如果为对象则会设置到实例的setupState上
```js
    function handleSetupResult(instance, setupResult, isSSR) {
      if (isFunction(setupResult)) {
      {
          instance.render = setupResult;
      }
      } else if (isObject(setupResult)) {
      instance.setupState = proxyRefs(setupResult);
      }
```
而我们混入的this属性,根据调试可以确定实例在this的`_`属性上,由此可以推断出以下常见数据所在位置
```js
//setup数据this['_'].setupState
//data数据   this['_'].$data
//render数据 this['_'].render
//props数据this['_'].props
```
关于修改数据,其中`setupState`最简单,他是通过ref函数创建的,而ref最后创建的是`RefImpl`的类实例对象,根据代码可知直接设置value属性即可
```js
    class RefImpl {
      constructor(value, isShallow2) {
      this.dep = new Dep();
      this["__v_isRef"] = true;
      this["__v_isShallow"] = false;
      this._rawValue = isShallow2 ? value : toRaw(value);
      this._value = isShallow2 ? value : toReactive(value);
      this["__v_isShallow"] = isShallow2;
      }
      get value() {
      {
          this.dep.track({
            target: this,
            type: "get",
            key: "value"
          });
      }
      return this._value;
      }
      set value(newValue) {
      const oldValue = this._rawValue;
      const useDirectValue = this["__v_isShallow"] || isShallow(newValue) || isReadonly(newValue);
      newValue = useDirectValue ? newValue : toRaw(newValue);
      if (hasChanged(newValue, oldValue)) {
          this._rawValue = newValue;
          this._value = useDirectValue ? newValue : toReactive(newValue);
          {
            this.dep.trigger({
            target: this,
            type: "set",
            key: "value",
            newValue,
            oldValue
            });
          }
      }
      }
    }
```
需要注意的是,Vue3中data属性是选项式data函数所返回的,而setup返回的是setupState属性,二者不能混为一谈,由于data其修改方式类似,暂且就不表了
除此之外还有一个props属性,props只有最顶层的数据触发才能进行修改,不能单纯的修改某个属性内容,例如
```js
instance.props.info={...instance.props.info,title:"123456"}
```
才可以成功触发数据响应
而render一般不推荐进行修改,暂且就不表了~
# 结语
撒花~


懒男孩 发表于 2024-10-19 22:28:16

ggnb

krystal 发表于 2024-11-4 17:58:59

道哥 生产模式下setupState 是空的 是没办法拿到数据吗

李恒道 发表于 2024-11-4 18:11:15

krystal 发表于 2024-11-4 17:58
道哥 生产模式下setupState 是空的 是没办法拿到数据吗
setup返回render是存在闭包的
setupState按道理是应该为空对象
从理论上来说vue需要diff,按道理也存在数据的注入位置
但是这部分我还没有研究
可能需要从vnode方面下手

目前vite/webpack5/vue3的常用注入位置都还没搞完
{:4_98:}
道阻且长啊


页: [1]
查看完整版本: 【油猴开发指南】Vue3的常见数据位置与触发响应