同源页面间的跨页面通信
BroadCast Channel
BroadCast Channel
可以用来创建一个用于广播的通信频道,所有同源页面都可以共享/监听(广播)频道,当其中某一个页面通过它发送发送的消息会被其他所有页面收到
创建
使用构造函数进行创建
const bc = new BroadcastChannel("demo")
接收一个string作为name标识channel,传入相同name使用同一个频道,可通过实例的.name
属性获得
console.log(bc.name)
发送信息
通过调用实例上的postMessage
方法发送消息
//可以传递一个对象用于发送消息
bc.postMessage({
channelName: "bc"
})
bc.addEventListener("postMessage", {})
监听信息
bc.onmessage = function(e) {
console.log("receive", e.data.channelName)
}
bc.addEventListener("message", e => {
console.log("receive", e.data.channelName)
})
对于错误也可以绑定监听
bc.onmessageerror = function(e) {
console.warn("error", e)
}
关闭
1、第一种:取消或修改相应的massge
事件监听
2、第二种:使用Broadcast Channel 实例提供的close
方法
bc.close();
取消message监听只是让页面不对广播消息进行响应,Broadcast Channel依然存在。
调用close方法会切断与Broadcast Channel的连接,浏览器能尝试回收该对象。
在关闭后调用postMessage会报错
Service Worker
长期运行在后台的一个worker,能够实现页面的双向通信。多页面共享间的Service Worker可以共享,将Server Worker作为消息的处理中心(中央站)即可实现广播效果
LocalStorage-StorageEvent
当LocalStorage
变化时,会触发storage
事件。可利用这个特性,在发送消息时,把消息写入到某个LocalStorage中,然后在各个页面内通过监听storage事件即可收到通知
window.addEventListener('storage', (e) => {
//console.log(e);
if(e.key === 'test'){
const data = e.newValue;
}
})
在各个页面添加以上代码,即可监听到LocalStorage的变化
注意:storage事件只有在值真正发生变化时才会触发
window.localStorage.setItem("test", 124);
window.localStorage.setItem("test", 124);
第二次不会触发storage事件,因为传入的值与第一次一样都是124
可以通过添加一个当前毫秒的事件戳来触发
madata.st = +(new Data);
window.localStorage.setItem("test", JSON.stringify(mydata))
url传参
js中url传参
window.location
属性 等同于 document.location
代码 | 操作 | 输出结果 |
---|---|---|
window.location.href | 获取整个url字符串 | |
window.location.hash | 获取#号后面的字符串 | #/lingshoustatis/?starttime=2018&endtime=2019 |
window.location.search | 获取href属性中?后的部分 | ?starttime=2018&endtime=2019 |
window.location.protocal | 获取url协议部分 | http: |
window.location.port | 获取端口号 | 8000 |
window.location.pathname | 获取对象指定文件名或路径 | /bui/ |
window.location.host | 获取location或URL的hostname和port号码 | localhost:8000 |
decodeURIComponent()
函数 是对url 进行解析unescape()
函数可对通过escape()
编码的字符串进行解码。- location的8个属性都是可读写的,但是只有href与hash的写才有意义,href会重新定位到一个URL,hash会跳到当前页面中的anchor(
<a id="name">
或者<div id="name">
)名字的标记处(如果有),而且页面不会被重新加载。
decodeURIComponent(window.location.hash.match(/pageFrom=(\w+)/g)).match(/=(\w+)/)
vue中路由传参
vue2与vue3路由传参写法差别不大
route
获取到的是浏览器当前访问的路由信息对象,包含了当前路由的路劲、名称、参数、原信息等数据router
获取到的是整个系统总的路由实例对象,包含了路由的跳转、导航守卫等方法
//vue2
this.$router
this.$route
//vue3
import { useRouter, useRoute } from 'vue-router';
const router = useRouter();
const route = useRouter();
-
动态路由传参
getDescribe(id){ this.$router.push({ path: `/describe/${id}` }) }
对应路由配置
{ path: '/describe/:id', name: 'Describe', component: Describe }
获取路由传参
this.$route.params.id
-
name匹配路由-params传参
this.$router.push({ name: 'Describe', params: { id: id } })
对应路由配置: 这里可以添加
:/id
也可以不添加,添加数据会在url后面显示,不添加数据就不会显示{ path: '/describe', name: 'Describe', component: Describe }
获取传参
this.$route.params.id
-
path匹配路由-query传参
query传递的参数会显示在url后面
?id=?
this.$router.push({ path: '/describe', query: { id: id } })
对应路由配置:
{ path: '/describe', name: 'Describe', component: Describe }
获取参数
this.$route.query.id
注意:后两种方式传参时,参数需要以字段的形式加入到对象大括号 {}
中
react中路由传参
类组件
-
params传参
- 刷新页面后参数不会消失
- 参数会在地址栏中显示
- 需要在
Route
中配置参数名称 - 缺点:传值太多不方便,且url会变很长;只能传递字符串,无法传递对象,解决方法:将json对象 -> 字符串传递,接收后将字符串 -> json对象
JSON.stringify()
JSON.parse()
路由配置页面
<Route path="/Capacity/view/:Id" component={production} />
link跳转页面,并传参
{ render: (value, record) => { const { id } = record || {}; // recoed 就是选中行的信息 ,解构赋值id ,拼接给跳转的路由 return <Link to={`/Capacity/view/${id}`}>{value}</Link>; } //或者<Link to={{pathname: '/Capacity/view/' + '121212'}}>跳转</Link> } this.props.history.push(`/Capacity/view/${id}`)
页面接收参数
export default class UserPage extends React.Component{ constructor(props) { super(props); this.state = { id: this.props?.match?.params?.id } } async componentDidMount() { this.setState({ id: this.props?.match?.params?.id }); await this.findDetail(this.state.id) } render() { return(<div>this.props.match.params.id</div>) } }
-
query传参
- 路由不需要配置
- 刷新页面参数丢失
- 参数为明文传递
- 不推荐
页面传参
<Link to={{pathname:'/production',query:{productionId:120,productionType:'fruits'}}}>跳转</Link> //使用js传参 this.props.router.push({pathname:'/production',query:{productionId:120,productionType:'fruits'}}); //或者 hashHistory.push({pathname:'/production',query:{productionId:120,productionType:'fruits'}}); //this.props.history.push()
获取参数
var { productionId } = this.props.location.query
-
state传参
- 路由不需要配置
- 刷新页面参数不消失,参数不会在地址栏中显示
页面传参
var data = {id:3,name:sam,age:36}; var path = { pathname:'/user', state:data, } <Link to={path}>用户</Link> hashHistory.push(path); //this.props.history.push(path) this.props.router.push(path);
获取参数
var { productionId } = this.props.location.state
-
search传参 ?后面传递的参数
- 地址栏显示参数
- 路由不需要配置
页面传参
<link to="web/departManange?tenantId=12121212">xxx</Link> //或者 this.props.history.push({pathname:"/web/departManange?tenantId" + row.tenantId});
获取参数
this.props.location.search
函数组件
-
路由参数 url params在/之后的参数
路由跳转
import {Link,NavLink} from 'react-router-dom' import {useNavigate} from 'react-router-dom'; <Link to="/login/123">跳转到登录</Link> const navigate = useNavigate(); navigate("/login/123");
配置路由
<Route path="login/:code" element={<Login />}></Route> <Route path="login" element={<Login/>}/>
获取路由参数
import { useParams } from 'react-router-dom' export default function Login() { const {code} = useParams(); return <div>{code}</div> }
-
路由参数 search params 在?后面的参数
路由跳转
navigate("/login?a=10&b=20");
接收参数
import { useSearchParams } from 'react-router-dom' const [searchParams, setSearchParams] = useSearchParams(); //第一种方式 const 变量名 = searchParams.get("键名"); //第二种方式 const items =searchParams.entries()/values()/keys(); for(let item of items){ }
IndexedDB/cookie
使用一些全局性(支持跨页面)的存储方案:IndexedDB、cookie
消息发送方:将消息存储到IndexedDB中
接收方:通过轮询去获取最新的消息
Shared Worker
支持两种消息:一种是post
,Shared Worker收到后会将该数据保存下来;另一种是get
,Shared Worker收到该消息后会将保存的数据通过postMessage
传给注册它的页面
Shared Worker无法主动通知所有页面,会使用轮询的方式,拉取最新的数据
window.open
+ window.opener
使用window.open()
打开页面时,该方法会返回一个被打开的页面的window引用,而在未显示指定noopener时,被打开的页面可以通过window.opener
获取到打开它的页面的引用——将页面之间建立起了一种树形结构的连接
1、将所有window.open
打开的页面的window对象收集起来
let childWins = [];
document.getElementById('btn').addEventListener('click', function(){
const win = window.open('./some/sample');
childWins.push(win);
})
2、发送消息时,页面需要同时通知它打开的所有页面与打开它的页面
// 过滤掉已经关闭的窗口
childWins = childWins.filter(w => !w.closed);
if (childWins.length > 0) {
mydata.fromOpenner = false;
childWins.forEach(w => w.postMessage(mydata));
}
if (window.opener && !window.opener.closed) {
mydata.fromOpenner = true;
window.opener.postMessage(mydata);
}
3、消息接收方,需要展示数据,还需要将消息再传递给它所知道的页面(打开它与被它打开)
注意:需要避免消息回传给发送方,防止消息在两者之间形成死循环传递
window.addEventListener('message', function (e) {
const data = e.data;
const text = '[receive] ' + data.msg + ' —— tab ' + data.from;
// 避免消息回传
if (window.opener && !window.opener.closed && data.fromOpenner) {
window.opener.postMessage(data);
}
// 过滤掉已经关闭的窗口
childWins = childWins.filter(w => !w.closed);
// 避免消息回传
if (childWins && !data.fromOpenner) {
childWins.forEach(w => w.postMessage(data));
}
});
window.open()
用于打开一个新的浏览器窗口或者查找一个已命名的窗口
window.open(URL, name, features, replace)
//新打开一个没有菜单栏、标题栏、工具栏,但是有滚动条、状态栏、地址栏且可伸缩窗口的方法调用如下:
window.open("index.html", "newWindow", "width=1024, height=700, top=0, left=0, titlebar=no, menubar=no, scrollbars=yes, resizable=yes, status=yes, , toolbar=no, location=yes");
URL:可选字符串,声明了要在新窗口显示的文档URL,省略或为空字符串时,新窗口不会显示任何文档
name:可选字符串,为新打开窗口的名字,可以通过改名字获取该窗口对象;也可以是<a>``<form>
标签的target属性_blank
,将在新标签页打开新窗口
features:可选字符串,只要配置了该参数,新窗口将在新窗口打开,是浏览器窗口的特征
replace:可选布尔值,规定装载到窗口的URL是否创建一个历史记录,还是替换
true:替换
false:新创建
window.opener
opener属性:可读可写,可返回对创建该窗口的window对象的引用(父窗口的引用),可使用它的属性和函数
非同源页面间的跨页面通信
iframe
通过嵌入同源iframe
作为桥,将非同源页面通信转换为同源页面通信
由于iframe与父页面之间可以通过指定origin
来忽略同源策略,可以在每个页面中嵌入一个iframe,iframe的src都使用同一个url,因此属于同源页面,在通信上可以复用上面同源页面的各种通信方式
1、需要在页面中监听iframe发来的消息
window.addEventListener('message', function(e){
})
2、当页面需要向其他非同源页面通信时,会先给iframe发送消息
接收两个参数,第二个参数是targetOrigin
,用于指定哪些窗口能接收到消息事件可以设置为iframe的URL
window.frames[0].window.postMessage(mydata, "*");
3、在所有iframe页面中,使用任意一种同源页面的跨页面通信方式传递信息
/** iframe内代码*/
const bc = new BroadcastChannel('demo');
//接收来自页面的消息后,通过BroadcastChannel在所有iframe之间传递
window.addEventListener('message', function(e) {
bc.postMessage(e.data);
})
4、其他iframe页面接收到通知后,将消息同步给所属业务页面
/** iframe内代码*/
// 对于收到的(iframe)广播消息,通知给所属的业务页面
bc.onmessage = function (e) {
window.parent.postMessage(e.data, '*');
};