变化
-
性能提升
-
源码升级
- 使用proxy替代defineProperty实现响应式
- 重写虚拟DOM的实现和Tree-Shaking
-
更好的支持TypeScript
-
新特性
创建Vue3工程
使用vue-cli
- 使用4.5版本以上vue-cli
- vue creat vue3 projectName
使用vite
- 开发环境无需打包,可以快速冷启动
- 更轻量快速热重载
- 按需编译
npm init vite-app projectName
//安装依赖
cd project
npm i
npm run dev
常用Composition API 组合式API
1.setup函数
-
组件中用到的数据,方法都需要配置在setup中
-
两种返回值
-
返回对象
- 返回内容可以在模版中直接使用
-
返回渲染函数
//手动引入渲染函数 import {h} from 'vue' //返回渲染函数 setup(){ function fun(){ //此处内容会直接将原有模版字符串覆盖掉 return ()=>h('h1','text') } }
-
-
在vue3中无法读取到vue2配置内容
-
若有重名, setup优先
-
setup不可以是async函数(后期配合suspense和异步组件可以使用async)
注意:
-
setup执行时间在beforeCreate之前,所以setup不含this(undefined)
-
setup参数
- props //父组件向子组件传数据
props: ["msg", "school"], setup(props) { console.log(props); }-
context
-
attrs //捕获父组件传子组件但是子组件并没有使用props接收的值 this.$attrs
-
emit //触发自定义事件 相当于this.$emit
- 在父组件中定义自定义事件后,子组件中使用context.emit(’事件’,value)触发
- 要在子组件emits中注册自定义事件
-
slots //插槽 this.$slot
-
2.ref函数
//引入ref函数 import {ref} from 'vue' let name = ref('章三')-
作用:定义一个响应式的数据
-
语法:const xxx = ref(initValue)
- 创建一个包含响应式数据的引用对象(reference对象)
- js中操作数据 xxx.value
- 模版中使用数据不需要value
-
说明
-
接受的数据类型可以是基本类型,也可以是对象类型
-
基本类型数据:响应式依靠Object.defineProperty的get和set完成
-
对象类型数据,内部借助了新函数 — reactive
- reactive函数内部封装了Proxy函数
-
-
实现
// 引入ref函数 import { ref } from "vue"; export default { name: "App", setup() { // 使用ref函数将数据转化成引用对象实现响应式 let name = ref("黎明"); let age = ref(18); function changeName() { // 使用.value形式双向绑定 (name.value = "王武"), (age.value = 20); } //使用setup返回对象t return { name, age, changeName, }; },3.reactive函数
- 定义对象类型的响应式数据
- 可以进行深层次的响应式数据
- 面向对象式
// 引入reactive函数 import { reactive } from "vue"; export default { name: "App", setup() { //使用reactive函数,将对象转化成响应式数据 let person = reactive({ name: "黎明", age: 18, job: { type: "UI设计师", salary: "30k", }, }); function changeName() { //通过reactive可以通过Proxy省略value (person.name = "王武"), (person.age = 30), (person.job.salary = "20k"); } //使用setup返回对象 return { person, changeName, };Vue3.0中响应式原理
Vue2.x中响应式
-
原理
- 对象类型:通过Object.defineProperty()对属性的读取,修改进行拦截实现数据劫持
- 数组类型:通过重写数组的一系列方法来实现拦截
-
不足
- 在数组中直接通过数组下标修改数据数据不会发生更新
- 添加,删除对象,数组身上属性时,页面不会刷新
//直接向对象中添加属性并不会引起页面的更新 // this.sex = "男"; //通过下列方法 /* ** 1.this.$set ** this.$set(this.person,'sex','男') ** 2.Vue.set ** Vue.set(this.person,'sex','男') */ -
实现
let person = { name: '章三', age: 20 } let p = {} Object.defineProperty(p, 'name', { get() { console.log("读取name属性"); return person.name }, set(name) { console.log("修改name属性") person.name = name } })Vue3.0响应式
- window.Proxy
- 实现
代理Proxy加反射Reflect
- 通过代理拦截对象中属性的任意变化,如添加删除修改
- 通过反射对源对象的属性进行操作
let persons = { a: 1, b: 2, } let p1 = new Proxy(persons, { get(target, propName) { console.log(`读取了${target}上的${propName}属性`); return Reflect.get(target, propName) }, set(target, propName, value) { console.log(`修改了${target}上的${propName}属性,修改为${value}`); console.log(target); Reflect.set(target, propName, value) } deleteProperty(target, propName){ console.log(`删除了${propName}`); Reflect.deleteProperty(target, propName) } })Reflect函数
-
方法
- set
- get
- deleteProperty
- defineProperty
-
发生重复定义错误时,以布尔值的方式记录,减少try,catch的重复
const x1 = Reflect.defineProperty(person, 'c', { get() { return 3 } }) console.log(x1); //true const x2 = Reflect.defineProperty(person, 'c', { get() { return 4 } }) console.log(x2); //falseRef函数和Reactive函数区别
-
定义数据
- ref定义基本类型数据
- reactive定义对象(数组)类型数据。(引用类型数据)
- ref也可以用来定义对象(数组)类型数据,内部会自动通过reactive转化为代理对象
-
原理
- ref仍旧使用Object.defineProperty的get和set来实现响应式(数据劫持)
- reactive通过Proxy来实现响应式,并通过reflect操作源对象数据
-
使用角度
- ref:操作数据需要通过.value,在模版中读取不需要
- reactive:操作读取均较为简洁
计算属性computed
//需要先引入computed
import { reactive, computed } from "vue";
//计算属性简写:
// person.fullName = computed(() => {
// return `${person.firstName}-${person.lastName}`;
// });
//计算属性:
person.fullName = computed({
get() {
return `${person.firstName}-${person.lastName}`;
},
set(value) {
const personName = value.split("-");
person.firstName = personName[0];
person.lastName = personName[1];
},
});
watch监视
- 注意:由于watch监视的是ref和reactive对象,所以普通类型的ref并不需要.value(.value会直接取到数值,watch不可以对数值进行监测)
- 当watch监视ref对象定义的对象(数组)数据类型时,可以通过.value或是配置项中开启deep,来达到深度监视的目的(此时仍无法获取oldValue)
1.监视ref对象
//监视单个属性
watch(
num,
(newVal, oldVal) => {
console.log(newVal, oldVal);
},
{ immediate: true, deep: true }
);
//监视多个属性
watch([num, msg], (newVal, oldVal) => {
console.log(newVal, oldVal);
});
2.监视reactive对象
-
当监视的是reactive定义的响应式数据时
- 无法获取oldValue的值
- 强制开启了深度监视
-
当监视的是reactive中定义的某一个属性时
- deep有效
- 监视reactive中某一个属性(此时可以正常获取到oldValue)
//监视单个属性
watch(
() => {
return person.job.salary;
},
(newValue, oldValue) => {
console.log(newValue, oldValue);
}
);
watchEffect函数
- 不需要指明监视的属性,会自动监视用到的属性
生命周期
名称变化
生命周期钩子
- beforeDestroy → beforeUnmount
- destroyed → unmounted
Composition API 钩子
- beforeCreated , created → setup
- 其余名称前面加on
自定义hook函数
- 本质是一个函数,将setup中用到composition API的函数进行封装
- 在使用过程中引入时直接引入不需要加大括号
toRef和toRefs函数
-
作用:将某个数据变成ref对象
-
使用:
-
toRef(obj,’atte’)
-
…toRefs(obj) //只能展开第一层
- 用于reactive包裹的响应式对象
-
其他组合式API
shallowReactive
- 只将第一层数据转化成响应式
shallowRef
- 不会处理对象类型响应式
- 对象数据后续功能不会修改对象中属性
readonly
- 响应式对象只读(深层次)
shallowReadonly
- 响应式对象只读(浅层次)
toRaw
- 将reactive生成的响应式对象转化成普通对象
- 转化后,对象内属性可以被修改,但是不会响应式刷新页面
markRaw
-
标记一个对象,使其永远不会成为响应式
-
应用:
- 复杂的第三方类库永远不应该是响应式
- 渲染具有不可变数据的大列表,跳过响应式优化性能
customRef
- 自定义ref
//自定义ref
function myRef(value) {
//返回customRef
return customRef((track,trigger) => {
//重写get,set方法
return {
get() {
console.log(`有人读取了${value}`);
//告诉get监听set的通知
track()
return value;
},
set(newValue) {
console.log(`有人修改了值,改为了${newValue}`);
value = newValue;
//通知get值已被修改
trigger()
},
};
});
}
let num = myRef("自定义customRef的使用");
- 实现一个防抖
//自定义ref
function myRef(value, delay) {
//返回customRef
return customRef((track, trigger) => {
let timer;
//重写get,set方法
return {
get() {
console.log(`有人读取了${value}`);
//告诉get监听set的通知
track();
return value;
},
set(newValue) {
console.log(`有人修改了值,改为了${newValue}`);
clearTimeout(timer);
timer = setTimeout(() => {
value = newValue;
//通知get值已被修改
trigger();
}, delay);
},
};
});
}
let num = myRef("qwe", 500);
provide和inject
- 祖孙组件间通信
- 父组件
import { provide } from 'vue'
provide ('car' , car )
- 后代组件
import { inject } from 'vue'
let car = inject ('car')
响应式数据的判断
- isRef
- isReactive
- isReadonly
- isProxy
组合式API优势
配置式API (Options API)
- 数据,方法分开存放,较为混乱
组合式API(composition API)
- 更优雅的组织数据,方法,使代码更为有序
新的组件
Fragment
- 不在需要根标签,组件内部会将多个标签包含在同一个fragment虚拟元素中
- 减少层级标签,减少内存占用
Teleport
Suspense
- 配合异步引入,实现骨架屏效果
import { defineAsyncComponent } from "vue";
const Demo = defineAsyncComponent(() => import("./components/Demo.vue")); //异步引入子组件
<Suspense>
<template v-slot:default>
<Demo />
</template>
<template v-slot:fallback>
<h2>加载中。。。。。</h2>
</template>
</Suspense>
其他改变
- 移除了keycode作为v-on的修饰符
- 移除了click.native,若子组件中不声明emits:[click],默认click为原生事件
- 移除了过滤器