进程和线程

  • 进程,计算机操作系统资源分配的基本单位。进程包含线程
  • 线程由进程管理。
  • 为了提升浏览器的稳定性和安全性,浏览器采用多进程模型

    浏览器的5个进程

浏览器渲染原理和性能优化 - 图1

  • 浏览器进程:负责界面显示、用户交互、子进程管理、提供存储等
  • 渲染进程:每个页卡都是单独的渲染进程,核心用于渲染页面
  • 网络进程:主要处理网络资源加载(html、css、js)
  • GPU进程:3d绘制,提高性能
  • 插件进程:浏览器安装的各种插件

浏览器进程和线程模型,参考资源
图解浏览器工作原理

从输入url到浏览器渲染页面发生了什么

1浏览器进程的相互调用

  • 当用户输入url地址给浏览器,浏览器进程开始工作,进行导航
  • 浏览器进程接到任务后就会调用渲染进程,让渲染进程准备着用于渲染页面
  • 然后浏览器进程还会调用网络进程加载所需要的资源,网络进程将接收到的资源交给渲染进程处理。
  • 渲染显示完毕,进程之间互相通信的过程称为IPC。
  • 期间还会使用到插件进程和GPU进程,这2个进程并不会每次全部使用。

2url请求过程

  • 网络七层模型 (物 数)底层硬件 网络层(IP) 传输层(tcp 分段传输) (会 表 应)http。

image.png

  • 查找缓存,检查缓存是否过期,有缓存直接返回缓存
  • 查询域名是否被解析过,通过DNS协议将域名解析成对应的IP地址( DNS协议基于UDP )
  • 请求如果是https协议,需要SSL协商加密(对称加密、非对称加密)
  • 通过IP地址寻址,排队等待。一个IP地址有可能发送多个http请求,最多同时发生6个http请求
  • TCP层,传输数据,通过三次握手建立连接。tcp传输数据的特点:可靠有序。
  • http请求(包含 请求行、请求头、请求体)
  • keep-alive默认开启,为了下次传输数据时,可以复用上次创建的TCP连接
  • 服务器接收到数据后, 返回不同类型的状态码

3HTTP发展历程

  • http0.9: 负责传输html,没有请求头 和 响应头
  • http1.0: 增加了响应头,可以通过http header的不同解析不同类型的资源
  • http1.1: 默认开启了keep-alive,TCP连接复用。 管线化【同时可以建立6个tcp链接】。服务器无法同时处理多个请求,会造成了队头阻塞。
  • http2: 用一个tcp链接来发送数据,
    • 一个域名只建立一个tcp链接【多路复用
    • 头部压缩,头部header字段可以进行压缩
    • 服务器可以推送数据到客户端
  • http3: 为了解决http2中一个数据包丢失后,要等待后边包发生完成才能继续重发的问题。http3解决tcp的队头阻塞,采用QUIC协议和UDP协议。

    http1实现过程

    ```javascript // 使用 node 模拟 http1 过程 const http = require(“http”); const path = require(“path”); const fs = require(“fs”); const url = require(“url”);

const staticDir = path.join(dirname, “static”); const server = http.createServer((req, res) => { let { pathname } = url.parse(req.url); if (pathname == “/favicon.ico”) return; console.log(pathname); if (pathname === “/“) { pathname = “/index.html”; fs.createReadStream(path.join(dirname, “index.html”)).pipe(res); } else { let reqUlr = path.join(staticDir, “../“, pathname); try { fs.accessSync(reqUlr); fs.createReadStream(reqUlr).pipe(res); } catch (e) { console.log(e, “err”); res.statusCode = 404; res.end(“not found”); } } }); server.listen(3001, () => { console.log(“server listening on 3001”); });

  1. <a name="WR00x"></a>
  2. #### 使用node模拟http2过程
  3. ```javascript
  4. // http2必须依赖https协议
  5. // openssl 生成电子签名
  6. // openssl req -newkey rsa:2048 -nodes -keyout res_private.key -x509 -days 365 -out cert.crt
  7. const http2 = require("http2");
  8. const path = require("path");
  9. const fs = require("fs");
  10. const { HTTP2_HEADER_PATH, HTTP2_HEADER_STATUS } = http2.constants;
  11. const server = http2.createSecureServer({
  12. cert: fs.readFileSync(path.resolve(__dirname, "./cert.crt")),
  13. key: fs.readFileSync(path.resolve(__dirname, "./res_private.key")),
  14. });
  15. const staticDir = path.join(__dirname, "static");
  16. server.on("stream", async (stream, headers) => {
  17. let requestPath = headers[HTTP2_HEADER_PATH];
  18. if (requestPath == "/") {
  19. requestPath = "/index.html";
  20. let dirs = fs.readdirSync(staticDir);
  21. dirs.forEach((dir) => {
  22. let pushPath = path.join(staticDir, dir);
  23. // http2 主动推送资源
  24. stream.pushStream(
  25. { [HTTP2_HEADER_PATH]: "/" + dir },
  26. (err, pushStream) => {
  27. fs.createReadStream(pushPath).pipe(pushStream);
  28. }
  29. );
  30. });
  31. stream.respondWithFile(path.join(__dirname, requestPath), {
  32. "Content-Type": "text/html",
  33. });
  34. } else {
  35. stream.respond({
  36. [HTTP2_HEADER_STATUS]: 404,
  37. });
  38. stream.end("not found");
  39. }
  40. });
  41. server.listen(3002, () => {
  42. console.log("http2 server run port: 3002");
  43. });

