浏览器

1. 跨标签页通讯

不同标签页间的通讯,本质原理就是去运用一些可以 共享的中间介质,因此比较常用的有以下方法:

1:设置同域下共享的localStorage与监听window.onstorage

       重复写入相同的值无法触发
       会受到浏览器隐身模式等的限制

一、localStorage (1)localStorage是什么? localStorage对象在修订过的HTML5规范中作为持久保存在客户端数据的方案取代了globalStorage,是Storage的实例。 注意:要访问一个localStorage对象,页面必须来自同一个域名(子域名无效),使用同一种协议,在同一个端口上。相当于globalStorage[localhost.host]。 (2)localStorage怎么用? localStorage作为Storage的实例,可以使用Storage实例的方法。 

        localStorage.setItem('name','lwf');
        localStorage.age=21;//注意后填加的放在localStorage的前面

        console.log(localStorage.getItem('name'));//lwf
        console.log(localStorage.age);//21
        console.log(localStorage);//Storage {age: "21", name: "lwf", length: 2}
        console.log(localStorage.key(0));//age,注意index从0开始


        console.log(localStorage.length);//2

        localStorage.removeItem('name');
        console.log(localStorage.getItem('name'));//null

        localStorage.clear();
        console.log(localStorage);//Storage {length: 0}

复制代码

(3)怎么使用localStorage实现多标签之间的通信?
index.html:

<input type="text">
    <button id="btn">Click</button>
    <script>
        window.onload=function(){
            var oBtn=document.getElementById("btn");
            var oInput=document.getElementsByTagName("input")[0];
            oBtn.onclick=function(){
                var val=oInput.value;
                localStorage.setItem("value",val);
            }
        }
    </script>
复制代码

test.html:

<script>
        window.addEventListener("storage",function(event){
            console.log("value is"+localStorage.getItem("value"));
            console.log("key is"+event.newValue);
        },false);    
    </script>

复制代码

注意:只能实现同一浏览器相同域名、相同协议、相同端口下的多个标签页之间的通信。不同浏览器没有该效果。

(4)localStorage可以实现同一浏览器多个标签页之间通信的原理 localStorage是Storage对象的实例。对Storage对象进行任何修改,都会在文档上触发storage事件。当通过属性或者setItem()方法保存数据,使用delete操作符或removeItem()删除数据,或者调用clear()方法时,都会发生该事件。这个事件的event对象有以下属性:

  • domin:发生变化的存储空间的域名;
  • key:设置或者删除的键名;
  • newValue如果是设置值,则为新值;如果是删除值,则是null;
  • oldValue:键被更改之前的值;

2:设置共享cookie与不断轮询脏检查(setInterval)

(1)cookie是什么? HTTP Cookie,通常直接叫做cookie,最初是在客户端用于存储回话信息的。该标准要求服务器对任意HTTP请求发送Set-Cookie HTTP头作为响应的一部分,其中包含回话信息。浏览器会存储这样的回话信息,并在这之后,通过每个请求添加Cookie HTTP头将信息发回服务器。

(2)cookie怎么用?
在JavaScript中,cookie的接口即document.cookie不太友好,可以自己封装相应的接口。
基本cookie操作:读取、写入、删除

//代码来源于JavaScript高级程序设计
var CookieUtil={
            get:function(name){
                var cookieName=encodeURIComponent(name)+"=",
                    cookieStart=document.cookie.indexOf(cookieName),
                    cookieValue=null;
                if(cookieStart>-1){
                    var cookieEnd=document.cookie.indexOf(";",cookieStart);
                    if(cookieEnd==-1){
                        cookieEnd=document.cookie.length;
                    }
                    cookieValue=decodeURIComponent(document.cookie.substring(cookieStart+cookieName.length,cookieEnd));
                }
                return cookieValue;
            },
            set:function(name,value,expires,path,domain,secure){//cookie的构成:名称、值、失效时间(何时应停止向浏览器发送cookie)、路径(向服务器发送cookie的特定域的路径)、域(cookie对于哪个域是有效的)、安全标志(指定后只能在使用SSL连接时才发送到服务器)
                var cookieText=encodeURIComponent(name) + "=" +encodeURIComponent(value);
                if(expires instanceof Date){
                    cookieText += "; expires="+expires.toGMTString();//时间为GMT格式,注意信息之间用“; ”分割
                }
                if(path){
                    cookieText += "; path="+path;
                }
                if(domain){
                    cookieText += "; domain="+domain;
                }
                if(secure){
                    cookieText += "; secure";
                }

                document.cookie = cookieText;
            },
            unset:function(name,path,domain,secure){//没有直接删除cookie的方法
                this.set(name,"",new Date(0),path,domain,secure);//使用相同路径、域、安全选项再次设置cookie,并将失效时间设置为过去的时间
            }
        };

复制代码

