Hydra

Hydra blog


  • 首页

  • 归档

输入url会发生什么

发表于 2022-03-14

一次完整的HTTP请求过程

  1. 域名解析

    浏览器会解析url这个域名对应的ip地址

    • 浏览器首先搜索自身的DNS缓存, 看看自身缓存中是否有对应的条目, 如果没有过期, 解析就到此结束.

    • 浏览器自身缓存没有找到对应的条目, 那么chrome浏览器就会搜索操作系统自身的DNS缓存, 如果没有过期则停止搜索, 解析到此结束

    • 浏览器在操作系统中没有找到对应条目, 浏览器会发起一个DNS系统调用, 向本地配置的DNS服务器发起域名解析请求, 这个请求是递归的, 也就是运营商DNS服务器必须给我们提供该IP的地址, 请求过程如下: 运营商DNS服务器首先查找自身的缓存, 如果能在到对应的条目, 且没有过期则解析成功, 如果没有找到对应的条目, 则运营商的DNS代我们的浏览器发送迭代DNS解析请求.

    • 运营商服务器会查找根域服务器的地址, 找到根域的DNS服务, 就会向其发送请求

未命名

发表于 2022-03-14

xss攻击

xss攻击即cross site script, 跨脚本攻击, 缩写是css, 为了和层叠样式表区分, 改成了xss

xss攻击是指攻击者在网站上注入恶意攻击的客户端代码, 通过恶意脚本对客户端网页进行篡改, 从而在用户浏览网页时, 对浏览器进行控制和获取用户隐私数据的一种攻击方式

攻击者对客户端网页注入恶意脚本包括js, 有时也会包含HTML, Flash, 有多种进行xss攻击, 共同特点是: 将一些隐私数据像cookie, session发送给攻击者, 将受害者重定向带一个受攻击者控制的网站, 在受害者机器上进行操作

xss攻击分为: 反射型, 存储型, 基于DOM

反射型(重定向到其他网站)

反射型xss只是简单把用户输入的数据”反射”给浏览器, 这种攻击方式往往需要攻击者诱使用户点击一个恶意连接, 或者提交一个表单, 或者进入一个恶意网站, 注入脚本进入被攻击者的网站

举个例子, 当用户访问某个网站时, 网站会不小心点击到恶意连接, 页面会跳转到攻击者预先准备的网页, 这个时候会执行js的脚本, 可能是恶作剧的脚本, 或者是能获取用户隐私的脚本

存储型(传入恶意代码至服务器)

存储型xss会把用户输入的数据存储在服务端, 当浏览器请求数据时, 脚本从服务器传过来并执行, 这种xss具有很强的稳定性

比较常见的一个场景是攻击方在论坛或社区等网站上提交一段恶意js脚本代码, 服务器接收后会保存下来,

其他用户再去访问这段内容时, 恶意脚本就会被执行

DOM型(破坏页面结构)

发生在客户端的界面上, 指用户在浏览网页时, 被注入了恶意代码修改了dom结构, 用户这时如果不小心点击了, 就会执行对应的脚本

防范xss攻击的方法

最简单的做法: 对所有cookie设置httponly, 这样设置后所有script脚本都不能读取cookie, 设置cookie的方法是setHeader

1
response.setHeader(` `"Set-Cookie"` `, "cookiename=httponlyTest;Path=/;Domain=domainvalue;Max-Age=seconds;HTTPOnly");

输入检查: 不要相信任何用户输入, 对任何用户的输入都要内容检查, 过滤, 建立可信任字符和HTML的白名单, 比如decodingMap, 对于特殊的字符, ‘>’, ‘<’都会过滤, 防止xss攻击

CSRF跨站请求伪造

csrf攻击是借助攻击者对受害者cookie骗取服务器的信任, 在受害者毫不知情的情况下伪造请求访问服务器, 在未授权的情况下执行需要权限的操作

cookie是服务器保存在客户端的一段小数据, 他会在每次客户端向服务器发送请求时携带发送, 主要用于保存会话状态管理, 自定义设置

所有浏览器所持有的cookie会分为两种, session Cookie(会话结束就被删除), permanent cookie(指定有效时间)

验证码: 对抗CSRF最有效的办法, CSRF往往是在用户不知情的情况下伪造请求, 但是验证码可以强迫用户必须与页面交互后才能发送请求, 但是验证码不是万能的, 不能所有操作都要验证码, 这只是一种辅助方案