4浏览器接收到资源后,渲染流程

image.png

  • 浏览器无法直接使用html,需要将html转化成DOM树.(document)
  • 浏览器无法解析纯文本的css样式,需要对css进行解析,解析成styleSheets,也称CSSOM(document.styleSheets)
  • 计算出DOM树中每个节点的具体样式 (Attachment【复合,连接】的意思)
  • 创建渲染树(布局树),将DOM树的可见节点添加到布局树CSSOM中。计算节点渲染到页面的坐标位置(此过程就是 layout 布局)
  • 根据布局树进行分层(根据定位属性,透明属性,transform属性,clip属性)生成图层树。
  • 将不同图层进行绘制,转交给合成线程处理。最终生成页面,显示到浏览器上【painting,Display】

模拟请求到渲染完成流程

面试题:css为什么放页面header中,js要放在页面底部。 1:css不会阻塞dom解析,只是会阻碍渲染,样式css如果放置到底部,可能会导致重绘 2:js会阻塞dom的解析,也阻塞渲染,影响页面结构的呈现。遇到js脚本时需要暂停dom解析去执行JavaScript,js可能会操作样式和dom结构,所以需要等待样式加载完成后再继续加载js

  1. <head>
  2. <link rel="stylesheet" href="./index.css" />
  3. </head>
  4. <body>
  5. <!-- 浏览器可以部分渲染 -->
  6. <div>123</div>
  7. <!-- css 不会阻塞html解析 -->
  8. <!-- 需要cssom 和 dom tree 生成布局树 -->
  9. <!-- css 会阻塞页面渲染 -->
  10. <!-- parserHTML -> parserStylesheet -> updateLayerTree -> paint -->
  11. </body>

通过浏览器的Performance查看
image.png

  1. <head>
  2. </head>
  3. </head>
  4. <body>
  5. <div>123</div>
  6. <!-- 样式放到底部,可能会造成重绘 -->
  7. <link rel="stylesheet" href="./index.css" />
  8. </body>

通过浏览器的Performance查看
无标题.png

  1. <head>
  2. <link rel="stylesheet" href="./index.css" />
  3. </head>
  4. <body>
  5. <div>123</div>
  6. <script>
  7. let s = 0;
  8. for (let i = 0; i < e * 10; i++) {
  9. s += i
  10. }
  11. </script>
  12. <!-- 遇到js脚本时需要暂停dom解析去执行JavaScript,js可能会操作样式和dom结构,所以需要等待样式加载完成后再继续加载js -->
  13. <div>456</div>
  14. </body>

