More in js,纵享丝滑开发体验!javascript
在说 More in js
以前,想必你们对 React
的 All in js
都不陌生,各位看官莫被干扰,本文与 React
无关。More in js
为笔者开发过程当中的一些实例尝试,可能你们都有过相似的开发经历,但少有人思考这么作的价值。vue
讲到 Vue 的 SFC 组件结构,你们首先想到的必定是 template
script
style
三部曲。实际表现形式以下:java
<template>
...
</template>
<script> ... </script>
<style> ... </style>
复制代码
上面的代码就是 Vue
的 SFC
基础结构了,那这跟咱们的 More in js
又有啥关系呢?react
咱们一般在业务组件开发时,避免不了出现复杂布局状况,组件嵌套(一层又一层,层层不同),此时再加上业务逻辑的处理,代码行数轻轻松松破 1000+
。web
那么在维护或开发此类代码时,可能就是为了加一个属性致使咱们在 template
和 script
之间反复横跳,开发体验较差,也带来了额外的时间成本。(此时某位暴躁老哥已经开始砸键盘了!)api
上图是笔者开发中的一个画布组件,其代码行数不算样式部分也突破了 2000+
,对于正在修改业务逻辑的我忽然须要给视图组件加个属性,直接促使肾上腺素飙升!markdown
对于上述问题的解决方案是在业务组件封装时产生的灵感。app
一般 UI
组件库的基础组件并不能知足咱们复杂的业务需求时,会对齐进行二次封装,那么对于源组件的 props
和 events
咱们也是须要暴露给外界来下降上手成本,只针对该处的场景提供便利性,纵享丝滑体验。异步
例如:async
<template>
<Button class="async-button" ref="main" v-bind="mergeProps" :loading="loading" @click.prevent="onClick" > <slot></slot> </Button>
</template>
<script> import { omit, merge } from 'lodash' export default { name: 'AsyncButton', props: { /** * 使用原始 Button 事件 * @default false */ useRaw: { type: Boolean, default: false } }, data() { return { loading: false } }, computed: { mergeProps() { const defaultProps = { type: 'primary' } return omit(merge(defaultProps, this.$attrs), 'loading') } }, methods: { /** * 处理点击事件。 */ onClick(e) { // useRaw: true 使用原始单击事件,false 使用异步单击事件 if (this.useRaw) { this.$emit('click', e) } else { this.loading = true this.$emit('click', () => (this.loading = false), e) } } } } </script>
复制代码
上面代码是对 Button
组件作的一层简单封装,让使用者可以在点击时自动启动加载状态,在须要时经过回调函数来关闭加载状态。
对于源 Button
组件的 props
经过 Vue
提供的 v-bind
和 $attrs
实现属性穿透,那么反过来咱们思考下是否能够把该方式应用到业务组件开发中?
答案是能够的。
提出的 More in js
概念就是为了让 Vue
开发者可以更好地专一于业务逻辑部分的处理,配合 IDE
的定义跳转功能,能够相对的优化上述问题。
<template>
<SpecialTabs class="data-point-tabs" left-label="上传文件" right-label="已上传的文件列表"> <!-- upload start --> <template slot="left"> <SingleUpload ref="singleUpload" v-bind="singleUpload" /> </template> <!-- upload end --> <!-- table start --> <template slot="right"> <!-- table --> <Table class="file-table" v-bind="table" @hook:created="loadTableData" /> <!-- modal start --> <Modal v-bind="modal" v-model="modal.show" transfer> <!-- modal footer --> <template slot="footer"> <Button @click="toggleModal(false)">取消</Button> <Button v-bind="confirmBtn" @click="submitForm">肯定</Button> </template> <!-- edit form --> <EditForm ref="editFormRef" :key="`edit-form_${updateFormFlag}`" :info="table.singleSelected" @on-validated="handleValidated" @on-submitted="handleSubmitted" ></PointForm> </Modal> <!-- modal end --> </template> <!-- table end --> </SpecialTabs>
</template>
<script> import SpecialTabs from '@/components/SpecialTabs' import SingleUpload from '@/components/SingleUpload' import { dataPointColumns } from './entity/data-point' import DeleteMixin from '@/mixins/delete' import EditForm from './components/edit-form.vue' import { showErrorMessage } from '@/util/error' export default { name: 'DataPoint', mixins: [DeleteMixin], components: { SpecialTabs, SingleUpload, EditForm }, data() { return { singleUpload: { autoUpload: true, loading: false, uploadRequest: this.uploadRequest, beforeUpload: this.handleBeforeUpload }, table: { border: true, loading: false, disabledHover: true, columns: dataPointColumns.call(this), data: [], singleSelected: {}, actionOptions: [...] }, modal: { title: '配置表单', maskClosable: false, show: false }, confirmBtn: { type: 'primary', loading: false }, updateFormFlag: 0 } }, computed: { /** * DeleteMixin */ deleteOption() { return { deleteUrl: '/db_table/delete', method: 'delete', loading: true, params: (selection) => ({ table_name: selection[0].table_name }), afterDelete: (flag) => flag && this.loadTableData() } } }, methods: { ... } } </script>
复制代码
上述代码是对 More in js
的一次尝试,组件的属性对象显示声明在 data
中,经过 v-bind
来动态绑定到模板上,可以提高代码的阅读性,让开发者更加专一业务逻辑层,对于视图层只关注在布局便可。
配合 IDE
的定义跳转功能,可以轻松实现对视图层组件的属性控制及添加,减小代码横跳次数,提高开发效率。
在 Vue2
中因为 options api
的结构,咱们的组件开发一般在单文件中,不会对内部业务逻辑过于聚合,因此 More in js
方式只会解决单文件内部的代码横跳问题。
可是在 Vue3
的 hooks api
中,咱们能够经过抽离业务逻辑到 @use/xxx
后,让视图层与逻辑层的代码高度聚合,使用 More in js
能够更好的维护组件状态,不须要频繁的切换文件来更新属性信息。
// dp.vue
<template>
<SpecialTabs class="data-point-tabs" left-label="上传文件" right-label="已上传的文件列表"> <!-- upload start --> <template slot="left"> <SingleUpload ref="singleUploadRef" v-bind="singleUploadAttrs" /> </template> <!-- upload end --> <!-- table start --> <template slot="right"> <!-- table --> <Table class="file-table" v-bind="tableAttrs" @hook:created="loadTableData" /> <!-- modal start --> <Modal v-bind="modal" v-model="modal.show" transfer> <!-- modal footer --> <template slot="footer"> <Button @click="toggleModal(false)">取消</Button> <Button v-bind="confirmBtn" @click="submitForm">肯定</Button> </template> <!-- edit form --> <EditForm ref="editFormRef" :key="`edit-form_${updateFormFlag}`" :info="tableAttrs.singleSelected" @on-validated="handleValidated" @on-submitted="handleSubmitted" ></PointForm> </Modal> <!-- modal end --> </template> <!-- table end --> </SpecialTabs>
</template>
<script setup> ... const { singleUploadAttrs, ... } = useUpload() const { tableAttrs, ... } = useTable() </script>
// @use/upload.js
...
export default function useUpload() {
const singleUploadAttrs = reactive({
autoUpload: true,
loading: false,
uploadRequest: uploadRequest,
beforeUpload: handleBeforeUpload
})
function uploadRequest() {...}
function handleBeforeUpload() {...}
...
return { singleUploadAttrs, ... }
}
// @use/table.js
...
export default function useTable() {
const tableAttrs = reactive({
border: true,
loading: false,
disabledHover: true,
columns: dataPointColumns(),
data: [],
singleSelected: {},
actionsOptions: [...]
})
function loadTableData() {
...
tableAttrs.data = [...]
}
...
return { tableAttrs, ... }
}
复制代码
随着 Vue3
的稳定,IDE
方面的插件也有了新的支持,官方维护的 Volar
脱颖而出,这里只重点说明 Volar
的编辑器拆分功能来让体验更加丝滑。
经过拆分编辑器,配合 More in js
方式,只须要关注左侧 hooks
代码便可让开发体验上升一个台阶。
本次灵感主要解决开发时繁琐的代码横跳,从而找出更优的开发体验,但非最优体验,因人而异,请各位看官勿喷。有何问题,可评论区留言,谢谢!
相逢便是缘,挥一挥手指,留下一个赞吧!(^▽^)