前段时间,因为一些新的语法糖和api弄的有点迷糊,故想整理一下3.0,3.2 相关的、常用的新api,语法糖等。可能会有遗漏,想起来了再补.
- ref
- reactive
- toRef
- toRefs
- unref,isRef
- isReactive
- toRaw
- markRaw
- v-model
- computed
- watch
- onMounted
- defineProps
- defineEmits
- defineExpose
- useRoute, useRouter
- await
- globalProperties
- css => v-bind()
- volar
- provide, inject
- solt, useSlots
Vue3.0 变量函数必须 return 出来,template中才能使用;
Vue3.2 在script标签中添加setup属性,替代了setup() { return {} }, 在标签内的变量函数,就可以template中使用
// 3.2
<template>
<div @click="handleClick">{{msg}}</div>
</template>
<script lang="ts" setup>
const msg = "setup语法糖"
const handleClick = (): void => { msg.value = "3.0 => function" }
</script>
<style>
</style>
// 3.0
<template>
<div @click="handleClick">{{msg}}</div>
</template>
<script lang="ts">
setup() {
const msg = "3.0 setup"
const handleClick = () => { msg.value = "3.0 => function" }
return { msg, handleClick }
}
</script>
<style>
</style>
ref
ref 是创建一个响应式的变量(官方推荐ref定义一个基础类型的变量,当然你也可以引用数据类型),返回的是一个RefImpl实例对象包裹的响应数据.
// 定义一个ref变量
const count = ref(1)
// 改变ref的值
count.value = 2
reactive
reactive 是创建一个响应式的引用类型的数据。
const obj = reactive({})
obj.name = "name"
ref和reactive的区别
- ref基本是用来创建一个基本数据类型,reactive是用来创建一个引用数据类型。
- ref的底层其实还是用object.defineProperty()来数据劫持的,reactive是用Proxy来实现劫持的。然后底层通过Reflect的get,set来改变内部数据。
- ref改变值需要在值后面加.value,当然在template的dom中显示不需要加。reactive改变值不需要加.value
unref, isRef
unref,isRef,见名知义,就是将ref类型的数据转换成普通的数据,判断是不是ref类型的数据
/* 这个可以根据情况灵活运用 */
const count = ref(0)
const value = computed(() => isRef(count) ? unref(count) : count)
isReactive
isReactive就是判断是不是reactive对象,返回boolean
const state = reactive({ name: "caicai" });
const isReac = computed(() => isReactive(state));
toRaw
toRaw 作用就跟unref一样,只不过是toRaw只将reactive定义的对象转换为普通的对象。使该对象不再是响应式的
const state = reactive({
name: "caicai"
});
const rawState = toRaw(state);
// 数据会变,但是视图不发生变化了
rawState.name = "chaoshen";
markRaw
markRaw标记一个对象,不能成为代理对象。返回也是一个普通对象
const state = reactive({
custom: markRaw({
age: 18
}),
name: "caicai"
});
// custom视图不会发生变化,视图显示age还是18.
setTimeout(() => {
state.custom.age = 22;
console.log(state.custom); // { age: 22 },数据会发生变化
}, 1000);
我个人感觉吧,有点鸡肋,可能是我目前遇到这样写的需求比较少,但是他有时候确实是会报这个警告。
就是这个,如果你不用markraw包一下你传入component,他就说你传入的组件是一个响应式的对象。
v-model
众所周知,vue父子组件通过props传值是单向数据流,在vue2里使用了.anyc异步处理了。在3里是用v-model做处理
// 父组件
<button @click="ok"></button>
<children v-model="visible"></children>
<script lang="ts" setup>
import children from "./children.vue";
const visible = ref(false);
const ok = (): void => visible.value = true
</script>
// 子组件
<div v-show="visible" @cancel="cancel"></div>
<script lang="ts" setup>
const props = defineProps({
visible: {
type: boolean,
default: () => false,
required: true
}
})
const emit = defineEmits(["update:visible"])
const cancel = (): void => {
emit("update:visible", false)
}
</script>
computed
computed 计算属性,功能没变,写法有点变化
<div>{{ sum }}</div>
const count = 2
const sum = computed(() => count * 2 );
watch
watch 监听属性嘛,和vue2一样
const count = ref(0);
watch(
count,
(newval, oldval) => {
console.log(newval, oldval)
},
{ immediate: true, deep: true })
监听reactive时
const state = reactive({
name: "caicai"
});
watch(
() => state.name,
(newval, oldval) => {
console.log(newval, oldval)
},
{ immediate: true, deep: true })
监听多个数据时
const state = reactive({
name: "caicai",
age: 18
});
watch(
[() => state.name, () => state.age],
(newval, oldval) => {
// newval 和 oldval 也是数据
console.log(newval, oldval)
},
{ immediate: true, deep: true })
onMounted
onMounted,就是mounted在vue3中的生命周期api
import { onMounted } from "vue"
onMonuted(() => console.log("页面加载完成"));
defineProps
在vue3.2中,props需要通过defineProps方法来返回。
<template>
<div>{{ showCount }}</div>
</template>
<script lang="ts" setup>
const props = defineProps({
count: {
type: number,
required: true,
defalut: () => 0
}
})
const showCount = computed(() => props.count * 2)
</script>
defineEmits
在vue3.2中,emit方法,通过defineEmits方法返回
//在vue3的时候还需要注册事件,做配置
emit: ["submit"]
/**************/
//3.2直接就可以同时注册,返回
const emit = defineEmits(["submit", "update:visible"])
const submit = () => {
emit("submit", formState)
}
const cancel = () => {
emit("update:visible", false)
}
defineExpose
defineExpose 代替了vue中的expose方法,常用于将子组件中方法,变量暴露出去。
// 父组件
<template>
<div>
<Children ref="childRef" />
</div>
</template>
<script setup>
import Children from "./children.vue";
components: { Children };
const childRef = ref();
console.log(childRef.value.count); // 2
</script>
// 子组件
<script setup>
const count = ref(2);
definedExpose({
count
})
</script>
useRoute,useRouter
在vue3中,useRoute,useRouter, 需要通过vue-router引入, 相当于vue2的 this.$route,this.$router,然后其他之前vue2的操作都可以进行
<script lang="ts" setup>
import { useRoute, useRouter } from "vue-router";
const route = useRoute();
const router = useRouter();
const handlePath = () => {
cosole.log(route.query);
router.push("/home");
}
</script>
await
在script-setup 语法糖之下,await语法可以直接使用。
<script setup lang="ts">
const post = await fetch(`/api/post/1`).then((r) => r.json())
</script>
它转换成标准组件的写法就是:
<script lang="ts">
import { defineComponent, withAsyncContext } from 'vue'
export default defineComponent({
async setup() {
const post = await withAsyncContext(
fetch(`/api/post/1`).then((r) => r.json())
)
return {
post
}
}
})
</script>
globalProperties
app.config.globalProperties 相当于vue2中的 vue.prototype
// main.js
import { createApp } from 'vue';
import App from './App.vue';
const app = createApp(App); // 获取原型
app.config.globalProperties.name = "caicai"; // 绑定参数
在组件中使用,需要引用getCurrentInstance
<script setup>
import { getCurrentInstance } from 'vue' // 获取原型
const { proxy } = getCurrentInstance() // 输出
console.log(proxy.name) // "caicai"
</script>
Bind
3.2 之后呢,我们之前梦寐以求的,在js里定义变量,计算dom的宽高,移动距离,然后拿变量在css里用。实现了。
<template>
<div class="wrap">
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
const bodyHeight = ref<string>("");
const countHeaderHeight = (): void => {
const headDom = document.querySelector(".head");
if (headDom) {
headerHeight.value = (headDom as any).offsetHeight;
}
bodyHeight.value = `calc(100% - ${headerHeight.value}px)`;
onMounted(() => {
countHeaderHeight();
});
};
</script>
<style scoped>
.wrap {
// 使用v-bind绑定state中的变量
height: v-bind(bodyHeight);
}
</style>
volar
与vetur相同,volar是一个针对vue的vscode插件,由于之前vetur对vue3和ts,兼容都不是很好,volar就相当于是vetur二代。集成了高亮,代码提示等等,还新增了一些列的功能,感兴趣的同学可以搜搜其具体的功能。
provide, inject
provide, inject 在vue3中是我比较常用的一种祖先后代传值的一种,毕竟一直props传值层数多了也挺烦的。比之前vue2的写法也要简单的多。
// vue2的 provide出去
provide () {
return {
tags: this.tags
}
}
inject: [
'tags'
],
在vue3中,用法简单,想要做到数据响应也很简单,只需要传递一个响应的数据就行
const state = reactive({
name: "caicai"
})
provide("state", state);
/****************/
const state = inject("state");
不管是父组件改变state的值,还是子组件的值,数据都是响应式的。
solt, useSlots
// 子组件
<template>
<slot name="footer" :scope="state" />
</template> <script setup>
import { useSlots, reactive } from 'vue'
const state = reactive({ name: 'caicai', age: 18 })
const slots = useSlots() // 可以拿到所有的插槽,视具体情况而用
</script>
// 父组件
<template>
<child>
<template #footer="{ scope }">
<p>姓名:{{ scope.name }},年龄{{ scope.age }}</p>
</template>
</child>
</template>
<script setup>
// 引入子组件
import child from './child.vue'
</script>