vue3复习

1.初识setup

<template>
 <h1>一个人的信息</h1>
 <h1>姓名: {{ name }}</h1>
 <h1>年龄: {{ age }}</h1>
 <h1>性别: {{ sex }}</h1>
<button @click="sayHello">vue3-sayHello</button>
<button @click="sayWelcome">vue2-sayWelcome</button>
<button @click="test1">vue2读取vue3中的数据和方法</button>
<button @click="test2">vue3读取vue2中的数据和方法</button>

<button @click="test">一个简单的测试</button>
</template>

<script>
// import { h } from 'vue'
export default {
  name: 'App',
  data() {
    return {
      sex: '男'
    }
  },
  methods: {
    sayWelcome() {
      alert('欢迎哈哈哈~')
    },
    // 测试一下看能不能在vue2中调vue3中的数据和方法. 测试是可以调取的
    test1() {
      console.log(this.name)
      console.log(this.sayHello)
      this.sayHello()
    }
  },
  // 此处只是测试一下setup,暂时不考虑响应式的问题
  setup() {
    // 数据
    let name = '张三'
    let age = 18

    // 方法
    function sayHello() {
      alert(`我叫${name},我${age}岁了`)
    }

    // 在vue3中读取vue2中的数据和方法. 读不了,会报错
    function test2() {
      console.log(this.sex)
      console.log(this.sayWelcome)
      this.sayWelcome()
    }

    // 一个简单的测试
    const test = () => {
      console.log(111)
      alert('我是用es6写的啊!')
    }

    // 返回一个对象(常用)
    return {
      name: name,
      age: age,
      sayHello: sayHello,
      test2: test2,
      test
    }

    // 返回一个函数(渲染函数)
    // return () => h('h1','尚硅谷') // 页面中只显示“尚硅谷”三个字
  }
}
</script>

2.ref函数

<template>
 <h1>一个人的信息</h1>
 <h1>姓名: {{ name }}</h1>
 <h1>年龄: {{ age }}</h1>
 <h1>职业: {{ job.type }}</h1>
 <h1>薪水: {{ job.salary }}</h1>
<button @click="changeInfo">修改人的信息</button>
</template>

<script>
import { ref, onMounted } from 'vue'
export default {
  name: 'App',
  setup() {
    // 数据
    let name = ref('张三')
    let age = ref(18)
    let job = ref({
      type: '前端工程师',
      salary: '30K'
    })

    let test = ref(123)
    onMounted(() => {
      console.log(test)
    })

    // 方法
    function changeInfo() {
      name.value = '李四'
      age.value = 48
      job.value.type = '后端工程师'
      job.value.salary = '20K'
    }

    // 返回一个对象(常用)
    return {
      test,
      name: name,
      age: age,
      job: job,
      changeInfo: changeInfo
    }
  }
}
</script>

3.reactive

<template>
 <h1>一个人的信息</h1>
 <h1>姓名: {{ person.name }}</h1>
 <h1>年龄: {{ person.age }}</h1>
 <h1>职业: {{ person.job.type }}</h1>
 <h1>薪水: {{ person.job.salary }}</h1>
 <h1>测试的数据: {{ person.job.a.b.c }}</h1>
 <h1>爱好: {{ person.hobbies }}</h1>

<button @click="changeInfo">修改人的信息</button>
</template>

<script>
import { reactive } from 'vue' // ref处理基本类型数据  reactive处理引用类型数据
export default {
  name: 'App',
  setup() {
    // 数据
    let person = reactive({
      name: '张三',
      age: 18,
      job: {
        type: '前端工程师',
        salary: '30K',
        a: {
          b: {
            c: 666
          }
        }
      },
      hobbies: ['抽烟', '喝酒', '玩游戏']
    })
    
    // 方法
    function changeInfo() {
      person.name = '李四'
      person.age = 48
      person.job.type = '后端工程师'
      person.job.salary = '20K'
      person.job.a.b.c = 999
      person.hobbies[0] = '打飞机'
    }

    // 返回一个对象(常用)
    return {
      person: person,
      changeInfo: changeInfo
    }
  }
}
</script>

