一、前端需要注意哪些seo

  1. 合理的title/description/keywords(权重逐渐减小,每个页面应该不一样)
  2. 语义化的html代码(容易让搜索引擎理解)
    主要有header aside section article p footer main等等
  3. 重要html代码放到最前面(搜索引擎抓取html是从上到下,有的搜索引擎抓取长度有限制)
  4. 重要的内容不要用js输出(搜索引擎不会执行js获取内容)
  5. 少用iframe
  6. 非装饰图片不用alt
  7. 提高网站速度

    二、从浏览器输入url到页面展示,经历哪些步骤

  8. DNS解析
  9. TCP连接(三次握手)
  10. 发送http请求
  11. 服务器处理请求并返回http报文
  12. 浏览器解析报文,并渲染页面
  13. 断开连接(四次挥手)

    1、浏览器如何DNS解析

    从上到下找,知道找到为止
    1、浏览器缓存
    2、操作系统缓存
    3、路由器缓存
    4、本地DNS服务器递归解析(求助根服务器)

    2、域名空间结构

  14. . 根域名 全世界13个根域名服务器
  15. 一级域名(顶级域) edu gov com org cn
  16. 二级域名 imooc baidu 等
  17. 主机名 www news
    图片.png
    图片.png

    3、OSI七层模型(参考模型)

    1、应用层

  18. 发送数据
    2、表示层
  19. 加密、压缩、转换二进制等
    3、会话层
  20. 判断数据类型 ftp(ASII/二进制)
  21. 是否远程传输
    4、传输层(确认收件人)
  22. 确认传输协议(TCP/UDP)
  23. 确认端口号
    5、网络层(选择哪个快递)
  24. 写入ip地址
  25. 选择传输路径
    6、数据链路层
  26. 打入MAC物理地址(局域网)
    7、物理层
  27. 网线之类,开始传输
打个比方更容易理解
端口:收件人
ip:区域地址
mac:区域中某个邮局
物理层:邮筒(开始传递)

4、与七层参考模型对应的是TCP/IP四层模型

  • 应用层
  • 传输层
  • 网络互联层
  • 网络接口层

    5、TCP/IP三次握手、四次分手

    (1)什么是TCP协议、三次握手

    TCP传输就像打电话,UPD就像发短信
    TCP协议:面向连接的、可靠的传输协议
    图片.png
    为什么是面向连接的、可靠的传输协议

  • 面向连接:当三次握手走通之后,双方要在内存开辟资源,然后app拿到资源,才能发送接收数据
  • 可靠的:三次握手中,都是在内核传输控制层完成,且如果有一方断开,另一方会失败重传,直到对方由回应,所以可靠

    // 应用层
    app=new HttpServer()
    // 传输控制层

    (2)四次分手

    连接占用着资源、端口号,所以要四次分手
    图片.png
    1、我想分手
    2、我知道了
    3、我也想分
    4、我知道了
    5、释放资源

【问题1】为什么连接的时候是三次握手,关闭的时候却是四次握手?

答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。

6、进程process和线程Thread

1、进程:同时进行多个程序
2、线程:一个进程中同时做多个事情
3、栈内存(stack):提供一个执行代码的环境
4、堆内存 存放引用数据(function Array object ...)

(1)GUI渲染线程与JS引擎线程互斥

由于JavaScript是可操纵DOM的,如果在修改这些元素属性同时渲染界面(即JS线程和UI线程同时运行),那么渲染线程前后获得的元素数据就可能不一致

因此为了防止渲染出现不可预期的结果,浏览器设置GUI渲染线程与JS引擎为互斥的关系,当JS引擎执行时GUI线程会被挂起, GUI更新则会被保存在一个队列中等到JS引擎线程空闲时立即被执行。

(2)渲染进程

图片.png
1、解析html
(1)遇到script标签停止解析,执行script标签终的js,如果是外联,等待下载,并且执行完成js
(2) 继续解析html 解析完成 触发 DOMContentLoaded事件


注意: 由于js可以操作css, 一但js操作了css, 那会先解析css,解析完成再执行js,然后再继续解析dom

2、解析css
解析css和解析Html是并行处理的 两者互不相关

(3)defer(延迟执行)和async(异步下载)

