Vue3基础学习

创建vue3项目

安装或升级vue至高版本
npm install -g @vue/cli
保证 vue cli 版本在 4.5.0 以上
vue --version
创建项目
vue create my-project

Composition API(常用部分)

1. setup()
  • 新的option,所有的组合API函数都在此使用,只在初始化时执行一次;
  • 函数如果返回对象,对象中的属性或方法在模板中可以直接使用;
2. ref

作用:定义一个数据的响应式
语法:const xxx = ref(initValue);

  • js中使用 xxx.value
  • 模板中使用不需要 .value
    例:
 <template>
   <div class="about">
     <h2>{{ count }}</h2>
     <button @click="update">更新</button>
   </div>
 </template>
 <script>
 import { ref } from "vue";
 export default {
   setup() {
     // 定义响应式数据 ref对象
     const count = ref(2);
     console.log("定义响应式数据 ref对象", count);
     // 更新响应式数据的函数
     function update() {
       count.value = count.value + 1;
     }
     return {
       count,
       update,
     };
   },
 };
 </script>
3. reactive
  • 作用:定义多个数据的响应式
  • const proxy = reactive(obj);接收一个普通对象然后返回该普通对象的响应式代理对象;
  • 响应式转换是“深层的”,会影响对象内部所有嵌套的属性;
  • 内部基于 ES6 的Proxy实现,通过代理对象操作源对象内部数据都是响应式的;
    例:
 <template>
   <div class="about">
     <h2>{{ state.name }}今年{{ state.age }}岁了</h2>
     <h2>他的妻子{{ state.wife.name }}今年{{ state.wife.age }}岁了</h2>
     <button @click="update">更新</button>
   </div>
 </template>
 <script>
 import { reactive } from "vue";
 export default {
   setup() {
     const state = reactive({
       name: "tom",
       age: 25,
       wife: {
         name: "lisa",
         age: 23,
       },
     });
     console.log(state, state.wife);
     const update = () => {
       state.name += "-";
       state.age += 1;
       state.wife.name += "+";
       state.wife.age += 1;
     };
     return {
       state,
       update,
     };
   },
 };
 </script>
4. 比较 Vue2 与 Vue3 的响应式(重要)
  • vue2的响应式
    核心:
    对象: 通过defineProperty对对象的已有属性值的读取和修改进行劫持(监视/拦截)
    数组: 通过重写数组更新数组一系列更新元素的方法来实现元素修改的劫持
  Object.defineProperty(data, 'count', {
    get () {},
    set () {}
  })

问题
对象直接新添加的属性或删除已有属性, 界面不会自动更新
直接通过下标替换元素或更新length, 界面不会自动更新 arr[1] = {}

  • vue3的响应式
    核心:
    通过Proxy(代理):拦截对data任意属性的任意操作,包括属性值的读写,属性的添加,属性的删除等等;
    通过 Reflect(反射):动态对被代理对象的相应属性进行特定的操作;
    文档:
    https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
    https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect
    例:
  <script>
  import { reactive } from "vue";
  export default {
    setup() {
      const user = reactive({
        name: "tom",
        age: 11,
      });
      /* 
      proxyUser是代理对象, user是被代理对象
      后面所有的操作都是通过代理对象来操作被代理对象内部属性
      */
      const proxyUser = new Proxy(user, {
        // 拦截读取属性值
        get(target, prop) {
          console.log("劫持get", target, prop);
          return Reflect.get(target, prop);
        },
        // 拦截设置属性值或添加新属性
        set(target, prop, val) {
          console.log("劫持set", prop, val);
          return Reflect.set(target, prop, val);
        },
        // 拦截删除属性
        deleteProperty(target, prop) {
          console.log("劫持delete属性", prop);
          return Reflect.deleteProperty(target, prop);
        },
      });
      // 读取属性值
      console.log(proxyUser === user);
      console.log(proxyUser.name, proxyUser.age);
      // 设置属性
      proxyUser.name = "bbb";
      proxyUser.age = 13;
      console.log("user:", user);
      // 添加属性
      proxyUser.sex = "男";
      console.log("user:", user);
      // 删除属性
      delete proxyUser.sex;
      console.log("user:", user);
    },
  };
  </script>
