Vue3 学习总结笔记 (十三)

1. Vue3 简介


Vue3 代号:One Piece(海贼王) 。

在这里插入图片描述


vue3 比 vue2 的好处:

  • 打包大小减少,渲染速度更快,内存减少。

源码升级:

  • 使用Proxy代替ObjectDefineProperty实现响应式。
  • vue3可以更好的支持TypeScript。

新的特性:

2. 创建Vue3项目工程

2.1 使用Vue-cli创建


前提:确保@vue/cli的版本在4.5.0之前上!

# 查看版本命令
vue -V  或者 vue --version
# 创建一个名为vue3_test的项目
vue create vue3_test
# 注意中间要选择Vue3版本!!!

在这里插入图片描述
在这里插入图片描述

2.2 使用Vite创建Vue3工程


Vite是新一代的前端开发构建工具。和Webpack功能一样。

官方地址:https://vitejs.cn/

vite的优点:

  • 开发环境中,无需打包操作,可快速的冷启动。
  • 轻量快速的热量载(HMR)。
  • 真正的按需编辑,不再等待整个应用编译完成。

在这里插入图片描述
图上前者是先将所有的模块准备好,在搭建好server服务器。后者是先准备好服务器,之后通过请求去找对应的模块,正好与前面反着,这样更加快一点。


命令如下:

# 创建工程
npm init vite-app <project-name>
# 进入工程目录
cd <project-name>
# 安装依赖
npm install
# 运行
npm run dev

3. Vue3 之 分析工程结构


main.js文件结构如下:

//引入的不再是Vue构造函数了,而引用的是createApp就是创建一个应用的工厂函数。
import { createApp } from 'vue'
import App from './App.vue'

//创建应用实例对象-app(类似于之前vue2中的vm,但app比vm更轻)
const app = createApp(App)
console.log('@@@',App)

//mount就是挂载,与vue3不同的是没有了$符号。
app.mount('#app')

setTimeout(()=>{
    //调用unmount可以卸载app应用
    app.unmount('#app')
},3000)

// 注意vue3构想的项目,并不支持vue2的写法!!!
// vue2 的写法,通过el或者$mount来指定容器,通过render来渲染。
// const vm = new Vue({
//     // el:'#app'
//     render:(h)=> h(App)
// })
//
// vm.$mount('#app')

createApp(App)的结构如下:
在这里插入图片描述

组件里面,模板结构可以没有根标签了。
在这里插入图片描述

3. Vue3 之 安装开发者工具


Vue的dev-tools安装包:https://gitee.com/it-sherlock/develop-common-parameters/tree/master/%E5%89%8D%E7%AB%AF/Vue/dev-tools
在这里插入图片描述

4. Vue3 之 setup


Composition API:组合式API

setup是Vue3的新的配置项。

setup是所有Composition API(组合API)的表演舞台。

组件中所用到的:数据,方法等等,均要配置在setup中!!! 而Vue2,数据要配置在data配置项中,方法配置在methods中。


setup可以直接返回数据和方法:
在这里插入图片描述

setup还可以返回渲染函数:
在这里插入图片描述


在vue3项目中,可以配置vue2的data,methods的写法,但是非常不建议!!

vue3的setup是不能够访问到vue2的data,methods等一些数据和方法的!!

总结:
在这里插入图片描述

注意:setup不能使用async修饰。如果加上了,async会将其的返回值包裹为一个promise对象,这样setup的返回值就不是一个单纯的对象了。

5. Vue3 之 ref函数

5.1 ref函数 处理基本数据类型


前提:先引入ref函数,从vue中!

import {ref} from 'vue'

vue2中的ref是作为一个属性,类似于id选择器,打标识用的。

在vue3中,多了一个ref函数,ref函数可以让定义的数据具有响应式。直接在setup定义并且返回的变量,是不具有响应式的,值变化页面并不会变化!

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

对于基本数据类型而言,ref函数用到了ObjectDefineProperty方法来实现的,走的是get和set方法来实现响应式。

5.2 ref函数 处理对象类型


ref函数,对于处理对象数据类型,是用的ES6的Proxy代理对象。

对于对象,直接使用 对象.value.属性名 来触发响应式就可以。

在这里插入图片描述

总结:
在这里插入图片描述

6. Vue3 之 reactive函数


前提:也是在vue中,引入reactive函数

import {reactive} from 'vue'

