最近在做vue3+plan+tsx+element-plus的项目,tsx在我这里是用来做组件的,以往的开发方式是sfc,就是vue页面的写法
<template>
</template>
<script>
</script>
<style scoped="scoped" lang="scss">
</style>
自从vue3出来之后,舍弃了很多方式,比如支持tsx,额,不解释了,我也是差不多知道意思,更详情的问度娘,我这里讲的就是主要tsx开发的时候碰到的一些问题还有vue3开发的一点总结,后续还会继续补充
我的大部分tsx知识都是在
https://zhuanlan.zhihu.com/p/563203507这篇文章里面学到的,有兴趣的朋友可以去看看
-
- tsx支持element-plus里el-table自定义模板<template #default="scope"></template>
这个也是困扰了我很久,在百度上找不到的,可能现在受众少吧,我也是从别人的文章里的蛛丝马迹里面找出来的,实现方式如下
这是用sfc方式的写法
<el-table-column label="Date" width="180">
<template #default="scope">
//在这里实现自定义内容,参数是 scope,比如要自定义显示图片就可以<img :src="scope.row.image"/>
</template>
</el-table-column>
用tsx方式的写法
<el-table-column v-slots={{
default: (props: Record<'scope', string>) =>
//在这里实现自定义内容,参数是props,比如要自定义显示图片就可以<img :src="props.row.image"/>
}}
/>
解释:v-slots必须(我也不知道有没有其他办法,就是写在外面就不行)写在标签里面,然后用双花括号{{}}写具体代码,
default 对应的就是 <template #default="scope"> 里的default
props对应的就是<template #default="scope">里的scope,是一个回调的数组
-
- 支持在<template #default="scope"></template>再声明一个具名插槽并且传这个参数回去
这是我在element-plus的基础上封装的一个组件,所以如果需要自定义内容的话还是要把数据给传下去,那就要用到作用域插槽了,不说了,上写法
<el-table-column
v-slots={{
default: (props: Record<'scope', string>) =>
slots.image && slots.image({row:props.row.image})
}}
/>
主要实现是slots.image && slots.image({row:props.row.image})
这相当于sfc的<slot name="image" :row="props.row.image"/>
这是作用域插槽
-
- v-slots写多个元素,比如写几个el-button时还是要用<>包围
v-slots={{
default: (prop: Record<'scope', string>) =>
<>
<el-button size="small">
更新
</el-button>
<el-button type="primary" size="small">
查看
</el-button>
<el-button type="danger" size="small" >
删除
</el-button>
</>
}}
-
- 那用tsx编写的组件如何导出去并注册(全局+局部)?
首先要注册成为组件
import { defineComponent,reactive,ref,watch } from "vue";
export default defineComponent({
name: "WayContent",
props,
setup(props, {slots }) {
return ()=>()
}
)}
在vue中将defineComponent拿出
声明name
在setup里面的return写组件页面,就是写<div></el-button>这些
setup第一个参数props是父组件传递过来的值,第二个参数是context,{slots}是从context取出slots属性,{slots,emit}就是从context里面取出emit属性
在defineComponent里面的props参数是描述父组件传递过来的值,像vue2那样写,不过没有默认值了,得自己做判断或者用计算属性
全局注册
在main.js或main.ts中
import WayContent from "@/components/WayContent";
const app = createApp(App);
app.component("WayContent", WayContent);
局部注册
在需要得地方
import WayContent from "@/components/WayContent";
然后可以直接使用<WayContent/>,不用再到components里面去注册了,不过或许是要在setup语法糖下才能这样写吧,没试过,就是这样
<script setup lang="ts">
import WayContent from "@/components/WayContent";
</scipt>
-
import { ref,watch } from "vue";
const test = ref()
watch(() => test.value, (newVal) => {
console.log(newVal)
}, {
deep: true,
immediate: true
})
deep,imediate这些配置的作用应该和vue2没啥区别,就是写法都改成组合式api写的了,监听的变量必须要加上.value
-
vue3还可以跟vue2一样写mixin,就是vue3向下包容的,只要没明确舍弃的vue2有的vue3都可以写,不过vue3不推荐vue2那种写法了,都是倾向于组合式,mixin也是如此
context.ts
import { ref,reactive,onMounted,watch } from "vue";
export const context =(gets) => {
// 数据列表
const list = ref()
// 搜索条件
const condition = ref()
// 分页配置
const pagination = ref({
pageNo: 1,
pageSize: 10,
total: 0,
layout: "total, sizes, prev, pager, next, jumper",
handleCurrentChange: (p)=>{
gets2(p,pagination.value.pageSize)
},
handleSizeChange: (p)=>{
gets2(pagination.value.pageNo,p)
}
})
// 获取数据
const gets2 = (pageNo,pageSize) =>{
let c = {};
c.pageNo = pageNo;
c.pageSize = pageSize;
if(condition.value){
for(let k in condition.value){
if(condition.value[k]){
c[k] = condition.value[k]
}
}
}
gets(c).then(res => {
list.value = res.data.data;
pagination.value.total = res.data.total;
})
}
//监听搜索条件的改变,当搜索条件改变了就触发请求获取数据
watch(() => condition.value, (newVal) => {
if(newVal){
gets2(1,pagination.value.pageSize)
}
}, {
deep: true,
immediate: true
})
//页面加载完成后请求获取数据
onMounted(() => {
gets2(1,pagination.value.pageSize)
})
return {
list,pagination,gets2,condition
}
}
在需要它的地方
<script setup lang="ts">
import { ref,onMounted,reactive } from "vue";
import { gets } from "@/api/article.ts"
import { context } from "@/mixin/context.ts"
const {list, pagination,condition} = context(gets);
</script>
如上所示
在context.ts里面可以写watch,声明响应式变量,然后引用的比如list,在引用的页面改变它context.ts里面的list也会跟着改变,然后也可以传递一个参数,比如我是传了一个方法gets进去,也都有用,他不再像是vue2那样
import context from "@/mixin/context"
export default {
mixin: [context]
}
这种写法了
-
input.tsx
import { defineComponent,reactive,ref,watch } from "vue";
const props = {
placeholder: { //提示内容
type:String,
},
modelValue: {
type: [String,Number],
required: true,
}
}
export default defineComponent({
name: "WayInput",
props,
emits: ['update:modelValue'],
setup(props, {emit,slots}) {
const modelValue = ref(props.modelValue)
watch(() => modelValue.value, (newVal) => {
emit('update:modelValue', newVal)
}, {
deep: true,
immediate: true
})
watch(() => props.modelValue, (newVal) => {
modelValue.value = newVal
}, {
deep: true,
immediate: true
})
return ()=>(
<div wid="wayInput">
<el-input clearable v-model={modelValue.value} placeholder={props.placeholder || "请输入内容"} />
</div>
)
}
})
使用它
<WayInput v-model="test"/>
import WayInput from "./input.tsx"
import { reactive } from "vue";
const test = reactive()
在input.tsx中主要是以下代码
const props = {
modelValue: {
type: [String,Number], //用来接受父组件的v-model
required: true,
}
}
const modelValue = ref(props.modelValue) //声明一个modelValue并把父组件的v-model值赋予给他
//监听modelValue的变化,把值传递给父组件的v-model
watch(() => modelValue.value, (newVal) => {
emit('update:modelValue', newVal)
}, {
deep: true,
immediate: true
})
//监听父组件的v-model变化,把值传递给modelValue
watch(() => props.modelValue, (newVal) => {
modelValue.value = newVal
}, {
deep: true,
immediate: true
})
//把modelValue.value绑定给el-input
<el-input clearable v-model={modelValue.value} placeholder={props.placeholder || "请输入内容"} />
扩展,如今vue3支持多v-model了,还能写函数,这些就不再阐述了,有兴趣的小伙伴可以去百度了解一下
-
另外一些细节的,比如ref,reactive的区别,还有setup语法糖等等,网上一搜一大把,我就不说了,还有,我在写一个可以传几个参数列表,增删查改配置的参数列表,就可以实现普通后台管理的搜索,展示,新增,修改,删除的组件,有兴趣的小伙伴可以收藏我的博客https://www.wayblogs.com/随时欢迎访问来关注我的进度