Provide (提供)#
要为组件后代提供数据,需要使用到 provide
选项:
js
export default {
provide: {
message: 'hello!'
}
}
对于 provide
对象上的每一个属性,后代组件会用其 key 为注入名查找期望注入的值,属性的值就是要提供的数据。
如果我们需要提供依赖当前组件实例的状态 (比如那些由 data()
定义的数据属性),那么可以以函数形式使用 provide
:
js
export default {
data() {
return {
message: 'hello!'
}
},
provide() {
// 使用函数的形式,可以访问到 `this`
return {
message: this.message
}
}
}
然而,请注意这不会使注入保持响应性。我们会在后续小节中讨论如何让注入转变为响应式。
应用层 Provide#
除了在一个组件中提供依赖,我们还可以在整个应用层面提供依赖:
js
import { createApp } from 'vue'
const app = createApp({})
app.provide(/* 注入名 */ 'message', /* 值 */ 'hello!')
在应用级别提供的数据在该应用内的所有组件中都可以注入。这在你编写插件时会特别有用,因为插件一般都不会使用组件形式来提供值。
Inject (注入)#
要注入上层组件提供的数据,需使用 inject
选项来声明:
js
export default {
inject: ['message'],
created() {
console.log(this.message) // injected value
}
}
注入会在组件自身的状态之前被解析,因此你可以在 data()
中访问到注入的属性:
js
export default {
inject: ['message'],
data() {
return {
// 基于注入值的初始数据
fullMessage: this.message
}
}
}
输入参数#
尽管其响应性不依赖 ref,组合式函数仍可接收 ref 参数。如果编写的组合式函数会被其他开发者使用,你最好在处理输入参数时兼容 ref 而不只是原始的值。unref()
工具函数会对此非常有帮助:
js
import { unref } from 'vue'
function useFeature(maybeRef) {
// 若 maybeRef 确实是一个 ref,它的 .value 会被返回
// 否则,maybeRef 会被原样返回
const value = unref(maybeRef)
}
如果你的组合式函数在接收 ref 为参数时会产生响应式 effect,请确保使用 watch()
显式地监听此 ref,或者在 watchEffect()
中调用 unref()
来进行正确的追踪。
除了 Vue 内置的一系列指令 (比如 v-model
或 v-show
) 之外,Vue 还允许你注册自定义的指令 (Custom Directives)。
我们已经介绍了两种在 Vue 中重用代码的方式:组件和组合式函数。组件是主要的构建模块,而组合式函数则侧重于有状态的逻辑。另一方面,自定义指令主要是为了重用涉及普通元素的底层 DOM 访问的逻辑。
一个自定义指令由一个包含类似组件生命周期钩子的对象来定义。钩子函数会接收到指令所绑定元素作为其参数。下面是一个自定义指令的例子,当一个 input 元素被 Vue 插入到 DOM 中后,它会被自动聚焦:
js
const focus = {
mounted: (el) => el.focus()
}
export default {
directives: {
// 在模板中启用 v-focus
focus
}
}
template
<input v-focus />
插件没有严格定义的使用范围,但是插件发挥作用的常见场景主要包括以下几种:
-
通过
app.component()
和app.directive()
注册一到多个全局组件或自定义指令。 -
通过
app.provide()
使一个资源可被注入进整个应用。 -
向
app.config.globalProperties
中添加一些全局实例属性或方法 -
一个可能上述三种都包含了的功能库 (例如 vue-router)。
这个组件中有一个 <button> 按钮来触发打开模态框,和一个 class 名为 .modal 的 <div>,它包含了模态框的内容和一个用来关闭的按钮。
当在初始 HTML 结构中使用这个组件时,会有一些潜在的问题:
position: fixed 能够相对于浏览器窗口放置有一个条件,那就是不能有任何祖先元素设置了 transform、perspective 或者 filter 样式属性。也就是说如果我们想要用 CSS transform 为祖先节点 <div class="outer"> 设置动画,就会不小心破坏模态框的布局!
这个模态框的 z-index 受限于它的容器元素。如果有其他元素与 <div class="outer"> 重叠并有更高的 z-index,则它会覆盖住我们的模态框。
<Teleport> 提供了一个更简单的方式来解决此类问题,让我们不需要再顾虑 DOM 结构的问题。让我们用 <Teleport> 改写一下 <MyModal>:
template
<button @click="open = true">Open Modal</button>
<Teleport to="body">
<div v-if="open" class="modal">
<p>Hello from the modal!</p>
<button @click="open = false">Close</button>
</div>
</Teleport>
不使用构建工具#
如果你没有使用任何构建工具,而是从 CDN 或其他源来加载 Vue,请确保在部署时使用的是生产环境版本(以 .prod.js
结尾的构建文件)。生产环境版本会被最小化,并移除了所有仅用于开发环境的代码分支。
- 如果需要使用全局变量版本(通过
Vue
全局变量访问):请使用vue.global.prod.js
。 - 如果需要 ESM 版本(通过原生 ESM 导入访问):请使用
vue.esm-browser.prod.js
。
更多细节请参考构建文件指南。
追踪运行时错误#
应用级错误处理 可以用来向追踪服务报告错误:
js
import { createApp } from 'vue'
const app = createApp(...)
app.config.errorHandler = (err, instance, info) => {
// 向追踪服务报告错误
}
诸如 Sentry 和 Bugsnag 等服务也为 Vue 提供了官方集成。
在 JavaScript 中访问透传 Attributes#
如果需要,你可以在 <script setup>
中使用 useAttrs()
API 来访问一个组件的所有透传 attribute:
vue
<script setup>
import { useAttrs } from 'vue'
const attrs = useAttrs()
</script>
如果没有使用 <script setup>
,attrs
会作为 setup()
上下文对象的一个属性暴露:
js
export default {
setup(props, ctx) {
// 透传 attribute 被暴露为 ctx.attrs
console.log(ctx.attrs)
}
}
需要注意的是,虽然这里的 attrs
对象总是反映为最新的透传 attribute,但它并不是响应式的 (考虑到性能因素)。你不能通过侦听器去监听它的变化。如果你需要响应性,可以使用 prop。或者你也可以使用 onUpdated()
使得在每次更新时结合最新的 attrs
执行副作用。
网友评论