设置referer Ckeck 在http协议中, http头中有一个字段叫Referer, 记录了http请求的来源地址, 通过检测请求来源是否合法, 来判断是否响应请求(缺点: referer是浏览器提供的, 存在被破解的风险, 并且referer也会被修改)

添加token验证 CSRF请求之所以能成功, 关键在于攻击者利用到获取的cookie来通过安全验证, 所以必须设置一个cookie之外的攻击者访问不到的数据, 并在服务器设置拦截器验证token, 如果token不准确, 就会拒绝这次访问

响应式布局

发表于 2022-03-13

响应式布局

含义: 指同一页面在不同屏幕尺寸下有不同的布局, 传统的开发方式是PC端那一套, 手机端再开发一套,

而是用响应式布局只要开发一套就够了, 缺点是css比较重

响应式设计与自适应设计的区别: 响应式开发一套界面, 通过检测视口标签, 针对不同客户端在客户端做代码处理, 来展现不同布局的内容; 自适应需要开发多套界面通过检测视口分辨率, 判断访问的设备是pc端, 平板, 手机, 从而请求服务层, 来返回不同的界面. 屏幕分成超小屏幕, 小屏幕,

中等屏幕, 大屏幕

媒体查询

针对不同媒体类型定义不同的样式, 当重置浏览器窗口大小的过程中, 会根据浏览器的高度和宽度重新渲染页面

百分比布局:

缺点:

  1. 计算困难, 定义一个元素的宽高, 必须按照设计稿, 换算成百分比
  2. width和height相对父元素的width和height, margin和padding不管垂直还是水平方向都是相对于父元素宽度的, border-radius则是相对于元素自身的, 这使得百分比的布局问题变得复杂

rem布局

REM是css3的新单位, 并且移动端的支持度很高. rem相对于根元素的font-szie大小, 根元素的font-size提供一个基准值, 当页面的size发生变化时, 只需要改变font-size值. 以rem为固定单位元素的大小也会发生响应式变化, 如果需要改变布局的大小, 只需要动态改变font-size就行了

rem布局方法:

  • 所有设置的固定值都用rem做单位, 在html上设置一个基准值, px和rem的比例相对应, 然后在效果图上获取px值, 布局时转换为rem

  • js获取真实的屏幕宽度, 让其除以设计稿的宽度, 算出比例, 把之前的基准值按照比例重新设置, 这样项目在移动端自适应了

缺点: 必须通过js来动态控制根元素font-size的大小, 也就是说, css样式和js的代码有一定耦合性, 必须把font-size的代码放在css之前

视口单位

css3新引入的单位, 与视图窗口有关, vw表示相对视图窗口的宽度, vh相对于视图窗口的高度, vmin和vmax是其中的较小, 较大值

计网3

发表于 2022-03-13

Http协议的请求和响应

  1. Http: 超文本传输协议(HyperText Transfer Protocol)

  2. Http协议的组成: 由Http请求和http响应组成

  3. Http组成的三部分: 请求行, 请求头, 请求体

  1. 请求行

    请求方式: POST, GET

    请求的资源: /DemoEE/form.html

    协议版本: HTTP/1.1(一般都是)

    HTTP/1.0, 发送请求, 创建一个连接, 获得一个web资源, 连接结束

    HTTP/1.1, 发送请求, 创建一次连接, 获得多个web资源, 保持连接

  2. 请求头

    请求头是客户端发送给服务器的信息, 使用键值对表示key, value

    if-Modified-Since: 浏览器通知服务器, 本地缓存的最后变更时间, 与另一个响应头Last-Modified共同控制浏览器页面的缓存

    cookie: 与会话有关技术, 用于存放浏览器缓存的cookie信息

    User-Agent: 浏览器通知服务器, 客户端, 浏览器, 操作系统的相关信息

    Accept: 浏览器可支持的MIME(媒体类型Multipurpose Internet Mail Extensions)类型, 文件描述的一种方式 text/html html文件, text/css css文件 text/javascript js文件

  3. 请求体

    请求方式是post时, 请求体有请求的参数 username=zs&password=123

    请求方式是get, 请求参数不会出现在请求体后, 会拼接在URL后面

get和post的区别:

Get方法的特点: 请求的数据会附在URL中, 以?分割URL和传输数据, 多个参数用&连接

除此之外, Get仅用于信息获取, 有安全性和幂等性

安全性: 指的是非修改信息, get请求不会产生副作用, 仅用于获取信息资源, 不会影响资源状态

幂等性: 指无论调用这个URL多少次, 数据都不会变

get的url具有长度限制

get只产生一个TCP数据包

