【Vue3笔记】【Vue3】

1.Proxy代理原理

这个proxy是ES6新增的属性,我们可以利用这个为对象新增属性,Vue3也是实现了这种代理机制对数据进行代理。比起Vue2中Object.defineProperty函数功能更加丰富和强大。

我们利用一个例子来演示proxy的原理

let user = {
            name: 'zhangsan'
        }
        const userProxy = new Proxy(user, {
            /* 
                target 表示目标对象
                propertyName 表示目标对象上的属性名,是一个字符串
            */
            get(target, propertyName) {
                console.log('@@@@get执行了...');
                return target[propertyName]
            },
            set(target, propertyName, value) {
                console.log('@@@@set执行了...');
                target[propertyName] = value
            },
            deleteProperty(target, propertyName) {
                console.log('@@@@deleteProperty执行了...');
                return target[propertyName]
            }
        })

我们定义了一个user对象,然后对user对象进行代理,这里面有三个函数,get() set()以及deleteProperty()。

get()函数表示当我们访问代理对象的属性时,get方法会被调用,返回访问属性的值。

set()函数表示当我们修改或者添加代理对象上的属性时,set()方法会被调用,返回修改或添加的属性的值。

deleteProperty()函数表示当我们对代理对象属性进行删除时,会被自动调用,返回true或false,true表示修改成功,false表示修改失败。

运行结果

在这里插入图片描述

2.setup函数

在Vue2中我们都知道数据都写在data里面,方法写在methods里面…但是在Vue3中这些都写在setup函数里面

注意:setup 中的 this undefined。所以setup this 是不可用的

示例

setup可以返回一个对象,该对象的属性、方法等均可以在模板语法中使用,例如插值语法。

APP.vue组件

<template>
  <h1>姓名:{{ name }}</h1>
  <h1>年龄:{{ age }}</h1>
  <button @click="sayHi">SAY HI</button>
</template>

