如何充分利用Composition API对Vue3项目进行代码抽离(下)

简介: 接上文。

对比一


  • 抽离前


<template>
  <div class="import-config-container" v-show="isShow">
    <div class="import-config-alert">
      <div class="close-import-config-alert" @click="closeAlert"></div>
      <div class="import-config-alert-title">导入配置</div>
      <div class="import-config-alert-remind">说明:需要上传之前保存导出的xxx.json配置文件,文件中的信息会完全覆盖当前信息</div>
      <form action="" class="form">
        <label for="import_config_input" class="import-config-label">
          上传配置文件
          <i v-if="hasFile == 1" class="fas fa-times-circle uploadErr uploadIcon"/>
          <i v-else-if="hasFile == 2" class="fas fa-check-circle uploadSuccess uploadIcon"/>
        </label>
        <input id="import_config_input" type="file" class="select-file" ref="inputFile" @change="fileChange">
      </form>
      <lp-button type="primary" class="import-config-btn" @_click="importConfig">确认上传</lp-button>
    </div>
  </div>
</template>
<script>
import {ref, inject} from 'vue'
import lpButton from '../../public/lp-button/lp-button'
export default {
    props: {
      isShow: {
        type: Boolean,
        default: true
      }
    },
    components: {
        lpButton
    },
    setup(props, {emit}) {
        const result = ref('none')     // 导入的结果
        const isUpload = ref(false)    // 判断是否上传配置文件
        const isImport = ref(false)    // 判断配置是否导入成功
        const isLoading = ref(false)   // 判断按钮是否处于加载状态
        const inputFile = ref(null)    // 获取文件标签
        const hasFile = ref(0)         // 判断文件的传入情况。0:未传入  1: 格式错误  2:格式正确
        const $message = inject('message')
        // 导入配置
        function importConfig() {
          let reader = new FileReader()
          let files = inputFile.value.files
          if(hasFile.value == 0) {
            $message({
              type: 'warning',
              content: '请先上传配置文件'
            })
          }
          else if(hasFile.value == 1) {
            $message({
              type: 'warning',
              content: '请上传正确格式的文件,例如xx.json'
            })
          }
          else if(hasFile.value == 2) {
            reader.readAsText(files[0])
            reader.onload = function() {
              let data = this.result
              window.localStorage.navInfos = data
              location.reload()
            }
          }
        }
        // 关闭弹窗
        function closeAlert() {
          emit('closeImportConfigAlert', false)
          hasFile.value = 0
        }
        function fileChange(e) {
          let files = e.target.files
          if(files.length === 0) {
            $message({
              type: 'warning',
              content: '请先上传配置文件'
            })
          }
          else {
            let targetFile = files[0]
            if(!/\.json$/.test(targetFile.name)) {
              hasFile.value = 1
              $message({
                type: 'warning',
                content: '请确认文件格式是否正确'
              })
            } else {
              hasFile.value = 2
              $message({
                type: 'success',
                content: '文件格式正确'
              })
            }
          }
        }
        return {
          result, 
          isUpload,
          isImport, 
          isLoading,
          importConfig, 
          closeAlert,
          inputFile,
          fileChange,
          hasFile
        }
    }
}
</script>


  • 抽离后


<template>
  <div class="import-config-container" v-show="isShowImportAlert">
    <div class="import-config-alert">
      <div class="close-import-config-alert" @click="handleImportConfigAlert(false)"></div>
      <div class="import-config-alert-title">导入配置</div>
      <div class="import-config-alert-remind">说明:需要上传之前保存导出的xxx.json配置文件,文件中的信息会完全覆盖当前信息</div>
      <form action="" class="form">
        <label for="import_config_input" class="import-config-label">
          上传配置文件
          <i v-if="hasFile == 1" class="fas fa-times-circle uploadErr uploadIcon"/>
          <i v-else-if="hasFile == 2" class="fas fa-check-circle uploadSuccess uploadIcon"/>
        </label>
        <input id="import_config_input" type="file" class="select-file" ref="inputFile" @change="fileChange">
      </form>
      <lp-button type="primary" class="import-config-btn" @_click="importConfig">确认上传</lp-button>
    </div>
  </div>
