创建工程


vue-cli 创建

  1. vue-cli 版本需要 4.5.0 以上版本
  2. vue 版本查看 vue -V
  3. 创建
    1. 目录下执行 vue create 项目名
    2. 选择 vue3

vite 创建

  1. npm init vite-app 项目名
    1. 作用:使用 vite-app(包) 创建项目
    2. 等同于:npx create-vite-app 项目名
      1. 使用 npx 工具
      2. 补全包名 create-vite-app
  2. 安装依赖:npm i
  3. 执行:npm run dev

npx 工具

  1. 作用:执行Node软件包的工具
  2. 执行方式(默认)
    1. 检查是否有要执行的包(create-vite-app)
    2. 若存在,则执行此包
    3. 若不存在,则表示未安装,npx 将安装此包(create-vite-app),并执行它

配置文件


main.js

vue2:引入的是 vue 构造函数

1
2
3
4
5
6
7
8
import Vue from 'vue'
import App from './App.vue'

const vm = new Vue({
render:h => h(App)
})
// 挂载
vm.$mount('#app')

vue3:引入的是 createApp 工厂函数

app 实例vm 实例 区别:app 实例 更精简

1
2
3
4
5
6
import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)
// 挂载
app.mount('#app')

规范


.vue 文件


template 模板

template 模板结构中可以不用跟标签,可以多个标签同时作为 template 子元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// .vue 文件

<template>
<h1>...</h1>
<p>...</p>
</template>

<script>
export default{...}
</script>

<style>
...
</style>

API

官方资料:https://cn.vuejs.org/api/


组合式 API (Composition)

说明

官方资料:https://cn.vuejs.org/guide/extras/composition-api-faq.html

  1. 是一系列API的集合,可以按需引入需要的API
  2. 使用函数而不是声明选项的方式书写 Vue 组件
  3. 需要配合 setup() 函数<script setup> 标签 使用
优势

官方资料:https://cn.vuejs.org/guide/extras/composition-api-faq.html

  1. 可以封装 组合式函数,实现更好的逻辑复用
  2. 选项式 API,功能和数据在代码中分离,不方便管理。组合式 API 可以根据用户 自由组织代码
  3. 组合式 API 主要利用基本的变量和函数,因此 支持 TypeScript 语法
  4. 可以 压缩代码体积
    1. 因为本地变量的名字可以被压缩,但对象的属性名则不能。
    2. <script setup> 中都是变量
    3. 选项式 API 的 this 是对象

setup( )

官方资料:https://cn.vuejs.org/api/

  1. 类型:生命钩子函数,Vue3 中的一个新配置项
  2. 作用:数据、方法等等,均要配置在 setup( ) 中定义
  3. 执行时机:在 beforeCreate 之前执行一次
  4. this:为 undefined
  5. 参数:
    1. props(参数1):代理对象,父组件自定义的属性,并且子组件进行 props 接收的属性
    2. context(参数2):上下文
      1. attrs:父组件自定义的属性,但是子组件未进行 props 接收的属性,相当于 vue2 中的 this.$attrs
      2. slots:接收的插槽内容,相当于 vue2 中的 this.$slots
        1. 值:值为代理对象,属性为调用子组件时定义的各子元素(插入插槽的元素)
        2. 匿名插槽为 default
        3. 调用具名插槽需要使用 v-slot:name值
      3. emit:方法,用来触发自定义事件
        1. 先通过 emits 配置项接收自定义事件(类似于 props)(不接受也能用会有提示)
        2. 触发自定义事件 context.emit("事件名", 参数1, 参数2)
        3. 除了 setup( ) 其他地方触发自定义事件 this.$emit("事件名", 参数1, 参数2)
  6. 返回值:
    1. 返回对象:对象中的属性、方法在模板中可以直接使用。(未定义为响应数据只能展示,模板中不能动态修改)
    2. 返回渲染函数:可以自定渲染内容。(不常用)
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      <template>
      <h1>小明</h1>
      <p>去哪里</p>
      </template>

      import { h } form "vue"
      import { ref } form "vue"

      export default{
      setup(){
      let name = ref("xx")
      let age = ref(16)

      function ff(){
      name.value = "小刚"
      age.value = 20
      console.lot(name,name.value)
      }
      // 返回对象
      return {
      name,
      age,
      ff
      }
      // 返回渲染函数
      return h => h("h1","yy") // 页面最终显示一个是yy内容的 h1 标签
      }
      }

