Vue3 语法
在 <template>
中使用的语法,Vue3 和 Vue2 几乎没有差别。但是在 <script>
中使用的语法,Vue3 有比较大的变化。
一、template 根节点
在 Vue3 中,<template>
不再需要根节点。
<template>
<div></div>
<h1></h1>
</template>
二、子组件渲染
在 Vue3 中,父组件中如果要渲染子组件,只需要“引入”和“渲染”两个步骤。
<template>
<HelloWorld />
</template>
<script setup>
import HelloWorld from './components/HelloWorld.vue';
</script>
三、setup
Vue3 从最早的测试版,然后到正式版,以及正式版后续的新提案,每一个版本的 setup 的语法都不太一样。目前我们采用最新版的语法:
<script setup>
</script>
在 VSCode 中,可以通过快捷键 v3
来自动生成该版本的组件基本代码。
四、组件的数据
1、ref
<template>
<h1>计数器:{{ count }}</h1>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const count = ref(0);
console.log(count.value);
</script>
语法说明:
- Vue3 的组件中可以通过
ref()
来定义组件内部数据; - 每个组件中使用
ref
时,都需要先引入; - 在调用
ref()
方法,可以传递一个参数来作为组件内部数据的初始值; ref()
定义的数据,在template
中使用时,可以直接通过变量名来访问;ref()
定义的数据,在script
中使用时,必须通过变量名.value
来访问;
2、reactive
<template>
<h1>计数器:{{ state.count }}</h1>
</template>
<script setup lang="ts">
import { reactive } from 'vue';
const state = reactive({
count: 0
});
console.log(state.count);
</script>
语法说明:
- Vue3 的组件中可以通过
reactive()
来定义组件内部数据; - 每个组件中使用
reactive
时,都需要先引入; - 在调用
reactive()
方法,需要传递一个数组或对象来作为组件数据的初始值; reactive()
定义的数据,在template
和script
中的访问方式是一样的;- 通过
reactive()
定义的数组或对象,在修改时不能修改数组或对象的地址;
五、组件的事件
<template>
<button @click="increment">+1</button>
</template>
<script setup lang="ts">
const increment = () => {
console.log("事件方法");
}
</script>
六、计算属性
<script setup lang="ts">
import { computed } from 'vue';
const 变量 = computed(() => {
return 计算结果;
})
</script>
七、侦听器
<script setup lang="ts">
import { ref, watch } from 'vue';
const count = ref(0);
watch(() => count.value, (newValue, oldValue) => {
console.log('count.value 发生变化了', newValue, oldValue);
});
</script>
https://blog.csdn.net/m0_49515138/article/details/128250061
语法说明:
watch()
在使用前需要先引入;watch()
接收两个函数作为参数,第一个函数的返回值就是需要侦听的数据,当侦听的数据发生变化时,会执行第二个函数;
深度侦听和立即侦听
watch(() => count.value, (newValue, oldValue) => {
console.log('count.value 发生变化了', newValue, oldValue);
}, {
immediate: true,
deep: true
});
八、watchEffect
<script setup lang="ts">
import { ref, watchEffect } from 'vue';
const count = ref(0);
watchEffect(() => {
console.log('你好', count.value);
})
</script>
语法说明:
watchEffect()
在使用前需要先引入;watchEffect()
需要接收一个函数作为参数;watchEffect()
会自动监听函数中用到的所有数据,当函数中任意数据发生改变时,watchEffect()
都会重新执行;
watch 和 watchEffect 的区别
- 默认情况下,watchEffect 在页面首次刷新时会执行一次,而 watch 不会;
- watch 可以接收到数据变化前后的值,而 watchEffect 没有;
- watch 需要指定侦听的数据,watchEffect 是自动侦听函数中用到的数据;
九、生命周期函数
Vue2 | Vue3 |
---|---|
beforeCreate | 无 |
created | 无 |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeDestroy | onBeforeUnmount |
destroyed | onUnmounted |
示例代码:
import { onMounted } from 'vue';
onMounted(() => {
console.log('组件挂载完成')
})
十、组件传值
1、父传子:prop
Vue3 中,和 Vue2 一样,也是通过 prop 来实现父组件给子组件传值。
父组件传递数据和 Vue2 的语法没有区别,但是子组件在接收 props 数据时,Vue3 的语法如下:
<template>
<div>
<h1>接收父组件传递的数据:{{ count }}</h1>
</div>
</template>
<script setup lang="ts">
const props = defineProps({
count: Number
});
console.log(props.count);
</script>
2、子组件接受值
通过defineProps 来接受, defineProps是无须引入的直接使用即可
这是TS特有的:
<template>
<div class="menu">
菜单区域 {{ title }}
<div>{{ data }}</div>
</div>
</template>
<script setup lang="ts">
defineProps<{
title:string,
data:number[],
obj:{
type:Object,
default(){
return {}
}
}
}>()
</script>
3、TS 特有的默认值方式
withDefaults是个函数也是无须引入开箱即用,接受一个props函数第二个参数是一个对象设置默认值
type Props = {
title?: string,
data?: number[]
}
withDefaults(defineProps<Props>(), {
title: "张三",
data: () => [1, 2, 3]
})
4、子传父:自定义事件 defineEmits
Vue3 中,和 Vue2 一样,也是通过自定义事件来实现子组件给父组件传值。
父组件绑定自定义事件和 Vue2 的语法没有区别,但是子组件中触发自定义事件时,Vue3 的语法如下:
<template>
<button @click="toFather">按钮</button>
</template>
<script setup lang="ts">
const emit = defineEmits(['getData'])
const toFather = () => {
// 调用自定义事件,并传值
emit('getData', 100);
}
</script>
<template>
<div class="menu">
<button @click="clickTap">派发给父组件</button>
</div>
</template>
<script setup lang="ts">
import { reactive } from 'vue'
const list = reactive<number[]>([4, 5, 6])
const emit = defineEmits(['on-click'])
const clickTap = () => {
emit('on-click', list)
}
</script>
5、父组件接受子组件的事件
<template>
<div class="layout">
<Menu @on-click="getList"></Menu>
<div class="layout-right">
<Header></Header>
<Content></Content>
</div>
</div>
</template>
<script setup lang="ts">
import Menu from './Menu/index.vue'
import Header from './Header/index.vue'
import Content from './Content/index.vue'
import { reactive } from 'vue';
const data = reactive<number[]>([1, 2, 3])
const getList = (list: number[]) => {
console.log(list,'父组件接受子组件');
}
</script>
6、子组件暴露给父组件内部属性
通过defineExpose
我们从父组件获取子组件实例通过ref
<Menu ref="menus"></Menu>
const menus = ref(null)
这时候父组件想要读到子组件的属性可以通过 defineExpose暴露
const list = reactive<number[]>([4, 5, 6])
defineExpose({
list
})
十一、ref、toRef、toRefs的区别
toRefs(obj)
:作用是将响应式对象中的所有属性转换为单独的响应式数据,原始数据变为普通对象,值是关联的
响应式数据展开后会失去响应式{}
<script lang="ts">
import { reactive } from "vue";
export default{
setup(){
const user = reactive<any>({
name: "张三",
age: 23,
addr: {
province: "河南",
city: "郑州"
}
})
return {
...toRefs(user);
let name = toRef(user, "name")
}
}
}
</script>
toRef(obj, key)
:作用是将响应式对象中的某一个属性转换为响应式数据,接受两个参数,第一个是哪个对象,第二个是哪个属性
十二、isref()
unref()
isref()
检查一个值是否为ref对象
unref()
如果参数是一个ref则返回它的value,否则返回参数本身
unref():是 val = isRef(val) ? val.value : val 的语法糖。
const valueRef = ref('');
const value = unref(valueRef);
if (!value) {
console.warning('请输入要拷贝的内容!');
return;
}
十三、defineExpose()
defineExpose()
:使用<script setup>
的组件默认是关闭的,可以通过模板引用或者$parent
链获取到组件的公开实例,不会暴露任何在 <script setup>
中声明的绑定
可以通过defineExpose()
编译器宏来显示指定在<script setup>
组件中要暴露出去的属性
<script setup>
import { ref } from 'vue'
const a = 1
const b = ref(2)
defineExpose({a, b})
</script>
十四、defineComponent()
在定义Vue组件时提供类型推导的辅助函数
十五、获取节点的原生dom对象
<infoCard v-show='showInfoWindow' :ref="(el) => infoWindow = el" />
<script setup lang="ts">
import { ref, getCurrentInstance } from "vue";
// 获取页面的实例对象
// const pageInstance = getCurrentInstance();
// 定义信息窗口
window.infoWindow = new AMap.InfoWindow({
// 使用自定义窗体
isCustom: true,
// 只有当组件渲染完毕后,拿到原生的dom对象
content: infoWindow.value.$el,
// 只有当组件渲染完毕后,通过pageInstance才能拿到原生的dom对象
// content: pageInstance?.refs.infoWindow,
anchor: "bottom-left",
offset: new AMap.Pixel(30, 0),
});
</script>
十六、toRaw获取原始对象
使用vue3.0时,因为底层是使用proxy进行代理的所以当我们打印一些值得时候是proxy代理之后的是Proxy
对象,Proxy对象里边的[[Target]]才是真实的对象。
1、通过vue中的响应式对象可使用 toRaw() 方法获取原始对象
//第一种获取target值的方式,通过vue中的响应式对象可使用toRaw()方法获取原始对象
import { toRaw } from 'vue'
let obj=toRaw(props.formAllValue)
2、通过json序列化之后可获取值
//第二种获取target值的方式,通过json序列化之后可获取值
JSON.parse(JSON.stringify(store.getters.menuList))
十七、getCurrentInstance
// 此段代码写入main.js中
app.config.globalProperties.$t = i18n.global.t;
import { getCurrentInstance } from "vue";
const _this = getCurrentInstance()?.appContext.config.globalProperties ;
console.log(_this)
十八、h()
createVNode()
h()
函数和createVNode()
函数都是创建dom节点,他们的作用是一样的,但是在VUE3中createVNode()函数的功能比h()函数要多且做了性能优化,渲染节点的速度也更快
h(标签, {属性},内容)
h(标签, {属性},[可以继续嵌套h()])
createVNode(标签, {属性},内容)
createVNode(标签, {属性},[可以继续嵌套createVNode()])
在createVNode()
或者 h()
中 设置ref
开发环境下可以正常使用,生产环境中报错Cannot read properties of null (reading 'refs')
解决方案:使用函数包含返回
<template>
<el-button :plain="true" @click="openVn">VNode</el-button>
</template>
<script lang="ts" setup>
import { ref, h } from 'vue'
import { ElMessage } from 'element-plus'
const messageRef = ref()
const openVn = () => {
ElMessage({
message: () => h('div', { ref: messageRef }, [
h('span', null, 'Message can be '),
h('i', { style: 'color: teal' }, 'VNode'),
]),
})
}
</script>
十九、markRaw()
/shallowRef()
/shallowReactive()
markRow()
:标记一个对象,使其永远不会变成一个响应式对象,跳过proxy
代理;
toRaw()
:将一个由reactive
生成的响应式对象转换为普通对象;
shallowRef()
:只处理基本数据类型的响应式,不进行对象的响应式处理;
shallowReactive()
:只处理对象最外层属性的响应式(浅响应式);