等价于 vue 2.x 中的 Vue.observable() 函数,vue 3.x 中提供了 reactive() 函数,用来创建 响应式的数据对象。
当(引用)数据 直接改变 不会让模版响应 更新渲染:
一秒后页面没有变化
<template> <div>count: {{state.count}}</div> </template> <script> export default { setup() { const state = { count: 0 } setTimeout(() => { state.count++ }) return { state } } } // 一秒后页面没有变化 </script>
reactive 创建的响应式数据对象,在对象属性发生变化时,模版是可以 响应更新渲染的:
一秒后页面数字从0变成1
<template> <div>count: {{state.count}}</div> </template> <script> import { reactive } from '@vue/composition-api' export default { setup() { const state = reactive({ count: 0 }) setTimeout(() => { state.count++ }, 1000) return { state } } } // 一秒后页面数字从0变成1 </script>
在 Javascript 中,原始类型(如 String,Number)只有值,没有引用。如果在一个函数中返回一个字符串变量,接收到这个字符串的代码只会获得一个值,是无法追踪原始变量后续的变化的。
页面没有变化
<template> <div>count: {{state.count}}</div> </template> <script> import { ref } from '@vue/composition-api' export default { setup() { const count = 0 setTimeout(() => { count++ }, 1000) return { count } } } // 页面没有变化 </script>
因此,包装对象 ref() 的意义就在于提供一个让我们能够在函数之间以引用的方式传递任意类型值的容器。这有点像 React Hooks 中的 useRef —— 但不同的是 Vue 的包装对象同时还是响应式的数据源。有了这样的容器,我们就可以在封装了逻辑的组合函数中将状态以引用的方式传回给组件。组件负责展示(追踪依赖),组合函数负责管理状态(触发更新)。
ref() 返回的是一个 value reference (包装对象)。一个包装对象只有一个属性:.value ,
该属性指向内部被包装的值。包装对象的值可以被直接修改。
直接修改包装对象的值
<script> import { ref } from '@vue/composition-api' export default { setup() { const count = ref(0) console.log('count.value: ', count.value) count.value++ // 直接修改包装对象的值 console.log('count.value: ', count.value) } } // 打印结果: // count.value: 0 // count.value: 1 </script>
当包装对象被暴露给模版渲染上下文,或是被嵌套在另一个响应式对象中的时候,它会被自动展开 (unwrap) 为内部的值:
包装对象 value 属性自动展开
<template> <div>ref count: {{count}}</div> </template> <script> import { ref } from '@vue/composition-api' export default { setup() { const count = ref(0) console.log('count.value: ', count.value) return { count // 包装对象 value 属性自动展开 } } } </script>
也可以用 ref() 包装对象作为 reactive() 创建的对象的属性值,同样属性值 ref() 包装对象也会模版上下文被展开:
包装对象 value 属性自动展开
<template> <div>reactive ref count: {{state.count}}</div> </template> <script> import { reactive, ref } from '@vue/composition-api' export default { setup() { const count = ref(0) const state = reactive({count}) return { state // 包装对象 value 属性自动展开 } } } </script>
在 Vue 2.x 中用实例上的 $refs 属性获取模版元素中 ref 属性标记 DOM 或组件信息,在这里用 ref() 包装对象也可以用来引用页面元素和组件;
<template> <div><p ref="text">Hello</p></div> </template> <script> import { ref } from '@vue/composition-api' export default { setup() { const text = ref(null) setTimeout(() => { console.log('text: ', text.value.innerHTML) }, 1000) return { text } } } // 打印结果: // text: Hello </script>
如果参数是一个 ref 则返回它的 value,否则返回参数本身。
unref():是 val = isRef(val) val.value : val 的语法糖。
const valueRef = ref(''); const value = unref(valueRef); if (!value) { console.warning('请输入要拷贝的内容!'); return; }
检查一个值是否为一个 ref 对象。
把一个响应式对象转换成普通对象,该普通对象的每个 property 都是一个 ref ,和响应式对象 property 一一对应。
并且,当想要从一个组合逻辑函数中返回响应式对象时,用 toRefs 是很有效的,该 API 让消费组件可以 解构 / 扩展(使用 … 操作符)返回的对象,并不会丢失响应性:
解构出来不丢失响应性
<template> <div> <p>count: {{count}}</p> <button @click="increment">+1</button> </div> </template> <script> import { reactive, toRefs } from '@vue/composition-api' export default { setup() { const state = reactive({ count: 0 }) const increment = () => { state.count++ } return { ...toRefs(state), // 解构出来不丢失响应性 increment } } } </script>
computed() 用来创建计算属性,computed() 函数的返回值是一个 ref 的实例。这个值模式是只读的:
import { ref, computed } from '@vue/composition-api' export default { setup() { const count = ref(0) const plusOne = computed(() => count.value + 1) plusOne.value = 10 console.log('plusOne.value: ', plusOne.value) console.log('count.value: ', count.value) } } // 打印结果: // [Vue warn]: Computed property was assigned to but it has no setter. // plusOne.value: 1 // count.value: 0
或者传入一个拥有 get 和 set 函数的对象,创建一个可手动修改的计算状态:
import { ref, computed } from '@vue/composition-api' export default { setup() { const count = ref(0) const plusOne = computed({ get: () => count.value + 1, set: val => { count.value = val - 1 } }) plusOne.value = 10 console.log('plusOne.value: ', plusOne.value) console.log('count.value: ', count.value) } } // 打印结果: // plusOne.value: 10 // count.value: 9