背景:作为使用三年 react.js 的搬运工,目前正在积极学习 vue.js 语法,而在学习的过程中,总是喜欢和 react.js 进行对比,这里也不是说谁好谁坏,而是怀有他有我也有,他没我还有的思想去学习,去总结。
- React18
- Vue3
最近看 vue 官网的时候,发现了 vue 中也使用 JSX/TSX,就比较的感兴趣。为什么呢?
因为在工作的三年过程中,都是使用的 React 进行开发的,而 React 就是使用的 JSX 语法进行开发的,对其基本语法也是比较熟悉的。若以后机会使用 vue 开发,那么 JSX 也不失为一种选择。
我记得最开始的,react jsx 的语法写习惯了,当写 vue 代码时,总会犯一个错误:
{}
和{{}}
的写法。vue 要使用
{{}}
, react 要使用{}
,不停的转变,最后造成的原因就是 react 那边喜欢写错,vue 这边也喜欢写错。 幸好的是,最近慢慢熟悉过来了
这里 react 中使用 JSX 使用语法就不用多做介绍了,写过 react 进行开发的码友都应该熟悉。
- map 进行循环
- 三目运算 或者 逻辑运算 进行判断
- className 代替 class
- 事件前面加上 on,采用驼峰式命名
- 变量使用 {} 包裹
常用的就这些,直接开始 Vue 中的 JSX 旅程吧。
Vue 系列:JSX
先来个简单的介绍吧(我也不知道是哪里抄的,哈哈哈)
JSX(JavaScript XML)是一种用于在 javaScript 中编写类似 XML 的语法扩展。
JSX 允许开发者在 JavaScript 代码中直接编写类似 HTML 的标记语法,以声明式地描述 UI 组件的结构和交互。通过使用 JSX,可以将 HTML 结构、组件逻辑和数据绑定等内容组合在一起,更加直观地描述页面结构和交互。
安装插件并使用
create-vue 和 Vue Cli 都有预置的 JSX 语法支持,但是 vite 创建的项目是需要安装插件的
虽然下面说的都是 JSX, 但是创建的项目是 ts,都是采用 tsx 的书写方式。
使用 vue + vite
创建的项目是不支持 JSX,需要安装一个插件@vitejs/plugin-vue-JSX
。
bash
pnpm add @vitejs/plugin-vue-JSX -D
ts
// vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import vueJSX from "@vitejs/plugin-vue-JSX";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), vueJSX()],
});
这样配置之后,就可以在项目中,使用 JSX 语法了。
使用 JSX 的两种方式
使用 JSX 文件存在两种形式,但都是使用 setup
函数的返回值来实现的。请确保返回的是一个函数而不是一个值!setup() 函数在每个组件中只会被调用一次,而返回的渲染函数将会被调用多次。
第一种方式 : 采用.vue
文件后缀名。
- 修改 script 标签上的 lang 属性,从 ts 改为 tsx。
- setup 函数返回一个 render 函数,该函数的返回值是 JSX 语法
ts
<script lang="tsx">
import { defineComponent, ref } from "vue";
export default defineComponent({
setup() {
const msg = ref("copyer");
return () => (
<div>{msg.value}</div>
);
},
});
</script>
第二种方式 :采用 .tsx
文件后缀名
也是采用的 setup 返回 render 函数,该函数的返回值是 JSX 语法
ts
import { defineComponent, ref } from "vue";
export default defineComponent({
setup() {
const msg = ref("copyer");
return () => <div>{msg.value}</div>;
},
});
具体采用哪种形式,就自己爱好了(其实 JSX 写法,本来在 vue 中很少使用)。
JSX VS template
template:
- 更加贴近实际的 html。
- 由于其确定的语法,更容易对模板做静态分析。这使得 Vue 的模板编译器能够应用许多编译时优化来提升虚拟 DOM 的性能表现。
JSX:
- 处理高度动态渲染逻辑的可重用组件中使用。
JSX 的具体细节
官网已经明确的指出了,Vue 的 JSX 转换方式与 React 中 JSX 的转换方式不同,所以 JSX 的写法肯定会存在差异。
jsx 写法形式
选项语法
ts
export default defineComponent({
name: "test",
props: {
name: {
type: String,
required: true,
},
},
// 拿取 props context: {emit, expose, slots, attrs }
setup(props, context) {
return () => {
return <div>{props.name}</div>;
};
},
});
函数语法(vue3.3+)
ts
import { defineComponent, ref } from "vue";
interface Props {
name: string;
}
const TestTsx = defineComponent<Props>(
(props, context) => {
const msg = ref<string>("copyer");
return () => (
<div>
<div>{msg.value}</div>
</div>
);
},
{
// 需要手动声明运行时的 props,不然拿不到 props
props: ["name"],
}
);
export default TestTsx;
不能使用 className,而是直接使用 class
ts
setup() {
const msg = ref("copyer");
return () => {
return <div class="red">{msg.value}</div>;
};
},
动态 class 和 style
ts
setup() {
const show = ref(false);
return () => (
<div class="content">
<div class={{ color: show.value }}>321321</div>
</div>
);
},
style 也是一样。
这种形式跟 react 采用 classnames 库的写法一致。
jsx 里面不能自动解包
就是针对 ref 变量,需要 .value
,在上面案例即可验证。
组件插槽使用
在 jsx 中使用插槽,都是以函数的形式调用
ts
const testslot = defineComponent({
setup(props, { slots }) {
return () => (
<div>
{/*默认插槽*/}
<div>{slots.default && slots.default()}</div>
{/*具名插槽*/}
<div>{slots.footer && slots.footer()}</div>
</div>
);
},
});
组件插槽传递
ts
setup(props, context) {
return () => {
return (
<div>
<TestJsxVue
v-slots={{
default: () => <span>我是默认插槽</span>,
footer: () => <span>我是具名插槽</span>,
}}
/>
</div>
);
};
},
这里的案例跟上面是对应的
注意:这里是使用的 v-slots
而不是 v-slot
作用域插槽就不用多说了,函数传递参数和函数接受参数。
事件修饰符
对于 .passive
、.capture
和 .once
事件修饰符,可以使用驼峰写法将他们拼接在事件名后面(on+事件名+修饰符
)。例如:
ts
setup(props, context) {
const btn = () => {
console.log("321======>", 321);
};
return () => {
return (
<div>
{/* 这里功能是好的,但是会飘红,不存在属性 onClickOnce */}
<button onClickOnce={btn}>点击</button>
</div>
);
};
},
对于事件和按键修饰符,可以使用 withModifiers 函数,vue 内部提供的。
支持部分的指令
- v-text
- v-html
- v-show
ts
setup(props, context) {
const text = ref("copyer");
const show = ref(true);
const btn = () => {
console.log(321);
};
return () => {
return (
<div>
{props.name}
<button
onClickOnce={btn}
v-text={text.value}
v-show={show.value}
></button>
</div>
);
};
},
v-model 在组件上的使用
v-model 在组件上使用,默认名称就是 modelValue,也可以自定义名称,采用 v-model:title="xxx"
的方式。
默认值
html
<!-- 定义 -->
<script lang="tsx">
import { defineComponent, ref } from "vue";
export default defineComponent({
props: {
{/* v-model 的默认值 */}
modelValue: {
type: String,
},
},
emits: ["update:modelValue"],
setup(props, { emit }) {
const btn = () => {
emit("update:modelValue", "321");
};
return () => (
<div>
<button onClick={btn}>{props.modelValue}</button>
</div>
);
},
});
</script>
ts
// 组件中使用
export default defineComponent({
setup() {
const text = ref("copyer");
return () => {
return (
<div>
<TestJsxVue v-model={text.value} />
</div>
);
};
},
});
取名为 title
html
<!-- 定义 -->
<script lang="tsx">
import { defineComponent, ref } from "vue";
export default defineComponent({
props: {
{/* v-model 的取名 */}
title: {
type: String,
},
},
emits: ["update:title"],
setup(props, { emit }) {
const btn = () => {
emit("update:title", "321");
};
return () => (
<div>
<button onClick={btn}>{props.title}</button>
</div>
);
},
});
</script>
ts
// 使用
export default defineComponent({
setup() {
const text = ref("copyer");
return () => {
return (
<div>
{/* 取名为 title,是一个数组,[绑定的值,名称] */}
<TestJsxVue v-model={[text.value, 'title']} />
</div>
)
}
}
})
存在其他的新 jsx 语法,以后慢慢收集。
总结
习惯 react 的 jsx 语法, 在 vue 写 jsx 应该也不在话下,当然 vue 还有一些个性的 jsx 语法,就需要在开发中平时积累。
上面存在错误的话,告诉我一下,因为 vue 我也不太会,我改正。