5. setup细节
  • setup执行的时机
    • 在beforeCreate之前执行(一次),此时组件对象还没创建;
    • this此时是undefined,不能通过this来访问data、computed、methods、props;
    • 其实所有的composition API相关回调函数中也都不可以;
  • setup返回值
    • 一般都返回一个对象:为模板提供数据,也就是模板中可以直接使用此对象中的所有属性或方法;
    • 返回对象中的属性会与data函数返回对象的属性合并成为组件对象的属性;
    • 返回对象中的方法会与methods中的方法合并成组件对象中的方法;
    • 如果有重名,setup优先;
    • 注意:
    • 一般不要混合使用:methods中可以访问setup提供的属性和方法,但在setup方法中不能访问data和methods;
    • setup不能是一个async函数,因为返回值不再是return的对象,而是promise,模板看不到return对象中的属性数据;
  • setup的参数
    • setup(props, context) / setup(props, {attrs, slots, emit})
    • props:包含props配置声明且传入了所有属性的对象;
    • attrs:包含没有在props配置中声明的属性对象,相当于 this.$attrs;
    • slots:包含所有传入的插槽内容对象,相当于 this.$slots;
    • emit:用来分发自定义事件的函数,相当于 this.$emit;
      例:
      父组件:
<template>
<div class="about">
  <h2>msg:{{ msg }}</h2>
  <button @click="fn('--')">更新</button>
  <child :msg="msg" msg2="deg" @upfn="fn" />
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, ref } from "vue";
import child from "./child2.vue";
export default defineComponent({
components: {
  child,
},
setup() {
  const msg = ref("abc");
  function fn(content: string) {
    msg.value += content;
  }
  return {
    msg,
    fn,
  };
},
});
</script>

子组件:

  <template>
    <div>
      <h3>{{ n }}</h3>
      <h3>msg: {{ msg }}</h3>
      <h3>msg2: {{ $attrs.msg2 }}</h3>
      <button @click="update">子组件修改</button>
    </div>
  </template>
  <script lang="ts">
  import { defineComponent, ref } from "vue";
  export default defineComponent({
    props: ["msg"],
    emits: ["upfn"], // 可选的, 声明了更利于程序员阅读, 且可以对分发的事件数据进行校验
    setup(props, { attrs, emit, slots }) {
      console.log("setup中this值为:", this);
      console.log(props.msg, attrs.msg2, slots);
      console.log(emit);
      const n = ref(2);
      function update() {
        // 分发自定义事件
        n.value += 2;
        emit("upfn", "+");
      }
      return {
        n,
        update,
      };
    },
  });
  </script>
6. reactive 与 ref 细节
  • reactive 与 ref 是Vue3 composition API中2个最重要的响应式API;
  • ref 用来处理基本数据类型,reactive 用来处理对象(递归深度响应式);
  • 如果用 ref 对象/数组,内部会自动将对象/数组转换成 reactive 的代理对象;
  • ref 内部:通过给 value 属性添加 getter/setter 来实现对数据的劫持;
  • reactive 内部:通过使用 Proxy 来实现对对象内部所有数据的劫持,并通过 Reflect 操作对象内部数据;
  • ref 的数据操作:在js中要 .value,模板中不需要(内部解析模板时会自动添加 .value)
