引言
有一些 H5 开发者耳熟能详的问题。
比如,老板会问你:“你这个 H5 页面为什么这么卡?为啥 IOS 那么流畅?”,
再比如,业务方会问你:“为什么这个功能你做不了?人家 Native 可以做?”,
再再比如,交互甚至也会经常挑战你:“为什么安卓能做到这样的交互效果,你不可以?”。
我只想说,作为 H5 开发,我们是有苦衷的 =,=
心痛的简直无法呼吸。
这个问题确实困扰了挺久
H5 页面中,浏览器/安卓 的 back(返回) 键,默认会直接回退到上一个页面,这听起来似乎十分正常。
但是对于一个以 Web App 为导向的产品来讲,当前习惯 APP 交互操作的用户的第一反应应该会是:如果当前页面有浮层的话,先解散浮层(不管你承不承认,事实如此)。
话不多说直接上图:
点击浏览器回退直接。。。 体验不够好!!!
说实话,关于这个交互的优化,一开始我是拒绝的。
浏览器默认行为!你要我怎样?我即便是改了,有很多副作用你知道吗?history
你能随随便便玩得起?
直到某天灵(kan)机(dao)一(jing)动(pin),有(bei)了(da)头(lian)绪(le)。
我觉得不做不行了
一样的套路,要把大象装冰箱,总共分几步?
第 1 步:浮层被点击唤起时,为 URL Push 一个 Hash 值(要唯一);
第 2 步:浮层被用户通过自定义关闭按钮解散时,主动去掉对应 Hash;
第 3 步:监听popstate
事件,用户点击浏览器/安卓回退按钮时,解散对应浮层(通过唯一 Hash 值匹配),此时 Hash 值会被浏览器默认 pop 掉,这也是点击回退之后浏览器并不回到前一个页面的关键!!!因为正常的路由并没有变化!!!
Done~
现在看起来就像那么回事了:
当然,还有复杂一点的,嵌套浮层的 case 需要考虑。不过这时候我觉得已经触及我文字表达能力的盲区了 =,=
上代码:
这是我封装好的一个 React 组件 Enback
的代码:
// Enback component
import React from 'react';
import PropTypes from 'prop-types';
let hashStr = location.hash;
class Enback extends React.Component {
static defaultProps = {
uniqueKey: 'newPop'
}
static propTypes = {
uniqueKey: PropTypes.string
}
constructor(props) {
super(props);
}
componentDidMount = () => {
const { uniqueKey, popStateCallback } = this.props;
// why handle history twice? https://stackoverflow.com/questions/11092736/window-onpopstate-event-state-null
if (location.hash) {
history.replaceState({ 'uniqueKey': uniqueKey }, null, `${location.hash}`);
history.pushState({ 'uniqueKey': uniqueKey }, null, `${location.hash}/${uniqueKey}`);
} else {
history.replaceState({ 'uniqueKey': uniqueKey }, null, `#`);
history.pushState({ 'uniqueKey': uniqueKey }, null, `#${uniqueKey}`);
}
hashStr = location.hash;
window.addEventListener('popstate', function(e) {
if (e.state && e.state.uniqueKey === uniqueKey) {
popStateCallback(uniqueKey);
hashStr = location.hash;
}
});
}
truncate(arr) {
var newArr = arr.join().split(',');
newArr.pop();
return newArr;
}
componentWillUnmount = () => {
this.setState = () => {
return;
};
const { uniqueKey } = this.props;
if (hashStr.indexOf('/') !== -1) {
history.replaceState(null, null, this.truncate(hashStr.split('/')));
} else {
if (uniqueKey === hashStr.split('').slice(1).join('')) {
history.replaceState(null, null, '#');
}
}
hashStr = location.hash;
}
render() {
return (
{
React.Children.map(
this.props.children,
child =>
{ child }
)
}
);
}
}
export default Enback;
这是用法:
// Useage
import Enback from '../../enback';
export default () => (
{/* here to hide your popup*/} }
>
This is a popup
);
不玩 React?没关系,这里有原生的做法:github.com/zhaoqize/bl…
BTW, 副作用其实还是有的。你们猜猜是啥?
At last
谢谢捧场,欢迎 issue 讨论,随手点个 Star 更好 →_→ GitRepo。