注意事项

  1. 尽量不与 vue2 配置混用(data、methods)
    1. vue2 配置(data、methos、computed…)中可以访问到 setup 中返回的属性和方法
    2. 但是 setup 中不能访问到 vue2 配置(data、methods、computed…)
    3. 如果有重名,setup 优先
  2. setup 不能是一个 async 函数,因为这样 return 返回值就不是一个普通对象了,模板看不到 return 对象中的属性
    • 如果组件被引用,且父组件中使用了异步引入,并且使用 <suspense> 组件进行了处理,则子组件 setup 可以定义为 async 函数,return 返回一个 proxy 对象,对象成功值为响应数据

定义响应数据


ref( )

作用

将基本类型数据包装成为可以进行响应式的对象 响应式引用,使得 该对象内部值 变化能够自动触发视图更新

返回值

  • 返回值的称呼:响应式引用响应式值
  • 返回值称呼解释:
    1. 响应式引用 这个词可以更明确地表达出这个对象的作用,即是一个引用(对象),指向一个基本类型的值,而这个引用具有响应式的特性。
    2. 响应式值 这个词则更加强调这个对象所包装的是一个值,而不是一个对象

注意事项

  1. 类型限制
    • 只能使用基本类型数据生成 响应式引用,此 响应式引用 中被包装的基本类型数据就具有响应式能力
    • 也可以包装对象,但是包装对象时底层默认调用的是 reactive 函数,获取此 响应式引用 的内部值,将获取到的是一个 响应式对象
  2. 访问方式
    1. javascript代码中
      • 通过 响应式引用value 属性 let a = refobj.value 获取 响应式引用 内部值
    2. 模板中
      • 直接使用 响应式引用 {{ refobj }}
  3. 更新方式
    • 直接修改 响应式引用value 属性值 refobj.value++
  4. 响应的数据只针对 响应式引用 的内部值
    1
    2
    3
    4
    5
    6
    7
    // a是一个普通的基本类型数据,它本身不是响应式对象或者响应式引用,只有通过ref函数或者reactive函数创建的对象或者引用才具有响应式能力。
    // 基本类型数据传参或赋值,则是一个新的值
    // 代码中,b是一个响应式引用,当你改变b.value的值时,只会更新b自己的状态,而不会影响到a的值。

    let a = 2
    let b = ref(a)
    b.value++ // a的值不会改变

用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
<div> {{ refobj }} </div>   // 不需要.value


import { ref } form 'vue' // 引入 ref

export default{
setup(){
let refobj = ref(123) // 生成响应式引用
let a = refobj.value // 获取响应式引用内部值
refobj.value++ // 更新响应式引用内部值
}
}


reactive( )

作用

将一个普通的 JavaScript 对象转化为一个 响应式对象,使得 该对象属性 变化能够自动触发视图更新

返回值

  1. 返回值称呼:响应式对象
  2. 实现原理:将 对象类型 包括 数组 数据处理为响应式的 proxy 代理对象

注意事项

  1. 响应式对象 是深度监听的,即改变响应式对象的任意后代属性,都会进行响应
  2. 响应式对象 属性的传递
    1. 响应式对象 其属性是 对象,当此属性作为值进行传递【赋值/传参】,变量接收的是此属性引用,最终改变的还是 响应式对象 的属性,因此 响应式对象 依旧可以被监听到
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      // 这段代码中,a.job.sex的值会发生改变么?--会!!
      // 因为a.job返回的是对job对象的引用,所以在更改b.sex时,实际上是更改a.job对象中的属性。
      // 由于a.job是响应式对象的属性,所以更改会被跟踪并触发响应式更新。
      // 因此,a.job.sex的值会发生改变

      let a = reactive({
      job:{
      sex:1
      }
      })
      let b = a.job
      b.sex++
    2. 响应式对象 其属性是 基本类型数据 ,当此属性作为值进行传递【赋值/传参】,变量接收的是一个新值,更改变量的值不会使原 响应式对象 响应
      • 原理:响应式系统在创建响应式对象时会将其属性的值通过 Object.defineProperty 方法转换为 getter 和 setter 形式
        1
        2
        3
        4
        5
        6
        7
        8
        9
        // 这段代码中,a.job.sex的值会发生改变么?--不会!!
        // 因为当取出响应式对象的基本类型属性赋值给其他变量时,变量接收到的是一个调用 getter 获取的新基本类型值。但是对属性的赋值调用的是 setter 会触发响应能力
        // 所以对b的操作不会影响a.age的值

        let a = reactive({
        age:12
        })
        let b = a.age
        b++