7. 计算属性和监听
  • 计算属性(computed)
    例:
  <template>
    <div class="about">
      <h2>double:{{ double }}</h2>
      <button @click="add">更新</button>
    </div>
  </template>
  <script lang="ts">
  import { computed, defineComponent, ref } from "vue";
  export default defineComponent({
    components: {},
    setup() {
      const count = ref(3);
      const double = computed(() => {
        return count.value * 2;
      });
      console.log(double, "============");
      function add() {
        count.value++;
      }
      return {
        count,
        double,
        add,
      };
    },
  });
  </script>
  • watch函数
    • 与watch配置功能一致;
    • 监视指定的一个或多个响应式数据,一旦数据变化,就自动执行监视回调;
    • 默认初始时不执行回调,但可以通过配置immediate: true,来指定初始时立即执行一次;
    • 通过配置deep: true,指定深度监视;
  <template>
    <div class="about">
      firstName: <input type="text" v-model="user.firstName" /> lastName:
      <input type="text" v-model="user.lastName" /> fullName:
      <input type="text" v-model="fullName" />
    </div>
  </template>
  <script lang="ts">
  import { computed, defineComponent, reactive, ref, watch } from "vue";
  export default defineComponent({
    components: {},
    setup() {
      const user = reactive({
        firstName: "A",
        lastName: "B",
      });
      const fullName = ref("");
      /* 
      使用watch的2个特性:
        深度监视
        初始化立即执行
      */
      watch(
        user,
        () => {
          fullName.value = user.firstName + "-" + user.lastName;
        },
        {
          immediate: true,
          deep: true,
        }
      );
      /* 
      watch一个数据
        默认在数据发生改变时执行回调
      */
      watch(fullName, (val) => {
        const names = val.split("-");
        user.firstName = names[0];
        user.lastName = names[1];
      });
      /* 
      watch多个数据: 
        使用数组来指定
        如果是ref对象, 直接指定
        如果是reactive对象中的属性,  必须通过函数来指定
      */
      watch([() => user.firstName, () => user.lastName, fullName], (val) => {
        console.log("监听多个数据:", val);
      });
      return {
        user,
        fullName,
      };
    },
  });
  </script>
  • watchEffect函数
    • 不用直接监听指定的数据,回调函数中使用的哪些响应式数据就监听哪些响应式数据;
    • 默认初始时就会执行第一次,从而可以收集需要监视的数据;
    • 监视数据发生变化时回调
      例:
  <template>
    <div class="about">
      firstName: <input type="text" v-model="user.firstName" /> lastName:
      <input type="text" v-model="user.lastName" /> fullName:
      <input type="text" v-model="fullName" />
    </div>
  </template>
  <script lang="ts">
  import { defineComponent, reactive, ref, watchEffect } from "vue";
  export default defineComponent({
    components: {},
    setup() {
      const user = reactive({
        firstName: "A",
        lastName: "B",
      });
      const fullName = ref("");
      /* 
      watchEffect: 监视所有回调中使用的数据
      */
      watchEffect(() => {
        console.log("watchEffect---");
        fullName.value = user.firstName + "-" + user.lastName;
      });
      return {
        user,
        fullName,
      };
    },
  });
  </script>
8. 生命周期
  • vue2生命周期
    • beforeCreate():实例在内存中被创建出来,还没有初始化好data和methods属性。
    • create():实例已经在内存中创建,已经初始化好data和method,此时还没有开始编译模板。
    • beforeMount():已经完成了模板的编译,还没有挂载到页面中。
    • mounted():将编译好的模板挂载到页面指定的容器中显示。
    • beforeUpdate():状态更新之前执行函数,此时data中的状态值是最新的,但是界面上显示的数据还是旧的,因为还没有开始重新渲染DOM节点。
    • updated():此时data中的状态值和界面上显示的数据都已经完成了跟新,界面已经被重新渲染好了!
    • beforeDestroy():实例被销毁之前。
    • destroyed():实例销毁后调用,Vue实例指示的所有东西都会解绑,所有的事件监听器都会被移除,所有的子实例也都会被销毁。组件已经被完全销毁,此时组建中所有data、methods、以及过滤器,指令等,都已经不可用了。
      vue2生命周期钩子函数
  • vue3生命周期
    • setup():开始创建组件之前,在 beforeCreate 和 created 之前执行,创建的是 data 和 method;
    • onBeforeMount():组件挂载到节点上之前执行;
    • onMounted():组件挂载完成之后执行;
    • onBeforeUpdate():组件更新之前执行;
    • onUpdate():组件更新完成之后执行;
    • onBeforeUnmount():组件卸载之前执行;
    • onUnmounted():组件卸载完成之后执行;
    • onActivated():在被包裹组建被激活的状态下执行;
    • onDeactivated():被包裹组件停止使用时调用执行(组件被移除时(手动));
    • onErrorCaptured():捕获一个来自子孙组件的错误时被调用(被动);
  • vue2与vue3对比
		Vue2----------------vue3
		beforeCreate   ->  setup()
		created        ->  setup()
		beforeMount    ->  onBeforeMount
		mounted        ->  onMounted
		beforeUpdate   ->  onBeforeUpdate
		updated        ->  onUpdated
		beforeDestroy  ->  onBeforeUnmount
		destroyed      ->  onUnmounted
		activated      ->  onActivated
		deactivated    ->  onDeactivated
		errorCaptured  ->  onErrorCaptured

