目录
组合式API
前言
组合式API的简单使用
ref和reactive
toRefs
watch与watchEffect响应式更改
在setup中使用计算属性
在setup中使用生命周期函数
setup中的参数
props
父组件向子组件传值
子组件从父组件接收值
context
获取父组件内子组件的属性
触发事件
暴露公共属性
返回页面的渲染函数
在setup中使用provide-inject
父组件向子组件发送值
子组件接收父组件值
组合式API语法糖
基本语法
基本使用
组合式API
前言
含义:在使用vue的7大属性来组织逻辑时都很有效,但是当我们的组件开始变大的时候,逻辑关注点的列表也会不断增长,因此对于同一个业务逻辑可能造成东一块西一块,这样项目便不容易维护,组合式API的出现就是为了解决这种问题,它可以把相关业务逻辑组合在一起,最终项目易于维护
组合式API的入口:vue的新属性setup
setup函数执行时机:其会在组件被创建之前执行——经验证setup函数会在beforeCreated函数之前执行
注意:在setup函数中应尽量避免使用this,因为他不会找到组件实例,setup的调用发生在data、methods等被解析之前,所以他们无法在setup中获取
组合式API的简单使用
<template>
<div class="hello">
<!-- helloworld组件内 -->
<h1>{{msg}}</h1>
<button @click="changeMsg()">改变msg</button>
</div>
</template>
<script>
export default {
//组合式API,将同一个逻辑关注点的相关代码收集到一起
//组合式API会在组件被创建之前执行
setup(){
//定义变量
let msg="Good idea"
//改变msg
function changeMsg(){
//注意:这里改变了msg并不会响应到模板页面中
msg="Nice"
console.log(msg);
}
//暴露变量,方法
return {msg,changeMsg}
}
}
</script>
ref和reactive
ref:定义基本数据类型
reactive:定义引用数据类型
目的:实现响应式
使用前提:import {ref,reactive} from 'vue'
<template>
<div class="hello">
<!-- helloworld组件内 -->
<!-- 模板会自动解析value值 -->
<h1>{{counter}}</h1>
<h1>{{user.name}}</h1>
<button @click="changeMsg()">改变msg</button>
<button @click="changeUser()">改变user</button>
</div>
</template>
<script>
//引入ref
import {ref,reactive} from 'vue'
export default {
setup(){
//定义变量
const msg="Good idea"
const counter=ref(msg)
//改变msg
function changeMsg(){
//实现响应式
counter.value="Nice"
}
//定义user
const user=reactive({
name:"lili",
age:18
})
//改变user
function changeUser(){
user.name="lala"
user.age=28
}
//暴露变量,方法
return {counter,changeMsg,user,changeUser}
}
}
</script>
注意:暴露的对象尽量不要用展开运算符进行解构,因为它会使对象中的属性不是响应式的
toRefs
toRefs作用:让结构赋值后的数据重新获得响应式
使用前提:import {reactive,toRefs} from 'vue'
<template>
<div class="hello">
<h1>{{children.name}}</h1>
<h1>{{name}}</h1>
<button @click="changeUser()">改变user</button>
</div>
</template>
<script>
//解构赋值响应式
import {reactive,toRefs} from 'vue'
export default {
setup(){
//定义user
const user=reactive({
name:"lili",
age:18,
children:{
name:"luck"
}
})
//改变user
function changeUser(){
user.name="lala"
}
//toRefs使解构后的数据重新具有响应式(加了toRefs后解构后的数据变成了对象,内部有value属性,同时也具有响应式功能)
return {changeUser,...toRefs(user)}
}
}
</script>
watch与watchEffect响应式更改
使用前提:import {ref,reactive,watch,watchEffect} from 'vue'
<template>
<div class="hello">
<h1>{{counter}}</h1>
<h1>{{user.name}}</h1>
<button @click="changeCounter()">改变counter</button>
<button @click="changeUser()">改变user</button>
</div>
</template>
<script>
//解构赋值响应式
import {ref,reactive,watch,watchEffect} from 'vue'
export default {
setup(){
const counter=ref("hello")
function changeCounter(){
counter.value="hi"
}
//监听属性
watch(counter,(newValue,oldValue)=>{
console.log("counter被改变了,新值为:"+newValue+"旧值为:"+oldValue);
})
const user=reactive({
name:"lili",
age:18
})
function changeUser(){
user.name="lala"
}
//监听对象
watchEffect(()=>{
//user.name若发生改变的话,那么就会执行该函数
console.log(user.name);
})
return {changeCounter,counter,user,changeUser}
}
}
</script>
注意:watchEffect不需要指定监听的属性,其会自动收集依赖,只要在回调中引入响应式的属性,只要这些属性发生改变,那么就会执行该函数;而watch就只能够监听指定的属性
在setup中使用计算属性
使用前提:import {ref,computed} from 'vue'
<template>
<div class="hello">
<h1>{{msg}}</h1>
<h1>{{reverseMsg}}</h1>
<button @click="changeMsg()">改变msg</button>
</div>
</template>
<script>
//解构赋值响应式
import {ref,computed} from 'vue'
export default {
setup(){
const msg=ref("hello")
function changeMsg(){
msg.value="hi"
}
//计算属性的使用
const reverseMsg=computed(()=>{
return msg.value.split("").reverse().join("")
})
return {msg,changeMsg,reverseMsg}
}
}
</script>
在setup中使用生命周期函数
前言:可以通过生命周期钩子函数前面加"on"?来访问组件的生命周期钩子
注意:因为setup是围绕beforeCreate和Created函数运行的,所以不需要显示的定义他们,换句话说,在这些钩子中编写的任何代码都应该直接在setup中编写
使用前提:import {onMounted} from "vue"(引入特定的生命周期钩子)
<template>
<div class="hello">
<h1>生命周期函数</h1>
</div>
</template>
<script>
//使用生命周期函数时必须引入
import {onMounted} from "vue"
export default {
setup(){
//没有调用,自动执行,还比以前有优势,以前同一个生命周期函数只能存在一个,现在可以存在多个
onMounted(() => {
console.log("挂载后");
})
onMounted(() => {
console.log("挂载后的第二个生命周期函数");
})
}
}
</script>
setup中的参数
注意:使用setup时他会接收两个参数(props,context)
props
父组件向子组件传值
<template>
<div id="first">
<!-- 父组件内 -->
<Second :msg="message" :name="username"></Second>
</div>
</template>
<script>
import Second from "./Second.vue";
export default {
data(){
return{
message:"helloworld",
username:"lili"
}
},
components:{
Second
}
}
</script>
子组件从父组件接收值
<template>
<div id="second">
<!-- 子组件内 -->
<h1>second</h1>
</div>
</template>
<script>
export default {
props: {
msg: {
type: String,
default:"你好"
},
name:{
type:String,
default:"大白"
}
},
//setup中获取父级传过来的值
setup(props) {
console.log(props);
//获取特定的传过来的值
console.log(props.msg);
}
}
</script>
context
理解:context为一个普通的js对象,暴露了其他可能在setup中有用的值
获取父组件内子组件的属性
<template>
<div id="first">
<!-- 父组件内 -->
<Second class="box"></Second>
</div>
</template>
<script>
import Second from "./Second.vue";
export default {
components:{
Second
}
}
</script>
<template>
<div id="second">
<h1>second</h1>
</div>
</template>
<script>
export default {
setup(props,context) {
//拿到父组件中定义在该组件内的属性
console.log(context.attrs);
}
}
</script>
插槽:context.slot等价于slot
触发事件
<template>
<div id="second">
<button @click="sendParent">发送数据</button>
</div>
</template>
<script>
import {ref} from 'vue'
export default {
setup(props,context) {
const count=ref(20)
//将值传给父组件
function sendParent(){
context.emit("send",count.value)
}
return {sendParent}
}
}
</script>
<template>
<div id="first">
<!-- 父组件内 -->
<Second @send="injectCount"></Second>
</div>
</template>
<script>
import Second from "./Second.vue";
export default {
methods:{
injectCount(value){
console.log(value);
}
},
components:{
Second
}
}
</script>
暴露公共属性
<template>
<div id="second">
<h1>second</h1>
</div>
</template>
<script>
import { ref,h } from 'vue';
export default {
setup(props,content){
const msg=ref("hello")
//在返回渲染函数的情况下还想向外暴露内容从而可以被外部使用
content.expose({
msg
})
return ()=>h("div","helloworld")
}
}
</script>
<template>
<div id="first">
<Second ref="second"></Second>
</div>
</template>
<script>
import Second from "./Second.vue";
export default {
components:{
Second
},
mounted(){
//父组件访问子组件内容
console.log(this.$refs.second.msg);
}
}
</script>
返回页面的渲染函数
使用:在父组件内使用子组件second
子组件内
使用前提:import { h } from 'vue';
<template>
<div id="second">
<h1>second</h1>
</div>
</template>
<script>
import { h } from 'vue';
export default {
setup(){
//h为渲染函数,第一个参数为渲染根元素,第二个元素为渲染根元素里的内容
//返回渲染元素后就会渲染该渲染函数内的元素,而不渲染当前的组件了
return ()=>h("div","helloworld")
}
}
</script>
在setup中使用provide-inject
父组件向子组件发送值
使用前提:import { provide,ref } from "vue";
provide函数的两个参数:(发送属性的名称,发送属性的值)
<template>
<div id="first">
<Second></Second>
<button @click="changeName">更改值</button>
</div>
</template>
<script>
import { provide,ref } from "vue";
import Second from "./Second.vue";
export default {
setup() {
//实现响应式
const name=ref("张三")
//父组件向子组件传值
provide('name', name)
function changeName(){
name.value='luck'
}
return {name,changeName}
},
components: {
Second
}
}
</script>
子组件接收父组件值
inject函数的两个参数:(inject的属性名称,接收属性的默认值)其返回的值就为接收的值
使用前提:import { inject } from 'vue';
<template>
<div id="second">
<h1>{{name}}</h1>
</div>
</template>
<script>
import { inject } from 'vue';
export default {
setup() {
const name=inject('name','lili')
return {name}
}
}
</script>
组合式API语法糖
基本语法
<script setup>
console.log("setup");
</script>
注意:这样写的代码会被编译成组件setup函数内的内容。这意味着与普通的script只在组件被首次引用的时候执行一次不同,<script setup>中的代码会在每次组件实例被创建的时候执行
基本使用
<template>
<div id="first">
<h1>{{a}}</h1>
<button @click="change">改变值</button>
<Second></Second>
</div>
</template>
<script setup>
import { ref } from 'vue'
import Second from "../components/Second.vue"
const a =ref(20)
function change(){
a.value=80
}
</script>
总结:
- 引入组件不需要注册
- 定义的变量可以直接在模板中使用,不需要导出
- 定义响应式的变量还是要从vue中引入相关内容
不需要导出数据的原因:顶层的setup绑定会将数据暴露给模板?
|