碎片化编写代码使得复杂项目难以维护
Composition API能将同一逻辑的代码收集到一起
setup
我们需要一个可以实际使用composition API的地方,这个地方就是setup函数
setup在组件创建之前(在声明周期beforeCreated之前)执行,接受props和context作为参数,通过返回值暴露给外部使用
const app = Vue.createApp({template: `<div @click="handleClick">{{name}}</div>`,mounted(){console.log(this.$options) // 获取当前实例的属性},setup(){return {name: 'jack',handleClick: ()=>{console.log('hello')}}}})
ref
下面的代码中,如果let count = 0, 当count改变时vue感知不到count的变化,而使用ref时,该对象就变成高度响应式
原理:通过proxy对数据进行封装变成proxy({value: 0})响应式引用,当数据变化时,触发模板等内容的更新
ref 只处理基础类型的数据
const app = Vue.createApp({template: `<div @click="handleClick">{{count}}</div>`,setup(){const {ref} = Vueconst count = ref(0)const handleClick = () => {count.value = count.value + 1;}return {count,handleClick,}}})
在template中,vue会将ref进行转化,不需要.value
reactive
与ref类型,reactive应用与非基础类型(对象,数组)
通过proxy对数据进行封装,使其变成响应式
const app = Vue.createApp({template: `<div @click="handleClick">{{user.name}}</div>`,setup(){const {reactive} = Vueconst user = reactive({name: 'jack'})const handleClick = () => {user.name = 'rose'}return {user,handleClick,}}})
有了ref和reactive后,就可以代替data了
readonly
接受一个对象 (响应式或纯对象) 或 ref 并返回原始对象的只读代理。只读代理是深层的:任何被访问的嵌套 property 也是只读的。
const original = reactive({ count: 0 })const copy = readonly(original)watchEffect(() => {// 用于响应性追踪console.log(copy.count)})// 变更 original 会触发依赖于副本的侦听器original.count++// 变更副本将失败并导致警告copy.count++ // 警告!
toRefs
为响应式对象的每个property创建ref(proxy), 以使响应式对象解构后仍为响应式
如果要为单个property创建ref,应使用toRef
const state = reactive({foo: 1,bar: 2})const stateAsRefs = toRefs(state)/*stateAsRefs 的类型:{foo: Ref<number>,bar: Ref<number>}*/// ref 和原始 property 已经“链接”起来了state.foo++console.log(stateAsRefs.foo.value) // 2stateAsRefs.foo.value++console.log(state.foo) // 3
const app = Vue.createApp({template: `<div @click="handleClick">{{name}}</div>`,setup(){const {reactive, toRefs} = Vueconst user = reactive({name: 'jack'})const {name} = toRefs(user)const handleClick = () => {name.value = 'rose'}return {name,handleClick,}}})
toRef
const app = Vue.createApp({template: `<div @click="handleClick">{{name}}</div>`,setup(){const {reactive, toRef} = Vueconst user = reactive({name: 'jack'})const name = toRef(user, 'name')const handleClick = () => {name.value = 'rose'}return {name,handleClick,}}})
context
setup的第二个参数是context, 这是一个普通的javascript对象,可用于替代this.$attrs, this.$slots, this.$emit
// MyBook.vueexport 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)}}
解构
// MyBook.vueexport default {setup(props, { attrs, slots, emit, expose }) {...}}
setup封装
如果将所有的数据和逻辑都写在setup里,会显得很混乱,应将负责各自功能的数据和逻辑独立封装
<script >const listRelativeEffect = () => {const {reactive} = Vueconst list = reactive([])const addToList = (value) => {console.log(value)list.push(value)}return {list, addToList}}const inputRelativeEffect = () => {const {ref} = Vueconst inputValue = ref('')const updateInputValue = (e) =>{inputValue.value = e.target.value}return {inputValue,updateInputValue}}const app = Vue.createApp({template: `<div><input :value="inputValue" @input="updateInputValue"/><button @click="()=>addToList(inputValue)">提交</button></div><ul><li v-for="item of list" :key="item">{{item}}</li></ul>`,setup(){const {list, addToList} = listRelativeEffect()const {inputValue, updateInputValue} = inputRelativeEffect()return {inputValue,list,updateInputValue,addToList}}})app.mount("#root")</script>
computed
const {ref, computed } = Vueconst count = ref(1)const plusOne = computed(() => count.value + 1)console.log(plusOne.value) // 2plusOne.value++ // 错误
getter/setter写法
const count = ref(1)const plusOne = computed({get: () => count.value + 1,set: val => {count.value = val - 1}})plusOne.value = 1console.log(count.value) // 0
watch
官方文档
watch有惰性
侦听单一源
// 侦听一个 getterconst state = reactive({ count: 0 })watch(() => state.count,(count, prevCount) => {/* ... */})// 直接侦听一个 refconst count = ref(0)watch(count, (count, prevCount) => {/* ... */})
侦听多个源
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {/* ... */})
watchEffect
立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。
- 没有惰性,立即执行
- 不需要传递侦听内容,自动感知依赖变化
- 不能获取之前的值 ```javascript const count = ref(0)
watchEffect(() => console.log(count.value)) // -> logs 0
setTimeout(() => { count.value++ // -> logs 1 }, 100)
<a name="p1TiE"></a>### 停止侦听watch和watchEffect都能停止侦听```javascriptconst stop = watchEffect(()=>{// ...setTimeout(()=>{stop()},5000)})
生命周期钩子
provide/inject
const app = Vue.createApp({setup(){const {ref, provide, readonly} = Vueconst count = ref(0)const addCount = () => {count.value += 1}provide('count', readonly(count))provide('addCount', addCount)},template:`<my-component />`})app.component('my-component', {setup(){const {inject} = Vueconst count = inject('count', 0)const addCount = inject('addCount')return {count, addCount}},template:`<div @click="addCount">{{count}}</div>`})
ref
使用composition API获取真实DOM节点
- 生命ref为null
与属性ref绑定, 等价于this.$refs
const app = Vue.createApp({setup(){const {ref, onMounted} = Vueconst hello = ref(null)onMounted(()=>{console.log(hello.value)})return {hello}},template:`<div ref="hello">hello world</div>`})
