组合式API
从官方文档来看,使用组合式API是为了解决碎片化,避免断地“跳转”相关代码。
setup 组件选项
- 新的 setup 选项在组件创建之前执行,一旦 props 被解析,就将作为组合式 API 的入口。
- setup 选项是一个接收 props 和 context 的函数,将 setup 返回的所有内容都暴露给组件的其余部分 (计算属性、方法、生命周期钩子等等) 以及组件的模板。
- setup 函数中的 props 是响应式的,当传入新的 prop 时,它将被更新。因为 props 是响应式的,你不能使用 ES6 解构,它会消除 prop 的响应性。如果需要解构 prop,可以在 setup 函数中使用 toRefs 函数。
//一般情况
props: {
title: String
},
setup(props) {
console.log(props.title)
}
//解构
import { toRefs } from 'vue'
setup(props) {
const { title } = toRefs(props)
console.log(title.value)
}
//特殊情况,如果title这个属性不存在,需要手动创建
setup(props) {
const title = toRef(props, 'title')
console.log(title.value)
}
- context 是一个普通的 JavaScript 对象,它暴露组件的三个 property。它不是响应式的,这意味着你可以安全地对 context 使用 ES6 解构。
//一般
setup(props, context) {
// Attribute (非响应式对象)
console.log(context.attrs)
// 插槽 (非响应式对象)
console.log(context.slots)
// 触发事件 (方法)
console.log(context.emit)
}
//解构
setup(props, { attrs, slots, emit }) {
...
}
- 访问组件的 property。执行 setup 时,组件实例尚未被创建,只能够访问到
props、attrs、slots、emit
,不能访问data、computed、methods
- 在setup()中不使用this,因为这时候组件实例还没有被创建
setup的使用
setup相当于data、 methods、watch的组合。
data+methods
<template>
<div>
<el-button type="primary" @click="addNumber">加一</el-button>
<div>现在的值是:{{ number }}</div>
</div>
</template>
<script>
//import { ref } from 'vue'
export default {
name: "HelloWorld",
props: {},
setup() {
let number = 0;
function addNumber() {
number += 1;
}
return {
number,
addNumber,
};
},
};
</script>
<style scoped>
</style>
一点反应都没有,因为他不是响应式的。按照官方文档你还需要使用一个新的 ref 函数,将它变成响应式的。
<script>
import { ref } from "vue";
export default {
name: "HelloWorld",
props: {},
setup() {
let number = ref(0);
function addNumber() {
number.value += 1;
}
return {
number,
addNumber,
};
},
};
</script>
ref 接收参数并将其包裹在一个带有 value property 的对象中返回,然后可以使用该 property 访问或更改响应式变量的值 ![在这里插入图片描述](https://www.icode9.com/i/ll/?i=2021071613581434.gif#pic_center)
简单试了一下,3.0是兼容2.0的
<script>
import { ref } from "vue";
export default {
name: "HelloWorld",
props: {},
setup() {
let number = ref(0);
// function addNumber() {
// number.value += 1;
// }
return {
number,
// addNumber,
};
},
methods: {
addNumber() {
this.number += 1;
},
},
};
</script>
这样也是可以正常运行的。但是既然打算用3.0就用3.0的语法去写,不要既有3.0又有2.0,这样更加影响后期的维护。
在setup中使用生命周期函数
在2.0中一般在mounted中调用初始化数据的函数,在3.0中可以在onMounted中调用函数
setup() {
let number = ref(0);
function addNumber() {
setTimeout(() => {
number.value += 1;
}, 3000);
}
//初始化数据
onMounted(addNumber);
return {
number,
addNumber,
};
},
在setup中使用监听
在3.0中监听也有一些区别,watch同样需要写在setup中,另外watch函数有两个参数,第一个是要监听的参数,第二个是响应的回调函数
<script>
import { watch, ref } from "vue";
export default {
name: "HelloWorld",
props: {},
setup() {
let number = ref(0);
function addNumber() {
number.value += 1;
}
watch(number, (nValue, oValue) => {
console.log("新值:" + nValue, "旧值:" + oValue);
});
return {
number,
addNumber,
};
},
};
</script>
在setup中使用computed
computed与watch的使用类似,这里就不介绍了。
渲染是函数方式
import { h, reactive, ref } from "vue";
export default {
name: "test",
props: {},
setup() {
const readersNumber = ref(0)
const book = reactive({ title: 'Vue 3 Guide' })
// 请注意这里我们需要显式调用 ref 的 value
return () => h('div', [
h('p', { style: 'font-size:30px' }, readersNumber.value),
h('p', { style: 'font-size:20px' }, book.title)
])
},
};
渲染时的方式不熟,有感兴趣的可以自己去百度。
注:
- ref要访问value,reactive访问自己定义的属性。
- ref好像一般用于number、string、array、Boolean等类型;reactive用于对象类型(ref也可以用于对象,不过比reactive多嵌套一层)
- 其他区别不清楚,没仔细研究。
生命周期函数
这个没什么好说的,看官方文档就好。
模板引用
普通元素的引用
<template>
<div>
<p ref="refP">元素上的ref</p>
</div>
</template>
<script>
import { onMounted, ref } from "vue";
export default {
setup() {
let refP = ref(null);
onMounted(() => {
console.log(refP);
});
return {
refP,
};
},
};
</script>
注:要等到元素全都初始化后,才能绑定到元素上
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