无标题.png

  1. <head>
  2. <link rel="stylesheet" href="./index.css" />
  3. </head>
  4. <body>
  5. <div>123</div>
  6. <script src="./index.js"></script>
  7. <!-- js和css并行加载,要等待js执行完毕,才能继续解析剩余DOM -->
  8. <div>456</div>
  9. </body>

先ParseHtml然后 ParseStylesheet 然后 layout tree,然后paint。
然后执行js代码,image.png
然后再次parseHtml,再次layout tree及paint
浏览器渲染原理和性能优化 - 图8

MDN文档 - 说明渲染流程

https://developer.mozilla.org/zh-CN/docs/Web/Performance/How_browsers_work#%E6%9E%84%E5%BB%BAdom%E6%A0%91

1基于TCP发送HTTP请求

模拟基于net模块的TCP协议发送http请求,代码实现

请求报文格式

  • 起始行:[方法][空格][请求URL][HTTP版本][换行符]
  • 首部:[首部名称][:][空格][首部内容][换行符]
  • 首部结束:[换行符]
  • 实体:

    响应报文格式

  • 起始行:[HTTP版本][空格][状态码][空格][原因短语][换行符]

  • 首部:[首部名称][:][空格][首部内容][换行符]
  • 首部结束:[换行符]
  • 实体:

    1. const net = require("net");
    2. class HTTPRequest {
    3. constructor(options = {}) {
    4. this.host = options.host;
    5. this.method = options.method || "GET";
    6. this.path = options.path || "/";
    7. this.port = options.port || 80;
    8. this.headers = options.headers;
    9. }
    10. send() {
    11. return new Promise((resolve, reject) => {
    12. // 构建http请求
    13. const rows = [];
    14. rows.push(`${this.method} ${this.path} HTTP/1.1`);
    15. Object.keys(this.headers).forEach((key) => {
    16. rows.push(`${key}: ${this.headers[key]}`);
    17. });
    18. let data = rows.join("\r\n") + "\r\n\r\n";
    19. // 创建tcp链接,传输http数据
    20. let socket = net.createConnection(
    21. {
    22. host: this.host,
    23. port: this.port,
    24. },
    25. () => {
    26. socket.write(data);
    27. }
    28. );
    29. const parser = new HttpParser();
    30. socket.on("data", (chunk) => {
    31. // console.log(chunk.toString());
    32. parser.parse(chunk);
    33. if (parser.result) {
    34. resolve(parser.result);
    35. }
    36. });
    37. });
    38. }
    39. }

    2解析响应结果

    请求到资源后,开始解析收到的服务端返回的资源,代码实现
    接收到的资源包括responseLine、body、header数据

    1. const parser = new HttpParser();
    2. socket.on("data", (chunk) => {
    3. // console.log(chunk.toString());
    4. parser.parse(chunk);
    5. if (parser.result) {
    6. resolve(parser.result);
    7. }
    8. });

    3解析html

    解析html就是对返回的body数据进行解析,使用 htmlparser2 第三方模块进行html解析。
    代码实现
    解析html生成DOMTree结构

    1. const parser = new HtmlParser.Parser({
    2. onopentag(name, attributes) {
    3. // console.log("start", name, attributes);
    4. let parent = DOMTree[DOMTree.length - 1];
    5. let element = {
    6. tagName: name,
    7. attributes: attributes,
    8. children: [],
    9. parent: parent,
    10. };
    11. parent.children.push(element);
    12. DOMTree.push(element);
    13. },
    14. ontext(text) {
    15. // console.log(text);
    16. let parent = DOMTree[DOMTree.length - 1];
    17. let textNode = {
    18. type: "text",
    19. text,
    20. };
    21. parent.children.push(textNode);
    22. },
    23. onclosetag(name) {
    24. let parent = DOMTree[DOMTree.length - 1];
    25. // console.log(name);
    26. if (name === "style") {
    27. parserCss(parent.children[0].text);
    28. }
    29. DOMTree.pop();
    30. },
    31. });
    32. parser.end(body);

    4解析css

    使用第三方模块css,对样式文件进行解析。生成stylesheet结构
    代码实现

    5计算样式

    1. function computedCss(element) {
    2. let attrs = element.attributes; //
    3. element.computedStyle = {};
    4. Object.entries(attrs).forEach(([key, value]) => {
    5. cssRules.forEach((rule) => {
    6. let selector = rule.selectors[0];
    7. if (
    8. (selector == "#" + value && key == "id") ||
    9. (selector == "." + value && key == "class")
    10. ) {
    11. rule.declarations.forEach(({ property, value }) => {
    12. element.computedStyle[property] = value;
    13. });
    14. }
    15. });
    16. });
    17. }

    6布局绘制

    update layout tree 和paint阶段

    1. // 浏览器上计算布局绘制的方法
    2. function layout(element) {
    3. //
    4. if ((Object.keys(element.computedStyle).length |= 0)) {
    5. let { background, width, height, top, left } = element.computedStyle;
    6. let code = `
    7. let canvas = document.createElement('canvas');
    8. canvas.width = window.innerWidth;
    9. canvas.height = window.innerHeight;
    10. let context = canvas.getContext('2d');
    11. context.fillStyle = "${background}";
    12. context.fillRect(${top}, ${left}, ${parseInt(width)}, ${parseInt(height)})
    13. `;
    14. fs.writeFileSync("./code.js", code);
    15. }
    16. }

    总结:DOM如何生成的

  • 通过HTTPRequest请求资源,

  • 服务端返回的类型是text/html 时,浏览器会将收到的数据通过 HTMLParser 进行解析。
  • 在解析前会执行预解析操作,预先加载js、css文件
  • 字节流 -> 分词器 -> tokens -> 根据tokens生成节点 -> 插入到DOM树
  • 遇到js,在解析DOM过程遇到script标签,HTMLParser 会停止,下载执行js脚本
  • 在js执行之前,需要等待当前脚本上的所有css文件加载解析完毕。js是依赖css的加载。
  • css样式文件尽量放在页面头部,css加载不会阻塞DOM Tree解析,浏览器用解析出来的DOMTree和CSSOM进行渲染,不会出现闪动问题。如果css放在底部,浏览器边解析边渲染,渲染出的结果不包含样式,后续会发生重绘操作
  • js文件放在html地步,防止js 的加载、解析、执行阻塞页面的正常渲染。

    Performance API

    页面加载事件流程图

    image.png
    image.png

    关键时间节点

    | 关键节点 | 描述 | 含义 | | —- | —- | —- | | TTFB | time to first byte | 首字节返回的事件 服务器的处理能力 | | TTI | time to Interactive | 到页面可以交互,花费的时长 | | DCL | DOMContentLoaded | DOMContent加载完成的时间 | | FP | First Paint | 首次绘制的时间 | | FCP | First Contentful Paint | 首次Content内容显示 | | FMP | First Meaning Paint | 有意义内容绘制完成的时间 | | LCP | Largest Contentful Paint | 最大Content内容绘制完成 | | FID | First Input Delay | input输入框延时时间 |
  1. <body>
  2. <!-- 需要等待所有的事件执行完毕后才能计算 -->
  3. <div style="background-color: red;width:100px;height:100px;"></div>
  4. <h1 elementtiming="meaningful">关键内容显示</h1>
  5. <script>
  6. window.addEventListener('DOMContentLoaded', function() {
  7. // let s = 0;
  8. // for (let i = 0; i < 100000000; i++) {
  9. // s += i;
  10. // }
  11. // console.log(s)
  12. setTimeout(() => {
  13. document.body.appendChild(document.createTextNode('hello'))
  14. }, 1000);
  15. })
  16. setTimeout(() => {
  17. const {
  18. fetchStart, // 开始访问
  19. requestStart, // 请求的开始
  20. responseStart, // 响应的开始
  21. responseEnd, // 响应的结束
  22. domInteractive, // dom可交互的时间点
  23. domContentLoadedEventEnd, // dom加载完毕 + domcontentloaded完成的事件的事件 $(function(){})
  24. loadEventStart // 所有资源加载完毕
  25. } = performance.timing;
  26. let TTFB = responseStart - requestStart; // 首字节返回的事件 服务器的处理能力
  27. let TTI = domInteractive - fetchStart; // 整个的一个可交互的时长
  28. let DCL = domContentLoadedEventEnd - fetchStart; // DOM 整个加载完毕
  29. let L = loadEventStart - fetchStart; // 所有资源加载完毕所用的时长
  30. console.log(TTFB, TTI, DCL, L);
  31. const paint = performance.getEntriesByType('paint'); // MDN
  32. console.log(paint[0].startTime); // FP 只是画像素了而已
  33. console.log(paint[1].startTime);// FCP 有内容才行
  34. }, 3000);
  35. // FMP first meaningful paint
  36. // 递归 看load的时间不为0 mutationObserver
  37. new PerformanceObserver((entryList,observer)=>{
  38. console.log(entryList.getEntries()[0]);
  39. observer.disconnect(); // 监控完后直接结束即可
  40. }).observe({entryTypes:['element']});
  41. // LCP
  42. new PerformanceObserver((entryList,observer)=>{
  43. entryList = entryList.getEntries();
  44. console.log(entryList[entryList.length - 1],entryList);
  45. observer.disconnect(); // 监控完后直接结束即可
  46. }).observe({entryTypes:['largest-contentful-paint']});
  47. // FID
  48. new PerformanceObserver((entryList,observer)=>{
  49. firstInput = entryList.getEntries()[0];
  50. if(!firstInput) return
  51. FID = firstInput.processingStart - firstInput.startTime;
  52. console.log(FID)
  53. observer.disconnect(); // 监控完后直接结束即可
  54. }).observe({type:['first-input'],buffered:true});
  55. </script>
  56. </body>

