组合式 API 基础
(就是将一个组件中的不同逻辑分离开来,放在不同的js文件中,再引入,使用者不需要知道怎么实现,只需要知道提供了哪些功能)
- setup 组件选项
- 新的 setup 选项在组件创建之前执行,一旦 props 被解析,就将作为组合式 API 的入口。
- setup 中避免使用
, setup的调用 在this
、data
、methods
解析之前computed
- setup 选项是一个接收
和props
的函数context
- 将setup 返回的函数 暴露给 组件的其余部分 (计算属性、方法、生命周期)
- 带 ref 的响应式变量
- 在 Vue 3.0 中,我们可以通过一个新的 ref 函数使任何响应式变量在任何地方起作用
-
对toRefs(props)
属性的响应式引用props
import { ref } from 'vue'
const counter = ref(0)
- ref 接收参数并将其包裹在一个带有 value property 的对象中返回,然后可以使用该 property 访问或更改响应式变量的值:
import { ref } from 'vue'
const counter = ref(0)
console.log(counter) // { value: 0 }
console.log(counter.value) // 0
- 在 setup 内注册生命周期钩子
- 为了使组合式 API 的功能和选项式 API 一样完整,我们还需要一种在 setup 中注册生命周期钩子的方法。
- 这些函数接受一个回调,当钩子被组件调用时,该回调将被执行。
import {ref, onMounted} from 'vue'
setup(){
const count = ref(0);
const getCount = function(){
return count + 6;
}
onMounted(getCount);
return {
count,
getCount
}
}
- watch 响应式更改
- 接收三个参数
- 一个想要侦听的响应式引用或 getter 函数
- 一个回调
- 可选的配置选项
import { ref, watch } from 'vue'
const counter = ref(0)
watch(counter, (newValue, oldValue) => {
console.log('The new counter value is: ' + counter.value)
})
- 独立的 computed 属性
- 与 ref 和 watch 类似,也可以使用从 Vue 导入的 computed 函数在 Vue 组件外部创建计算属性。
import { onMounted , ref , toRefs , computed , watch } from 'vue'
setup(props){
// 将 name 变成 props name 属性的引用
const { name } = toRefs(props);
// 将 username 变成响应式的
const username = ref('');
// 方法
const getUserName = function(){
username.value ='ch' + name;
}
// 生命周期函数
onMounted(getUserName);
// 监听 username 变化 触发 getUserName方法
watch(username,getUserName);
// 计算属性
const enName = computed(()=>'en' + name.value)
return {
username,
enName,
getUserName
}
}
Setup
- 接收两个参数
- props
- context
- props
- props 是响应式的
(不要使用 ES6解构 会去掉它的响应式)
- 如果需要解构
,可以在prop
函数中使用setup
函数来完成此操作:toRefs
import { toRefs } from 'vue';
setup(props){
// name 不再是响应式
// let { name } = props;
// 这里的name是响应式的
let { name } = toRefs(props);
}
- 对于非必填(可选)的props 不可以使用 toRefs() 结构
- toRefs 将不会为 title(可选参数) 创建一个 ref ,需要使用 toRef 替代它
import { toRefs , toRef } from 'vue';
setup(props){
let { name } = toRefs(props);
// title 非必填(可选)
const title = toRef(props, 'title')
}
- Context
- 对象格式,暴露组件的三个属性
-
不是响应式的可以使用 ES6 安全解构Context
export default {
setup(props, { attrs, slots, emit }) {
...
}
}
-
: Attribute 非响应式对象attrs
-
: 插槽 非响应式对象slots
-
: 触发事件 方法emit
-
和attrs
是有状态的对象,它们总是会随组件本身的更新而更新。这意味着你应该避免对它们进行解构,并始终以 attrs.x 或 slots.x 的方式引用 property。 请注意,与 props 不同,attrs 和 slots 是非响应式的。如果你打算根据 attrs 或 slots 更改应用副作用,那么应该在 onUpdated 生命周期钩子中执行此操作slots
- 访问组件的 property
-
的执行是在 组件创建之前,无法访问setup
、methonds
、data
。computed
-
可以访问setup
及props
context(attrs,slots,emit)
-
- 结合模板使用
- 如果 setup 返回一个对象,那么该对象的 property 以及传递给 setup 的 props 参数中的 property(原来就可以访问到 props) 就都可以在模板中访问到
- 注意,从
返回的setup
在模板中访问时是被自动浅解包的,因此不应在模板中使用refs
。.value
- 使用渲染函数
-
还可以返回一个渲染函数,该函数可以直接使用在同一作用域中声明的响应式状态setup
返回对象的响应式副本reactive
-
import { h , reactive } form 'vue'
setup(){
const img = reactive({ src:"https:xxxxxx" });
return () => h('img', { src:img.src })
}
- 使用 this
- 在
中setup
不是该活跃实例的引用,因为他是在解析其它组件选项之前被调用的this
- 在
生命周期钩子
- 通过在生命周期钩子前面加上 “on” 来访问组件的生命周期钩子。
- 接收一个回调函数,当钩子被组件调用的时候执行
setup(){
onMounted(()=>{})
}
Provide/Inject
-
Provide/Inject
- 需要先引入 import { provide , inject } form ‘vue’
- provide 接收两个参数
- name
- value
- inject 两个参数
- name
- 默认值(可选)
import { provide , inject } form 'vue'
// parent.vue
setup(){
provide('name','xiaohei');
provide('addr',{city:'',town:''});
}
//child.vue
setup(){
let cname = inject('name','hah');
let caddr = inject('addr');
}
-
响应性
- 为了增加 provide 值和 inject 值之间的响应性,我们可以在 provide 值时使用 ref 或 reactive。
import { provide , ref , reactive } form 'vue';
// parent.vue
setup(){
let name = ref('xiaohei');
let addr = reactive({city:'',town:''});
provide('name',name);
provide('addr',addr);
}
-
修改响应式 property
- 修改响应式 属性的 时候,最好是在定义响应式的组件内进行修改
- 有时我们需要在注入组件的内部修改
中的值,可以通过inject
一个方法来修改这个值provide
import { provide , ref , reactive } form 'vue';
// parent.vue
setup(){
let age = ref(1);
let addr = reactive({city:'',town:''});
const updateAge = function(){
age.value +=1;
}
provide('name',name);
provide('addr',addr);
provide('updateAge',updateAge);
}
// child.vue
setup(){
let age = inject('age');
let addr = inject('addr');
const updateAge = inject('updateAge');
return{
age,
addr,
updateAge
}
},
methods:{
update(){
this.updateAge();
}
}
- 不允许 inject 的组件 修改 provide 传递的属性 添加
readonly
readonly 接受一个对象 (响应式或纯对象) 或 ref 并返回原始对象的只读代理。只读代理是深层的:任何被访问的嵌套 property 也是只读的。
与 reactive 一样,如果任何 property 使用了 ref,当它通过代理访问时,则被自动解包:
import { readonly , provide , reactive } from 'vue'
setup(){
let age = ref(1);
let work = reactive({money:'',addr:''}) ;
provide('age' , readonly(age)); // 只读属性
provide('work' , readonly(work)); // 只读属性
}
模板引用
- 在使用组合式 API 时,响应式引用和模板引用的概念是统一的。为了获得对模板内元素或组件实例的引用,我们可以像往常一样声明 ref 并从 setup() 返回:
<template>
<div ref="root">This is a root element</div>
</template>
<script>
import { ref, onMounted } from 'vue'
export default {
setup() {
const root = ref(null)
onMounted(() => {
// DOM元素将在初始渲染后分配给ref
console.log(root.value) // <div>这是根元素</div>
})
return {
root
}
}
}
</script>
- 这里我们在渲染上下文中暴露
,并通过root
,将其绑定到 div 作为其ref="root"
。在ref
补丁算法中,如果虚拟 DOM
的VNode
键对应于渲染上下文中的ref
,则ref
的相应元素或组件实例将被分配给该VNode
的值。这是在虚拟 DOM 挂载/打补丁过程中执行的,因此模板引用只会在初始渲染之后获得赋值。ref
大致意思就是 模板里面声明的 ref='root' 如果与下文中暴露的 ref 所赋值的 变量名一样,就会把这个模板中的ref 与下文中的那个ref赋值的变量关联起来
-
JSX 中的用法
export default {
setup() {
const root = ref(null)
return () =>
h('div', {
ref: root
})
// with JSX
return () => <div ref={root} />
}
}
-
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()
在 DOM 挂载或更新之前运行副作用,所以当侦听器运行时,模板引用还未被更新。watchEffect()
- 因此,使用模板引用的侦听器应该用
选项来定义,这将在flush: 'post'
更新后运行副作用,确保模板引用与DOM
保持同步,并引用正确的元素。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(() => {
console.log(root.value) // => <div></div>
},
{
flush: 'post'
})
return {
root
}
}
}
</script>