不要用reactive函数来处理基本数据类型,reactive专门用来处理对象类型!!

ref函数也能够处理对象类型,原因就是它在处理对象的时候底层也去使用了reactive函数。

reactive的底层不再通过ObjectDefineProperty函数达到响应式了。而是通过Proxy实现。
在这里插入图片描述


reactive函数可以支持深层次的对象数据的响应式,也支持数组数据的响应式。
在这里插入图片描述

小知识点:

  • vue2中通过索引修改的数组中的数据是不会触发响应式的,一般在vue2通过$set来触发数据数据的响应式索引。
  • 当然,在vue2中,通过索引项内的某个属性是可以触发响应式的!例如:vm.list[3].a = 456。
  • vue3就弥补了vue2的这个缺点,即便是通过索引赋值也能够触发响应式!

reactive函数使用:

  • 一般形式是:const 代理对象 = reactive(源对象) 就是将源对象加工成为代理对象(Proxy的实例对象,简称proxy对象)。
    在这里插入图片描述

reactive的内部基于ES6的Proxy实现,通过代理对象操作源对象内部数据进行操作。

7. Vue3 之 与vue2进行比较

7.1 Vue2 响应式原理以及注意事项


vue2的数组push,做了两件事,一个是往数组中添加数据,另一个是更新页面数据。

因为Vue2使用的是ObjectDefineProperty方法中的get和set方法,对于获取就用get,对于修改就用set,然而对于添加和删除却是无可奈何的!!

vue2对象添加一个属性:

  • 新增属性不能直接通过 person.sex = ‘女’的方式来添加,这样不会触发响应式!
    在这里插入图片描述
    vue2删除一个属性:
  • 直接通过原生的 delete this.person.name 删除name是不会触发响应式的!
    在这里插入图片描述

vue2数组的操作:
在这里插入图片描述

总结:
在这里插入图片描述

7.2 Vue3 的响应式原理

7.2.1 Proxy对象


有了reactive,vue3就弥补了上面vue2不能直接添加删除,数组索引项不能直接赋值的缺点!
在这里插入图片描述

还是那句话,Object.defineProperty能检测get获取和set设置,但是不能够检测到删除和添加属性。所以还是有小毛病的!
在这里插入图片描述


ES6的 Proxy对象:

  • Proxy对象在window上面,通过window.proxy来查看。
//源数据
let person = {
    name:'张三',
    age:18
}
const p = new Proxy(person,{})
console.log(p)

在这里插入图片描述

//Vue3的响应式,通过配置第二个参数handler对象来达到响应式
const p = new Proxy(person,{
    //读取p的某个属性时调用
    get(target, propName, receiver) {
        console.log(`有人读取了p身上的${propName}属性`)
        console.log(target)
        console.log(propName)
        return target[propName]
    },
    //修改p的某个属性时调用 和 新增p的某个属性时调用
    set(target, propName, value, receiver) {
        console.log(`有人修改了p身上的${propName}属性,我要更新页面了!`)
        console.log(target)
        console.log(propName)
        console.log(value)
        target[propName] = value
    },
    //删除p的某个属性时调用
    deleteProperty(target, propName) {
        console.log(`有人删除了p身上的${propName}属性,我要更新页面了!`)
        //delete返回的是一个布尔值,同时deleteProperty也要返回一个布尔值!!
        let boolean = delete target[propName]
        return boolean
    }
})

7.2.2 Reflect对象


Reflect也是可以对对象的属性进行获取,修改,新增,删除操作。
在这里插入图片描述
在这里插入图片描述


Object.defineProperty与 Reflect的对比:
在这里插入图片描述

Reflect相对比较健壮!!


vue3底层的对对象的增删改查,并不会直接使用target操作,而是通过Reflect函数进行操作如下:

//Vue3的响应式,通过配置第二个参数handler对象来达到响应式
const p = new Proxy(person,{
    //读取p的某个属性时调用
    get(target, propName, receiver) {
        console.log(`有人读取了p身上的${propName}属性`)
        console.log(target)
        console.log(propName)
        return Reflect.get(target,propName)
    },
    //修改p的某个属性时调用 和 新增p的某个属性时调用
    set(target, propName, value, receiver) {
        console.log(`有人修改了p身上的${propName}属性,我要更新页面了!`)
        console.log(target)
        console.log(propName)
        console.log(value)
        Reflect.set(target,propName,value)
    },
    //删除p的某个属性时调用
    deleteProperty(target, propName) {
        console.log(`有人删除了p身上的${propName}属性,我要更新页面了!`)
        //delete返回的是一个布尔值,同时deleteProperty也要返回一个布尔值!!
        let boolean = Reflect.deleteProperty(target,propName)
        return boolean
    }
})

