Vue3学习笔记

58 阅读1分钟

Vue3

01-初始 Setup

<template>
  <div class="hello">
    <h3>Setup</h3>
    <span>姓名: {{ name }}</span>
    <span>年龄: {{ age }}</span>
    <div>
      <button @click="sayHello">说Hello</button>
      <button @click="Vue3ReadVue2">Vue3中读取Vue2的内容</button>
    </div>

    <hr />
    <h3>data</h3>
    <span>姓名: {{ nameData }}</span>
    <span>年龄: {{ ageData }}</span>
    <div>
      <button @click="sayMethodHello">说Hello</button>
      <button @click="Vue2ReadVue3">Vue2中读取Vue3的内容</button>
    </div>
    <!-- vue2 vue3 都有的时候,以 vue3 为准 -->
    <p>{{ a }}</p>
  </div>
</template>

<script>
import { h } from "vue";
export default {
  name: "Setup",
  // vue3 中不建议这么书写, 建议使用 setup
  data() {
    return {
      nameData: "李四",
      ageData: 15,
      a: 300,
    };
  },
  methods: {
    sayMethodHello() {
      alert(`我叫${this.nameData},年龄${this.ageData}, 你好啊`);
    },
    // vue2 可以读取到 vue3 setup里面的数据和方法
    Vue2ReadVue3() {
      alert(`我叫${this.name},年龄${this.age}, 你好啊`);
      console.log(this.sayHello);
    },
  },

  // 此处只是测试 setup 暂时不考虑响应式
  setup() {
    let name = "张三";
    let age = 13;
    let a = 200;

    // 定义方法
    function sayHello() {
      alert(`我叫${name},年龄${age}, 你好啊`);
    }
    // vue3 不可以读取到 vue2 data methods 里面的数据和方法
    function Vue3ReadVue2() {
      alert(`我叫${nameData},年龄${ageData}, 你好啊`);
    }

    // 第一种,返回一个 对象,然后 template 就可以直接使用
    return {
      name,
      age,
      sayHello,
      Vue3ReadVue2,
    };

    // 第二种, 返回一个渲染函数,会把 template 全部替换掉
    // return () => h('h3', '说Hello')
  },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
  margin: 40px 0 0;
}

ul {
  list-style-type: none;
  padding: 0;
}

li {
  display: inline-block;
  margin: 0 10px;
}

a {
  color: #42b983;
}
</style>

main.js

// 引入的不再是Vue, 而是 createApp, 引入一个名字为 createApp 的工厂函数
import { createApp } from "vue";
import App from "./App.vue";

// 创建应用实例对象-app (类似之前vue2的vm,但是比vm更轻)
const app = createApp(App);
app.mount("#app");
// console.log(app);

// 移除 app 实例
// setTimeout(() => {
//   app.unmount("#app");
// }, 1000);

// 这个是Vue2的写法,而且 vue3 的包不兼容 vue2 的引入方法了
// const vm = new Vue({
//   render: (h) => h(App),
// });
// vm.$mount("#app");

02-ref 函数

<template>
  <div class="hello">
    <h3>个人信息</h3>
    <h4>姓名: {{ name }}</h4>
    <h4>年龄: {{ age }}</h4>
    <h4>工作类型: {{ job.type }}</h4>
    <h4>工作薪水: {{ job.money }}</h4>
    <button @click="changeInfo">修改个人信息</button>
  </div>
</template>

<script>
import { ref } from "vue";
export default {
  name: "App",
  setup() {
    let name = ref("张三");
    let age = ref(13);
    let job = ref({
      type: "web",
      money: "30K",
    });
    // 方法
    function changeInfo() {
      // 基本数据类型
      name.value = "李四";
      age.value = 14;
      console.log(name);
      // name 引用对象
      // RefImpl : {
      //     "__v_isShallow": false,
      //     "dep": {
      //         "w": 0,
      //         "n": 0
      //     },
      //     "__v_isRef": true,
      //     "_rawValue": "张三",
      //     "_value": "张三",
      //      "value": "张三",
      // }

      // 引用数据类型
      job.value.type = "UI";
      job.value.money = "25K";
      console.log(job);
      // job 引用对象
      // RefImpl: {
      //     "__v_isShallow": false,
      //     "dep": {
      //         "w": 0,
      //         "n": 0
      //     },
      //     "__v_isRef": true,
      //     "_rawValue": {
      //         "type": "UI",
      //         "money": "25K"
      //     },
      //     "_value": {
      //         "type": "UI",
      //         "money": "25K"
      //     }
      //      "value" : Proxy(Object)   ---> Proxy
      // }
    }
    return {
      name,
      age,
      job,
      changeInfo,
    };
  },
};
</script>
<style>
#app {
  text-align: center;
}
</style>