用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
{{ p.name }} // 访问
</template>

<script>
import { reactive } form "vue" // 引入api

export default{
setup(){
let p = reactive({ // 实例化响应式对象
name:"xxx"
})
console.log(p.name) // 访问和赋值触发响应
return {p}
}
}
</script>



ref 对比 reactive

定义的数据

  • ref( ) 用来定义:基本数据类型
  • reactive( ) 用来定义:对象(或数组)类型数据
  • 备注:ref( ) 也可以用来定义 对象(或数组)类型数据,它内部会自动通过 reeactive( ) 转为 代理对象

实现原理

  • ref( ) 通过 Object.defineProperty( )getset 来实现响应式(数据劫持)
  • reactive( ) 通过使用 Proxy 来是实现响应式(数据劫持),并通过 Reflect 操作 源对象 内部数据

使用方式

  • ref( ) 定义的数据:操作数据需要 .value,读取数据时 模板中 直接读取 不需要 .value
  • reactive( ) 定义的数据:操作数据与读取数据 均不需要 .value

toRef/toRefs

作用

将响应式对象的基本类型属性转换为响应式引用的函数

疑问

  1. 用处是什么
    • 响应式对象的基本类型属性,如果进行赋值、传参,变量接收到的数据将是一个新的基本类型数据,因此对变量的修改并不会触发响应式对象的响应能力
    • 使用 toRef()/toRefs 将响应式对象的属性转换为响应式引用,并且对此响应式引用的内部值进行更改,也会影响响应式对象的属性,这是 ref() 做不到的
  2. 为什么不用 ref() 函数进行转换
    • 如果将响应式对象的数据作为参数,调用 ref() 函数生成响应式引用,ref() 接收到的也是一个新的基本类型数据。
    • 这个响应式引用将是一个独立的数据,响应能力只针对 ref() 返回的此响应式引用本身,并不会响应式改变响应式对象的属性值

toRef 和 toRefs 区别

  1. toRef()
    1. 通过 响应式对象 的某一个属性,返回一个 响应式引用
    2. 如果此属性是对象,则返回的 响应式引用 的 value 属性,指向实参 响应式对象 的对应属性
  2. toRefs()
    1. 通过 响应式对象 返回由其属性生成的多个 响应式引用 组成的对象
    2. 属性名是 响应式对象 的属性名,值是对应转换的 响应式引用
    3. 如果此属性是对象,则返回的 响应式引用 的 value 属性,指向实参 响应式对象 的对应属性

注意事项

  1. toRefs() 转换后的属性,需要在模板中使用 .value 来访问其值
  2. 如果直接将一个普通对象的属性传给 toRef,会抛出错误

用法

toRef()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<div>{{ aa.age }}</div> // 引用式对象的属性会响应变化
<button v-bind:click="bb++">按钮+</button> // 直接调用转换后的响应式引用
</template>

import { reactive,toRef } from "vue" // 引用API

export default {
setup(){
let aa = reactive({
name:"小明",
age:16
})
let bb = toRef(aa,"age") // 参数1:响应式对象。参数2:需要转换的属性(字符串)
return {
aa,
bb
}
}
}
toRefs()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<div>{{ aa.age }}</div> // 引用式对象的属性会响应变化
<button v-bind:click="bb.age.value++">按钮+</button> // 直接调用对应属性的响应式引用,且必须使用 value 属性
</template>

import { reactive,toRef } from "vue" // 引用API

export default {
setup(){
let aa = reactive({
name:"小明",
age:16
})
let bb = toRefs(aa) // 参数:响应式对象
return {
aa,
bb
}
}
}

shallowRef/shallowReactive

作用

进行浅层的数据响应,深层的数据不具有响应能力

  1. shallowRef
    1. 用于生成浅层响应式引用
    2. 只对基本类型数据有效
      1. 如果参数是对象,不会调用 reactive
      2. 生成的响应式引用其内部值,value 指向的只是普通object对象,不是 响应式对象
      3. 因此修改此对象属性不具有响应效果
    3. 直接修改响应式引用的内部值 value,会有响应式效果
      1. 即:refobj.value = xxx,此种情况会触发响应
      2. 参数是对象,直接修改响应式引用的内部值 refobj.value = xxx,也会触发响应
  2. shallowReactive
    1. 用于生成浅层响应式对象
    2. 响应式对象 属性的修改,具有响应效果
    3. 响应式对象 属性的属性…的修改,不具有响应效果
