赞
踩
uniapp 项目: 使用了uview框架 1.x版本
其中对于组件 u-navbar , 想要进行二次简单封装,把一些固定样式/固定配置等预写好, 同时对默认的返回按钮做一层前置拦截,方便后续扩展.
封装的组件为: navBar
<navBar :title="pageTitle" :isBack="true" :navbarHeight.sync="navbarHeight" :customBack="abc" ></navBar>
这其中, 返回的点击事件我们要自定义的话, 可以通过:customBack
属性来传递 function , 如此处的 abc
而在 navbar
组件内部:
<template> <u-navbar ref='navbarex' id="nmyui" class="navbar" :is-back="isBack" :custom-back="customBackBefore" :title="title" :title-bold="true" title-color="#fff" :title-width="titleWidth" back-icon-color="#fff" :is-fixed="fixed" :border-bottom="border" title-size="36" :background="background" :titleStyle="titleStyle"> <view v-if="$slots.left" class="left"> <slot name="left"></slot> </view> <view v-if="$slots.right" class="right"> <slot name="right"></slot> </view> </u-navbar> </template>
navbar
组件内部的 props之一
// 自定义返回逻辑
customBack: {
type: Function,
default: null
},
我们在组件内部其实就是直接摆放了一个 u-navbar
组件
而在处理对默认返回按钮的前置拦截的时候, 我们其实是给 u-navbar
的属性 :custom-back="customBackBefore"
提供了一个组件内部实现的函数 customBackBefore
这里再说一下,
navbar
是二次封装组件, 其参数有customBack
, 外部会给他传递诸如abc
这种函数
u-nabar
是uview的导航组件,navbar
就是对它的二次封装, 它的自定义返回事件参数名custom-back
, 在本次封装中, 固定给他一个customBackBefore
的处理方法(目的是用来做前置拦截等)
问题就出在 customBackBefore
这里说一句, app端(Android是ok的, iOS没验证,大概率也是ok的)
出问题的是小程序端
先来看报错
第一眼看到function, 又想到小程序, 第一个怀疑的是 小程序的特性对funtion的处理肯定不一样
于是 查到这一篇文章 :
为什么木有人啊??【报Bug】自定义组件模式中子组件props接收不到数组中的function
别的没仔细看, 就看了个这个
好吧, 那就用时间机制, 也就是发个 emit的方式
于是代码做修改
navbar
外部使用:
navbar
:
来看看什么下场…
点了多次返回按钮, 只打印, 没有后续了, 也没报错, 也没触发
于是去查了emit的处理, 有说不能用驼峰命名的, 有说该用 this.$parent.abc()
的
好像两条路都走不通了
好吧, 继续耐着性子排查(期间各种尝试不表.)
总之最后, 看回到
点进去发现:
这时候才开始, 把目光放到了 this
这个罪魁祸首上面
其实上面不管用 this.$props.customBack()
or this.$emit(‘goback’)
, 最大的问题就是在 this
上出了问题
因为平时主要做app端开发, 小程序接触不多, 对于这句话的理解不够深刻
uni-app坑:Props传递Function,该方法里的this指向子组件而不是父组件
这句话怎么理解呢?
举个例子 - (这里写伪代码了)
父组件 component-parent
中,我们设置一个state 叫做 parentTitle:'i am parent'
子组件 component -sub
中,我们设置一个state 叫做 subTitle:'i am sub'
// 父组件
<component-parent>
<component-sub :mylogger="logger"/>
</component-parent>
//父组件方法
logger(){
console.log(this.parentTitle)
console.log(this.subTitle)
}
//子组件实现
<component-sub @tap="mylogger"/>
由于在小程序中, Props传递Function,该方法里的this指向子组件而不是父组件
所以, 其实在父组件的 logger
函数体中, this已经指向了 子组件 component-sub
, 讲白了, 此时的this.就是子组件的this
所以日志会输出
undefined
i am sub
也就是说在父组件的 logger
方法中, 能拿到 this.subTitle
, 但是拿不到this.parentTitle
, 因为this的指向以及改变了
由于上面的特性, 我们也看到了uview对于 u-navbar
的处理, 他们为了使用者进行自定义返回的时候避免这个坑,
他们做了如下处理
goBack() {
// 如果自定义了点击返回按钮的函数,则执行,否则执行返回逻辑
if (typeof this.customBack === 'function') {
// 在微信,支付宝等环境(H5正常),会导致父组件定义的customBack()函数体中的this变成子组件的this
// 通过bind()方法,绑定父组件的this,让this.customBack()的this为父组件的上下文
this.customBack.bind(this.$u.$parent.call(this))();
} else {
uni.navigateBack();
}
通过bind的方法,把this的指向又交还给了父组件, 这样作为使用者,就可以肆无忌惮的通过props传递function, 从而实现多种多样的自定义返回处理.
⇒ 爸爸就该有爸爸的样子.jgp
对于瞎玩二次封装的我们来讲, 在 navbar
子组件内部,我们的:custom-back="customBackBefore"
中, customBackBefore
的函数体内部, this到底现在指向了谁??
打印一下
作为子组件的navbar
竟然持有函数 abc
? 那很显然在
methods: {
// 自定义返回-前置拦截
customBackBefore() {
console.log("navbar-customer-back-before")
// this.$props.customBack()
console.log(this)
}
}
此刻的子组件中的 customBackBefore
函数体中, this指向的是父组件的
(当然,在子组件的 mounted的生命周期等其他函数中, this还是指向子组件本身的)
customBackBefore
中this指向的是父组件我的理解:
1.在未做二次封装之前, 我们一般直接使用 uview的u-navbar
, 一旦自定义导航事件函数, uview会帮我们处理, 把 Props传递Function,该方法里的this指向子组件而不是父组件
这个问题内部处理, 使得我们任何的自定义导航事件函数:custom-back="xxxx"
,在xxx
中都能正确的拿回父组件的this
2.在做二次封装的时候, 最终的u-navbar拿到的是page组件的this. 在重新bind的时候, 被navbar组件内部的:custom-back拦截了
这里有好几个方法,不过其中有坑, 我们来分析一下, 选那些合适~~~
先看看navbar组件的customBackBefore
,这里会添加事件的触发
// 自定义返回-前置拦截
customBackBefore() {
console.log("navbar-customer-back-before")
}
}
再看看page 父组件中的abc函数体, 这里通过两个this,来打印确定this的指向.
methods: {
abc() {
// page页的state
console.log(this.pageTitle)
// navbar子组件的dec
console.log(this.dec)
uni.showToast({
title: "1abc2"
})
},
}
// 自定义返回-前置拦截
customBackBefore() {
console.log("navbar-customer-back-before")
// 既然this已经明确指向page父组件, 那显然直接调用this.abc()是可以生效的
this.abc()
}
}
看看日志
该方案直接就躺平了, 就直接拿父组件的函数abc来执行, 日志输出方面也表明了, 在abc函数内部的this指向是page的,大问题没有.缺点是导致组件无法通用化, 和封装的目的相违背 pass
坚持props传funtion -
navbar
子组件中,修正this指向navbar
自己
既然 问题出在this的指向不对,那我们做一下this的指向重置即可
navbar
子组件全局设置一个 that
用来缓存 navbar
的this
在mounted周期函数里赋值,(因为此时的子组件navbar在这个生命周期里,this是指向自身的)
然后再 customBackBefore
函数体重使用即可
不过,这里插一句, 还记得那句话么
uni-app坑:Props传递Function,该方法里的this指向子组件而不是父组件
对于page父组件而言, 我们封装的 navBar
同样会面临abc函数体中,this指向子组件(navbar
)的问题
所以,我们来一各个测试看看this
的指向
that.$props.customBack()
的写法
打印结果
结果显示: this的指向既不指向page父组件, 也不指向navbar子组件… pass
直接的 that.customBack()
的写法
打印结果
结果显示: 调用是成功, 但是 this的指向出现了问题, page父组件的abc函数体中,this指向了navbar
子组件
针对2.2的改进, 用
bind
的方案来解决this
的指向问题
打印结果
结果符合预期, this的指向也正确了 success
当然, paga父组件里也要做一下改造,
打印结果
结果也非常符合预期, this的指向也是正确的指向了父组件~~ success
一圈搞下来, 我们有了两种解决办法
方案2.3 和 方案3
至此,对于
uview(1.x)
的组件u-navbar
的二次封装中遇到的自定义事件的处理, 也算完成.
至于选择 上面哪两种方案?
我从封装的角度出发, 我会采用方案2.3
毕竟, 二次封装也是为了后续使用方便, 还是延用u-navbar
的自定义风格,比较合适点~
能看到这的, 都是不嫌弃我文笔凌乱的, 总之~ 谢谢观看, 如果有帮助或启发, 那就是本篇的荣幸~
以上两个方法, 都是基于 在 vue组件 mounted的生命周期里, 让全局变量 that = this
这里面存在一个重大的问题
that永远是只能赋值一次, 且 会被下个navBar组件的创建加载过程, 触发 that指向的覆盖…
这就造成了bug
如: A->B->C
A默认返回 that= A.this
B默认返回 that= B.this
C自定义返回 that=C.this
一圈路由跳转下拉, that最终指向了 C ,
当C返回到B的时候, B的navbar的that 依然是C的this…
就算及时清理that, 也会存在that 无法在B的时候 重新被 that= B.this
(其实也不是没有, 可以利用B的onShow来重新挂载, 但是这样就显得非常麻烦…)
所以… 放弃拦截方案了,
直接把自定义事件透传给 u-tabbar 组件, 等后续我有解决办法了再行尝试
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。