vue3-组合式API

前段时间学习之后总结的,最近有空码上来,过程中参考了别人的文章,总结错了请指出。


Vue 3.0 中文文档仓库

诞生缘由

在 Vue2 中,随着功能的增长,复杂组件的代码变得越来越难以理解和维护。为解决这个问题,在vue3中引入了Composition API也称为组合式API,用于提取和重用多个组件之间的逻辑。

vue2.0与vue3.0区别

  1. 数据的双向绑定
    Vue2.0使用Object.defineProperty ,Vue 3.0使用ES6的新特性porxy。
    1. defineProperty性能比proxy差
    2. defineProperty只能监听单一属性,需要把对象遍历一遍进行监听, proxy可以劫持整个对象。
  2. 完全ts支持
    3.0用ts重写,可使用ts强大的类型系统、类型提示。
  3. 兼容vue2.0
  4. 性能快1~2倍
    1. diff算法优化:引入了静态标记。当数据发生改变需要与之前的虚拟节点对比时,会通过静态标记进行对比。
    2. 静态提升:2.0中无论元素是否参与更新,每次渲染时都会重建,3.0中使用了静态提升,对于不参与更新的元素,只会被创建一次,在渲染时直接复用。
    3. porxy的使用。
  5. 更先进的组件
    例如fragment组件更简单,有一个虚拟的父级,不再限制最外层只有一个节点。
  6. 组合式API-Composition API
    类似React的Hooks。
    2.0的Options API代码组织不灵活:
    在 props 里面设置接收参数。
    在 data 里面设置变量。
    在 computed 里面设置计算属性。
    在 watch 里面设置监听属性。
    在 methods 里面设置事件方法。

一、setup

简介

  • setup函数是一个新的组件选项。作为在组件内使用Composition API的入口点。

调用时机

  1. 创建组件实例,然后初始化props,然后调用setup函数。
  2. 从生命周期钩子的视角来看,它会在beforeCreate钩子之前被调用。

使用

  1. 如果setup返回一个对象,则对象的属性将会被合并到组件模板的渲染上下文。
  2. 如果setup返回一个函数,也可以使用其中的响应式数据。
<template>
  <div>
    <p>{{ count }}</p>
  </div>
</template>
<script>
export default {
  name: "App",
  props: {
    msg: String,
    default: () => ""
  },
  setup() {
    const count = 0;
    return { count };
    //return () => h("div", [count.value]);
  }
};
</script>

注意:

  1. 不用ref来定义count的初始值,在使用过程中无法响应式操作。
  2. setup()中this是undefined。
<template>
  <div>
    <p>{{ count }}</p>
    <button @click="addCount">增加</button>
  </div>
</template>
<script>
export default {
  name: "App",
  setup() {
    let count = 0;
    function addCount() {
      console.log(count);
      count += 1;
    }
    return { count, addCount };
  }
};
</script>

二、ref

简介

  • ref()函数用来根据给定的值创建一个响应式的数据对象,ref()函数调用的返回值是一个对象,这个对象上只包含一个value属性。

使用

  • 作为响应式数据对象。
<template>
  <div>{{ count }}</div>
  <button @click="addCount">增加</button>
</template>
<script>
import { ref } from "vue";
export default {
  name: "App",
  setup() {
    const count = ref(0);
    function addCount() {
      console.log(count.value);
      count.value += 1;
    }
    return { count, addCount };
  }
};
</script>
  • 调用原生DOM
<template>
  <div ref="divBox">ref的调用用法</div>
</template>
<script>
import { onMounted, ref } from "vue";
export default {
  name: "HelloWorld",
  setup() {
    const divBox = ref(null);
    onMounted(() => {
      const dom = divBox.value;
      console.log("dom", dom);
      console.log("parentElement", dom.parentElement);
    });
    return {
      divBox
    };
  }
};
</script>

注意:

  • 这里的onMounted与vue2中的mounted是一样的,是vue3的生命周期新用法。
    在这里插入图片描述

三、reactive

简介

  • reactive函数和ref作用非常接近,但是它的参数必须是一个对象。

使用

<template>
  <div>{{ state.age }}</div>
  <button @click="change">改变</button>
</template>
<script>
import { reactive } from "vue";
export default {
  name: "HelloWorld",
  setup() {
    const state = reactive({
      age: 18
    });
    function change() {
      state.age = 20;
    }
    return {
      state,
      change
    };
  }
};
</script>

注意:

  • reactive中传递的参数必须是json对象或者数组,如果传递了其他对象(比如new Date()),在默认情况下修改对象,界面不会自动更新,如果也需要具有响应式,可以通过重新赋值的方式实现。

不会改变的情况:

<template>
  <div>{{ state.time }}</div>
  <button @click="change">改变</button>
</template>
<script>
import { reactive } from "vue";
export default {
  name: "HelloWorld",
  setup() {
    const state = reactive({
      time: new Date()
    });
    function change() {
      state.time.setDate(state.time.getDate() + 1);
      console.log(state.time);
    }
    return {
      state,
      change
    };
  }
};
</script>

会改变的情况:

<template>
  <div>
    <p>{{ state.time }}</p>
    <button @click="change">改变</button>
  </div>
</template>
<script>
import { reactive } from "vue";
export default {
  name: "HelloWorld",
  setup() {
    const state = reactive({
      time: new Date()
    });
    function change() {
      const newTime = new Date(state.time);
      newTime.setDate(state.time.getDate() + 1);
      state.time = newTime;
      console.log(state.time);
    }
    return { state, change };
  }
};
</script>

四、ref与reactive认识

  1. ref本质是reactive,当我们给ref传递一个值后,ref函数底层会自动把ref转换成reactive,例如:

    ref(18) -> reactive({value: 18})
    
  2. 在template模板里面,ref会自动加上.value, 但是如何判断的呢?

    1. 打印ref对象会发现,ref对象里面有一个私有属性__v_isRef: true,有这个属性就自动添加.value。
    2. 我们可以用isRef()、isReactive()来判断是什么类型。

    初始化代码:

    <template>
      <div>
        <p>{{ count }}</p>
        <button @click="addRef">加ref</button>
        <p>{{ state.age }}</p>
        <button @click="addReactive">加reactive</button>
      </div>
    </template>
    <script>
    import { ref, reactive } from "vue";
    export default {
      name: "HelloWorld",
      setup() {
        const count = ref(0);
        function addRef() {
          count.value++;
          console.log("count", count);
        }
        const state = reactive({
          age: 18
        });
        function addReactive() {
          state.age++;
          console.log("state", state);
        }
        return { count, addRef, state, addReactive };
      }
    };
    </script>
    

    代码提取:

    <template>
      <div>
        <p>{{ count }}</p>
        <button @click="addRef">加ref</button>
        <p>{{ state.age }}</p>
        <button @click="addReactive">加reactive</button>
      </div>
    </template>
    <script>
    import { ref, reactive } from "vue";
    function addRef_() {
      const count = ref(0);
      function addRef() {
        count.value++;
        console.log("count", count);
      }
      return { count, addRef };
    }
    function addReactive_() {
      const state = reactive({
        age: 18
      });
      function addReactive() {
        state.age++;
        console.log("state", state);
      }
      return { state, addReactive };
    }
    export default {
      name: "HelloWorld",
      setup() {
        const { count, addRef } = addRef_();
        const { state, addReactive } = addReactive_();
        return { count, addRef, state, addReactive };
      }
    };
    </script>
    

    isRef()、isReactive()的使用:

    <template>
      <div>
        <p>{{ count }}</p>
        <button @click="addRef">加ref</button>
        <p>{{ state.age }}</p>
        <button @click="addReactive">加reactive</button>
      </div>
    </template>
    <script>
    import { ref, reactive, isRef, isReactive } from "vue";
    function addRef_() {
      const count = ref(0);
      function addRef() {
        count.value++;
        console.log("isRef", isRef(count));
        console.log("isReactive", isReactive(count));
      }
      return { count, addRef };
    }
    function addReactive_() {
      const state = reactive({
        age: 18
      });
      function addReactive() {
        state.age++;
        console.log("isReactive", isReactive(state));
        console.log("isRef", isRef(state));
      }
      return { state, addReactive };
    }
    export default {
      name: "HelloWorld",
      setup() {
        const { count, addRef } = addRef_();
        const { state, addReactive } = addReactive_();
        return { count, addRef, state, addReactive };
      }
    };
    </script>
    

五、递归监听

  • 默认情况下,无论ref还是reactive都是递归监听。

验证reactive:

<template>
  <div>
    <p>{{ state.a }}</p>
    <p>{{ state.a1.b }}</p>
    <p>{{ state.a1.b1.c }}</p>
    <button @click="change">改变</button>
  </div>
</template>
<script>
import { reactive } from "vue";
function changeReactive_() {
  const state = reactive({
    a: "a",
    a1: {
      b: "b",
      b1: {
        c: "c"
      }
    }
  });
  function change() {
    state.a = "1";
    state.a1.b = "2";
    state.a1.b1.c = "3";
  }
  return { state, change };
}
export default {
  name: "HelloWorld",
  setup() {
    const { state, change } = changeReactive_();
    return { state, change };
  }
};
</script>

验证ref:

<template>
  <div>
    <p>{{ state.a }}</p>
    <p>{{ state.a1.b }}</p>
    <p>{{ state.a1.b1.c }}</p>
    <button @click="change">改变</button>
  </div>
</template>
<script>
import { ref } from "vue";
function changeRef_() {
  const state = ref({
    a: "a",
    a1: {
      b: "b",
      b1: {
        c: "c"
      }
    }
  });
  function change() {
    state.value.a = "1";
    state.value.a1.b = "2";
    state.value.a1.b1.c = "3";
  }
  return { state, change };
}
export default {
  name: "HelloWorld",
  setup() {
    const { state, change } = changeRef_();
    return { state, change };
  }
};
</script>

六、非递归监听

  • shallowReactive()

    <template>
      <div>
        <p>{{ state.a }}</p>
        <p>{{ state.a1.b }}</p>
        <p>{{ state.a1.b1.c }}</p>
        <button @click="change">改变</button>
      </div>
    </template>
    <script>
    import { shallowReactive } from "vue";
    function changeReactive_() {
      const state = shallowReactive({
        a: "a",
        a1: {
          b: "b",
          b1: {
            c: "c"
          }
        }
      });
      function change() {
        state.a = "1";
        state.a1.b = "2";
        state.a1.b1.c = "3";
        console.log(state);
        console.log(state.a1);
        console.log(state.a1.b1);
      }
      return { state, change };
    }
    export default {
      name: "HelloWorld",
      setup() {
        const { state, change } = changeReactive_();
        return { state, change };
      }
    };
    </script>
    
  • shallowRef()

    • 不可递归:
    <template>
      <div>
        <p>{{ state.a }}</p>
        <p>{{ state.a1.b }}</p>
        <p>{{ state.a1.b1.c }}</p>
        <button @click="change">改变</button>
      </div>
    </template>
    <script>
    import { shallowRef } from "vue";
    function changeReactive_() {
      const state = shallowRef({
        a: "a",
        a1: {
          b: "b",
          b1: {
            c: "c"
          }
        }
      });
      function change() {
        state.value.a = "1";
        state.value.a1.b = "2";
        state.value.a1.b1.c = "3";
        console.log(state);
        console.log(state.value.a1);
        console.log(state.value.a1.b1);
      }
      return { state, change };
    }
    export default {
      name: "HelloWorld",
      setup() {
        const { state, change } = changeReactive_();
        return { state, change };
      }
    };
    </script>
    
    • 可以递归,但有缺陷:只能这样改,无法指定修改

      <template>
        <div>
          <p>{{ state.a }}</p>
          <p>{{ state.a1.b }}</p>
          <p>{{ state.a1.b1.c }}</p>
          <button @click="change">改变</button>
        </div>
      </template>
      <script>
      import { shallowRef } from "vue";
      function changeReactive_() {
        const state = shallowRef({
          a: "a",
          a1: {
            b: "b",
            b1: {
              c: "c"
            }
          }
        });
        function change() {
          state.value = {
            a: "1",
            a1: {
              b: "2",
              b1: {
                c: "3"
              }
            }
          };
          console.log(state);
          console.log(state.value.a1);
          console.log(state.value.a1.b1);
        }
        return { state, change };
      }
      export default {
        name: "HelloWorld",
        setup() {
          const { state, change } = changeReactive_();
          return { state, change };
        }
      };
      </script>
      
    • 强制修改:

      <template>
        <div>
          <p>{{ state.a }}</p>
          <p>{{ state.a1.b }}</p>
          <p>{{ state.a1.b1.c }}</p>
          <button @click="change">改变</button>
        </div>
      </template>
      <script>
      import { shallowRef, triggerRef } from "vue";
      function changeReactive_() {
        const state = shallowRef({
          a: "a",
          a1: {
            b: "b",
            b1: {
              c: "c"
            }
          }
        });
        function change() {
          state.value.a1.b1.c = "3";
          triggerRef(state);
          /* state.value.a = "1";
          state.value.a1.b = "2";
          state.value.a1.b1.c = "3"; */
          console.log(state);
          console.log(state.value.a1);
          console.log(state.value.a1.b1);
        }
        return { state, change };
      }
      export default {
        name: "HelloWorld",
        setup() {
          const { state, change } = changeReactive_();
          return { state, change };
        }
      };
      </script>
      

    注意:

    • vue3只提供了triggerRef(), 没有提供triggerReactive(),所以使用shallowReactive()无法主动更新界面。
    • shallowRef的本质:
      1. ref(18) -> reactive({value: 18})
      2. shallowRef(18) -> shallowReactive({value: 18})
      