网络优化策略

  • 减少http请求次数,合并js、css,使用内嵌css、js
  • 设置服务端缓存,提高服务器处理速度,缓存使用【强缓存、协商缓存】
  • 避免重定向,重定向会降低响应速度(301 、302)
  • 使用dns-prefetch,进行dns 预解析
  • 采用域名分片技术,将资源放到不同的域名下,解决同一个域名最多处理6个tcp链接问题
  • 采用cdn加速访问速度
  • gzip压缩优化,对传输资源进行体积压缩

    1. Content-Encoding: gzip
  • 加载数据优先级preload【预先请求当前页面需要的资源】,prefetch【将来页面中使用的资源】,将数据缓存到http缓存中

    1. // 可以设置preload,进行预先加载,设置加载高优先级
    2. <link rel="preload" href="style.css">

    关键渲染路径【重要】

    浏览器渲染原理和性能优化 - 图11
    image.png

  • 重排【回流】reflow:添加元素、删除元素、修改大小、移动元素位置、获取元素位置相关信息

  • 重绘 repaint:页面中元素样式的改变并不影响它在文档流中的位置。

    强制同步布局问题

    JavaScript强制将计算样式和布局操作提前到当前的任务中 ```javascript function reflow() {

    let el = document.getElementById(‘app’); let node = document.createElement(‘h1’); node.innerHTML = ‘hello’; el.appendChild(node); // 强制同步布局 console.log(app.offsetTop); // 获取位置就会导致 重排 (重新布局)

} window.addEventListener(‘load’, function() { reflow(); });

  1. ```javascript
  2. console.log(app.offsetTop); // 不再触发布局
  3. function reflow() {
  4. let el = document.getElementById('app');
  5. let node = document.createElement('h1');
  6. node.innerHTML = 'hello';
  7. el.appendChild(node);
  8. // console.log(app.offsetTop);// 强制同步布局
  9. }
  10. window.addEventListener('load', function() {
  11. for (let i = 0; i < 100; i++) {
  12. reflow();
  13. }
  14. });