例:
child2.vue文件

<template>
  <div>
    <h3>msg: {{ msg }}</h3>
    <button @click="update">更新</button>
  </div>
</template>
<script lang="ts">
import {
  defineComponent,
  onBeforeMount,
  onBeforeUnmount,
  onBeforeUpdate,
  onMounted,
  onUnmounted,
  onUpdated,
  ref,
} from "vue";
export default defineComponent({
  beforeCreate() {
    console.log("beforeCreate()");
  },
  created() {
    console.log("created()");
  },
  beforeMount() {
    console.log("beforeMount()");
  },
  mounted() {
    console.log("mounted()");
  },
  beforeUpdate() {
    console.log("beforeUpdate()");
  },
  updated() {
    console.log("updated()");
  },
  beforeUnmount() {
    console.log("beforeUnmount()");
  },
  unmounted() {
    console.log("unmounted()");
  },
  setup(props, { attrs, emit, slots }) {
    const msg = ref("abc");
    const update = () => {
      msg.value += "-";
    };
    onBeforeMount(() => {
      console.log("--onBeforeMount");
    });
    onMounted(() => {
      console.log("--onMounted");
    });
    onBeforeUpdate(() => {
      console.log("--onBeforeUpdate");
    });
    onUpdated(() => {
      console.log("--onUpdated");
    });
    onBeforeUnmount(() => {
      console.log("--onBeforeUnmount");
    });
    onUnmounted(() => {
      console.log("--onUnmounted");
    });
    return {
      msg,
      update,
    };
  },
});
</script>

about.vue文件

<template>
  <div class="about">
    <button @click="isShow = !isShow">切换</button>
    <child2 v-if="isShow" />
  </div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import child2 from "./child2.vue";
export default defineComponent({
  components: {
    child2,
  },
  data() {
    return {
      isShow: true,
    };
  },
});
</script>
9. 自定义hook函数
  • 使用 vue3 的组合 API 封装的可复用的功能函数;
  • 自定义 hook 的作用类似于 vue2 中的 mixin 技术;
  • 自定义 hook 的优势:很清楚复用功能代码的来源,更清晰易懂;
    例:收集用户鼠标点击的页面坐标
    hooks/useMousePosition.ts
import { onMounted, onUnmounted, ref } from "vue";
/* 
收集用户鼠标点击的页面坐标
*/
export default function useMousePosition() {
const x = ref(1);
const y = ref(1);
// 用于收集点击事件坐标的函数
const updatePosition = (e: MouseEvent) => {
  x.value = e.pageX;
  y.value = e.pageY;
};
// 挂载后绑定监听
onMounted(() => {
  document.addEventListener("click", updatePosition);
});
// 卸载前解除绑定
onUnmounted(() => {
  document.removeEventListener("click", updatePosition);
});
return {
  x,
  y,
};
}

about.vue

<template>
<div class="about">
  <h2>x:{{ x }} y:{{ y }}</h2>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
/* 
在组件中引入并使用自定义hook
自定义hook的作用类似于vue2中的mixin技术
自定义Hook的优势: 很清楚复用功能代码的来源, 更清楚易懂
*/
import useMousePosition from "@/lib/hooks/useMousePosition";
export default defineComponent({
components: {},
setup() {
  const { x, y } = useMousePosition();
  return {
    x,
    y,
  };
},
});
</script>
10. toRefs

把一个响应式对象转换成普通对象,该普通对象的每个 property 都是一个 ref
例:

<template>
  <div class="about">
    <h3>foo: {{ state.foo }}</h3>
    <h3>bar: {{ state.bar }}</h3>
    <h3>{{ stateAsRefs.foo }}</h3>
    <h3>{{ stateAsRefs.foo.value }}</h3>
    <button @click="fn">点击</button>
  </div>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs } from "vue";