</template>
<script>
/* API */
import { inject } from 'vue'
/* 组件 */
import lpButton from '@/components/public/lp-button/lp-button'
/* 功能模块 */
import importConfigFunction from '@/use/importConfig'
export default {
    components: {
        lpButton
    },
    setup() {
        const $message = inject('message')
        const { 
          isShowImportAlert,
          handleImportConfigAlert,
          result,  
          isUpload, 
          isImport, 
          isLoading, 
          importConfig, 
          closeAlert, 
          inputFile, 
          fileChange, 
          hasFile 
        } = importConfigFunction($message)
        return {
          isShowImportAlert,
          handleImportConfigAlert,
          result, 
          isUpload,
          isImport, 
          isLoading,
          importConfig, 
          closeAlert,
          inputFile,
          fileChange,
          hasFile
        }
    }
}
</script>


  • 抽离出的代码文件


// 导入配置功能
import { ref } from 'vue'
const isShowImportAlert = ref(false),   // 导入配置弹框是否展示
      result = ref('none'),             // 导入的结果
      isUpload = ref(false),            // 判断是否上传配置文件
      isImport = ref(false),            // 判断配置是否导入成功
      isLoading = ref(false),           // 判断按钮是否处于加载状态
      inputFile = ref(null),            // 获取文件元素
      hasFile = ref(0)                  // 判断文件的传入情况。0:未传入  1: 格式错误  2:格式正确
export default function importConfigFunction($message) {
    // 控制弹框的展示
    function handleImportConfigAlert(value) {
        isShowImportAlert.value = value
        if(!value) hasFile.value = 0
    }
    // 上传的文件内容发生改变
    function fileChange(e) {
        let files = e.target.files
        if(files.length === 0) {
            $message({
            type: 'warning',
            content: '请先上传配置文件'
            })
        }
        else {
            let targetFile = files[0]
            if(!/\.json$/.test(targetFile.name)) {
                hasFile.value = 1
                $message({
                    type: 'warning',
                    content: '请确认文件格式是否正确'
                })
            } else {
            hasFile.value = 2
                $message({
                    type: 'success',
                    content: '文件格式正确'
                })
            }
        }
    }
    // 导入配置
    function importConfig() {
        let reader = new FileReader()
        let files = inputFile.value.files
        if(hasFile.value == 0) {
          $message({
            type: 'warning',
            content: '请先上传配置文件'
          })
        }
        else if(hasFile.value == 1) {
          $message({
            type: 'warning',
            content: '请上传正确格式的文件,例如xx.json'
          })
        }
        else if(hasFile.value == 2) {
          reader.readAsText(files[0])
          reader.onload = function() {
            let data = this.result
            window.localStorage.navInfos = data
            location.reload()
          }
        }
    }
    return {
        isShowImportAlert,
        result,
        isUpload,
        isImport,
        isLoading,
        inputFile,
        hasFile,
        handleImportConfigAlert,
        fileChange,
        importConfig,
    }
}


对比二


  • 抽离前


<template>
    <!-- 此处因代码太多,暂时省略,详情可以点击文末的项目源码查看 -->
