template 和 jsx 用法对比
整体结构
- jsx 类似vue3中的setup钩子函数?
import { defineComponent, reactive, ref } from 'vue';
export default defineComponent({
props: {},
setup: (props, {}) => {
return () => {
return <></>;
};
},
});
或者具名组件
import { defineComponent, reactive, ref } from "vue";
export const DownloadButton = defineComponent({
props: {},
setup: (props, { slots }) => {
return () => {
return <></>;
};
},
});
import { defineComponent, reactive, ref } from "vue";
export const Test = defineComponent((props, { expose }) => {
return () => {
return <></>;
};
});
Test.props = {
value: String,
loading: {
type: Boolean,
default: false,
},
};
- vue (.vue文件和原生html结构类似,vue3.2支持)
<script setup>
import { reactive, ref } from "vue";
</script>
<template>
<div></div>
</template>
<style scoped></style>
使用jsx需要进行的配置
首先,devDependencies 中下载 @vitejs/plugin-vue-jsx 插件
"devDependencies": {
"@vitejs/plugin-vue": "^4.2.3",
"@vitejs/plugin-vue-jsx": "^3.0.1**",//**下载这个插件
"vite": "^4.4.6"
}
其次,vite.config.js 中引入这个插件,放到 plugins 中
import { fileURLToPath, URL } from "node:url";
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()],
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
});
变量渲染
- jsx 属性不加冒号,驼峰命名,单花括号进行变量渲染
<SearchTable formColumns={searchOptions.value} onFinish={search} formState={formState} />
- vue 动态属性前面需要加冒号 双花括号进行变量渲染
<ItemList v-for="(item, index) in state.itemList" :itemData="item" :key="index" >{{item.name}}</ItemList>
循环
- jsx map循环
import { computed, defineComponent, reactive, ref } from "vue";
export default defineComponent({
props: {},
setup: (props, {}) => {
const arr = [1, 2, 3, 4];
return () => {
return (
<div>
{arr.map((item) => {
return <span>{item}</span>;
})}
</div>
或者
<div>
{arr.map((item) => (
<span>{item}</span>
))}
</div>
);
};
},
});
- vue v-for 指令
<script setup>
import { reactive, ref, computed } from "vue";
const arr = [4, 2, 3, 4];
</script>
<template>
<div>
<span v-for="item in arr">{{ item }}</span>
</div>
</template>
if/else
- jsx js逻辑运算符 与或
import { computed, defineComponent, reactive, ref } from "vue";
export default defineComponent({
props: {},
setup: (props, {}) => {
const show = false;
return () => {
return <div>{show && <span>666</span> || <span>888</span>}</div>;
};
},
});
- vue v-if/v-else 指令
<script setup>
import { reactive, ref, computed } from "vue";
const show = true;
</script>
<template>
<div>
<span v-if="show">666</span>
<span v-else>888</span>
</div>
</template>
事件写法
- jsx 采用驼峰类似onClick表示函数监听
import { defineComponent, reactive, ref } from "vue";
export default defineComponent({
props: {},
setup: (props, {}) => {
const test = ref(1);
const change = (e, params) => {
test.value = 4;
console.log(e);
console.log("params", params);
};
const handle = (e) => {
console.log(e);
};
return () => {
return (
<div>
{/* 有传参的情况,需要再次声明一个函数,例如传入record */}
<div
onClick={(e) => {
change(e, 666);
}}
>
xxx页面
</div>
{/* 无传参情况,一个回调函数搞定 */}
<div
onClick={(e) => {
console.log(e);
}}
>
yyy页面
</div>
<div onClick={handle}>yyy页面</div>
<div>{test.value}</div>
</div>
);
};
},
});
- vue 采用@加事件名,后面的逻辑可跟jsx保持一致或者直接执行某些逻辑(内联事件处理器-通常用于简单场景)(jsx不支持内联事件处理器,必须包裹在函数里面执行)
<script setup>
import { reactive, ref } from "vue";
const test = ref(1);
const change = (params) => {
console.log("param", params);
};
</script>
<template>
<div
@click="
() => {
test = 4;
}
"
或者
@click="test = 4" // jsx 不支持
或者
@click="change(4)" // jsx 不支持,jsx必须包裹在箭头函数里面再执行具体某个传参函数
>
home页面
</div>
<div>{{ test }}</div>
</template>
<style scoped></style>
空标签的使用,组件中只有一个父级元素
- jsx 组件中必须只有一个根元素
export default defineComponent({
props: {},
setup: (props, {}) => {
const test = ref(1);
const change = () => {
test.value = 4;
};
return () => {
return (
<div>
<div onClick={change}>about页面</div>
<div>{test.value}</div>
</div>
或者采用以下空的闭合标签
<>
<div onClick={change}>about页面</div>
<div>{test.value}</div>
</>
);
};
},
});
- vue 支持多个根元素
<template>
<div>home页面</div>
<div>{{ test }}</div>
</template>
computed / ref 在模版中的处理
- jsx 模版中渲染需要.value
export default defineComponent({
props: {},
setup: (props, {}) => {
const test = ref(666);
const abc = computed(() => {
return "999";
});
return () => {
return (
<>
{test.value} {abc.value}
</>
);
};
},
});
- vue 模版中渲染不需要.value
<script setup>
import { reactive, ref, computed } from "vue";
const num = ref(1);
const test = computed(() => {
return "999";
});
</script>
<template>
<div>num:{{ num }}</div>
<div>
{{ test }}
</div>
</template>
computed 写法
- jsx 可以在两个return中间直接写类似于计算属性返回的表达式,这样模版里面使用不需要.value。
import { computed, defineComponent, reactive, ref } from "vue";
export default defineComponent({
props: {},
setup: (props, {}) => {
const abc = ref(999);
const test = computed(() => {
return abc.value + 111;
});
return () => {
const test1 = abc.value + 111;
return (
<div>
<div>{test.value}</div>
<div>{test1}</div>
<div
onClick={() => {
abc.value = 666;
}}
>
点击
</div>
</div>
);
};
},
});
- vue
<script setup>
import { reactive, ref, computed } from "vue";
const test = computed(() => {
return "999";
});
</script>
<template>
<div>
{{ test }}
</div>
</template>
props传参
- jsx 通过props.来获取
import { computed, defineComponent, reactive, ref } from "vue";
export const Test = defineComponent({
props: {
name: String,
},
setup: (props, { emit }) => {
console.log("props.name", props.name);
return () => {
return <div>{props.name}</div>;
};
},
});
export default defineComponent({
props: {},
setup: (props, { emit }) => {
return () => {
return <Test name="tom" />;
};
},
});
-
vue 通过 defineProps 编译器宏显示,不需要导入; template中可以直接使用,或者props. 来使用 。setup中必须使用props. 来使用。
{{ props.title }}
{{ title }}
emit
- jsx
import { computed, defineComponent, reactive, ref } from "vue";
export default defineComponent({
props: {},
setup: (props, { emit }) => {
return () => {
return (
<div
onClick={() => {
emit("close");
}}
>
关闭
</div>
);
};
},
});
-
vue 通过 defineEmits 编译器宏显示,不需要导入
expose用法
- jsx
import { computed, defineComponent, reactive, ref } from "vue";
export default defineComponent({
props: {},
setup: (props, { expose }) => {
expose({
a: 666,
});
return () => {
return null;
};
},
});
-
vue 通过 defineExpose 编译器宏显示,不需要导入
attrs用法
- jsx setup中解构处理使用
import { computed, defineComponent, reactive, ref } from "vue";
export const Test = defineComponent({
props: {},
setup: (props, { attrs }) => {
return () => {
return (
<div>
{attrs.name}
{attrs.age}
</div>
);
};
},
});
export default defineComponent({
props: {},
setup: (props, {}) => {
const show = false;
return () => {
return <Test name="test" age="18" />;
};
},
});
- vue 使用useAttrs获取attrs
<script setup>
import { reactive, ref, useAttrs } from "vue";
const arrts = useAttrs();
</script>
<template>
<div>
{{ arrts.name }}
{{ arrts.age }}
</div>
</template>
<style scoped></style>
插槽slot
- jsx 利用v-slots插入插槽,利用slots.default等接受插槽内容
import { computed, defineComponent, reactive, ref } from "vue";
export const Test = defineComponent({
props: {
name: String,
},
setup: (props, { slots }) => {
return () => {
return (
<div>
{slots.default()}
<div>{slots.tom()}</div>
</div>
);
};
},
});
export default defineComponent({
props: {},
setup: (props, { emit }) => {
return () => {
return (
<Test
v-slots={{
default: () => {
return 888;
},
tom: () => {
return <h1>999</h1>;
},
}}
/>
);
};
},
});
- vue
匿名插槽
<script setup>
import { reactive, ref, computed } from "vue";
import Test from "./Test.vue";
</script>
<template>
<div>
<Test title="test1">我是插槽1</Test>
或者
<Test title="test1">
<template #default>
我是插槽1
</template>
</Test>
</div>
</template>
Test.vue实现如下
<script setup>
import { reactive, ref, useAttrs } from "vue";
</script>
<template>
<div>
<slot></slot>
或者
<slot name="default"></slot>
</div>
</template>
<style scoped></style>
具名插槽
<script setup>
import { reactive, ref, computed } from "vue";
import Test from "./Test.vue";
</script>
<template>
<div>
<Test title="test1">
<template #tom> 名字是tom插槽 </template>
</Test>
</div>
</template>
Test.vue实现如下
<script setup>
import { reactive, ref, useAttrs } from "vue";
</script>
<template>
<div>
<slot name="tom"></slot>
</div>
</template>
<style scoped></style>
ref
- jsx -常量传入ref
export default defineComponent({
props: {},
setup: (props, { emit }) => {
const domRef = ref();
onMounted(() => {
console.log(domRef.value);
});
return () => {
return <div ref={domRef}>666</div>;
};
},
});
- vue -domRef字符串传给ref
<script setup>
import { reactive, ref, computed, onMounted } from "vue";
const domRef = ref();
onMounted(() => {
console.log(domRef.value);
});
</script>
<template>
<div ref="domRef">888</div>
</template>
css写法
- jsx -样式需要使用驼峰来命名,比如fontSize
style写法
import { computed, defineComponent, reactive, ref } from "vue";
export default defineComponent({
props: {},
setup: (props, { emit }) => {
return () => {
return (
<div
style={{
color: "red",
}}
>
666
</div>
);
};
},
});
@emotion/css 写法, 需要 npm i @emotion/css -D
import { computed, defineComponent, reactive, ref } from "vue";
import { css } from "@emotion/css";
export default defineComponent({
props: {},
setup: (props, { emit }) => {
return () => {
return (
<div
class={css({
color: "pink",
})}
>
666
</div>
);
};
},
});
class或者className的写法 ,需要@emotion/css库支持
注意: Vue 的 JSX 转换方式与 React 中 JSX 的转换方式不同,可以使用 HTML attributes 比如 class 和 for 作为 props - 不需要使用 className 或 htmlFor
import { computed, defineComponent, reactive, ref } from "vue";
import { css } from "@emotion/css";
export default defineComponent({
props: {},
setup: (props, { emit }) => {
return () => {
return (
<div
class={css({
color: "pink",
".test": {
fontSize: 26,
},
".test1": {
textDecoration: "underline",
},
})}
>
666
<span className="test test1">888</span>
或者
<span class="test test1">888</span>
或者
<span className={'test1 test'}>888</span>
或者
<span class={'test1 test'}>888</span>
</div>
);
};
},
});
- vue
style写法
<script setup>
import { reactive, ref, computed } from "vue";
</script>
<template>
<div style="color: red;">666</div>
</template>
class 写法
<script setup>
import { reactive, ref, computed } from "vue";
</script>
<template>
<div class="addColor">666</div>
</template>
<style>
.addColor {
color: blue;
}
</style>
customRender中返回组件处理区别
- jsx 可以直接写dom或者引入组件
customRender: ({ record, value }) => {
return (
<span
class={css({
cursor: "pointer",
":hover": {
color: "var(--el-color-primary)",
},
})}
onClick={() => {
router.push({
name: "Archives-query-enterprise-detail",
query: {
title: record.fentName,
id: record.fentBusinessCode,
firstYear: record.freportYear,
current: 1,
routerName: "重点排放单位详情",
},
});
}}>
{value}
</span>
);
},
// 或者类似直接引入组件
import {Stack} from "@/rockontrol/component";
{
title: "操作",
customRender: ({ value }) => {
return (
<Stack
style={{
color: "var(--el-color-primary)",
cursor: "pointer",
justifyContent:'center'
}}
onClick={() => {
router.push({name:"month-report-detail"});
}}
>
查看
</Stack>
);
},
},
-
vue 需要利用h函数来进行处理
import { h } from 'vue'
import KeyEntJump from "./KeyEntJump.vue";
{
title: "重点排放单位",
dataIndex: "fentName",
fixed: "left",
width: 300,
customRender: ({ record }) => {
return h(KeyEntJump, {
title: record.fentName,
id: record.fentBusinessCode,
firstYear: record.freportYear.slice(0, -1),
});
}
},
单个jsx文件支持导出多组件
- jsx --- 一个jsx文件里面书写多个组件,组件可具名导出(两个组件内容关联紧密有共同导入内容,可以使用这种模式? 增加可读性,减少导入? ),也可供别的组件引用。
import { computed, defineComponent, reactive, ref } from "vue";
export const MyCom = defineComponent({
props: {},
setup: (props, {}) => {
return () => {
return <>888</>;
};
},
});
export default defineComponent({
props: {},
setup: (props, {}) => {
return () => {
return (
<div>
<MyCom />
</div>
);
};
},
});
当然,如果jsx文件中需要被路由直接引用,必须有默认的export default组件,以下行为不支持
import { computed, defineComponent, reactive, ref } from "vue";
export const MyCom = defineComponent({
props: {},
setup: (props, {}) => {
return () => {
return <>888</>;
};
},
});
export const MyCom1 = defineComponent({
props: {},
setup: (props, {}) => {
return () => {
return <>999</>;
};
},
});
-
vue
.vue 只支持一个匿名组件的导出,不能写多个组件。{{ test }}
jsx文件支持导出其他变量
- jsx 可以导出其他的函数方法等内容,比如以下在一个jsx文件中可方便共同使用AMapProvideKey常量,并且导出useAMap。
const AMapProvideKey = Symbol("amap-key");
export const useAMap = () => inject(AMapProvideKey);
export const AMapProvider = defineComponent({
props: {
key: String | undefined,
opts: Object,
loadOpts: Object,
},
setup: (props, context) => {
//todo:: 待优化
window._AMapSecurityConfig = {
securityJsCode: "b4db2d2dc121f0ffb398025c760295cb",
};
const divRef = ref();
const amap = shallowRef();
provide(AMapProvideKey, amap);
- vue .vue 文件只能匿名导出组件,其他内容无法导出,只能依靠.js 来处理
import { provide } from "vue";
const AnalysisStr = "analysis$";
export const analysisProvide = (data) => {
provide(AnalysisStr, data);
};
export const useAnalysisProvide = () => {
return inject(AnalysisStr);
};
<script setup>
import CarbonEmission from "./components/CarbonEmission";
import EnergyConsumption from "./components/EnergyConsumption";
import dayjs from "dayjs";
import { analysisProvide } from "@/views/analysis/ctx";
const state = reactive({
type: "1",
year: dayjs().year() + "",
componentName: null,
});
analysisProvide(state);
性能对比
官网描述--由于template其确定的语法,更容易对模板做静态分析。这使得 Vue 的模板编译器能够应用许多编译时优化来提升虚拟 DOM 的性能表现。
小结
.vue 优点:
1、熟悉的语法:模板语法基于 HTML,对于那些熟悉 HTML 的开发人员来说更加自然和易懂。
2、更简单的模板:模板语法不需要像 JSX 语法一样用 JavaScript 写模板,因此可以更加简单和易于维护。
3、更好的性能:由于 Vue3 的改进和优化,模板语法的性能比 Vue2 更好,尤其是在渲染大量静态内容时。
.vue 缺点:
1、不够灵活:模板语法只能使用指令和插值表达式来绑定数据和行为,相对来说不太灵活。
2、不够可复用:由于模板语法的结构比较固定,因此相对来说不够可复用。
JSX 的优点:
1、更加灵活:由于 JSX 是使用 JavaScript 编写的,因此可以更加灵活地操作数据和组件。
2、更加可复用:由于 JSX 可以在 JavaScript 中嵌套 HTML 标签和组件,因此可以更加轻松地实现组件的复用。
3、更加直观:相对于模板语法来说,JSX 更加直观和易于理解,特别是对于那些熟悉 JavaScript 的开发人员。
JSX 的缺点:
1、学习成本高:相对于模板语法来说,JSX 的学习成本更高,需要掌握更多的 JavaScript 语法和特性。
2、更容易出错:由于 JSX 是使用 JavaScript 编写的,因此更容易出现拼写错误、逻辑错误等问题。
总的来说,模板语法和 JSX 语法在 Vue3 中都有各自的优缺点和适用情况。模板语法简单易学,性能好,适合开发小型项目;而 JSX 语法灵活可复用,适合开发大型项目。开发人员应该根据实际情况灵活选择使用哪种方式,也可以在两种方式之间切换,以达到最佳的开发效果和用户体验。