缘由
在uniapp开发的小程序项目中需要用到登录模块,不像别的系统一样是页面级别的login,而是类似弹窗的组件。
- 封装login组件,在每个需要鉴权的页面/组件手动引入该组件,然后根据逻辑去判断显示与否, 这种方法能实现,但是感觉很笨,pass
- 封装login组件,在每个需要鉴权的页面,直接通过this.就可以调用出对应的login弹窗,类似this.$message,支持回调success,也可以通过async,await的方式接收对应的登录结果
采用2方式引发的问题
-
研究了vue的component api的实现方法,其实就是利用vue.extend(具体可自行查阅相关文档)去扩展一个子类,在调用的时候创建子类,并通过document.body.appendChild去添加对应的dom节点,然后调用子类的销毁回调,并移除dom上节点....
-
将上述思路,直接在uniapp上实现,因为uniapp用的vue语法以及小程序标签,类小程序api,所以以为也行得通,但是最后运行发现,document报错!!!才反应过来小程序并没有document,运行在h5就没问题
-
查阅相关文档,可以使用vue-inset-loader曲线完成目标
step1
安装依赖: npm install vue-inset-loader
#vue.config.js添加loader处理
const path = require('path')
module.exports = {
configureWebpack: {
module: {
rules: [{
test: /\.vue$/,
use: {
loader: path.resolve(__dirname, "./node_modules/vue-inset-loader")
},
}]
},
}
}
# 在pages.json添加配置
"insetLoader": {
//配置
"config": {
//将需要引入的组件名起了个confirm的名字在下面label中使用
//右侧"<login ref='confirm' />"为需要插入的组件标签
"login": "<login ref='login' />"
},
// 全局配置
//需要挂在的组件名
"label": ["login"],
//根元素的标签类型 也就是插入到页面哪个根元素下默认为div 但是uniapp中需要写为view
"rootEle": "view"
}
#main.js 挂载全局组件
import Login from '@/components/login/login.vue'
Vue.component('login', Login)
(其实到这里,你打开页面就会发现,login被挂载到每一个页面级别的组件中,component组件里面就不会挂载,使用了这个loader,配置了那么多东西,也就是给你省了在每个页面添加,但是也给一些没有必要挂在login的页面,挂载上去了...)
step2
login组件
isShow: false // 控制组件的显示与否,默认false
showLogin (options) {
return new Promise((resolve, reject) => {
const defaultOptions = {
success: null, // 成功后的回调
fail: null // 失败后的回调
}
this.options = Object.assign({}, defaultOptions, options)
this.loginShow = true // 显示
// 处理成这样 是为了兼容success回调/async await的写法
this.options.success = this.options.success || function successResolve () {
resolve(true)
}
this.options.fail = this.options.fail || function failResolve () {
reject(new Error('登录失败'))
}
})
},
// 成功的处理函数
loginSuccess () {
const confirmHandler = this.options.success
if (confirmHandler && typeof confirmHandler === 'function') {
uni.showToast({
title: '登录成功',
duration: 1000
})
this.loginShow = false
// 如果传入success就直接执行回调,如果没传,就resolve(true),外部await后同步执行后续逻辑
confirmHandler()
}
}
main.js
// 全局挂载该方法
Vue.prototype.$showLogin = function (that, options) {
const currentCase = seekLogin(that)
// 将当前页面级别的实例传进来
return currentCase.$refs.login.showLogin(options)
}
// 往上遍历找出页面级别实例,因为只有页面级才有login组件
function seekLogin (currentCase) {
if (!currentCase.$refs.login) {
return seekLogin(currentCase.$parent)
}
return currentCase
}
调用
// 调用一,this为当前实例
await this.$showLogin(this)
// ...成功登录后续逻辑
// 调用二
this.$showLogin(this, {
success: function () {
console.log('成功登录后续逻辑')
},
fail: function () {
console.log('登录失败后续逻辑')
}
)
总结
就是为了强行用this.$的方式去使用login,但是这样的写法,容易拓展,后续的页面开发就可以不用关心login的引入,成功登录父子组件通信的问题; 缺点也很明显,在每个页面级都会引入该组件