在Vue3中使用JSX

    0

什么是JSX

JSX,即JavaScript XML,是一种允许开发者在JavaScript代码中编写与HTML类似的结构的语法扩展,并能够通过诸如Babel这样的编译器转换为createElement(React中)或createVNode(Vue中)的函数调用,从而创建虚拟DOM。JSX结合了JavaScript的逻辑处理能力与标记语言的声明式编写模式,使得你可以在单个文件中处理UI和业务逻辑,实现组件的组织和复用。

React和Vue中的对比

JSX虽然在React和Vue中的使用方式非常相似,但还是存在一些差别:

  • 在React中,JSX转换后使用React.createElementreact/jsx-runtime ,而Vue 3使用createVNode
  • Vue中的JSX需要特定的Babel插件支持,例如@vue/babel-plugin-jsx
  • 在React中,CSS类使用className属性绑定,而for属性在JSX中需要替换为htmlFor。而在Vue中,则保持classfor的标准HTML属性使用。

入门基础语法

1. 创建基础元素

jsx
const element = <h1>Hello, world!</h1>;

2. 嵌入表达式

jsx
const name = 'c.chen'; const element = <h1>Hello, {name}</h1>; const avatar = <img src={user.avatarUrl}></img>;

3. 条件渲染

jsx
const avatar = user.avatarUrl ? <img src={user.avatarUrl} /> : null; const avatar = user.avatarUrl && <img src={user.avatarUrl} />;

4. 使用样式

行内样式
jsx
const 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'], };
javascript
if (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" }

基础用法

  1. 文件名以.tsx结尾
  2. 自定义组件都是大驼峰,和HTML区分开
  3. 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 中,推荐以直接引入的形式使用组件。

jsx
import { defineComponent } from 'vue'; import Counter from './Counter'; export default defineComponent({ name: 'App', setup() { return () => ( <div> <h1>Hello Vue 3 with TSX!</h1> <Counter /> </div> ); }, });

高阶用法

JSX高阶用法

Fragment

类似于template,在JSX中不能使用template,会直接不渲染。

jsx
const 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()} />

动态组件

jsx
const 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> </> ) } })

插槽

jsx
const 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> ); }, };

指令

jsx
const 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

jsx
import { 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

jsx
<div class={css({ color: 'var(--n-text-secondary)' })} > css in js style </div>

Css Module

  1. 根目录创建类型定义文件
typescript
//typings.d.ts declare module '*.scss' { const content: { [className: string]: string }; export default content; }
  1. 在tsconfig.json文件中,确保你的"include"部分包含了SCSS文件的路径
json
{ "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.scss"], }
jsx
import 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

  1. webpack...没成功
  2. vite项目可以使用

F&Q

怎么页面中出现莫名奇妙的 0

jsx
const list = [] const app = list.length && list.map(item => )

附录

评论区

共有评论 0

暂无评论