/* 
  toRefs 可以将一个响应式 reactive 对象的所有原始属性转换为响应式的 ref 属性
*/
export default defineComponent({
  components: {},
  setup() {
    const state = reactive({
      foo: "a",
      bar: "b",
    });
    const stateAsRefs = toRefs(state);
    console.log(state, "------");
    console.log("stateAsRefs:", stateAsRefs);
    setTimeout(() => {
      state.foo += "+";
      state.bar += "-";
    }, 2000);
    const fn = () => {
      stateAsRefs.foo.value += "*"; // stateAsRefs的属性都是ref格式,修改时需要添加 .value
      stateAsRefs.bar.value += "*";
    };
    return {
      state,
      stateAsRefs,
      fn,
    };
  },
});
</script>
11. ref获取元素

利用 ref函数获取组件中的标签元素
例:让输入框自动获取焦点

<template>
  <div class="about">
    <input type="text" />=========
    <input type="text" ref="inputRef" />
  </div>
</template>
<script lang="ts">
import { defineComponent, onMounted, ref } from "vue";
export default defineComponent({
  components: {},
  setup() {
    const inputRef = ref<HTMLElement | null>(null);
    console.log(inputRef);
    onMounted(() => {
      inputRef.value && inputRef.value.focus();
    });
    return {
      inputRef,
    };
  },
});
</script>

Composition API(其它部分)

1. shallowReactive 与 shallowRef
  • shallowReactive:只处理了对象内最外层属性的响应式(也就是浅响应式);
  • shallowRef:只处理了 value 的响应式,不进行对象的 reactive 处理;
    什么时候用浅响应式呢?
    • 一般情况下使用 ref 和 reactive 即可
    • 如果有一个对象数据,结构比较深,但改变时只是外层属性变化 ===> shallowReactive
    • 如果有一个对象数据,后面会产生新的对象来替换 ===> shallowRef
      例:
<template>
  <div class="about">
    <h3>m1: {{ m1 }}</h3>
    <h3>m2: {{ m2 }}</h3>
    <h3>m3: {{ m3 }}</h3>
    <h3>m4: {{ m4 }}</h3>
    <button @click="update">更新</button>
  </div>
</template>
<script lang="ts">
import {
  defineComponent,
  reactive,
  shallowReactive,
  ref,
  shallowRef,
} from "vue";
export default defineComponent({
  components: {},
  setup() {
    const m1 = reactive({ a: 1, b: { c: 2 } });
    const m2 = shallowReactive({ a: 1, b: { c: 2 } });
    const m3 = ref({ a: 1, b: { c: 2 } });
    const m4 = shallowRef({ a: 1, b: { c: 2 } });
    function update() {
      // m1.b.c += 1;
      // m2.b.c += 1; // m1不修改,只修改m2.b.c 页面不会改变
      // m3.value.a++;
      m4.value.a++; // 只修改m4 页面不会改变
      console.log(m4.value.a);
    }
    return {
      m1,
      m2,
      m3,
      m4,
      update,
    };
  },
});
</script>
2. readonly 与 shallowReadonly
  • readonly:深度只读数据
    获取一个对象(响应式或纯对象)或 ref 并返回原始代理的只读代理
    只读代理是深层的,访问的任何嵌套 property 也是只读的
  • shallowReadonly:浅只读数据
    创建一个代理,使其自身的 property 为只读,但不执行嵌套对象的深度只读转换
    应用场景:
    在某些特定情况下,可能不希望对数据进行更新的操作,那就可以包装成一个只读代理对象来读取数据,而不能修改或删除。
    例:
  <template>
    <div class="about">
      <h3>m1: {{ m1 }}</h3>
      <button @click="update">更新</button>
    </div>
  </template>
  <script lang="ts">
  import { defineComponent, reactive, readonly, shallowReadonly } from "vue";
  export default defineComponent({
    components: {},
    setup() {
      const m1 = reactive({ a: 1, b: { c: 2 } });
      const rState1 = readonly(m1);
      const rState2 = shallowReadonly(m1);
      function update() {
        // rState1.a++; // error  Cannot assign to 'a' because it is a read-only property.
        // rState2.a++; // error  Cannot assign to 'a' because it is a read-only property.
        rState2.b.c++; // 正确执行
      }
      return {
        m1,
        update,
      };
    },
  });
  </script>