4.vue3的响应式原理

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script type="text/javascript">
        // 源数据
        let person = {
            name: '张三',
            age: 18
        }

        // vue2响应式原理
        // let p = {}
        // Object.defineProperty(p,'name',{
        //     configurable: true,
        //     // 读取name时调用
        //     get() {
        //         return person.name
        //     },
        //     // 修改name时调用
        //     set(value) {
        //         console.log('有人修改了name属性被我发现了,我要去更新界面')
        //         person.name = value
        //     }
        // })
        // Object.defineProperty(p,'age',{
        //     get() {
        //         return person.age
        //     },
        //     set(value) {
        //         console.log('有人修改了age属性被我发现了,我要去更新界面')
        //         person.age = value
        //     }
        // })

        // vue3响应式原理  Proxy是代理对象  Reflect是反射对象
        /** 
         * 通过Proxy(代理):拦截对象中任意属性的变化,包括:属性值的读写,属性的添加,属性的删除
         * 通过Reflect(反射):对源对象的属性进行操作
        */
        const p = new Proxy(person, {
            // 读取
            get(target, propName) {
                console.log(`有人读取了p身上的${propName}属性`, target, propName) // target就是person源对象,propName是属性名
                // return target[propName]
                return Reflect.get(target, propName)
            },
            // 新增/修改
            set(target, propName, value) {
                console.log(`有人修改了p身上的${propName}属性,我要去更新页面了`)
                // target[propName] = value
                Reflect.set(target, propName, value)
            },
            // 删除
            deleteProperty(target, propName) {
                console.log(`有人删除了p身上的${propName}属性,我要去更新页面了`)
                // return delete target[propName]
                return Reflect.deleteProperty(target, propName)
            }
        })

        let obj = { a: 1, b: 2 }
        // 通过Object.defineProperty去操作
        // try {
        //     Object.defineProperty(obj, 'c', {
        //         get() {
        //             return 3
        //         }
        //     })
        //     Object.defineProperty(obj, 'c', {
        //         get() {
        //             return 34
        //         }
        //     })
        // } catch (error) {
        //     console.log(err)
        // }

        // 通过Reflect.defineProperty去操作
        const x1 = Reflect.defineProperty(obj, 'c', {
            get() {
                return 3
            }
        })
        console.log(x1)

        const x2 = Reflect.defineProperty(obj, 'c', {
            get() {
                return 3
            }
        })
        if (x2) {
            console.log('某某某操作成功了!')
        } else {
            console.log('某某某操作失败了!')
        }
        // console.log('@@@')
    </script>
</body>

</html>

5.setup的两个注意点

----------App.vue父组件----------
<template>
  <Demo :msg="msg" school="尚硅谷" afanfan="小汤圆" @hello="showHelloMsg">
    <div>哈哈哈</div>

    <template v-slot:enen>
      <div>嗯嗯嗯</div>
    </template>

    <template v-slot:heihei>
      <div>嘿嘿嘿</div>
    </template>
  </Demo>
</template>

<script>
import { ref } from "vue"; // ref处理基本类型数据  reactive处理引用类型数据
import Demo from './components/demo.vue'
export default {
  name: 'App',
  components:{
    Demo
  },
  setup() {
    let msg = ref('hahah')
    function showHelloMsg(value) {
      alert(`你好啊,你触发了hello事件,我收到的参数是${value}`)
    }
    return {
      msg,
      showHelloMsg
    }
  }
}
</script>


----------demo.vue子组件-----------
<template>
  <h1>一个人的信息</h1>
  <h1>姓名: {{ person.name }}</h1>
  <h1>年龄: {{ person.age }}</h1>
  <h1>{{msg}}</h1>
  <h1>{{afanfan}}</h1>
  <button @click="test">测试触发一下Demo组件的Hello事件</button>

  <!-- 哈哈哈 -->
  <slot></slot>

  <!-- 这里如果想显示父组件的东西,就要用到插槽slot -->
  <!-- 嗯嗯嗯 -->
  <slot name="enen"></slot>

  <!-- 嘿嘿嘿 -->
  <slot name="heihei"></slot>
</template>

<script>
import { reactive } from "vue"; // ref处理基本类型数据  reactive处理引用类型数据
export default {
  name: "demo",
  props: ["msg", "school","afanfan"],
  // emits: ["hello"],
  beforeCreate() {
    console.log("---beforeCreate---"); 
  },
  setup(props, context) {
    console.log(context)
    console.log('---setup---') // 在beforeCreate之前执行
    console.log(props) // Proxy {msg: "hahah", school: "尚硅谷", afanfan: "小汤圆"}
    // console.log(context.attrs) // 相当于vue2中的$attrs
    // console.log(context.emit) // 触发自定义事件的
    console.log(context.slots); // 插槽

    // console.log(this) // undefind
    // 数据
    let person = reactive({
      name: "张三",
      age: 18,
    });

    // 方法
    function test() {
      context.emit("hello", 666);
    }
    // 返回一个对象(常用)
    return {
      person: person,
      test: test,
    };
  },
};
</script>

