Vue笔记6--组合式API setup
1、组合式api-setup
组合式api将同一个逻辑关注点的代码收集在一起。在组件被创建前执行,props解析完成后被作为组合式api入口。setup取代了beforeCreate()
和created()
,由于是组件在被创建之前执行,不要使用this.
。
那么现在的问题是原本的数据data:{}
、监听器watch:
、计算属性computed:
、生命周期钩子函数
这四个基本的部分该怎样去改写。
注:还有一个基础部分方法方法methods:
export default {
data() {···},
setup() {
console.log('setup');
const msg = 'hello';
console.log(msg);
},
···
}
1.1响应式数据
ref
实现原本data:{}
,普通的setup中变量不是响应式需要借助ref()
,ref是一个函数会返回一个带有value属性的对象。先引入
import { ref, reactive ,toRef} from 'vue';
setup() {
const counter = ref(0)
function changeCounter() {
counter.value++
}
return {counter,changeCounter};
},
<!-- 模板自动解析value值不需要counter.vlaue -->
<h2>{{ counter }}</h2>
<button @click="changeCounter">改变counter</button>
封装看似没必要,但是可以保持不同数据类型行为统一。
reactive
reactive定义响应式的引用的数据对象
const obj = reactive({
name: "张三",
age: "18",
children: {
name: "小张"
}
})
return { obj, changeObj };
<h2>{{ obj }}--{{ obj.children.name }}</h2>
<button @click="changeObj">改变obj</button>
这其中我想要直接拿到孩子的名字,可以使用...
解构(es6语法)。代价是没有响应式。
return { ...obj};
toRef
toRef(object)
使解构后数据重新获得响应式两种return都可
let {name,children}=toRef(obj)
return {name,children };
return { ...toRef(obj)};
这个时候既可以直接拿到孩子的名字,也可以响应式
<h2>{{ children.name }}</h2>
1.2响应式监听
watch
响应式监听,在选项式api中watch是一个对象,这里是函数。接受三个参数:
- 一个想要监听的响应式引用或者getter函数
- 一个回调
- 可选配置选项(用的少)
setup() {
const counter = ref(0)
function changeCounter() {
counter.value++
}
const user = reactive({
name: '张三',
age: 18
})
function changeUserName() {
user.name = '李四'
}
//watch(侦听的响应式引用,回调函数)
watch(counter,(newVal,oldVal)=>{
console.log("newVal",newVal)
console.log("oldVal",oldVal)
})
return { counter, changeCounter, user, changeUserName };
}
但是此时用这种方式并不能监听到user属性的改变
watchEffect
可以深度监听,参数只有一个回调函数。不需要指定监听的属性,组件初始化的时候执行一次。只要在回调中引用到了响应式的属性,自动收集依赖。
watchEffect(() => {
console.log(user.name)
})
注意watch和watchEffect区别。
1.3Computed:
想在想要将个一个字符串反转后输出
const msg =ref('helloworld')
在选项式api中,computed
是一个属性,做法:
conmputed:{
reverseMsg:function(){
return this.message.split('').reverse().join('')
}
在组合式api中,computed()
是一个函数,做法:
<script>
import { computed } from '@vue/reactivity';
...
</script>
setup() {
...
const reverseMsg = conmputed(() => {
return msg.value.split('').reverse().join('')
})
console.log(reverseMsg.value)
},
computed()
返回值为一个带有value属性的对象
但疑惑的是
const user = reactive({
name: '张三',
age: 18,
reverseMsg: computed(() => {
return msg.value.split('').reverse().join('')
})
})
想要console.log打印,console.log(user.reverseMsg.value)
报undefind得不到结果,得console.log(user.reverseMsg)
猜想:user的内容是以key;value的键值对的形式储存,reverseMsg只不过是个键对应的也只有值。
1.4生命周期钩子
在setup中,引入后,通过在前面加“on”来访问组件的生命周期钩子,比如beforeMount
通过onBeforeMount
访问。
import { onBeforeMount,onMounted,onBeforeUpdate,onUpdated } from 'vue';
但是没有onBeforeCreate和onCreated,因为setup本身就包含着他们,直接在setup中写即可。
函数中有一个参数,接受一个回调函数,钩子被组件调用时将会执行。可重复执行
setup() {
onBeforeMount(() => {//挂载渲染之前
console.log('onBeforeMount');
})
onMounted(() => {//挂载渲染之后
console.log('onMounted');
})
onMounted(() => {//挂载渲染之后
console.log('onMounted');
})
return {};
}
2、Setup中组件间交互
解决了组件基本部分改写的问题,新的问题变成组件之间如何传递数据。Setup接受两个参数props
和context
2.1props
父传子响应式,传入新的prop将被更新
父组件App.vue
中
<Content :message='message' />
子组件Content.vue
中
export default {
props: {
message: {
type: String,
default: "你好"
}
},
setup(props){
console.log(props.message)
},
...
}
因为ES6解构会消除prop的响应性,解构可以在setup中使用toRefs完成此操作,如
setup(props) {
const { message } = props
console.log(message)
onUpdated(() => {
console.log(message)
})
},
这样从props拿到message不是响应式,onUpdated返回的还是最固定的值,要改为:
setup(props) {
console.log(props.message)
const { message } = toRefs(props)
console.log(message.value)
onUpdated(() => {
console.log(message.value)
})
},
注:toRefs()
返回的是对象,访问需要.value
如果props中有一个数据是可选的(父组件不给也没关系),那么需要props中可能就不存在他。这时torefs()
不会管他,需要用toRef
特别照顾,如
setup(props) {
const 可选数据 = toRef(props,'可选数据')
},
2.2context
context是一个普通js对象,非响应式可以安全进行ES6解构
暴露其他可能在setup中有用的值
context.attrs
Attribute,非响应式对象=$attrs
context.slots
插槽,非响应式对象=$slots
context.emit
触发事件,方法=$emit
context.expose
暴露公共property (函数)
.attrs Attribute
<Content class="box" id="content"/>
在父组件App.vue中被这样使用时,class和id就会在attrs
中
.slots插槽
见以前笔记
.emit触发事件
在子组件Content.vue中定义了一个counter想传给父组件,子传父自定义事件
const counter = ref(20)
function sendeParent() {
context.emit('injectCountent', counter.value)
}
<button @click="sendePrarent">发送数据</button>
父组件App.vue中
通过@绑定了在子组件里写的inject事件,点击按钮事件触发
<Content @injectCountent="injectCountent"/>
methods:{
injectCountent(value){
console.log(value)
}
},
.expose暴露
暴露公共property(函数、财产、所有物),如使用渲染函数时
子组件Content.vue中
先引入,再让setup返回一个渲染函数
import { ref, h } from 'vue'
setup(props, context) {
const counter = ref(20)
function sendeParent() {
context.emit('injectCountent', counter.value)
}
return () => h('div', counter.value)
},
父组件App.vue中
<Content class="box" id="content"/>
这时候页面上就会显示一个20,只要返回了渲染函数,子组件Content.vue其他标签(比如按钮)就会被渲染函数替换掉。那么就意味着sendeParent()
没法通过设置一个按钮点击事件使用,这时可以用expose解决。
子组件Content.vue中
setup(props, context) {
const counter = ref(20)
function sendeParent() {
context.emit('injectCountent', counter.value)
}
context.expose({
sendeParent, counter
})
return () => h('div', counter.value)
},
父组件App.vue中
可以直接通过ref来调用,这时候就又可以使用sendeParent来拿到子组件的数据了。
<Content ref="content" @injectCountent="injectCountent"/>
mounted(){
console.log(this.$refs.content)
this.$refs.content.sendeParent()
},
2.3访问组件的property
执行setup的时候只能访问到以下property(财产):props、attrs、slots、emit。换句话说无法访问以下组件选项:data、computed、methods、refs。
2.4Provide/Inject
import引入后
父组件App.vue中
两个参数1 name(string类型)
2 value
setup() {
provide('name', '张三')
return {};
},
子组件Content.vue中
两个参数1 name(string类型)
2 默认值(可选)
setup(props, context) {
const name = inject('name')
return { name }
},
<h2>{{ name }}</h2>
但是这样拿到的数据并不是响应式的,需要使用ref如下
父组件App.vue中
setup() {
const name =ref('张三')
provide('name',name)
return {};
},
3、语法糖
在单文件组件(SFC)中使用组合式api编译式语法糖,SFC允许组件的模板、逻辑、样式被封装在单个组件中。
<script setup>
import Content from './components/Content.vue';
</script>
可以直接替代export default注册等vue2的语法,甚至下面template也可以抛弃dvi根元素(但是建议还是就加上)。
<script >
import Content from './components/Content.vue';
export default {
components: { Content }
}
</script>
<template>
<div>
<Content/>
</div>
<Content/>
</template>
script setup语法糖
- 引入组件不需要注册
- 定义变量,在模板使用不需要return暴露出去,模板直接使用
定义响应式的变量还是需要从vue中引入ref,addB()也无需返回直接使用。
import { ref } from 'vue';
const b = ref(10)
function addB(){
b.value++
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