首先说一下vue3主要特性:
一、setup函数的特性以及作用
可以确定的是 Vue3.0 是兼容 Vue2.x 版本的 也就是说我们再日常工作中 可以在 Vue3 中使用 Vue2.x 的相关语法 但是当你真正开始使用 Vue3 写项目时 你会发现他比 Vue2.x 方便的多
Vue3 的一大特性函数 ---- setup
1、setup函数是处于 生命周期函数 beforeCreate 和 Created 两个钩子函数之间的函数 也就说在 setup函数中是无法 使用 data 和 methods 中的数据和方法的
2、setup函数是 Composition API(组合API)的入口
3、在setup函数中定义的变量和方法最后都是需要 return 出去的 不然无法再模板中使用
二、setup函数的注意点:
1、由于在执行 setup函数的时候,还没有执行 Created 生命周期方法,所以在 setup 函数中,无法使用 data 和 methods 的变量和方法
2、由于我们不能在 setup函数中使用 data 和 methods,所以 Vue 为了避免我们错误的使用,直接将 setup函数中的this修改成了 undefined
3、setup函数只能是同步的不能是异步的
以下为具体的一些用法:
1、 子传父
setup (props, ctx) {
// 这里的ctx可以随便命名,它暴露了组件的三个property
// Attribute (非响应式对象)
console.log(ctx.attrs)
// 插槽 (非响应式对象)
console.log(ctx.slots)
// 触发事件 (方法)
console.log(ctx.emit)
// 暴露公共 property (函数)
console.log(ctx.expose)
const changeParentNum = data => {
// 通过ctx调用emit事件 需要注意的是Vue2.x中使用 $emit切勿混淆
ctx.emit('handle', data)
}
return {
changeParentNum
}
}
2、 父传子
直接上代码
在父里面引用组件,为其绑定属性:
<Form :form='form' :data='data' ref="iForm" />
在setup函数里定义好变量,切记必须return出去
setup(props, { attrs, slots, emit, expose }) {
const form = reactive({
})
const iForm = ref() // 表单dom
const data = reactive({
sourceNumber: '',
})
return {
form,
data,
}
},
子组件中(这里只说明怎么使用,代码仅供参考):
以element表单组件为列:
<el-form :model="data" ref="form">
<el-row>
<el-col v-for="col in form" :key="col.label" :span="col.span">
</el-col>
</el-row>
</el-form>
props: {
form: Object,
data: Object,
},
setup(props, context) {
return {
form: props.form,
data: props.data,
}
},
父组件用reactive定义数组,给子组件传值会传不过去,必须在reactive里定义才行,不然就不是响应式对象。
父组件里面定义的值为:
<Table :table='table' :pager='pager' :data='tableData.data' />
// 表格数据
let tableData = reactive({
data: [],
})
子组件取值:
<el-table :data="data" style="width: 100%" row-key="oid" border>
在setup里面用计算属性监听值的改变
const data = computed(() => props.data)
return {
data,
}
3、 子取父里面的属性/方法
在vue2.x中可以使用 this.$parent.+父组件的方法名/父组件的属性名 直接使用父组件的data定义的字段或者调用方法,在3.x中是有区别的:
获取父组件中的属性:
import { getCurrentInstance,reactive } from 'vue'
setup (props, content) {
// 通过getCurrentInstance取父的属性
const { proxy } = getCurrentInstance()
const childVal = proxy.$parent.val
}
获取父组件中的方法:
setup (props, content) {
// 通过getCurrentInstance取父的属性
const { proxy } = getCurrentInstance()
proxy.$parent.method
}
4、 父组件取子组件中的属性/方法
<child ref="dom1"></child>
<el-button size="small" icon="el-icon-search" @click="handleSearch">搜索</el-button>
import { defineComponent, reactive, toRefs, ref } from 'vue'
setup(props, ctx) {
// 这里用ref和toRefs都可以,两者的区别可以自行去了解
const dom1 = ref() // 表单dom
// const dom1 = reactive({
// console.log(dom1.value)
// 可以触发事件
// handleSearch() {
// console.log(dom1.value)
// },
// })
nextTick(() => {
console.dir(dom1.value); // console.dir()可以显示一个对象所有的属性和方法
});
return {
dom1,
...toRefs(dom1),
}
},
5、 生命周期
vue2 | vue3 |
---|---|
选项式 API | Hook inside setup |
beforeCreate | Not needed* |
created | Not needed* |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
errorCaptured | onErrorCaptured |
renderTracked | onRenderTracked |
renderTriggered | onRenderTriggered |
activated | onActivated |
deactivated | onDeactivated |
6、 watch监听
单个ref对象监听(这里以监听组件的dom为例):
<form-info @handleSearch='handleSearch' ref="formInfo" />
<table-info ref="tableInfo" />
// Setup里监听
const tableInfo = ref()
watch(tableInfo, (newData, old) => {
console.log(newData)
})
多个对象监听:
const formInfo = ref(0)
const tableInfo = ref(1)
// Setup里监听
watch([formInfo, tableInfo], ([formDom, tableDom], [formState, tableState]) => {
console.log(formDom) // 返回formDom的Proxy对象
console.log(tableDom) // 返回tableDom的Proxy对象
console.log(formState) // ref里的值
console.log(tableState) // ref里的值
})
需要注意的一点是,如果监听store里面的值,略微有点变化:
const store = useStore()
watch(() => {
return store.state.val
},(newVal,oldVal) =>{
})
7、获取路由
import { defineComponent, ref, reactive, watch, toRaw, getCurrentInstance } from 'vue';
export default {
setup () {
const { proxy }: any = getCurrentInstance();
console.log(proxy.$router.currentRoute.value);
const router = useRouter();
console.log(router.currentRoute.value);
watch(
() => router,
(newValue, oldValue) => {
console.log(newValue.currentRoute.value, '新的路由');
console.log(newValue.currentRoute.value, '旧的路由');
},
{ immediate: true }
);
}
}
8、store的使用
import { useStore } from 'vuex'
// 在setup里直接使用:
const store = useStore()
console.log(store)
9、vue3.2组件通信方式
9.1、父传子–defineProps
// 父组件
<template>
<sub-component ref="sub" :info='value' :list="list" />
<div>
<input
v-model="inputValue"
type="text"
placeholder="请输入"
/>
<div>
<button @click="handleAddBtn">
添加
</button>
</div>
</div>
</template>
<script setup lang="ts">
const list = ref(['JavaScript', 'HTML', 'CSS'])
const handleAddBtn = () =>{
list.value.push(inputValue.value)
inputValue.value = ''
}
</script>
// 子组件
<template>
<div>
<ul>
<li v-for="item in props.list" :key="item">{{item}}</li>
</ul>
</div>
</template>
<script setup lang="ts">
import {defineProps} from 'vue'
// 这里可以引入'defineProps',也可以不用引,因为vue3.2已经内置了此方法
// 'defineEmits'和'defineExpose'也是一样,可引可不引
const props = defineProps({
list: {
type: Array,
default: () => []
}
})
</script>
9.2、子传父–defineEmits
// 子组件
<template>
<button @click="handelAdd">新增</button>
</template>
<script lang="ts" setup>
const $emit = defineEmits(['addBtn'])
const handelAdd= () =>{
$emit('addBtn', '新增的数据')
}
</script>
// 父组件
<template>
<sub-component ref="sub" @addBtn="getAdd" />
</template>
<script lang="ts" setup>
const getAdd= (val: string) =>{
console.log(val)
}
</script>
9.3、组件暴露自己的属性–defineExpose
<template>
<p>{{ msg }}</p>
</template>
<script lang="ts" setup>
const msg = ref('向外暴露属性')
defineExpose({
msg
})
</script>
// 父组件
<template>
<sub-component ref="selfRef" />
<button @click="handelProperty">新增</button>
</template>
<script lang="ts" setup>
const selfRef = ref()
const handelProperty = () =>{
console.log(selfRef.value.msg)
}
</script>