缘由
最近项目处于自我驱动状态,那么些东西就尽可能的往规范,长远,学习的角度去写。
项目使用element-plus,vue3
一、高阶组件和高阶函数
1、高阶函数的特点
把一个函数当另一个函数的参数
传参固定,结果就是固定的
说白了就是传入一个函数,返回一个函数。包装的函数可以进行修饰,但是所有参数都传递给传入的函数。我的理解是装饰器语法,和回调函数的深度使用。
2、高阶组件
高阶组件和高阶函数是同样类似的原理,react是最早提出,vue社区用的很少。但是从传入一个组件,返回一个组件的作用来说,我们也许曾经写过。
二、vue高阶组件我们需要解决的最大问题
其实就是参数透传的问题,我要得到所有的参数,并且是不需要经过props定义的,所有组件上的参数,然后全部丢给封装的组件。然后我们再把组件返回出去,进行使用。
vue2语法下,我们有一个$attres可以得到所有参数,同理,vue3也有这个参数,基于这方面的高阶函数,已经有比较多的文章实现过了
然后vue3的组合式api方式,就很有意思了。
官方文档下说明,steup可以返回一个对象,和一个函数
返回对象则将数据融入上下文,函数的话是一个jsx语法。相关地址
接下来jsx语法是我们需要使用的方式。
三、先丢出来注意事项
1、vue3不需要引入新的babel,直接就支持jsx语法使用
2、JSX无法和scoped配合使用,大家心里要有数
3、iscustomelement参数是没有用的,想要在templete里面无警告则需要在vue.config.js中增加
chainWebpack: config => {
// 参考https://github.com/vuejs/vue-cli/issues/5610
config.module
.rule('vue')
.use('vue-loader')
.loader(require.resolve('vue-loader-v16'))
.tap(options => {
options.compilerOptions = {
...(options.compilerOptions || {}),
isCustomElement: tag => /^jsx-custom/.test(tag), // 定义全局解析,用于jsx
}
return options
})
},
4、如果在使用jsx下不应该使用v-slot应该用v-solts
5、v-solts没有生效的原因,应该是你的组件不是一个未定义的组件,也是上面iscustomelement参数配合解决的原因。
你不应该放在一个类似div,span的有效组件上面,要放在类似的
<a v-slot/>
这里a组件未定义过
6、虽然基本实现了高阶组件,但是我们配合ref使用的时候发现父组件无法操作到子组件的函数,原因是子组件只管渲染去了,不管对象的处理。那么我们需要给当前高阶组件绑定可供外部操作的函数
const self = getCurrentInstance()
// 为当前组件对象绑定外部可操作的函数
self.proxy.handColse = onColse
7、jsx下面的具名插槽使用,无奈没有找到官方文档,试了很久也不行。最后采用h渲染函数方式
{
// 因为是插槽所以直接用渲染函数影响不大,并且v-slots方法不知道怎么用具名插槽
[
h('default', ctx.slots.default()),
h('footer', ctx.slots.footer())
]
}
8、2020年12月17日更新,关于jsx也可以使用scoped css的方式
使用demo:
<script lang="tsx">
import { defineComponent, withScopeId, getCurrentInstance } from 'vue'
export default defineComponent({
setup(props, ctx) {
const instance = getCurrentInstance()
const scopeId = instance.type.__scopeId
const withId = withScopeId(scopeId)
return withId(() => <div class="ceshi">fdsafas</div>)
},
})
</script>
<style scoped lang="scss">
.ceshi {
width: 100px;
height: 100px;
background: red;
}
</style>
四、代码呈现
这里是对el-drawer组件进行了高阶组件封装
<script lang="jsx">
/*
jsx使用手册
https://github.com/vuejs/jsx-next/blob/dev/packages/babel-plugin-jsx/README-zh_CN.md
*/
import { ref, getCurrentInstance, h } from 'vue'
export default {
name: 'CopyDrawer',
emits: [],
setup(props, ctx) {
const self = getCurrentInstance()
let doneF = null
const root = ref('root')
function handleClose(done) {
doneF = done
}
function onColse() {
root.value.handleClose() // 调用内部关闭方法给handleClose参数传递done事件
doneF()
}
// 为当前组件对象绑定外部可操作的函数
self.proxy.handColse = onColse
const slots = {
default: () => <div>A</div>,
foo: () => <span>B</span>
};
return () => (
<el-drawer ref={root} {...ctx.attrs} withHeader={false} before-close={handleClose}>
<div className="copy-el-drawer">
{
// 自定义顶部标题
}
<div className="title">
<span>{ctx.attrs.title}</span>
<i className="icon el-icon-close" onClick={onColse} />
</div>
{
// 这里会报错,不用管,等vue官方修复isCustomElement问题
// <jsx-custom v-slots="ctx.slots" />
}
{
// 因为是插槽所以直接用渲染函数影响不大,并且v-slots方法不知道怎么用具名插槽
[
h('default', ctx.slots.default()),
h('footer', ctx.slots.footer())
]
}
</div>
</el-drawer>
)
},
}
</script>
<style lang="scss">
.copy-el-drawer {
width: 100%;
height: 100%;
position: relative;
.title {
width: 100%;
font-size: $h4-16;
height: 40px;
display: flex;
padding: 20px;
align-items: center;
color: $info;
justify-content: space-between;
}
.icon {
font-size: $h2-24;
&:hover {
color: $font-color;
}
}
}
</style>
使用
<copy-drawer
:close-on-press-escape="false"
size="60%"
title="新增话术"
v-model="drawer"
direction="rtl"
@close="onClose"
destroy-on-close
ref="addFrom"
>
<div class="insert-drawer">
<el-button @click="ceshi">测试</el-button>
<el-form :model="formData" :rules="rules"></el-form>
</div>
<template #footer>
<span>524551</span>
</template>
</copy-drawer>
五、关于element-plus的bug
上次发了一篇element的bug记录。这次不多,官方修复很快。这里我使用了抽屉组件,所以又发现两个。
1、modal参数不能设置为false,否则就无法使用了,已经提交issues
2、官方文档中的closeDrawer参数通过ref等方式是获取不到的,已经提交issues
个人处理方式是操作handleClose,具体看官方的更新情况
代码
const root = ref('root')
root.value.handleClose() // 调用内部关闭方法给handleClose参数传递done事件
六、踩坑基本完毕
今天从想要实现这个功能到现在,一个业务代码也没有写。但也有所收获。而且基于此实现了高阶组件之后,再用templete方式就很简单了。
还有这里的jsx使用方式和ant-desgin还有element-plus不太一样。我是纯组合式api,但是他们用了render。各有千秋。
完毕!