post方法特点: 根据Http规范, post可能修改服务器上的资源请求, 例如我们在点赞时, 就是在提交post请求

post请求信息放在请求数据中, 所以请求信息没有长度限制

post方法会产生两个TCP数据包, 浏览器会把请求头发给服务器, 待服务器响应100 continue后, 浏览器在发送请求数据, 服务器响应200 ok, 看起来get请求的传输比post块, 实际上在网络良好的情况下可以忽略不计

GET 和 POST 的区别: 本质没有区别, get和post都是HTTP请求协议的请求方法, 而http又是TCP/IP协议的通信协议之一, 所以都是TCP连接

**区分: ** 在http协议中, 会对这些不同的请求设置不同的类别管理, 例如单独获取资源的请求为GET, 修改服务器资源的请求为POST. 服务器对它们的请求报文格式作出要求

HTTP1.0, 1.1, 2.0的版本区别

  • HTTP/1.0

HTTP/1.0规定浏览器与服务器只保持短暂的连接, 连接无法复用, 每个TCP连接只能发送一个请求, 浏览器的每次请求都需要与服务器建立一个TCP连接, 服务器完成请求处理后立刻断开TCP连接, 服务器不跟踪每个客户也不记录过去的请求.

  • HTTP1.1

引入了持久连接, TCP连接默认不关闭, 可以被多个请求复用

引入了管道机制, 通一个TCP连接里面, 客户端可以发送多个请求

服务端是顺序执行的, 效率还有提升空间

HTTP2.0

2.0重点解决通信效率问题, HTTP2.0采用了多路复用技术, 在一个连接里面, 浏览器和服务端都可以同时发送多个请求和回应, 不用按照顺序一一对应, 这样做是使用了二进制分帧, 即HTTP2.0会将所有传输的信息分割为更小的消息和帧, 对它们采用二进制格式编码

多路复用的优化: Header压缩, 服务端推送

Header压缩: Http请求和响应是由请求行, 请求头 请求体组成, 一般对于请求体是会进行gzip压缩的, 但是请求行,请求头是没有进行压缩的, 以纯文本进行传输, 所以http2.0提出对请求头, 请求行压缩

常用状态码:

100 继续发送请求

200 请求完成 ok

301 资源重定向 Move Permanently 会导致浏览器将书签重新保存

302 临时重定向 Found 浏览器书签保存的url不变

304 客户端发送附带条件的请求, 但为满足服务器的条件,

400 客户端错误

401 无权限访问

403 forbidden

404 资源不存在

500 Internal Server Error 服务器错误

502 网关错误

未命名

发表于 2022-03-12

双向数据绑定

计网1

发表于 2022-03-12

OSI的七层模型分别是什么, 各自的功能是什么?

应用层: 文件传输, 电子邮件, 文件服务, 虚拟终端, 各种应用软件

协议 TFTP HTTP SNMP FTP SMTP DNS Telnet

表示层: 数据格式化, 代码转换, 数据加密

协议: 没有协议

会话层: 控制应用程序之间的会话能力, 如不同软件数据分发给不同的软件

协议: 没有协议

传输层: 提供端对端传输数据的基本功能, 如TCP, UDP

协议: TCP, UDP

网络层: 定义IP编址, 定义路由功能, 如不同设备的数据转发

协议: IP ICMP RIP, OSPF BGP IGMP

数据链路层: 定义数据的基本格式, 如何传输, 如何标识, 如网卡MAC地址

协议: SLIP, CSLIP, PPP, ARP, RARP, MTU

物理层: 以二进制数据形式在物理媒体上传输数据

协议: ISO2110 IEE802

在四层, 传输数据被称为段(Segments)

在第三层被称为包(Packets)

二层网络层被数据称为帧(FRames)

一层物理层时数据被称为比特流(Bits)

网络七层模型是一个标准, 而非实现

网络五层模型是一个实现的应用模型

网络五层模型是由七层模型简化而来

三次挥手

  1. 第一次握手: 客户端给服务端SYN报文, 并指明客户端的初始化序列号(ISN). 此时客户端处于SYN_Send状态

  2. 第二次握手: 服务器收到客户端的SYN报文后, 会以自己的SYN报文作为应答, 并且也是指定了自己的初始化序列号ISN, 同时会把客户端的ISN + 1作为ACK的值, 表示自己已经收到了客户端的SYN, 此时服务器处于SYN_RCVD的状态

  3. 第三次握手: 客户端收到SYN报文后, 会发送一个ACK报文, 也是把服务器ISN + 1作为ACK的值, 表示收到了SYN的报文, 客户端处于established

  4. 服务器收到ACK报文. 也处于established的状态, 此时双方建立了连接

