vue3 API 详解笔迹
文章目录
- vue3 API 详解笔迹
- createApp()
- createSSRApp()
- app.mount()
- app.unmount()
- app.component()
- app.directive()
- app.use()
- app.mixin()
- app.provide()
- app.runWithContext()
- app.version
- app.config
- app.config.errorHandler
- app.config.warnHandler
- app.config.performance
- app.config.compilerOptions
- app.config.globalProperties
- app.config.optionMergeStrategies
- defineComponent()
- 函数签名
- defineAsyncComponent()
- defineCustomElement()
- setup()
- 访问 Props
- 与渲染函数一起使用
- ref()
- computed()
- reactive()
- readonly()
- watchEffect()
- watchPostEffect()
- watchSyncEffect()
- watch()
- isRef()
- unref()
- toRef()
- toValue()
- toRefs()
- isProxy()
- isReactive()
- isReadonly()
createApp()
创建一个应用实例。
function createApp(rootComponent: Component, rootProps?: object): App
第一个参数是根组件。第二个参数可选,它是要传递给根组件的 props。
可以直接内联根组件:
import { createApp } from 'vue'
const app = createApp({
/* 根组件选项 */
})
也可以使用从别处导入的组件:
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
createSSRApp()
以 SSR 激活模式创建一个应用实例。用法与 createApp() 完全相同。
SSR
是Server Side Render
的缩写,简单来讲:服务端渲染 就是网页上面呈现的内容在服务器端就已经生成好了,当用户浏览网页时,服务器把这个在服务端生成好的完整的html结构内容响应给浏览器,而浏览器拿到这个完整的html结构内容后直接显示(渲染)在页面上的过程。
app.mount()
将应用实例挂载在一个容器元素中。
interface App {
mount(rootContainer: Element | string): ComponentPublicInstance
}
参数可以是一个实际的 DOM
元素或一个 CSS
选择器 (使用第一个匹配到的元素)。返回根组件的实例。
如果该组件有模板或定义了渲染函数,它将替换容器内所有现存的 DOM
节点。否则在运行时编译器可用的情况下,容器元素的 innerHTML
将被用作模板。
在 SSR
激活模式下,它将激活容器内现有的 DOM
节点。如果出现了激活不匹配,那么现有的 DOM
节点将会被修改以匹配客户端的实际渲染结果。
对于每个应用实例,mount()
仅能调用一次。
import { createApp } from 'vue'
const app = createApp(/* ... */)
app.mount('#app') || app.mount(document.body.firstChild)
app.unmount()
卸载一个已挂载的应用实例。卸载一个应用会触发该应用组件树内所有组件的卸载生命周期钩子。
const app = createApp(App)
app.use(ElementPlus)
app.use(router)
app.use(pinia)
app.mount('#app')
let timer = setTimeout(()=>{
app.unmount()
},2000)
app.component()
如果同时传递一个组件名字符串及其定义,则注册一个全局组件;如果只传递一个名字,则会返回用该名字注册的组件 (如果存在的话)。
import { createApp } from 'vue'
const app = createApp({})
// 注册一个选项对象
app.component('my-component', {
/* ... */
})
// 得到一个已注册的组件
const MyComponent = app.component('my-component')
app.directive()
如果同时传递一个名字和一个指令定义,则注册一个全局指令;如果只传递一个名字,则会返回用该名字注册的指令 (如果存在的话)。
import { createApp } from 'vue'
const app = createApp({
/* ... */
})
// 注册(对象形式的指令)
app.directive('my-directive', {
/* 自定义指令钩子 */
})
// 注册(函数形式的指令)
app.directive('my-directive', () => {
/* ... */
})
// 得到一个已注册的指令
const myDirective = app.directive('my-directive')
app.use()
安装一个插件。
第一个参数应是插件本身,可选的第二个参数是要传递给插件的选项。
插件可以是一个带 install()
方法的对象,亦或直接是一个将被用作 install()
方法的函数。插件选项 (app.use()
的第二个参数) 将会传递给插件的 install()
方法。
若 app.use()
对同一个插件多次调用,该插件只会被安装一次。
import { createApp } from 'vue'
import MyPlugin from './plugins/MyPlugin'
const app = createApp({
/* ... */
})
app.use(MyPlugin)
app.mixin()
应用一个全局 mixin
(适用于该应用的范围)。一个全局的 mixin
会作用于应用中的每个组件实例。
不推荐
Mixins
在 Vue 3
支持主要是为了向后兼容,因为生态中有许多库使用到。在新的应用中应尽量避免使用 mixin
,特别是全局 mixin
。
若要进行逻辑复用,推荐用组合式函数来替代。
app.provide()
提供一个值,可以在应用中的所有后代组件中注入使用。
第一个参数应当是注入的 key,第二个参数则是提供的值。返回应用实例本身。
import { createApp } from 'vue'
const app = createApp(/* ... */)
app.provide('message', 'hello')
let name = ref('name');
function setName (str){
name.value = str
}
provide('name',{name,setName})
app.runWithContext()
使用当前应用作为注入上下文执行回调函数。
在App
上增加了一个runWithContext()
可以用于确保对应用程序级别的全局变量存在,比如通过provide
的各个值,可以用于改进vue
生态的各个包,pinia/vue-router
之类的
需要一个回调函数并立即运行该回调。在回调同步调用期间,即使没有当前活动的组件实例,inject()
调用也可以从当前应用提供的值中查找注入。回调的返回值也将被返回。
import { inject } from 'vue'
app.provide('id', 1)
const injected = app.runWithContext(() => {
return inject('id')
})
console.log(injected) // 1
app.version
提供当前应用所使用的 Vue
版本号。这在插件中很有用,因为可能需要根据不同的 Vue
版本执行不同的逻辑。
console.log(app.version,'version')
app.config
每个应用实例都会暴露一个 config
对象,其中包含了对这个应用的配置设定。你可以在挂载应用前更改这些属性 (下面列举了每个属性的对应文档)。
import { createApp } from 'vue'
const app = createApp(/* ... */)
console.log(app.config)
app.config.errorHandler
用于为应用内抛出的未捕获错误指定一个全局处理函数。
错误处理器接收三个参数:错误对象、触发该错误的组件实例和一个指出错误来源类型信息的字符串。
它可以从下面这些来源中捕获错误:
- 组件渲染器
- 事件处理器
- 生命周期钩子
setup()
函数- 侦听器
- 自定义指令钩子
- 过渡 (Transition) 钩子
app.config.errorHandler = (err, instance, info) => {
// 处理错误,例如:报告给一个服务
}
app.config.warnHandler
用于为 Vue
的运行时警告指定一个自定义处理函数。
警告处理器将接受警告信息作为其第一个参数,来源组件实例为第二个参数,以及组件追踪字符串作为第三个参数。
这可以用户过滤筛选特定的警告信息,降低控制台输出的冗余。所有的 Vue 警告都需要在开发阶段得到解决,因此仅建议在调试期间选取部分特定警告,并且应该在调试完成之后立刻移除。
app.config.warnHandler = (msg, instance, trace) => {
// `trace` 是组件层级结构的追踪
}
app.config.performance
设置此项为 true
可以在浏览器开发工具的“性能/时间线”页中启用对组件初始化、编译、渲染和修补的性能表现追踪。仅在开发模式
和支持 performance.mark API
的浏览器中工作。
app.config.compilerOptions
配置运行时编译器的选项。设置在此对象上的值将会在浏览器内进行模板编译时使用,并会影响到所配置应用的所有组件。另外你也可以通过 compilerOptions
选项在每个组件的基础上覆盖这些选项。
此配置项仅在完整构建版本,即可以在浏览器中编译模板的 vue.js
文件中可用。如果你用的是带构建的项目配置,且使用的是仅含运行时的 Vue
文件版本,那么编译器选项必须通过构建工具的相关配置传递给 @vue/compiler-dom
。
vue-loader
:通过compilerOptions loader
的选项传递。并请阅读如何在vue-cli
中配置它。vite
:通过@vitejs/plugin-vue
的选项传递。
app.config.compilerOptions.isCustomElement
指定一个方法来识别 Vue
以外 (例如通过 Web Components API
) 定义的自定义元素。如果一个组件匹配了这个条件,它就不需要在本地或全局注册,Vue
也不会抛出 Unknown custom element
的警告。
// 任何以 'ion-' 开头的元素都会被识别为自定义元素
app.config.compilerOptions.isCustomElement = tag => tag.startsWith('ion-')
app.config.compilerOptions.whitespace
默认情况下,Vue
会移除/压缩模板元素之间的空格以产生更高效的编译结果:
- 元素内的多个开头/结尾空格会被压缩成一个空格
- 元素之间的包括折行在内的多个空格会被移除
- 文本结点之间可被压缩的空格都会被压缩成为一个空格
将值设置为preserve
可以禁用 (2) 和 (3)。
//默认 condense
app.config.compilerOptions.whitespace = 'preserve'
app.config.compilerOptions.delimiters
用于配置模板内文本插值的分隔符。
这个选项一般会用于避免和同样使用双大括号语法的服务端框架发生冲突。
// 将分隔符设置为 ES6 模板字符串风格
app.config.compilerOptions.delimiters = ['${', '}']
app.config.compilerOptions.comments
默认情况下,Vue
会在生产环境下移除模板内的 HTML
注释。将这个选项设置为 true
可以强制 Vue
在生产环境下保留注释。而在开发环境下注释是始终被保留的。
这个选项一般会用于依赖 HTML
注释的其它库和 Vue
配合使用。
app.config.globalProperties
一个用于注册能够被应用内所有组件实例访问到的全局属性的对象。
这是对 Vue 2
中 Vue.prototype
使用方式的一种替代,此写法在 Vue 3
已经不存在了。与任何全局的东西一样,应该谨慎使用。
如果全局属性与组件自己的属性冲突,组件自己的属性将具有更高的优先级。
app.config.globalProperties.msg = 'hello'
在页面中获取globalProperties
中的属性:
import { getCurrentInstance } from "vue";
const { appContext } = getCurrentInstance();
const callHello = ()=>{
appContext.config.globalProperties.$hello('jay');
}
callHello()
app.config.optionMergeStrategies
一个用于定义自定义组件选项的合并策略的对象。
一些插件或库对自定义组件选项添加了支持 (通过注入全局 mixin
)。这些选项在有多个不同来源时可能需要特殊的合并策略 (例如 mixin
或组件继承)。
可以在 app.config.optionMergeStrategies
对象上以选项的名称作为 key
,可以为一个自定义选项注册分配一个合并策略函数。
合并策略函数分别接受在父实例和子实例上定义的该选项的值作为第一和第二个参数。
defineComponent()
在定义 Vue
组件时提供类型推导的辅助函数。
// 选项语法
function defineComponent(
component: ComponentOptions
): ComponentConstructor
// 函数语法 (需要 3.3+)
function defineComponent(
setup: ComponentOptions['setup'],
extraOptions?: ComponentOptions
): () => any
第一个参数是一个组件选项对象。返回值将是该选项对象本身,因为该函数实际上在运行时没有任何操作,仅用于提供类型推导。
注意返回值的类型有一点特别:它会是一个构造函数类型,它的实例类型是根据选项推断出的组件实例类型。这是为了能让该返回值在 TSX
中用作标签时提供类型推导支持。
你可以像这样从 defineComponent()
的返回类型中提取出一个组件的实例类型 (与其选项中的 this
的类型等价):
const Foo = defineComponent(/* ... */)
type FooInstance = InstanceType<typeof Foo>
函数签名
defineComponent()
还有一种备用签名,旨在与组合式 API
和 渲染函数或 JSX 一起使用。
与传递选项对象不同的是,它需要传入一个函数。这个函数的工作方式与组合式 API 的 setup() 函数相同:它接收 props
和 setup
上下文。返回值应该是一个渲染函数——支持 h()
和 JSX
:
import { ref, h } from 'vue'
const Comp = defineComponent(
(props) => {
// 就像在 <script setup> 中一样使用组合式 API
const count = ref(0)
return () => {
// 渲染函数或 JSX
return h('div', count.value)
}
},
// 其他选项,例如声明 props 和 emits。
{
props: {
/* ... */
}
}
)
defineAsyncComponent()
定义一个异步组件,它在运行时是懒加载的。参数可以是一个异步加载函数,或是对加载行为进行更具体定制的一个选项对象。
defineCustomElement()
这个方法和 defineComponent
接受的参数相同,不同的是会返回一个原生自定义元素类的构造器。
除了常规的组件选项,defineCustomElement()
还支持一个特别的选项 styles
,它应该是一个内联 CSS
字符串的数组,所提供的 CSS
会被注入到该元素的 shadow root
上。
返回值是一个可以通过 customElements.define()
注册的自定义元素构造器。
setup()
setup()
钩子是在组件中使用组合式 API
的入口,通常只在以下情况下使用:
- 需要在非单文件组件中使用组合式 API 时。
- 需要在基于选项式 API 的组件中集成基于组合式 API 的代码时。
我们可以使用响应式 API
来声明响应式的状态,在 setup()
函数中返回的对象会暴露给模板和组件实例。其他的选项也可以通过组件实例来获取 setup()
暴露的属性:
<template>
<p>{{ count }}</p>
<button @click="handleAdd">handleAdd</button>
</template>
<script>
import {ref} from "vue";
export default {
name: "page",
setup() {
let count = ref(0)
const handleAdd = function () {
count.value++
}
return {
count, handleAdd
}
},
}
</script>
<style scoped>
</style>
在模板中访问从 setup
返回的 ref
时,它会自动浅层解包,因此你无须再在模板中为它写 .value
。当通过 this
访问时也会同样如此解包。
setup()
自身并不含对组件实例的访问权,即在 setup()
中访问 this
会是 undefined
。你可以在选项式 API
中访问组合式 API
暴露的值,但反过来则不行。
setup()
应该同步地返回一个对象。唯一可以使用 async setup()
的情况是,该组件是 Suspense 组件的后裔。
访问 Props
setup
函数的第一个参数是组件的 props
。和标准的组件一致,一个 setup
函数的 props
是响应式的,并且会在传入新的 props
时同步更新。
export default {
props: {
title: String
},
setup(props) {
console.log(props.title)
}
}
请注意如果你解构了 props
对象,解构出的变量将会丢失响应性。因此我们推荐通过 props.xxx
的形式来使用其中的 props
。
如果你确实需要解构 props
对象,或者需要将某个 prop
传到一个外部函数中并保持响应性,那么你可以使用 toRefs()
和 toRef()
这两个工具函数:
import { toRefs, toRef } from 'vue'
export default {
setup(props) {
// 将 `props` 转为一个其中全是 ref 的对象,然后解构
const { title } = toRefs(props)
// `title` 是一个追踪着 `props.title` 的 ref
console.log(title.value)
// 或者,将 `props` 的单个属性转为一个 ref
const title = toRef(props, 'title')
}
}
传入 setup
函数的第二个参数是一个 Setup
上下文对象。上下文对象暴露了其他一些在 setup
中可能会用到的值:
export default {
setup(props, context) {
// 透传 Attributes(非响应式的对象,等价于 $attrs)
console.log(context.attrs)
// 插槽(非响应式的对象,等价于 $slots)
console.log(context.slots)
// 触发事件(函数,等价于 $emit)
console.log(context.emit)
// 暴露公共属性(函数)
console.log(context.expose)
}
}
该上下文对象是非响应式的,可以安全地解构:
export default {
setup(props, { attrs, slots, emit, expose }) {
...
}
}
attrs
和 slots
都是有状态的对象,它们总是会随着组件自身的更新而更新。这意味着你应当避免解构它们,并始终通过 attrs.x
或 slots.x
的形式使用其中的属性。此外还需注意,和 props
不同,attrs
和 slots
的属性都不是响应式的。如果你想要基于 attrs
或 slots
的改变来执行副作用,那么你应该在 onBeforeUpdate
生命周期钩子中编写相关逻辑。
暴露公共属性
expose
函数用于显式地限制该组件暴露出的属性,当父组件通过模板引用访问该组件的实例时,将仅能访问 expose
函数暴露出的内容:
export default {
setup(props, { expose }) {
// 让组件实例处于 “关闭状态”
// 即不向父组件暴露任何东西
expose()
const publicCount = ref(0)
const privateCount = ref(0)
// 有选择地暴露局部状态
expose({ count: publicCount })
}
}
与渲染函数一起使用
setup
也可以返回一个渲染函数,此时在渲染函数中可以直接使用在同一作用域下声明的响应式状态:
返回一个渲染函数将会阻止我们返回其他东西。对于组件内部来说,这样没有问题,但如果我们想通过模板引用将这个组件的方法暴露给父组件,那就有问题了。
我们可以通过调用 expose()
解决这个问题:
import { h, ref } from 'vue'
export default {
setup(props, { expose }) {
const count = ref(0)
const increment = () => ++count.value
expose({
increment
})
return () => h('div', count.value)
}
}
此时父组件可以通过模板引用来访问这个 increment
方法。
ref()
接受一个内部值,返回一个响应式的、可更改的 ref
对象,此对象只有一个指向其内部值的属性 .value
。
ref
对象是可更改的,也就是说你可以为 .value
赋予新的值。它也是响应式的,即所有对 .value
的操作都将被追踪,并且写操作会触发与之相关的副作用。
如果将一个对象赋值给 ref
,那么这个对象将通过 reactive()
转为具有深层次响应式的对象。这也意味着如果对象中包含了嵌套的 ref
,它们将被深层地解包。
若要避免这种深层次的转换,请使用 shallowRef()
来替代。
const count = ref(0)
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
computed()
接受一个 getter 函数
,返回一个只读的响应式 ref
对象。该 ref
通过 .value
暴露 getter
函数的返回值。它也可以接受一个带有 get
和 set
函数的对象来创建一个可写的 ref
对象。
const count = ref(1)
const plusOne = computed(() => count.value + 1)
console.log(plusOne.value) // 2
plusOne.value++ // 错误
创建一个可写的计算属性 ref:
const count = ref(1)
const plusOne = computed({
get: () => count.value + 1,
set: (val) => {
count.value = val - 1
}
})
plusOne.value = 1
console.log(count.value) // 0
调试:
const plusOne = computed(() => count.value + 1, {
onTrack(e) {
debugger
},
onTrigger(e) {
debugger
}
})
reactive()
返回一个对象的响应式代理。
响应式转换是“深层”
的:它会影响到所有嵌套的属性。一个响应式对象也将深层地解包任何 ref
属性,同时保持响应性。
值得注意的是,当访问到某个响应式数组或 Map
这样的原生集合类型中的 ref
元素时,不会执行 ref
的解包。
若要避免深层响应式转换,只想保留对这个对象顶层次访问的响应性,请使用 shallowReactive()
作替代。
返回的对象以及其中嵌套的对象都会通过 ES Proxy
包裹,因此不等于源对象,建议只使用响应式代理,避免使用原始对象。
创建一个响应式对象:
const obj = reactive({ count: 0 })
obj.count++
ref 的解包:
const count = ref(1)
const obj = reactive({ count })
// ref 会被解包
console.log(obj.count === count.value) // true
// 会更新 `obj.count`
count.value++
console.log(count.value) // 2
console.log(obj.count) // 2
// 也会更新 `count` ref
obj.count++
console.log(obj.count) // 3
console.log(count.value) // 3
注意当访问到某个响应式数组或 Map
这样的原生集合类型中的 ref
元素时,不会执行 ref
的解包:
const books = reactive([ref('Vue 3 Guide')])
// 这里需要 .value
console.log(books[0].value)
const map = reactive(new Map([['count', ref(0)]]))
// 这里需要 .value
console.log(map.get('count').value)
将一个 ref
赋值给一个 reactive
属性时,该 ref
会被自动解包:
const count = ref(1)
const obj = reactive({})
obj.count = count
console.log(obj.count) // 1
console.log(obj.count === count.value) // true
readonly()
接受一个对象 (不论是响应式还是普通的) 或是一个 ref
,返回一个原值的只读代理。
只读代理是深层的:对任何嵌套属性的访问都将是只读的。它的 ref
解包行为与 reactive()
相同,但解包得到的值是只读的。
要避免深层级的转换行为,请使用 shallowReadonly()
作替代。
const original = reactive({ count: 0 })
const copy = readonly(original)
watchEffect(() => {
// 用来做响应性追踪
console.log(copy.count)
})
// 更改源属性会触发其依赖的侦听器
original.count++
// 更改该只读副本将会失败,并会得到一个警告
copy.count++ // warning!
watchEffect()
立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行。
第一个参数就是要运行的副作用函数。这个副作用函数的参数也是一个函数,用来注册清理回调。清理回调会在该副作用下一次执行前被调用,可以用来清理无效的副作用,例如等待中的异步请求 (参见下面的示例)。
第二个参数是一个可选的选项,可以用来调整副作用的刷新时机或调试副作用的依赖。
默认情况下,侦听器将在组件渲染之前执行。设置 flush: 'post'
将会使侦听器延迟到组件渲染之后再执行。详见回调的触发时机。在某些特殊情况下 (例如要使缓存失效),可能有必要在响应式依赖发生改变时立即触发侦听器。这可以通过设置 flush: 'sync'
来实现。然而,该设置应谨慎使用,因为如果有多个属性同时更新,这将导致一些性能和数据一致性的问题。
有时副作用函数会执行一些异步的副作用,这些响应需要在其失效时清除 (场景:有一个页码组件里面有5个页码,点击就会异步请求数据。于是做一个监听,监听当前页码,只要有变化就请求一次。问题:如果点击的比较快,从1到5全点了一遍,那么会有5个请求,最终页面会显示第几页的内容?第5页?那是假定请求第5页的ajax响应的最晚,事实呢?并不一定。于是这就会导致错乱。还有一个问题,连续快速点5次页码,等于我并不想看前4页的内容,那么是不是前4次的请求都属于带宽浪费?这也不好。
于是官方就给出了一种解决办法:
侦听副作用传入的函数可以接收一个 onInvalidate 函数作入参,用来注册清理失效时的回调。
当以下情况发生时,这个失效回调会被触发:
- 侦听器被停止 (如果在 setup() 或生命周期钩子函数中使用了 watchEffect,则在组件卸载时)
- 副作用即将重新执行时;
返回值是一个用来停止该副作用的函数。
<template>
<input type="text" v-model="obj.name">
<button @click="handleStop">停止监听</button>
</template>
<script setup>
import {ref, defineAsyncComponent, onMounted, computed, reactive, shallowReactive, readonly, watchEffect} from "vue";
import DemoBox from '@/components/DemoBox/index.vue'
const obj = reactive({name:'张三'})
const storeName = watchEffect(()=>{
console.log(obj,'obj')
console.log(obj.name,'obj.name')
})
function handleStop(){
//关闭
storeName()
}
</script>
<style scoped>
</style>
watchPostEffect()
watchEffect()
使用 flush: 'post'
选项时的别名。
watchSyncEffect()
watchEffect()
使用 flush: 'sync'
选项时的别名。
watch()
侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数。
watch()
默认是懒侦听的,即仅在侦听源发生变化时才执行回调函数。
第一个参数是侦听器的源。这个来源可以是以下几种:
- 一个函数,返回一个值
- 一个 ref
- 一个响应式对象
- …或是由以上类型的值组成的数组
第二个参数是在发生变化时要调用的回调函数。这个回调函数接受三个参数:新值、旧值,以及一个用于注册副作用清理的回调函数。该回调函数会在副作用下一次重新执行前调用,可以用来清除无效的副作用,例如等待中的异步请求。
当侦听多个来源时,回调函数接受两个数组,分别对应来源数组中的新值和旧值。
immediate
:在侦听器创建时立即触发回调。第一次调用时旧值是undefined
。deep
:如果源是对象,强制深度遍历,以便在深层级变更时触发回调。flush
:调整回调函数的刷新时机。onTrack / onTrigger
:调试侦听器的依赖。
与 watchEffect()
相比,watch()
使我们可以:
- 懒执行副作用;
- 更加明确是应该由哪个状态触发侦听器重新执行;
- 可以访问所侦听状态的前一个值和当前值。
<template>
<input type="text" v-model="obj.name">
<input type="text" v-model="str">
{{obj.name}}
</template>
<script setup>
import {
ref,
defineAsyncComponent,
onMounted,
computed,
reactive,
shallowReactive,
readonly,
watchEffect,
watch
} from "vue";
import DemoBox from '@/components/DemoBox/index.vue'
const obj = reactive({name:'张三'})
const str = ref(0)
// watch(obj,(newVal,oldVal)=>{
// console.log(newVal,oldVal)
// })
// watch(()=>obj.name,(newVal,oldVal)=>{
// console.log(newVal,oldVal)
// })
watch([()=>obj.name,str],([newVal1,newVal2],[oldVal1,oldVal2])=>{
console.log(newVal1,oldVal1,'reactive')
console.log(newVal2,oldVal2,'ref')
})
</script>
<style scoped>
</style>
停止侦听器
const stop = watch(source, callback)
// 当已不再需要该侦听器时:
stop()
副作用清理:
watch(id, async (newId, oldId, onCleanup) => {
const { response, cancel } = doAsyncWork(newId)
// 当 `id` 变化时,`cancel` 将被调用,
// 取消之前的未完成的请求
onCleanup(cancel)
data.value = await response
})
isRef()
检查某个值是否为 ref
。
请注意,返回值是一个类型判定 (type predicate
),这意味着 isRef
可以被用作类型守卫:
const obj = reactive({name:'张三'})
const str = ref(0)
console.log(isRef(str)) true
console.log(isRef(obj)) false
unref()
如果参数是 ref
,则返回内部值,否则返回参数本身。这是 val = isRef(val) ? val.value : val
计算的一个语法糖。
const obj = reactive({name:'张三'})
const str = ref(0)
console.log(unref(str)) 0
console.log(unref(obj)) { "name": "张三" }
toRef()
可以将值
、refs
或 getters
规范化为 refs (3.3+)
。
也可以基于响应式对象上的一个属性,创建一个对应的 ref
。这样创建的 ref
与其源属性保持同步:改变源属性的值将更新 ref
的值,反之亦然。
<template>
<input type="text" v-model="obj.name">
{{obj.name}}--{{obj1}}--{{obj2}}--{{obj3}}
</template>
<script setup>
import {
reactive, toRef
} from "vue";
const obj = reactive({name:'张三'})
const obj1 = toRef(obj,'name')
const obj2 = toRef(obj.name)
const obj3 = toRef(()=>obj.name)
obj1.value = '李四'
obj.name='王五'
console.log(obj.name,'obj')
console.log(obj1,'obj1')
console.log(obj2)
</script>
toValue()
将值、refs 或 getters
规范化为值。这与 unref()
类似,不同的是此函数也会规范化 getter
函数。如果参数是一个 getter
,它将会被调用并且返回它的返回值。
这可以在组合式函数中使用,用来规范化一个可以是值、ref
或 getter
的参数。
const str1 = ref('123')
console.log(str1)
console.log(toValue(str1))
toRefs()
将一个响应式对象转换为一个普通对象,这个普通对象的每个属性都是指向源对象相应属性的 ref
。每个单独的 ref
都是使用 toRef()
创建的。
template
要想访问toRefs
的值,需要带上.value
如果不带上,就会出现双引号。template
要想访问toRef
的值,不需要带上.value
<template>
{{arr.name}}--{{arr1}}--{{arr2.name.value}}
</template>
<script setup>
import {reactive, ref, toRef, toRefs, toValue} from "vue";
const str1 = ref('123')
const arr = reactive({name:'张三'})
const arr1 = toRef(arr,'name')
const arr2 = toRefs(arr)
</script>
<style scoped>
</style>
isProxy()
检查一个对象是否是由 reactive()
、readonly()
、shallowReactive()
或 shallowReadonly()
创建的代理。
const str1 = ref('123')
const arr = reactive({name:'张三'})
const str2 = readonly(ref(23))
const str3 = shallowReactive(reactive({name:'李四'}))
const str4 = shallowReadonly(ref(45))
let str5 = 5
console.log(isProxy(str1))
console.log(isProxy(arr))
console.log(isProxy(str2))
console.log(isProxy(str3))
console.log(isProxy(str4))
console.log(isProxy(str5))
isReactive()
检查一个对象是否是由 reactive()
或 shallowReactive()
创建的代理。
isReadonly()
检查传入的值是否为只读对象。只读对象的属性可以更改,但他们不能通过传入的对象直接赋值。
通过 readonly()
和 shallowReadonly()
创建的代理都是只读的,因为他们是没有 set
函数的 computed() ref
。