03-reactive 函数

<template>
  <div class="hello">
    <h3>个人信息</h3>
    <h4>姓名: {{ person.name }}</h4>
    <h4>年龄: {{ person.age }}</h4>
    <h4>工作类型: {{ person.job.type }}</h4>
    <h4>工作薪水: {{ person.job.money }}</h4>
    <h4>深层次对象C: {{ person.job.a.b.c }}</h4>
    <h4>数组爱好: {{ person.hobby }}</h4>
    <button @click="changeInfo">修改个人信息</button>
  </div>
</template>

<script>
import { reactive } from "vue";
export default {
  name: "App",
  setup() {
    let person = reactive({
      name: "张三",
      age: 13,
      job: {
        type: "web",
        money: "30K",
        a: { b: { c: 10 } },
      },
      // 按照下标修改数据也支持了
      hobby: ["吃饭", "睡觉", "打游戏"],
    });
    // 方法
    function changeInfo() {
      // 基本数据类型
      person.name = "李四";
      person.age = 14;

      person.job.type = "UI";
      person.job.money = "60K";
      person.job.a.b.c = 11;
      person.hobby[1] = "打架";
    }
    return { person, changeInfo };
  },
};
</script>
<style>
#app {
  text-align: center;
}
</style>

04-Vue3 响应式原理

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      // 源数据
      let person = {
        name: "Tom",
        age: 12,
      };

      //   Vue2 响应式原理
      //   Object.defineProperty(p, "name", {
      //     // 有人读取 name 属性
      //     get() {
      //       return person.name;
      //     },
      //     // 有人修改 name 属性
      //     set(value) {
      //         p.name = value;
      //     },
      //   });
      //   Object.defineProperty(p, "age", {
      //     // 有人读取 name 属性
      //     get() {
      //       return person.age;
      //     },
      //     // 有人修改 name 属性
      //     set(value) {
      //         p.age = value;
      //     },
      //   });

      //   Vue3 响应式原理
      const p = new Proxy(person, {
        // 读取时候调用
        get(target, propName) {
          console.log(`有人 读取 了P身上的属性: ${propName}`);
          return Reflect.get(target, propName);
        },
        // 修改或者追加时候调用
        set(target, propName, value) {
          console.log(`有人 修改 了P身上的属性: ${propName}`);
          Reflect.set(target, propName, value);
        },
        // 删除时候调用
        deleteProperty(target, propName, value) {
          console.log(`有人 删除 了P身上的属性: ${propName}`);
          return Reflect.defineProperty(target, propName);
        },
      });

      //   学习 Reflect 新东西
      //   let obj = { a: 1, b: 2 };
      //   Object 不能重复定义;
      //   报错 Uncaught TypeError: Cannot redefine property: c
      //   Object.defineProperty(obj, "c", {
      //     get() {
      //       return 2;
      //     },
      //   });
      //   Object.defineProperty(obj, "c", {
      //     get() {
      //       return 3;
      //     },
      //   });
      //   Reflect 可以重复定义
      //   const x1 = Reflect.defineProperty(obj, "c", {
      //     get() {
      //       return 2;
      //     },
      //   });
      //   const x2 = Reflect.defineProperty(obj, "c", {
      //     get() {
      //       return 3;
      //     },
      //   });
      //   console.log(x1); // true
      //   console.log(x2); // false
    </script>
  </body>
</html>

05-props_slots_emit

05-props_slots_emit\App.vue

<template>
  <div class="hello">
    <Demo @showHello="showHello" six="男" message="吃饭吧">
      <template v-slot:lgq>
        <div>我是父组件插槽</div>
      </template>
    </Demo>
  </div>