总结:
在这里插入图片描述

8. reactive函数 和 ref函数 有什么区别


在这里插入图片描述

9. 重点注意事项

9.2 回忆Vue2的几个组件属性


$attrs属性的作用:
在这里插入图片描述

$slot的作用:

  • 最终将虚拟dom转为真实dom。
    在这里插入图片描述
  • 一般都会使用template标签加slot属性指定slot名字,这样就能指定安放标签。
  • 指定slot名字两种写法:slot=“xxx” 或 v-slot:xxx 。
    在这里插入图片描述
    在这里插入图片描述

9.3 Vue3 setup的两个注意点


setup的执行时机:

  • 在beforeCreate之前执行一次,this是undefined。
    在这里插入图片描述

setup的props参数使用:

  • 注意:vue3父组件传过来的属性也要将其放到props配置项里面!
    在这里插入图片描述

setup的第二个参数context(上下文):

  • context.attrs的作用:
    在这里插入图片描述

  • context.emit 和 emits配置项的使用:
    在这里插入图片描述

  • context.slots的使用:
    在这里插入图片描述


总结:
在这里插入图片描述

10. Vue3 之 Computed 计算属性


在setup配置项里面,是肯定不会用this的,因为setup在beforeCreate之前会自动执行,打印过this是undefined!

vue2形式的computed也是可以在vue3的项目中使用的,对应的this自然就是vue对象。

vue3将computed变成了一个组合式api!
在这里插入图片描述

vue3的写法如下:

  • 记得导入computed的api。
import {computed} from 'vue'

setup(){
  let person = reactive({
    firstName: '张',
    lastName: '三',
    age: 18,
  })

  //计算属性 简写形式只考虑计算属性读,没有考虑计算属性的改
  // 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
  }
 }

总结:
在这里插入图片描述

11. Vue3 之 watch属性

11.1 Vue2的watch使用


Vue2的watch使用:

watch:{
  //vue2的监听简易写法:直接通过 数据对象名定义函数来监听
  // sum(newValue,oldValue){
  //   console.log('sum的值变化了',newValue,oldValue)
  // },
  //vue2监听完整写法:
  sum:{
    immediate:true, // 配置立即监听属性,一进来就先执行一次。
    deep:true, // 深度监视属性
    handler(newValue,oldValue){
      console.log('sum的值变化了',newValue,oldValue)
    }
}

11.2 Vue3 watch监听ref函数定义的数据


watch监听ref函数如下:

<template>
  <h2>当前求和为:{{sum}}</h2>
  <button @click="sum++">点我+1</button>
  <h2>当前的信息为:{{msg}}</h2>
  <button @click="msg += '!'">修改信息</button>
</template>

<script>
import {ref,watch} from 'vue'

export default {
  name:'Demo',
  setup(){
    //数据
    let sum = ref(0)
    let msg = ref('你好啊')

    //情况一:监视ref所定义一个的响应式数据
    // watch(sum,(newValue,oldValue)=>{
    //   console.log('sum变了',newValue,oldValue)
    // })

    //情况二: 监视ref所定义的多个响应式数据
    //第一个参数:监听的数据。第二个参数:监听的函数。第三个参数:配置项
    watch([sum,msg],(newValue,oldValue)=>{
      //此时的newValue和oldValue就变成了数组
      console.log(newValue) //[0, '你好啊!']
      console.log(oldValue)//[0, '你好啊']
      console.log('sum变了',newValue,oldValue)
    },{immediate:true,deep:true})

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

11.3 Vue3 watch监听reactive函数的数据


监听格式如下:

  • 注意点1:如果watch直接监听的是reactive函数生成的代理对象,是无法正常获取oldValue的!!
//就算用ref函数操作,因为是对象,对于对象ref还是走的reactive函数!
let person = reactive({
  name:'张三',
  age:18
})
//监视reactive所定义的一个响应式数据
watch(person,(newValue,oldVale)=>{
  //注意:这里newValue和oldVale一样,也就是无法获得正常的oldValue
  //这个问题目前无法解决!
  console.log(newValue)//{name: '张三~', age: 19}
  console.log(oldVale)//{name: '张三~', age: 19}
  console.log('person的age变化了')
})

解决办法:如果无法业务上面真的需要获取到oldValue的话,那就只能把这个属性从对象中拿出来,放到ref函数中使用!

//单独把age拿出来,用ref来声明就可以监听到!!
let age = ref(18)

//就算用ref函数操作,因为是对象,对于对象ref还是走的reactive函数!
let person = reactive({
  name:'张三', 
})
//监视reactive所定义的一个响应式数据
watch(age,(newValue,oldVale)=>{
  //注意:这里newValue和oldVale一样,也就是无法获得正常的oldValue
  //这个问题目前无法解决!
  console.log(newValue)
  console.log(oldVale)
  console.log('person的age变化了')
})
  • 注意点2:Vue3的watch强制开启了深度监视(deep配置无效!)。
    在这里插入图片描述

  • 注意点3:监听reactive函数对象的某个属性,步骤如下:

let person = reactive({
  name:'张三',
  age:18,
  job:{
    j1:{
      salary: 20
    }
  }
})
//对象的某个属性,必须要用函数才能监听!!!
watch(()=>person.age,(newValue,oldVale)=>{
  console.log(newValue)
  console.log(oldVale)
  console.log('person的age变化了')
})
  • 注意点4:监听对象的多个属性,形式如下:
//监视reactive所定义的一个响应式数据中的 多个属性!
let person = reactive({
  name:'张三',
  age:18,
  job:{
    j1:{
      salary: 20
    }
  }
})
//那就将其多个函数写成数组的形式
watch([()=>person.age,()=>person.name],(newValue,oldVale)=>{
  console.log(newValue)
  console.log(oldVale)
  console.log('person变化了')
})
  • 注意点5:特殊情况!监听reactive代理对象的对象属性,这个时候就不再是强制深度监视,deep配置就奏效了!!!如果监视的是reactive代理对象,对于这个watch就是强制深度监视!!!
 let person = reactive({
   name:'张三',
   age:18,
   job:{
     j1:{
       salary: 20
     }
   }
 })
 //监听一个对象里面的对象属性,因为我们更改的是对象中的对象的对象数据!!
 //所以必须开启深度监视deep!!
 watch(()=>person.job,(newValue,oldVale)=>{
   console.log(newValue)
   console.log(oldVale)
   console.log('person的job变化了')
 },{deep:true})

总结:
在这里插入图片描述

12. Vue3 之 watch的 .value问题


注意点1:监听ref定义的基本数据类型,不能添加sum.value!!

  • 对于基本数据类型,如果添加上了.value那监听的就是sum.value的这个值!并不是sum这个变量!
//数据
let sum = ref(0)
let msg = ref('你好啊')

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

//监听ref定义的基本数据类型,不能添加sum.value!!
//如果添加上了.value那监听的就是sum.value的这个值!并不是sum这个变量!
watch(sum,(newValue,oldValue)=>{
  console.log('sum的值变化了',newValue,oldValue)
})

注意点2:ref代理对象的形式,通过.value来实现监听的效果。

  • 因为他是refImpl对象(ref函数)里面包含了proxy(reactive函数)对象,所以直接监视对象是监视不到的!
    在这里插入图片描述
  • 更加简单的方法,就是直接配置一个deep:false就可以了。深度监视
let person = ref({
  name:'张三',
  age:18,
  job:{
    j1:{
      salary: 20
    }
  }
})
watch(person,(newValue,oldValue)=>{
  console.log('person的值变化了',newValue,oldValue)
},{deep:true}) // 开启深度监视就能监视到了!

13. Vue3 之 watchEffect函数


watchEffect也是起到监听的效果,但宇watch不同!

  • watchEffect遵循用到了谁就监视谁!
export default {
  name:'Demo',
  setup(){
    //数据
    let sum = ref(0)
    let msg = ref('你好啊')

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

    //watchEffect是用了谁,就监视谁!
    watchEffect(()=>{
      const x1 = sum.value
      const x2 = person.job.j1.salary
      console.log('watchEffect所指定的回调执行了!')
    })

    return {
      sum,
      msg,
      person
    }
  }

总结:

  • computed和watchEffect都是自己使用的数据发生变化就会调用执行,但是还是有区别的!
    在这里插入图片描述

版权声明:本文为IT_Holmes原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。