1.vue3
1)vue2与vue3的区别
1. 性能提升
1)打包大小减少41%
2)初次渲染快55%, 更新渲染快133%
3)内存减少54%
4)使用Proxy代替defineProperty实现数据响应式
5)重写虚拟DOM的实现和Tree-Shaking
2. 新增特性
1)Composition (组合) API
2)setup
ref 和 reactive
computed 和 watch
新的生命周期函数
provide与inject
3)新组件
Fragment - 文档碎片
Teleport - 瞬移组件的位置
Suspense - 异步加载组件的loading界面
4)其它API更新
全局API的修改
将原来的全局API转移到应用对象
模板语法变化
2)创建项目
1.方式1:脚手架方式
//安装或者升级
npm install -g @vue/cli
//保证 vue cli 版本在 4.5.0 以上
vue --version
//创建项目
vue create my-project
2.方式2:vite 创建
npm init vite-app <project-name>
cd <project-name>
npm install
npm run dev
3)文件介绍
1.main.ts文件
// 引入createApp函数,创建对应的应用,产生应用的实例对象
import { createApp } from 'vue'
import App from './App.vue'
// 创建App应用返回对应的实例对象,调用mount方法进行挂载
createApp(App).mount('#app')
2.App.vue文件
<template>
<!--Vue2中的html模版中必须要有一对根标签,Vue3组件的html模版中可以没有根标签-->
<img alt="Vue logo" src="./assets/logo.png">
<!--使用这个子级组件-->
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
</template>
<script lang="ts">
// defineComponent函数,目的是定义一个组件,内部可以传入一个配置对象
import { defineComponent } from 'vue';
// 引入一个子级组件
import HelloWorld from './components/HelloWorld.vue';
// 暴露出去一个定义好的组件
export default defineComponent({
// 当前组件的名字是App
name: 'App',
// 注册组件
components: {
// 注册一个子级组件
HelloWorld
}
});
</script>
<style>
</style>
4)Composition (组合) API
1. setup
1)使用
新的option, 所有的组合API函数都在此使用, 只在初始化时执行一次,函数如果返回对象, 对象中的属性或方法, 模板中可以直接使用
// setup(props,context){
// const number =10
// return {
// number
// }
// }
2)setup参数
1.props
是一个对象,里面有父级组件向子级组件传递的数据,并且是在子级组件中使用props接收到的所有的属性
2.context参数属性
attrs: 包含没有在props配置中声明的属性的对象, 相当于 this.$attrs
slots: 包含所有传入的插槽内容的对象, 相当于 this.$slots
emit: 用来分发自定义事件的函数, 相当于 this.$emit
3)细节
1.setup是在beforeCreate生命周期回调之前就执行了,而且就执行一次,由此可以推断出:setup在执行的时候,当前的组件还没有创建出来,
也就意味着:组件实例对象this根本就不能用
2.setup中的对象中的属性和data函数中的对象中的属性会合并为组件对象的属性,setup中的对象中的方法和methods对象中的方法会合并为组件对象的方法
在Vue3中尽量不要混合的使用data和setup及methods和setup,方法和属性如果有重名的话setup优先级更高
setup不能是一个async函数: 因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性数据
2.ref
一般用来定义一个基本类型的响应式数据返回的是一个Ref对象,对象中有一个value属性,如果需要对数据进行操作,需要使用该Ref对象调用value属性的方式进行数据的操作
html模版中是不需要使用.value属性的写法
1)数据的响应式处理
// 也可以用ref来处理对象/数组, 内部会自动将对象/数组转换为reactive的代理对象
import { ref } from 'vue';
const count = ref(0)
// 方法
function updateCount(){
// 报错的原因:count是一个Ref对象,对象是不能进行++的操作
// count++
count.value++
}
<h3>{{ count }}</h3>
2)获取元素
<input type="text" ref="inputRef" />
const inputRef = ref<HTMLElement | null>(null)
onMounted(() => {
inputRef.value && inputRef.value.focus() // 自动获取焦点
})
3.reactive
定义多个响应式数据,内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据都是响应式的
const obj = {
name: '小明',
age: 20,
wife: {
name: '小甜甜',
age: 18,
cars: ['奔驰', '宝马', '奥迪'],
},
}
// 通过改变user属性才有响应式,直接改变obj属性没有响应式
// user为代理对象,obj是目标对象
// reactive<any>(obj)或const obj: any = {}方式增加类型限制
// 通过user点的方式去炒作属性才有响应式,如果user拆包后再去使用属性,则属性不具备响应式
const user = reactive(obj)
4.computed计算属性
import {computed} from 'vue'
// 计算属性的函数中如果只传入一个回调函数,表示的是get
// fullName1是个ref类型,通过fullName1.value操作值
// fullName1需要在setup中返回
const fullName1 = computed(() => {
return user.firstName + '_' + user.lastName
})
const fullName2 = computed({
get() {
return user.firstName + '_' + user.lastName
},
set(val: string) {
const names = val.split('_')
user.firstName = names[0]
user.lastName = names[1]
},
})
5.watch和watchEffect
import {watch,watchEffect} from 'vue'
watch(
//参数1:监视的属性,参数2:回调,参数3:配置对象,immediate默认会执行一次watch,deep 深度监视
user,
(newVal) => {
fullName3.value = firstName + '_' + lastName
},
{ immediate: true, deep: true }
)
// watch可以监听多个数据,当我们使用watch监视非响应式的数据(user是响应式数据,user.firstName不是)的时候,代码需要改成回调
watch([()=>user.firstName, ()=>user.lastName,fullName3], () => {
// 这里的代码就没有执行,fullName3是响应式的数据,但是,user.firstName,user.lastName不是响应式的数据
console.log('====')
})
//组件挂载和响应式数据方式变化都会执行回调
watchEffect(() => {
const names = fullName3.value.split('_')
user.firstName = names[0]
user.lastName = names[1]
})
6.toRefs
可以把一个响应式对象转换成普通对象,该普通对象的每个属性都是一个 ref,相当于是让reactive包装后的对象的每个属性都具有响应式
const state = reactive({
name: '自来也',
age: 47,
})
const { name, age } = toRefs(state)
setInterval(() => {
name.value += '==='
}, 1000)
7.其它Composition (组合) API
1)shallowReactive和shallowRef
shallowReactive : 只处理了对象内最外层属性的响应式(也就是浅响应式)
shallowRef: 只处理了value的响应式, 不进行对象的reactive处理
import {shallowReactive,shallowRef} from 'vue'
// 改变m2.car.name没有响应式效果
const m2 = shallowReactive({
name: '鸣人',
age: 20,
car: {
name: '奔驰',
color: 'red',
},
})
//改变m4.value.name没有响应式效果,改变m4.value有响应式效果
const m4 = shallowRef({
name: '鸣人',
age: 20,
car: {
name: '奔驰',
color: 'red',
},
})
2)readonly和shallowReadonly
import {reactive, readonly, shallowReadonly } from 'vue'
深度只读,浅度只读
const state = reactive({
name: '佐助',
age: 20,
car: {
name: '奔驰',
color: 'yellow',
},
})
//不能改state2.name和state2.car.name的值
const state2 = readonly(state)
//不能改state2.name的值,可以改state2.car.name的值
const state2 = shallowReadonly(state)
3)toRaw和markRaw
toRaw:将一个响应式对象临时变成非响应式对象
markRaw:将一个响应式对象变成非响应式对象,后面不能才变成响应式对象
import { defineComponent, markRaw, reactive, toRaw } from 'vue'
const state = reactive<UserInfo>({
name: '小明',
age: 20,
})
const testToRaw = () => {
// 把代理对象变成了普通对象了,数据变化,界面不变化
const user = toRaw(state)
user.name += '=='
console.log('哈哈,我好帅哦')
}
const testMarkRaw = () => {
const likes = ['吃', '喝']
// markRaw标记的对象数据,从此以后都不能再成为代理对象了,界面不变化
state.likes = markRaw(likes)
setInterval(() => {
if (state.likes) {
state.likes[0] += '='
}
}, 1000)
}
4)toRef
为源响应式对象上的某个属性创建一个 ref对象, 二者内部操作的是同一个数据值, 更新时二者是同步的
区别ref: 拷贝了一份新的数据值单独操作, 更新时相互不影响
import { defineComponent, reactive, toRef, ref } from 'vue'
const state = reactive({
age: 5,
money: 100,
})
// 把响应式数据state对象中的某个属性age变成了ref对象了,age改变会影响state.age
const age = toRef(state, 'age')
// 把响应式对象中的某个属性使用ref进行包装,变成了一个ref对象,age改变不会影响state.age
const money = ref(state.money)
5)customRef
创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制
import { customRef, defineComponent, ref } from 'vue'
// 自定义hook防抖的函数
function useDebouncedRef<T>(value: T, delay = 200) {
let timeOutId: number
return customRef((track, trigger) => {
return {
// 取值走这个逻辑
get() {
// 告诉Vue追踪数据
track()
return value
},
// 设置值走这个逻辑
set(newValue: T) {
clearTimeout(timeOutId)
timeOutId = setTimeout(() => {
value = newValue
// 告诉Vue更新界面
trigger()
}, delay)
},
}
})
}
6)provide 与 inject
组件跨层级通信
import { defineComponent, provide,inject, ref } from 'vue'
//父组件
const color = ref('red')
// 提供数据
provide('color',color)
// 子孙组件,注入的操作
const color = inject('color')
7)响应式数据的判断
// isRef: 检查一个值是否为一个 ref 对象
console.log(isRef(ref({}))) //true
// isReactive: 检查一个对象是否是由 reactive 创建的响应式代理
console.log(isReactive(reactive({}))) //true
// isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
console.log(isReadonly(readonly({}))) //true
// isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理
console.log(isProxy(readonly({}))) //true
console.log(isProxy(reactive({}))) //true
8)Fragment和Teleport组件
在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中
//Teleport 提供了一种干净的方法, 让组件的html在父组件界面外的特定标签(很可能是body)下插入显示
<Teleport to="body">
<div>
</div>
</Teleport>
9)Suspense组件
//加载异步组件的空白时间会加载Loading.....的内容
<Suspense>
<template #default>
<!--异步组件-->
<AsyncAddress />
</template>
<template v-slot:fallback>
<!--loading的内容-->
<h2>Loading.....</h2>
</template>
</Suspense>
10)路由加载
import { defineAsyncComponent } from 'vue'
// Vue2中的动态引入组件的写法:(在Vue3中这种写法不行)
// const AsyncComponent = () => import('./AsyncComponent.vue')
// Vue3中的动态引入组件的写法
// const AsyncComponent = defineAsyncComponent(
// () => import('./AsyncComponent.vue')
// )
5)vue3响应式原理
通过Proxy和Reflect实现响应式,可以通过改变数组索引实现响应式
// 目标对象
const user = {
name: '佐助',
age: 20,
wife: {
name: '小樱',
age: 19
}
}
// 把目标对象变成代理对象
// 参数1:user---->target目标对象
// 参数2:handler---->处理器对象,用来监视数据,及数据的操作
const proxyUser = new Proxy(user, {
// 获取目标对象的某个属性值
get(target, prop) {
console.log('get方法调用了')
return Reflect.get(target, prop)
},
// 修改目标对象的属性值/为目标对象添加新的属性
set(target, prop, val) {
console.log('set方法调用了')
return Reflect.set(target, prop, val)
},
// 删除目标对象上的某个属性
deleteProperty(target, prop) {
console.log('delete方法调用了')
return Reflect.deleteProperty(target,prop)
}
})
6)生命周期函数
1.vue2
vue2.x中的beforeDestroy和destroyed这两个生命周期回调已经在vue3中改名为beforeUnmount和unmounted,在setup方法外面使用
2.vue3
//在setup里面使用,beforeCreate和created被setup取代,相同的生命周期函数setup比较先执行
import {onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted } from 'vue'
onBeforeMount(()=>{
console.log('3.0中的onBeforeMount')
})