<script>
  export default {
    name: 'App',
    setup() {
      let name = '张三'
      let age = 19
      function sayHi(){
        alert('hello Vue3')
      }
      return {name,age,sayHi}
  }
</script>

setup也可以返回一个渲染函数,从而执行渲染函数,渲染页面,原来页面中的标签会被覆盖。

<template>
  <h1>hello</h1>
</template>

<script>
  import {h} from 'vue'
  export default {
    name: 'App',

    setup() {
      return function () {
        return h('h2','欢迎来学习Vue3')
      }
      /* 简写 */
      //return ()=>h('h2','欢迎来学习Vue3')
    }
  }
</script>

3.ref 对基本数据类型完成响应式

底层用的是Object.defineProperty实现的数据代理。

示例代码

<template>
  <h1>姓名:{{ namerefIml }}</h1>
  <h1>年龄:{{ agerefIml }}</h1>
  <button @click="upDateData">更新数据(ref响应式)</button>
</template>

<script>
  import { ref } from 'vue'
  export default {
    name: 'App',

    setup() {
      let namerefIml = ref('张三')
      console.log(namerefIml);
      let agerefIml = ref(19)

      function upDateData() {
        namerefIml.value = '李四'
        agerefIml.value=20
      }

      return {namerefIml,agerefIml,upDateData}
    }
  }
</script>

console.log(namerefIml);后,我们可以在控制台观察到,RefImpl(引用对象中的value有响应式)

在这里插入图片描述

上述代码中,当我们点击按钮,页面的数据也随之改变,实现了响应式。

4.ref对 对象实现响应式

示例代码

<template>
  <h1>姓名:{{ user.name }}</h1>
  <h1>年龄:{{ user.age }}</h1>
  <!-- <button @click="upDateData">更新数据(ref响应式)</button> -->
</template>

<script>
  import { ref } from 'vue'
  export default {
    name: 'App',

    setup() {
      let user = ref({
        name: 'jack',
        age:19
      })
      console.log(user);
      return {user}
    }
  }
</script>

结果:

在这里插入图片描述

从上图可以观察到,Vue3底层对对象进行代理时,外层先用Object.defineProxy进行代理,如果是对象类型的话,此对象用Proxy进行代理。

如何改变ref代理的对象内属性的值?

示例代码

对引用对象userRefImpl上的value中代理的对象的属性的值进行修改,从而实现响应式,页面随之更新。

<template>
  <h1>姓名:{{ userRefImpl.name }}</h1>
  <h1>年龄:{{ userRefImpl.age }}</h1>
  <button @click="upDateData">更新对象数据(ref响应式)</button>
</template>

<script>
  import { ref } from 'vue'
  export default {
    name: 'App',

    setup() {
      let userRefImpl = ref({
        name: 'jack',
        age:19
      })
      console.log(userRefImpl)
      function upDateData() {
        userRefImpl.value.name = 'smith'
        userRefImpl.value.age=21
      }
      return {userRefImpl,upDateData}
    }
  }
</script>

5.reactive添加响应式

注意:reactive不支持给基本数据类型添加响应式,只能给对象添加响应式,对象属性的添加,删除,修改都具有响应式。

示例代码

<template>
  <h1>姓名:{{ userProxy.name }}</h1>
  <h1>年龄:{{ userProxy.age }}</h1>
  <button @click="upDateData">更新对象数据(reactive响应式)</button>
</template>

<script>
  import { reactive} from 'vue'
  export default {
    name: 'App',
    /* 
      注意:reactive不支持给基本数据类型添加响应式,只能给对象添加响应式
    */
    setup() {
      let userProxy = reactive({
        name: 'jack',
        age:19
      })
      console.log(userProxy)
      function upDateData() {
        userProxy.name = 'smith'
        userProxy.age=22
      }
      return {userProxy,upDateData}
    }
  }
</script>

console.log(userProxy),我们可以看到底层是一个代理对象,用代理对象实现了为对象添加响应式

在这里插入图片描述

6.props父传子数据

示例代码

App.vue(父组件)

<template>
  <User name="张三" age="20" :sex="sex"></User>
</template>

<script>
  import {ref} from 'vue'
  import User from './components/User.vue'
  export default {
    name: 'App',
    components:{User},
    setup() {
      let sex = ref('男')
      return {sex}
    }
  }
</script>

User.vue(子组件)

<template>
  <h1>演示props父传子数据</h1>
  <h2>姓名:{{ name }}</h2>
  <h2>年龄:{{ age }}</h2>
  <h2>性别:{{ sex }}</h2>
</template>

<script>
    export default {
        name: 'User',
        props:['name','age','sex'],
        setup(props) {
            console.log(props.name);
            console.log(props.age);
            console.log(props.sex);
        }
    }
</script>

运行结果:

props传递过来的数据都具有响应式。

在这里插入图片描述

7.Vue3生命周期

在这里插入图片描述

演示代码

生命周期可以分为两个形式演示,一种是选项式API(写在setup外面的),另一种是组合式API(写在setup内的)

父组件APP.vue

<template>
  <button @click="isDisplay = !isDisplay">显示或隐藏</button>
  <User v-if="isDisplay"></User>
</template>

<script>
  import {ref} from 'vue'
  import User from './components/User.vue'
  export default {
    name : 'App',
    components : {User},
    setup(){
      //console.log('setup:安装组合式API')
      // data
      let isDisplay = ref(true)
      // 返回对象
      return {isDisplay}
    },
    /* beforeCreate(){
      console.log('beforeCreate')
      console.log('正在初始化选项式API')
    },
    created(){
      console.log('选项式API初始化完成')
      console.log('created')
    } */
  }
</script>

子组件User.vue

<template>
    <h1>User组件</h1>
    <h2>计数器:{{counter}}</h2>
    <button @click="counter++">计数</button>
</template>

<script>
    import {ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted} from 'vue'
    export default {
        name : 'User',
        setup(){
            console.log('setup...')
            // 采用组合式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')
            }) */

            // data
            let counter = ref(0)
            // 返回一个对象
            return {counter}
        },

        // 选项式API
       /*  beforeCreate(){
            console.log('beforeCreate')
        },
        created(){
            console.log('created')
        }, */

        // 在Vue3当中,生命周期钩子函数有两种写法:
        // 第一种写法:选项式API(以下这种方式就是选项式API)
        // 第二种写法:组合式API
        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>

