vue3基础语法
一.组合式 API 也就是composition-api
(1) vue2 组件的局限性
组件越大,可续性越差
相同的代码逻辑很难在多个组件中复用
(2) 提供以下几个函数
setup :组合api 的方法都是写在这里面的
ref : 定义响应式数据 字符串 bool
reactive :定义响应式数据 对象
watchEffet :监听数据变化
watch :监听数据变化
computed :计算属性
toRefs :解构响应式对象数据
生命周期的hooks
(2.1) setup
官网
setup 函数会在 beforeCreate、created 之前执行,setup的生命周期 在 beforeCreate、created 之后执行
setup 有2个参数
props 的父组件传递过来的参数
ctx 上下文对象
ctx.attrs
ctx.slots
ctx.parent
ctx.root
ctx.emit
ctx.refs
在 setup() 函数中无法访问到 this【没有 this。这意味着,除了 props 之外,你将无法访问组件中声明的任何属性——本地状态、计算属性或方法。】
执行 setup 时,组件实例尚未被创建,你只能访问以下 property【props,attrs, slots, emit 】,你将无法访问以下组件选项【data,computed ,methods】
(2.2) ref ,reactive 定义响应式数据
ref 【定义字符串,num,bool,数组】
reactive【定义对象】
1.ref ,reactive 定义响应式数据
{ {title}}{ {userInfos.username}}—-{ {userInfos.age}}
双向数据绑定 影响ref,reactive
(2.3) toRefs 解构响应式对象数据
2.toRefs 解构响应式对象数据
{ {worksName}}
setup() {
// 2.toRefs 解构响应式对象数据
var worksInfos = reactive(\{
worksName: "张三",
worksDesi: "到好多好好的"
\});
return \{
...toRefs(worksInfos) //...worksInfos 这样的话可以显示数据,但是不能双向绑定数据
\};
}
(2.4) computed 计算属性 【只能在setup】
<input v-model="lastName" />
<div>\{ \{fullName\}\}</div>
setup() \{
//3.computed :计算属性
var userInfos2 = reactive(\{
firstName: "王",
lastName: "一搏"
\});
var fullName = computed(() => \{
return userInfos2.firstName + userInfos2.lastName;
\});
return \{
...toRefs(userInfos2),
fullName
\};
(2.5) readonly “深层”的只读代理 (少用)
将响应式 数据(ref,reactive定义的数据),转正非响应式的数据(也就是原始数据)
readonly 传入一个对象(响应式对象),返回一个原始对象 只能读取不能修改。
var obj={
username:"李小英"
}
obj 就是原始数据, 可以渲染在页面中,但是不能双向绑定
var userInfos = reactive({
username: "张三",
age: 20
});
userInfos 响应式数据,
可以把响应式数据 转成非响应式数据,
const copy = readonly(userInfos) //copy.username 只能读取,不能修改
(2.6) watchEffet 监听数据变化
在响应式地跟踪其依赖项时,立即运行一个函数,并在更改依赖项时重新运行它。
4.watchEffet 监听数据变化 —侦听器
<div>\{ \{data.count\}\}</div>
<button @click="stop">手动关闭侦听器</button>
const data = reactive(\{
count: 1,
num: 1
\});
const stop = watchEffect(() => \{
console.log("侦听器:", data.count);
\});
setInterval(() => \{
data.count++;
\}, 1000);
(2.7) watch , watch与watchEffect区别
1.watch与watchEffect区别:
const data = reactive({
count: 10,
num: 1
});
懒执行,也就是说只有侦听的值变更时才执行回调
watchEffect(() => {
console.log("侦听器:", data.count);// 可以输出1
});
watch(data, () => {
console.log("watch 监听:", data.count);// 输出2
});
watch 只有监听到值(data.count)发生变化 才执行,watchEffect都可以执行
更明确哪些状态的改变会触发侦听器重新运行
watchEffect 可以监听具体的对象【如:data.count】,watch 只能监听ref或是reactive定义的对象【如: data】
访问侦听状态变化前后的值
watch(numVal, (newVal, oldVal) => {
console.log("numVal新值跟旧值的变化:", newVal, oldVal);
});
案例:
5.watch 监听数据变化 —侦听器
<div>\{ \{data.count\}\}</div>
<div>watch 双向数据绑定,前后数据</div>
\{ \{numVal\}\}
<input v-model="numVal" />
<input v-model=data.count"/>
const data = reactive(\{
count: 10,
num: 1
\});
// (1).简单的引用
// 要监听对象data ,但是不能监听对象里的值data.count
watch(data, () => \{
console.log("watch 监听:", data.count);
\});
setInterval(() => \{
data.count++;
\}, 1000);
var numVal = ref("1");
// (2).watch 的两个参数,代表新的值和旧的值
watch(numVal, (newVal, oldVal) => \{
console.log("numVal新值跟旧值的变化:", newVal, oldVal);
\});
//(3). watch 多个值,返回的也是多个值的数组
watch(\[numVal, data\], (newValue, oldValue) => \{
console.log("old", oldValue); //\["13",Proxy \{count: "144", num: 1\}\]
console.log("new", newValue); //\["134",Proxy \{count: "144", num: 1\}\]
\});
// (4).使用 getter 的写法 watch reactive 对象中的一项
watch(\[numVal, () => data.count\], (newValue, oldValue) => \{
console.log("old", oldValue); //old (2) \["1", 10\]
console.log("new", newValue); //new (2) \["1", "103"\]
console.log("updated:" + numVal.value + data.count); //updated:1103
\});
(2.8) 生命周期
生命周期的vue3更新:
在vue3中对生命周期钩子的命名进行一些简单的命名更新,并且在setup函数中提供了新的生命周期函数钩子。
1. 命名改变:
vue3对beforeDestroy 和destroy进行命名的修改,分别修改为beforeUnmount,unmounted。这个是命名的改变,回调时机上并没有发生任何的改变。
2. setup函数中提供了新的生命周期函数钩子
官网
都是 setup () 内部调用生命周期钩子,没有beforeCreate,created ,其他都是一样
(2.9)Provide / Inject
官网
不管组件层次 结构多深,都可以使用这个Provide(传递参数) / Inject (接受参数)
1.非组合api 写法
app.vue
祖父组件:{ {location}}
provides:
Provide / Inject
location.vue:
孙组件接受参数
祖父组件定义provide,孙组件inject接受:{ {location}}
点击fn,不能改变location.vue的值,就是没有响应的效果
没有办法 双向绑定
2. 组合api (重点)
祖父组件:{ {location}}
双向数据绑定:
姓名 { {username}}:
provides:
Provide / Inject
location.vue:
孙组件接受参数
1.祖父组件定义provide,孙组件inject接受:{ {location}}
用户信息: { {userInfos.username}}
2.provide inject实现父子组件传值的时候,子组件改变数据也会影响父组件
姓名:
3. 总结:
1.祖父组件定义provide,孙组件inject接受
2.provide inject实现父子组件传值的时候,子组件改变数据也会影响父组件
4. 不想子组件改变父组件的数据
最后,如果要确保通过 provide 传递的数据不会被 inject 的组件更改,我们建议对提供者的 property 使用 readonly
provide(“userInfos”, readonly(userInfos));
二.响应性
(1)比较vue2 和 vue3 响应式
1. vue2 的响应式:
核心:
对象:通过defineProperty对对象的已有属性值的读取和修改进行劫持(监视、拦截)
数组:通过重写数组更新数组一系列更新元素的方法实现元素修改的劫持【简单的理解:普通数组变成响应数组,对一个数组的元素进行更改的话,它会对这个更改的方法进行重新编写】
object.defineProperty(data,’count’,{
get()\{\},
set()\{\}
})
问题:
1). 对象直接新添加的属性或删除已有属性,界面不会自动更新
2). 直接通过下标替换元素或更新length,界面不会自动更新 arr[1] ={}
所以后面才有$set,去响应数据
{ {key}}:{ {val}}
<button @click="add">修改数据</button>
</div>
//因为vue2 是通过object.defineProperty 来操作数据,所以不能检测数据的变动(虽然打印出来的数据是更新的,但是界面的数据没更新)
export default \{
data() \{
return \{
user: \{
name: "王一博",
age: 20
\}
\};
\},
methods: \{
add() \{
this.user.sex = "男";
console.log( this.user);
\}
\}
\};
注意同样的代码在vue3 里面就可以。不旦数据更新,界面也更新。原因是vue3使用prory
2.vue3 的响应式:
核心:
1).通过proxy(代理):拦截对data任意属性的任意(13种)操作,包括属性值的读写,属性的添加,属性的删除等
2).通过reflect(反射):动态对被代理对象的相应属性进行特定的操作
(2) 什么是响应式
响应性是一种允许我们以声明式的方式去适应变化的一种编程范例。
简单的理解:对对象进行修改,对象数据会改变,界面上也会发生改变
(3)深入vue3响应性原理
就是把目标对象变成代理对象(prory),然后通过代理对象对数据进行操作,通过reflect在反射回来。
let user = {
name: "王一博",
age: 20,
words: \{
adds: "g区18号",
tell: "1727283848744",
cars: \["宝马", "奥迪", "本田"\]
\}
\};
// 把目标对象变成代理 对象
// 使用prory 从new Proxy开始接受2个参数
// 参数1:target目标对象----》 user
// 参数2:Handel 处理器对象,用来监视数据及数据操作----》\{\}
const proxyUser = new Proxy(user, \{
// 获取目标对象的某个属性值
//target目标对象。property被获取的属性名。
get(target, prop) \{
console.log("get方法调用了");
return Reflect.get(target, prop);
\},
// 修改、添加目标对象的属性
//target目标对象。property被获取的属性名 value 值
set(target, prop, value) \{
console.log("set方法调用了");
return Reflect.set(target, prop, value);
\},
// 删除目标对象的某个属性值
//target目标对象。property被获取的属性名
deleteProperty(target, prop) \{
console.log("delete方法调用了");
return Reflect.deleteProperty(target, prop);
\}
\});
// 通过代理对象获取目标对象中的某个属性值
console.log(proxyUser.name);
// 通过代理对象更新目标对象上的某个属性值
proxyUser.name = "肖战";
console.log("修改属性值:", user);
//通过代理对象向目标对象添加一个新的属性
proxyUser.sex = "男";
console.log("添加新属性:", user);
//通过代理对象删除目标对象的某个属性值
delete proxyUser.age;
console.log("删除age 后:", user);
//深度的添加属性值
proxyUser.words.name = "航天信息";
console.log("深度的添加属性值:", user);
(4)什么是prory
proxy 是一个对象,可以代理一个对象或函数,允许拦截被代理的对象或函数
(5) Reflect
reflect
三.有意思的Teleport
官网
teleport 是vue3 内置组件,接受2个函数(to:指定将在其中移动
请注意,这将移动实际的 DOM 节点,而不是被销毁和重新创建,并且它还将保持任何组件实例的活动状态。所有有状态的 HTML 元素 (即播放的视频) 都将保持其状态。
核心就是to的操作
<template>
<div style="position: relative;">
<h3>Tooltips with Vue 3 Teleport</h3>
<div>
<modal-button></modal-button>
</div>
</div>
</template>
<script>
import Vue from "vue";
var modalButton = {
template: `
<button @click="modalOpen = true">
打开弹窗
</button>
<teleport to="body">
<div v-if="modalOpen" class="modal">
<div>
I'm a modal!
<button @click="modalOpen = false">
Close
</button>
</div>
</div>
</teleport>
`,
data() {
return {
modalOpen: false
};
}
};
export default {
components: {
"modal-button": modalButton
},
created() {}
};
</script>
<style>
.modal {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.modal div {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: white;
width: 300px;
height: 300px;
padding: 5px;
}
</style>
四.VueConf 2021
1.视频
2.尤雨溪:做技术哪有什么两全之策,都是取舍和平衡
还没有评论,来说两句吧...