6.computed计算属性

-----------App.vue----------
<template>
  <Demo />
</template>

<script>
import Demo from './components/demo.vue' // ref处理基本类型数据  reactive处理引用类型数据
export default {
  name: 'App',
  components:{
    Demo
  }
}
</script>

----------demo.vue-----------
<template>
  <h1>一个人的信息</h1>
  姓: <input type="text" v-model="person.firstName" />
  <br />
  名: <input type="text" v-model="person.lastName" />
  <br />
  <span>全名:{{ person.fullName }}</span>
  <br />
  全名: <input type="text" v-model="person.fullName" />
</template>

<script>
import { reactive, computed } from "vue"; // ref处理基本类型数据  reactive处理引用类型数据
export default {
  name: "demo",

  // vue2的计算属性写法
  // computed: {
  //   fullName() {
  //     return this.person.firstName + "-" + this.person.lastName;
  //   },
  // },

  setup() {
    // 数据
    let person = reactive({
      firstName: "张",
      lastName: "三",
      fullName: "",
    });
    
    // vue3的计算属性(没有考虑计算属性被修改的情况)
    // person.fullName = computed(() => {
    //   return person.firstName + "-" + person.lastName;
    // });

    // 完整的写法(考虑读和写)
    person.fullName = computed({
      get() {
        return person.firstName + "-" + person.lastName;
      },
      set(value) {
        console.log(value); // 张df-三1123w
        const newArr = value.split("-");
        console.log(newArr); // ['张df','三1123w']
        person.firstName = newArr[0];
        person.lastName = newArr[1];
      },
    });

    // 返回一个对象(常用)
    return {
      person,
    };
  },
};
</script>


7.watch函数

-----------App.vue------------
<template>
  <Demo />
</template>

<script>
import Demo from './components/demo.vue' // ref处理基本类型数据  reactive处理引用类型数据
export default {
  name: 'App',
  components:{
    Demo
  }
}
</script>


----------demo.vue-------------
<template>
  <h2>当前求和为: {{ sum }}</h2>
  <button @click="sum++">点我加1</button>
  <hr />
  <h2>当前的信息为: {{ msg }}</h2>
  <button @click="msg += '!'">点我加1</button>
  <hr>
  <h2>姓名:{{ person.name }}</h2>
  <h2>年龄:{{ person.age }}</h2>
  <h2>薪资:{{ person.job.j1.salary }}K</h2>
  <button @click="person.name+='~'">修改姓名</button>
  <button @click="person.age++ ">修改年龄</button>
  <button @click="person.job.j1.salary++ ">涨薪</button>
</template>

<script>
import { ref, reactive, watch } from "vue"; // ref处理基本类型数据  reactive处理引用类型数据
export default {
  name: "demo",
  watch: {
    // vue2简单写法
    // sum(newVal, oldVal) {
    //   console.log("sum的值变了","newVal===", newVal,"oldVal===", oldVal);
    //   // 点击后打印 sum的值变了 newVal=== 1 oldVal=== 0
    // },

    // vue2完整写法
    // sum: {
    //   immediate: true,
    //   deep: true,
    //   handler(newVal,oldVal) {
    //     console.log("sum的值变了","newVal===", newVal,"oldVal===", oldVal);
    //     // sum的值变了 newVal=== 0 oldVal=== undefined
    //   }
    // }
  },
  setup() {
    // 数据
    let sum = ref(0);
    let msg = ref("hello");
    let person = reactive({
      name: '张三',
      age: 18,
      job: {
        j1: {
          salary: 20
        }
      }
    })

    // let person = ref({
    //   name: '张三',
    //   age: 18
    // })

    // vue3监视一:ref所定义的响应式数据
    // watch(sum, (newVal, oldVal) => {
    //   console.log("sum的值变了", "newVal===", newVal, "oldVal===", oldVal);
    // });

    // vue3监视二:ref所定义的多个响应式数据
    // vue3里面可以调用多次watch函数,比如我既想监视sum,又想监视msg,那么可以写两个watch.但是比较麻烦
    // watch(sum, (newVal, oldVal) => {
    //   console.log("sum的值变了", "newVal===", newVal, "oldVal===", oldVal);
    // },{immediate: true,deep:true});
    // watch(msg, (newVal, oldVal) => {
    //   console.log("msg的值变了", "newVal===", newVal, "oldVal===", oldVal);
    // },{immediate: true,deep:true});

    // 所以简单的写法是:
    // watch([sum,msg], (newVal, oldVal) => {
    //   console.log("sum或msg的值变了", "newVal===", newVal, "oldVal===", oldVal);
    // },{immediate: true});

    // vue3监视三:reactive所定义的响应式数据的全部属性(用的多)
    // 1.此处无法正确的获取oldVal(bug) 2.强制开启了深度监视(deep配置无效)
    watch(person,(newVal, oldVal)=>{
      console.log('person变化了',newVal,oldVal)
      console.log(newVal.name)
    },{deep:true}) // 此处的deep配置无效

    // vue3监视四:reactive所定义的响应式数据的某一个属性
    // watch(()=>person.age,(newVal, oldVal)=>{
    //   console.log('person的age变化了',newVal,oldVal)
    // })

    // vue3监视五:reactive所定义的响应式数据的某些属性
    // watch([()=>person.name,()=>person.age],(newVal, oldVal)=>{
    //   console.log('person的name或age变化了',newVal,oldVal)
    // })

    // vue3监视六:特殊情况
    // watch(()=>person.job,(newVal, oldVal)=>{
    //   console.log('person的job变化了',newVal,oldVal)
    // },{deep:true}) // 此处由于监视的是reactive所定义的对象中的某个属性,所有deep配置有效

    return {
      sum,
      msg,
      person
    };
  },
};
</script>


