Composition API仅仅是vue3中新增的API,我们仍然可以使用options API
https://v3.cn.vuejs.org/guide/composition-api-introduction.html
setup 是composition API的入口
setup需要返回一个对象,返回的对象可以使用在模板、methods、computed以及生命周期的钩子函数中
setup是在props被解析完毕,但是在组件实例被创建之前执行的,所以setup内部无法通过this获取到组件的实例,因为组件实例还未被创建。所以在setup中也无法访问到组件中的data、computed、methods
setup 有两个参数:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
x: {
{
x }} <br>
y: {
{
y }}
</div>
<script type="module">
// reactive 将来一个对象转换成响应式对象,并且该对象的嵌套属性也都会转换成响应式对象,返回的是proxy对象
// 这里不使用observeable作为函数的名称是为了避免和另一个函数库rxjs重名出现混淆
// createApp 用来创建vue对象
import {
createApp, reactive } from './node_modules/vue/dist/vue.esm-browser.js'
const app = createApp({
/**
* setup返回一个对象,这个对象的所有属性会被到当前组件模板
* 它有两个参数,
* 第一个是 props
* 第二个是 content 上下文对象(attrs,emit,slots)
* 注意 props 对象是响应式的,watchEffect 或 watch 会观察和响应 props 的更新:
* 然而不要解构 props 对象,那样会使其失去响应性:
*/
setup () {
// 是composition API的入口
// const position = useMousePosition()
const {
x, y } = useMousePosition()
return {
x,
y
}
}
})
console.log(app)
app.mount('#app')
</script>
</body>
</html>
setup中也可以使用生命周期钩子函数,但是前面需要加上on然后首字母大写
setup 是在组件初始化之前执行的,是在beforeCreate和created之间执行的
unmounted类似之前的destroyed
下面两个都是在render函数被重新调用时触发
renderTracked 首次调用render的时候也会触发
renderTriggered 首次调用render的时候不会触发
import {
createApp, reactive, onMounted, onUnmounted} from './node_modules/vue/dist/vue.esm-browser.js'
function useMousePosition () {
const position = reactive({
x: 0,
y: 0
})
const update = e => {
position.x = e.pageX
position.y = e.pageY
}
onMounted(() => {
window.addEventListener('mousemove', update)
})
onUnmounted(() => {
window.removeEventListener('mousemove', update)
})
return position
}
const app = createApp({
setup () {
// 是composition API的入口
const position = useMousePosition()
return {
position
}
}
})
console.log(app)
app.mount('#app')
这三个函数都是创建响应式数据的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
x: {
{
x }} <br>
y: {
{
y }}
</div>
<script type="module">
// reactive 将来一个对象转换成响应式对象,并且该对象的嵌套属性也都会转换成响应式对象,返回的是proxy对象
// 这里不使用observeable作为函数的名称是为了避免和另一个函数库rxjs重名出现混淆
// createApp 用来创建vue对象
// toRefs 可以把一个响应式对象的所有属性也转换成响应式的
import {
createApp, reactive, onMounted, onUnmounted, toRefs } from './node_modules/vue/dist/vue.esm-browser.js'
function useMousePosition () {
// 第一个参数 props
// 第二个参数 context,attrs、emit、slots
// reactive 把传入的对象包装成了proxy对象,将来访问position的x和y的时候,会调用代理对象的
// getter 拦截手机依赖,当x和y变化之后会调用代理对象的setter进行拦截触发更新
const position = reactive({
x: 0,
y: 0
})
const update = e => {
position.x = e.pageX
position.y = e.pageY
}
onMounted(() => {
window.addEventListener('mousemove', update)
})
onUnmounted(() => {
window.removeEventListener('mousemove', update)
})
return toRefs(position)
}
const app = createApp({
setup () {
// 是composition API的入口
// const position = useMousePosition()
// 当把代理对象解构的时候,就相当于定义了x和y两个变量,来接收position.x和position.y
// 而基本类型的赋值就是把值在内存中复制一份。所以这里的x y就是两个基本类型的变量,跟代理对象无关
const {
x, y } = useMousePosition()
return {
x,
y
}
}
})
console.log(app)
app.mount('#app')
</script>
</body>
</html>
作用:把对象的每一个属性都转成响应式数据
内部实现原理:
toRefs要求传入的参数必须是代理对象
内部会创建一个新的对象,遍历传入的代理对象的所有属性,把所有属性的值都转换成响应式对象,然后再挂载到新创建的对象上,最后把新创建的对象返回。
它内部会为代理对象的每一个属性创建一个具有value属性的对象,该对象是响应式的。value属性具有getter和setter,这一点和ref函数类似。getter里返回代理对象中对象属性的值。setter中给代理对象属性赋值。所以返回的每一个属性都是响应式的
作用:把普通数据转换成响应式数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<button @click="increase">按钮</button>
<span>{
{
count }}</span>
</div>
<script type="module">
import {
createApp, ref } from './node_modules/vue/dist/vue.esm-browser.js'
function useCount () {
// 调用ref 返回的是一个对象,对象中只有一个属性value,这个value的值就是0,并且这个value属性是有
// getter和setter的
const count = ref(0) // 基本类型数据转换成响应式对象
return {
count,
increase: () => {
count.value++
}
}
}
createApp({
setup () {
return {
...useCount()
}
}
}).mount('#app')
</script>
</body>
</html>
ref参数如果是对象的话,内部会调用reactive返回一个代理对象
作用,简化模板中的代码,缓存计算的结果,数据变化时才会重新计算
两种用法
1、watch(()=>count.value+1)
传入一个获取值的函数,函数内部依赖响应式的数据,当依赖的数据发生变化后会重新执行该函数获取数据。
computed函数返回一个不可变的响应式对象,类似于使用ref创建的对象,只有一个value属性。获取计算属性的值要通过value属性来获取。模板中使用计算属性可以使用value
2、传入一个对象,该对象具有getter和setter,返回一个不可变得响应式对象
const count = ref(1)
const plusOne = computed({
get: () => count.value + 1,
set: val => {
count.value = val - 1
}
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<button @click="push">按钮</button>
未完成:{
{
activeCount }}
</div>
<script type="module">
import {
createApp, reactive, computed } from './node_modules/vue/dist/vue.esm-browser.js'
const data = [
{
text: '看书', completed: false },
{
text: '敲代码', completed: false },
{
text: '约会', completed: true }
]
createApp({
setup () {
const todos = reactive(data)
const activeCount = computed(() => {
return todos.filter(item => !item.completed).length
})
return {
activeCount,
push: () => {
todos.push({
text: '开会',
completed: false
})
}
}
}
}).mount('#app')
</script>
</body>
</html>
watch3个参数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<p>
请问一个 yes/no 的问题:
<input v-model="question">
</p>
<p>{
{
answer }}</p>
</div>
<script type="module">
// https://www.yesno.wtf/api
import {
createApp, ref, watch } from './node_modules/vue/dist/vue.esm-browser.js'
createApp({
setup () {
const question = ref('')
const answer = ref('')
watch(question, async (newValue, oldValue) => {
const response = await fetch('https://www.yesno.wtf/api')
const data = await response.json()
answer.value = data.answer
})
return {
question,
answer
}
}
}).mount('#app')
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<button @click="increase">increase</button>
<button @click="stop">stop</button>
<br>
{
{
count }}
</div>
<script type="module">
import {
createApp, ref, watchEffect } from './node_modules/vue/dist/vue.esm-browser.js'
createApp({
setup () {
const count = ref(0)
const stop = watchEffect(() => {
console.log(count.value)
})
return {
count,
stop,
increase: () => {
count.value++
}
}
}
}).mount('#app')
</script>
</body>
</html>
关于watchEffect更多的信息,查看官方文档:https://composition-api.vuejs.org/zh/api.html#watcheffect