布局抖动(layout thrashing)

  1. const element = document.getElementById('box');
  2. let start;
  3. function step(timestamp) {
  4. if (start === undefined)
  5. start = timestamp;
  6. const elapsed = timestamp - start;
  7. element.style.transform = 'translateX(' + Math.min(0.1 * elapsed, 800) + 'px)';
  8. if (elapsed < 2000) {
  9. window.requestAnimationFrame(step);
  10. }
  11. }
  12. window.requestAnimationFrame(step);
  1. function sleep(d) {
  2. for (var t = Date.now(); Date.now() - t <= d;);
  3. }
  4. const tasks = [
  5. () => {
  6. console.log("task1");
  7. sleep(10);
  8. },
  9. () => {
  10. console.log("task2");
  11. sleep(10);
  12. },
  13. () => {
  14. console.log("task3");
  15. sleep(10);
  16. },
  17. ];
  18. requestIdleCallback(taskLoop, { timeout: 1000 })
  19. function taskLoop(deadline) {
  20. console.log('本帧剩余时间', deadline.timeRemaining());
  21. while ((deadline.timeRemaining() > 1 || deadline.didTimeout) && tasks.length > 0) {
  22. performUnitOfWork();
  23. }
  24. if (tasks.length > 0) {
  25. console.log(`只剩下${deadline.timeRemaining()}ms,时间片到了等待下次空闲时间的调度`);
  26. requestIdleCallback(taskLoop);
  27. }
  28. }
  29. function performUnitOfWork() {
  30. tasks.shift()();
  31. }

