theme: vue-pro highlight: atom-one-light
需要一个登录弹窗,在任何地方都能使用。
需求
- 在任何页面内能访问
- 在路由拦截能访问
解决方案
任何地方都能访问,第一个想到的是挂载原型链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()
}
这里就有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
里面单独引用的Loading
、Message
等等,在项目加载前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. 需要登陆的按钮都触发窗口
- 用一个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()
}
}
- 使用
<header>
<vs-button v-login @click="other" transparent>其它</vs-button>
<vs-button @click="login" transparent>登录</vs-button>
</header>
other() {
console.log('[suni other]')
}
版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: YYsuni 原文链接:https://juejin.im/post/6960163479512678437