文章目录
什么是组合式API
官方文档https://v3.cn.vuejs.org/guide/composition-api-introduction.html#%E4%BB%80%E4%B9%88%E6%98%AF%E7%BB%84%E5%90%88%E5%BC%8F-api
痛点:使用 (data、computed、methods、watch) 组件选项来组织逻辑通常都很有效。然而,当我们的组件开始变得更大时,逻辑关注点的列表也会增长。尤其对于那些一开始没有编写这些组件的人来说,这会导致组件难以阅读和理解。
对比
选项式API写法
component.vue
export defalut {
components: {},
data() {
return {}
},
computed:{},
watch:{},
methods:{},
mounted() {}
}
组合式API写法
(setup 选项是一个接收 props 和 context 的函数,我们将在之后进行讨论。)
component.vue
export default {
components: { },
props: {
user: {}
},
setup(props) { // 不应在setup中使用this,因为它不会访问到组件实例。setup调用在data、computed.... 之前。
**使用 `toRefs` 创建对 `props` 中的 `user` property 的响应式引用**
const { user } = toRefs(props)
**使用ref声明响应式变量,使用counter.value进行访问,保证响应式不会在某个地方丢失。**
import { ref, onMounted, watch, computed } from 'vue'
const counter = ref(0)
console.log(counter.value) // 0
**声明methods**
const plusCounter = function() {
counter ++;
}
**访问生命周期钩子函数,前缀为on。**
onMounted() {}
**使用watch**
watch(counter, (newValue, oldValue) => {
console.log('The new counter value is: ' + counter.value)
})
**使用computed,为了访问新创建的计算变量的 value,我们需要像 ref 一样使用 .value property**
const twiceTheCounter = computed(() => counter.value * 2)
return {
counter,
plusCounter,
twiceTheCounter
}
** 这里返回的任何内容都可以用于组件的其余部分,并且ref在模板中不需要用.value进行访问。**
}
// 组件的“其余部分”
}
深入setup
使用 setup 函数时,它将接收两个参数:props、context
props
因为 props 是响应式的,你不能使用 ES6 解构,它会消除 prop 的响应性。如果需要解构 prop,可以在 setup 函数中使用 toRefs 函数来完成此操作:
import { toRefs } from 'vue'
setup(props) {
const { title } = toRefs(props)
console.log(title.value)
}
如果某个prop是可选的,未传入的情况toRefs不会为其创建一个ref,这是需要用到toRef
import { toRef } from 'vue'
setup(props) {
const title = toRef(props, 'title')
console.log(title.value)
}
context
传递给 setup 函数的第二个参数是 context。context 是一个普通 JavaScript 对象(所以可以安全的使用解构),暴露了其它可能在 setup 中有用的值:
export default {
setup(props, context) {
// 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
执行 setup 时,你只能访问以下 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
})
return () => h('div', count.value)
}
}
这个 increment 方法现在将可以通过父组件的模板 ref 访问。
组合式 API 语法糖 (<script setup>)
# 基本语法、释义
<script setup>
console.log('hello script setup')
</script>
- 里面的代码会被编译成组件 setup() 函数的内容。这意味着与普通的 <script> 只在组件被首次引入的时候执行一次不同,<script setup> 中的代码会在每次组件实例被创建的时候执行。
- 当使用 <script setup> 的时候,任何在 <script setup> 声明的顶层的绑定 (包括变量,函数声明,以及 import 导入的内容) 都能在模板中直接使用
# 原有的API在<script setup>中的使用
<script setup>
使用组件
import MyComponent from './MyComponent.vue'
响应式
import { ref } from 'vue'
const count = ref(0)
props
const props = defineProps({
foo: String
})
emit
const emit = defineEmits(['change', 'delete'])
defineExpose(组件中要暴露出去的属性)
const a = 1
const b = ref(2)
defineExpose({
a,
b
})
自定义指令 (必须遵循 vNameOfDirective 这样的命名规范)
const vMyDirective = {
beforeMount: (el) => {
// 在元素上做些操作
}
}
useSlots 和 useAttrs (也可以可以在模板中直接通过$slots和$attrs来访问)
import { useSlots, useAttrs } from 'vue'
const slots = useSlots()
const attrs = useAttrs()
</script>