减少回流重绘

  • 脱离文档流
  • 渲染时给图片增加固定宽高
  • 尽量使用css3动画
  • 可以使用will-change将元素提取到单独图层中

浏览器的渲染
https://www.w3cplus.com/performance/css-rendering-engine.html

静态文件优化

图片优化

图片格式

  • jpg
  • png
  • gif
  • webp
  • svg

    图片优化

  • 避免空src图片

  • 减小图片尺寸,节约用户流量
  • img标签设置alt属性,提升图片加载失败时的用户体验
  • 原生 loading:lazy 图片懒加载

    1. <img loading="lazy" src="./images/1.jpg" width="300" height="450">
  • 不同环境下采用不同尺寸和像素的图片

  • 对于较大的图片可以考虑采用渐进式图片
  • 采用base64 URL图片,减少请求次数
  • 采用雪碧图合并图标

    html优化

  • 语义化html:代码简洁清晰,利于搜索引擎,便于团队开发

  • 提前声明字符编码,让浏览器快速确定如何渲染网页内容
  • 减少html嵌套关系、减少DOM节点数量
  • 删除多余空格、空行、注释及无用属性
  • html减少 iframes 使用,iframe 会阻塞onload 事件

css优化

  • 降低样式层级嵌套、减少使用通配符
  • 删除多余空格、空行、注释及无用单位,进行css压缩
  • 使用外链css,可以对css进行缓存
  • 添加媒体字段,只加载有效的css文件

    1. <link href="index.css" rel="stylesheet" media="screen and (min-width:1024px)" />
  • 可以使用css的contain属性【组件化思想】,将元素进行隔离,可以节约渲染资源

  • 减少使用@import使用,@import采用的是串行加载

js优化

  • 通过async、defer异步加载js文件。async和defer不阻塞dom解析。async加载完js会立即执行;defer会在最后执行,加载的js有执行顺序要求。