image

  • async(异步下载)
    <script async src="script.js"></script>
    async 属性表示异步执行引入的JavaScript,与 defer 的区别在于,如果已经加载好,就会开始执行,无论此刻是 HTML 解析阶段还是 DOMContentLoaded 触发之后。
    需要注意的是,这种方式加载的 JavaScript 依然会阻塞 load 事件;也就是async-script可能在 DOMContentLoaded 触发之前或之后执行,但一定在 load 触发之前执行。
  • defer(延迟执行)
    defer 属性表示延迟执行引入的 JavaScript,即这段 JavaScript 加载时 HTML 并未停止解析,这两个过程是并行的。整个 document 解析完毕且 defer-script 也加载完成之后(这两件事情的顺序无关),会执行所有由 defer-script 加载的 JavaScript 代码,然后触发 DOMContentLoaded 事件。
    async、defer与DOMContentLoaded的执行先后关系
    再谈DOMContentLoaded与渲染阻塞—分析html页面事件与资源加载

最新总结:

onLoad: 所有资源加载完成包含async js/图片/css

onload不会等setTimout

DOMContentLoad:指的是加载、解析完成DOM,包括执行每一个js(包括defer js),和是否渲染到页面没关系

渲染:只要DOM和css完成render就会渲染(边解析边渲染),和js没直接关系(只要它不是在前面阻塞就好)

没有css情况下

DOM解析完成直接渲染到页面,无需等defer js

有css情况下

DOM解析完成 然后css解析完成,才会第一次渲染DOM,此时js(defer js或者放在底部的js)可能还在下载执行过程中,下载执行完成才会 DOMContentLoad,需要说明有css情况,js(包含defer js,不包含async js, 换句话说async会阻塞渲染)也会等css加载完成,才执行

同时如果css放在底部,html会先渲染一次,然后加载css再渲染一次

defer js会在所有执行js后再执行,

是否在setTimeout之后,要看setTimout时间

(4)渲染页面时常见哪些不良现象

由于浏览器的渲染机制不同,在渲染页面时会出现两种常见的不良现象—-白屏问题和FOUS(无样式内容闪烁)

FOUC

由于浏览器渲染机制(比如firefox),再CSS加载之前,先呈现了HTML(坑爹,css还没执行完就开始了第一次渲染),就会导致展示出无样式内容,然后样式突然呈现的现象;

白屏
  • 有些浏览器渲染机制(比如chrome)要先构建DOM树和CSSOM树,构建完成后再进行渲染,如果CSS部分放在HTML尾部,由于CSS未加载完成,浏览器迟迟未渲染,从而导致白屏;
  • 也可能是把js文件放在头部,脚本会阻塞后面内容的呈现,脚本会阻塞其后组件的下载,出现白屏问题。
其它注意点:
  • css加载不会阻塞DOM树的解析
  • css加载会阻塞DOM树的渲染
  • css加载会阻塞后面js语句的执行
    由于js可能会操作之前的Dom节点和css样式,因此浏览器会维持html中css和js的顺序。所以css会阻塞后面js的执行
  • 当页面只存在css,或者js都在css前面,那么DomContentLoaded不需要等到css加载完毕
  • 当页面里同时存在css和js,并且js在css后面的时候,DomContentLoaded必须等到css和js都加载完毕才触发。
  • 当js线程执行时,渲染线程呈挂起状态,只有当js线程空闲时渲染线程才会执行。所以显示提示和隐藏提示的dom操作都被浏览器记下来了并放在gui渲染线程的任务队列中,但都没有立刻进行渲染,而是在当前函数完成后(js线程已处于空闲状态),进行最终的dom渲染,而我们的用户则基本感受不到这个过程

    function sleep(duration) {
      let start = Date.now();
    
      while (duration + start > Date.now()) {
    
      }
    }
    for (var i = 0; i < 1000000; i++) {
      document.getElementById('box').innerText = i
    }
    document.getElementById('box').innerText = 'haha'
    
    new Promise((resolve) => {
      sleep(5000)
      resolve()
    }).then(() => {
      document.getElementById('box').innerText = "good"
    })
    setTimeout(()=>{
      document.getElementById('box').innerText = "bad"
    },5000)
    console.log('主执行栈宏任务');
    执行过程
    观察不到浏览器渲染0,1,2,...,哈哈
    直到5s后渲染出good(因为微任务会阻塞渲染)
    再5s后渲染出bad
    image

宏任务微任务和渲染得关系

7、操作系统多任务

单核CPU执行多任务:让每个任务轮流执行,0.01s就切换,感觉像是多任务
多核CPU: 可以多任务,但是任务太多了,操作系统也会轮流执行