运行结果:

在这里插入图片描述

9.自定义事件

自定义事件可以用于子组件向父组件传递数据,在父组件中自定义事件,然后子组件触发事件,触发的同时传递数据给父组件,父组件接收到子组件传递的数据。

示例代码

App.vue 父组件

语法: 这里的函数用于接收子组件传来的数据

<组件 @自定义事件名=“函数”></组件>

<template>
  <User @event1="showInfo"></User>
</template>

<script>
  import User from './components/User.vue'
  export default {
  components: { User },
    name: 'App',
    setup() {
      function showInfo(name, age) {
        alert('姓名:'+name+" 年龄:"+age)
      }  
      return {showInfo}
    }
    
  }
</script>

User.vue 子组件

语法:

context.emit(‘事件名字’,数据)

<template>
  <button @click="triggerEvent">子传父</button>
</template>

<script>
    export default {
        name: 'User',
        setup(props,context) {
            function triggerEvent() {
                context.emit('event1','张三',19)
            }
            return {triggerEvent}    
        }
    }
</script>

<style>

</style>

10.全局事件总线

10.1绑定事件传递数据

首先我们要先安装mitt这个插件

npm i mitt

通常我们会在src下建立utils文件夹(工具),在utils文件夹下引入mitt插件

event-bus.js

import mitt from 'mitt'

export default mitt()

emitter是引入的mitt插件 import emitter from ‘./utils/event-bus.js’

emitter.on(‘事件名’,函数处理数据)用来绑定事件,emitter.emit(‘触发事件名’,数据(可以是对象,也可以是基本数据类型))

App.vue

<template>
  <User></User>
</template>

<script>
  import emitter from './utils/event-bus.js'
  import User from './components/User.vue'
  import { onMounted } from 'vue'
  export default {
    components: { User },
    name: 'App',
    setup() {
      onMounted(() => {
        emitter.on('event1',showInfo)
      })
      
      function showInfo(user) {
        alert('姓名:'+user.name+" 年龄:"+user.age)
      }  
      return {showInfo}
    }
  }
</script>

User.vue

<template>
  <button @click="triggerEvent">全局事件总线</button>
</template>

<script>
    import emitter from '../utils/event-bus.js'
    export default {
        name: 'User',
        setup() {
            function triggerEvent() {
                emitter.emit('event1',{name:'张三',age:21})
            }
            return {triggerEvent}    
        }
    }
</script>

<style>

</style>
10.2解绑事件
//引入插件
import emitter from '../utils/event-bus.js'

//解绑所有绑定的事件
emitter.all.clear()

//解绑指定事件
emitter.off('事件名')

11.计算属性

计算属性顾名思义就是通过一系列计算(处理)后所得到的一个属性,重点是属性,可以直接在插值语法中使用。

语法:
/* 简写形式 当我们不需要set方法时,可以使用简写形式。*/
let reversedName = computed(() => {
    return data.name.split('').reverse().join('')
})
/* 完整写法 */
let reversedName = computed({
    get() {
      ....
      return 处理过后的值
    },
    //当计算属性的值发生改变,set方法就会执行
    set(val) {
      ....
    }
 })

我们通过一个小案例来讲解vue3中的计算属性

反转字符串案例

App.vue

<template>
  输入字符串:<input type="text" v-model="data.name"><br><br>
  反转之后的:<input type="text" v-model="reversedName">
</template>

<script>
import { computed, reactive } from 'vue'
  export default {
    name: 'App',
    setup() {
      let data = reactive({
        name:''
      }) 
      /* 简写形式 */
     /*  let reversedName = computed(() => {
        return data.name.split('').reverse().join('')
      }) */
      /* 完整写法 */
      let reversedName = computed({
        get() {
          return data.name.split('').reverse().join('')
        },
        set(val) {
          console.log(val);
        }
      })
      return {data,reversedName}
    }
  }