1
2
3
4
5
6
7
8
9
import { shallowRef,shallowReactive } from 'vue'

let a = shallowRef(11) // 只对基本类型数据有效
let b = shallowReactive({
name:"123", // 具有响应效果
obj:{ // 具有响应效果(如果修改obj的值 b.obj = {..} | b.obj = 123)
sex:456 // 不具有响应效果
}
})

readonly/shallowReadonly

作用:传入参数 响应式对象响应式引用 ,返回一个 新的 只读的 响应式对象

  • readonly
    1. 新的 响应式对象 的所有属性或值,都不具有响应效果
    2. 实参为 响应式引用,返回的新的 响应式对象 的用法和 响应式引用 用法相同。即:代码中用 value 属性,模板中不需要 value
  • shallowReadonly
    1. 实参为 响应式对象
      1. 返回的新的 响应式对象 的子属性不具有响应式,但是其后代属性具有响应效果
      2. 并且响应效果关联传入的实参响应式对象。即修改孙子属性,新响应式对象触发响应,传入的实参响应式对象也会触发响应
    2. 实参为 响应式引用
      1. 返回的新的 响应式对象 的内部值,不具有响应式效果
      2. 返回的新的 响应式对象 的用法和 响应式引用 用法相同。即:代码中用 value 属性,模板中不需要 value
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

<template>
<div>b.obj.sex</div> // 实参响应式对象触发响应效果
<button v-bind:cleck="f.obj.sex++"></button> // 修改新的响应式对象的孙子属性
</template>

import {readonly,shallowReadonly} from 'vue'

export default{
setup(){
let a = ref(0)
let b = reactive({
name:"xxx",
obj:{
sex:123
}
})

let c = readonly(a) // 修改c的值,a的内部值不具有响应效果
let d = readonly(b) // 修改d的属性,b的属性不具有响应效果

let e = shallowReadonly(a) // 修改e的值,a的内部值不具有响应效果
let f = shallowReadonly(b) // 修改f的子属性,b的子属性不具有响应效果,但是修改f的孙子即之后属性,b的对应属性具有相应效果

return{
b,
f
}
}
}

toRaw/markRaw

toRaw

作用:使用 响应式对象 作为参数,返回一个数据相同的非响应式普通对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

import { ref,reactive,toRaw,markRaw } from "vue"

export default {
setup(){
let b = reactive({
name:"xxx",
age:12,
obj:{
sex:123
}
})
let c = toRaw(b) // 返回一个数据相同的普通对象

return {
b,c
}
}
}

markRow

作用:标记一个 普通对象,此对象将来不管是添加给 响应式对象 作为属性,还是使用 reactive 都不会是响应式

注意事项

此对象某属性是 响应式对象响应式引用,即使对象被 markRow 标记,对象依旧可以被转换为 响应式对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<template>
<div>{{ c.age }}</div>
<button v-bind:click="ff">按钮+</button>
</template>

import { ref,reactive,toRaw,markRaw } from "vue"

export default {
setup(){
let b = reactive({
name:"xxx",
age:12,
obj:{
sex:123
}
})
let c = {
age:18,
//obj:ref(0) // 如果属性是响应式,则此对象依旧可以被转换为响应式对象
}
markRaw(c) // 标记为普通对象
c = reactive(c) // 对象不会转换成响应对象

function ff(){
c.age++ // 不具有响应能力
}

return {
b,ff,c
}
}
}

customRef

作用

自定义 ref 函数,用来定制生成的数据其响应方式。即:可以自定义数据实现响应时的额外操作

用法
  1. 引入 customRef 函数
  2. 自定义函数
  3. 函数中调用 customRef 函数
    1. 参数:回调函数
      • 回调函数的参数
        1. track():用于追踪数据,将组件实例和数据进行关联
        2. trigger():触发组件重新渲染,渲染时根据依赖列表重新加载数据
      • 回调函数的返回值:具有 getter/setter 的对象
        1. getter:自定义响应式引用 获取数据时调用
        2. setter:自定义响应式引用 修改数据时调用
    2. 返回值:生成的 自定义响应式引用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { customRef } from 'vue'                 // 引入API