8、https和http

图解HTTP之HTTPS详解

过程

  • 访问 https://baidu.com 会自动向443端口发请求(只要是https协议,就向443端口发请求,即使你指定了端口号也不行)
  • SSL

    目前,应用最广泛的是TLS 1.0,接下来是SSL 3.0。但是,主流浏览器都已经实现了TLS 1.2的支持。

TLS 1.0通常被标示为SSL 3.1,TLS 1.1为SSL 3.2,TLS 1.2为SSL 3.3。

1、在对SSL介绍之前先了解加密方法

主要的加密方法分为两种:

  • 共享密钥加密(对称密钥加密)
  • 公开密钥加密(非对称密钥加密)

(1)共享密钥加密:加密与解密使用同一个密钥
也就是说在加密的同时,也会把密钥发送给对方。在发送密钥过程中可能会造成密钥被窃取
一般所有客户端和服务端用一个密匙k
算法f 密匙k
加密:f1(k,data)=y 解密:f2(k,y)=data

  • 缺点:发送消息会将y和k都发给服务端,黑客可以截取到y和k从而解密
    (2)公开密钥(非对称密钥)
  • 使用一对非对称密钥。一把叫私有密钥,另一把叫公开密钥;
  • 私有密钥不让任何人知道,公有密钥随意发送。

也就是说,发送密文方使用对方的公开密钥进行加密,对方接受到信息后,使用私有密钥进行解密。再不使用私有密钥情况下很难还原信息。
刚开始pk 和 sk都在服务端
f1(pk,data)=y;
f2(sk,y)=data;
f1(sk,data)=y;
f2(pk,y)=data;

  • 缺点:客户端访问时先获取到公匙pk(黑客也知道),然后加密并发送给服务端,服务端通过sk解密,看似完美,但是服务端发送给客户端难办了:通过sk加密,黑客和客户端都可以通过pk解密;通过pk加密,客户端又没有私密
    (3) 非对称+对称
    客户端随机生成一个key,通过非对称加密发送到服务端,服务端解密得到key, 回复客户端ok, 之后的发送信息通过使用key对称加密传输信息,可以避免被黑客攻击
  • 缺点:看似完美,但是还是有缺点(中介人攻击)
    图片.png
    上述问题主要因为客户端不知道拿到的pk是黑客的还是服务端的,因此需要保证pk是从客户端拿到的
    (4)非对称+对称+ca
  • 客户端存有ca大量的公匙信息,也就是知道cpk
  • 客户端想获得服务端得pk但是又不想让黑客拦截,需要服务端从ca机构获取一个证书,这个证书是把服务端pk,用csk加密生成的,也就是f(pk, csk)=license
  • 客户端从服务端请求证书,获取到后可以用spk去解密,获得服务端得pk
  • 然后f(key,pk) = data发送给服务端,服务端用f(data,sk) = key获取到key
  • 接下来双方通过对称加密key, 传输数据

图片.png
这样保证了黑客是无法知道pk的
详细过程确认pk的详细过程
https原理
一文看懂HTTPS、证书机构(CA)、证书、数字签名、私钥、公钥
图片.png

9、浏览器渲染机制

图片.png
步骤:
1、浏览器拿到html资源后,浏览器再内存中开辟一块栈内存(大小由电脑 内存大小决定)给代码提供执行环境,同时分配一个主线程去一行行解析和执行代码(进栈出栈)
2、浏览器碰到link/script/img等请求后会开辟新的线程去加载这些资源文件(这些任务会放到task queue中),主线程不会停下
3、第一次走完主线程会生成DOM tree, 开始查看task queue的任务,取出任务并执行 css执行完生成CSSOM
4、DOM tree和CSSOM会生成render tree
5、render tree 经过回流计算绘制的位置、布局,经过重绘计算绘制的颜色、样式等
6、然后浏览器交给GUI去渲染(display)

10、重绘和回流

1、重绘

元素样式的改变(但宽高、位置、大小不变)
outline visible color background-color等

2、回流

2.1 什么情况下会引起回流

