Home

tsx开发的一些经验

# 风格

vue3.0拥有多种不同的开发风格,首先是.vue文件支持v2的选项式api,还有v3的新特性组合式api,这里不做介绍,tsx的开发风格也有两种,一是直接.tsx文件开发,二是.vue文件中用<script lang="tsx">,这两种tsx风格在具体编码时,也会有风格变化,比如是否使用render(){}函数,还是在setup(){}中直接return一个箭头函数来渲染dom。

# tsx格式文件

海豚调度系统全站都是用.tsx开发,后面会总结纯tsx开发的经验。 tsx文件开发vue组件需要重点了解以下:

  • defineComponent函数

为了让 TypeScript 正确地推导出组件选项内的类型。不加这个那么就不会有任何vue提示。

import { defineComponent } from 'vue'
const Content = defineComponent({
  name: 'DSContent',
  setup() {}
})
export default Content
  • 渲染Dom方式
  1. render(){}函数
import { defineComponent } from 'vue'
const Content = defineComponent({
  name: 'DSContent',
  setup() {
    const name = 111
    return {name} 
    //把render中需要的变量return出来,使用的时候需要加this,
    //响应式变量自动解构即使用时无需加value
  },
  render(){
     return (
      <div>{this.name}</div>
     )
  }
})
export default Content
  1. setup(){}中直接return
import { defineComponent } from 'vue'
const Content = defineComponent({
  name: 'DSContent',
  setup() {
    const name = 111
    return (
      <div>{name}</div>
     )
  },
 
})
export default Content
  1. 思考,这两种方式同时存在哪个优先级高?更推荐哪种?

测试证明,哪个写在前面哪个就先执行。

第一种直接return的方式,可少写代码,使用时也不用this,缺点是代码相对杂乱,第二种render函数的方式虽然多写了一些代码,但是有边界感,可以清晰的定位需要的对象及渲染部分。

<div id={dynamicId} onClick={this.handle}> hello, {userName} </div>
<div onClick={(event) => { this.handle(item) }}> 事件传参 </div>

# vue格式文件写tsx

在vue文件中通过<script lang="tsx">

<script lang="tsx">
import { defineComponent } from 'vue'
export default defineComponent({
  name: 'xxx',
  props: {},
  setup(props, { attrs, emit, slots, expose }) {
    ...
    return () => {
      return(
        <div/>
      )
    }
  },
  
})
</script>
<style lang="less" scoped>
:deep(.class){}
</style>

要说这种方式的优点,就是写css方便,纯tsx格式文件不能用style,只能用css-module 相对麻烦点。

其实这种也可以用render,甚至可以用template,当他们俩共存时,render优先级更高,当然这俩种形式不是主流,如果混乱使用可能被当成另类。

# 总结

开发中应该选择何种方式开发呢,首选单文件组件 (opens new window)的方式开发,复杂的组件可以用vue格式写tsx开发。当然也可以像某些系统一样,全站都有.tsx文件,这种更适合习惯tsx的程序员。

# 全站tsx的经验分享

规范性做法一个组件分三个文件

  1. index.tsx 参考 tsx格式文件-渲染方式- render(){}函数,这里是编写组件的文件
import { defineComponent, ref } from 'vue'
import { NLayout, NLayoutContent, NLayoutHeader, useMessage } from 'naive-ui'
import SideBar from './components/sidebar'
import { useDataList } from './use-dataList'
const Content = defineComponent({
  name: 'DSContent',
  setup() {
    const {
      state, //数据
      changeMenuOption, //方法
    } = useDataList()

    onMounted(() => {
      changeMenuOption(state) //根据业务页面加载完成执行
    })
    //把解构的数据和方法return给渲染使用
    return {
      ...toRefs(state),
      changeMenuOption,
    }
  },
  render() {
    return (
      <NLayout style='height: 100%'>
        <NLayoutHeader style='height:167px'>
        ....
        </NLayoutHeader>
        <NLayout has-sider position='absolute' style='top:167px'>
          {this.isShowSide && (
            <SideBar/>
          )}
          <NLayoutContent
            native-scrollbar={false}
            style='padding: 16px 22px'
            contentStyle={'height: 100%'}
          >
            <router-view key={this.$route.fullPath} />
          </NLayoutContent>
        </NLayout>
      </NLayout>
    )
  }
})
  1. index.module.css 模块化css 给tsx使用
import styles from './index.module.scss'
<div class={[styles.menu_box, styles['right-menu'],'tab-vertical']}></div>
.menu_box{
  overflow-x: auto;
  .right-menu {
    width:100%;
  }
}
:global(.tab-vertical){
  width:100%
}
   

index.module.scss 中的样式会自动渲染成带作用域选择器,html中{styles.xxx}也会渲染成带作用域的类名,这样就实现了scope的功能。

如果要覆写组件样式或者不希望带作用域,用:global

  1. use-datalist.ts

一些数据的定义,或一些方法可以抽出来放在这里,目的是让组件代码简洁,把功能全写在ts里,有点网页三剑客时代的感觉,js代码单独抽出来,tsx当作html,css-modele当作css。

分享ts里的书写思路: 定义一个reactive响应数据,定义一些方法如异步请求、业务操作等,最终导出给html使用

  const variables = reactive({
    columns: [],
    tableWidth: DefaultTableWidth,
    tableData: [],
    page: ref(1),
    pageSize: ref(10),
    searchVal: ref(null),
    totalPage: ref(1),
    showModalRef: ref(false),
    statusRef: ref(0),
    row: {},
    loadingRef: ref(false)
  })
  //异步请求
  const getTableData = (params: any) => {
    const { state } = useAsyncState(
      queryProjectListPaging(params).then((res: ProjectRes) => {
        variables.tableData = res.totalList as any
        variables.loadingRef = false
      }),
      {}
    )
    return state
  }
  //最终导出
  return {
    variables,
    getTableData
  }

使用

import { useTable } from './use-table'
const list = defineComponent({
  setup(){
    const { variables, getTableData, createColumns } = useTable()
    //可以定制一些业务方法,组织好参数,灵活运用吧
    const requestData = () => {
      getTableData({
        pageSize: variables.pageSize,
        pageNo: variables.page,
        searchVal: variables.searchVal
      })
    }

    return {
      ...toRefs(variables),
      requestData,
    }
  },
  render() {
    return (
       <NDataTable data={this.tableData}/>
    )
  }
})
export default list