3. toRaw 与 MarkRaw
  • toRaw
    返回有 reactive 或 readonly 方法转换成响应式代理的普通对象;
    这是一个还原方法,可用于临时读取,访问不会被代理/跟踪,写入时也不会触发界面更新;
  • markRaw
    标记一个对象,使其永远不会转换为代理,返回对象本身
    应用场景:
    有些值不应被设置为响应式的,例如复杂的第三方类实例或 vue 组件对象;
    当渲染具有不可变数据源的大列表时,跳过代理转换可以提高性能;
  <template>
    <div class="about">
      <h3>state: {{ state }}</h3>
      <button @click="testToRaw">测试toRaw</button>
      <button @click="testMarkRaw">测试MarkRaw</button>
    </div>
  </template>
  <script lang="ts">
  import { defineComponent, reactive, toRaw, markRaw } from "vue";
  export default defineComponent({
    components: {},
    setup() {
      const state = reactive<any>({
        name: "tom",
        age: 16,
      });
      function testToRaw() {
        const user = toRaw(state);
        user.age++; // 界面不会更新
      }
      function testMarkRaw() {
        const likes = ["a", "b"];
        state.likes = markRaw(likes);
        console.log(state, "----");
        setTimeout(() => {
          state.likes[0] += "-"; // 界面不会更新
        }, 1000);
      }
      return {
        state,
        testToRaw,
        testMarkRaw,
      };
    },
  });
  </script>
4. toRef

toRef:为源响应式对象上的某个属性创建一个 ref对象,二者内部操作的是同一个数据值,更新时二者同步修改;
ref:拷贝了一份新的数据值单独操作,更新时互相不影响;
应用:当要将某个 prop 的 ref 传递给复合函数是,toRef 很有用;
例:

<template>
  <div class="about">
    <h3>state: {{ state }}</h3>
    <h3>foo: {{ foo }}</h3>
    <h3>foo2: {{ foo2 }}</h3>
    <button @click="update">更新</button>
  </div>
</template>
<script lang="ts">
import { defineComponent, reactive, ref, toRef } from "vue";
/*
  toRef:为源响应式对象上的某个属性创建一个 ref对象, 二者内部操作的是同一个数据值, 更新时二者是同步的
  ref: 拷贝了一份新的数据值单独操作, 更新时相互不影响
  应用: 当要将某个 prop 的 ref 传递给复合函数时,toRef 很有用
*/
export default defineComponent({
  components: {},
  setup() {
    const state = reactive({
      foo: 1,
      bar: 2,
    });
    const foo = toRef(state, "foo");
    const foo2 = ref(state.foo);
    console.log(foo);
    console.log(foo2);
    function update() {
      state.foo++; //foo也会改变
      // foo.value++; // state数据也会改变
      // foo2.value++; // foo和state中的数据不会更新
    }
    return {
      state,
      foo,
      foo2,
      update,
    };
  },
});
</script>
5. customRef

customRef 用于自定义返回一个 ref对象,并对其依赖项跟踪和更新触发进行显示控制,接受工厂函数;
接受两个参数:用于追踪的 track 与用于触发响应的 trigger,并返回一个带有 get 和 set 属性的对象
例:

<template>
  <div class="about">
    <input type="text" v-model="keyword" />
    <h3>keyword: {{ keyword }}</h3>
  </div>
</template>
<script lang="ts">
import { customRef, defineComponent } from "vue";
export default defineComponent({
  components: {},
  setup() {
    const keyword = useDebouncedRef("", 300);
    return {
      keyword,
    };
  },
});
/* 
实现函数防抖的自定义ref
*/
function useDebouncedRef<T>(value: T, delay = 200) {
  let timeout: number;
  return customRef((track, trigger) => {
    return {
      get() {
        track(); // 告诉Vue追踪数据
        return value;
      },
      set(newValue: T) {
        clearTimeout(timeout);
        setTimeout(() => {
          value = newValue;
          // 告诉Vue去触发界面更新
          trigger();
        }, delay);
      },
    };
  });
}
</script>
6. provide 与 inject

