[Vue]Vue3学习笔记(尚硅谷)(二)

简介: [Vue]Vue3学习笔记(尚硅谷)(二)


  • 实现原理:
  • 通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。
//源数据
let person = {
  name:'张三',
  age:18
}
//模拟Vue3中实现响应式
//Proxy对属性的增删改查都可以监测得到
//#region 
const p = new Proxy(person,{
  //有人读取p的某个属性时调用
  get(target,propName){
    console.log(`有人读取了p身上的${propName}属性`)
    return target[propName]
  },
  //有人修改p的某个属性、或给p追加某个属性时调用
  set(target,propName,value){
    console.log(`有人修改了p身上的${propName}属性,我要去更新界面了!`)
    target[propName] = value
  },
  //有人删除p的某个属性时调用
  deleteProperty(target,propName){
    console.log(`有人删除了p身上的${propName}属性,我要去更新界面了!`)
    return delete target[propName]
  }
})
//#endregion
  • 通过Reflect(反射): 对源对象的属性进行操作。
// Reflect(反射)
// 读取对象指定属性的值
Reflect.get(object, '属性名')
// 修改对象指定属性的值
Reflect.set(object, '属性名', '新属性值')
// 删除对象指定属性
Reflect.deleteProperty(object, '属性名')
//模拟Vue3中实现响应式
//#region 
const p = new Proxy(person,{
  //有人读取p的某个属性时调用
  get(target,propName){
    console.log(`有人读取了p身上的${propName}属性`)
    return Reflect.get(target,propName)
  },
  //有人修改p的某个属性、或给p追加某个属性时调用
  set(target,propName,value){
    console.log(`有人修改了p身上的${propName}属性,我要去更新界面了!`)
    Reflect.set(target,propName,value)
  },
  //有人删除p的某个属性时调用
  deleteProperty(target,propName){
    console.log(`有人删除了p身上的${propName}属性,我要去更新界面了!`)
    return Reflect.deleteProperty(target,propName)
  }
})
//#endregion
  • MDN文档中描述的Proxy与Reflect:
new Proxy(data, {
  // 拦截读取属性值
    get (target, prop) {
      return Reflect.get(target, prop)
    },
    // 拦截设置属性值或添加新属性
    set (target, prop, value) {
      return Reflect.set(target, prop, value)
    },
    // 拦截删除属性
    deleteProperty (target, prop) {
      return Reflect.deleteProperty(target, prop)
    }
})
proxy.name = 'tom'

🥽 reactive对比ref

  • 从定义数据角度对比:
  • ref用来定义:基本类型数据
  • reactive用来定义:对象(或数组)类型数据
  • 备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过reactive转为代理对象
  • 从原理角度对比:
  • ref通过Object.defineProperty()getset来实现响应式(数据劫持)。
  • reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。
  • 从使用角度对比:
  • ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value
  • reactive定义的数据:操作数据与读取数据:均不需要.value
  • 一般将数据封装在一个data对象中,利用reactive函数将该对象变为响应式数据对象

🥽 setup的两个注意点

  • setup执行的时机
  • 在beforeCreate之前执行一次,this是undefined,在setup无法使用this。
  • setup的参数
  • props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。
  • context:上下文对象
  • attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明接收的属性, 相当于 this.$attrs。存放没有被组件props配置项接收的数据,如果组件外部传递过来的数据都被组件props配置项接收,则该对象为空。
  • slots: 收到的插槽内容, 相当于 this.$slots。Vue3中具名插槽使用v-slot:插槽名
  • emit: 分发自定义事件的函数, 相当于 this.$emit。Vue3绑定的自定义事件在组件中需要使用emits配置项接收。

App.vue

<template>
  <Demo @hello="showHelloMsg" msg="你好啊" school="尚硅谷">
    <!-- Vue3中具名插槽使用 `v-slot:插槽名` -->
    <template v-slot:qwe>
      <span>尚硅谷</span>
    </template>
    <template v-slot:asd>
      <span>尚硅谷</span>
    </template>
  </Demo>
</template>
<script>
  import Demo from './components/Demo'
  export default {
    name: 'App',
    components:{Demo},
    setup(){
      // 自定义事件的处理函数
      function showHelloMsg(value){
        alert(`你好啊,你触发了hello事件,我收到的参数是:${value}!`)
      }
      return {
        showHelloMsg
      }
    }
  }
</script>

Demo.vue

<template>
  <h1>一个人的信息</h1>
  <h2>姓名:{{person.name}}</h2>
  <h2>年龄:{{person.age}}</h2>
  <button @click="test">测试触发一下Demo组件的Hello事件</button>
