组合API
使用选项编写组件的逻辑代码在大多数情况下都有效。但是,当使用Vue构建大型项目时,组件代码会比较复杂,这可能会导致组件难以阅读和理解,尤其对于一开始没有参与编写这些组件的人。能够将与一个逻辑关注点相关的代码配置在一起会更好,而这正是组合式API能够做到的。
1、什么是组合API
Vue 3.0中新增了组合API的功能,它是一组附加的、基于函数的API,可以更加灵活地组织组件代码。通过组合API可以使用函数而不是声明选项的方式来编写Vue组件。因此,使用组合API可以将组件代码编写为多个函数,每个函数处理一个特定的功能,不再需要按选项组织代码。
组合API可以更好地和TypeScript集成,同时,组合API可以和现有的基于选项的API一起使用。需要注意的是,组合API是在选项(data、methods和computed)之前进行解析,因此组合API无法访问这些选项中定义的属性。
2、setup()函数
setup()函数是一个新的组件选项,它是组件内部使用组合API的入口。setup()函数在组件实例创建之前、初始化Prop之后调用,而且setup()函数是在beforeCreate钩子函数之前调用。
setup()函数可以返回一个对象或函数,对象的属性会合并到组件模板渲染的上下文中。示例代码如下:
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script type="text/javascript">
const vm = Vue.createApp({
setup() {
//创建一个响应式对象
const data = Vue.reactive({
number: 10
});
function add() {
data.number += 1;
}
//返回一个对象,对象中的属性可以在模板中使用
return {
data,
add
}
}
});
vm.mount('#app');
</script>
上述代码中,setup()函数返回的是一个对象,该对象有两个属性,一个是响应式对象,另一个是函数。在组件的模板中可以直接使用这两个属性。代码如下:
<div id="app">
<button @click="add">{{data.number}}</button>
</div>
setup()函数中不能使用this。但是,当和现有的基于选项的API一起使用时,在选项中可以通过this访问setup()函数返回的属性。
setup()函数可以接收两个可选的参数。
- 第一个参数是响应式的props对象,通过该参数可以访问props选项中定义的Prop。
- 第二个参数是一个上下文(context)对象,该对象是一个JavaScript对象,它暴露了attrs、slots和emit三个属性。其中,attrs和slots是有状态的对象,它们会随着组件的更新而发生变化,但是这两个对象本身并不是响应式的,因此不能对它们进行解构。
由于setup()函数接收的props对象是响应式的,因此在组件外部传入新的Prop值时,props对象会随着更新。示例代码如下:
<div id="app">
<my-demo :msg="msg"></my-demo>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script type="text/javascript">
const vm = Vue.createApp({
data() {
return {
msg: '海阔凭鱼跃,天高任鸟飞。'
}
}
});
vm.component('my-demo', {
props: ['msg'],
setup(props) {
Vue.watchEffect(()=> {
console.log(props.msg);
})
},
template: `<p>{{msg}}</p>`
})
vm.mount('#app');
</script>

在浏览器中打开上述示例的页面后,在控制台中输入
const app = document.querySelector('#app').__vue_app__
app._instance.proxy.msg = 'hello'
按Enter键后,可以看到页面内容发生了变化。因此,调用watchEffect()方法或watch()方法可以监听props对象,并对修改做出响应。

3、响应式API
3.1、reactive()方法
reactive()方法用于将定义的JavaScript对象转换为响应式对象。示例代码如下:
<div id="app">
<div>姓名:{{data.name}}</div>
<div>年龄:{{data.age}}</div>
<p>
<button @click="data.age=25">修改年龄</button>
</p>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script type="text/javascript">
const vm = Vue.createApp({
setup() {
//创建一个响应式对象
const data = Vue.reactive({
name: 'Tony',
age: 20
});
//返回一个对象,对象中的属性可以在模板中使用
return {
data
}
}
});
vm.mount('#app');
</script>
上述代码中,使用reactive()方法创建了一个响应式对象data,并以对象的形式返回该对象。当data对象发生变化时,视图会自动更新。运行结果如图所示。


3.2、watchEffect()方法
watchEffect()方法用来监听数据的变化,类似于Vue 2.x中的watch选项。该方法接收一个函数作为参数,它会立即执行一次该函数,同时会跟踪函数里面用到的所有响应式状态,当状态发生变化时会重新运行该函数。示例代码如下:
<div id="app">
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script type="text/javascript">
const vm = Vue.createApp({
setup() {
//创建一个响应式对象
const data = Vue.reactive({
count: 1
});
Vue.watchEffect(()=> {
document.body.innerHTML = `计数器:${data.count}`
});
setInterval(()=> {
data.count++;
}, 1000);
}
});
vm.mount('#app');
</script>
上述代码中,当响应式对象data发生变化时,会重新运行watchEffect()方法的参数。运行结果如图所示。

3.3、ref()方法
reactive()方法可以为一个JavaScript对象创建响应式代理,如果需要对某个基本数据类型(如数值类型、字符串类型)的数据创建响应式代理对象,可以通过ref()方法实现。该方法接收一个原始值作为参数,返回一个响应式的对象,该对象只有一个value属性指向内部值。示例代码如下:
<div id="app">
<p>{{data}}</p>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script type="text/javascript">
const vm = Vue.createApp({
setup() {
//创建一个响应式对象
const data = Vue.ref(100);
setInterval(()=> {
data.value++;
}, 1000);
return {
data
}
}
});
vm.mount('#app');
</script>