三次握手的目的:

  1. 确认双方的接受能力, 发送能力是否正常
  2. 指定自己的初始化序列号, 为后面的可靠传送做准备
三次握手中可以携带数据吗

第一和第二次握手可以不携带数据, 第三次握手可以携带数据. 如果第一次就能携带数据的话, 如果有人要恶意攻击服务器, 那他每次都在第一次握手的SYN报文中放入大量数据, 攻击者根本不用理会服务器的接受, 发送能力是否正常, 疯狂发送SYN报文, 会让服务器花费很多时间, 内存来接受这些报文, 也就是说, 第一次握手可以放数据的话, 服务器会很容易遭到攻击

第一次握手:客户端发送网络包,服务端收到了。这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。
第二次握手:服务端发包,客户端收到了。这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。
第三次握手:客户端发包,服务端收到了。这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。

为什么需要四次挥手?三次不行?

  1. 第一次挥手: 客户端发送一个FIN报文, 报文中会指定一个序列号, 此时客户端处于FIN_WAIT1
  2. 第二次挥手: 服务器收到FIN报文, 会发送ACK报文, 且把客户端的序列号值 + 1作为ACK报文的序列号, 表明已经收到客户端的报文了, 此时服务器处于CLOSE_WAIT状态
  3. 第三次挥手: 如果服务端也想断开连接了, 服务器发送FIN报文, 且指定一个序列号, 此时客户端处于LAST_ACK状态
  4. 第四次挥手: 客户端收到FIN后, 一样发送了一个ACK报文作为应答, 且把服务端的序列号值 + 1作为自己的ACK序列号值, 此时客户端处于TIME_WAIT, 需要过一阵阵子确保服务端收到自己的ACK报文后才进入CLOSED状态
  5. 服务端收到ACK之后, 就处于关闭连接了, 处于CLOSED状态

TIME_WAIT状态, 客户端发送ACK后不会关闭, 而是等一阵子才关闭, 要确保服务器已经收到我们的ACK报文, 如果没有收到的话, 服务器会重新发送FIN报文, 就知道之前的报文丢失了, 再次发送报文

TIME_WAIT持续时间至少是一个报文的来回时间, 一般会设置一个计时, 如果这个计时没有收到FIN报文, 则代表发送成功, 此时就处于CLOSED状态

未命名

发表于 2022-03-12

Vue基本原理

当一个Vue实例创建时, Vue会遍历data中的所有属性, 用Object.defineProperty()进行数据劫持, 通过get和set函数, 当数据被访问时, 会调用get函数, 当数据被修改时, 会调用set函数, 然后在内部追踪相关依赖, 在属性被访问和修改时发生相应变化. 每个组件实例都有一个watcher实例, 它会在组件中把属性记录为依赖, 之后当依赖的setter被调用时, 会通知watcher重新计算, 从而致使它在关联的组件得以更新

一些想法

发表于 2022-03-12

看完帅地的春招之路, 又有了一点新的体会, 怕忘了, 在这里记一笔

很早之前我就看过一次, 那次看完只有春招这么个概念, 当春招真的落到自己头上时再去翻这篇文章, 又有了新的体会.

我的一面是字节跳动, 人生第一次面试, 紧张得前言不搭后语, 有几次面试官问我我一问三不知, 开始之前我以为他会问js的相关知识, 结果上来就是数据库, 计网五层模型啥的, 这些我都完全没准备, 场面特别尴尬, 我和面试官都是不是那种特别能活跃气氛的人, 然后面试官让我实现一个instanceof函数, 这个太可惜了, 当时刚好那个东西没有看过, 就考了. 然后是实现flat函数. 这个只知道有这么个东西, 真的没用过, 结果自然是不会. 然后是一个二叉树的题, 太紧张然后没写出来, 直接gg

这次让我感觉最深的地方是, 真的不能只背面经, 会死得很惨. 当面试官问你面经题时, 你最好不是真的只会面经. 因为他如果在这里稍微提一两个小问题, 你不会, 那就gg了, 他会判断你是背面经的. 背面经是技术岗的明规则, 但如果只是会面经还是不够, 除非是小公司缺人门槛特别低.

