前言
不少前端小伙伴问过我,说业务代码写了2、3年了,应该如何再进一步?村长的回答中大多数会提到两个关键点:加强工程化实践能力和底层原理源码知识。工程化能力对于小伙伴们在工作中解决问题有直接影响,如果你想成为小组内的核心程序员,拿更高的职位和薪资,能够给大家搭架子,解决问题,重难点突破是非常关键的几个考察点。实践中,我们如何刻意提升自己这方面的能力,我觉得给公司搭建一套组件库就很好。在这个过程中大家需要解决不少工程问题,比如:
- 如何构建一个敏捷的开发、发布环境
- 如何实现组件库文档系统
- 引入jsx/tsx
- 组件的设计与编写
- 代码质量:eslint、单测等
- 风格化、国际化等
知识储备
本次我要带大家编写一个Tree组件,会用到JSX,因此我们提前做一些知识储备:
- 配置Vite支持JSX
- Vue3中的JSX:jsx-next语法学习
Vite中引入JSX
本次Tree组件实现要用到JSX支持,所以小伙伴们有必要先了解如何在Vite项目中引入JSX。
我们可以通过一个叫做@vitejs/plugin-vue-jsx插件实现,
首先安装它:
yarn add @vitejs/plugin-vue-jsx -D
配置一下该插件,
vite.config.ts
:
// vite.config.js
import vueJsx from '@vitejs/plugin-vue-jsx'
export default {
plugins: [
vueJsx({
// options are passed on to @vue/babel-plugin-jsx
})
]
}
测试一下,
components/Tree.tsx
export default () => <div>tree</div>
如何定义一个组件
在TSX文件中定义组件有几种常用方式:
- 函数式组件:最简单的方式,你可以把它理解为
函数,但是不同之处它可以直接返回JSXsetup
export default (props, ctx) => <div>tree</div>
-
:传递defineComponent
选项,也就是Options API,这种缺点是要访问render
this
export default defineComponent({
render() {
return <div>tree</div>
}
})
- defineComponent:传递
选项,利用Composition API,避免setup
,是推荐的形式this
export default defineComponent({
setup(props, ctx) {
return () => <div>tree</div>
}
})
Vue3中JSX/TSX特殊语法
如果有过react的开发经验,可以发现除了vue中独有的几个新概念:
slot
、
directive
、
emit
等以外,其他都是相同的,因此这里仅关注这些特殊部分:
指令
指令使用需要我们转换思维,有几种不同情况:
- 常用指令v-model,v-show跟以前用法类似,但也要注意后面是
,不是{}
""
<input type="text" v-model={this.counter} />
- v-if使用条件语句或三元表达式代替
<div>{ condition ? <span>A</span> : <span>B</span> }</div>
- v-for使用map代替
import { defineComponent, ref } from "vue";
const App = defineComponent({
setup(){
const list = ref<string[]>([])
return () => {
list.value.map((data,index) => <p key={index}>{data}</p>)
}
}
});
插槽
JSX中想要实现Vue中的插槽写法也有很大不同,主要利用一个叫做
v-slots
的指令来实现:
export default defineComponent({
setup() {
return () => (
<Child
v-slots={{
prefix: () => <i>prefix</i>, // 具名插槽
suffix: (props: Record<"name", string>) => <span>{props.name}</span>, // props可作插槽作用域的作用
}}
>
默认插槽内容
</Child>
);
},
});
const Child = defineComponent({
setup(props, { slots }) {
return () => (
<>
默认插槽: {slots.default && slots.default()}
<br />
具名插槽: {slots.prefix && slots.prefix()}
<br />
作用域插槽:{slots.suffix && slots.suffix({ name: "suffix" })}
</>
);
},
});
emit
defineComponent({
emits: ["click"],
setup(props ,{ emit }) {
return () => (
<button onClick={() => {emit("click")}}>点我触发emit</button>
);
},
});