开始之前
vue3 虽然已经出了快1年了,也没怎么好好用过,就是刚出来的时候尝了个鲜。然后紧跟着就一直在鼓捣 .net 的东西,前阵子搞了搞 docker,最近终于有时间正儿八经的用一下 vue3 了。哪里上手呢,之前有人问我有没有升级 jz-gantt 组件的计划。正好赶着这段时间连学习带升级,把它搞了。
什么是 jz-gantt
具体的甘特组件,之前写过 文章,同时也可以到 GitHub 查看源码,放了两个版本。
文档我也更新了,在 这里。
欢迎有需要的朋友们点个 star,也欢迎提 issue。
升级小记
vue3 的升级确实带来了更多的变化,Composition API 提供了更多的灵活性,所以在写组件时,我选择将之前的 jsx 方式更换为普通的 vue 文件方式。
不用 vuex 也是必须的,毕竟是内部组件,根本用不到组件,之前组件通信用的是 provide
和 inject
,本来这样的方式在 vue3 中问题也不大,但是既然有更灵活的 Composition API,为什么不用用呢。
全局变量
Composition API 的灵活,我觉得不仅仅是可以复用,它打破了原来但组件中变量的界限,现在可以在一个组件中给全局变量赋值,然后在其他组件中使用,这样的方式简直爽的不要不要的。
当然这种方式一定要注意加载的先后顺序。
比如将根组件 ref 一下:
<template>
<div ref="rootRef" class="gt-root">
</div>
</template>
平时我们需要在组件内部定义一个变量关联该根组件,然后可以通过 provide 方法将其注册到全局。
现在我们可以用钩子函数,轻松解决。在 composables
文件夹下定义一个 useRootRef.ts
的文件:
import { ref } from "vue";
const rootRef = ref<HTMLDivElement>();
export default function () {
return { rootRef };
}
将 rootRef
放在函数外面,它就是一个全局变量,当有其他调用该方法的地方出现时,依旧用的是同一个变量,简单多了。而且我们甚至可以在初始的地方使用一个 init 方法,使用的地方套一个 readonly
,避免二次修改:
import { ref, readonly } from "vue";
const rootRef = ref<HTMLDivElement>();
export function useInitRootRef() {
return { rootRef };
}
export default function () {
return { rootRef: readonly(rootRef) };
}
这样的方式既整洁,又方便。现在哪里需要使用 rootRef
,只需要:
const { rootRef } = useRootRef();
即可。
数据代理
得益于 vue3 大量使用 Proxy,我这组件内部的 Proxy 代理数据的方法直接就可以抛弃了。而且也不存在内部检测不到的情况,相比之前确实方便多了。
但是刚开始的时候,确实对于 ref
、reactive
、computed
等这些响应式方法,用起来有一些别扭。经常会有莫名其妙的 undefined
的 bug,一查就能发现不是少写了 value 就是多写了 value。现在用多了也就慢慢少了。
对于在 setup
中的内容,一定要套上这些方法,不然数据完全不是响应式的。
对于 props
,不能直接解构它们,需要逐一通过 toRefs
或者 toRef
来解构,要不然就需要每次都用 props.xxx
来使用。
这些使用上的变化都是为了能有更好的响应式,用多了发现它们用起来还是比较统一的,一些希望的固定值也可以得到比较好的保留。
还有一个就是 reactive
和 computed
的区别。对于某些场景,如样式的传递:
<div :style="rootStyle"></div>
const { bgColor } = toRefs(props);
const rootStyle = computed(() => {
return {
background-color: bgColor.value
}
});
// const rootStyle = reactive({background-color: bgColor.value});
像这样的数据传递,最好用 computed
返回,不要用 reactive
,它可能不会响应。
生命周期
有了 setup
,基本可以将大部分内容搬到这里面来。生命周期也不例外,我用的比较多的是 onMounted
和 onUpdated
。里面的写法大同小异,但是有趣的是,它可以写多个,这也是很灵活的一面,可以将一个逻辑片段放在一个 composables
文件中,然后用过引用的方式调用,这样代码既简洁,可读性还提高了。
watch
watch
方法比之前有意思多了,它可以接收一个列表,同时监控多个属性,这样的方式对于我这个组件来说,根组件大量样式属性,变化时需要调用同一方法,简直爽的不要不要的。
watch(() => [props.bodyStyle, props.color, ...], () => saveStyle())
setup
vue3 最大的变化,在 vue 文件中,可能就体现在这个 setup
函数中了。前面也说了,它可以包含大部分内容。这个函数本身处于生命周期的 beforeCreate
和 created
之间,所以它也是有缺陷的,比如调用组件中的属性,没有 this 等等。
但可以通过参数解决,vue3 在该周期内将组件实例保存到一个变量中,我们可以通过 getCurrentInstance
方法获取。
这里有个问题,我本地开发没问题,但是打包为生产环境之后,该方法永远获取到的是 undefined
,看了看好像大家都在说不要在生产环境下使用该方法,遂我放弃使用它。
那么有什么方法呢?
setup(props, {slots, attrs, emit}) {}
这些应该可以解决大部分问题。
对于 setup 中返回的内容过多,可以通过语法糖 <script lang="ts" setup>
轻松解决。
那么随之而来的问题是,参数又该如何定义?
const props = defineProps(sliderProps);
const emit = defineEmits<{
(e: "row-click", data: any): void;
}>();
const slots = useSlots();
const attrs = useAttrs();
上面的 props
与 emit
定义方式类似,我分别用了参数与类型声明定义,两种方式均可。而 slots
和 attrs
可以获取到想要的插槽内容以及传入内容。
不过使用 setup
语法糖需要注意,不要在模板中写过多不管内容,因为它会默认导出所有定义的内容。推荐将不同的逻辑块封装在不同的 Composition API 中,用到调用即可。
最后
其实 vue3 更多的是思想上的变化,我觉得使用 Composition API 更贴近 react,同时它也保留了 vue2 的用法,叫 Option API。两种方式没有谁更好,只有谁更适合。
文章评论
大佬能发一下ts版的示例吗
@tyty 源码在 github,你看一下
大佬为什么expand-all这个属性无效