我的解决方案是 以面经为基础骨架, 对每个问题进行延伸拓展, 最好是面试官问什么, 你都能扯一嘴, 用自己的理解去回答问题, 而不是以面经的机械化作答(网上机械化答案太多了). 重点: 把面经当成目录用, 对于重点不会就全网搜答案, 越详细越好(遵循“取法乎上,仅得其中;取法乎中,仅得其下;取法乎下,无所得矣!”的原则) , 因为学习的过程就是忘记的过程, 学习得越多, 忘得就越多, 最终剩下的就是你真正暂时学到的内容, 只背面经, 最终一无所获

Vue知识点1

发表于 2022-03-11

Vue生命周期

  • beforeCreate在实例初始化后, 数据观测(data observer)之前被调用

  • created 实例已经创建完成之后被调用, 在这一步, 实例已经完成以下配置: 数据观测(data observer), 属性和方法的运算, watch和event回调, 这里没有$el

  • beforeMounted: 在挂载开始之前被调用, 相关render函数被首次调用

  • mounted: el被新创建的vm.$el替换, 并挂载到实例上后首次调用该钩子函数

  • beforeUpdate: 数据更新时调用, 发生在虚拟dom打补丁之前这里适合访问现有的dom, 比如手动移除已添加的事件监听. 该钩子在服务器端渲染期间不被调用, 因为只有初次渲染会在服务端进行
  • updated: 由于数据更改导致虚拟DOM重新渲染和打补丁, 在这之后会调用该钩子
  • beforeDestroy: 实例销毁前被调用, 在这一步, 实例仍然完全可用, 该钩子在服务器渲染端不能被调用
  • destroyed: Vue实例销毁后调用, 调用后Vue实例指示的所有东西会解绑, 所有的事件监听被移除, 子实例被销毁, 服务端渲染期间不会被调用

Vue的通讯的方式

  1. props, emit(最常用的父子通讯方式)

    父组件传入属性, 子组件通过props接受, 就可以在内部thisXXX的方式使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // section父组件
    <template>
    <div class="section">
    <com-article :articles="articleList"></com-article>
    </div>
    </template>

    <script>
    import comArticle from './test/article.vue'
    export default {
    name: 'HelloWorld',
    components: { comArticle },
    data() {
    return {
    articleList: ['红楼梦', '西游记', '三国演义']
    }
    }
    }
    </script>


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 子组件 article.vue
    <template>
    <div>
    <span v-for="(item, index) in articles" :key="index">{{item}}</span>
    </div>
    </template>

    <script>
    export default {
    props: ['articles']
    }
    </script>

    总结: prop只可以从上一级组件传递到下一级组件(父子组件), 即所谓的单向数据流. 而且prop只读, 不可被修改, 修改会失效并警告

    子组件向父组件传值

    对于$emit, $emit绑定一个自定义事件, 当这个语句被执行时, 就会将参数arg传递给父组件, 父组件通过v-on监听并接受参数. 通过一个例子, 点击页面渲染出来到的ariticle的item, 父组件中显示数组的下标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 父组件中
<template>
<div class="section">
<com-article :articles="articleList" @onEmitIndex="onEmitIndex"></com-article>
<p>{{currentIndex}}</p>
</div>
</template>

<script>
import comArticle from './test/article.vue'
export default {
name: 'HelloWorld',
components: { comArticle },
data() {
return {
currentIndex: -1,
articleList: ['红楼梦', '西游记', '三国演义']
}
},
methods: {
onEmitIndex(idx) {
this.currentIndex = idx
}
}
}
</script>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<div>
<div v-for="(item, index) in articles" :key="index" @click="emitIndex(index)">{{item}}</div>
</div>
</template>

<script>
export default {
props: ['articles'],
methods: {
emitIndex(index) {
this.$emit('onEmitIndex', index)
}
}
}
</script>

ref和refs

如果在普通的DOM元素上使用, 引用指向的就是DOM元素, 如果引用在子组件上, 就指向子组件实例, 可以通过实例直接调用组件的方法或数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 子组件 A.vue