</template>
<script>
  import {reactive} from 'vue'
  export default {
    name: 'Demo',
    props:['msg','school'],
    emits:['hello'], // 绑定的自定义事件在组件中需要使用emits配置项接收
    setup(props,context){
      // console.log('---setup---',props)
      // console.log('---setup---',context)
      // console.log('---setup---',context.attrs) //相当与Vue2中的$attrs
      // console.log('---setup---',context.emit) //触发自定义事件的。
      console.log('---setup---',context.slots) //插槽
      //数据
      let person = reactive({
        name:'张三',
        age:18
      })
      //方法
      function test(){
        // 触发自定义事件
        context.emit('hello',666)
      }
      //返回一个对象(常用)
      return {
        person,
        test
      }
    }
  }
</script>

🥽 computed函数

  • 与Vue2.x中computed配置功能一致,在Vue3中可以使用Vue2中的写法(向下兼容)。在Vue3中计算属性使用computed函数。
  • 写法
import {computed} from 'vue'
setup(){
    ...
  //计算属性——简写
    let fullName = computed(()=>{
        return person.firstName + '-' + person.lastName
    })
    //计算属性——完整
    let fullName = computed({
        get(){
            return person.firstName + '-' + person.lastName
        },
        set(value){
            const nameArr = value.split('-')
            person.firstName = nameArr[0]
            person.lastName = nameArr[1]
        }
    })
}
<template>
  <h1>一个人的信息</h1>
    姓: <input type="text" v-model="person.firstName"><br/>
    名: <input type="text" v-model="person.lastName"><br/>
    <p>全名: {{person.fullName}}</p>
</template>
<script>
  import {reactive, computed} from 'vue'
  export default {
    name: 'Demo',
    setup(){
      //数据
      let person = reactive({
        firstName: '张',
            lastName: '三'
      })
        // 计算属性
        person.fullName = computed(()=>{
          return person.firstName + '-' + person.lastName
        })
      //返回一个对象
      return {
        person,
      }
     }
  }
</script>

🥽 watch函数

  • 与Vue2.x中watch配置功能一致,在Vue3中可以使用Vue2中的写法(向下兼容)。
  • 两个小“坑”:
  • 监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)。
  • 监视reactive定义的响应式数据中某个属性时:deep配置有效。
//数据
 let sum = ref(0)
   let msg = ref('你好啊')
   let person = reactive({
    name:'张三',
    age:18,
    job:{
      j1:{
        salary:20
      }
    }
   })
//情况一:监视ref定义的响应式数据
// 第一个参数为监视的数据,第二个参数为回调函数,第三个参数为配置属性
watch(sum,(newValue,oldValue)=>{
  console.log('sum变化了',newValue,oldValue)
},{immediate:true})
//情况二:监视多个ref定义的响应式数据
// newValue,oldValue为数组类型数据
watch([sum,msg],(newValue,oldValue)=>{
  console.log('sum或msg变化了',newValue,oldValue)
}) 
/* 情况三:监视reactive定义的响应式数据
      若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue!!
      如果需要获取监视对象中某个属性的oldValue,可以采用将该属性单独取出使用ref的形式
      若watch监视的是reactive定义的响应式数据,则强制开启了深度监视 
*/
// 如果监视的对象为使用ref函数实现响应式,则需要监视`变量.value`,对于对象类型数据ref借助了reactive
watch(person,(newValue,oldValue)=>{
  console.log('person变化了',newValue,oldValue)
},{immediate:true,deep:false}) //此处的deep配置不再奏效
//情况四:监视reactive定义的响应式数据中的某个属性
watch(()=>person.job,(newValue,oldValue)=>{
  console.log('person的job变化了',newValue,oldValue)
},{immediate:true,deep:true}) 
//情况五:监视reactive定义的响应式数据中的某些属性
watch([()=>person.job,()=>person.name],(newValue,oldValue)=>{
  console.log('person的job变化了',newValue,oldValue)
},{immediate:true,deep:true})
//特殊情况
// 监视reactive所定义的对象中的某个属性,且该属性也为对象类型
watch(()=>person.job,(newValue,oldValue)=>{
    console.log('person的job变化了',newValue,oldValue)
},{deep:true}) //此处由于监视的是reactive所定义的对象中的某个属性,所以deep配置有效
  • 对于使用ref函数定义的数据,如果为基本数据类型,则使用watch监视时,不使用变量.value的形式,因为不能监视一个字面量,监视的需要为一个存放数据的结构;如果为对象类型,此时value的值为Proxy对象,如果没有开启深度监视,只要value对应的对象的引用没有改变相当于值没有修改,解决方法:
  1. 开启深度监视,监视Proxy对象中属性的变化
  2. 监视变量.value,由于Proxy对象时ref函数借助reactive函数生成的,监视变量.value相当于监视reactive函数生成的Proxy对象

🥽 watchEffect函数

  • watch的套路是:既要指明监视的属性,也要指明监视的回调。
  • watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
  • watchEffect有点像computed:
  • 两个函数中所依赖的数据发生变化,都会重新执行一次回调。
  • 但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
  • 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。