8.watch监视ref数据的说明

<template>
  <h2>当前求和为: {{ sum }}</h2>
  <button @click="sum++">点我加1</button>
  <hr />
  <h2>当前的信息为: {{ msg }}</h2>
  <button @click="msg += '!'">点我加1</button>
  <hr />
  <h2>姓名:{{ person.name }}</h2>
  <h2>年龄:{{ person.age }}</h2>
  <h2>薪资:{{ person.job.j1.salary }}K</h2>
  <button @click="person.name += '~'">修改姓名</button>
  <button @click="person.age++">修改年龄</button>
  <button @click="person.job.j1.salary++">涨薪</button>
</template>

<script>
import { ref, watch } from "vue"; // ref处理基本类型数据  reactive处理引用类型数据
export default {
  name: "demo",
  setup() {
    // 数据
    let sum = ref(0);
    let msg = ref("hello");
    let person = ref({
      name: "张三",
      age: 18,
      job: {
        j1: {
          salary: 20,
        },
      },
    });

    console.log(sum);
    console.log(msg);
    console.log(person);

    // 注意:这里不能写成watch(sum.value,(a,b)=>{}),因为sum是基本类型数据,所以不能.value
    watch(sum, (newVal, oldVal) => {
      console.log("sum的值变了", "newVal===", newVal, "oldVal===", oldVal);
    });

    // 方法一
    // watch(person.value, (newVal, oldVal) => {
    //   console.log("person的值变了", "newVal===", newVal, "oldVal===", oldVal);
    // });

    // 方法二
    watch(person,(newVal,oldVal)=>{
      console.log("person的值变了", "newVal===", newVal, "oldVal===", oldVal)
    },{deep:true})

    return {
      sum,
      msg,
      person
    };
  },
};
</script>


9.watchEffect函数

<template>
  <h2>当前求和为: {{ sum }}</h2>
  <button @click="sum++">点我加1</button>
  <hr />
  <h2>当前的信息为: {{ msg }}</h2>
  <button @click="msg += '!'">点我加1</button>
  <hr />
  <h2>姓名:{{ person.name }}</h2>
  <h2>年龄:{{ person.age }}</h2>
  <h2>薪资:{{ person.job.j1.salary }}K</h2>
  <button @click="person.name += '~'">修改姓名</button>
  <button @click="person.age++">修改年龄</button>
  <button @click="person.job.j1.salary++">涨薪</button>

  <h1>watchEffect的值: {{ res }}</h1>
</template>