export default {
data () {
return {
name: 'Vue.js'
}
},
methods: {
sayHello () {
console.log('hello')
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 父组件 app.vue

<template>
<component-a ref="comA"></component-a>
</template>
<script>
export default {
mounted () {
const comA = this.$refs.comA;
console.log(comA.name); // Vue.js
comA.sayHello(); // hello
}
}
</script>

eventBus

eventbus又称事件总线, 在vue中可以使用它来作为沟通桥梁的概念, 组件共用相同的事件中心, 可以向该中心注册发送事件或接收事件, 所有组件都可以通知其他组件

缺点是项目较大难以维护

  1. 初始化

    1
    2
    3
    // event-bus.js
    import Vue from 'vue'
    export const EventBus = new Vue()
  2. 发送事件

    加入有两个组件: additionNum, showNum, 这两个组件可以是兄弟组件, 也可以是父子组件, 这里以兄弟组件为例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <template>
    <div>
    <show-num-com></show-num-com>
    <addition-num-com></addition-num-com>
    </div>
    </template>

    <script>
    import showNumCom from './showNum.vue'
    import additionNumCom from './additionNum.vue'
    export default {
    components: { showNumCom, additionNumCom }
    }
    </script>

    发送事件组件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    // addtionNum.vue 中发送事件
    <template>
    <div>
    <button @click="additionHandle">+加法器
    </button>
    </div>
    </template>
    <script>
    import {EventBus} from './event-bus.js'
    console.log(EventBus)
    export default{
    data(){
    return {
    num: 1
    }
    },
    methods: {
    additionHandle(){
    EventBus.$emit('addition', {
    num: this.num ++
    })
    }
    }
    }
    </script>

    接受事件组件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // showNum.vue 组件
    <template>
    <div>
    计算和: {{count}}
    </div>
    </template>
    <script>
    import {EventBus} from './event-bus.js'
    export default{
    data(){
    return {
    count: 0
    }
    },

    methods(){
    EventBus.$on('addition', param => {
    this.count = this.count + param.num;
    })
    }
    }
    </script>
    Vuex

    Vuex是一个专门为Vue.js应用程序开发的状态管理模式. 它采用集中存储管理应用的所有组件和方法, 并以相应的规则保证状态以一种可以预测的方式发生改变. Vuex解决了多个视图依赖同一状态和来自不同视图的行为需要变更同一状态的问题, 将开发者的经历聚集在数据的更新而不是数据在组件间的传递

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <template>
    <div id="app">
    <ChildA/>
    <ChildB/>
    </div>
    </template>

    <script>
    import ChildA from './components/ChildA' // 导入A组件
    import ChildB from './components/ChildB' //
    </script>

vue懒加载

为什么要懒加载

Vue是单页面应用, 如果没有应用懒加载, 运用webpack打包的文件很大, 造成进入首页时, 需要加载的内容过多, 白屏时间很长, 懒加载有利于页面划分, 需要的时候加载页面, 减少首页加载压力, 减少加载用时

一句话: 进入首页不用一次加载过多资源, 造成用时过长

懒加载

也叫延迟加载, 需要的时候再去加载, 随用随加载

根据功能可以划分为图片懒加载和组件懒加载

图片懒加载

一张图片就是一个img标签, 浏览器是否发起图片请求是根据的src属性, 实现懒加载的关键是, 在图片没有进入可视区域时, 先不给的src值, 这样浏览区就不会发送请求了, 等待图片进入可视区域再给src赋值

实现懒加载有四个步骤:

  1. 加载loading图片
  2. 判断哪些图片要加载
  3. 隐形加载图片
  4. 替换真图片

加载loading图片

加载loading图片在html就实现的

1
2
3
4
<div>
// src存放的是伪图片 等待的图片 data-src是自定义属性, 存放的是真实的图片
<img class="lazy" src="img/loading.gif" data-src="img/pic1.jpg"></img>
</div>

判断图片进入可视区域

用到了onload事件, onload事件会在页面和图像加载完成后立即发生

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 懒加载实现原理
function lazyload(imgs){
// 可视区域高度
var h = window.innerHeight;
//滚动区域高度
var s = document.documentElement.scrollTop || document.body.scrollTop;
for(var i=0;i<imgs.length;i++){
//图片距离顶部的距离大于可视区域和滚动区域之和时懒加载
if ((h+s) > getTop(imgs[i])) {
// 真实情况是页面开始有2秒空白,所以使用setTimeout定时2s
(function(i){
setTimeout(function(){
// 不加立即执行函数i会等于9
// 隐形加载图片或其他资源,
//创建一个临时图片,这个图片在内存中不会到页面上去。实现隐形加载
var temp = new Image();
temp.src = imgs[i].getAttribute('data-src');//只会请求一次
// onload判断图片加载完毕,真图片加载完毕,再赋值给dom节点
temp.onload = function(){
// 获取自定义属性data-src,用真图片替换假图片
imgs[i].src = imgs[i].getAttribute('data-src')
}
},2000)
})(i)
}
}
}

隐形加载 和 替换真图片

  • 当图片的offsetTop 小于 scrollTop + window.innerHeight时, 就会开始加载
  • 隐形加载: 首先创建一个临时图片, 图片不会到页面上, 而是保存在内存中, 然后发起请求, 请求完成后, 把它赋值给src属性, 这样就完成了图片替换

路由懒加载 和 组件懒加载

路由懒加载

vue异步实现懒加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: resolve => (require(['@/components/HelloWorld', resolve]))
}
]
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)
const HelloWorld = () => import('@/components/HelloWorld')
export default new Router({
routers: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
}
]
})
  1. 原来的写法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<div class="hello">
