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
代碼便可讓開發體驗上升一個臺階。
本次靈感主要解決開發時繁瑣的代碼橫跳,從而找出更優的開發體驗,但非最優體驗,因人而異,請各位看官勿噴。有何問題,可評論區留言,謝謝!
相逢便是緣,揮一揮手指,留下一個贊吧!(^▽^)