vue3 后台管理项目实战(附开源代码)

8,980 阅读3分钟

一、背景

vue3出来已经一年多了,为了防止被卷没,六月份时候比较闲就把公司项目用VUE3里里外外重构了一遍,到现在公司所有项目已经都在采用vue3版本,已有项目也在空闲时候做出了陆续升级。虽然写了几个VUE3项目,但很多东西还一直停留在应用层面,甚至说不清,先把我用到的梳理一遍。代码不停,内卷不止

附上github开源地址:
github.com/909652769/v…

二、技术栈

webpack => vite2
vue2 => vue3
vueRouter3 => vueRouter4
vuex3 => vuex4
pug
stylus
typescript

vite2

在Vite2官网文档中,有这么一段话:

在浏览器支持 ES 模块之前,开发者没有以模块化的方式开发 JavaScript 的原生机制。这也是 “打包” 这个概念出现的原因:使用工具抓取、处理和链接我们的源码模块到文件中,使其可以运行在浏览器中。

时过境迁,我们见证了许多诸如 webpackRollup 和 Parcel 等工具的诞生,这些工具极大地改善了前端开发者的开发体验。

然而,当我们开始构建越来越大型的应用时,需要处理的 JavaScript 代码量也呈指数级增长。大型项目包含数千个模块的情况并不少见。我们开始遇到性能瓶颈 —— 使用 JavaScript 开发的工具通常需要很长时间(甚至是几分钟!)才能启动开发服务器,即使使用 HMR,文件修改后的效果也需要几秒钟才能在浏览器中反映出来。如此循环往复,迟钝的反馈会极大地影响开发者的开发效率和幸福感。

Vite 旨在利用生态系统中的新进展解决上述问题:浏览器开始原生支持 ES 模块,越来越多 JavaScript 工具使用编译型语言编写。

webpack 在启动开发服务器时,基于打包器的方式是在提供服务前去急切地抓取和构建你的整个应用。

vite 只需要在浏览器请求源码时进行转换并按需提供源码。根据情景动态导入的代码,即只在当前屏幕上实际使用时才会被处理

vite 的启动构建是 webpack 的 10-100 倍,开发幸福感大大提升,具体可参考官方文档emmm

vue3

vue3比vue2来说,性能上更好代码体积更小,并且有更好的ts支持
vue2所有的全局API在vue3中都调用createApp来创建。 vue2中是不支持多根节点组件的,vue3组件可以包含多个根节点。
...

vue3 相较 vue2 变化还是蛮多的,如全局 API模板指令组件渲染函数自定义元素等都有一定的变化。 也移除了一些API如filter$destroy$on$off等。

下列举一下比较常用也是我项目中用到的写法变化:

组合式API

在vue3项目中,最显眼的变化就是setup的出现了,在vue文档中有这样的说明:

通过创建 Vue 组件,我们可以将界面中重复的部分连同其功能一起提取为可重用的代码段。仅此一项就可以使我们的应用在可维护性和灵活性方面走得相当远。然而,我们的经验已经证明,光靠这一点可能并不够,尤其是当你的应用变得非常大的时候——想想几百个组件。处理这样的大型应用时,共享和重用代码变得尤为重要。

如果能够将同一个逻辑关注点相关代码收集在一起会更好。而这正是组合式 API 使我们能够做到的

既然我们知道了为什么,我们就可以知道怎么做。为了开始使用组合式 API,我们首先需要一个可以实际使用它的地方。在 Vue 组件中,我们将此位置称为 setup

setup函数是组合API的入口函数。
setup它会在beforeCreate钩子之前被调用。
setup由于在执行时候,还没有执行created生命周期方法,所以在setup函数中,无法使用datamethods
setup它接收propscontext两个参数。
setup函数中定义的变量和方法最后都是需要 return 出去的,不然无法在模板中使用。

全局 API

在测试期间,全局配置很容易意外地污染其他测试用例。用户需要仔细存储原始全局配置,并在每次测试后恢复 (例如重置 Vue.config.errorHandler)。有些 API 像 Vue.use 以及 Vue.mixin 甚至连恢复效果的方法都没有,这使得涉及插件的测试特别棘手。
为了避免这些问题,在 Vue 3 中我们引入一个全新的全局API createApp

// vue2
import Vue from "vue";
Vue.prototype.$moment = moment;

// vue3
import { createApp } from 'vue'
const app = createApp(App)
app.config.globalProperties.$moment = moment;

watch

watch API 与选项式 API this.$watch (以及相应的 watch 选项) 完全等效。watch 需要侦听特定的数据源,并在单独的回调函数中执行副作用。默认情况下,它也是惰性的——即回调仅在侦听源发生更改时被调用。

let count = 0
// vue2
watch: {
    count: (res) => {
        /* res */
    }
}

// vue3
watch(() => count,
    (newValues) => {
        /* newValues */
    }
)
// vue3监听多个
watch(() => [count1, count2],
    (newValues1, newValues2) => {
        /* newValues1 */
        /* newValues2 */
    }
)

keep-alive

<keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 <transition> 相似,<keep-alive> 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在组件的父组件链中。
当组件在 <keep-alive> 内被切换,它的 activated 和 deactivated 这两个生命周期钩子函数将会被对应执行。
主要用于保留组件状态或避免重新渲染。

// vue2
<keep-alive>
    <router-view />
</keep-alive>

// vue3
// keepAliveList需要缓存的组件列表
<router-view v-slot="{ Component }">
    <keep-alive :include="keepAliveList">
        <Component :is="Component"></Component>
    </keep-alive>
</router-view>

完整对比

vue2

<template lang="pug">
    .layout-container
        .layout-head
            span {{count}}
            // ...
        .layout-page
            keep-alive
                router-view
</template>
<script>
export default {
    name: "layout",
    data() {
        count: 0,
        // ...
    },
    watch: {
        count: (res) => {
            // ...
        }
    },
    created(){
        // ...
    },
    mounted: () => {
        // ...
    },
    methods: {
        initPage() {
            // ...
        }
    }
}
</script>

vue3

<template lang="pug">
.layout-container
    .layout-head
        span {{count}}
        // ...
    .layout-page
        router-view(v-slot="{ Component }")
            keep-alive(:include="keepAliveList")
                Component(:is="Component")
</template>
<script lang="ts">
import { defineComponent, reactive, onMounted, watch } from 'vue'
export default defineComponent({
    name: 'layout',
    setup() {
        const state:any = reactive({
            count: 0,
            // ...
        })
        
        watch(() => count,
            (newValues) => {
                // ...
            }
        )
        
        onMounted(() => {
            // ...
        })
        
        const initPage:Function = () => {
            // ...
        }
        
        return {
            ...toRefs(state),
            initPage
        }
    }
})
</script>

最后

还有挺多没梳理的,也不知道怎么梳理,基础不够写的不好轻点骂~,毕竟我也是个API工程师