export default {
setup(){
function ff (value){ // 自定义的ref函数
return customRef((track,trigger)=>{ // 返回值为调用 customRef 函数的返回值
return { // customRef 回调函数返回值为 getter/setter 对象
get(){
track() // 追踪数据
return value
},
set(newValue){
value = newValue
trigger() // 触发渲染
}
}
})
}

let a = ff(0) // 生成响应式数据

return {
a
}
}
}

计算属性


computed

  1. 作用:计算属性
  2. 引入:import {computed} from vue
  3. 特征:computed 是一个函数
  4. 定义:在 setup( ) 配置项中进行调用
  5. 执行时机
    1. 初始化的时候
    2. 依赖数据发生改变时候

完整定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{{person.name}}
{{person.age}}
<input v-module="person.title">

`import {reactive,computed} from 'vue'` // 必须引入

export default{
setup(){
let person = reactive({
name:"小明",
age:16
})

person.title = computed({ //将计算属性给到响应数据,可以通过响应数据直接使用
get(){
return person.name+":"+person.age
}
set(val){
person.name = val
}
})

return {
preson
}
}
}

简写(只能读取,回调函数不能传参,会报错)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{{person.title}}

`import {reactive,computed} from 'vue'` // 必须引入

export default{
setup(){
let person = reactive({
name:"小明",
age:16
})

person.title = computed(()=>{ // 回调函数不能传参,会报错
return person.name+":"+person.age // 只能读取
})

return {
preson
}
}
}

监视属性


watch

  1. 作用:数据监视
  2. 引入:import { watch } from vue
  3. 特征:watch 是一个函数
  4. 定义:在 setup( ) 配置项中进行调用

监视 ref 响应数据

  • 参数
    1. 监听的属性
    2. 回调函数
    3. 配置项
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      // 引入需要的函数
      import { ref,watch } from 'vue'

      export default {
      setup(){
      // 定义 ref 响应式数据
      let sum = ref(0)
      let msg = ref(0)
      let obj = ref({
      name:"小明",
      age:12
      })

      // 监听一个 ref 响应式数据
      watch(sum,(newValue,oldValue)=>{
      console.log("新数据",newValue)
      console.log("旧数据",oldValue)
      })

      // 监听多个 ref 响应式数据
      watch([sum,msg],(newValue,oldValue)=>{
      console.log("新数据",newValue) // 新数据组成的数组,下标与 参数1 中监听的属性所在下标相对应
      console.log("旧数据",oldValue) // 旧数据组成的数组,下标与 参数1 中监听的属性所在下标相对应
      })

      // 配置监听方式
      watch([sum,msg],(newValue,oldValue)=>{
      console.log("修改数据",newValue,oldvalue)
      },{
      immediate:true // 初始就监听一次
      })

      // 监听 ref 定义的对象响应数据【方法一】
      // ref 定义对象类型,其实调用了 reactive ,因此 obj.value 为 Proxy 类型对象
      watch(obj.value,(newValue,oldValue)=>{ // obj.value 等同于监视 reactive 响应数据
      console.log("新数据",newValue)
      console.log("旧数据",oldValue)
      })

      // 监听 ref 定义的对象响应数据【方法二】
      // ref 定义对象类型,其实调用了 reactive ,因此 obj.value 为 Proxy 类型对象
      watch(obj,(newValue,oldValue)=>{
      console.log("新数据",newValue)
      console.log("旧数据",oldValue)
      },{
      deep:true // 需要开启深度监听才可实现
      })
      }
      }

监视 reactive 响应数据