元素大小、位置、布局发生变化,导致需要重新布局(重新计算所有元素的位置)

  • dom增删
  • 元素尺寸发生变化
  • 内容发生变化(文本或不同尺寸图片)
  • 窗口resize

    2.2 如何减少回流

    1、避免操作dom
    vue/react 数据响应,虚拟dom,diff算法能尽可能少的操作dom
    2、样式集中改变

    dom.style.cssText="width:10px;height:20px;..."
    dom.className=".box"

    3、分离读写
    不要把读写写一块,浏览器由渲染队列,样式写一块,浏览器会先加入渲染队列,知道下一个不是样式,才进行一次回流;如果中间断开了会回流多次

    dom.style.width="10px"
    dom.style.height="10px"

    4、元素批量修改
    (1)createDocumentFragment(避免循环appendChild这种)
    (2)模板字符串拼接
    (3)缓存布局信息(和分离读写差不多)
    看上去回流一次,其实是两次,因为碰到了document.clientWidth

    dom.style.width = document.clientWidth+10px
    dom.style.height = document.clientWidth+10px

    改为

    x=document.clientWidth+10px
    y=document.clientWidth+10px
    dom.style.width=x
    dom.style.height=y

    5、动画用absolute fix 可以脱离文档流,不会影响其它布局
    6、css3硬件加速(GPU加速)
    transform filter opacity 不会影响回流重绘,可能会耗损内存
    7、牺牲平滑度换取速度
    比如每秒移动1px, 移动100px 需要100次,会发生100次回流
    但每秒10px只需10次回流
    8、避免table布局,避免使用css的js表达式
    table是一层一层渲染的,会发生多次回流

11、性能优化

(1)减少http请求次数、大小

  • 资源压缩合并(js/css/雪碧图)
  • 图片懒加载(滚动到具体位置时再加载)
  • 音视频走流文件(只加载一部分m3u8)

    (2)DNS Prefetch

    // 浏览器自动解析
    <meta http-equiv="x-dns-prefetch-control" content="on">
    // 手动
    <meta http-equiv="x-dns-prefetch-control" content="off">
    <link rel="dns-prefetch" href="//www.imooc.com">

    其它还有

  • preconnect
    preconnect用于启动预链接,其中包含DNS查找,TCP握手,以及可选的TLS协议,允许浏览器减少潜在的建立连接的开销。

    <link rel="preconnect" href="//example.com">
    <link rel="preconnect" href="//cdn.example.com" crossorigin>
  • prefetch

    <link rel="prefetch" href="//example.com/next-page.html" as="html" crossorigin="use-credentials">
    <link rel="prefetch" href="/library.js" as="script">
  • prerender

    <link rel="prerender" href="//example.com/next-page.html">
  • Preload(css)

    <link rel="preload" href="/styles/other.css" as="style">

    (3)图片懒加载

  • getBoundingClientRect判断图片什么时候出现在可视化区域
  • HTML5 有一个新的 IntersectionObserver API,它可以自动观察元素是否可见
  • IntersectionObserver API 使用教程

    {
      time: 3893.92,
      rootBounds: ClientRect {
        bottom: 920,
        height: 1024,
        left: 0,
        right: 1024,
        top: 0,
        width: 920
      },
      boundingClientRect: ClientRect {
         // ...
      },
      intersectionRect: ClientRect {
        // ...
      },
      intersectionRatio: 0.54,
      target: element
    }
public UNSAFE_componentWillUnmount() {
  // 停止观察
  io.unobserve(element);

  // 关闭观察器
  io.disconnect();
}
public UNSAFE_componentWillMount() {
    this.io = new IntersectionObserver((entries)=>{
      entries.forEach((item)=>{
        if (item.intersectionRatio <= 0) {return};
        const ele = item.target.getElementsByTagName('img')[0];
        if(!ele.src){
          ele.src = ele.getAttribute('property') || '';
          console.log('Loaded new items');
        }
      })
    });
  }

  public ref =(ele:any)=>{
    this.io.observe(ele);
  }

(4)响应式图片

尽可能通过srcset,sizes和<picture>元素使用响应式图片。还可以通过<picture>元素使用WebP格式的图像(体积更小)
响应式图像教程

<h1>不同像素比</h1>
<img
        src="./assets/1x.png"
        srcset="./assets/1x.png 1x,
             ./assets/2x.png 2x,
             ./assets/3x.png 3x"
        alt=""
>
<h1>不同屏幕</h1>
<img
        src="./assets/1x.png"
        srcset="./assets/1x.png 200w,
             ./assets/2x.png 400w,
             ./assets/3x.png 800w"
        sizes="(max-width: 440px) 100vw,
            (max-width: 900px) 33vw,
            254px"
        alt=""
>

