yarn install
yarn serve
可以通过传递一个 ref property 或 reactive 对象给 provide 来改变这种行为
我们也可以在组合式 API 中使用 provide/inject。两者都只能在当前活动实例的 setup() 期间调用。
使用组合式 API 为 MyMarker 组件提供用户的位置
按照打印顺序: provide--> computed --->created
watch: 需要所监听的元素改变后才会触发,所以刚开始的时候不会触发watch,触发watch时执行的是beforeUpdate和updated这两个生命钩子函数,如果响应式数据发生了变化,子组件依赖了此数据,则会执行生命周期函数的更新
在 setup() 中使用 provide 时,我们首先从 vue 显式导入 provide 方法。这使我们能够调用 provide 来定义每个 property
setup中的provide和单独的provide可以混用,是不是意味着setup中其他的元素也可以和vue2中的一起使用呢?
而且外面的元素的优先级比setup中的优先级要高
我们之前曾经在一个多标签的界面中使用 is attribute 来切换不同的组件:
<component :is="currentTabComponent"></component>
// <!-- 失活的组件将会被缓存!-->
<keep-alive>
<component :is="currentTabComponent"></component>
</keep-alive>
在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块。为了实现这个效果,Vue 有一个 defineAsyncComponent 方法:
const { createApp, defineAsyncComponent } = Vue;
const app = createApp({});
const AsyncComp = defineAsyncComponent(
() =>
new Promise((resolve, reject) => {
resolve({
template: "<div>I am async!</div>",
});
})
);
app.component("async-example", AsyncComp);
尽管存在 prop 和事件,但有时你可能仍然需要在 JavaScript 中直接访问子组件。为此,可以使用 ref attribute 为子组件或 HTML 元素指定引用 ID。
<base-input ref="usernameInput"></base-input>;
this.$refs.usernameInput.focusInput();
$refs 只会在组件渲染完成之后生效。也就是 mounted 钩子函数之后才可以有效。这仅作为一个用于直接操作子元素的“逃生舱”——你应该避免在模板或计算属性中访问 $refs。计算属性的执行是在 created 之前,对 data 进行处理,如果修改值之后的话,计算属性会重新计算,但是初始化的时候,计算熟悉还是在前面执行的
将同一个逻辑关注点相关代码收集在一起会更好 使用组合式 API,我们首先需要一个可以实际使用它的地方。在 Vue 组件中,我们将此位置称为 setup
在 setup 中你应该避免使用 this,因为它不会找到组件实例。setup 的调用发生在 data property、computed property 或 methods 被解析之前,所以它们无法在 setup 中被获取
setup 选项是一个接收 props 和 context 的函数.此外, setup 返回的所有内容都暴露给组件的其余部分 (计算属性、方法、生命周期钩子等等) 以及组件的模板
export default {
components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
props: {
user: {
type: String,
required: true,
},
},
setup(props) {
console.log(props); // { user: '' }
return {}; // 这里返回的任何内容都可以用于组件的其余部分
},
// 组件的“其余部分”
};
在 Vue 3.0 中,我们可以通过一个新的 ref 函数使任何响应式变量在任何地方起作用,ref 为我们的值创建了一个响应式引用。在整个组合式 API 中会经常使用引用的概念
组合式 API 上的生命周期钩子与选项式 API 的名称相同,但前缀为 on:即 mounted 看起来会像 onMounted
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted } from 'vue'
// 组件中
setup (props) {
// 通过ref实现数据的响应式更新
const repositories = ref([])
const getUserRepositories = async () => {
repositories.value = await fetchUserRepositories(props.user)
}
// onMounted和普通的钩子函数mounted类似
onMounted(getUserRepositories) // 在 `mounted` 时调用 `getUserRepositories`
// 如果 setup 返回一个对象,那么该对象的 property 以及传递给 setup 的 props 参数中的 property 就都可以在模板中访问到:
return {
// 注意,从 setup 返回的 refs 在模板中访问时是被自动浅解包的,因此不应在模板中使用 .value
repositories,
getUserRepositories
}
}
import { ref, watch } from "vue";
const counter = ref(0);
// watch接受三个参数
// 1.counter:想要侦听的响应式引用或 getter 函数
// 2.回调函数
// 3.可选的配置选项
watch(counter, (newValue, oldValue) => {
console.log("The new counter value is: " + counter.value);
});
与 ref 和 watch 类似,也可以使用从 Vue 导入的 computed 函数在 Vue 组件外部创建计算属性
import { ref, computed } from "vue";
const counter = ref(0);
const twiceTheCounter = computed(() => counter.value * 2);
counter.value++;
console.log(counter.value); // 1
console.log(twiceTheCounter.value); // 2
使用 setup 函数时,它将接收两个参数:
- 1,props
- 2,context
export default {
props: {
title: String,
},
// setup 函数中的 props 是响应式的,当传入新的 prop 时,它将被更新
// props 是响应式的,不能使用 ES6 解构,否则就会消除 prop 的响应性
setup(props) {
// 1,props默认不能结构,如果需要解构 prop,可以在 setup 函数中使用 toRefs 函数来完成此操作:
const { title } = toRefs(props);
console.log(title.value);
},
};
当 title 是可选参数的时候,用户没有传入 title 时,toRefs 将不会为 title 创建一个 ref。这时可以用 toRef 进行替代.没有值得时候,创建了 title 这个 ref,他的创建的的_defaultValue 值是 undefined。当有值的时候就返回传递的值
import { toRef } from 'vue'
setup(props) {
const title = toRef(props, 'title')
console.log(title.value)
}
传递给 setup 函数的第二个参数是 context。context 是一个普通 JavaScript 对象,暴露了其它可能在 setup 中有用的值:
export default {
// context 是一个普通的 JavaScript 对象,也就是说,它不是响应式的,这意味着你可以安全地对 context 使用 ES6 解构
// setup(props, context) {
setup(props, { attrs, slots, emit, expose }) {
// Attribute (非响应式对象,等同于 $attrs)
// console.log(context.attrs);
// 插槽 (非响应式对象,等同于 $slots)
// console.log(context.slots);
// 触发事件 (方法,等同于 $emit)
// console.log(context.emit);
// 暴露公共 property (函数)
// console.log(context.expose);
},
};
attrs 和 slots 是有状态的对象,它们总是会随组件本身的更新而更新。这意味着你应该避免对它们进行解构,并始终以 attrs.x 或 slots.x 的方式引用 property。请注意,与 props 不同,attrs 和 slots 的 property 是非响应式的。如果你打算根据 attrs 或 slots 的更改应用副作用,那么应该在 onBeforeUpdate 生命周期钩子中执行此操作
// 可以访问的property
props
attrs
slots
emit
// 将无法访问以下组件选项:
data
computed
methods
refs (模板 ref)
setup 还可以返回一个渲染函数,该函数可以直接使用在同一作用域中声明的响应式状态
// import { h, ref, reactive } from "vue";
// export default {
// setup() {
// const readersNumber = ref(0);
// const book = reactive({ title: "Vue 3 Guide" });
// // 请注意这里我们需要显式使用 ref 的 value
// return () => h("div", [readersNumber.value, book.title]);
// },
// };
将这个组件的方法通过模板 ref 暴露给父组件时可以通过调用 expose 来解决这个问题,给它传递一个对象,其中定义的 property 将可以被外部组件实例访问 ?
// import { h, ref } from "vue";
// export default {
// setup(props, { expose }) {
// const count = ref(0);
// const increment = () => ++count.value;
// expose({
// // increment 方法现在将可以通过父组件的模板 ref 访问
// increment,
// });
// return () => h("div", count.value);
// },
// };
在 setup() 内部,this 不是该活跃实例的引用,因为 setup() 是在解析其它组件选项之前被调用的,所以 setup() 内部的 this 的行为与其它选项中的 this 完全不同。这使得 setup() 在和其它选项式 API 一起使用时可能会导致混淆
在渲染上下文中暴露 root,并通过 ref="root",将其绑定到 div 作为其 ref。在虚拟 DOM 补丁算法中,如果 VNode 的 ref 键对应于渲染上下文中的 ref,则 VNode 的相应元素或组件实例将被分配给该 ref 的值。这是在虚拟 DOM 挂载/打补丁过程中执行的,因此模板引用只会在初始渲染之后获得赋值 作为模板使用的 ref 的行为与任何其他 ref 一样:它们是响应式的,可以传递到 (或从中返回) 复合函数中
<template>
<div ref="root">This is a root element</div>
</template>
// setup执行其实就是在beforeCreate 和 created之前进行一系列的操作
<script>
import { ref, onMounted } from 'vue'
export default {
setup() {
const root = ref(null)
onMounted(() => {
// DOM 元素将在初始渲染后分配给 ref
console.log(root.value) // <div>This is a root element</div>
})
return {
root
}
}
}
</script>
组合式 API 模板引用在 v-for 内部使用时没有特殊处理。相反,请使用函数引用执行自定义处理
<template>
<div v-for="(item, i) in list" :ref="el => { if (el) divs[i] = el }">
{{ item }}
</div>
</template>
<script>
import { ref, reactive, onBeforeUpdate } from 'vue'
export default {
setup() {
const list = reactive([1, 2, 3])
const divs = ref([])
// 确保在每次更新之前重置ref
onBeforeUpdate(() => {
divs.value = []
})
return {
list,
divs
}
}
}
</script>
与生命周期钩子的一个关键区别是,watch() 和 watchEffect() 在 DOM 挂载或更新之前运行副作用,所以当侦听器运行时,模板引用还未被更新
<template>
<div ref="root">This is a root element</div>
</template>
<script>
import { ref, watchEffect } from 'vue'
export default {
setup() {
const root = ref(null)
watchEffect(() => {
// 这个副作用在 DOM 更新之前运行,因此,模板引用还没有持有对元素的引用。
console.log(root.value) // => null
},
// 使用模板引用的侦听器应该用 flush: 'post' 选项来定义,这将在 DOM 更新后运行副作用,确保模板引用与 DOM 保持同步,并引用正确的元素
{
flush: 'post'
})
return {
root
}
}
}
</script>