</template>
<script>
import {ref, inject} from 'vue'
import {useStore} from 'vuex'
import urlAlert from '../public/urlAlert/urlAlert'
import tagAlert from '../public/tabAlert/tabAlert'
import carousel from './childCpn/carousel'
import search from './childCpn/search'
import { exchangeElements } from '../../utils/utils'
<script>
export default {
    components: {
        urlAlert,
        tagAlert,
        carousel,
        search,
    },
    setup() {
        const store = useStore()
        const catalogue = store.state.catalogue
        const moduleUrl = store.state.moduleUrl
        const moduleSearch = store.state.moduleSearch
        const $message = inject('message')
        const $confirm = inject('confirm')
        const editWhich = ref(-1)
        // 弹出添加URL的框
        function addMoreUrl(id) {
            store.commit('changeUrlInfo', [
                {key: 'isShow', value: true},
                {key: 'whichTag', value: id},
                {key: 'alertType', value: '新增网址'}
            ])
        }
        // 处理无icon或icon加载失败的图片,令其使用默认svg图标
        function imgLoadErr(e) {
            let el = e.target
            el.style.display = 'none'
            el.nextSibling.style.display = 'inline-block'
        }
        function imgLoadSuccess(e) {
            let el = e.target
            el.style.display = 'inline-block'
            el.nextSibling.style.display = 'none'
        }
        // 进入编辑状态
        function enterEdit(id) {
            if(id != editWhich.value) {
                editWhich.value = id
            } else {
                editWhich.value = -1
            }     
        }
        // 修改标签弹框弹出
        function editTagAlertShow(tab) {
            store.commit('changeTabInfo', [
                {key: 'isShowAddTabAlert', value: true},
                {key: 'tagName', value: tab.name},
                {key: 'trueIcon', value: tab.icon},
                {key: 'isSelected', value: true},
                {key: 'currentIcon', value: tab.icon},
                {key: 'id', value: tab.id},
                {key: 'alertType', value: '修改标签'}
            ])
        }
        // 删除标签以及标签下的所有网址
        function deleteTag(id) {
            $confirm({
                content: '确定删除该标签以及该标签下所有网址吗?'
            })
            .then(() => {
                store.commit('remove', id)
                $message({
                    type: 'success',
                    content: '标签页及子网址删除成功'
                })
            })
            .catch(() => {})
        }
        // 删除某个网址
        function deleteUrl(id) {
            $confirm({
                content: '确定删除该网址吗?'
            })
            .then(() => {
                store.commit('remove', id)
                $message({
                    type: 'success',
                    content: '网址删除成功'
                })
            })
            .catch(() => {})      
        }
        // 弹出修改URL的弹框
        function editUrl(url) {
            store.commit('changeUrlInfo', [
                {key: 'url', value: url.url},
                {key: 'icon', value: url.icon},
                {key: 'id', value: url.id},
                {key: 'name', value: url.name},
                {key: 'isShow', value: true},
                {key: 'alertType', value: '修改网址'}
            ])
        }
        function judgeTabIsShow(i) {
            const URLS = catalogue[i]['URLS']
            let length = URLS.length
            for(let j = 0; j < length; j++) {
                if(moduleSearch.searchWord == '') return false;
                else if(URLS[j].name.toLowerCase().indexOf(moduleSearch.searchWord.toLowerCase()) !== -1) return true;
            }
            return false
        }
        function judgeUrlIsShow(i, j) {
            const url = catalogue[i]['URLS'][j]
            if(url.name.toLowerCase().indexOf(moduleSearch.searchWord.toLowerCase()) !== -1) return true;
            return false;
        }
        let elementNodeDragged = null   // 被移动的地址框元素
        let elementNodeLocated = null  // 移入的地址框元素
        let draggedId = -1   // 被移动地址框的id
        // 地址框开始拖拽
        function urlBoxDragStart(e) {
            const el = e.target
            if(el.nodeName !== 'LI') return;
            // 记录当前被拖拽地址框元素
            elementNodeDragged = el    
            // 将被拖拽对象隐藏
            el.style.display = 'fixed'
            el.style.opacity = 0
        }
        // 地址框拖拽结束
        function urlBoxDragEnd(e) {
            let el = e.target
            el.style.display = 'inline-block'
            el.style.opacity = 1
            // 获取当前正在编辑标签中所有url的排序
            let timer = setTimeout(() => {
                const result = []
                const children = elementNodeLocated.parentNode.children
                let length = children.length
                for(let i = 0; i < length - 1; i++) {
                    result.push(children[i].getAttribute('data-id'))
                }
                store.commit('dragEndToUpdate', {tabId: editWhich.value, result})
                clearTimeout(timer)
            }, 500)
        }
        // 被拖动的地址框触碰到其它的地址框
        function urlBoxEnter(e, tabId) {
            if(tabId != editWhich.value) return;
            let el = e.target
            while(el.nodeName !== 'LI') el = el.parentNode;        // 若子元素触发dragenter事件,则查找到父元素li标签
            if(el === elementNodeDragged) return;     // 避免自己拖拽进入自己的情况
            if(elementNodeLocated !== el) elementNodeLocated = el    // 记录被移入的地址框元素
            exchangeElements(elementNodeDragged, el)     //  地址框位置互换
        }
        return {
            catalogue, 
            addMoreUrl, 
            moduleUrl, 
            moduleSearch,
            imgLoadErr,
            imgLoadSuccess, 
            enterEdit, 
            editTagAlertShow,
            deleteTag,
            deleteUrl,
            editUrl,
            editWhich,
            judgeTabIsShow,
            judgeUrlIsShow,
            urlBoxDragStart,
            urlBoxDragEnd,
            urlBoxEnter,
        }
    }
}
</script>


  • 抽离后