监视 reactive 响应数据的注意事项:
  1. 无法获取 oldValue 旧数据,newValue 和 oldValue 得到的数据都是新改变的数据。
  2. 强制开启了深度监视(监听属性的属性的属性…),即使配置监听参数(deep:false),依旧无效
  3. 监听 reactive 响应数据的某属性,属性必须为函数返回值
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    // 引入需要的函数
    import { reactive,watch } from 'vue'

    export default {
    setup(){
    // 定义 reactive 响应式数据
    let sum = reactive({
    name:"小明",
    age:12,
    obj:{
    tx:23
    }
    })
    // 监听 reactive 响应式数据
    watch(sum,(newValue,oldValue)=>{
    console.log("新数据",newValue) // 获取新改变的数据 sum
    console.log("旧数据",oldValue) // 获取的还是新数据 sum
    })

    // 监听 reactive 响应式数据中的某一个属性
    watch(()=>sum.name,(newValue,oldValue)=>{
    console.log("新数据",newValue) // 获取的是新数据 sum.name
    console.log("旧数据",oldValue) // 获取的是旧数据 sum.name
    })

    // 监听 reactive 响应式数据中的多个属性
    watch([()=>sum.name,()=>sum.age],(newValue,oldValue)=>{
    console.log("新数据",newValue) // 新数据组成的数组,下标与 参数1 中监听的属性所在下标相对应 [sum.name,sum.age]
    console.log("旧数据",oldValue) // 旧数据组成的数组,下标与 参数1 中监听的属性所在下标相对应 [sum.name,sum.age]
    })

    // 监听 reactive 响应式数据中的后代对象属性(属性(obj)=>属性(obj)=>属性(obj)...),必须配置配置项【deep:true】
    watch(()=>sum.obj,(newValue,oldValue)=>{
    console.log("新数据",newValue) // 都获取的是新数据
    console.log("旧数据",oldValue) // 都获取的是新数据
    },{
    deep:true // 必须配置,如此才可以进行单独监听 reactive 响应式数据的后代对象属性 sum.obj
    })

    // 监听 reactive 响应式数据中的后代基本类型属性(属性(obj)=>属性(obj)=>属性(基本类型)...),无需配置【deep】
    watch(()=>sum.obj.tx,(newValue,oldValue)=>{
    console.log("新数据",newValue) // 获取的是新数据 sum.obj.tx
    console.log("旧数据",oldValue) // 获取的是旧数据 sum.obj.tx
    }) // 无需配置配置项【deep:true】
    }
    }

watchEffect

  1. watchEffect 是一个函数
  2. 参数为一个回调函数
  3. 作用:根据回调函数中用到的属性,自动进行监视
    1. 回调函数中用到了某属性,当属性值发生改变,回调函数将被触发
    2. 只针是基本类型的属性,当属性的值发生改变,并且回调函数使用了此属性,则回调函数才会被触发
    3. 当属性是引用类型,回调函数中使用了此属性,但是修改的是此引用属性的属性,则回调函数不会被触发
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import { ref,reactive,watchEffect } from 'vue'

<button v-on:click="sum++">按钮</button>
<button v-on:click="obj.gz++">按钮</button>

export default {
setup(){
let sum = ref(0)
let obj = reactive({
name:"张三",
job:{
gz:1
}
})

// 监听属性
watchEffect(()=>{
let a = sum.value // 回调函数中用到了 sum,当 sum 值发生改变就会触发此回调函数
let b = obj // 点击按钮修改的是 obj.job.gz 的值,回调函数引用的是 obj ,因此点击按钮修改数据【不会触发此回调函数】
let c = obj.jbo.gz // 点击按钮修改的是 obj.job.gz 的值,并且回调函数引用的是 obj.job.gz,所以当点击按钮修改数据【会触发此回调函数】
})

return {
sum,
obj
}
}
}

数据通信


provide/inject

作用
  1. 用于祖组件向后代组件传递数据(不能用于子组件向祖先组件传递数据)
  2. 组件之间是嵌套调用的关系 祖>子>后代
  3. 父子组件一般使用 props 进行数据传递
用法
  1. 祖组件引入 provide 函数
  2. 祖组件调用 provide("别名",数据) 提供传输的数据
  3. 后代组件引入 inject 函数
  4. 后代组件调用 inject("别名") 获取祖组件提供的数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//---------------祖组件---------------

import { provide } from 'vue'

export default {
setup(){
let a = ref(0)
provide("aa",a)
}
}

//----------------end-----------------

//---------------后代组件---------------

import { inject } from 'vue'

export default {
setup(){
let aa = inject("aa")
console.log("aa")
}
}

//---------------- end-----------------


props

  1. 使用选项式API,则使用 props 配置项进行配置
  2. 使用组合式API配合 <script setup> 标签,则使用 defineProps 函数接收 props 数据
1
2
3
4
5
6
7
8
9
10
<script setup>
import { defineProps } from 'vue'

const props = defineProps({
msg: String,
msg2:Function
})