使用:

 //设置cookie
        CookieUtil.set("name",'lwf');
        CookieUtil.set("age",21);

        // 读取cookie
        console.log(CookieUtil.get("name"));
        console.log(CookieUtil.get("age"));

        // 删除cookie
        CookieUtil.unset("name");
        CookieUtil.unset("age");
复制代码

(3)怎么使用cookie实现多标签之间的通信?
index.html

<input type="text">
 <button id="btn">Click</button>
    <script>
        window.onload=function(){
            var oBtn=document.getElementById("btn");
            var oInput=document.getElementsByTagName("input")[0];
            oBtn.onclick=function(){
                var val=oInput.value;
               CookieUtil.set("name",val);
               console.log(CookieUtil.get("name"));
            } 
            var cookieUtil={//...}
        }
    <script>
复制代码

test.html

window.onload=function(){
            var  getCookie  = function( keyName){
                var items = [] , json = {};
                var cookie = document.cookie;
                if( cookie.length > 0 ){
                    items = cookie.split(';');
                    for(var i = 0;i < items.length;i++){
                        json[items[i].split('=')[0]] = items[i].split('=')[1] ;
                    }
                    return unescape(json[keyName]);    
                }else{
                    return '';
                }
            }

            setInterval(function(){    
                console.log("name=" + getCookie("name"));    
            }, 10000);    
        }; 
复制代码

4、cookie能实现同一浏览器多个标签页之间通信的原理 cookie的path:一个页面产生的cookie只能被与这个页面的同一目录或者其他子目录下的页面访问。因此,通常把cookie的path设置为一个更高级别的目录,从而使更多的页面共享cookie,实现多页面之间相互通信。 

补充:
path:cookie所在的目录,默认为/,即根目录, 通常用来解决同域下cookie的访问问题
domain :cookie所在的域,默认为请求的地址,通过设置document.domain可以实现跨域访问 

3:通过父页面window.open()和子页面postMessage

    
异步下,通过 window.open('about: blank')tab.location.href = '*'

postMessage是html5引入的API可以更方便、有效、安全的解决这些问题。postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。

postMessage(data,origin)方法接受两个参数:

  1. data
    :要传递的数据,
    html5规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify()方法对对象参数序列化,在低版本IE中引用json2.js可以实现类似效果。

  2. origin
    :字符串参数,指明目标窗口的源,
    协议+主机+端口号[+URL],URL会被忽略,所以可以不写,这个参数是为了安全考虑,someWindow.postMessage()方法只会在someWindow所在的源(
    url的protocol, host, port
    )和指定源一致时才会成功触发message event,当然如果愿意也可以将参数设置为"*",someWindow可以在任意源,如果要指定和当前窗口同源的话设置为"/"。

MessageEvent的属性:

data:顾名思义,是传递来的message
source:发送消息的窗口对象
origin:发送消息窗口的源(协议+主机+端口号)


同域父子页面间通讯:

父页面a.html:

//> localhost:9011/a.html
<h1 class="header">page A</h1>
<div class="mb20">
    <textarea name="ta" id="data" cols="30" rows="5">hello world</textarea>
    <button style="font-size:20px;" onclick="send()">post message</button>
</div>
<!-- 不跨域的情况 -->
<iframe src="b.html" id="child" style="display: block; border: 1px dashed #ccc; height: 300px;"></iframe>

<script>
function send() {
    var data = document.querySelector('#data').value;

    // 注意: 只会触发当前window对象的message事件
    // 也可以访问子页面的window对象,触发子页面的message事件 (window.frames[0].postMessage(...))
    // window.postMessage(data, '/'); 
   // data = {name: 'sandy', age: 20, fav: {sing: true, shop: false}}; // 也可以传普通对象
    window.frames[0].postMessage(data, '/'); // 触发同域子页面的message事件
    //window.frames[0].postMessage(data, 'http://localhost:9022/'); // 触发跨域子页面的messag事件
}

// 当前页面执行 window.postMessage(..)
// 或
// 子页面执行 parent.postMessage(...) 都会触发下面的回调, messageEvent.source不同而已
window.addEventListener('message', function(messageEvent) {
    var data = messageEvent.data;// messageEvent: {source, currentTarget, data}
    console.info('message from child:', data);
}, false);
</script>
复制代码

子页面b.html

//> localhost:9011/b.html
<h1 class="header">page B</h1>

<input type="text" id="inp" value="some contents..">
<button onclick="send()">send</button>

<script>
window.addEventListener('message', function(ev) {
    // if (ev.source !== window.parent) {return;}
    var data = ev.data;
    console.info('message from parent:', data);
}, false);

function send() {
    var data = document.querySelector('#inp').value;
    // window.postMessage(data, '*'); // 触发当前页面的message事件
    parent.postMessage(data, '*'); // 触发父页面的message事件
    // parent.postMessage(data, 'http://localhost:9011/'); // 若父页面的域名和指定的不一致,则postMessage失败
}
</script>
复制代码

跨域父子页面间通讯:

父页面a.html:

//> localhost:9011/a.html
<h1 class="header">page A</h1>
<div class="mb20">
    <textarea name="ta" id="data" cols="30" rows="5">hello world</textarea>
    <button style="font-size:20px;" onclick="send()">post message</button>
</div>
<!-- 跨域的情况 -->
<iframe src="http://localhost:9022/b.html" id="child" style="display: block; border: 1px dashed #ccc; height: 300px;"></iframe>

<script>
function send() {
    var data = document.querySelector('#data').value;

    window.frames[0].postMessage(data, 'http://localhost:9022/'); // 触发跨域子页面的messag事件
}

window.addEventListener('message', function(messageEvent) {
    var data = messageEvent.data; 
    console.info('message from child:', data);
}, false);
</script>
复制代码

子页面b.html

//> localhost:9022/b.html
<h1 class="header">page B</h1>

<input type="text" id="inp" value="some contents..">
<button onclick="send()">send</button>

<script>
window.addEventListener('message', function(ev) {
    // if (ev.source !== window.parent) {return;}
    var data = ev.data;
    console.info('message from parent:', data);
}, false);

function send() {
    var data = document.querySelector('#inp').value;
    parent.postMessage(data, 'http://localhost:9011/'); // 若父页面的域名和指定的不一致,则postMessage失败
    // parent.postMessage(data, '*'); // 触发父页面的message事件
}
</script>
复制代码

4:借助服务端或者中间层实现

2. 浏览器架构

  • 用户界面
  • 主进程
  • 内核

      渲染引擎

      js 引擎

               执行栈

     事件触发线程

               消息队列

                          微任务

                           宏任务

     网络异步线程

     定时器线程

3. 浏览器下事件循环(Event Loop)

事件循环是指: 执行一个宏任务,然后执行清空微任务列表,循环再执行宏任务,再清微任务列表

  • 微任务 microtask(jobs): promise / ajax / Object.observe(该方法已废弃)
  • 宏任务 macrotask(task): setTimout / script / IO / UI Rendering

4. 从输入 url 到展示的过程

  • DNS 解析
  • TCP 三次握手
  • 发送请求,分析 url,设置请求报文(头,主体)
  • 服务器返回请求的文件 (html)
  • 浏览器渲染

      1.HTML parser --> DOM Tree

            标记化算法,进行元素状态的标记 

            dom 树构建

      2.CSS parser --> Style Tree

             解析 css 代码,生成样式树

     3.attachment --> Render Tree

        结合 dom树 与 style树,生成渲染树

     4.layout: 布局

      5.GPU painting: 像素绘制页面

5. 重绘与回流

当元素的样式发生变化时,浏览器需要触发更新,重新绘制元素。这个过程中,有两种类型的操作,即重绘与回流。

  • 重绘(repaint): 当元素样式的改变不影响布局时,浏览器将使用重绘对元素进行更新,此时由于只需要UI层面的重新像素绘制,因此 损耗较少
  • 回流(reflow): 当元素的尺寸、结构或触发某些属性时,浏览器会重新渲染页面,称为回流。此时,浏览器需要重新经过计算,计算后还需要重新页面布局,因此是较重的操作。会触发回流的操作:
页面初次渲染 浏览器窗口大小改变 元素尺寸、位置、内容发生改变 元素字体大小变化 添加或者删除可见的 dom 元素 激活 CSS 伪类(例如::hover) 查询某些属性或调用某些方法
  • clientWidth、clientHeight、clientTop、clientLeft
  • offsetWidth、offsetHeight、offsetTop、offsetLeft
  • scrollWidth、scrollHeight、scrollTop、scrollLeft
  • getComputedStyle()
  • getBoundingClientRect()
  • scrollTo()

回流必定触发重绘,重绘不一定触发回流。重绘的开销较小,回流的代价较高。

最佳实践:

css

  • 避免使用table布局
  • 将动画效果应用到position属性为absolutefixed的元素上

javascript

  • 避免频繁操作样式,可汇总后统一 一次修改
  • 尽量使用class进行样式修改
  • 减少dom的增删次数,可使用 字符串 或者 documentFragment 一次性插入
  • 极限优化时,修改样式可将其display: none后修改
  • 避免多次触发上面提到的那些会触发回流的方法,可以的话尽量用 变量存住

6. 存储

我们经常需要对业务中的一些数据进行存储,通常可以分为 短暂性存储 和 持久性储存。

  • 短暂性的时候,我们只需要将数据存在内存中,只在运行时可用

  • 持久性存储,可以分为 浏览器端 与 服务器端

浏览器:
  • cookie: 通常用于存储用户身份,登录状态等
    • http 中自动携带, 体积上限为 4K, 可自行设置过期时间
  • localStorage / sessionStorage: 长久储存/窗口关闭删除, 体积限制为 4~5M
  • indexDB
服务器:
  • 分布式缓存 redis
  • 数据库


转载于:https://juejin.im/post/5c81dbb1e51d4539e10d84dc

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值