刷新和关闭拦截
beforeunload 事件
触发机制: 浏览器的刷新和关闭按钮被点击,点击跳转路由的按钮 (全局生效)
侦听机制: 页面初始化和卸载时侦听事件
缺点:
- 默认样式,无法改变
- 全局生效
useEffect(()=>{
window.addEventListener('beforeunload', beforeunload);
},[])
useEffect(()=>{
window.addEventListener('beforeunload', beforeunload);
},[])
const beforeunload=(ev)=>{
if (ev) {
ev.returnValue = '';
}
}
返回、前进的弹框拦截
1.react-router-dom中的Prompt组件
触发机制:浏览器的返回和前进按钮被点击(路由发生变化)
缺点: 不能自定义样式
import React from 'react'
import {Prompt } from "react-router-dom"
export default function StopRoute(){
const [isOpen,setIsOpen]=useState(false)
const lanjieReturn=()=>{
(location)=>{
if(!isOpen) {
let leave = window.confirm("您确定要离开该页面吗?")
if(!leave) {
return false
}
}else {
setIsOpen(false)
/* 返回false是拦截的关键 */
return false
}
}
}
return (
<>
/* message是用来显示内容的 when是用来控制拦截的时机的(也就是弹框出现的时机) */
<Prompt message="您确定要离开该页面吗?" when={isOpen} /> //第一种用法 message直接写字符串
<Prompt //第二种用法 message是函数,但是函数内部只能是三木运算,且不用return (其实质也就是用法一的直接写字符串)
message = {() => {
this.state.isOpen? false: "您确定要离开该页面吗?"
}}
<Prompt message = {lanjieReture}/> //第三种用法 mesage是函数,并且不是三目运算,则函数必须返回false,并且不能自定义弹框,因为react的显示必须写在render里面,这里的函数rentuen的是false,不是DOM,所以只能只用js的原生弹框来拦截
</>
)
}
2.useHistory().block
触发机制: 浏览器的返回和前进按钮被点击
缺点:
- 用到的页面,其组件在路由或者在任何地方不能使用高阶组件(也就是不能再被别的组件包裹),否则失效
- 如果用到的页面不是首页,那么进来的页面最好使用push,不能使用replace,否则不好使
import React, {useEffect, useState} from 'react';
import {useHistory} from 'react-router';
import Dialog from '@/components/Dialog'
export default function UserConfirmationTwo(props) {
const {when = false} = props;
const [isShowModal, setIsShowModal] = useState(false);
const history = useHistory();
const [nextLocation, setNextLocation] = useState(null);
const [action, setAction] = useState();
const [unblock, setUnblock] = useState(null);
useEffect(() => {
if (!when || unblock) {
return;
}
const cancel = history.block((nextLocation, action) => {
if (when) {
setIsShowModal(true);
}
setNextLocation(nextLocation);
setAction(action);
return false;
});
setUnblock(() => {
return cancel;
});
}, [when, unblock]);
useEffect(() => {
return () => {
unblock && unblock();
};
}, []);
function onConfirm() {
unblock && unblock();
if (action === 'PUSH') {
history.push(nextLocation);
} else if (action === 'POP') {
history.goBack();
} else if (action === 'REPLACE') {
history.replace(nextLocation);
}
props.closeStopRouteDialog(1);
setIsShowModal(false);
}
function onCancel() {
props.closeStopRouteDialog(0); //点击取消和确定的事件
setIsShowModal(false);
}
return (
<>
{isShowModal && <Modal
title="是否返回?"
width='450'
visible={isShowModal}
confirm={()=>onConfirm()}
close={onCancel}
>
</Modal>}
</>
);
}
3.pushState popState
触发机制:浏览器的返回和前进按钮被点击
缺点: 没缺点了,js原生的
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
*{
margin:0;
padding:0;
}
.menu{
width: 100px;
height: 40px;
font-size: 30px;
line-height: 40px;
text-align: center;
border:1px solid #000000;
float: left;
margin-left: 50px;
user-select: none;
}
p{
clear: both;
display: none;
}
.dialog{
width: 100%;
height: 100%;
background: black;
opacity: 0.5;
position: absolute;
top: 0;
left: 0;
display: none;
}
.container{
width: 500px;
height: 200px;
position: absolute;
z-index: 100;
left:calc(50% - 250px);
top:calc(50% - 100px);
background: #FFFFFF;
padding:24px;
display: flex;
flex-direction: column;
align-items: start;
justify-content: space-between;
}
.title{
color:#000000;
font-size: 30px;
line-height: 30px;
font-weight: 600;;
}
.btn{
}
.btnLeft{
width: 50px;
height: 30px;
background-color:red;
margin-right:395px;;
}
.btnRight{
width: 50px;
height: 30px;
background-color:chartreuse
}
</style>
</head>
<body>
<div class='menu'>水果</div>
<div class='menu'>蔬菜</div>
<div class='menu'>零食</div>
<div class='menu'>饮料</div>
<br>
<br>
<p>猕猴桃
苹果
梨</p>
<p>白菜
土豆
地瓜</p>
<p>辣条
牛肉干
薯片</p>
<p>可乐
雪碧
果汁</p>
<div class='dialog'>
<div class='container'>
<div class='title'>确定要返回吗</div>
<div class="btn">
<button class='btnLeft'>取消</button>
<button class='btnRight'>确定</button>
</div>
</div>
</div>
<script>
var arr,divs;
let dialog=document.querySelector('.dialog')
init()
function init(){
// 当历史前进或者后退时就会收到这个事件
window.onpopstate=popStateHandler;
arr=Array.from(document.getElementsByTagName("p"));
divs=Array.from(document.querySelectorAll(".menu"));
cancel=document.querySelector('.btnLeft')
ensure=document.querySelector('.btnRight')
arr[0].style.display="block";
for(var i=0;i<divs.length;i++){
divs[i].onclick=clickHandler;
}
cancel.addEventListener('click',handleClick)
ensure.addEventListener('click',handleClick)
}
function clickHandler(){
var index=divs.indexOf(this);
// history.pushState({state:1},"","#"+this.innerHTML);
// 在历史记录列表中增加数据,后面的#内容标示当前跳转部分
history.pushState({index:index}, "", document.URL);
changeMenu(index);
}
function popStateHandler(){
console.log(history.state);
// changeMenu(history.state.index)
dialog.style.display='block'
}
function changeMenu(index){
for(var i=0;i<arr.length;i++){
if(i===index){
arr[i].style.display="block";
}else{
arr[i].style.display="none";
}
}
}
function handleClick(e){
if(e.target.innerHTML==='确定'){
dialog.style.display='none'
console.log('点击了确定')
location.href='http://www.baidu.com'
return ;
}
dialog.style.display='none'
console.log('点击了取消')
// window.location.href='http://www.baidu.com'
}
</script>
</body>
</html>