console.log(props.msg) // 访问 props 中的 msg 属性
</script>

父组件获取子组件实例和数据

  1. 父组件 html 中调用子组件
  2. 子组件定义实例上定义 ref 属性
  3. 子组件如果使用 <script setup> 标签,则需要手动暴露数据
    • defineExpose({ aa,bb,cc })
  4. 父组件通过定义相同 ref 属性名变量的响应数据获取子组件实例
1
2
3
4
5
6
7
8
9
10
11
<!-- 父组件 -->

<script setup>
let zzj = ref(null)
console.log(zzj.value) // 获取子组件实例
console.log(zzj.value.fn()) // 调用子组件中方法
</script>

<template>
<child ref="zzj"></child>
</template>
1
2
3
4
5
6
7
8
9
10
<!-- 子组件 -->

<script setup>
function fn(){
console.log("yyyyyyy")
}
defineExpose({ // 手动暴露子组件中数据
fn
})
</script>

判断响应式数据

1
2
3
4
5
6
7
8

import {isRef,isReactive,isReadonly,isProxy} from 'vue'

isRef(...) // 判断值是否是 `ref` 创建的响应式引用
isReactive(...) // 判断值是否是 `reactive` 创建的响应式对象
isReadonly(...) // 判断值是否是 `readonly` 创建的只读的响应式对象
isProxy(...) // 判断值是否是 `reactive` 或 `readonly` 创建的代理数据


hook

官方资料:https://cn.vuejs.org/guide/reusability/composables.html

hook 是一个函数,叫做 组合式函数 ,是使用 组合式API 封装的函数

  1. 利用 Vue 的组合式 API 来封装的 有状态逻辑的函数
  2. 特征:
    1. 普通函数在调用后,只能获取函数当前处理的数据
    2. 使用组合式 API 生成的 组合式函数,执行一次后可以持续修改或判断其返回的值(因为使用了相应的组合式API,使数据成为了动态绑定数据等效果)
  3. 用法:
    1. 创建 hook 目录(非强制)
    2. 目录中创建相关 js 文件,文件名常以 use 开头(非强制)
    3. js 文件引入相关 组合式API
    4. js 文件中暴露使用了 组合式API 的自定义函数 组合式函数
    5. 其他组件就可以引用这个 组合式函数

组件


fragment

vue2 中需要一个根节点,但是 vue3 可以有多个根节点,原因是在虚拟dom中有一个 标签作为根节点,此节点不参与渲染


teleport

作用

将其定义的组件在指定元素下渲染。作为指定元素的最后一个子元素

特点
  1. <teleport> 只改变了渲染的 DOM 结构,它不会影响组件间的逻辑关系,只会影响 DOM 结构和 CSS 样式
  2. <teleport> 可以有多个根元素
  3. to 的值可以是一个 CSS 选择器字符串,也可以是一个 DOM 元素对象
  4. to 属性值发生改变就会重新渲染。即:to 属性值绑定响应数据,改变值就可随时改变元素所在位置
1
2
3
4
5
6
7
8
<!-- 在 body 元素下渲染元素 -->
<Teleport to="body">
<div v-if="open" class="modal">
<p>Hello from the modal!</p>
<button @click="open = false">Close</button>
</div>
</Teleport>


suspense

作用

当子组件是通过异步加载方式进行加载,可以控制子组件未加载成功的显示内容

特点
  1. 使用异步加载子组件,并且使用 <suspense> 进行处理,子组件的 setup 可以是 async 函数,返回值为 proxy 对象,成功状态值为 响应类型数据
  2. 为什么使用:当子组件未加载完成,祖先组件都将不会显示。使用之后可以先显示祖先组件
用法
  1. 导入异步引入组件函数 import { defineAsyncComponent } from 'vue'
  2. 调用函数异步引入组件 const child = defineAsyncComponent(()=>import("./child.vue"))
  3. 使用 <suspense> 组件处理子组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

<suspense>
<template v-slot:default> // 当组件加载成功显示组件 v-slot:default 固定写法
<child/>
</template>
<template v-slot:fallback> // 当组件未加载成功显示组件 v-slot:default 固定写法
请稍等,还未加载完成!
</template>
</suuspense>


import { defineAsyncComponent } from "vue" // 导入用来异步引入组件的函数

const child = defineAsyncComponent(()=>import("./child.vue")) // 调用函数,参数为回调函数,返回值为 import 函数返回值
export default{
components:{child} // 绑定组件
}