</script>

12.ref监视和reactive监视

12.1ref监视

对ref包裹的数据(基本数据类型)进行监视

//对ref包裹的数据进行监视
watch(监视的数据, (newValue, oldValue) => {
     console.log(newValue,oldValue);
})
//如果我们发现监视的多个数据具有相同的业务,我们可以把多个需要监视的数据放到数组中
watch([数据1,数据2,...], (newValue, oldValue) => {
    console.log(newValue,oldValue);
})

点我加一案例

<template>
  <h1>数字:{{ counter }}</h1>
  <h1>数字2:{{ counter2 }}</h1>
  <button @click="counter++">监视counter 点我加一</button>
  <br><br>
  <button @click="counter2++">监视counter2 点我加一</button>
</template>

<script>
  import { watch,ref } from 'vue'
  export default {
    name: 'App',
    setup() {
      let counter = ref(1)
      let counter2= ref(100)
      
      /* watch(counter, (newValue, oldValue) => {
        console.log(newValue,oldValue);
      })
      watch(counter2, (newValue, oldValue) => {
        console.log(newValue,oldValue);
      }) */
      watch([counter,counter2], (newValue, oldValue) => {
        console.log(newValue,oldValue);
      })
      return {counter,counter2}
    }
  }
</script>
12.2reactive监视

下面的案例我们演示了对reactive包裹的对象进行监视时的几种情况。

<template>
  <h1>数字:{{ data.counter }}</h1>
  <button @click="data.counter++">监视counter 点我加一</button>

  <h1>数字:{{ data.a.b.number }}</h1>
  <button @click="data.a.b.number++">监视counter 点我加一</button>
</template>

<script>
  import { watch,reactive } from 'vue'
  export default {
    name: 'App',
    setup() {
      let data = reactive({
        counter: 1,
        a: {
          b: {
            number:3
          }
        }
      })
      /* 对data对象进行监听,无法对旧值进行监听。 */
      /* watch(data, (newValue, oldVue) => {
        console.log(newValue,oldVue);
      }) */
      
      /* 如果只想监听一个属性 */
      /* watch(()=>data.counter, (newValue, oldVue) => {
        console.log(newValue,oldVue);
      }) */
      
      /* deep生效 */
      /* watch(()=>data.a.b, (newValue, oldVue) => {
        console.log(newValue,oldVue);
      }, { deep: false }) */

      /* deep失效 */
      watch(data.a.b, (newValue, oldVue) => {
        console.log(newValue,oldVue);
      },{deep:false})
      return {data}
    }
  }
</script>

13.watchEffect

可以监视属性的变化,值一旦改变,watchEffect就会执行,注意:浏览器打开时会自动执行一次。

示例代码:

counter1和counter2值一旦改变,watchEffect会自动执行回调函数

<template>
  <h2>counter1:{{ data.counter1 }}</h2>
  <button @click="data.counter1++">点我加1</button>
  <hr>
  <h2>counter2:{{ data.counter2 }}</h2>
  <button @click="data.counter2++">点我加1</button>
  <hr>
  <h2>counter3:{{ data.counter3 }}</h2>
  <button @click="data.counter3++">点我加1</button>
</template>

<script>
import { reactive, watchEffect } from 'vue'
  export default {
    name: 'App',
    setup() {
      let data = reactive({
        counter1: 1,
        counter2: 2,
        counter3:3
      }) 
      //这里监视两个属性,counter1和counter2值一旦改变,watchEffect会自动执行回调函数
      watchEffect(() => {
        let a = data.counter1
        let b = data.counter2
        console.log(a,b);
      })
      return {data}
    }
  }
</script>

13.hooks实现代码复用

当一段程序需要多个使用时,我们可以把这一段程序提取出来,然后再用到的组件中引入。

示例:

App.vue组件

<template>
  数字1:<input type="number" v-model="data.number1"><br><br>
  数字2:<input type="number" v-model="data.number2"><br><br>
  求和结果:{{data.result}}<br><br>
  <button @click="sum">求和</button>