shallowRef、shallowReactive的理解

  1. 在vue2.x中使用defineProperty来实现响应式数据的,在vue3.x中是通过proxy来实现。
  2. 以下代码我是在单独js文件中编写, 编辑器内运行的。
  3. 非源码,实现过程只是为了理解方法的原理

proxy简单实现:

const obj = {
  name: "老大",
  age: 20
};
const state = new Proxy(obj, {
  get(obj, key) {
    console.log("获取", obj[key]);
    return obj[key];
  },
  set(obj, key, value) {
    obj[key] = value;
    console.log("赋值", obj);
    return true;
  }
});
state.name = "老小";

shallowRef()、shallowReactive() 简单实现:

function shallowReactive(obj) {
  return new Proxy(obj, {
    get(obj, key) {
      return obj[key];
    },
    set(obj, key, value) {
      obj[key] = value;
      console.log(obj);
      console.log("更新UI");
      return true;
    }
  });
}
function shallowRef(val) {
  return shallowReactive({
    value: val
  });
}
const obj = {
  a: "a",
  a1: {
    b: "b",
    b1: {
      c: "c"
    }
  }
};
/* const state = shallowReactive(obj);
state.a = "1";
state.a1.b = "2";
state.a1.b1.c = "3"; */
const state = shallowRef(obj);
state.value = {
  a: "1",
  a1: {
    b: "2",
    b1: {
      c: "3"
    }
  }
};

ref与reactive响应式理解

// let obj = {name: "老大", age: 20}
/* let arr = [
  1,
  {
    name: "老大",
    age: 20
  }
]; */
function reactive(obj) {
  if (typeof obj === "object") {
    if (obj instanceof Array) {
      // 如果是一个数组,那么每个元素是不是对象,是的话咱们是不是需要再把他包装成proxy
      obj.forEach((item, index) => {
        if (typeof item === "object") {
          obj[index] = reactive(item);
        }
      });
    } else {
      // 如果是对象,那么没个属性是不是对象,是的话是不是要把它包装成proxy
      for (let key in obj) {
        const item = obj[key];
        if (typeof item === "object") {
          obj[key] = reactive(item);
        }
      }
    }
    return new Proxy(obj, {
      get(obj, key) {
        return obj[key];
      },
      set(obj, key, value) {
        obj[key] = value;
        // console.log(obj);
        console.log("更新UI");
        return true;
      },
    });
  } else {
    console.log(`${obj}: 不是对象`);
  }
}
function ref(val) {
  return reactive({
    value: val
  });
}
const obj = {
  a: "a",
  a1: {
    b: "b",
    b1: {
      c: "c"
    }
  }
};
/* const state = reactive(obj);
state.a = "1";
state.a1.b = "2";
state.a1.b1.c = "3"; */
const arr = [
  {
    name: "老大",
    age: 20,
  },
  {
    name: "老二",
    age: 18,
  },
];
const state = reactive(arr);
state.push({
  name: "健康",
  age: 1000,
});
console.log(state.length);

放到vue项目中使用:

<template>
  <div>
    <p>{{ state.a }}</p>
    <p>{{ state.a1.b }}</p>
    <p>{{ state.a1.b1.c }}</p>
    <button @click="change">改变</button>
  </div>
</template>
<script>
function reactive(obj) {
  if (typeof obj === "object") {
    if (obj instanceof Array) {
      // 如果是一个数组,那么每个元素是不是对象,是的话咱们是不是需要再把他包装成proxy
      obj.forEach((item, index) => {
        if (typeof item === "object") {
          obj[index] = reactive(item);
        }
      });
    } else {
      // 如果是对象,那么没个属性是不是对象,是的话是不是要把它包装成proxy
      for (const key in obj) {
        const item = obj[key];
        if (typeof item === "object") {
          obj[key] = reactive(item);
        }
      }
    }
    return new Proxy(obj, {
      get(obj, key) {
        return obj[key];
      },
      set(obj, key, value) {
        obj[key] = value;
        // console.log(obj);
        console.log("更新UI");
        return true;
      }
    });
  } else {
    console.log(`${obj}: 不是对象`);
  }
}
function ref(val) {
  return reactive({
    value: val
  });
}
function changeReactive_() {
  const state = reactive({
    a: "a",
    a1: {
      b: "b",
      b1: {
        c: "c"
      }
    }
  });
  function change() {
    state.a = "1";
    state.a1.b = "2";
    state.a1.b1.c = "3";
    console.log(state);
    console.log(state.a1);
    console.log(state.a1.b1);
  }
  return { state, change };
}
export default {
  name: "HelloWorld",
  setup() {
    const { state, change } = changeReactive_();
    return { state, change };
  }
};
</script>
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bug菌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值