<h1>不同屏幕不同像素</h1>
<picture>
    <source srcset="./assets/2x.png,
                      ./assets/3x.png 2x"
            media="(min-width: 990px)">
    <source srcset="./assets/1x.png,
                      ./assets/2x.png 2x"
            media="(min-width: 750px)">
    <img srcset="./assets/1x.png,
                      ./assets/2x.png 2x"
         alt="Shopify Merchant, Corrine Anestopoulos">
</picture>
<h1>
    不同类型图片支持
</h1>
<picture>
    <source type="image/svg+xml" srcset="./assets/1x.png,
                      ./assets/2x.png 2x">
    <source type="image/webp" srcset="./assets/1x.png,
                      ./assets/2x.png 2x">
    <img src="./assets/2x.png" alt="ACME Corp">
</picture>

(5)非核心代码异步加载

  • 异步无阻塞加载JS async deffer
  • 异步加载css

    (6)利用浏览器缓存

  • 强缓存(问都不问,直接拿过来用)(如果以下两个服务器都下发了,以后者为准)
    Expires 过期的绝对时间(服务器下发的,会有偏差)
    Cache-Control 相对时间 3600s内就不请求数据
  • 协商缓存(有缓存,但要问下服务器要不要用)
    Last-Modified 上次修改的时间
    if-Modified-Since
    Etag 哈希值
    If-None-Match
    image

    // node协商缓存
    const http = require('http');
    const querystring = require('querystring');
    let number = 1;
    let date = new Date().getTime();
    let etag = `W/"${date}"`;
    setInterval(() => {
      number++;
      date = new Date().getTime();
      etag = `W/"${date}"`;
    }, 150000);
    
    const server = http.createServer((req, res) => {
      if (req.method === 'POST') {
         
      } else {
          res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:8888');
          res.setHeader('Cache-Control', 'max-age=0');
          res.setHeader('Last-Modified', date);
          // res.setHeader('ETag', etag);
    
          let ifModifiedSince = req.headers['if-modified-since'] *1;//获取
          if (ifModifiedSince >= date) {
              res.statusCode = 304;
              return res.end();//直接返回
          }
    
          res.end(JSON.stringify({
              url: req.url,
              method: req.method,
              data: number
          }));
      }
    });
    server.listen(9000, () => {
      console.log('listening on port 9000 ~');
    });

    (7)http2(基于https)

  • 多路复用(一个连接并发发送多个请求,http1是启用多个连接(6-8个),由数量限制、且资源损耗)
  • 二进制传输(http1文本传输)
  • header压缩(:表示)
  • 后端主动发送response, 而不是前端请求(不需要有请求过程,直接读取 serser push)

    (8) SSR(服务端渲染)

  • 返回的html是js执行玩的代码,不需要再请求资源
  • SSR: next.js nuxt.js
  • SSG: gatsby

    12、performance数据分析

    image

    connectEnd: 1599389439394
    connectStart: 1599389439328
    domComplete: 1599389442115
    domContentLoadedEventEnd: 1599389440900
    domContentLoadedEventStart: 1599389440895
    domInteractive: 1599389440890
    domLoading: 1599389440830
    ​
    domainLookupEnd: 1599389439328
    domainLookupStart: 1599389439317
    fetchStart: 1599389439308
    loadEventEnd: 1599389442128
    loadEventStart: 1599389442115
    navigationStart: 1599389439308
    redirectEnd: 0
    redirectStart: 0
    requestStart: 1599389439394
    responseEnd: 1599389440827
    responseStart: 1599389439426
    secureConnectionStart: 1599389439361
    unloadEventEnd: 0
    ​
    unloadEventStart: 0

    DNS查询耗时 :domainLookupEnd - domainLookupStart

TCP链接耗时 :connectEnd - connectStart

request请求耗时 :responseEnd - responseStart

解析dom树耗时 : domComplete- domInteractive

白屏时间 :domComplete - navigationStart

domready时间 :domContentLoadedEventEnd - navigationStart

onload时间 :loadEventEnd - navigationStart

参考文档

从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理
css加载会造成阻塞吗
嗨,送你一张Web性能优化地图
js更新dom后页面及时渲染问题(js线程阻塞和解决办法)
使用window.performance分析web前端性能
TCP三次握手四次挥手
TCP协议中的三次握手和四次挥手(图解)


lihaixing
463 声望719 粉丝

前端就爱瞎折腾


« 上一篇
React——fiber
下一篇 »
前端算法题