</template>

<script>
import Demo from "./Demo.vue";
export default {
  name: "App",
  components: { Demo },
  setup() {
    function showHello(val) {
      alert(val);
    }
    return {
      showHello,
    };
  },
};
</script>
<style>
#app {
  text-align: center;
}
</style>

05-props_slots_emit\Demo.vue

<template>
  <div class="hello">
    <h3>个人信息</h3>
    <h4>姓名: {{ person.name }}</h4>
    <h4>年龄: {{ person.age }}</h4>
    <button @click="showHello">触发Demo的Hello事件</button>
    <slot name="lgq"></slot>
  </div>
</template>

<script>
import { reactive } from "vue";
export default {
  name: "Demo",
  props: ["six", "message"],
  emits: ["showHello"], // vue3 的 emit 事件需要在子组件的这里定义一下
  // setup 比 beforeCreate 先执行
  setup(props, context) {
    console.log(props); // 可以直接接收到 props  --->  Proxy(Object) {six: '男', message: '吃饭吧'}
    console.log(context.attrs); // 相当于 vue2 的 $attrs

    // 父组件 这个写法就可以拿到 slots 了   v-slot:lgq
    console.log(context.slots);

    console.log("------first------");
    let person = reactive({
      name: "张三",
      age: 13,
    });
    function showHello() {
      context.emit("showHello", "showHello事件被Demo触发了");
    }
    return { person, showHello };
  },
  beforeCreate() {
    console.log("------second------");
  },
};
</script>
<style>
#app {
  text-align: center;
}
</style>

06-computed

<template>
  <div class="hello">
    <h3>个人信息</h3>
    姓氏: <input type="text" v-model="person.firstName" /><br />
    名字: <input type="text" v-model="person.lastName" /><br />
    全名: <input type="text" v-model="person.fullname" />
    <h4>姓氏: {{ person.firstName }}</h4>
    <h4>名字: {{ person.lastName }}</h4>
    <h4>全名: {{ person.fullname }}</h4>
  </div>
</template>

<script>
import { reactive, computed } from "vue";
export default {
  name: "Demo",
  //   不要写Vue2的计算属性
  //   computed: {
  //     fullname() {
  //       return this.person.firstName + "-" + this.person.lastName;
  //     },
  //   },

  setup() {
    let person = reactive({
      firstName: "张",
      lastName: "三",
    });
    // 计算属性(简写,不考虑计算属性被修改的形式)
    // person.fullname = computed(() => {
    //   return person.firstName + "-" + person.lastName;
    // });

    // 计算属性(完整写法,不考虑计算属性被修改的形式)
    person.fullname = computed({
      get() {
        return person.firstName + "-" + person.lastName;
      },
      set(value) {
        const nameArr = value.split("-");
        person.firstName = nameArr[0];
        person.lastName = nameArr[1];
      },
    });
    return { person };
  },
};
</script>
<style>
#app {
  text-align: center;
}
</style>

07-watch 函数

<template>
  <div class="hello">
    <h2>当前求和为: {{ sum }}</h2>
    <button @click="sum += 1">点我加 1</button>
    <hr />
    <input type="text" v-model="msg" />
    <hr />
    <span>{{ person.name }}</span>
    <span>-----</span>
    <span>{{ person.age }}</span>
    <span>-----</span>
    <span>{{ person.job.j1.money }}</span> <span>K</span>
    <div>
      <button @click="person.name += 1">改变名字</button>
      <button @click="person.age += 1">改变年龄</button>
      <button @click="person.job.j1.money += 1">改变薪资</button>
    </div>
    <hr />
  </div>
</template>