<script>
import { ref, reactive, watchEffect } from "vue"; // ref处理基本类型数据  reactive处理引用类型数据
export default {
  name: "demo",
  setup() {
    // 数据
    let sum = ref(0);
    let msg = ref("hello");
    let res = ref(null)
    let person = reactive({
      name: "张三",
      age: 18,
      job: {
        j1: {
          salary: 20,
        },
      },
    });

    // 监视
    // watch(sum, (newVal, oldVal) => {
    //   console.log("sum的值变了", "newVal===", newVal, "oldVal===", oldVal);
    // });

    // 0  20  watchEffct所指定的回调执行了
    /**
     * watch:既要指明监视的属性,也要指明监视的回调
     * watchEffct: 不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性
     * watchEffct有点像computed:
     *    1.computed注重的是计算出来的值(回调函数的返回值),所以必须要写返回值
     *    2.watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值
     */
    watchEffect(()=>{
      const x1 = sum.value
      console.log(x1) // 0
      const x2 = person.job.j1.salary
      console.log(x2) // 20
      console.log('watchEffct所指定的回调执行了')

      res.value = x1 + x2 // 初始化为20  x1/x2增加  res也会跟着增加
    })

    return {
      sum,
      msg,
      res,
      person
    };
  },
};
</script>


10.生命周期

----------app.vue----------
<template>
  <button @click="isShowDemo=!isShowDemo">切换隐藏/显示</button>
  <Demo v-if="isShowDemo" />
</template>

<script>
import { ref } from "vue";
import Demo from "./components/demo.vue"; // ref处理基本类型数据  reactive处理引用类型数据
export default {
  name: "App",
  components: {
    Demo,
  },
  setup() {
    let isShowDemo = ref(true);
    return {
      isShowDemo
    }
  },
};
</script>


----------demo.vue-------------
<template>
  <h2>当前求和为: {{ sum }}</h2>
  <button @click="sum++">点我加1</button>
</template>

