Skip to content
文章目录

vue使用tsx

Cannot find module ‘./index.module.scss‘ or its corresponding type declarations.ts

项目创建 typed-css.d.ts 文件

ts
declare module '*.module.css' {
  const classes: { readonly [key: string]: string }
  export default classes
}

declare module '*.module.sass' {
  const classes: { readonly [key: string]: string }
  export default classes
}

declare module '*.module.scss' {
  const classes: { readonly [key: string]: string }
  export default classes
}

将其配置到tsconfig.json

json
{
  "compilerOptions": {
    "module": "ESNext",
    "moduleResolution": "Node",
    "target": "ESNext",
    "jsx": "preserve", //就是这个参数
    "experimentalDecorators": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true
  },
  "exclude": ["node_modules", "**/node_modules/*"],
  "include": ["src", "docs", "typed-css.d.ts"]
}

关闭报错的文件,重新打开或重启 vscode

踩坑 VSCode 报错 ReferenceError: React is not defined

安装并配置 @vitejs/plugin-vue-jsx 插件

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(), // 添加这一句
  ],
})

配置 tsconfig.json "jsx": "preserve"

json
{
  "compilerOptions": {
    "module": "ESNext",
    "moduleResolution": "Node",
    "target": "ESNext",
    "jsx": "preserve", //就是这个参数
    "experimentalDecorators": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true
  },
  "exclude": ["node_modules", "**/node_modules/*"]
}

关闭报错的文件,重新打开或重启 vscode

vue tsx 中使用 ref/reactive 变量/事件和样式

说明
注意ref变量,在html中需要`.value`才能显示

INFO

使用 css 模块化功能: 将 css/less/scss/sass 等文件命名为 xxx.module.scss/xxx.module.less/xxx.module.css 然后再在 tsx/jsx 文件中引入使用即可,参考上面的例子

props 与 v-model 使用

生命周期钩子

Sub01.tsx

tsx
import { defineComponent, onBeforeUnmount, onMounted, onUpdated, ref, toRefs } from 'vue'

export default defineComponent({
  props: {
    count: {
      type: Number,
      required: true,
    },
  },
  setup(props) {
    const { count } = toRefs(props)
    const mountedStatus = ref<string>('onMounted未执行')
    onMounted(() => {
      setTimeout(() => {
        mountedStatus.value = '子组件onMounted'
      }, 5000)
    })
    onBeforeUnmount(() => {
      alert('子组件卸载')
    })
    return () => (
      <>
        <p>子组件count:{count.value}</p>
        <div>{mountedStatus.value}</div>
      </>
    )
  },
})

slot 使用

对 slot 做了如下处理

  • 给 slot dom 多包了一层
  • 给 slot dom 添加了一个额外的 click 事件
tsx
import { defineComponent, onMounted } from 'vue'

export default defineComponent({
  setup(props, ctx) {
    // console.log(ctx)
    // console.log(ctx.slots.default && ctx.slots.default())
    //@ts-ignore
    const vNode = ctx.slots.default()[0] as VNode
    // 此时的vNode还只是vNode,并没有在浏览器中渲染完毕,所以vNode.el为null
    // console.log(vNode.el)
    // 给slot节点多包一层
    const wrapperSlot = <h3>{vNode}</h3>
    onMounted(() => {
      // 此时的vNode已经在浏览器中渲染完毕,所以vNode.el为实际的dom对象了
      const dom = vNode.el as HTMLElement
      dom.addEventListener('click', () => {
        alert('tsx click')
      })
    })
    // function tsxOnClick($event) {
    //   console.log('tsxOnClick', $event)
    // }
    // let oldClickCall = vNode.props.onClick
    // /*
    // 这种方式可以直接在vNode还没渲染成真实的dom节点的时候,就将事件添加上去,
    // 但如果vNode代指的dom本来就通过vue绑定了对应的事件的话,就有可能被覆盖。
    // 比如下面的例子:如果事先已经绑定了一个click事件,这里再直接绑定一个click,那就会将原来的clikc事件覆盖
    // 所以最好的方式,还是通过dom.addEventListener方式绑定
    // */
    // vNode.props.onClick = $event => {
    //   if (oldClickCall) {
    //     oldClickCall()
    //   }
    //   tsxOnClick($event)
    // }
    return () => (
      <>
        <div>这是子组件</div>
        {wrapperSlot}
      </>
    )
  },
})

WARNING

注意这里获取 slot 的 dom 对象的方式有缺陷,必须是 slot 原本的 dom 有 ref 属性这种方式才能获取到。

比如:上面例子的 slot 的原始 dom 是下面这个,因为有设置ref="spanDomRef"这个,所以 tsx 子组件中,才能在 mounted 回调中获取到对应 dom 对象,如果没有设置 ref,那么得到的结果依然为 null

html
<span ref="spanDomRef" @click="onClick" @mouseenter="onMouseEnter1(), onMouseEnter2(11)">
  我是通过默认插槽填充的内容
</span>

最稳妥的获取插槽 dom 方式详见:vue 中如何获取插槽的 dom 对象#jsx-tsx-语法中

使用第三方组件

使用 emit 自定义事件

相关资料

前端工程化~强大的 glob 语法

DevUI 中 VUE 的 TSX 函数式组件实践

再聊 Vue 的 TSX 函数式组件

VUE2 进阶至 VUE3 七(TSX & 深入 v-model)

Vite + Vue3 使用 TSX/JSX