<script>
import { ref, watch, reactive } from "vue";
export default {
  name: "Demo",

  //   Vue2监听
  //   watch: {
  //     // 简写,
  //     // sum(newValue, oldValue) {
  //     //   console.log('sum变化了',newValue, oldValue);
  //     // },
  //     // 全写
  //     // sum: {
  //     //   deep: true,
  //     //   immediate: true,
  //     //   handler(newValue, oldValue) {
  //     //     console.log('sum变化了',newValue, oldValue);
  //     //   },
  //     // },
  //   },

  setup() {
    let sum = ref(0);
    let msg = ref("hello");
    let person = reactive({
      name: "TOM",
      age: 12,
      job: {
        j1: {
          money: 13,
        },
      },
    });
    // Vue3监听;
    // 情况一: 监听 ref 定义的一个数据
    // watch(
    //   sum,
    //   (newValue, oldValue) => {
    //     console.log("sum变化了", newValue, oldValue); // sum变化了 1 0
    //   },
    //   {
    //     immediate: true,
    //   }
    // );

    // 情况二: 监听 ref 定义的多个数据
    // watch(
    //   [sum, msg],
    //   (newValue, oldValue) => {
    //     console.log("sum或者msg变化了", newValue, oldValue); // sum变化了 (2) [1, 'hello'] (2) [0, 'hello']
    //   },
    //   {
    //     immediate: true,
    //   }
    // );

    // 情况三: 监听 reactive 定义的响应式数据
    // 注意1: 无法正确拿到 newValue, oldValue
    // 注意2: 强制开启了深度检测,deep: false,也没用
    // watch(
    //   person,
    //   (newValue, oldValue) => {
    //     console.log("person 变化了", newValue, oldValue); // sum变化了 (2) [1, 'hello'] (2) [0, 'hello']
    //   },
    //   {
    //     immediate: true,
    //     deep: false, // 此处的deep无效
    //   }
    // );

    // 情况四: 监听 reactive 定义的响应式数据中的某一个属性
    // watch(
    //   () => person.name, // 检测某一个属性需要写成函数的形式
    //   (newValue, oldValue) => {
    //     console.log("name 变化了", newValue, oldValue); // sum变化了 (2) [1, 'hello'] (2) [0, 'hello']
    //   },
    //   {
    //     immediate: true,
    //   }
    // );

    // 情况五: 监听 reactive 定义的响应式数据中的某些属性
    // watch(
    //   [() => person.name, () => person.age], // 检测某一个属性需要写成函数的形式
    //   (newValue, oldValue) => {
    //     console.log("name 或者 age 变化了", newValue, oldValue); // sum变化了 (2) [1, 'hello'] (2) [0, 'hello']
    //   },
    //   {
    //     immediate: true,
    //   }
    // );

    // 特殊情况: 监听一个对象,却修改对象深层里的数据,那么这个时候需要开启 deep: true
    watch(
      () => person.job, // 检测某一个属性需要写成函数的形式
      (newValue, oldValue) => {
        console.log("job 变化了", newValue, oldValue); // sum变化了 (2) [1, 'hello'] (2) [0, 'hello']
      },
      {
        immediate: true,
        deep: true,
      }
    );
    return { sum, msg, person };
  },
};
</script>
<style>
#app {
  text-align: center;
}
</style>

09-watch 监听是否加 value

<template>
  <div class="hello">
    <h2>当前求和为: {{ sum }}</h2>
    <button @click="sum += 1">点我加 1</button>
    <hr />
    <input type="text" v-model="msg" />
    <hr />
    <span>{{ person.name }}</span>
    <span>-----</span>
    <span>{{ person.age }}</span>
    <span>-----</span>
    <span>{{ person.job.j1.money }}</span> <span>K</span>
    <div>
      <button @click="person.name += 1">改变名字</button>
      <button @click="person.age += 1">改变年龄</button>
      <button @click="person.job.j1.money += 1">改变薪资</button>
    </div>
    <hr />
  </div>
</template>

<script>
import { ref, watch, reactive } from "vue";
export default {
  name: "Demo",
  setup() {
    let sum = ref(0);
    let msg = ref("hello");
    let person = ref({
      name: "TOM",
      age: 12,
      job: {
        j1: {
          money: 13,
        },
      },
    });
    // 监听 ref 定义的一个数据,不能加value,加了value就是常亮了
    watch(
      sum,
      (newValue, oldValue) => {
        console.log("sum变化了", newValue, oldValue);
      },
      {
        immediate: false,
      }
    );
    // person 这个是一个对象,里面很多数据,也不能加 value
    // person.value 的意思就是监视的 reactive 创建的深度对象,这样就可以拿到 oldvalue
    watch(
      person,
      (newValue, oldValue) => {
        console.log("person变化了", newValue, oldValue);
      },
      {
        deep: true,
      }
    );
    return { sum, msg, person };
  },
};
</script>
<style>
#app {
  text-align: center;
}
</style>