provide 和 inject 提供依赖注入,功能类似 vue2 的provide/inject
实现跨层级组件(祖孙)间通信
例:
父组件

<template>
  <div class="about">
    <h2>父组件</h2>
    <p>当前颜色:{{ color }}</p>
    <button @click="color = 'red'"></button>
    <button @click="color = 'yellow'"></button>
    <button @click="color = 'blue'"></button>
    <child2 />
  </div>
</template>
<script lang="ts">
import { defineComponent, provide, ref } from "vue";
import child2 from "./child2.vue";
export default defineComponent({
  components: {
    child2,
  },
  setup() {
    const color = ref("red");
    provide("color", color); //父组件提供
    return {
      color,
    };
  },
});
</script>

子组件:

<template>
  <div>
    <h2>子组件</h2>
    <GrandSon />
  </div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import GrandSon from "./GrandSon.vue";
export default defineComponent({
  components: {
    GrandSon,
  },
});
</script>

孙子组件:

<template>
  <div>
    <h2>孙组件</h2>
    <p>接收到父组件的颜色:{{ color }}</p>
  </div>
</template>
<script lang="ts">
import { defineComponent, inject } from "vue";
export default defineComponent({
  setup() {
    const color = inject("color"); //子组件注入数据
    return {
      color,
    };
  },
});
</script>
7. 响应式数据的判断
  • isRef:检查一个值是否为一个ref对象;
  • isReactive:检查一个对象是否是由 reactive 创建的响应式代理;
  • isReadonly:检查一个对象是否是由 readonly 创建的只读代理;
  • isProxy:检查一个对象是否是由 reactive 或 readonly 方法创建的代理;

其它新组合和API

新组建

1)Fragment(片段)

在vue2中:组件必须有一个根标签;
在vue3中:组件可以没有根标签,内部会将多个标签包含在一个 Fragment 虚拟元素中;
好处:减少标签层级,减少内存占用;
例:

<template>
  <div>aaa</div>
  <div>bbb</div>
</template>
2)Teleport(瞬移)

Teleport提供了一种干净的方法,让组件的html在父组件界面外的特定标签(很可能是body)下插入显示;
例:
父组件:

<template>
  <h2>父组件</h2>
  <child2 />
</template>
<script lang="ts">
import { defineComponent } from "vue";
import child2 from "./child2.vue";
export default defineComponent({
  components: {
    child2,
  },
});
</script>

子组件:

<template>
  <button @click="modalOpen = true">点击(teleport)</button>
  <div>父级是引用的父组件</div>
  <teleport to="body">
    <div v-if="modalOpen" class="modal">
      没有插入到父组件中,直接插入到了body标签下面
      <button @click="modalOpen = false">Close</button>
    </div>
  </teleport>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
export default defineComponent({
  setup() {
    const modalOpen = ref(false);
    return {
      modalOpen,
    };
  },
});
</script>
3)Suspense(不确定的)

它们允许我们的应用程序在等待异步组件时渲染一些后备内容,可以让我们创建一个平滑的用户体验
例:

<template>
  <Suspense>
    <template v-slot:default>
      <child2 />
    </template>
    <template v-slot:fallback>
      <h1>loading....</h1>
    </template>
    <!-- <template #default>
      <child2 />
    </template>
    <template #fallback>
      <h1>Loadding...</h1>
    </template> -->
  </Suspense>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import child2 from "./child2.vue";
export default defineComponent({
  components: {
    child2,
  },
});
</script>

child.vue

<template>
  <h1>{{ result }}</h1>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
  setup() {
    return new Promise((resolve) => {
      setTimeout(() => {
        return resolve({ result: "ok!" });
      }, 2000);
    });
  },
});
</script>

注:export default defineComponent({}) 与 export default {}的区别:

<script >
export default({
  // js写法
})
</script>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
  // ts写法
})
</script>

defineComponent让编辑器有更好的提示和类型推导

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值