为vue单页面应用添加提示保存功能
errol发表于2023-04-15 02:38:28 | 分类为 编程 | 标签为vue提示保存vue-router页面刷新关闭页面js

在实际的生产应用中,提示保存是一个非常普遍且有用的功能之一,可以有效避免在用户填写或修改数据信息之后因误操作而导致数据丢失的情况,如不小心点到返回按钮、关闭按钮、刷新按钮等等,尤其是在需要填写大量数据信息的场景,这一功能显得尤为重要,否则用户误操后,需要再次花费大量的时间精力来填充数据,是非常影响用户体验的。

这一功能在vue单页面应用中实现起来非常简单。

该功能的逻辑虽然比较简单,但却意外的实用有效,很大程度上增加了用户操作的容错率

一般情况下,vue单页面应用都依赖于vue-router插件,用来做页面组件之间的导航。

vue-router具有导航守卫的概念,主要用来通过跳转或取消的方式守卫导航(即是否放行跳转至下一个路由组件),一共分为以下三种类型:

  • 全局守卫
  • 独享守卫
  • 组件内守卫

而本文要实现的功能将会依赖于组件内守卫。(其他类型守卫请参考vue-router文档) 如字面含义,该类型守卫属于组件级别的守卫,可以在每个独立的组件内使用,主要包含了以下三种导航守卫。

  • beforeRouteEnter(to, from, next)
  • beforeRouteUpdate(to, from, next)
  • beforeRouteLeave(to, from, next)

守卫以钩子函数的形式出现,每种守卫的调用时机都不同:beforeRouteEnter是在进入路由组件之前(组件实例还没创建完成)调用,beforeRouteUpdate是在路由组件更新之前调用,最后的beforeRouteLeave则是在离开路由组件之前调用。

看起来可能会显得有一点复杂,但庆幸的是,提示保存功能仅仅需要用到其中的一个钩子函数:beforeRouteLeave。

下面开始着手实现这一功能。

给组件添加beforeRouteLeave钩子

为了更接近真实的应用场景,除了添加钩子内的逻辑外,还需要额外添加一些方法和数据,如表单字段、比较函数等。

const defaultFormData = {
    id: "",
    name: "",
    description: ""
};
export default {
    data() {
        return {
            formData: { ...defaultFormData }, // 表单数据
            original: { ...defaultFormData }, // 用于保存原始的表单数据(ajax请求时,会将数据保存于该变量)
            keysChecked: Object.keys(defaultFormData), // 需要校验的key
            skipComparison: false // 是否要跳过校验
        }    
    },
    beforeRouteLeave(to, from, next) {
        // 跳过比较,直接放行;比如,已经提交保存了的情况下
        if (this.skipComparison) {
            return next();
        }
        // 比较整个表单字段,如果字段全部相等时,放行,否则提示;
        const r = this.compare(this.formData, this.original);
        r === 0 ? next() : this.alert(next);
    },
    methods: {
        // 两个对象的字段全部相等时,返回0,否则返回-1
        compare(o1, o2) {
            if (o1 === null || o2 === null) {
                return 0;
            }
            for (let key of this.keysChecked) {
                if (o1[key] !== o2[key]) {
                    return -1;
                }
            }
            return 0;
        },
        alert(next) {
            this.$confirm('内容尚未保存,改动部分将会被丢弃,是否继续?', '提示', {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning'
            }).then(() => {
                next();
            }).catch(() => {
                next(false);
            });
        }
    }
}

最后别忘了将属性绑在表单元素上。

image

在编写了上述简单的代码后,我们已经成功实现了离开当前路由组件时提示保存的功能, 由于填写了数据又没保存,所以点击返回时,应用弹出了提示对话框。

image

但有一点需要注意的地方。

插件的router.go(n)方法,类似于window.history.go(n),是在浏览器的history记录中向前或者后退,并不经过vue-router。所以使用该方法返回上一个路由组件时,会出现钩子逻辑不能正常运行的现象,因此想要正常触发提示保存功能,就不能使用这个函数,可以使用push()或者replace()作为代替。

添加侦听器监听beforeunload事件

实际上,虽然我们已经实现了路由组件返回前的提示功能,但是以上的逻辑并不能应用于关闭浏览器标签页或刷新浏览器页面,这两种情况还需要做额外的处理,不过也不必担心,这部分逻辑也相当简单的,只需要往window对象添加一个侦听器监听beforeunload事件即可。

该侦听器会在相应的时候,对以上两种情况进行拦截。

export default {
    // ...略
    mounted() {
        window.addEventListener('beforeunload', this.beforeUnloadHandler);
    },
    destroyed() {
        // 别忘了卸载侦听器
        window.removeEventListener("beforeunload", this.beforeUnloadHandler);
    },
    methods() {
        // ...略
        // 使用同样的比较逻辑,如果发生了变动,就提示用户;
        beforeUnloadHandler(e) {
            const r = this.compare(this.formData, this.original);
            if (r !== 0) {
                e.returnValue = "温馨提示";
            }
        }    
    }
}

有点可惜的是,这个弹窗并不能自定义提示信息。

未保存数据,点击刷新页面时的提示信息

image

未保存数据,关闭标签页时的提示信息

image

目前,我们已经为所有的情况添加了提示保存功能:离开当前路由组件、关闭标签页、刷新页面,原理和实现还是比较简单的,唯一要注意的是,检测数据是否发生改动的方法很可能会因项目而异,有的比较简单(如文中的示例),有的就比较复杂,可能是因为属性多,或者属性的结构复杂等等,这需要根据项目的实际的情况来实现该方法。

此外,还可以考虑把以上的逻辑都抽为一个模块,随后在需要提示保存功能的地方引入相关的方法和数据即可,这样无论是从维护角度还是使用角度来说,都是比较方便的。

以上就是本篇文章的全部内容,希望对你有所帮助。

返回