对Vue也算比较熟悉,熟悉的原因很简单,三大前端框架,只有Vue的官方文档中文最全。刚进入工作时用过Vue2,还是内嵌到HTML里面的形式,low是low了点,好处就是当时用(学)起来有没有什么负担,工作里的实践,闲暇中阅读文档,周末大致看看视频,到也不难理解,主要官方的中文文档很详细了,只要你耐得住性子去看,去学。

Vue3出来也挺长时间了,在Vue3的文档还没有中文翻译的时候我就去看了,当时对Vue这东西理解尚浅,加上一些琐事,没时间去学,学不动了,真的学不动了。不怎么忙的时间里,我于是静下心来了解了一下。

说升级,对于使用者来说,无外乎废弃了一些API,写法上多了些选择,至于性能提升,代码优化,这些东西我们并不太需要知道。Vue之所以保留旧版的写法,完全是为了照顾旧版本,让使用者就算打算升级也可以更平滑的升级。这对于前端框架来说非常难得,此处点名@angular, 完全不管旧版本的死活。

尤大官方也说,不建议使用旧版的语法,推荐使用组合式API,那就试试去理解学习组合式API。对于会Vue2的来说理解最重要的,升级的底层核心我们触碰不到,也不需要触碰到,理解Vue2的痛点,以及缺陷,再去理解Vue3新语法解决了什么问题,这个是目前主要需要做的事。

Vue3简单的说就是把Vue2中分的零零碎碎的代码放在了一起,首先我们回想Vue2中有哪几块代码,data (响应式的属性) methods (基本的代码逻辑)计算属性,还有一个监听属性,生命周期先不考虑,尤大认为这种写法逻辑太过分散,一个数据可能需要要经过三到四个不同的地方,这时候我们就需要在不同的块中找代码,这个一个块里上百行代码是非常常见的。需要跳来跳去的看代码,容易造成关注点混乱。

组合式写法就是为了解决这种情况,而且组合式的方向对了,没写过React,据说是很像,简单的一点组合式代码提示会更友好,明眼人一看,起码给IDE的计算负担减低不少。不管是JS还是TS。那么学习组合式需要注意什么呢,不要把它想的很难,什么简直都需要重学,实际上就是换了一种写法,定义data变成了ref以及reactive,加上一些奇奇怪怪的语法糖,定义methods,直接定义函数就行了,监听属性,计算属性,都有对应的写法,生命周期随便再看看。Vue3内嵌Html的用法就这些。组件化时加了两个东西一个props以及自定义事件( emit), 这两个东西没怎么变,至少基于JS来说。对于TS这两个变化略多,可以说一种写法一种定义或者声明方式,主要还是这两个东西历史包袱过重。

Vue3据我目前所了解的,算上旧版本的写法,这种写法不配拥有姓名,所以只能称之为旧版本写法,算上旧版本的写法有四种了。很多啊,我没有兴趣去研究那么多,基于目前来看,<script setup >这种写法应该是最优雅的,尤大官方微博也说了,Vue3 + TS+ setup + Volar = 真香。下面主要代码介绍这种写法。

这种写法不能使用HTML来写,只能使用单文件方式来写,主要看写法

组件HelloWord

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<h3 v-if="visible">是否显示 {{ visible }} sumcom = {{ sumcom }}</h3>
<button type="button" @click="test">点击抛出事件</button>
</div>
</template>

<script setup lang="ts">
import { watch, ref, computed, toRefs } from 'vue';

// props,只声明类型时的写法
const props = defineProps<{
msg: string
visible: boolean
}>()

// props有默认值时的写法
/* interface Props {
msg?: string
labels?: string[]
}
const props = withDefaults(defineProps<Props>(), {
msg: 'hello',
labels: () => ['one', 'two']
}) */
// end props有默认值时的写法


// 自定义事件,这种写法代码提示十分友好,且在使用组件时的代码提示也十分友好
const emit = defineEmits<{
(e: 'change', id: number): void
(e: 'update', value: string): void
}>()
// 定义一个普普通通方法
const test = () => {
// 抛出自定义事件,这里的代码提示十分友好,
emit('update', '从前从前')
}

// 响应式
const sum = ref(200)
// 计算属性
const sumcom = computed((): number => { return sum.value * 4 })

// 监听
watch(sum, (newVal, oldVal) => {
console.log(newVal + "----" + oldVal);
})

defineExpose({
// 导出组件内的东西,给组件外部访问,父组件需要访问子组件内数据时,需要导出,也可以导出函数,
sum
})
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>

组件APP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<template>
<img alt="Vue logo" src="./assets/logo.png" />
<HelloWorld msg="欢迎来到你的VUe+TS项目" :visible="flag" @update="upd" ref="model" />
</template>

<script setup lang="ts">
import { ref } from '@vue/reactivity';
import { onMounted } from '@vue/runtime-core';
import HelloWorld from './components/HelloWorld.vue';

const flag = ref(false)

const upd = (value: string): void => {
console.log(value)
flag.value = !flag.value
}
// 暂时只能这样,实际上不报错,但是TS语法会报错,只能这样定义了。
interface expHello {
sum: number
}
// 对应子组件
const model = ref<InstanceType<typeof HelloWorld> & expHello>()

onMounted(() => {
// 访问子组件里的属性
console.log(model.value?.sum);

})

</script>

<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>

封面

封面
封面