<One-com></One-com>
1234
</div>
</template>
<script>
import One from './one'
export default{
components: {
'One-com': One
},
data() {
return {
msg: 'Welcome to Your Vue.js App'
}
}
}
</script>
  1. const方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<div class="hello">
<One-com></One-com>
1234
</div>
</template>
<script>
const One = () => import('./One')
export default{
components: {
'One-com': One
},
data() {
return {
msg: 'Welcome to Your Vue.js App'
}
}
}
</script>
  1. 异步方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<div class="hello">
<One-com></One-com>
1234
</div>
</template>
<script>
const One = () => import('./One')
export default{
components: {
'One-com': resolve => (['./one'], resolve);
},
data() {
return {
msg: 'Welcome to Your Vue.js App'
}
}
}
</script>

Vue响应式原理

整体思路 是数据劫持 + 观察者模式

对象内部通过defineReactive方法, 使用Object.defineProperty将属性进行劫持(只会劫持已经存在的属性), 数组则通过数组重写方式实现的. 当页面使用对应属性时, 每个属性都有自己的dep属性, 存放它所收集的watcher(依赖收集), 当属性变化后通知自己对应的watcher去派发更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Observer{
constructor(value){
this.walk(value);
}
walk(data){
// 对象上所有属性一次进行观测
let keys = Object.keys(data);
for(let i = 0; i < keys.length; i++){
let key = keys[i];
let value = data[key];
defineReactive(data, key, value);
}
}
}
// 数据劫持核心
function defineReactive(data, key, value){
observe(value); // 递归
Object.defineProperty(data, key, {
get() {
console.log('获取值');
return value;
},
set(newValue){
console.log('设置值');
value = newValue;
},
});
}
export function observe(value){
// 如果传过来是对象或数组, 进行属性劫持
if(Object.prototype.toString.call(value) === 'object Object' || Array.isArray(value)) {
return new Oberver(value);
}
}

未命名

发表于 2022-03-10

进程与线程

进程是资源分配的最小单位, 线程是cpu调度的最小单位

一个进程就是程序运行的实例, 详细解释就是, 启动一个程序时, 操作系统会为该程序创建一块内存, 用来存放代码, 运行中的数据, 和一个执行任务的主线程主线程, 这个运行环境就是主线程.

进程是运行在虚拟内存上的, 虚拟内存是用来解决用户对硬件资源的无限需求和有限的硬件资源之间的矛盾. 从操作系统上看, 虚拟内存即交换文件; 从处理器角度看, 虚拟内存即虚拟地址空间

进程和线程的关系

  • 进程中的任意一线程出错, 都会导致整个进程崩溃
  • 线程间共享进程的数据
  • 当一个进程关闭以后, 操作系统会回收进程所占的内存, 防止内存泄漏
  • 进程之间内容相互隔离, 数据不会共享, 也避免了数据写出到其他线程上

进程和线程的区别

  • 进程能看做独立应用, 线程不能
  • 资源: 进程是cpu资源分配的最小单位, 线程是cpu调度的最小单位
  • 通信: 线程之间可以直接共享同一进程的资源, 而进程通信需要借助进程间通信
  • 系统开销: 由于创建或撤销进程, 系统都要为之分配或回收资源, 而进程通信需要借助进程间通信

浏览器的渲染进程的线程共有五种:

  1. GUI渲染进程, 负责渲染浏览器, 解析html, css, 构建DOM树, 构建CSSOM树, 构建渲染树和绘制页面; 当发生重绘和回流时就会执行该线程

  2. js引擎线程, 也称js内核, 负责处理js脚本程序, 解析js脚本

    GUI线程和JS引擎线程是互斥的, 所以如果js执行时间过长, 会造成页面渲染不连贯, 页面渲染加载阻塞

  3. 时间触发线程, 属于浏览器而不是js引擎, 用来控制事件循环

  4. 定时器触发线程, 定时器触发线程是setInterval, setTimeout所在线程, 浏览器定时计数, 不是js引擎计数, 以为js是单线程的, 计时完毕后, 添加到事件任务会被添加到任务队列等待执行, 计时器最小为4ms

  5. 异步http请求线程, xmlHttpRequest连接后通过浏览器新开一个线程请求