10-watchEffect

<template>
  <div class="hello">
    <h2>当前求和为: {{ sum }}</h2>
    <button @click="sum += 1">点我加 1</button>
    <hr />
    <input type="text" v-model="msg" />
    <hr />
    <span>{{ person.name }}</span>
    <span>-----</span>
    <span>{{ person.age }}</span>
    <span>-----</span>
    <span>{{ person.job.j1.money }}</span> <span>K</span>
    <div>
      <button @click="person.name += 1">改变名字</button>
      <button @click="person.age += 1">改变年龄</button>
      <button @click="person.job.j1.money += 1">改变薪资</button>
    </div>
    <hr />
  </div>
</template>

<script>
import { ref, watch, reactive, watchEffect } from "vue";
export default {
  name: "Demo",
  setup() {
    let sum = ref(0);
    let msg = ref("hello");
    let person = reactive({
      name: "TOM",
      age: 12,
      job: {
        j1: {
          money: 13,
        },
      },
    });

    // watch 监听
    // watch(
    //   sum,
    //   (newValue, oldValue) => {
    //     console.log("sum变化了", newValue, oldValue);
    //   },
    //   {
    //     immediate: true,
    //   }
    // );

    // watchEffect监听,监视器中用到了哪个数据,就监视他
    watchEffect(() => {
      const x1 = sum.value;
      const x2 = person.job.j1.money;
      console.log("watchEffect监听上来就会执行一次,默认 immediate为true");
      console.log(sum);
    });
    return { sum, msg, person };
  },
};
</script>

11-生命周期

<template>
  <div class="hello">
    <h2>当前求和为: {{ sum }}</h2>
    <button @click="sum += 1">点我加 1</button>
    <hr />
  </div>
</template>

<script>
import {
  ref,
  onBeforeMount,
  onMounted,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount,
  onUnmounted,
} from "vue";
export default {
  name: "Demo",

  setup() {
    let sum = ref(0);

    // 通过组合式API的形式书写生命周期 (比配置项的形式书写生命周期快一些)
    // beforeCreate() created(),就直接书写在 setup 里面即可
    onBeforeMount(() => {
      console.log("----01-onBeforeMount----");
    });
    onMounted(() => {
      console.log("----02-onMounted----");
    });
    onBeforeUpdate(() => {
      console.log("----03-onBeforeUpdate----");
    });
    onUpdated(() => {
      console.log("----04-onUpdated----");
    });
    onBeforeUnmount(() => {
      console.log("----05-onBeforeUnmount----");
    });
    onUnmounted(() => {
      console.log("----06-onUnmounted----");
    });

    return { sum };
  },

  // 通过配置项的形式书写生命周期
  // beforeCreate(){
  //   console.log('----01-beforeCreate----');
  // },
  // created(){
  //   console.log('----02-created----');
  // },
  // beforeMount(){
  //   console.log('----03-beforeMount----');
  // },
  // mounted(){
  //   console.log('----04-mounted----');
  // },
  // beforeUpdate(){
  //   console.log('----05-beforeUpdate----');
  // },
  // updated(){
  //   console.log('----06-updated----');
  // },
  // beforeUnmount(){
  //   console.log('----07-beforeUnmount----');
  // },
  // unmounted(){
  //   console.log('----08-unmounted----');
  // }
};
</script>

12-自定义 hook 函数

12-自定义 hook 函数\App.vue

<template>
  <div class="hello">
    <button @click="isShowDemo = !isShowDemo">切换显示隐藏</button>
    <Demo v-if="isShowDemo" />
  </div>
</template>

<script>
import { ref } from "vue";
import Demo from "./Demo.vue";
export default {
  name: "App",
  components: { Demo },
  setup() {
    let isShowDemo = ref(true);
    return { isShowDemo };
  },
};
</script>
<style>
#app {
  text-align: center;
}
</style>