watchEffect(()=>{
    const x1 = sum.value
    const x2 = person.age
    console.log('watchEffect配置的回调执行了')
})

🥽 生命周期

  • Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名:
  • beforeDestroy改名为 beforeUnmount
  • destroyed改名为 unmounted
    App.vue
<template>
  <button @click="isShowDemo = !isShowDemo">切换隐藏/显示</button>
  <Demo v-if="isShowDemo"/>
</template>
<script>
  import {ref} from 'vue'
  import Demo from './components/Demo'
  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 } from 'vue'
export default {
  name: 'Demo',
  setup() {
    //数据
    let sum = ref(0)
    //返回一个对象(常用)
    return { sum }
  },
  //通过配置项的形式使用生命周期钩子
  //#region
  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---')
  }
  //#endregion
}
</script>

  • Vue3.0也提供了 Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下:
  • beforeCreate===>setup()
  • created=======>setup()
  • beforeCreatecreated没有对应组合式API,setup()相当于beforeCreatecreated,对于配置项setup()的执行时机早于配置项beforeCreatecreated
  • beforeMount ===>onBeforeMount
  • mounted=======>onMounted
  • beforeUpdate===>onBeforeUpdate
  • updated =======>onUpdated
  • beforeUnmount ==>onBeforeUnmount
  • unmounted =====>onUnmounted
  • 如果组合式API的生命周期与配置项形式的生命周期一起写,组合式API的生命周期的执行时机优先于配置项形式的生命周期。
  • 一般情况下统一使用组合式API的生命周期,或者统一使用配置项形式的生命周期
<template>
  <h2>当前求和为:{{sum}}</h2>
  <button @click="sum++">点我+1</button>
</template>
<script>
  import {ref,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from 'vue'
  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}
    },
    //通过配置项的形式使用生命周期钩子
    //#region 
    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---')
    },
    //#endregion
  }
</script>

相关文章
|
1天前
|
存储 JavaScript 前端开发
vue尚品汇商城项目-day05【30.登录与注册静态组件(处理公共图片资源问题)+31.注册的业务+登录业务】
vue尚品汇商城项目-day05【30.登录与注册静态组件(处理公共图片资源问题)+31.注册的业务+登录业务】
10 1
|
1天前
|
存储 JavaScript API
vue尚品汇商城项目-day05【33.token令牌理解+34.用户登录携带token获取用户信息+35.退出登录】
vue尚品汇商城项目-day05【33.token令牌理解+34.用户登录携带token获取用户信息+35.退出登录】
7 0
|
1天前
|
JavaScript API 数据安全/隐私保护
vue尚品汇商城项目-day05【36.导航守卫理解】
vue尚品汇商城项目-day05【36.导航守卫理解】
7 0
|
1天前
|
JavaScript API
vue尚品汇商城项目-day06【37.获取交易数据+38.用户地址信息展示+39.交易信息展示及交易页面完成+40.提交订单+41.支付组件内获取订单号与展示支付信息】
vue尚品汇商城项目-day06【37.获取交易数据+38.用户地址信息展示+39.交易信息展示及交易页面完成+40.提交订单+41.支付组件内获取订单号与展示支付信息】
6 0
|
1天前
|
JavaScript 前端开发
vue尚品汇商城项目-day06【vue插件-42.支付页面中使用ElementUI以及按需引入】
vue尚品汇商城项目-day06【vue插件-42.支付页面中使用ElementUI以及按需引入】
6 0
|
JavaScript 测试技术 容器
Vue2+VueRouter2+webpack 构建项目
1). 安装Node环境和npm包管理工具 检测版本 node -v npm -v 图1.png 2). 安装vue-cli(vue脚手架) npm install -g vue-cli --registry=https://registry.
1039 0
|
10天前
|
JavaScript
vue组件中的插槽
本文介绍了Vue中组件的插槽使用,包括单个插槽和多个具名插槽的定义及在父组件中的使用方法,展示了如何通过插槽将父组件的内容插入到子组件的指定位置。
|
8天前
|
JavaScript
vue消息订阅与发布
vue消息订阅与发布
|
5天前
|
JavaScript
理解 Vue 的 setup 应用程序钩子
【10月更文挑战第3天】`setup` 函数是 Vue 3 中的新组件选项,在组件创建前调用,作为初始化逻辑的入口。它接收 `props` 和 `context` 两个参数,内部定义的变量和函数需通过 `return` 暴露给模板。`props` 包含父组件传入的属性,`context` 包含组件上下文信息。`setup` 可替代 `beforeCreate` 和 `created` 钩子,并提供类似 `data`、`computed` 和 `methods` 的功能,支持逻辑复用和 TypeScript 类型定义。
26 11
|
8天前
|
JavaScript 前端开发 IDE
Vue学习笔记5:用Vue的事件监听 实现数据更新的实时视图显示
Vue学习笔记5:用Vue的事件监听 实现数据更新的实时视图显示
下一篇
无影云桌面