Vue3
创建工程
vue-cli 创建
- vue-cli 版本需要 4.5.0 以上版本
- vue 版本查看
vue -V - 创建
- 目录下执行
vue create 项目名 - 选择
vue3
- 目录下执行
vite 创建
npm init vite-app 项目名- 作用:使用
vite-app(包)创建项目 - 等同于:
npx create-vite-app 项目名- 使用
npx 工具 - 补全包名
create-vite-app
- 使用
- 作用:使用
- 安装依赖:
npm i - 执行:
npm run dev
npx 工具
- 作用:执行Node软件包的工具
- 执行方式(默认)
- 检查是否有要执行的包(create-vite-app)
- 若存在,则执行此包
- 若不存在,则表示未安装,npx 将安装此包(create-vite-app),并执行它
配置文件
main.js
vue2:引入的是
vue 构造函数
1 | |
vue3:引入的是
createApp 工厂函数
app 实例 和 vm 实例 区别:app 实例 更精简
1 | |
规范
.vue 文件
template 模板
template 模板结构中可以不用跟标签,可以多个标签同时作为 template 子元素
1 | |
API
官方资料:https://cn.vuejs.org/api/
组合式 API (Composition)
说明官方资料:https://cn.vuejs.org/guide/extras/composition-api-faq.html
- 是一系列API的集合,可以按需引入需要的API
- 使用函数而不是声明选项的方式书写 Vue 组件
- 需要配合
setup() 函数或<script setup> 标签使用
官方资料:https://cn.vuejs.org/guide/extras/composition-api-faq.html
- 可以封装 组合式函数,实现更好的逻辑复用
- 选项式 API,功能和数据在代码中分离,不方便管理。组合式 API 可以根据用户 自由组织代码
- 组合式 API 主要利用基本的变量和函数,因此 支持 TypeScript 语法
- 可以 压缩代码体积
- 因为本地变量的名字可以被压缩,但对象的属性名则不能。
<script setup>中都是变量- 选项式 API 的
this是对象
setup( )
官方资料:https://cn.vuejs.org/api/
- 类型:生命钩子函数,Vue3 中的一个新配置项
- 作用:数据、方法等等,均要配置在
setup( )中定义 - 执行时机:在
beforeCreate之前执行一次 this:为undefined- 参数:
props(参数1):代理对象,父组件自定义的属性,并且子组件进行props接收的属性context(参数2):上下文attrs:父组件自定义的属性,但是子组件未进行props接收的属性,相当于vue2 中的 this.$attrsslots:接收的插槽内容,相当于vue2 中的 this.$slots- 值:值为代理对象,属性为调用子组件时定义的各子元素(插入插槽的元素)
- 匿名插槽为
default - 调用具名插槽需要使用
v-slot:name值
emit:方法,用来触发自定义事件- 先通过
emits配置项接收自定义事件(类似于props)(不接受也能用会有提示) - 触发自定义事件
context.emit("事件名", 参数1, 参数2) - 除了
setup( )其他地方触发自定义事件this.$emit("事件名", 参数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 标签
}
}
注意事项
- 尽量不与 vue2 配置混用(data、methods)
- vue2 配置(data、methos、computed…)中可以访问到 setup 中返回的属性和方法
- 但是 setup 中不能访问到 vue2 配置(data、methods、computed…)
- 如果有重名,setup 优先
- setup 不能是一个 async 函数,因为这样 return 返回值就不是一个普通对象了,模板看不到 return 对象中的属性
- 如果组件被引用,且父组件中使用了异步引入,并且使用
<suspense>组件进行了处理,则子组件setup可以定义为async函数,return 返回一个proxy对象,对象成功值为响应数据
- 如果组件被引用,且父组件中使用了异步引入,并且使用
定义响应数据
ref( )
作用
将基本类型数据包装成为可以进行响应式的对象 响应式引用,使得 该对象 的 内部值 变化能够自动触发视图更新
返回值
- 返回值的称呼:响应式引用 或 响应式值
- 返回值称呼解释:
响应式引用这个词可以更明确地表达出这个对象的作用,即是一个引用(对象),指向一个基本类型的值,而这个引用具有响应式的特性。响应式值这个词则更加强调这个对象所包装的是一个值,而不是一个对象
注意事项
- 类型限制 :
- 只能使用基本类型数据生成
响应式引用,此响应式引用中被包装的基本类型数据就具有响应式能力 - 也可以包装对象,但是包装对象时底层默认调用的是
reactive 函数,获取此响应式引用的内部值,将获取到的是一个响应式对象
- 只能使用基本类型数据生成
- 访问方式 :
- javascript代码中 :
- 通过
响应式引用的value属性let a = refobj.value获取响应式引用内部值
- 通过
- 模板中 :
- 直接使用
响应式引用{{ refobj }}
- 直接使用
- javascript代码中 :
- 更新方式 :
- 直接修改
响应式引用的value属性值refobj.value++
- 直接修改
- 响应的数据只针对
响应式引用的内部值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 | |
reactive( )
作用
将一个普通的 JavaScript 对象转化为一个 响应式对象,使得 该对象 的 属性 变化能够自动触发视图更新
返回值
- 返回值称呼:响应式对象
- 实现原理:将
对象类型包括数组数据处理为响应式的proxy 代理对象
注意事项
响应式对象是深度监听的,即改变响应式对象的任意后代属性,都会进行响应响应式对象属性的传递响应式对象其属性是对象,当此属性作为值进行传递【赋值/传参】,变量接收的是此属性引用,最终改变的还是响应式对象的属性,因此响应式对象依旧可以被监听到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++响应式对象其属性是基本类型数据,当此属性作为值进行传递【赋值/传参】,变量接收的是一个新值,更改变量的值不会使原响应式对象响应- 原理:响应式系统在创建响应式对象时会将其属性的值通过 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++
- 原理:响应式系统在创建响应式对象时会将其属性的值通过 Object.defineProperty 方法转换为 getter 和 setter 形式
用法
1 | |
ref 对比 reactive
定义的数据
ref( )用来定义:基本数据类型reactive( )用来定义:对象(或数组)类型数据- 备注:
ref( )也可以用来定义对象(或数组)类型数据,它内部会自动通过reeactive( )转为代理对象
实现原理
ref( )通过Object.defineProperty( )的get和set来实现响应式(数据劫持)reactive( )通过使用Proxy来是实现响应式(数据劫持),并通过Reflect操作源对象内部数据
使用方式
ref( )定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要 .valuereactive( )定义的数据:操作数据与读取数据均不需要 .value
toRef/toRefs
作用
将响应式对象的基本类型属性转换为响应式引用的函数
疑问
- 用处是什么
- 响应式对象的基本类型属性,如果进行赋值、传参,变量接收到的数据将是一个新的基本类型数据,因此对变量的修改并不会触发响应式对象的响应能力
- 使用
toRef()/toRefs将响应式对象的属性转换为响应式引用,并且对此响应式引用的内部值进行更改,也会影响响应式对象的属性,这是ref()做不到的
- 为什么不用
ref()函数进行转换- 如果将响应式对象的数据作为参数,调用
ref()函数生成响应式引用,ref()接收到的也是一个新的基本类型数据。 - 这个响应式引用将是一个独立的数据,响应能力只针对
ref()返回的此响应式引用本身,并不会响应式改变响应式对象的属性值
- 如果将响应式对象的数据作为参数,调用
toRef 和 toRefs 区别
toRef()- 通过
响应式对象的某一个属性,返回一个响应式引用 - 如果此属性是对象,则返回的
响应式引用的 value 属性,指向实参响应式对象的对应属性
- 通过
toRefs()- 通过
响应式对象返回由其属性生成的多个响应式引用组成的对象 - 属性名是
响应式对象的属性名,值是对应转换的响应式引用 - 如果此属性是对象,则返回的
响应式引用的 value 属性,指向实参响应式对象的对应属性
- 通过
注意事项
- toRefs() 转换后的属性,需要在模板中使用 .value 来访问其值
- 如果直接将一个普通对象的属性传给 toRef,会抛出错误
toRef()用法
1 | |
1 | |
shallowRef/shallowReactive
作用
进行浅层的数据响应,深层的数据不具有响应能力
- shallowRef
- 用于生成浅层响应式引用
- 只对基本类型数据有效
- 如果参数是对象,不会调用
reactive - 生成的响应式引用其内部值,value 指向的只是普通object对象,不是
响应式对象 - 因此修改此对象属性不具有响应效果
- 如果参数是对象,不会调用
- 直接修改响应式引用的内部值 value,会有响应式效果
- 即:
refobj.value = xxx,此种情况会触发响应 - 参数是对象,直接修改响应式引用的内部值
refobj.value = xxx,也会触发响应
- 即:
- shallowReactive
- 用于生成浅层响应式对象
- 此
响应式对象属性的修改,具有响应效果 - 此
响应式对象属性的属性…的修改,不具有响应效果
1 | |
readonly/shallowReadonly
作用:传入参数 响应式对象 或 响应式引用 ,返回一个 新的 只读的 响应式对象
readonly:- 新的
响应式对象的所有属性或值,都不具有响应效果 - 实参为
响应式引用,返回的新的响应式对象的用法和响应式引用用法相同。即:代码中用 value 属性,模板中不需要 value
- 新的
shallowReadonly- 实参为
响应式对象:- 返回的新的
响应式对象的子属性不具有响应式,但是其后代属性具有响应效果 - 并且响应效果关联传入的实参响应式对象。即修改孙子属性,新响应式对象触发响应,传入的实参响应式对象也会触发响应
- 返回的新的
- 实参为
响应式引用:- 返回的新的
响应式对象的内部值,不具有响应式效果 - 返回的新的
响应式对象的用法和响应式引用用法相同。即:代码中用 value 属性,模板中不需要 value
- 返回的新的
- 实参为
1 | |
toRaw/markRaw
toRaw
作用:使用 响应式对象 作为参数,返回一个数据相同的非响应式普通对象
1 | |
markRow
作用:标记一个 普通对象,此对象将来不管是添加给 响应式对象 作为属性,还是使用 reactive 都不会是响应式
此对象某属性是 响应式对象 或 响应式引用,即使对象被 markRow 标记,对象依旧可以被转换为 响应式对象
1 | |
customRef
作用自定义 ref 函数,用来定制生成的数据其响应方式。即:可以自定义数据实现响应时的额外操作
用法- 引入
customRef函数 - 自定义函数
- 函数中调用
customRef函数- 参数:回调函数
- 回调函数的参数
- track():用于追踪数据,将组件实例和数据进行关联
- trigger():触发组件重新渲染,渲染时根据依赖列表重新加载数据
- 回调函数的返回值:具有 getter/setter 的对象
- getter:
自定义响应式引用获取数据时调用 - setter:
自定义响应式引用修改数据时调用
- getter:
- 回调函数的参数
- 返回值:生成的
自定义响应式引用
- 参数:回调函数
1 | |
计算属性
computed
- 作用:计算属性
- 引入:
import {computed} from vue - 特征:
computed是一个函数 - 定义:在
setup( )配置项中进行调用 - 执行时机
- 初始化的时候
- 依赖数据发生改变时候
完整定义
1 | |
简写(只能读取,回调函数不能传参,会报错)
1 | |
监视属性
watch
- 作用:数据监视
- 引入:
import { watch } from vue - 特征:
watch是一个函数 - 定义:在
setup( )配置项中进行调用
监视
ref响应数据
- 参数
- 监听的属性
- 回调函数
- 配置项
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响应数据
- 无法获取 oldValue 旧数据,newValue 和 oldValue 得到的数据都是新改变的数据。
- 强制开启了深度监视(监听属性的属性的属性…),即使配置监听参数(deep:false),依旧无效
- 监听
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
- watchEffect 是一个函数
- 参数为一个回调函数
- 作用:根据回调函数中用到的属性,自动进行监视
- 回调函数中用到了某属性,当属性值发生改变,回调函数将被触发
- 只针是基本类型的属性,当属性的值发生改变,并且回调函数使用了此属性,则回调函数才会被触发
- 当属性是引用类型,回调函数中使用了此属性,但是修改的是此引用属性的属性,则回调函数不会被触发
1 | |
数据通信
provide/inject
作用- 用于祖组件向后代组件传递数据(不能用于子组件向祖先组件传递数据)
- 组件之间是嵌套调用的关系 祖>子>后代
- 父子组件一般使用
props进行数据传递
- 祖组件引入
provide函数 - 祖组件调用
provide("别名",数据)提供传输的数据 - 后代组件引入
inject函数 - 后代组件调用
inject("别名")获取祖组件提供的数据
1 | |
props
- 使用选项式API,则使用
props配置项进行配置 - 使用组合式API配合
<script setup>标签,则使用defineProps函数接收props数据
1 | |
父组件获取子组件实例和数据
- 父组件 html 中调用子组件
- 子组件定义实例上定义
ref属性 - 子组件如果使用
<script setup>标签,则需要手动暴露数据defineExpose({ aa,bb,cc })
- 父组件通过定义相同 ref 属性名变量的响应数据获取子组件实例
1 | |
1 | |
判断响应式数据
1 | |
hook
官方资料:https://cn.vuejs.org/guide/reusability/composables.html
hook 是一个函数,叫做 组合式函数 ,是使用 组合式API 封装的函数
- 利用 Vue 的组合式 API 来封装的 有状态逻辑的函数
- 特征:
- 普通函数在调用后,只能获取函数当前处理的数据
- 使用组合式 API 生成的 组合式函数,执行一次后可以持续修改或判断其返回的值(因为使用了相应的组合式API,使数据成为了动态绑定数据等效果)
- 用法:
- 创建 hook 目录(非强制)
- 目录中创建相关 js 文件,文件名常以
use开头(非强制) - js 文件引入相关
组合式API - js 文件中暴露使用了
组合式API的自定义函数组合式函数 - 其他组件就可以引用这个
组合式函数
组件
fragment
vue2 中需要一个根节点,但是 vue3 可以有多个根节点,原因是在虚拟dom中有一个
teleport
作用将其定义的组件在指定元素下渲染。作为指定元素的最后一个子元素
特点<teleport>只改变了渲染的 DOM 结构,它不会影响组件间的逻辑关系,只会影响 DOM 结构和 CSS 样式<teleport>可以有多个根元素to的值可以是一个 CSS 选择器字符串,也可以是一个 DOM 元素对象to属性值发生改变就会重新渲染。即:to属性值绑定响应数据,改变值就可随时改变元素所在位置
1 | |
suspense
作用当子组件是通过异步加载方式进行加载,可以控制子组件未加载成功的显示内容
特点- 使用异步加载子组件,并且使用
<suspense>进行处理,子组件的setup可以是async函数,返回值为proxy对象,成功状态值为响应类型数据 - 为什么使用:当子组件未加载完成,祖先组件都将不会显示。使用之后可以先显示祖先组件
- 导入异步引入组件函数
import { defineAsyncComponent } from 'vue' - 调用函数异步引入组件
const child = defineAsyncComponent(()=>import("./child.vue")) - 使用
<suspense>组件处理子组件
1 | |
其他改变
全局组件转移1 | |
data配置项必须是函数
过渡类名1 | |
- keyCode作为 v-on 的修饰符,也不再支持 config.keyCodes
- v-on.native 修饰符【 vue3中子组件只要不使用
emits接收就不认为是自定义事件 】 - 过滤器
filter【 vue3中都用计算属性 】
实现原理
响应式原理
vue2 响应式原理
- 对象类型:通过
Object.defineProperty( )【get/set】对属性的读取、修改进行拦截(数据劫持)- 不会触发响应的情况:通过调用对象属性的方式,添加/删除 属性,界面不会更新
- 数组类型:通过重写数组的相关方法来实现拦截
- 不会触发响应的情况:直接通过数组下标修改数组元素,界面不会更新
vue3 响应式原理
- 通过
Proxy(代理):拦截对象中任意属性的变化- 对象属性进行 增删改查 操作时,可自定义执行相关操作
- 不使用
Object.defineProperty进行数据劫持,是因为会出现无法触发响应的情况
- 通过
Refiect(反射):对 源对象 的属性进行操作- 如 Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false
1 | |
生命周期
优先级- setup
- beforeCreate(配置项)
- created(配置项)
- onBeforeMount
- beforeMount(配置项)
- onMounted
- mounted(配置项)
- onBeforeUpdate
- updated(配置项)
- onBeforeUnmount
- beforeUnmount(配置项)
- onUnmounted
- unmounted(配置项)
1 | |