其他改变

全局组件转移
1
2
3
4
5
6
7
8
9
// vue2                     // vue3
Vue.config.xxx app.config.xxx
Vue.config.productionTip 移除(生产环境提示)
Vue.component app.component
Vue.directive app.directive
Vue.mixin app.mixin
Vue.use app.use
Vue.prototype app.config.globalProperties

data配置项

data配置项必须是函数

过渡类名
1
2
3
4
5
/* vue2 */      /* vue3 */
.v-enter .v-enter-from
.v-enter-to .v-enter-to
.v-leave .v-leave-from
.v-leave-to .v-leave-to
移除
  1. keyCode作为 v-on 的修饰符,也不再支持 config.keyCodes
  2. v-on.native 修饰符【 vue3中子组件只要不使用 emits 接收就不认为是自定义事件 】
  3. 过滤器 filter 【 vue3中都用计算属性 】

实现原理


响应式原理

vue2 响应式原理

  • 对象类型:通过 Object.defineProperty( )【get/set】 对属性的读取、修改进行拦截(数据劫持)
    • 不会触发响应的情况:通过调用对象属性的方式,添加/删除 属性,界面不会更新
  • 数组类型:通过重写数组的相关方法来实现拦截
    • 不会触发响应的情况:直接通过数组下标修改数组元素,界面不会更新

vue3 响应式原理

  • 通过 Proxy(代理):拦截对象中任意属性的变化
    • 对象属性进行 增删改查 操作时,可自定义执行相关操作
    • 不使用 Object.defineProperty 进行数据劫持,是因为会出现无法触发响应的情况
  • 通过 Refiect(反射):对 源对象 的属性进行操作
    • 如 Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
let person = {
name:"小明",
age:16
}

const p = new Proxy(person,{
// 【读取】:属性时调用
// target:操作的对象(person)
// propName:操作的属性名(string类型)
get(target,propName){
console.log(target,propName) // {name:"小明",age:16},"name"
return Reflect.get(target,propName) // 相当于 target[propNamme]
},

// 【修改、添加】:属性时调用
// target:操作的对象(person)
// propName:操作的属性名(string类型)
// value:添加修改的属性值
set(target,propName,value){
console.log(target,propName,value) // {name:"小明",age:16},"name","小刚"
Reflect.set(target,propName,value) // 相当于 target[propNamme] = value
}

// 【删除】:属性时的操作
deleteProperty(target,propName){
console.log("删除了某个属性")
return Reflect.deleteProperty(target,propName) // // 相当于 delete target[propNamme]
}
})


生命周期

优先级
  1. setup
  2. beforeCreate(配置项)
  3. created(配置项)
  4. onBeforeMount
  5. beforeMount(配置项)
  6. onMounted
  7. mounted(配置项)
  8. onBeforeUpdate
  9. updated(配置项)
  10. onBeforeUnmount
  11. beforeUnmount(配置项)
  12. onUnmounted
  13. unmounted(配置项)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<script>

// 引入需要的 API
import { onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted } from "vue"

export default {
setup(){ // 初始化生命周期、事件、数据监测、数据代理
...

// 通过组合式API,设置生命周期钩子函数
// beforeCreate() 和 create() 没有对应组合式 API,这两个钩子函数就对应 setup() 钩子函数

onBeforeMount(()=>{...}) //生成虚拟DOM之后
onMounted(()=>{...}) //虚拟DOM挂载到页面之后

onBeforeUpdate(()=>{...}) //修改数据之后
onUpdated(()=>{...}) //修改数据并且渲染到页面后

onBeforeUnmount(()=>{...}) //调用销毁Vue实例函数,但在实际销毁Vue实例之前
onUnmounted(()=>{...}) //实际销毁Vue实例之后
},


// 通过配置项方式设置生命周期钩子函数

beforeCreate(){}, //初始化生命周期、事件之后
created(){}, //初始化数据监测、数据代理之后

beforeMount(){}, //生成虚拟DOM之后
mounted(){}, //虚拟DOM挂载到页面之后

beforeUpdate(){}, //修改数据之后
updated(){}, //修改数据并且渲染到页面后

beforeUnmount(){}, //调用销毁Vue实例函数,但在实际销毁Vue实例之前
unmounted(){} //实际销毁Vue实例之后

}
</script>