12-自定义 hook 函数\Demo.vue

<template>
  <div class="hello">
    <h2>当前求和为: {{ sum }}</h2>
    <button @click="sum += 1">点我加 1</button>
    <hr />
    <h2>当前点击的坐标为{{ point.x }},{{ point.y }}</h2>
  </div>
</template>

<script>
import { ref } from "vue";
import usePoint from "./hooks/usePoint";
export default {
  name: "Demo",

  setup() {
    let sum = ref(0);
    let point = usePoint();
    return { sum, point };
  },
};
</script>

12-自定义 hook 函数\hooks\usePoint.js

import { onBeforeUnmount, onMounted, reactive } from "vue";
// 实现鼠标打点
export default function savePoint() {
  //   数据;
  let point = reactive({
    x: 0,
    y: 0,
  });

  //   方法;
  function savePoint(event) {
    console.log(event);
    point.x = event.x;
    point.y = event.y;
  }

  //   周期钩子;
  onMounted(() => {
    window.addEventListener("click", savePoint);
  });
  onBeforeUnmount(() => {
    window.removeEventListener("click", savePoint);
  });

  return point;
}

13-toRef toRefs

<template>
  <div class="hello">
    <h4>{{ person }}</h4>
    <span>{{ name }}</span>
    <span>-----</span>
    <span>{{ age }}</span>
    <span>-----</span>
    <span>{{ job.j1.money }}</span> <span>K</span>
    <div>
      <button @click="name += 1">改变名字</button>
      <button @click="age += 1">改变年龄</button>
      <button @click="job.j1.money += 1">改变薪资</button>
    </div>
    <hr />
  </div>
</template>

<script>
import { reactive, toRef, toRefs } from "vue";
export default {
  name: "Demo",
  setup() {
    let person = reactive({
      name: "TOM",
      age: 12,
      job: {
        j1: {
          money: 13,
        },
      },
    });

    return {
      person,

      // toRefs 快捷把所有数据都 toRef
      ...toRefs(person),

      // name: toRef(person, "name"),
      // age: toRef(person, "age"),
      // money: toRef(person.job.j1, "money"),

      // 这样的写法就会导致修改的时候 person 里面的数据不会改,
      // 因为你是把这个值取出来,把他搞成了 ref
      // name: toRef(person.name),
      // age: toRef(person.age),
      // money: toRef(person.job.j1.money),
    };
  },
};
</script>
<style>
#app {
  text-align: center;
}
</style>

14-shallowReactive 和 shallowRef

<template>
  <div class="hello">
    <h4>当前x值为{{ x.y }}</h4>
    <button @click="x.y += 1">x+1</button>
    <hr />
    <h4>{{ person }}</h4>
    <span>{{ name }}</span>
    <span>-----</span>
    <span>{{ age }}</span>
    <span>-----</span>
    <span>{{ job.j1.money }}</span> <span>K</span>
    <div>
      <button @click="name += 1">改变名字</button>
      <button @click="age += 1">改变年龄</button>
      <button @click="job.j1.money += 1">改变薪资</button>
    </div>
    <hr />
  </div>
</template>

<script>
import { reactive, toRefs, shallowReactive, shallowRef, ref } from "vue";
export default {
  name: "Demo",
  setup() {
    // shallowReactive 只处理对象最外层属性的响应式(浅响应式).
    // let person = shallowReactive({
    let person = reactive({
      name: "TOM",
      age: 12,
      job: {
        j1: {
          money: 13,
        },
      },
    });

    // shallowRef 只处理基本数据类型的响应式, 不进行对象的响应式处理.
    let x = ref(0);
    // let x = shallowRef({
    //   y: 0,
    // });
    console.log(x);
    return {
      person,
      x,
      ...toRefs(person),
    };
  },
};
</script>
<style>
#app {
  text-align: center;
}
</style>

15-readonly 和 shallowReadonly