</template>

<script>
   //引入公共复用的代码
  import sum from './hooks/sum'
  export default {
    name: 'App',
    setup() {
      //调用引入的js程序
      return sum()
    }
    
  }
</script>

sum.js(公共代码)代码一般提取到一个单独的js文件,放在hooks文件夹下

import { reactive } from 'vue'
export default function () {
    let data = reactive({
        number1: 0,
        number2: 0,
        result: 0
    })
    function sum() {
        data.result = data.number1 + data.number2
    }
    return { data, sum }
}

14.浅层次shallowRef

shallowRef包裹对象没有响应式 ,只是浅层次响应式,对于直接包裹基本数据类型有响应式,但是包裹对象没有响应式

一般用于对系统性能优化,如果值不需要修改,那么可以放在shallowRef里面,提高性能。

示例

//具有响应式
let data = shallowRef(1)

//没有响应式
let data = shallowRef({
    counter:1
})

15.浅层次shallowReactive

浅层次shallowReactive 对象的第一层具有响应式,其他不具有响应式。

示例

let data = shallowReactive({
    counter:1,//具有响应式
    a: {
        counter2:10  //没有响应式
    }
})

16.响应式数据判断

isRef:检查某个值是否为 ref。

isReactive:检查一个对象是否是由 reactive() 或 shallowReactive() 创建的代理。

isProxy:检查一个对象是否是由 reactive()、readonly()、shallowReactive() 或 shallowReadonly() 创建的代理。

isReadonly:检查传入的值是否为只读对象。

17.深只读和浅只读

深只读:所有数据都不能修改

浅只读:只有被包裹对象的第一层只读

 /* 深只读 */
data = readonly(data)

/* 浅只读,只有第一层只读 */
data = shallowReadonly(data)

18.toRef和toRefs

当我们进行了多层的嵌套,出现的问题在于插值语法中多次重复的写data.

 let data = reactive({
        counter1 : 1,
        counter2 : 100,
        a : {
          b : {
            counter3 : 1000
          }
        }
      })

插值语法的问题,我们会发现每次都要进行data.

<h2>{{data.counter1}}</h2>
<h2>{{data.a.b.counter3}}</h2>

我们可不可以经过优化写成如下这样?去掉data.

<h2>{{counter1}}</h2>
<h2>{{a.b.counter3}}</h2>

解决方法

// toRef
// toRef语法格式:toRef(对象, '该对象中的属性名')
// toRef函数执行之后会生成一个全新的对象:ObjectRefImpl(引用对象)
// 只要有引用对象,就有value属性,并且value属性是响应式的。(value有对应的set和get。)

//第一种toref,vue3中返回时这样写
return {
       	counter1 : toRef(data, 'counter1'),
        counter3 : toRef(data.a.b, 'counter3')
      }
//第二种toRefs
return {
        data,
        ...toRefs(data)
       }

19.转换为原始&标记为原始

toRaw 与 markRaw

toRaw:将响应式对象转换为普通对象。只适用于 reactive 生成的响应式对象。

markRaw:标记某个对象,让这个对象永远都不具备响应式。比如在集成一些第三方库的时候,比如有一个巨大

的只读列表,不让其具备响应式是一种性能优化。

用法:

//如果data对象具有响应式,但是由于项目需求,现在data不需要响应式了,那么可以写下面这个代码
let rawObj = toRaw(data)

//标记某个对象,让这个对象永远都不具备响应式
markRaw(对象)

20.provide和inject

这两个是用于组件之间数据的传递的,一般建议用在隔代组件之间数据的传递

用法:

provide('名字', 数据) //发送数据方

inject('名字')//接收数据方

//注意provide和inject的名字一定要保持一致

21.语法糖

//在script标签中 加人setup data,methods等不需要再写在setup函数中
<script setup>
  import {ref} from 'vue'
  import User from './components/User.vue'//组件可直接引入后使用,不需要再注册

  // data
  let counter = ref(0)
  // methods
  function add(){
    ...
  }
  // watch
  // computed
  // ....
</script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值