vue概念
- 用于构建用户界面的 JavaScript 框架–渐进式框架。
- 基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型。
vue核心功能
- 声明式渲染:拓展了模板语法,描述HTML跟js状态间的关系
- 响应性:自动跟踪js状态并在发生变化时响应式的更新DOM
渐进式框架
- 无需构建步骤,渐进式增强静态的 HTML
- 在任何页面中作为 Web Components 嵌入
- 单页应用 (SPA)
- 全栈 / 服务端渲染 (SSR)
- Jamstack / 静态站点生成 (SSG)
- 开发桌面端、移动端、WebGL,甚至是命令行终端中的界面
API风格
选项式 — 不适用构建工具/低复杂度
- 包含多个选项的对象来描述组件的逻辑如,data、methods、mounted,选项中定义的属性都会暴露在函数内部的this,this指向当前组件实例
组合式API — 完整单页面应用
- 使用导入的API函数来描述组件逻辑。通常搭配
<script setup>
使用
- 使用导入的API函数来描述组件逻辑。通常搭配
vue3优点
- 性能的提升
- 打包大小减少41%
- 初次渲染快55%, 更新渲染快133%
- 内存减少54%
- 源码的升级
- 使用Proxy代替defineProperty实现响应式
- 重写虚拟DOM的实现和Tree-Shaking
- 拥抱TypeScript
- 新的特性
- Composition API(组合API)
- setup配置
- ref与reactive
- watch与watchEffect
- provide与inject
- 新的内置组件
- Fragment
- Teleport
- Suspense
- 其他改变
- 新的生命周期钩子
- data 选项应始终被声明为一个函数
- 移除keyCode支持作为 v-on 的修饰符
- Composition API(组合API)
创建项目 + vite
- vite
- node版本要16以上
- 使用create-vue
- 安装:npm create vue@latest
模板语法
- 文本插值:
{{}}
- 原始html:
v-html=""
- Attribute绑定:
(v-bind:xxx="" 简写 :xxx="")
- 正常情况:值为null或undefined,attribute将会从渲染的元素上移除
- 布尔型:值为真值或一个空字符串,元素会包含这个attribute;为其他假值时忽略
- 动态绑定多个值:对象包含多个属性。使用不带参的v-bind,绑定到单个元素上。
- v-bind=”obj”
- 支持javascript表达式或者调用函数
指令(Directives)
- 期望值为一个javascript表达式,表达式的值变化时响应式更新DOM
- javascript表达式使用v-bind(:),函数使用v-on(@)
- 参数:
:xxx=""
- 动态参数:
:[字符串或null]=""
- 三限制:禁空格、禁引号、禁大写
- 修饰符.: 触发事件
响应式原理
常用 Composition API
setup
- 代表使用组合式API,
<script setup>
是setup函数的语法糖 <script setup>
获取props,emit,context- 子组件引入:import { useContext, defineProps, defineEmit } from ‘vue’
- emit:
- 定义:
const emit = defineEmit(['父组件在子组件标签上传的事件名'])
- 在子组件的某个事件触发:emit(‘事件名’, 子组件传递的数据),父组件通过事件参数拿到
- 定义:
- ctx:
const ctx = useContext()
- props:
const props = defineProps({ 父组件在子组件标签上传的属性名: 该属性的类型 })
- 注意点:
- setup执行的时机
- 在beforeCreate之前执行一次,this是undefined。
- setup的参数
- props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性
- context:上下文对象
- attrs:值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于
this.$attrs
- slots:收到的插槽内容, 相当于
this.$slots
- emit:分发自定义事件的函数, 相当于
this.$emit
- attrs:值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于
- setup执行的时机
ref函数
- 作用: 声明一个响应式的状态
- 使用:引入import { ref } from ‘vue’
- 语法:
const xxx = ref(initValue)
- JS中操作数据:
xxx.value
- 模板中读取数据: 不需要.value,直接:
<div>{{xxx}}</div>
- JS中操作数据:
- 优点:
- 深层响应性
- 应用于任意值,即改变就会检测
- shallow ref 可放弃深层响应性
- 非原始值将通过 reactive() 转换为响应式代理
- DOM更新时机
- 更改响应式状态后,DOM会自动更新,但并不是同步
- 等待一个状态改变后的DOM更新完成,使用nextTick()
- 深层响应性
- 备注:
- 接收的数据可以是:基本类型、也可以是对象类型。
- 基本类型的数据:响应式依然是靠
Object.defineProperty()
的get与set完成的。 - 对象类型的数据:使用reactive处理
reactive函数
- 作用: 定义一个对象类型的响应式数据,返回一个代理对象(Proxy的实例对象,简称proxy对象)
- 语法:const 代理对象= reactive(源对象)接收一个对象(或数组)
- 使用:代理对象
- 局限性:只能用于对象类型(对象、数组和Map、Set这样的集合类型),并且不能替换整个对象,解构不友好
- 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。
reactive 和 ref 区别
- 从定义数据角度对比:
- ref用来定义:基本类型数据
- reactive用来定义:对象(或数组)类型数据
- 注:ref也可以定义对象(或数组)类型数据, 它内部会自动通过reactive转为代理对象
- 从原理角度对比:
- ref通过Object.defineProperty()的get与set来实现响应式(数据劫持)
- reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据
- 从使用角度对比:
- ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value
- reactive定义的数据:操作数据与读取数据:均不需要.value
⭐ 计算属性computed — 依赖响应式状态的复杂逻辑
- 与Vue2中computed配置功能一致☞VUE2-基础(主了解)
- 计算属性值会基于其响应式依赖被缓存,只有依赖改变才会去重新计算
- 写法:
- 引入钩子:import { computed } from ‘vue’
- 使用:computed(()=> {})
侦听器 watch — 在状态变化时执行一些“副作用”
与Vue2中watch配置功能一致☞VUE2-基础(主了解)
两个小“坑”:
- 监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)
- 监视reactive定义的响应式数据中某个属性时:deep配置有效
写法:
- 引入钩子:import { watch } from ‘vue’
- 基本使用:watch(变量,(newValue,oldValue)=>{},{immediate:true})
- 监视多个ref定义的响应式数据:
- watch([变量1,变量2],(newValue,oldValue)=>{})
- 监视reactive定义的响应式数据中的某个属性(监视的是对象中的某个属性,所以deep配置有效)
- watch(()=>对象.属性,(newValue,oldValue)=>{},{immediate:true,deep:true})
- 监视reactive定义的响应式数据中的多个属性
- watch([()=>对象.属性,()=>对象.属性2],(newValue,oldValue)=>{},{immediate:true,deep:true})
深层侦听器
- 使用 deep: true 开启,开销很大,无必要不使用
即时回调的侦听器
- 使用 immediate: true 开启
watchEffect函数
- watch的套路是:既要指明监视的属性,也要指明监视的回调。
- watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性
- 使用:watchEffect(()=>{})
- 注:仅会在其同步执行期间,才追踪依赖。在使用异步回调时,只有在第一个 await 正常工作前访问到的属性才会被追踪。
回调的触发时机
- 默认,用户创建的侦听器回调,都会在Vue 组件更新前被调用
- 在侦听器回调中能访问被 Vue 更新之后的 DOM,指明 flush: ‘post’ 选项
停止侦听器
- 场景:用异步回调创建一个侦听器,它不会绑定到当前组件上,必须手动停止它,以防内存泄漏
- 停止一个侦听器:调用 watch 或 watchEffect 返回的函数
- const unwatch = watchEffect(() => {})
- unwatch()
⭐ 生命周期钩子
- Vue3可继续使用Vue2的生命周期钩子,但有两个更名:
beforeDestroy
改名为 beforeUnmountdestroyed
改名为 unmounted
Vue3的Composition API 形式的生命周期钩子,与Vue2中钩子对应关系如下:(vue3:vue2)
beforeCreate
==>setup()
created
==>setup()
beforeMount
==>onBeforeMount
mounted
==>onMounted
beforeUpdate
==>onBeforeUpdate
updated
==>onUpdated
beforeUnmount
==>onBeforeUnmount
unmounted
==>onUnmounted
vue3生命周期钩子的作用
beforeCreate
- 在组件实例初始化完成后立即调用,data() 和 computed 等选项处理之前立即调用
created
- 已完成响应式数据、计算属性、方法和侦听器。
- 然而,此时挂载阶段还未开始,因此 $el 属性仍不可用
beforeMount
- 在组件被挂载之前调用,还没创建DOM节点
mounted
- 在组件挂载之后调用,所有同步子组件都已经被挂载。
- 其自身的 DOM 树已经创建完成并插入了父容器中。
- 处理副作用
beforeUpdate
- 在组件即将因为一个响应式状态变更而更新其DOM树之前调用。
- 在 Vue 更新 DOM 之前访问 DOM 状态
update
- 在组件因为一个响应式状态变更而更新其 DOM 树之后调用。
- 父组件的更新钩子将在其子组件的更新钩子之后调用。
- 在组件的任意 DOM 更新后被调用
beforeUnmount
- 在一个组件实例被卸载之前调用
- 当这个钩子被调用时,组件实例依然还保有全部的功能
unmounted
- 在一个组件实例被卸载之后调用,
- 可以在这个钩子中手动清理一些副作用,例如计时器、DOM 事件监听器或者与服务器的连接。
activated
- 组件实例是
<KeepAlive>
缓存树的一部分,当组件被插入到 DOM 中时调用
- 组件实例是
deactivated
- 组件实例是
<KeepAlive>
缓存树的一部分,当组件从 DOM 中被移除时调用
- 组件实例是
模板引用
- ref:直接访问底层DOM元素,DOM 元素或子组件实例被挂载后,可以直接引用
- 使用:
- 引入:import { ref, onMounted } from ‘vue’
- 使用:
<view ref="" />
v-for中的模板引用:
- ref的值是数组
- ref 数组并不保证与源数组相同的顺序
函数模板引用:
:ref绑定为一个函数
,函数会收到元素引用作为其第一个参数<input :ref="(el) => {}"/>
组件上的ref:
- 用在组件上获取的是组件实例
其它 Composition API
shallowReactive 与 shallowRef
- shallowReactive:只处理对象最外层属性的响应式(浅响应式)。
- shallowRef:只处理基本数据类型的响应式, 不进行对象的响应式处理。
- 使用场景:
- 如果有一个对象数据,结构比较深, 但变化时只是外层属性变化 ===> shallowReactive
- 如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 ===> shallowRef
readonly 与 shallowReadonly
- readonly: 让一个响应式数据变为只读的(深只读)
- shallowReadonly:让一个响应式数据变为只读的(浅只读)
- 应用场景: 不希望数据被修改时。
toRaw 与 markRaw
- toRaw:
- 作用:将一个由
reactive
生成的响应式对象转为普通对象 - 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新
- 作用:将一个由
- markRaw:
- 作用:标记一个对象,使其永远不会再成为响应式对象
- 应用场景:
- 有些值不应被设置为响应式的,例如复杂的第三方类库等
- 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能
customRef
- 作用:创建一个自定义的 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<template>
<input type="text" v-model="keyword">
<h3>{{keyword}}</h3>
</template>
<script>
import {ref,customRef} from 'vue'
export default {
name:'Demo',
setup(){
//自定义一个myRef
function myRef(value,delay){
let timer
//通过customRef去实现自定义
return customRef((track,trigger)=>{
return{
get(){
track() //告诉Vue这个value值是需要被“追踪”的
return value
},
set(newValue){
clearTimeout(timer)
timer = setTimeout(()=>{
value = newValue
trigger() //告诉Vue去更新界面
},delay)
}
}
})
}
let keyword = myRef('hello',500) //使用程序员自定义的ref
return { keyword }
}
}
</script>
provide 与 inject
- 作用:实现祖先与后代组件间通信
- 套路:父组件有一个 provide 选项来提供数据,后代组件有一个 inject 选项来开始使用这些数据
- 具体写法:
- 祖组件中:使用provide(‘通信的别名’,要传递的变量)
- 后代组件中:const 得到的变量 = inject(‘通信的别名’)
响应式API:工具函数
- isRef(): 检查一个值是否为一个 ref 对象
- unref(): 如果参数是 ref,则返回内部值,否则返回参数本身
- isReactive(): 检查一个对象是否是由
reactive
创建的响应式代理 - isReadonly(): 检查一个对象是否是由
readonly
创建的只读代理 - isProxy(): 检查一个对象是否是由
reactive
或者readonly
方法创建的代理 - toRef():创建一个 ref 对象,其value值指向另一个对象中的某个属性。
- 语法:const name = toRef(person,’name’)
- toRefs(): 与
toRef
功能一致,值指向一个对象- 语法:toRefs(person)
新的组件
Fragment
- 在Vue2中: 组件必须有一个根标签
- 在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中
- 好处: 减少标签层级, 减小内存占用
Teleport
- 定义: 将组件html结构移动到指定位置的技术
- 使用:teleport标签包裹的内容是移动的内容,标签上指定to=”移动位置”
Suspense
- 等待异步组件时渲染一些额外内容
- 使用步骤:
- 引入钩子:import {defineAsyncComponent} from ‘vue’
- 异步引入组件:const 组件名 = defineAsyncComponent(()=>import(‘组件路径’))
- 使用
Suspense
包裹组件,并配置好对应插槽的内容
- 使用
其他
全局API的转移
- Vue 2 有许多全局 API 和配置在vue3中调整。
- 将全局的API,即:
Vue.xxx
调整到应用实例(app
)上全局 API 实例 API ( app
)Vue.config.xxxx app.config.xxxx Vue.config.productionTip 移除 Vue.component app.component Vue.directive app.directive Vue.mixin app.mixin Vue.use app.use Vue.prototype app.config.globalProperties
- 将全局的API,即:
其他改变
- data选项应始终被声明为一个函数。
- 过度类名的更改:.v-enter-from,.v-leave-to(vue3写法)
- 移除keyCode作为 v-on 的修饰符,同时也不再支持
config.keyCodes
- 移除
v-on.native
修饰符 - 移除过滤器(filter)