1. composition api
1.1 data → ref / reactive
ref
和 reactive
使得我们主动去包装那些会修改的数据,不变的则将原始对象交给模板;在Vue2中,为了能在模板中使用一些数据,我们通常将那些不变的数据也放在data里面,而Vue默认会为这些数据做响应式,所以就有了Object.freeze()这种优化手段。而这个问题在Vue3中就不用担心了
import { ref, reactive } from "vue";
const type = ref("order");
const pageInfo = reactive({
pageIndex: 0,
pageSize: 20,
});
// methods:直接声明成一个函数
const change = function (e, num) {
pageInfo.pageSize = num;
type.value = "task"; // ref包装的在读取时要使用.value,在模板中不需要
};
用ref
包装原始类型,会被包装成一个RefImpl实例,劫持value属性。用reactive
包装引用类型,返回一个Proxy代理对象
image.png
image.png
1.2 props
- options写法
export default {
props: {
pageType: {
type: String,
default: "task",
},
taskList: {
type: Array,
default: () => [{}, {}],
},
},
};
- 在组合式setup函数中,props也需要选项式定义,并且在setup的第一个参数接收
- 在
<script setup>
中,defineProps
不需要手动引入,且不能重复调用
// 如果仅在模板中使用props,可以不用接收defineProps的返回值
defineProps({
pageType: {
type: String,
default: "task",
},
taskList: {
type: Array,
default: () => [{}, {}],
},
});
// 如果需要在js中使用props,则接收defineProps的返回值
const props = defineProps(["pageType"]);
console.log(props.pageType)
1.3 computed方法
import { reactive, computed } from "vue";
const person = reactive({firstName: '张', lastName: '三'})
person.fullname = computed(() => person.firstName + person.lastName);
1.4 watch
image.png-
watch一个ref
image.png -
watch一个响应式对象(监听整个对象的话,无法拿到正确的oldValue)
import { reactive, watch } from "vue";
const pageInfo = reactive({
pageIndex: { value: 0 },
pageSize: 20,
});
watch(pageInfo, () => { // 深层监听,内部变化都会触发
console.log("pageInfo发生变化", pageInfo);
});
- 如果只需要监听对象的某一个属性,需要用一个返回该属性的 getter 函数
watch(
() => pageInfo.pageIndex.value,
() => {
console.log("pageInfo发生变化", pageInfo);
}
);
- watchEffect简写 ↓
import { watchEffect } from "vue";
watchEffect(() => {
console.log("type变化", type.value, pageInfo.pageSize);
});
// 发现甚至没有传我要watch谁,vue会自动跟踪回调的响应式依赖(你用谁我就监视谁)
// 也就是type.value 和 pageInfo.pageSize变化时都会执行该回调
// watchEffect的immediate为true
有点像computed,初始执行一次,依赖值改变时再执行一次
computed注重返回值。watchEffect注释过程
- watchEffect的好处
对于有多个依赖项的侦听器来说,避免多次使用watch来侦听
watchEffect() 将只跟踪回调中被使用到的属性,而不是像深度跟踪器一样递归跟踪所有的属性
1.5 自定义指令
- options中需要声明directives选项
export default {
directives: {
focus: (el) => { el.focus(); }
}
};
- 在
<script setup>
中使用命名方式来确定一个自定义指令
const vFocus = (el) => { // 类型可以是函数也可以是对象,对象的话可以配置指令的各个钩子
el.focus();
};
// 只看代码的话会觉得这怎么就是个自定义指令呢?
// Vue定义的:<script setup> 中
// 靠命名来判定它是一个自定义指令:1: 变量以 v 开头 2: 驼峰式命名
// 那么假如我不知道这个规则,刚好这样命名了一个变量,值为原始类型?
结果是:不会报错,不过也没有什么效果罢了
指令钩子
bind -》 beforeMount 指令与元素成功绑定
inserted -》 mounted 指令所在元素插入页面时
update -》updated 父节点和子节点更新后调用
指令定义为函数类型时,默认钩子是:
——在Vue3中:mounted 和 updated
——在vue2中是:bind 和 update
2. setup选项:
setup是一个函数,需要有一个返回值(
<script setup>
则不需要返回值)
- 当返回值是一个对象,则对象属性都能用于模板中
- 也可以返回一个渲染函数,则忽略模板中的内容
但不能是一个async函数。唯一可以使用
async setup()
的情况是,该组件是 Suspense 组件的后裔。
setup中不包含对组件实例的访问权,它的执行执行时机在beforeCreate之前。
在 <script setup> 中创建的变量不会作为属性添加到组件实例中,这使得它们无法从选项式 API 中访问。如果是setup函数的返回值,则会添加,选项式API中可访问
image.pngsetup函数收到的2个参数
3. toRef和toRefs
image.png image.png image.png将响应式对象的某个属性交给另一个变量去使用时,不想丢失响应式。
4. shallowReactive和shallowRef
只对对象的根级别属性做响应式
-
shallowReactive
image.png
-
shallowRef
image.png
5. toRaw
返回代理对象的原对象。代理对象和原对象的联系:
- 一个是在reactiveMap中保存了这种关联关系(类型是WeakMap,key是原对象,value是代理对象)
- 一种是在new Proxy时配置的getter函数内部,可以同时访问到原对象和代理对象的引用
- readonly包装一个reactive生成的响应式对象,再对它进行toRaw,结果还是最初的原始对象。所以toRaw并不是简单读取这个响应式对象上的
__v_raw
的属性,而是要递归取最原始的那个对象
const src = { info: { name: "祖先" } };
const ret = reactive(src);
const locked = readonly(ret);
console.log(toRaw(locked) === src); // true —— readonly包装的对象toRaw也是那个最初的src,而不是ret
console.log(toRaw(ret) === src); // true
image.png
6. markRaw
将一个新对象挂在响应式对象上时它也会变成响应式对象,如果你不想为这个内部的对象做响应式,可以使用markRaw
const a = reactive({
age: 20,
tool: markRaw(Tooltip),
});
使用 markRaw 的前提是:你这个对象(例如上面的Tooltip)是一个不会更改的对象,而且不应在模板中使用
因为 markRaw 虽然为你跳过了响应式,一旦你改了这个对象(Tooltip,而且模板中展示了这个Tooltip)
而当响应式对象中其他数据发生变更(例如改变age)导致模板更新,则这个对象的变更也会反映在模板上
-
markRaw和readonly的区别
image.png
7. customRef
借用vue的【收集依赖track】和【通知更新trigger】,再加上自己的getter和setter拦截逻辑,创建一个自定义ref。
- 如果不拦截set的赋值逻辑,只是在值修改的时候执行其他逻辑,那么watch也可以实现
- 不过想要拦截getter的话,就可以用customRef,自已定义【一部分】getter和setter逻辑
- customRef传入一个工厂函数,这个工厂函数接收2个参数:track, trigger,并返回一个有get和set方法的对象。
import { customRef } from "vue";
const debounceChangeValue = (value, interval) => {
let timer = null;
return customRef((track, trigger) => {
// customRef传入一个工厂函数,这个工厂函数接收2个参数:track, trigger
// 并返回一个有get和set方法的对象
return {
get() {
track(); // 收集依赖(必需)
return value;
},
set(newValue) {
clearTimeout(timer);
timer = setTimeout(() => {
value = newValue;
trigger(); // 数据修改之后需要通知依赖(必需)
}, interval);
},
};
});
};
const text = debounceChangeValue("初始值", 300);
8. provide/inject
在
<script setup>
中,祖先已经可以向后代组件提供响应式数据了
-
响应式数据的控制权应掌握在提供方手里(可以对外提供 【readonly的数据】和【修改函数】供后代调用,而修改权仍然在提供方)
image.png
9. 一些判断函数: isRef / isReactive / isReadonly / isProxy
10. 内置组件:Teleport 传送
它可以将组件内部的一部分模板传送到该组件DOM之外的结构中去,即该组件内部的DOM不再受组件层级的影响。
<Teleport to="body">
<p>我的DOM直接添加在body下面</p>
</Teleport>
<Teleport to="#app">
<p>我的DOM直接追加在#app里面</p>
</Teleport>
网友评论