进程之前的通信方式

  1. 管道通信 管道是最基本的线程通信机制. 管道就是操作系统内核开辟出的一段缓冲区进程1可以将数据拷贝到这段缓冲区上, 进程2就可以读取了
    • 管道只能单向通信
    • 依赖文件系统
    • 生命周期随进程
    • 面向字节流服务
    • 管道内部提供同步机制

浏览器渲染

  • 首先解析收到的文档, 根据文档定义一棵DOM树, DOM树是由DOM元素及属性结点组成
  • 对CSS进行解析, 生成CSSOM规则树
  • 根据DOM和CSSOM规则树构建渲染树. 渲染树的结点被称为渲染对象, 渲染对象和DOM大部分都是一一对应的
  • 当渲染对象被创建并添加到树中时, 没有位置和大小, 生成渲染树后进行回流(重绘), 这一阶段浏览器主要弄清各个结点在页面的几何位置和大小
  • 布局阶段结束后, 遍历渲染树并调用paint方法, 将他们显示在屏幕上, 绘制使用UI组件

这个过程是逐步完成的, 它会解析一部分就显示一部分

浏览器渲染优化

针对js : js会阻塞html和css的解析, 优化如下

  • 将js文件放在body后面
  • body中间尽量不要写script
  • async是在下载完成后, 立即异步加载, 加载后立刻执行

针对CSS

  • link 浏览器会派发一个新等线程(http线程)去加载资源, 与此同时GUI线程会 继续向下渲染代码
  • style GUI直接渲染
  • @important 会阻碍渲染, 直到引用的资源被返回

针对DOM树和CSSOM树

  • html代码层级不要太深
  • 减少css代码层级

减少回流和重绘

  • 减少dom操作
  • 不要使用table布局

浏览器本地存储方式

  1. cookie

    最早被提出的本地存储方式, 在此之前, 服务端是无法判断网络中两个请求是否是同一个用户发起的, 为了解决这个问题, cookie就出现了

    cookie的特性

    • cookie一旦被创建, 名称就无法修改
    • cookie无法跨域名, a域名和b域名的cookie是无法共享的, 这也是由cookie的隐私安全性决定的, 这样就能阻止非法获取其他网站的cookie
    • 每个域名下的cookie数量不能超过20个, 每个cookie的大小不能超过4kb
    • cookie在请求一个新页面时会被发送过去

    跨域名共享cookie的方法:

    • 使用nginx反向代理

    cookie使用场景

    • 最常见的使用场景是cookie和session结合使用, 将sessionId存储到cookie中, 每次发送请求都会携带这个sessionId, 这样服务端就会知道是谁发送的请求, 从而响应相应的信息

    LocalStorage

    localStorage是html5新引入的特性

    **优点: **

    大小为5M

    localStorage是持久存储, 除非主动清理, 否则一直存在

    仅存储在本地, 不像cookie那样每次http请求都会携带

    缺点

    隐私模式无法读取localStorage

    受同源策略限制,

浏览器同源策略

同源是指: 协议, 域名, 端口号必须一致

限制如下

当前域名下js脚本不能访问其他域下的cookie, localStorage, sessionStorage

当前域下js脚本不能访问操作其他域下的DOM

当前域下ajax无法发送跨域请求

解决跨域问题:

**CORS: **

浏览器事件机制

事件是指用户操作网页时发生的交互动作, 如click/move, 还可以是文档加载, 窗口滚动,大小调整. 事件被封装成一个对象, 该对象包含所有event信息(属性)和对事件进行的操作(方法)

现代浏览器有三种事件模型:

DOM0级事件, 这种模型不会传播, 没有事件流概念, 直接在元素的属性上绑定事件

DOM2级别事件: addEventListener绑定事件, 分三个阶段, 捕获阶段, 事件从document对象依次向子元素传播, 最终到达被点击的结点, 目标阶段, 当事件不断的传递直到目标节点的时候,最终在目标节点上触发这个事件. 冒泡阶段:事件冒泡即事件开始时,事件发生所在的节点,然后逐级传播到较为不具体的节点

stopPropagation()

Vue基础

当一个Vue实例被创建, Vue会遍历data中所有的属性, 用Object.defineProperty将他们都转换为getter, setter, 并在内部追踪相关依赖

12…7<i class="fa fa-angle-right"></i>

63 日志
1 分类
14 标签
© 2022 hydra
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.4