<template>
  <div class="hello">
    <h4>当前求和为{{ sum }}</h4>
    <button @click="sum++">点我++</button>
    <hr />
    <h4>{{ person }}</h4>
    <span>{{ name }}</span>
    <span>-----</span>
    <span>{{ age }}</span>
    <span>-----</span>
    <span>{{ job.j1.money }}</span> <span>K</span>
    <div>
      <button @click="name += 1">改变名字</button>
      <button @click="age += 1">改变年龄</button>
      <button @click="job.j1.money += 1">改变薪资</button>
    </div>
    <hr />
  </div>
</template>

<script>
import { reactive, toRefs, readonly, shallowReadonly, ref } from "vue";
export default {
  name: "Demo",
  setup() {
    let person = reactive({
      name: "TOM",
      age: 12,
      job: {
        j1: {
          money: 13,
        },
      },
    });
    // readonly 让一个响应式数据变为只读的(深只读).
    // person = readonly(person);

    // shallowReadonly 让一个响应式数据变为只读的(浅只读).
    person = shallowReadonly(person);

    let sum = ref(0);
    sum = readonly(sum);
    return {
      sum,
      ...toRefs(person),
    };
  },
};
</script>
<style>
#app {
  text-align: center;
}
</style>

16-toRaw 和 markRaw

<template>
  <div class="hello">
    <h4>当前求和为{{ sum }}</h4>
    <button @click="sum++">点我++</button>
    <hr />
    <span>{{ name }}</span>
    <span>-----</span>
    <span>{{ age }}</span>
    <span>-----</span>
    <span>{{ job.j1.money }}</span> <span>K</span>
    <span>-----</span>
    <span>{{ person.car }}</span>
    <br />
    <button @click="name += 1">改变名字</button>
    <button @click="age += 1">改变年龄</button>
    <button @click="job.j1.money += 1">改变薪资</button>
    <br />
    <button @click="addCar">给人添加一台车</button>
    <button @click="person.car.name += 1">换车名</button>
    <button @click="person.car.price += 1">换价格</button>
    <hr />
  </div>
</template>

<script>
import { reactive, toRefs, ref, toRaw, markRaw } from "vue";
export default {
  name: "Demo",
  setup() {
    let person = reactive({
      name: "TOM",
      age: 12,
      job: {
        j1: {
          money: 13,
        },
      },
    });
    let sum = ref(0);

    // toRaw 将一个由reactive生成的对象变为不具有响应式的原始对象
    // person = toRaw(person);

    function addCar() {
      let car = { name: "BMW", price: "50W" };
      // markRaw 标记一个对象,使其永远不会再成为响应式对象.
      // 数据是变化了,仅仅是因为不具有响应式,页面不会更新了
      person.car = markRaw(car);
    }
    return {
      sum,
      person,
      addCar,
      ...toRefs(person),
    };
  },
};
</script>
<style>
#app {
  text-align: center;
}
</style>

17-customRef

<template>
  <input type="text" v-model="keyword" />
  <h3>{{ keyword }}</h3>
</template>

<script>
import { ref, customRef } from "vue";
export default {
  name: "App",
  setup() {
    // 自定义ref myRef
    function myRef(value, delay) {
      let timer;
      return customRef((track, trigger) => {
        return {
          get: () => {
            console.log(`有人读取了 myRef 中数据 我把 ${value} 给他了`);
            track(); // 告诉vue这个值需要追踪 value 这个数据的变化
            return value;
          },
          set: (newValue) => {
            console.log(`有人修改了 myRef 中数据 改为了:  ${newValue} `);
            value = newValue;
            clearTimeout(timer);
            timer = setTimeout(() => {
              trigger(); // 通知vue去重新解析模板,这样视图就会重新读取数据,那么就会重新走 get,
            }, delay);
          },
        };
      });
    }
    // let keyword = ref("hello"); // 使用 vue 提供的内置 ref
    let keyword = myRef("hello", 500); // 使用 开发者 自己定义的 ref
    return {
      keyword,
    };
  },
};
</script>
<style>
#app {
  text-align: center;
}
</style>

18-provide 和 inject

18-provide 和 inject\App.vue

<template>
  <div class="App">
    <h2>我是 App----{{ name }}----{{ price }}</h2>
    <Child />
  </div>
</template>