<template>
  <!-- 此处因代码太多,暂时省略,详情可以点击文末的项目源码查看 -->
</template>
<script>
/* API */
import { inject } from 'vue'
import { useStore } from 'vuex'
/* 组件 */
import urlAlert from '@/components/public/urlAlert/index'
import tabAlert from '@/components/public/tabAlert/index'
import carousel from './carousel'
import search from './search'
/* 功能模块 */
import baseFunction from '@/use/base'
import editFunction from '@/use/edit'
import urlAlertFunction from '@/use/urlAlert'
import tabAlertFunction from '@/use/tabAlert'
import searchFunction from '@/use/search'
export default {
    components: {
        urlAlert,
        tabAlert,
        carousel,
        search,
    },
    setup() {
        const catalogue = useStore().state.catalogue
        const $message = inject('message')
        const $confirm = inject('confirm')
        // 一些基础的方法
        let { imgLoadErr, imgLoadSuccess } = baseFunction()
        // url框编辑下的相关变量及功能
        let { 
            editWhich, 
            handleEdit, 
            deleteTab, 
            deleteUrl, 
            urlBoxDragStart, 
            urlBoxDragEnd, 
            urlBoxEnter 
        } = editFunction($message, $confirm)
        // 弹出 “新增”、“修改” url弹框
        let { showNewUrlAlert, showEditUrlAlert } = urlAlertFunction()
        // 搜索功能相关的变量及方法
        let { moduleSearch, judgeTabIsShow, judgeUrlIsShow } = searchFunction()
        // 展示修改tab的弹框
        let { showEditAddTab } = tabAlertFunction()
        return {
            catalogue, 
            showNewUrlAlert, 
            moduleSearch,
            imgLoadErr,
            imgLoadSuccess, 
            handleEdit, 
            showEditAddTab,
            deleteTab,
            deleteUrl,
            showEditUrlAlert,
            editWhich,
            judgeTabIsShow,
            judgeUrlIsShow,
            urlBoxDragStart,
            urlBoxDragEnd,
            urlBoxEnter,
        }
    }
}
</script>


  • 抽离出的代码文件(此处涉及到很多个模块,因此只展示两个吧)


// 搜索功能
import {  } from 'vue'
import store from '@/store/index'
// 变量
const moduleSearch = store.state.moduleSearch     // 搜索相关的全局状态
export default function searchFunction() {
    // 搜索框的输入改变
    function inputSearchContent(value) {
        store.commit('changeSearchWord', value)
    }
    // 控制搜索框的展示
    function handleSearchBox() {
        if(moduleSearch.isSearch) {
            store.commit('changeIsSearch', false)
            store.commit('changeSearchWord', '')
        } else {
            store.commit('changeIsSearch', true)
        }         
    }
    // 判断标签是否显示
    function judgeTabIsShow(i) {
        return store.getters.judgeTabIsShow(i)
    }
    // 判断url是否显示
    function judgeUrlIsShow(i, j) {
        return store.getters.judgeUrlIsShow(i, j)
    }
    return {
        moduleSearch,
        inputSearchContent,
        handleSearchBox,
        judgeTabIsShow,
        judgeUrlIsShow,
    }
}