image.png

  • 减少DOM操作,缓存访问过的元素
  • 操作不直接应用到DOM上,应用在虚拟DOM节点,最后一次性应用到DOM
  • 使用webworker解决程序阻塞问题
  • IntersectionObserver ```html

浏览器渲染原理和性能优化 - 图14 浏览器渲染原理和性能优化 - 图15 浏览器渲染原理和性能优化 - 图16 浏览器渲染原理和性能优化 - 图17 浏览器渲染原理和性能优化 - 图18 浏览器渲染原理和性能优化 - 图19 浏览器渲染原理和性能优化 - 图20 浏览器渲染原理和性能优化 - 图21 浏览器渲染原理和性能优化 - 图22 浏览器渲染原理和性能优化 - 图23

  1. <script>
  2. const observer = new IntersectionObserver(function(changes) {
  3. changes.forEach(function(element, index) {
  4. if (element.intersectionRatio > 0) {
  5. observer.unobserve(element.target);
  6. element.target.src = element.target.dataset.src;
  7. }
  8. });
  9. });
  10. function initObserver() {
  11. const listItems = document.querySelectorAll('img');
  12. listItems.forEach(function(item) {
  13. observer.observe(item);
  14. });
  15. }
  16. initObserver();
  17. </script>

  1. - 虚拟滚动 virtual-scroll-list
  2. - requestAnimationFrame requestIdleCallback
  3. - ![Frame生命周期.png](https://cdn.nlark.com/yuque/0/2022/png/737887/1648801676474-691edc77-355e-4ac2-a0c6-ea363e402a1a.png#clientId=u7f409bed-ab7d-4&crop=0&crop=0&crop=1&crop=1&from=ui&id=u29a39433&margin=%5Bobject%20Object%5D&name=Frame%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.png&originHeight=1068&originWidth=2000&originalType=binary&ratio=1&rotation=0&showTitle=false&size=237580&status=done&style=none&taskId=ua5fa5e1c-6230-49de-bd5f-8589efe58b2&title=)
  4. - 避免时eval
  5. - 使用时间委托,减少事件绑定个数
  6. - 尽量使用canvas 动画和css3动画
  7. <a name="ddtOo"></a>
  8. ### 图标优化
  9. 可以使用字体图标
  10. <a name="xiWRs"></a>
  11. ## 核心优化策略
  12. - 关键资源个数越多,首次页面加载时间就越长
  13. - 关键资源的大小,内容越小,下载时间越短
  14. - 优化白屏:内联css和内联js移除文件下载,压缩文件体积
  15. - 预加载,预渲染,打包时进行预渲染
  16. - 使用ssr加速首屏加载,有利于seo
  17. <a name="KgIHO"></a>
  18. ## 浏览器存储
  19. - cookiecookie过期时间内一直有效,存储大小现在4k,同时还限制字段个数,不适合大量的数据存储,每次请求会携带cookie信息,主要用来做身份检查
  20. - 设置cookie有效期
  21. - 根据不同子域 domain 划分 cookie 减少传输
  22. - 静态资源域名和cookie域名采用不同域名,避免静态资源访问时携带的cookie
  23. - localStoragechrome下最大存储5M,除非手动清除,否则一直存在。利用localStorage存储静态资源。
  24. - sessionStorage:会话级别存储,可用于页面间的传值
  25. - indexDB:浏览器本地数据库,可以用于[渲染大列表时的列表数据](https://gitee.com/shenshuai89/webworkers/blob/master/src/worker.js)
  26. <a name="DWYNB"></a>
  27. ## 增加PWA离线缓存(progressive web app)
  28. webapp用户体验差,不能进行离线访问,用户粘性底,pwa就是为了解决这一问题产生的技术,让webapp具有快速、可靠、安全的特点
  29. - web app Mainfest:将网站添加的桌面,类似native的体验
  30. - Service Worker:离线缓存内容,配合cache api
  31. - Push API& Notification Api 消息推送和提醒
  32. - App Shell & App Skeleton:骨架屏
  33. <a name="QUkuf"></a>
  34. ## LightHouse使用
  35. ```javascript
  36. npm install lighthouse -g
  37. lighthouse http://www.taobao.com