【Vue 技巧】做一个全局登录弹窗,任何地方访问__Vue.js
发布于 3 年前 作者 banyungong 1473 次浏览 来自 分享
粉丝福利 : 关注VUE中文社区公众号,回复视频领取粉丝福利

theme: vue-pro highlight: atom-one-light

需要一个登录弹窗,在任何地方都能使用。

需求

  1. 在任何页面内能访问
  2. 在路由拦截能访问

解决方案

任何地方都能访问,第一个想到的是挂载原型链Vue.prototype.$someThing。而访问一个实例,一般是ref去访问,路由是在Vue配置时组成了守卫,这时还访问不了vue实例refs是实例属性,就更访问不了组件实例了,可能可以动态改路由守卫,但是这里准备用单开一个vue实例,这样就可以暴露一个单独实例出来了。

步骤

1. 新建一个js文件,引入main.js

由于我的项目搭建是@/components/index.js负责注册全局组件,所以这里在@/components/index.js引入。

// @/components/index.js
import Vue from 'vue'
import '@/components/LoginDialog' //在这引入全局登录弹窗
import SvgIcon from '@/components/SvgIcon.vue'

Vue.component('SvgIcon', SvgIcon)

2. 新建一个实例,挂载到document.body

import Vue from 'vue'
import LoginDialog from './index.vue'

// 这里可以用Vue.extend()创建子类,但是我没打算注册为组件,就直接`new Vue`
const loginDialog = new Vue(LoginDialog) 
loginDialog.$mount(document.createElement('div'))
document.body.appendChild(loginDialog.$el)

Vue.prototype.$loginDialog = loginDialog

export default loginDialog

3. 在路由守卫里访问

直接引入实例

import router from './router'
import store from '@/store'
import loginDialog from '@/components/LoginDialog'

const whiteList = ['/']

router.beforeEach((to, from, next) => {
    if (store.getters.token) next()
    else if (whiteList.indexOf(to.path) > -1) next()
    else loginDialog.open()
})

4. 在页面内访问

<vs-button @click="login" transparent>登录</vs-button>
login() {
    this.$loginDialog.open()
}

image.png

这里就有2个根实例

FAQ

1. 窗口关闭逻辑

弹窗代码

<template>
    <vs-dialog class="login-dialog" auto-width not-padding v-model="active">
        <el-form class="form">
            <vs-input autocomplete="off" label-placeholder="Username" color="rgb(59,222,200)" v-model="form.username">
                <template #icon>
                    <i class="bx bx-user"></i>
                </template>
            </vs-input>

            <vs-input type="password" label-placeholder="Password" color="rgb(59,222,200)" v-model="form.password">
                <template #icon>
                    <i class="bx bx-lock-open-alt"></i>
                </template>
            </vs-input>

            <vs-button block size="large">登 录</vs-button>
        </el-form>
    </vs-dialog>
</template>

<script>
export default {
    data() {
        return {
            active: false,
        }
    },
    methods: {
        open() {
            this.active = true
        },
    }
}
</script>

关闭逻辑就是Vuesax <vs-dialog>自带的关闭,我只关注了窗口打开,关闭是组件自己在处理。结果是,如果不打开,这个组件元素div就不会出现在html文件里;弹出时,就会出现。

2. 为什么不写进<App>,用store控制

考虑这么一个场景,如果这个登录验证需要在项目渲染之前,那要怎么调用这个弹窗?

其实放<App>里面,还可以直接访问this.$store,this.$router之类,但是这样就是不能满足在主项目vue实例渲染之前的处理。而在element-ui里面单独引用的LoadingMessage等等,在项目加载前Loading,期间做一下验证请求。

import { Loading, Message  } from 'element-ui';

3. 为什么要全局

  • 为什么挂载$loginDialog全局

这个是写vue的习惯,常用插件就会直接挂载原型链,平时就this.$someThing使用,这样在别人看你项目时,根据main.js文件能很清楚知道,在项目中用了那些插件,而不是突然出现在某个页面里。当然react不这么认为。这

  • 为什么直接引入Vue操作,而不是MyPlugin.install = function (Vue, options) {}

因为我没想着开发插件,而在我的项目中,Vue是肯定存在且是同一个,有什么options我直接放在实例里就好了。

参考《JavaScript高级程序设计(第4版)》原文 章节(26.4)使用ES6模块

一个页面上有多少个入口模块没有限制,重复加载同一个模块也没有限制。同一个模块无论在一个页面中被加载多少次,也不管它是如何加载的,实际上都只会加载一次

4. 需要登陆的按钮都触发窗口

  1. 用一个vue指令
import Vue from 'vue'
import loginDialog from '@/components/LoginDialog'
import store from '@/store'

Vue.directive('login', {
    bind: function (el) {
        // true能优先触发事件
        el.addEventListener('click', login, true)
    }
})

function login(e) {
    if (!store.getters.token) {
        // 用于取消所有后续事件捕获或事件冒泡,并阻止调用任何后续事件处理程序(DOM3 Events 中新增)
        e.stopImmediatePropagation()
        loginDialog.open()
    }
}
  1. 使用
<header>
    <vs-button v-login @click="other" transparent>其它</vs-button>
    <vs-button @click="login" transparent>登录</vs-button>
</header>
other() {
    console.log('[suni other]')
}

image.png

版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: YYsuni 原文链接:https://juejin.im/post/6960163479512678437

回到顶部