<script>
import Child from "./components/Child.vue";
import { provide, reactive, toRefs } from "vue";
export default {
  name: "App",
  components: { Child },
  setup() {
    let car = reactive({
      name: "BMW",
      price: "10k",
    });
    provide("car", car);
    return {
      ...toRefs(car),
    };
  },
};
</script>
<style>
.App {
  padding: 10px;
  background-color: rgb(207, 90, 180);
}
</style>

18-provide 和 inject\components\Child.vue

<template>
  <div class="Child">
    <h2>我是 Child</h2>
    <Son />
  </div>
</template>

<script>
import Son from "./Son.vue";
export default {
  name: "Child",
  components: { Son },
};
</script>
<style>
.Child {
  padding: 10px;
  background-color: yellow;
}
</style>

18-provide 和 inject\components\Son.vue

<template>
  <div class="Son">
    <h2>我是 Son-----{{ car.name }}----{{ car.price }}</h2>
  </div>
</template>

<script>
import { inject } from "vue";
export default {
  name: "Son",
  setup() {
    let car = inject("car");
    return { car };
  },
};
</script>
<style>
.Son {
  background-color: rgb(91, 187, 147);
  padding: 10px;
}
</style>

19-响应式数据的判断

<template>
  <div class="App">
    <h2>我是 App----{{ name }}----{{ price }}</h2>
  </div>
</template>

<script>
import {
  reactive,
  readonly,
  ref,
  toRefs,
  isRef,
  isReactive,
  isReadonly,
  isProxy,
} from "vue";
export default {
  name: "App",
  setup() {
    let sum = ref(0);
    let car = reactive({
      name: "BMW",
      price: "10k",
    });
    let car2 = readonly(car);

    // - isRef: 检查一个值是否为一个 ref 对象
    console.log(isRef(sum)); // true
    // - isReactive: 检查一个对象是否是由 `reactive` 创建的响应式代理
    console.log(isReactive(car)); // true
    // - isReadonly: 检查一个对象是否是由 `readonly` 创建的只读代理
    console.log(isReadonly(car2)); // true
    // - isProxy: 检查一个对象是否是由 `reactive` 或者 `readonly` 方法创建的代理
    console.log(isProxy(car)); // true
    console.log(isProxy(sum)); // false
    return {
      ...toRefs(car),
    };
  },
};
</script>
<style>
.App {
  padding: 10px;
  background-color: rgb(207, 90, 180);
}
</style>

20-teleport 组件

20-teleport 组件\App.vue

<template>
  <div>
    <button @click="isShow = true">点我弹窗</button>
    <teleport to="body">
      <div class="mask" v-if="isShow">
        <div class="dialog">
          <h4>我是弹窗内容</h4>
          <h4>我是弹窗内容</h4>
          <button @click="isShow = false">关闭弹窗</button>
        </div>
      </div>
    </teleport>
  </div>
</template>

<script>
import { ref } from "vue";

export default {
  name: "Dialog",
  setup() {
    let isShow = ref(false);
    return { isShow };
  },
};
</script>
<style>
.mask {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  background-color: rgba(0, 0, 0, 0.5);
}
.dialog {
  width: 200px;
  height: 200px;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  background-color: #ffffff;
  border-radius: 4px;
  padding: 10px;
}
</style>

21-Suspense 组件

21-Suspense 组件\App.vue

<template>
  <div class="Child">
    <h2>我是 Child</h2>
    <h3>{{ sum }}</h3>
  </div>
</template>

<script>
import { ref } from "vue";

export default {
  name: "Child",
  async setup() {
    let sum = ref(0);
    let p = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve({ sum });
      }, 1000);
    });
    return await p;
  },
};
</script>
<style>
.Child {
  padding: 10px;
  background-color: yellow;
}
</style>

21-Suspense 组件\components\Child.vue

<template>
  <div class="Child">
    <h2>我是 Child</h2>
    <h3>{{ sum }}</h3>
  </div>
</template>

<script>
import { ref } from "vue";

export default {
  name: "Child",
  async setup() {
    let sum = ref(0);
    let p = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve({ sum });
      }, 1000);
    });
    return await p;
  },
};
</script>
<style>
.Child {
  padding: 10px;
  background-color: yellow;
}
</style>