如果将ref()方法创建的响应式代理对象作为属性返回,那么在模板中访问时不需要添加.value。
示例:更改商品数量。
实现购物车中更改商品数量的操作。单击“+”按钮增加商品数量,单击“-”按钮减少商品数量,代码如下:
<div id="app">
商品数量:
<button @click="data--" :disabled="data === 1 ? true :false">-</button>
{{data}}
<button @click="data++">+</button>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script type="text/javascript">
const vm = Vue.createApp({
setup() {
//创建一个响应式对象
const data = Vue.ref(1);
return {
data
}
}
});
vm.mount('#app');
</script>

3.4、computed()方法
和computed选项的作用一样,computed()方法用于创建计算属性。该方法接收一个getter函数,并返回一个不可修改的ref对象。示例代码如下:
<div id="app">
<p>{{newData}}</p>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script type="text/javascript">
const vm = Vue.createApp({
setup() {
const data = Vue.ref(10);
const newData = Vue.computed(()=> data.value + 10);
return {
newData
}
}
});
vm.mount('#app');
</script>

示例:转换字符串。
将字符串“HTML+CSS+JavaScript”转换为首字母大写,其他字母小写的形式。代码如下:
<div id="app">
<p>{{newData}}</p>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script type="text/javascript">
const vm = Vue.createApp({
setup() {
const data = Vue.ref('HTML + CSS + JavaScript');
const newData = Vue.computed(()=> {
const d = data.value;
return d.charAt(0).toUpperCase() + d.substr(1).toLowerCase();
});
return {
newData
}
}
});
vm.mount('#app');
</script>

3.5、watch()方法
watch()方法相当于Vue根实例选项对象中的watch选项。该方法用于监听特定的数据,并在回调函数中应用。当被监听的数据发生变化时,才会调用回调函数。
watch()方法可以接收两个参数。如果使用该方法监听的是一个ref对象,那么第一个参数是需要监听的ref对象,第二个参数是当监听的数据发生变化时触发的回调函数。示例代码如下:
<div id="app">
请输入米数:<input type="text" size="6" v-model="data">
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script type="text/javascript">
const vm = Vue.createApp({
setup() {
const data = Vue.ref(0);
Vue.watch(data, ()=> {
console.log(data.value + '米 = ' + data.value * 100 + '厘米');
});
return {
data
}
}
});
vm.mount('#app');
</script>
运行上述代码,当文本框中的数字发生变化时,浏览器控制台会输出单位“米”和“厘米”之间的换算结果,如图所示。

如果使用watch()方法监听一个响应式对象中的某个属性,那么第一个参数需要使用返回该属性的函数的方式。示例代码如下:
<div id="app">
<p>商品原价格:{{data.price}}</p>
请输入新价格:<input type="text" size="6" v-model="data.newprice">
<p>{{data.text}}</p>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script type="text/javascript">
const vm = Vue.createApp({
setup() {
const data = Vue.reactive({
price: 399,
newprice: '',
text: ''
});
Vue.watch(()=> data.newprice, (newValue)=> {
data.text ='原价格:' + data.price + ' 新价格:' + newValue;
});
return {
data
}
}
});
vm.mount('#app');
</script>

4、生命周期钩子函数
与基于选项的API相比,组合API中的生命周期钩子函数也发生了变化,将选项中的生命周期钩子函数改成了onXxx()函数的形式。需要注意的是,beforeCreate和created两个钩子函数被删除了,取而代之的是setup()函数。选项API和组合API的钩子函数的对应关系如表所示。
| 选项API钩子函数 | 组合API钩子函数 |
|---|---|
| beforeCreate | 没有对应的onXxx()函数,取而代之的是setup()函数 |
| created | 有对应的onXxx()函数,取而代之的是setup()函数 |
| beforeMount | onBeforeMount |
| mounted | onMounted |
| updated | onUpdated |
| beforeUnmount | onBeforeUnmount |
| unmounted | onUnmounted |
5、使用ref获取DOM元素
在Vue 3.0中,使用ref()方法除了可以对某个原始值创建响应式代理对象,还可以获取模板中的指定DOM元素。要获取指定DOM元素,首先需要为该元素添加一个ref属性,然后在setup()函数中声明一个名称与ref属性值相同的变量,并传入一个空值null,再通过“变量名.value”的形式就可以获取到该元素。示例代码如下:
<div id="app">
<div ref="ele"></div>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script type="text/javascript">
const vm = Vue.createApp({
setup() {
//变量名必须和模板中ref属性值相同,并传入一个空值null
const ele = Vue.ref(null);
Vue.onMounted(() => {
//获取指定元素的标签名并显示
document.body.innerHTML = ele.value.tagName;
});
return {
ele
}
}
});
vm.mount('#app');
</script>
上述代码中,为div元素添加了ref属性,属性值为ele,在setup()函数中声明了一个名称同样为ele的变量,并传入空值null,这样,通过ele.value就可以获取到该div元素。运行结果如图所示。

示例:单机文本改变样式。
定义一行文本,当单击文本时为文本设置颜色和大小,代码如下:
<div id="app">
<div ref="demo" @click="setStyle">{{text}}</div>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script type="text/javascript">
const vm = Vue.createApp({
setup() {
const text = Vue.ref('一寸光阴一寸金');
const demo = Vue.ref(null);
const setStyle = ()=> {
demo.value.style = "color:blue; font-size:50px";
};
return {
text, demo, setStyle
};
}
});
vm.mount('#app');
</script>

4万+

被折叠的 条评论
为什么被折叠?



