
老项目遗留了一些代码,VUE 页面里面的代码太长了 想把 TS 代码拆分出来,请问最佳实践是什么样子的? 旧代码:
<template> <div class="table-tree-container"> <div class="list-tree-wrapper"> <div class="list-tree-operator"> <t-input v-model="filterText" placeholder="请输入过滤关键词" @change="onTreeInput"> <template #suffix-icon> <search-icon size="var(--td-comp-size-xxxs)" /> </template> </t-input> <t-button block @click="onCleanActive" variant="dashed" size="small" class="mt-2"> <template #icon> <i class="ri-close-circle-line mr-1"></i> </template> 清空选中项 </t-button> <t-tree class="mt-2" :data="categoryTree" activable v-model:actived="treeActived" @active="onTreeActive" :filter="filterByText" expand-all :keys="{ value: 'id', label: 'cate_name' }" hover expand-on-click-node /> </div> <div class="list-tree-content"> <div class="index-container"> <t-card :bordered="false" :title="cardTitle" class="list-card-container"> <template #actions> <t-button @click="onAdd" v-permission="'article/add'"> <template #icon> <i class="ri-add-line mr-1"></i> </template> 添加 </t-button> </template> <t-space> <t-select :optiOns="REC_OPTIONS" v-model="searchForm.rec" clearable placeholder="请选择推荐位" /> <t-select :optiOns="STATUS_OPTIONS" v-model="searchForm.status" clearable placeholder="请选择状态" /> <t-input v-model="searchForm.kw" placeholder="请输入你需要搜索的内容" clearable /> <t-button @click="onSearch" theme="default"> <template #icon> <i class="ri-search-line mr-1"></i> </template> 搜索 </t-button> <t-dropdown :optiOns="DROPDOWN_OPTIONS" :max-column-width="200"> <t-button variant="outline" theme="success"> 选中项 <i class="ri-arrow-down-s-line ml-1 icon-valign-top"></i> </t-button> </t-dropdown> </t-space> <t-row :gutter="16" class="table-container"> <t-col> <t-table :data="data" stripe :columns="TABLE_COLUMNS" :vertical-align="'top'" :hover="true" :pagination="pagination" @select-change="onSelectChange" @page-change="onPageChange" :loading="dataLoading" :row-key="'id'"> <template #id="{ row }"> <var>{{ row['id'] }}</var> </template> <template #status="{ row }"> <t-tag v-if="Number(row['status']) === 1" theme="success" variant="light"> 公开</t-tag> <t-tag v-else-if="Number(row['status']) === 0" theme="danger" variant="light"> 锁定</t-tag> <t-tag v-else theme="primary" variant="light"> 定时发布</t-tag> </template> <template #sort_by="{ row }"> <t-input-number v-permission="'article/sort'" v-model="row.sort_by" @change="(v) => onSortChange(row, v)" theme="column"></t-input-number> </template> <template #created_at="{ row }"> {{ formatTime(row['created_at']) }} </template> <template #cid="{ row }"> {{ formatCate(row['cid']) }} </template> <template #author="{ row }"> {{ row['author'] }} </template> <template #op="{ row }"> <a class="t-button-link" v-permission="'article/edit'" @click="onEdit(row)">编辑</a> </template> </t-table> </t-col> </t-row> </t-card> </div> </div> </div> </div> </template> <script setup lang="ts"> import { ref, onMounted, reactive } from 'vue'; import { MessagePlugin, DialogPlugin } from 'tdesign-vue-next'; import { STATUS_OPTIONS, REC_OPTIONS } from '@/config/global'; import helper from '@/utils/helper'; import { getArticleList, updateArticleSort, updateArticleStatus, deleteArticle } from '@/api/article'; import { IApiResponse } from '@/types'; import { TABLE_COLUMNS } from '@/pages/article/constants'; import { useRouter } from 'vue-router'; import { getArticleStore } from '@/store'; const router = useRouter(); const cardTitle = ref(helper.getPageTitle()); const cateId = ref(0); const categoryTree = ref([]); const articleStore = getArticleStore(); const fetchCategoryTree = async () => { const { treeList } = articleStore; if (helper.len(treeList) === 0) { try { await articleStore.getTreeCategory(); categoryTree.value = articleStore.treeList; } catch (e) { return Promise.reject(e); } } else { categoryTree.value = treeList; } }; const formatCate = (v) => { if (helper.len(categoryTree.value) <= 0) { return ''; } let item = categoryTree.value.find((ele) => { if (Number(ele.id) === Number(v)) { return ele; } return null; }); if (!item) { return v; } return item.cate_name; }; const formatTime = (v) => { if (!v) { return ''; } return helper.time().dateTimeDisplay(v); }; const OnAdd= () => { let url = '/article/add'; if (cateId.value) { url += '?cate_id=' + cateId.value; } router.push(url); }; const OnEdit= (row) => { router.push('/article/edit?id=' + row.id); }; const OnSearch= () => { fetchData(searchForm.value); }; const data = ref([]); const dataLoading = ref(false); const pagination = reactive({ current: 1, pageSize: Number(import.meta.env.VITE_APP_PAGE_SIZE), total: 0, showJumper: true, }); const searchForm = ref({ kw: '', cid: null, rec: null, status: null, page: pagination.current, page_size: pagination.pageSize, }); const OnSortChange= async (row, value) => { try { helper.fullLoading(); console.log(value, row); const resp: IApiRespOnse= await updateArticleSort({ id: row.id, value: value }); if (resp.code === 0) { MessagePlugin.success('操作成功'); await fetchData(); } else { return Promise.reject(new Error(resp.message)); } } catch (e) { MessagePlugin.error(e.message); } finally { helper.hideLoading(); } }; const OnPageChange= async (pageInfo) => { const { current, pageSize } = pageInfo; pagination.current = current; pagination.pageSize = pageSize; await fetchData({ page: current, page_size: pageSize }); }; const OnSelectChange= (value) => { selection.value = value; }; const fetchData = async (params = null) => { dataLoading.value = true; try { let def = { ...searchForm.value }; if (params) { def = Object.assign(def, params); } const res: IApiRespOnse= await getArticleList(def); if (res.code === 0) { const { data_list = [] } = res.data; data.value = data_list; pagination.total = res.data.total_count; } else { return Promise.reject(new Error(res.message)); } } catch (e) { data.value = []; console.log(e); //MessagePlugin.error(e.message); pagination.total = 0; } finally { dataLoading.value = false; } }; const OnTreeActive= async (v, c) => { searchForm.value.cid = c.node.data.id; router.push({ query: { cate_id: searchForm.value.cid, }, }); await fetchData(); }; const OnCleanActive= async () => { router.push({ query: {}, }); searchForm.value.cid = null; treeActived.value = []; await fetchData(); }; const treeActived = ref([]); const filterByText = ref(); const filterText = ref(); const OnTreeInput= () => { filterByText.value = (node) => { const rs = node.label.indexOf(filterText.value) >= 0; return rs; }; }; onMounted(async () => { cateId.value = helper.getQueryToNumber('cate_id', router); if (cateId.value) { searchForm.value.cid = cateId.value; } await fetchCategoryTree(); await fetchData(); }); const selection = ref([]); const DROPDOWN_OPTIOnS= [ { content: '删除选中内容', value: 2, onClick: () => { dropdownClick({ value: 2 }); }, }, { content: '修改为公开', value: 1, onClick: () => { dropdownClick({ value: 1 }); }, }, { content: '修改为隐藏', value: 0, onClick: () => { dropdownClick({ value: 0 }); }, }, ]; const OnStatus= async (params) => { try { helper.fullLoading(); const resp: IApiRespOnse= await updateArticleStatus(params); if (resp.code === 0) { MessagePlugin.success('操作成功'); await fetchData(); } else { return Promise.reject(new Error(resp.message)); } } catch (e) { MessagePlugin.error(e.message); } finally { helper.hideLoading(); } }; const OnDelete= async (params) => { const dialogNode = DialogPlugin.confirm({ header: '系统提示', body: '确定要删除吗?', theme: 'info', onConfirm: async () => { dialogNode.hide(); await deleteAction(params); }, }); }; const deleteAction = async (params) => { try { helper.fullLoading(); const resp: IApiRespOnse= await deleteArticle(params); if (resp.code === 0) { MessagePlugin.success('操作成功'); await fetchData(); } else { return Promise.reject(new Error(resp.message)); } } catch (e) { MessagePlugin.error(e.message); } finally { helper.hideLoading(); } }; const dropdownClick = (data) => { if (data.value === 1 && helper.len(selection.value) === 0) { MessagePlugin.warning('请选择要操作的数据'); return false; } let params = { ids: selection.value.join(',') }; if (data.value === 2) { onDelete(params); } else { params['status'] = data.value; onStatus(params); } }; </script> <style lang="less" scoped> .table-tree-container { background-color: var(--td-bg-color-container); border-radius: var(--td-radius-medium); .t-tree { margin-top: var(--td-comp-margin-xxl); } } .list-tree-wrapper { overflow-y: hidden; background: #fff; } .list-tree-operator { width: 210px; float: left; padding: var(--td-comp-paddingTB-xxl) var(--td-comp-paddingLR-xxl); } .list-tree-content { border-left: 1px solid var(--td-border-level-1-color); overflow: auto; } </style> 1 zcf0508 2023-08-19 15:40:04 +08:00 试试把代码复制到 https://hook.huali.cafe 看一下右侧的依赖关系,把关联比较明确的函数和方法独立出去改写成 hook 。 这是工具是我做的一个开源项目 https://github.com/zcf0508/vue-hook-optimizer/blob/master/README_cn.md |
2 lscho 2023-08-19 15:54:59 +08:00 把相对独立的拆分为 hooks 就行了,大概看了一下代码,你这个 categoryTree 就应该拆分出去,这样就少了一大坨代码。 然后增删改查四个逻辑,也可以单独写个 hooks 拆分。 剩下的基本就是每个页面独有的功能了,不太多 |
3 sunmoon1983 OP 嗯,我想把这个组优件中所有的 TS 代码都写成一个 TS 文件里,然后这个组件的解构应该就是 ``` artcile ----index.vue ----index.ts ----index.less ``` 不知道这样行不行 |
4 sunmoon1983 OP @zcf0508 好的,我拜读一下 |
5 zcf0508 2023-08-19 18:50:35 +08:00 via Android @sunmoon1983 如果只是拆语言不是拆逻辑,那没什么用 |
6 murmur 2023-08-19 19:04:35 +08:00 如果没有复用就不拆,现在的 IDE 都有函数列表,先完成功能再去优化,路由直接做下懒加载就完了 |
8 murmur 2023-08-19 19:11:57 +08:00 @zcf0508 有的开发尤其是企业级应用,表单复杂,字段繁多,逻辑复杂,那就是应用的特性,没必要强求,而且 vue 对着 template 一点就过去了,何必为难自己 |
9 yrj 2023-08-19 21:22:48 +08:00 功能提取成 hook ,视图拆分成组件 |
10 netabare 2023-08-20 03:12:33 +08:00 via Android 都用了 vue3 ,感觉最主要的就是拆组件然后用 emit 和 props 来进行组件间传递吧。不过一大块已经有的页面往下拆确实不好拆。 按照语言拆没什么用吧,这看着就像写 angular 一样。vue 的思路应该是一个 vue 文件就是一个 self contained 的组件然后组件可以嵌套起来吧。说起来 op 代码里是不是用到了嵌套 template ,感觉也许可以从这里开始? |
11 ruoxie 2023-08-20 12:41:22 +08:00 |
12 siwadiya 2023-10-11 18:41:18 +08:00 才三百多行,不算多 |