// url框的拖拽排列
import { ref } from 'vue'
import { exchangeElements, debounce } from '@/utils/utils'
import store from '@/store/index'
//变量
let elementNodeDragged = null,     // 被移动的地址框元素
    elementNodeLocated = null,     // 移入的地址框元素
    editWhich = ref(-1)            // 记录正在编辑的tab索引
export default function editFunction($message, $confirm) {
        // 控制编辑状态
        function handleEdit(id) {
            if(id != editWhich.value) {
                editWhich.value = id
            } else {
                editWhich.value = -1
            }     
        }
        // 删除标签以及标签下的所有网址
        function deleteTab(id) {
            $confirm({
                content: '确定删除该标签以及该标签下所有网址吗?'
            })
            .then(() => {
                store.commit('remove', id)
                $message({
                    type: 'success',
                    content: '标签页及子网址删除成功'
                })
            })
            .catch(() => {})
        }
        // 删除某个网址
        function deleteUrl(id) {
            $confirm({
                content: '确定删除该网址吗?'
            })
            .then(() => {
                store.commit('remove', id)
                $message({
                    type: 'success',
                    content: '网址删除成功'
                })
            })
            .catch(() => {})      
        }
        // 地址框开始拖拽
        function urlBoxDragStart(e) {
            const el = e.target
            if(el.nodeName !== 'LI') return;
            // 记录当前被拖拽地址框元素
            elementNodeDragged = el    
            // 将被拖拽对象隐藏
            el.style.display = 'fixed'
            el.style.opacity = 0
        }
        // 拖拽后更新Vuex中的正确排序
        let dragEndToUpdate = debounce(function() {
            // 获取当前正在编辑标签中所有url的排序
            const result = []
            const children = elementNodeLocated.parentNode.children
            let length = children.length
            for(let i = 0; i < length - 1; i++) {
                result.push(children[i].getAttribute('data-id'))
            }
            store.commit('dragEndToUpdate', {tabId: editWhich.value, result}) 
        }, 500)
        // 地址框拖拽结束
        function urlBoxDragEnd(e) {
            let el = e.target
            el.style.display = 'inline-block'
            el.style.opacity = 1
            dragEndToUpdate()
        }
        // 被拖动的地址框触碰到其它的地址框
        function urlBoxEnter(e, tabId) {
            if(tabId != editWhich.value) return;
            let el = e.target
            while(el.nodeName !== 'LI') el = el.parentNode;          // 若子元素触发dragenter事件,则查找到父元素li标签
            if(el === elementNodeDragged) return;                    // 避免自己拖拽进入自己的情况
            if(elementNodeLocated !== el) elementNodeLocated = el    // 记录被移入的地址框元素
            exchangeElements(elementNodeDragged, el)                 //  地址框位置互换
        }
    return {
        editWhich,
        handleEdit,
        deleteTab,
        deleteUrl,
        urlBoxDragStart,
        urlBoxDragEnd,
        urlBoxEnter,
    }
}


最后


细心的小伙伴应该发现了,刚才给大家展示的代码中,有一段是各种拖拽的实现方法,没错!!我在闲暇之余给我的项目加上了编辑模式下的 拖拽排列功能 ,也算是完成了之前大家对我提的建议之一啦,欢迎各位前去体验新功能~


项目体验链接http://lpyexplore.gitee.io/nav-infos/


在体验完后,希望有心的小伙伴们能在github上给我提提Issues,我看到会第一时间回复的(如果催我做账号功能的小伙伴多,我后期可能会考虑加上)


项目源码链接https://github.com/Lpyexplore/nav-url(欢迎各位Star,多提意见,多交流啊~)


本文所阐述的代码抽离方法是我改过很多遍后定下来的,不知道后面还会有什么问题,但目前看来,对于以后的维护和管理应该是会方便很多的,如果大家有更好的意见或想法,可以留下评论,或者加我vx:Lpyexplore333私底下交流


最后谢谢各位的耐心观看

