【Vue3】响应式
2022/12/05 12:15:132022/12/01 16:15:15
Vue3 中使用了 Proxy 来创建响应式对象,仅将 getter / setter 用于 ref(文档位置)。
响应性语法糖是实验性功能,默认是禁用的。
使用方式
setup(props, ctx)
使用选项式 API 时可以在 setup()
中声明响应式状态,其返回值如果是对象则会暴露给模板和组件实例,如果返回一个渲染函数 h()
则会替代 <template>
模板(需调用 expose()
方法声明要导出的属性)。
- props 解构需要借助工具函数,否则会失去响应性。
export default {
setup(props, { attrs, slots, emit, expose }) {
// 将 `props` 转为一个其中全是 ref 的对象,然后解构
const { title } = toRefs(props);
// `title` 是一个追踪着 `props.title` 的 ref
console.log(title.value);
// 或者,将 `props` 的单个属性转为一个 ref
const title = toRef(props, "title");
},
};
ctx 是
setup 上下文对象
,这是个非响应式对象,可以正常解构。- ctx.attrs 透传 Attributes(非响应式的对象,等价于 $attrs)。
- ctx.slots 插槽(非响应式的对象,等价于 $slots)。
- ctx.emit 触发事件(函数,等价于 $emit)。
- ctx.expose 调用该函数后,当父组件通过模板引用访问该组件实例时,只能访问
expose
函数暴露出的内容(函数)。
export default {
setup(props, { attrs, slots, emit, expose }) {
// 让组件实例处于 “关闭状态”,即不向父组件暴露任何东西
expose();
const publicCount = ref(0);
// 有选择地暴露局部状态
expose({ count: publicCount });
// 返回一个渲染函数将会阻止我们返回其他东西。
// 对于组件内部来说,这样没有问题,但如果我们想通过模板引用将这个组件的方法暴露给父组件,就需要调用 expose() 方法了
return () => h("div", count.value);
},
};
<script setup>
在组件中使用见 组件配置(<script setup>
)。
在 <script setup>
标签中的导入和声明可以在当前组件模板中直接使用,可以理解为模板中的表达式和 <script setup>
中的代码处在同一个作用域中。
API
reactive()
reactive()
是深层响应,可用 shallowReactive() 方法实现浅层响应。
为保证访问代理的一致性,对同一个原始对象调用 reactive()
会总是返回同样的代理对象,而对一个已存在的代理对象调用 reactive()
会返回其本身:
// 在同一个对象上调用 reactive() 会返回相同的代理
console.log(reactive(raw) === proxy); // true
// 在一个代理上调用 reactive() 会返回它自己
console.log(reactive(proxy) === proxy); // true
reactive() 的局限性
reactive()
方法只对对象类型有效。只有用
reactive()
返回的proxy
对象访问的属性才是响应式的。
const state = reactive({ count: 0 });
// 只有这种调用得到的值是响应式的。
state.count;
// n 是一个局部变量,同 state.count
// 失去响应性连接
let n = state.count;
// 不影响原始的 state
n++;
// count 也和 state.count 失去了响应性连接
let { count } = state;
// 不会影响原始的 state
count++;
// 该函数接收一个普通数字,并且
// 将无法跟踪 state.count 的变化
callSomeFunction(state.count);
ref()
ref()
方法将传入的值包装为一个带 .value
属性的 ref
对象,这样就能将任何值都包装为响应式对象,在未被解包时需要用 .value
去访问属性。
ref 的解包
- 当
ref
对象在模板中作为顶层属性被访问时,它们会被自动“解包”,不需要使用.value
就能读取数据。 - 当
ref
对象被嵌套在一个深层响应式对象中,作为属性被访问或更改时,它会自动解包,因此会表现得和一般的属性一样。