什么是JSX
JSX,即JavaScript XML,是一种允许开发者在JavaScript代码中编写与HTML类似的结构的语法扩展,并能够通过诸如Babel这样的编译器转换为createElement
(React中)或createVNode
(Vue中)的函数调用,从而创建虚拟DOM。JSX结合了JavaScript的逻辑处理能力与标记语言的声明式编写模式,使得你可以在单个文件中处理UI和业务逻辑,实现组件的组织和复用。
React和Vue中的对比
JSX虽然在React和Vue中的使用方式非常相似,但还是存在一些差别:
- 在React中,JSX转换后使用
React.createElement
或react/jsx-runtime
,而Vue 3使用createVNode
。 - Vue中的JSX需要特定的Babel插件支持,例如
@vue/babel-plugin-jsx
。 - 在React中,CSS类使用
className
属性绑定,而for
属性在JSX中需要替换为htmlFor
。而在Vue中,则保持class
和for
的标准HTML属性使用。
入门基础语法
1. 创建基础元素
jsxconst element = <h1>Hello, world!</h1>;
2. 嵌入表达式
jsxconst name = 'c.chen'; const element = <h1>Hello, {name}</h1>; const avatar = <img src={user.avatarUrl}></img>;
3. 条件渲染
jsxconst avatar = user.avatarUrl ? <img src={user.avatarUrl} /> : null; const avatar = user.avatarUrl && <img src={user.avatarUrl} />;
4. 使用样式
行内样式
jsxconst img1 = <img style={{width:"200px"}}></img>; const customStyle = { width: "200px" } const img2 = <img style={customStyle}></img>;
类绑定和外部样式文件
jsx// css module import styles from './app.module.css'; const imgElement = <img class={styles.myImage} />; const imgClass = <img class="my-image"></img>;
在Vue3中如何上手
1. Babel配置 @vue/babel-plugin-jsx
webpack
javascript//babel.config.js module.exports = { presets: ['@vue/cli-plugin-babel/preset'], };
javascriptif (vueVersion === 2) { presets.push([require('@vue/babel-preset-jsx'), jsxOptions]) } else if (vueVersion === 3) { plugins.push([require('@vue/babel-plugin-jsx'), jsxOptions]) }
Vite
javascript//vite.config.ts import vueJsx from '@vitejs/plugin-vue-jsx'; const config = defineConfig({ plugins: [ vue(), vueJsx(), ] }
2. tsconfig.json
json//tscongfig.json { "jsx": "preserve", "jsxImportSource": "vue" }
基础用法
- 文件名以
.tsx
结尾 - 自定义组件都是大驼峰,和HTML区分开
- Vue渲染函数基础用法
无状态组件(函数式组件)
jsx// UI = fn(state) function MyComponent(props, { slots, emit, attrs }) { return <NButton>{props.message}</NButton> } const App = () => <div>111</div>;
有状态组件
jsx<template> <div> <slot></slot> <p>Count: {{ count }}</p> <button ref="buttonRef" @click="increment">Increment</button> <NInput v-model="baseInfo.description" /> </div> </template> <script lang="ts" setup> import { ref, defineProps, defineEmits, PropType } from 'vue'; import { NInput } from '@nio-fe/lego'; const props = defineProps({ modelValue: String as PropType<'large' | 'small'> }); const emits = defineEmits(['update:modelValue']); const count = ref(0); const baseInfo = ref({ description: '' }); const buttonRef = ref<HTMLButtonElement>(); // Define methods const increment = () => { count.value++; }; const updateDescription = (value: string) => { baseInfo.value.description = value; emits('update:modelValue', value); }; const externalMethod = () => console.log('External method called'); defineExpose({ externalMethod }); </script>
引用组件
在 JSX 中,推荐以直接引入的形式使用组件。
jsximport { defineComponent } from 'vue'; import Counter from './Counter'; export default defineComponent({ name: 'App', setup() { return () => ( <div> <h1>Hello Vue 3 with TSX!</h1> <Counter /> </div> ); }, });
高阶用法
Fragment
类似于template
,在JSX中不能使用template
,会直接不渲染。
jsxconst App = () => ( name === 'cc' && <> <span>I'm</span> <span>Fragment</span> </> );
v-model修饰符
jsx<NInput v-model={[inputValue.value, ['trim']]} /> <NInput modelValue={inputValue.value.trim()} onUpdate:modelValue={newVal=>inputValue.value = newVal.trim()} />
动态组件
jsxconst ComponentA = defineComponent({ setup(){ return ()=> <div>ComponentA</div> } }) const ComponentB = defineComponent({ setup(){ return ()=> <div>ComponentB</div> } }) const ColumnDiff = defineComponent({ setup() { const components = { 'component-a': <ComponentA />, 'component-b': <ComponentB />, }; const componentName = 'component-a'; const ComponentToRender = components[componentName]; return () => ( <> <div class={'container mx-auto'}> --1-- {ComponentToRender} --2-- {ComponentToRender ? h(ComponentToRender) : null} --3-- <ComponentToRender /> </div> </> ) } })
插槽
jsxconst A = (props, { slots }) => ( <> <h1>{slots.default ? slots.default() : 'foo'}</h1> <h2>{slots.bar?.()}</h2> </> ); const App = { setup() { const slots = { bar: () => <span>B</span>, }; return () => ( <A v-slots={slots}> <div>A</div> </A> ); }, };
指令
jsxconst app = createApp(); app.directive('highlight', { mounted(el, binding) { el.style.backgroundColor = binding.value; }, }); app.directive('tooltip', NVTooltip) app.directive('popconfirm', NVPopconfirm) app.directive('loading', NVLoading) app.component('my-component', { setup() { return () => ( <div v-highlight="'yellow'"> This will be highlighted in yellow. </div> ); }, }); app.mount('#app');
透传props
jsximport { defineComponent, h } from 'vue'; // 引入子组件 import ChildComponent from './ChildComponent.vue'; export default defineComponent({ name: 'ParentComponent', props:{ a:String, }, setup(props, { attrs, slots, emit }) { // 所有的父 props 都会以 props 参数的形式接收 // attrs 包含了非 props 的属性,例如 class, style, id 等 return () => ( <ChildComponent {...props} {...attrs}> {slots.default ? slots.default() : ''} </ChildComponent> ); }, });
搭配Lego使用
使用按需引入,Lego官方文档
样式处理
Css In Js
- Emotion 官方文档
jsx<div class={css({ color: 'var(--n-text-secondary)' })} > css in js style </div>
Css Module
- 根目录创建类型定义文件
typescript//typings.d.ts declare module '*.scss' { const content: { [className: string]: string }; export default content; }
- 在tsconfig.json文件中,确保你的"include"部分包含了SCSS文件的路径
json{ "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.scss"], }
jsximport styles from './demo.module.scss' .header{ height: 80px; &-left{ color: var(--n-text-primary); } } <div class={styles.header}> <div class={styles.headerLeft}> name </div> </div>
Tailwind Css
- webpack...没成功
- vite项目可以使用
F&Q
怎么页面中出现莫名奇妙的 0
jsxconst list = [] const app = list.length && list.map(item => )