相关文章
|
2月前
|
JavaScript 容器
乾坤qiankun框架搭建 主应用为vue3的项目。
乾坤qiankun框架搭建 主应用为vue3的项目。
166 2
|
1月前
|
数据采集 监控 JavaScript
在 Vue 项目中使用预渲染技术
【10月更文挑战第23天】在 Vue 项目中使用预渲染技术是提升 SEO 效果的有效途径之一。通过选择合适的预渲染工具,正确配置和运行预渲染操作,结合其他 SEO 策略,可以实现更好的搜索引擎优化效果。同时,需要不断地监控和优化预渲染效果,以适应不断变化的搜索引擎环境和用户需求。
|
25天前
|
JavaScript 前端开发
如何在 Vue 项目中配置 Tree Shaking?
通过以上针对 Webpack 或 Rollup 的配置方法,就可以在 Vue 项目中有效地启用 Tree Shaking,从而优化项目的打包体积,提高项目的性能和加载速度。在实际配置过程中,需要根据项目的具体情况和需求,对配置进行适当的调整和优化。
|
1月前
|
JavaScript 前端开发 API
Vue 3新特性详解:Composition API的威力
【10月更文挑战第25天】Vue 3 引入的 Composition API 是一组用于组织和复用组件逻辑的新 API。相比 Options API,它提供了更灵活的结构,便于逻辑复用和代码组织,特别适合复杂组件。本文将探讨 Composition API 的优势,并通过示例代码展示其基本用法,帮助开发者更好地理解和应用这一强大工具。
30 1
|
2月前
|
JavaScript
如何在 Vue 项目中选择合适的模块格式
【10月更文挑战第20天】选择合适的模块格式需要综合考虑多个因素,没有一种绝对正确的选择。需要根据项目的具体情况进行权衡和分析。在实际选择过程中,要保持灵活性,根据项目的发展和变化适时调整模块格式。
21 7
|
1月前
Vue3 项目的 setup 函数
【10月更文挑战第23天】setup` 函数是 Vue3 中非常重要的一个概念,掌握它的使用方法对于开发高效、灵活的 Vue3 组件至关重要。通过不断的实践和探索,你将能够更好地利用 `setup` 函数来构建优秀的 Vue3 项目。
|
2月前
|
JavaScript 前端开发 编译器
在 Vue 项目中使用 ES 模块格式的优点
【10月更文挑战第20天】在 Vue 项目中使用 ES 模块格式具有众多优点,这些优点共同作用,使得项目能够更高效、更可靠地开发和运行。当然,在实际应用中,还需要根据项目的具体情况和需求进行合理的选择和配置。
37 6
|
1月前
|
JavaScript 测试技术 UED
解决 Vue 项目中 Tree shaking 无法去除某些模块
【10月更文挑战第23天】解决 Vue 项目中 Tree shaking 无法去除某些模块的问题需要综合考虑多种因素,通过仔细分析、排查和优化,逐步提高 Tree shaking 的效果,为项目带来更好的性能和用户体验。同时,持续关注和学习相关技术的发展,不断探索新的解决方案,以适应不断变化的项目需求。
|
2月前
|
缓存 JavaScript 前端开发
《基础篇第4章:vue2基础》:使用vue脚手架创建项目
《基础篇第4章:vue2基础》:使用vue脚手架创建项目
74 3
|
2月前
|
缓存 JavaScript 前端开发
深入理解 Vue 3 的 Composition API 与新特性
本文详细探讨了 Vue 3 中的 Composition API,包括 setup 函数的使用、响应式数据管理(ref、reactive、toRefs 和 toRef)、侦听器(watch 和 watchEffect)以及计算属性(computed)。我们还介绍了自定义 Hooks 的创建与使用,分析了 Vue 2 与 Vue 3 在响应式系统上的重要区别,并概述了组件生命周期钩子、Fragments、Teleport 和 Suspense 等新特性。通过这些内容,读者将能更深入地理解 Vue 3 的设计理念及其在构建现代前端应用中的优势。
39 0
深入理解 Vue 3 的 Composition API 与新特性

热门文章

最新文章