vue3代码编写
团队内的vue3已经升级一年,在这一年中vue也在不停的更新,为了最大化组合式api带来的优势,便需要合理规范代码的编写方式…
1.从vue2到vue3
-
vue2组件采用配置式API,导致同样的功能块分散,并和其他的功能块混合。我们希望统一功能块的代码可以放在(封装)一起,在空间上增加可读性:
-
总的来说,就是将一个大型的vue2代码块变成许多小型的vue2块:
假设一个页面中有三个功能模块,分别是A,B,C,那么一下就是写法上的不同
1// vue2
2
3export default {
4 data() {
5 return {
6 dataA: dataA,
7 dataB: dataB,
8 dataC: dataC
9 }
10 },
11 computed: {
12 computedA() {
13 return dataA
14 },
15 computedB() {
16 return dataB
17 },
18 computedC() {
19 return dataC
20 }
21 },
22 mounted() {
23 this.methodA()
24 this.methodB()
25 this.methodC()
26 },
27 methods: {
28 async methodA() {
29 console.log(computedA)
30 },
31 async methodB() {
32 console.log(computedB)
33 },
34 async methodC() {
35 console.log(computedC)
36 }
37 }
38}
39
40// vue3
41
42export default {
43 setup() {
44 // A
45 const dataA = ref()
46 const computedA = computed(() => {
47 return dataA.value
48 })
49 const methodA = () => {
50 console.log(computedA.value)
51 }
52 onMounted(() => {
53 methodA()
54 })
55
56 // B
57 const dataB = ref()
58 const computedB = computed(() => {
59 return dataB.value
60 })
61 const methodB = () => {
62 console.log(computedB.value)
63 }
64 onMounted(() => {
65 methodB()
66 })
67
68
69 // C
70 const dataC = ref()
71 const computedC = computed(() => {
72 return dataC.value
73 })
74 const methodC = () => {
75 console.log(computedC.value)
76 }
77 onMounted(() => {
78 methodC()
79 })
80
81 return {
82 dataA: dataA,
83 computedA,
84 methodA,
85 dataB: dataB,
86 computedB,
87 methodB,
88 dataC: dataC
89 computedC,
90 methodC,
91 }
92 }
93}
可以看到原来只能在vue2中配置一次的属性(computed等),setup中可以多次调用,从而实现了同一功能代码的整合,并且你还可以将某些可复用的携带vueApi的代码封装:
1// C.js
2export default () => {
3 const dataC = ref()
4 const computedC = computed(() => {
5 return dataC.value
6 })
7 const methodC = () => {
8 console.log(computedC.value)
9 }
10 onMounted(() => {
11 methodC()
12 })
13
14 return {
15 dataC: dataC
16 computedC,
17 methodC
18 }
19}
20
21// 调用
22import C from 'c.js'
23
24const c = C()
2. 代码编写优化
如上,我们已经不需要上下滚动编辑器以调试代码了,但是有两个问题希望可以解决。一个是写法上更加灵活了也会导致混乱的问题,如果没有遵循一定的规范,则代码会变成面向过程似的结构,一篇到底;第二个是每个需要在模板中使用的变量与方法都需要收到return,相对麻烦
- 借助reactive和toRefs,重新规整代码
1export default {
2 setup() {
3 // A
4 const A = reactive({
5 dataA: dataA,
6 computedA: computed(() => {
7 return dataA.value
8 }),
9 methodA() {
10 console.log(computedA.value)
11 }
12 })
13 onMounted(() => {
14 A.methodA()
15 })
16
17 // B
18 const B = reactive({
19 dataB: dataB,
20 computedB: computed(() => {
21 return dataB.value
22 }),
23 methodB() {
24 console.log(computedB.value)
25 }
26 })
27 onMounted(() => {
28 B.methodB()
29 })
30
31 return {
32 ...toRefs(A),
33 ...toRefs(B)
34 }
35 }
36}
这样写的好处在于,同一块的功能被包裹在一个reactive中相对独立(当然不同模块之间可以相互调用),代码结构更加的清晰,而且只需return整个模块,模块中新增的变量会自动导出在模板中可用
3. setup语法糖的发展
1. css变量
vue提出在单文件中使用当前实例的变量
1<script>
2export default {
3 setup () {
4 return {
5 opacity: 0,
6 font: {
7 weight: 100
8 }
9 }
10 }
11}
12</script>
13
14<style>
15div {
16 opacity: v-bind(opacity);
17 font-weight: v-bind('font.weight');
18}
19</style>
在此语法糖之前,想通过变量修改样式都需要通过在模板中直接绑定样式对象或者以修改类名的方式来实现,现在可以直接使用css样式变量了,不得不说是个很奈斯的语法糖
2. <script setup>和ref文档
vue提出在单文件组件中引入<script setup>的类型,可以自动将所有顶级变量声明暴露给模板使用,同时可以消除ref.value的写法
1<script setup>
2// 引入的 Foo 组件可以直接在 template 里使用了!
3import Foo from './Foo.vue'
4// 就像在普通的 setup() 中一样编写代码,无须return变量
5ref: a = 1
6// 该变量可以像普通变量那样使用
7console.log(a++)
8// 想要获取到原本的变量的话需要在变量前面加一个💲符号
9console.log($count.value)
10const fn = () => {}
11<script>
至此许多具有争议的论点被广大开发者提出:
从写法上,确实实使得代码更加的简洁,减少了组件声明以及手动暴露变量的代码,这也是编者比较认可的,但是确实带来不少的问题
-
根据文档中提出,部分情况还是只能使用普通的<script>标签,如:
- 部分选项(inheritAttrs)无法声明
- 声明只需要执行一次的代码
- 不支持使用render函数
-
无法像普通的<script>标签那样只暴露部分变量,暴露所有变量必将导致生成的代码变大,而暴露变量的问题,上面我们已经用toRefs解决大部分问题,相比之下,反而会产生下面的问题:
1<script setup>
2const data = reactive({
3 a: 1,
4 b: 2
5})
6// 因为语法糖自动暴露data,而模板中使用a变量的时候需要写data.a,多了一层
7
8// 如果想在模板中直接使用a变量,则需要解构:
9const { a, b } = ...toRefs(data)
10// 这样又变成,每新增一个变量都要手动解构,与最开始的return无异,而且还要写a.value
11
12// 如果想规避这个问题就不能使用reactive,则每个基础数据都必须使用ref
13const a = ref(1)
14ref: b = 2
15// 这样一来,又分散了代码,产生了ref.value的问题
16<script>
-
针对ref的语法糖,已经不在是javascript了,原来的vue正因为是逐渐式的框架带来的便利,甚至于所写的代码可以直接在浏览器中运行(只要引入vue.js),而这样的创新语法(至少对于vue来说),则只能存在于编译之前,一时难以接受,并且遭到大家的反对。
-
有一就有二,如果维持这样的创新,那么将来大家写的就不再是js,而是被vue‘挟持’的js了。况且这些语法糖只能在setup语法糖中使用,不利于封装
总之大家的看法各异,在代码编写上必将产生不同的选择,编者也担心vue生态或许会分散,从而解决问题的效率变低
3. 新的ref文档
因为以上提出的各种原因,以及社区开发者的反对意见,旧的ref语法糖最终还是被废弃,而新的ref语法糖重新进入实验
1<script setup>
2// 在setup语法糖中引入了全局变量$ref,直接使用可以不写.value
3let a = $ref(1)
4// a可以直接使用不加.value
5a++
6</script>
此语法糖虽然遵守了js的语法,但是还是只能在setup语法糖中才能使用,而且不支持在单文件组件外使用,没有让这种语法生效在Vue的特定环境之外,所有编者还是有些失落
让vue成为开发者编写js的中间平台,对开发者来说始终会产生一些心理负担,【改变js的编写方式】与【改变编写的内容】之间的区别,还是让开发者小心翼翼的考虑,争取不会有一天被vue‘套住’。
只是可怜那波使用了第一波语法糖的开发人员,所幸团队内还没有在情况明朗之前贸然使用
4. ref语法糖改进文档
新的ref语法糖过多久就又被改进了,改进版主要是把全局变量改为只有$这俩变量了
1<script setup>
2import { ref } from 'vue'
3
4const a = $(ref(1))
5// a可以直接使用不加.value
6a++
7// $$可以得到a的ref变量
8console.log($$(a))
9
10// 也就是说
11const aRef = ref(1)
12const a = $(aRef)
13// a可以直接使用不加.value
14a++
15console.log(aRef === $$(a))
16</script>
17
晕,目前为止,所有ref语法糖都只能看看,不敢在项目里用