<script>
import { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from "vue"; // ref处理基本类型数据  reactive处理引用类型数据
export default {
  name: "demo",
  setup() {
    console.log('---setup---')
    let sum = ref(0);

    // 通过组合式API的形式去使用生命周期钩子
    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 {
      sum,
    };
  },

  // 通过配置项的形式使用生命周期钩子
  // 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---");
  // }
};
</script>


11.自定义hook

-----------App.vue-----------
<template>
  <button @click="isShowDemo=!isShowDemo">切换隐藏/显示</button>
  <Demo v-if="isShowDemo" />
</template>

<script>
import { ref } from "vue";
import Demo from "./components/demo.vue"; // ref处理基本类型数据  reactive处理引用类型数据
export default {
  name: "App",
  components: {
    Demo,
  },
  setup() {
    let isShowDemo = ref(true);
    return {
      isShowDemo
    }
  },
};
</script>


--------------demo.vue-----------
<template>
  <h2>当前求和为: {{ sum }}</h2>
  <button @click="sum++">点我加1</button>
  <hr>
  <h2>当前点击时鼠标的坐标为: x:{{ point.x }} y:{{ point.y }}</h2>
</template>

<script>
// import { ref, reactive, onMounted, onBeforeUnmount } from "vue"; // ref处理基本类型数据  reactive处理引用类型数据
import { ref } from "vue"; // ref处理基本类型数据  reactive处理引用类型数据
import usePoint from '../hooks/usePoint'
export default {
  name: "demo",
  setup() {
    console.log('---setup---')
    let sum = ref(0);
    let point = usePoint()
    // let point = reactive({
    //   x: 0,
    //   y: 0
    // })

    // function savePoint(event){
    //   console.log(event.pageX,event.pageY)
    //   point.x= event.pageX
    //   point.y= event.pageY
    // }

    // onMounted(()=> {
    //   window.addEventListener('click', savePoint)
    // })

    // onBeforeUnmount(()=> {
    //   window.removeEventListener('click', savePoint)
    // })

    return {
      sum,
      point
    };
  },
};
</script>


-----------/hooks/usePoint.js-----------------
import { reactive, onMounted, onBeforeUnmount } from "vue"; // ref处理基本类型数据  reactive处理引用类型数据

function savePoint() {
  // 实现鼠标'打点'相关的数据
  let point = reactive({
    x: 0,
    y: 0
  })

  // 实现鼠标'打点'相关的生命周期钩子
  function savePoint(event) {
    console.log(event.pageX, event.pageY)
    point.x = event.pageX
    point.y = event.pageY
  }

  onMounted(() => {
    window.addEventListener('click', savePoint)
  })

  onBeforeUnmount(() => {
    window.removeEventListener('click', savePoint)
  })

  return point
}

export default savePoint

// hook本质是一个函数,把setup函数中使用的Composition API进行了封装,类似于vue2中的mixin
// Composition API: ref,reactive,onMounted,onBeforeUnmount...
// 自定义hook的优势:复用代码,让setup中的逻辑更清楚易懂

12.toRef与toRefs

------------App.vue------------
<template>
  <button @click="isShowDemo=!isShowDemo">切换隐藏/显示</button>
  <Demo v-if="isShowDemo" />
</template>

<script>
import { ref } from "vue";
import Demo from "./components/demo.vue"; // ref处理基本类型数据  reactive处理引用类型数据
export default {
  name: "App",
  components: {
    Demo,
  },
  setup() {
    let isShowDemo = ref(true);
    return {
      isShowDemo
    }
  },
};
</script>


-----------demo.vue-------------
<template>
  <!-- <h4>{{ person }}</h4> -->
  <h2>姓名:{{ name }}</h2>
  <h2>年龄:{{ age }}</h2>
  <!-- <h2>薪资toRef:{{ salary }}K</h2> -->
  <h2>薪资toRefs:{{ job.j1.salary }}K</h2>
  <button @click="name += '~'">修改姓名</button>
  <button @click="age++">修改年龄</button>
  <!-- <button @click="salary++">toRef涨薪</button> -->
  <button @click="job.j1.salary++">涨薪</button>

</template>

<script>
// toRef:创建一个ref对象,其value值指向另一个对象中的某个属性
// toRefs:与toRef功能一致,但可以批量创建多个ref对象,语法:toRefs(person)
// import { reactive, toRef } from "vue"; // ref处理基本类型数据  reactive处理引用类型数据
import { reactive, toRefs } from "vue"; // ref处理基本类型数据  reactive处理引用类型数据
export default {
  name: "demo",
  setup() {
    let person = reactive({
      name: "张三",
      age: 18,
      job: {
        j1: {
          salary: 20
        }
      }
    })

    const x = toRefs(person)
    console.log(x) // {age:ObjectRefImpl{}...}

    return {
      // name: toRef(person, "name"),
      // age: toRef(person, "age"),
      // salary: toRef(person.job.j1, "salary")

      // person,
      ...toRefs(person)
    };
  },
};
</script>


13.shallowReactive和shallowRef

--------------App.vue----------------
<template>
  <button @click="isShowDemo=!isShowDemo">切换隐藏/显示</button>
  <Demo v-if="isShowDemo" />
</template>

<script>
import { ref } from "vue";
import Demo from "./components/demo.vue"; // ref处理基本类型数据  reactive处理引用类型数据
export default {
  name: "App",
  components: {
    Demo,
  },
  setup() {
    let isShowDemo = ref(true);
    return {
      isShowDemo
    }
  },
};
</script>

--------------demo.vue---------------
<template>
  <!-- <h4>{{ person }}</h4> -->
  <h4>当前的x值是:{{ y.z }}</h4>
  <!-- 有效果的 -->
  <button @click="y={z:666}">点我替换x</button>
  <!-- 没效果的 -->
  <button @click="y.z++">点我替换y.z++</button>
  <hr>
  <h2>姓名:{{ name }}</h2>
  <h2>年龄:{{ age }}</h2>
  <h2>薪资:{{ job.j1.salary }}K</h2>

  <!-- 姓名可以修改 -->
  <button @click="name += '~'">修改姓名</button>

  <!-- 年龄可以修改 -->
  <button @click="age++">修改年龄</button>

  <!-- 薪资不能修改 -->
  <button @click="job.j1.salary++">涨薪</button>

</template>

<script>
// 1.shallowReactive:如果有一个对象数据,结构数据比较深,但变化时只是最外层属性变化  ===>shallowReactive
// 2.shallowRef:如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来代替  ===> shallowRef
import { ref, reactive, toRefs, shallowReactive, shallowRef } from "vue"; // ref处理基本类型数据  reactive处理引用类型数据
export default {
  name: "demo",
  setup() {
    // let person = reactive({
    //   name: "张三",
    //   age: 18,
    //   job: {
    //     j1: {
    //       salary: 20
    //     }
    //   }
    // })

    // shallowReactive: 只处理对象最外层属性的响应式(浅响应式)
    let person = shallowReactive({
      name: "张三",
      age: 18,
      job: {
        j1: {
          salary: 20
        }
      }
    })

    let person111 = reactive({
      name: "张三",
      age: 18,
      job: {
        j1: {
          salary: 200
        }
      }
    })

    let x = ref(0)

    // shallowRef: 只处理基本数据类型的响应式,不进行对象的响应式处理
    // 我现在很明确,以后不会修改y里面的z
    let y = shallowRef({
      z:0
    })


    return {
      person,
      person111,
      x,
      y,
      ...toRefs(person), // 薪资改不了
      // ...toRefs(person111), // 薪资改的了
    };
  },
};
</script>


14.readonly和shallowReadoniy

----------------App.vue----------------
<template>
  <button @click="isShowDemo=!isShowDemo">切换隐藏/显示</button>
  <Demo v-if="isShowDemo" />
</template>

<script>
import { ref } from "vue";
import Demo from "./components/demo.vue"; // ref处理基本类型数据  reactive处理引用类型数据
export default {
  name: "App",
  components: {
    Demo,
  },
  setup() {
    let isShowDemo = ref(true);
    return {
      isShowDemo
    }
  },
};
</script>


-------------demo.vue------------------
<template>
  <h4>当前求和为:{{ sum }}</h4>
  <button @click="sum++">点我++</button>
  <hr>
  <h2>姓名:{{ name }}</h2>
  <h2>年龄:{{ age }}</h2>
  <h2>薪资:{{ job.j1.salary }}K</h2>
  <button @click="name += '~'">修改姓名</button>
  <button @click="age++">修改年龄</button>
  <button @click="job.j1.salary++">涨薪</button>
</template>

<script>
import { ref, reactive, toRefs, readonly, shallowReadonly } from "vue"; // ref处理基本类型数据  reactive处理引用类型数据
export default {
  name: "demo",
  setup() {
    let sum = ref(0)

    let person = reactive({
      name: "张三",
      age: 18,
      job: {
        j1: {
          salary: 20
        }
      }
    })

    person = readonly(person) // person里的属性值都不能修改 ... 名字,年龄,涨薪 都点击不了
    // person = shallowReadonly(person) // person第一层属性不能修改,深层次的可以改。也就是说,名字和年龄不能改,涨薪可以改
    sum = shallowReadonly(sum) // sum++执行不了

    return {
      sum,
      ...toRefs(person)
    };
  },
};
</script>



15.toRaw和markRaw

<template>
  <h4>当前求和为:{{ sum }}</h4>
  <button @click="sum++">点我++</button>
  <hr>
  <h2>姓名:{{ name }}</h2>
  <h2>年龄:{{ age }}</h2>
  <h2>薪资:{{ job.j1.salary }}K</h2>
  <h3 v-show="person.car">座驾:{{ person.car }}</h3>
  <button @click="name += '~'">修改姓名</button>
  <button @click="age++">修改年龄</button>
  <button @click="job.j1.salary++">涨薪</button>
  <button @click="showRawPerson">输出最原始的person</button>
  <button @click="addCar">给人添加一台车</button>
  <button @click="person.car.name+='!'">换车名</button>
  <button @click="changePrice">换价格</button>
</template>

<script>
import { ref, reactive, toRefs, toRaw, markRaw } from "vue"; // toRaw 用的不多
export default {
  name: "demo",
  setup() {
    let sum = ref(0)

    let person = reactive({
      name: "张三",
      age: 18,
      job: {
        j1: {
          salary: 20
        }
      },
      car: '',
      // axios: '' 用markRaw标记,不可变
    })

    // toRaw 只能处理reactive缔造的响应式对象,不能处理ref类型的数据
    
    /**
     * toRaw: 
     * 作用: 将一个由reactive生成的响应式对象转为普通对象
     * 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新
     */
    function showRawPerson() {
      console.log(person) // Proxy {name: "张三", age: 18, job: {…}}
      const p = toRaw(person)
      console.log(p) // {name: "张三", age: 18, job: {…}}
    }

    
    /**
     * markRaw
     * 作用:标记一个对象,使其永远不会再成为响应式对象
     * 应用场景:
     *      1.有些值不应被设置为响应式的,例如复杂的第三方类库等
     *      2.当渲染具有不可变数据源的大列表时,跳过响应式转换可以提供性能
     */
    function addCar() {
      let car = { name: '奔驰', price: 40 }
      // person.car = car
      person.car = markRaw(car)
    }

    function changePrice() {
      person.car.price++
      console.log(person.car.price) // 页面显示的价格是40不会变化
    }

    return {
      sum,
      person,
      ...toRefs(person),
      showRawPerson,
      addCar,
      changePrice
    };
  },
};
</script>


16.customRef

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

<script>
// import { ref } from "vue";
import { customRef } from "vue"; // 作用:创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显式控制

export default {
  name: "App",
  setup() {
    let timer = null
    // 自定义一个ref名为: ref_afanfan
    function ref_afanfan(value, time) {
      // let timer = null
      // console.log("---myRef---", value); // ---myRef--- hello
      return customRef((track, trigger) => {
        return {
          get() {
            console.log(`有人从myRef这个容器中读取数据了,我把${value}给他了`);
            track(); // 通知vue追踪数据的变化(提前和get商量一下,让他认为这个value是有用的)
            return value;
          },
          set(newValue) {
            console.log(`有人把myRef这个容器中的数据改为了:${newValue}`);

            // 防抖
            clearTimeout(this.timer)
            this.timer =  setTimeout(() => {
              value = newValue;
              trigger(); // 通知vue去重新解析模板
            }, time);
          },
        };
      });
    }

    // let keyWord = ref('hello'); // 使用vue提供的ref
    let keyWord = ref_afanfan("hello",500);

    return {
      timer,
      keyWord,
    };
  },
};
</script>

17.provide与inject

----------App.vue------------
<template>
  <div class="app">
    <h3>我是app组件(祖) --- {{ name }} {{ price }}</h3>
    <child />
  </div>
</template>

<script>
import { reactive, toRefs, provide } from "vue";
import child from "./components/child.vue";

export default {
  name: "App",
  components: { child },
  setup() {
    let car = reactive({ name: "奔驰", price: "40W" });
    // 给自己的后代组件传递数据,只要在这里执行了provide,后代子组件都可以通过inject获取数据
    // 父传子props    父传孙provide
    provide("car", car);
    return {
      ...toRefs(car),
    };
  },
};
</script>

<style>
.app {
  background-color: #ccc;
  padding: 10px;
}
</style>


---------child.vue-----------
<template>
  <div class="child">
    <h3>我是child组件(子)</h3>
    <son/>
  </div>
</template>

<script>
import son from './son.vue'
export default {
  name: "child",
  components: { son },
  setup() {
    return {
    };
  },
};
</script>

<style>
  .child {
    background-color: skyblue;
    padding: 10px;
  }
</style>


------------son.vue----------------
<template>
  <div class="son">
    <h3>我是son组件(孙)--- {{ car.name }}--- {{ car.price }}</h3>
  </div>
</template>

<script>
import { inject } from 'vue'
export default {
  name: "son",
  setup() {
    let car =  inject('car')
    console.log('===', car)
    return {
      car
    };
  },
};
</script>

<style>
  .son {
    background-color: orange;
    padding: 10px;
  }
</style>

18.响应式数据的判断

<template>
  <!-- 在vue2中,组件必须有一个根标签;在vue3中,组件可以没有根标签,内部会将多个标签包含在一个Fragment虚拟元素中 好处是减少标签层级,减小内存占用 -->
  <h3>我是app组件</h3>
  <h3>我是app组件</h3>
  <h3>我是app组件</h3>
</template>

<script>
import { ref, reactive, toRefs, readonly, isRef, isReactive, isReadonly, isProxy } from "vue";

// isRef  检查一个值是否为ref对象
// isReactive // 检查一个对象是否由reactive创建的响应式代理 
// isReadonly // 检查一个对象是否由readonly创建的只读代理
// isProxy // 检查一个对象是否由reactive或者readonlty方法创建的代理

export default {
  name: "App",
  components: {  },
  setup() {
    let car = reactive({ name: '奔驰', price: '40W' })
    let sum = ref(0)
    let car2 = readonly(car)

    console.log(isRef(sum)) // true
    console.log(isReactive(car)) // true
    console.log(isReadonly(car2)) // true
    console.log(isProxy(car)) // true
    console.log(isProxy(sum)) // false  ref底层用的是Object.defineProperty() 不是proxy  所以这里为false

    return {
      ...toRefs(car)
    };
  },
};
</script>

19.teleport传送

<template>
  <div>
    <button @click="isShow = true">点我弹个窗</button>
    <!-- teleport:传送   to="html" 传送到html中去   to="body" 传送到body中去 -->
    <!-- 此处的to="#afanfan" 在public里的index.html里有<div id="afanfan"></div> -->
    <teleport to="#afanfan">
      <div v-if="isShow" class="mask">
        <div class="dialogStyle">
          <h3>我是一个弹窗</h3>
          <h4>一些内容</h4>
          <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: rgb(0, 0, 0, 0.5);
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
}
.dialogStyle {
  width: 300px;
  height: 300px;
  background-color: green;
}
</style>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值