浏览器原理及常见考点

  • 一、浏览器安全
    • 1、XSS(跨站脚本)攻击
    • 2、CSRF攻击
    • 3、中间人攻击
    • 4、哪些操作会引起浏览器安全问题???
    • 5、网络劫持有哪几种?如何防范?
  • 二、 进程与线程
    • 1、进程与线程的概念
    • 2、浏览器的进程
    • 3、浏览器渲染进程的线程有哪些
    • 4、进程之间的通信方式
    • 5、僵尸、孤儿进程
    • 6、死锁产生的原因,如何解决
    • 7、如何实现浏览器内多个标签页之间的通信
    • 8、对serviceWroker的理解
  • 三、浏览器缓存机制
    • 1、对浏览器缓存机制的理解
    • 3、为什么需要浏览器缓存???
  • 四、浏览器的组成
    • 1、对浏览器的理解
  • 五、浏览器渲染原理
    • 1、浏览器渲染过程
    • 2、浏览器渲染优化
    • 3、渲染过程中遇到JS文件如何处理?
    • 4、什么是文档的预解析
    • 5、css如何阻塞文档解析??
    • 6、如何优化关键渲染路径?
  • 六、浏览器本地存储
    • 1、浏览器本地存储方式及使用场景
    • 2、cookie有哪些字段
    • 3、Cookie、LocalStorage、SessionStorage区别
  • 七、浏览器同源策略
  • 1、什么是同源策略
    • 2、如何解决跨域问题
      • 1、CORS
      • 2、JSONP
      • 3、postMessage跨域
      • 4、nginx代理跨域
      • 5、nodejs 中间件代理跨域
      • 6、document.domain + iframe跨域
      • 7、WebSocket协议跨域
  • 八、浏览器事件机制
    • 1、事件是什么?事件模型?
    • 2、如何阻止事件冒泡
    • 3、对事件委托的理解
    • 5、同步和异步的区别
    • 6、对事件循环的理解
    • 7、微任务与宏仁务
    • 8、什么是执行栈
  • 九、浏览器的垃圾回收

对于前端来说,所做的项目是运行在浏览器上的,所以有必要对浏览器进行详细的了解,在面试中也会经常考察到。

一、浏览器安全

1、XSS(跨站脚本)攻击

XSS 的本质是因为网站没有对恶意代码进行过滤,与正常的代码混合在一起了,浏览器没有办法分辨哪些脚本是可信的,从而导致了恶意代码的执行。

攻击者可以通过这种攻击方式可以进行以下操作:
● 获取页面的数据,如DOM、cookie、localStorage;
● DOS攻击,发送合理请求,占用服务器资源,从而使用户无法访问服务器;
● 破坏页面结构;
● 流量劫持(将链接指向某网站);

xss可以分为存储型、反射型、DOM型

● 存储型指的是恶意脚本会存储在目标服务器上,当浏览器请求数据时,脚本从服务器传回并执行。
● 反射型指的是攻击者诱导用户访问一个带有恶意代码的 URL 后,服务器端接收数据后处理,然后把带有恶意代码的数据发送到浏览器端,浏览器端解析这段带有 XSS 代码的数据后当做脚本执行,最终完成 XSS 攻击。
● DOM 型指的通过修改页面的 DOM 节点形成的 XSS。

反射型 XSS 跟存储型 XSS 的区别是:存储型 XSS 的恶意代码存在数据库⾥,反射型 XSS 的恶意代码存在 URL ⾥。

  • 如何防御XSS攻击
    ● 可以从浏览器的执行来进行预防,一种是使用纯前端的方式,不用服务器端拼接后返回(不使用服务端渲染)。另一种是对需要插入到 HTML 中的代码做好充分的转义。对于 DOM 型的攻击,主要是前端脚本的不可靠而造成的,对于数据获取渲染和字符串拼接的时候应该对可能出现的恶意代码情况进行判断。
    ● 使用 CSP ,CSP 的本质是建立一个白名单,告诉浏览器哪些外部资源可以加载和执行,从而防止恶意代码的注入攻击。
    ●对一些敏感信息进行保护,比如cookie使用http-only,使得脚本无法获取。也可以使用验证码,避免脚本伪装成用户执行一些操作。

    使用CSP,csp的本质是建立一个白名单,告诉浏览器哪些外部资源可以加载和执行,从而防止恶意代码注入攻击-
    只需要配置规则,如何拦截有浏览器自己实现,

开启csp的两种方式:

  • 设置http首部中的Content-Security-Policy
  • 一种是设置meta标签

2、CSRF攻击

跨站请求伪造攻击,攻击者诱导用户进入一个第三方网站,然后该网站向被攻击网站发送跨站请求。如果用户在被攻击网站中保存了登录状态,那么攻击者就可以利用这个登录状态,绕过后台的用户验证,冒充用户向服务器执行一些操作。

CSRF 攻击的本质是利用 cookie 会在同源请求中携带发送给服务器的特点,以此来实现用户的冒充。

● GET 类型的 CSRF 攻击,比如在网站中的一个 img 标签里构建一个请求,当用户打开这个网站的时候就会自动发起提交。
● POST 类型的 CSRF 攻击,比如构建一个表单,然后隐藏它,当用户进入页面时,自动提交这个表单。
● 链接类型的 CSRF 攻击,比如在 a 标签的 href 属性里构建一个请求,然后诱导用户去点击。

如何防御:

  • 1、本质是,cookie在同源请求中会携带,要进行同源检测,服务器根据http请求头中的origin或者referer信息来判断,是否为允许访问的站点,
  • 2、使用 CSRF Token 进行验证 服务器向用户返回一个随机数 Token ,当网站再次发起请求时,在请求参数中加入服务器端返回的 token ,然后服务器对这个 token 进行验证。这种方法解决了使用 cookie 单一验证方式时,可能会被冒用的问题,但是这种方法存在一个缺点就是,我们需要给网站中的所有请求都添加上这个 token
  • 3、对 Cookie 进行双重验证
  • 4、在设置 cookie 属性的时候设置 Samesite ,限制 cookie 不能作为被第三方使用

3、中间人攻击

中间⼈ (Man-in-the-middle attack, MITM) 是指攻击者与通讯的两端分别创建独⽴的联系, 并交换其所收到的数据, 使通讯的两端认为他们正在通过⼀个私密的连接与对⽅直接对话, 但事实上整个会话都被攻击者完全控制。在中间⼈攻击中,攻击者可以拦截通讯双⽅的通话并插⼊新的内容。

4、哪些操作会引起浏览器安全问题???

  • XSS:跨站脚本 (Cross-Site Scripting, XSS): ⼀种代码注⼊⽅式, 为了与 CSS 区分所以被称作 XSS。早期常⻅于⽹络论坛, 起因是⽹站没有对⽤户的输⼊进⾏严格的限制, 使得攻击者可以将脚本上传到帖⼦让其他⼈浏览到有恶意脚本的⻚⾯, 其注⼊⽅式很简单包括但不限于 JavaScript / CSS / Flash 等;
  • iframe的滥⽤: iframe中的内容是由第三⽅来提供的,默认情况下他们不受控制,他们可以在iframe中运⾏JavaScirpt脚本、Flash插件、弹出对话框等等,这可能会破坏前端⽤户体验;
  • 跨站点请求伪造(Cross-Site Request Forgeries,CSRF): 指攻击者通过设置好的陷阱,强制对已完成认证的⽤户进⾏⾮预期的个⼈信息或设定信息等某些状态更新,属于被动攻击
  • 恶意第三⽅库: ⽆论是后端服务器应⽤还是前端应⽤开发,绝⼤多数时候都是在借助开发框架和各种类库进⾏快速开发,⼀旦第三⽅库被植⼊恶意代码很容易引起安全问题。

5、网络劫持有哪几种?如何防范?

⽹络劫持分为两种:
(1)DNS劫持: (输⼊京东被强制跳转到淘宝这就属于dns劫持)
● DNS强制解析: 通过修改运营商的本地DNS记录,来引导⽤户流量到缓存服务器
● 302跳转的⽅式: 通过监控⽹络出⼝的流量,分析判断哪些内容是可以进⾏劫持处理的,再对劫持的内存发起302跳转的回复,引导⽤户获取内容
(2)HTTP劫持: (访问⾕歌但是⼀直有贪玩蓝⽉的⼴告),由于http明⽂传输,运营商会修改你的http响应内容(即加⼴告)

DNS劫持由于涉嫌违法,已经被监管起来,现在很少会有DNS劫持,⽽http劫持依然⾮常盛⾏,最有效的办法就是全站HTTPS,将HTTP加密,这使得运营商⽆法获取明⽂,就⽆法劫持你的响应内容。

二、 进程与线程

1、进程与线程的概念

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

一个进程就是一个程序的运行实例。

启动一个程序的时候,操作系统会为该程序创建一块内存,用来存放代码、运行中的数据和一个执行任务的主线程,我们把这样的一个运行环境叫进程

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

如果程序很多时,内存可能会不够,操作系统为每个进程提供一套独立的虚拟地址空间,从而使得同一块物理内存在不同的进程中可以对应到不同或相同的虚拟地址,变相的增加了程序可以使用的内存

进程与线程的关系:

  • 进程中任一线程执行出错,都会导致整个进程的崩溃
  • 线程之间共享进程中的数据
  • 当一个进程关闭之后,操作系统会回收进程所占用的内存
  • 进程之间的内容相互隔离

2、浏览器的进程

chrome浏览器的进程有:

  • 多个渲染进程
  • 多个插件进程
  • 一个网络请求进程
  • 一个GPU进程
  • 一个浏览器主进程

这些进程的功能:
● 浏览器进程:主要负责界面显示、用户交互、子进程管理,同时提供存储等功能。
● 渲染进程:核心任务是将 HTML、CSS 和 JavaScript 转换为用户可以与之交互的网页,排版引擎 Blink 和 JavaScript 引擎 V8 都是运行在该进程中,默认情况下,Chrome 会为每个 Tab 标签创建一个渲染进程。出于安全考虑,渲染进程都是运行在沙箱模式下。
● GPU 进程:其实, GPU 的使用初衷是为了实现 3D CSS 的效果,只是随后网页、Chrome 的 UI 界面都选择采用 GPU 来绘制,这使得 GPU 成为浏览器普遍的需求。最后,Chrome 在其多进程架构上也引入了 GPU 进程。
● 网络进程:主要负责页面的网络资源加载,之前是作为一个模块运行在浏览器进程里面的,直至最近才独立出来,成为一个单独的进程。
● 插件进程:主要是负责插件的运行,因插件易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面造成影响。

打开一个网页至少需要四个进程:

  • 一个网络进程
  • 一个渲染进程
  • 一个GPU
  • 一个浏览器进程

3、浏览器渲染进程的线程有哪些

核心任务是将 HTML、CSS 和 JavaScript 转换为用户可以与之交互的网页,排版引擎 Blink 和 JavaScript 引擎 V8 都是运行在该进程中,默认情况下,Chrome 会为每个 Tab 标签创建一个渲染进程。出于安全考虑,渲染进程都是运行在沙箱模式下。

  • JS引擎线程
  • 事件触发线程
  • 定时器触发线程
  • GUI渲染线程
  • 异步http请求线程

(1)GUI渲染线程
负责渲染浏览器页面,解析HTML、CSS,构建DOM树、构建CSSOM树、构建渲染树和绘制页面;当界面需要重绘或由于某种操作引发回流时,该线程就会执行。

注意:GUI渲染线程和JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。

(2)JS引擎线程
JS引擎线程也称为JS内核,负责处理Javascript脚本程序,解析Javascript脚本,运行代码;JS引擎线程一直等待着任务队列中任务的到来,然后加以处理,一个Tab页中无论什么时候都只有一个JS引擎线程在运行JS程序;

注意:GUI渲染线程与JS引擎线程的互斥关系,所以如果JS执行的时间过长,会造成页面的渲染不连贯,导致页面渲染加载阻塞。

(3)事件触发线程
事件触发线程属于浏览器而不是JS引擎,用来控制事件循环;当JS引擎执行代码块如setTimeOut时(也可是来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件触发线程中;当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理;

注意:由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行);

(4)定时器触发进程
定时器触发进程即setInterval与setTimeout所在线程;浏览器定时计数器并不是由JS引擎计数的,因为JS引擎是单线程的,如果处于阻塞线程状态就会影响记计时的准确性;因此使用单独线程来计时并触发定时器,计时完毕后,添加到事件队列中,等待JS引擎空闲后执行,所以定时器中的任务在设定的时间点不一定能够准时执行,定时器只是在指定时间点将任务添加到事件队列中;

注意:W3C在HTML标准中规定,定时器的定时时间不能小于4ms,如果是小于4ms,则默认为4ms。

(5)异步http请求线程
● XMLHttpRequest连接后通过浏览器新开一个线程请求;
● 检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将回调函数放入事件队列中,等待JS引擎空闲后执行;

4、进程之间的通信方式

  • 管道通信 管道就是操作系统在内核中开辟的一段缓冲区,进程1可以将需要交互的数据拷贝到这段缓冲区,进程2就可以读取了。
  • 消息队列通信
  • 信号量通信
  • 享内存通信
  • 套接字通信

5、僵尸、孤儿进程

● 孤儿进程:父进程退出了,而它的一个或多个进程还在运行,那这些子进程都会成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
● 僵尸进程:子进程比父进程先结束,而父进程又没有释放子进程占用的资源,那么子进程的进程描述符仍然保存在系统中,这种进程称之为僵死进程。

6、死锁产生的原因,如何解决

所谓死锁,是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。

系统中的资源可以分为两类:
● 可剥夺资源,是指某进程在获得这类资源后,该资源可以再被其他进程或系统剥夺,CPU和主存均属于可剥夺性资源;
● 不可剥夺资源,当系统把这类资源分配给某进程后,再不能强行收回,只能在进程用完后自行释放,如磁带机、打印机等。

产生死锁的原因:
(1)竞争资源
● 产生死锁中的竞争资源之一指的是竞争不可剥夺资源(例如:系统中只有一台打印机,可供进程P1使用,假定P1已占用了打印机,若P2继续要求打印机打印将阻塞)
● 产生死锁中的竞争资源另外一种资源指的是竞争临时资源(临时资源包括硬件中断、信号、消息、缓冲区内的消息等),通常消息通信顺序进行不当,则会产生死锁
(2)进程间推进顺序非法
若P1保持了资源R1,P2保持了资源R2,系统处于不安全状态,因为这两个进程再向前推进,便可能发生死锁。例如,当P1运行到P1:Request(R2)时,将因R2已被P2占用而阻塞;当P2运行到P2:Request(R1)时,也将因R1已被P1占用而阻塞,于是发生进程死锁

产生死锁的必要条件:

● 互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用。
● 请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。
● 不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。
● 环路等待条件:在发生死锁时,必然存在一个进程——资源的环形链

预防死锁的方法:

● 资源一次性分配:一次性分配所有资源,这样就不会再有请求了(破坏请求条件)
● 只要有一个资源得不到分配,也不给这个进程分配其他的资源(破坏请保持条件)
● 可剥夺资源:即当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源(破坏不可剥夺条件)
● 资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反(破坏环路等待条件)

7、如何实现浏览器内多个标签页之间的通信

实现多个标签页之间的通信,本质上都是通过中介者模式来实现的。因为标签页之间没有办法直接通信,因此我们可以找一个中介者,让标签页和中介者进行通信,然后让这个中介者来进行消息的转发。通信方法如下:

  • websocket协议,因为 websocket 协议可以实现服务器推送,所以服务器就可以用来当做这个中介者。标签页通过向服务器发送数据,然后由服务器向其他标签页推送转发。
  • 使用 ShareWorker 的方式,shareWorker 会在页面存在的生命周期内创建一个唯一的线程,并且开启多个页面也只会使用同一个线程。这个时候共享线程就可以充当中介者的角色。标签页间通过共享一个线程,然后通过这个共享的线程来实现数据的交换。
  • 使用 localStorage 的方式,我们可以在一个标签页对 localStorage 的变化事件进行监听,然后当另一个标签页修改数据的时候,我们就可以通过这个监听事件来获取到数据。这个时候 localStorage 对象就是充当的中介者的角色。
  • 使用 postMessage 方法,如果我们能够获得对应标签页的引用,就可以使用postMessage 方法,进行通信。

8、对serviceWroker的理解

三、浏览器缓存机制

1、对浏览器缓存机制的理解

(1)强缓存
使用强缓存策略时,如果缓存资源有效,则直接使用缓存资源,不必再向服务器发起请求。
强缓存策略可以通过两种方式来设置,分别是 http 头信息中的 Expires 属性和 Cache-Control 属性。

Expires这个时间是一个绝对时间,它是服务器的时间,因此可能存在这样的问题,就是客户端的时间和服务器端的时间不一致,或者用户可以对客户端时间进行修改的情况,这样就可能会影响缓存命中的结果。

(2)协商缓存
协商缓存也可以通过两种方式来设置,分别是 http 头信息中的 Etag 和 Last-Modified 属性

  • 服务器通过在响应头中添加 Last-Modified 属性来指出资源最后一次修改的时间,当浏览器下一次发起请求时,会在请求头中添加一个 If-Modified-Since 的属性,属性值为上一次资源返回时的 Last-Modified 的值。当请求发送到服务器后服务器会通过这个属性来和资源的最后一次的修改时间来进行比较,以此来判断资源是否做了修改。如果资源没有修改,那么返回 304 状态,让客户端使用本地的缓存。如果资源已经被修改了,则返回修改后的资源。

last-modified的缺点:

Last-Modified 标注的最后修改时间只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,那么文件已将改变了但是 Last-Modified 却没有改变,这样会造成缓存命中的不准确。

  • 服务器在返回资源的时候,在头信息中添加了 Etag 属性,这个属性是资源生成的唯一标识符,当资源发生改变的时候,这个值也会发生改变。在下一次资源请求时,浏览器会在请求头中添加一个 If-None-Match 属性,这个属性的值就是上次返回的资源的 Etag 的值。服务接收到请求后会根据这个值来和资源当前的 Etag 的值来进行比较,以此来判断资源是否发生改变,是否需要返回资源。通过这种方式,比 Last-Modified 的方式更加精确。

3、为什么需要浏览器缓存???

对于浏览器的缓存,主要针对的是前端的静态资源,最好的效果就是,在发起请求之后,拉取相应的静态资源,并保存在本地。如果服务器的静态资源没有更新,那么在下次请求的时候,就直接从本地读取即可,如果服务器的静态资源已经更新,那么我们再次请求的时候,就到服务器拉取新的资源,并保存在本地。这样就大大的减少了请求的次数提高了网站的性能 这就要用到浏览器的缓存策略了。

所谓的浏览器缓存指的是浏览器将用户请求过的静态资源,存储到电脑本地磁盘中,当浏览器再次访问时,就可以直接从本地加载,不需要再去服务端请求了。

使用浏览器缓存,有以下优点:
● 减少了服务器的负担,提高了网站的性能
● 加快了客户端网页的加载速度
● 减少了多余网络数据传输

4、点击刷新按钮或者按 F5、按 Ctrl+F5 (强制刷新)、地址栏回车有什么区别?
● 点击刷新按钮或者按 F5:浏览器直接对本地的缓存文件过期,但是会带上If-Modifed-Since,If-None-Match,这就意味着服务器会对文件检查新鲜度,返回结果可能是 304,也有可能是 200。
● 用户按 Ctrl+F5(强制刷新):浏览器不仅会对本地文件过期,而且不会带上 If-Modifed-Since,If-None-Match,相当于之前从来没有请求过,返回结果是 200。
● 地址栏回车: 浏览器发起请求,按照正常流程,本地检查是否过期,然后服务器检查新鲜度,最后返回内容。

四、浏览器的组成

1、对浏览器的理解

浏览器的主要功能是将用户选择的 web 资源呈现出来,它需要从服务器请求资源,并将其显示在浏览器窗口中,资源的格式通常是 HTML,也包括 PDF、image 及其他格式。用户用 URI(Uniform Resource Identifier 统一资源标识符)来指定所请求资源的位置。

五、浏览器渲染原理

1、浏览器渲染过程

浏览器渲染主要有以下步骤:
● 首先解析收到的文档,根据文档定义构建一棵 DOM 树,DOM 树是由 DOM 元素及属性节点组成的。
● 然后对 CSS 进行解析,生成 CSSOM 规则树。
● 根据 DOM 树和 CSSOM 规则树构建渲染树。渲染树的节点被称为渲染对象,渲染对象是一个包含有颜色和大小等属性的矩形,渲染对象和 DOM 元素相对应,但这种对应关系不是一对一的,不可见的 DOM 元素不会被插入渲染树。还有一些 DOM元素对应几个可见对象,它们一般是一些具有复杂结构的元素,无法用一个矩形来描述。
● 当渲染对象被创建并添加到树中,它们并没有位置和大小,所以当浏览器生成渲染树以后,就会根据渲染树来进行布局(也可以叫做回流)。这一阶段浏览器要做的事情是要弄清楚各个节点在页面中的确切位置和大小。通常这一行为也被称为“自动重排”。
● 布局阶段结束后是绘制阶段,遍历渲染树并调用渲染对象的 paint 方法将它们的内容显示在屏幕上,绘制使用 UI 基础组件。

2、浏览器渲染优化

1)针对JavaScript:JavaScript既会阻塞HTML的解析,也会阻塞CSS的解析。因此我们可以对JavaScript的加载方式进行改变,来进行优化:
(1)尽量将JavaScript文件放在body的最后
(2) body中间尽量不要写

(2)针对CSS:使用CSS有三种方式:使用link、@import、内联样式,其中link和@import都是导入外部样式。它们之间的区别:
● link:浏览器会派发一个新等线程(HTTP线程)去加载资源文件,与此同时GUI渲染线程会继续向下渲染代码
● @import:GUI渲染线程会暂时停止渲染,去服务器加载资源文件,资源文件没有返回之前不会继续渲染(阻碍浏览器渲染)
● style:GUI直接渲染

(3)针对DOM树、CSSOM树:
可以通过以下几种方式来减少渲染的时间:
● HTML文件的代码层级尽量不要太深
● 使用语义化的标签,来避免不标准语义化的特殊处理
● 减少css层级,因为选择器是从左向右进行解析的

(4)减少回流与重绘:
● 操作DOM时,尽量在低层级的DOM节点进行操作
● 不要使用table布局, 一个小的改动可能会使整个table进行重新布局
● 使用CSS的表达式
● 不要频繁操作元素的样式,对于静态页面,可以修改类名,而不是样式。
● 使用absolute或者fixed,使元素脱离文档流,这样他们发生变化就不会影响其他元素
● 避免频繁操作DOM,可以创建一个文档片段documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中
● 将元素先设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘。
● 将DOM的多个读操作(或者写操作)放在一起,而不是读写操作穿插着写。这得益于浏览器的渲染队列机制。

浏览器针对页面的回流与重绘,进行了自身的优化——渲染队列

浏览器会将所有的回流、重绘的操作放在一个队列中,当队列中的操作到了一定的数量或者到了一定的时间间隔,浏览器就会对队列进行批处理。这样就会让多次的回流、重绘变成一次回流重绘。

将多个读操作(或者写操作)放在一起,就会等所有的读操作进入队列之后执行,这样,原本应该是触发多次回流,变成了只触发一次回流。

3、渲染过程中遇到JS文件如何处理?

JavaScript 的加载解析执行会阻塞文档的解析,也就是说,在构建 DOM 时,HTML 解析器若遇到了 JavaScript,那么它会暂停文档的解析,将控制权移交给 JavaScript 引擎,等 JavaScript 引擎运行完毕,浏览器再从中断的地方恢复继续解析文档。也就是说,如果想要首屏渲染的越快,就越不应该在首屏就加载 JS 文件,这也是都建议将 script 标签放在 body 标签底部的原因。当然在当下,并不是说 script 标签必须放在底部,因为你可以给 script 标签添加 defer 或者 async 属性。

4、什么是文档的预解析

当执行 JavaScript 脚本时,另一个线程解析剩下的文档,并加载后面需要通过网络加载的资源。这种方式可以使资源并行加载从而使整体速度更快。需要注意的是,预解析并不改变 DOM 树,它将这个工作留给主解析过程,自己只解析外部资源的引用,比如外部脚本、样式表及图片。

5、css如何阻塞文档解析??

理论上,既然样式表不改变 DOM 树,也就没有必要停下文档的解析等待它们。然而,存在一个问题,**JavaScript 脚本执行时可能在文档的解析过程中请求样式信息,**如果样式还没有加载和解析,脚本将得到错误的值,显然这将会导致很多问题。

所以如果浏览器尚未完成 CSSOM 的下载和构建,而我们却想在此时运行脚本,那么浏览器将延迟 JavaScript 脚本执行和文档的解析,直至其完成 CSSOM 的下载和构建。也就是说,在这种情况下,浏览器会先下载和构建 CSSOM,然后再执行 JavaScript,最后再继续文档的解析。

6、如何优化关键渲染路径?

为尽快完成首次渲染,需要最大限度减小,以下三种可变因素:

(1)关键资源的数量
(2)关键路径的长度
(3)关键字节的长度

关键资源是可能阻止网页首次渲染的资源。这些资源越少,浏览器的工作量就越小,对 CPU 以及其他资源的占用也就越少。

关键路径长度受所有关键资源与其字节大小之间依赖关系图的影响:某些资源只能在上一资源处理完毕之后才能开始下载,并且资源越大,下载所需的往返次数就越多

要减少字节数,我们可以减少资源数(将它们删除或设为非关键资源),此外还要压缩和优化各项资源,确保最大限度减小传送大小。

六、浏览器本地存储

1、浏览器本地存储方式及使用场景

  • cookie
    Cookie是最早被提出来的本地存储方式,在此之前,服务端是无法判断网络中的两个请求是否是同一用户发起的,为解决这个问题,Cookie就出现了。Cookie的大小只有4kb,它是一种纯文本文件,每次发起HTTP请求都会携带Cookie。

cookie的特性:
● Cookie一旦创建成功,名称就无法修改
● Cookie是无法跨域名的,也就是说a域名和b域名下的cookie是无法共享的,这也是由Cookie的隐私安全性决定的,这样就能够阻止非法获取其他网站的Cookie
● 每个域名下Cookie的数量不能超过20个,每个Cookie的大小不能超过4kb
● 有安全问题,如果Cookie被拦截了,那就可获得session的所有信息,即使加密也于事无补,无需知道cookie的意义,只要转发cookie就能达到目的
● Cookie在请求一个新页面的时候都会被发送过去

如果需要域名之间跨域共享cookie,两种方法:

  • 使用Nginx反向代理
  • 在一个站点登录之后、往其他网站写coolie。服务端的Session存储到一个节点,Cookie存储sessionId

cookie的使用场景:
● 最常见的使用场景就是Cookie和session结合使用,我们将sessionId存储到Cookie中,每次发请求都会携带这个sessionId,这样服务端就知道是谁发起的请求,从而响应相应的信息。
● 可以用来统计页面的点击次数

LocalStorage:
优点:
● 在大小方面,LocalStorage的大小一般为5MB,可以储存更多的信息
● LocalStorage是持久储存,并不会随着页面的关闭而消失,除非主动清理,不然会永久存在
● 仅储存在本地,不像Cookie那样每次HTTP请求都会被携带

缺点:
● 存在浏览器兼容问题,IE8以下版本的浏览器不支持
● 如果浏览器设置为隐私模式,那我们将无法读取到LocalStorage
● LocalStorage受到同源策略的限制,即端口、协议、主机地址有任何一个不相同,都不会访问

localStorage.setItem('APPLE','5DOLLARS')let data = localStorage.getItem('key');localStorage.removeItem('key');localStorage.clear();localStorage.key(index);

LocalStorage的使用场景:
● 有些网站有换肤的功能,这时候就可以将换肤的信息存储在本地的LocalStorage中,当需要换肤的时候,直接操作LocalStorage即可
● 在网站中的用户浏览信息也会存储在LocalStorage中,还有网站的一些不常变动的个人信息等也可以存储在本地的LocalStorage中

3、sessionStorage
SessionStorage 主要用于临时保存同一窗口(或标签页)的数据,刷新页面时不会删除,关闭窗口或标签页之后将会删除这些数据。

● SessionStorage和LocalStorage都在本地进行数据存储;
● SessionStorage也有同源策略的限制,但是SessionStorage有一条更加严格的限制,SessionStorage只有在同一浏览器的同一窗口下才能够共享;
● LocalStorage和SessionStorage都不能被爬虫爬取;

SessionStorage的使用场景
● 由于SessionStorage具有时效性,所以可以用来存储一些网站的游客登录的信息,还有临时的浏览记录的信息。当关闭网站之后,这些信息也就随之消除了。

2、cookie有哪些字段

  • Name
  • Value,cookie的值,对于认证cookie,value值包括web服务器所提供的访问令牌;
  • Size:
  • Path,可以访问此cookie的页面路径。 比如domain是abc.com,path是/test,那么只有/test路径下的页面可以读取此cookie。
  • Secure,指定是否使用HTTPS安全协议发送Cookie。使用HTTPS安全协议,可以保护Cookie在浏览器和Web服务器间的传输过程中不被窃取和篡改。该方法也可用于Web站点的身份鉴别,即在HTTPS的连接建立阶段,浏览器会检查Web网站的SSL证书的有效性。但是基于兼容性的原因(比如有些网站使用自签署的证书)在检测到SSL证书无效时,浏览器并不会立即终止用户的连接请求,而是显示安全风险信息,用户仍可以选择继续访问该站点。
  • Domain,可以访问该cookie的域名**,Cookie 机制并未遵循严格的同源策略,**允许一个子域可以设置或获取其父域的 Cookie
  • HTTP:字段包含HTTPOnly 属性 ,该属性用来设置cookie能否通过脚本来访问,默认为空,即可以通过脚本访问。在客户端是不能通过js代码去设置一个httpOnly类型的cookie的,这种类型的cookie只能通过服务端来设置。该属性用于防止客户端脚本通过document.cookie属性访问Cookie,有助于保护Cookie不被跨站脚本攻击窃取或篡改。但是,HTTPOnly的应用仍存在局限性,一些浏览器可以阻止客户端脚本对Cookie的读操作,但允许写操作;此外大多数浏览器仍允许通过XMLHTTP对象读取HTTP响应中的Set-Cookie头。
  • Expires/Max-size:此cookie的超时时间。若设置其值为一个时间,那么当到达此时间后,此cookie失效。不设置的话默认值是Session,意思是cookie会和session一起失效。当浏览器关闭(不是浏览器标签页,而是整个浏览器) 后,此cookie失效。

总结:
服务器端可以使用 Set-Cookie 的响应头部来配置 cookie 信息。一条cookie 包括了5个属性值 expires、domain、path、secure、HttpOnly。其中 expires 指定了 cookie 失效的时间,domain 是域名、path是路径,domain 和 path 一起限制了 cookie 能够被哪些 url 访问。secure 规定了 cookie 只能在确保安全的情况下传输,HttpOnly 规定了这个 cookie 只能被服务器访问,不能使用 js 脚本访问。

3、Cookie、LocalStorage、SessionStorage区别

浏览器端常用的存储技术是 cookie 、localStorage 和 sessionStorage。
● cookie:其实最开始是服务器端用于记录用户状态的一种方式,由服务器设置,在客户端存储,然后每次发起同源请求时,发送给服务器端。cookie 最多能存储 4 k 数据,它的生存时间由 expires 属性指定,并且 cookie 只能被同源的页面访问共享。
● sessionStorage:html5 提供的一种浏览器本地存储的方法,它借鉴了服务器端 session 的概念,代表的是一次会话中所保存的数据。它一般能够存储 5M 或者更大的数据,它在当前窗口关闭后就失效了,并且 sessionStorage 只能被同一个窗口的同源页面所访问共享。
● localStorage:html5 提供的一种浏览器本地存储的方法,它一般也能够存储 5M 或者更大的数据。它和 sessionStorage 不同的是,除非手动删除它,否则它不会失效,并且 localStorage 也只能被同源页面所访问共享。

七、浏览器同源策略

1、什么是同源策略

同源策略限制了从同一个源加载的文档或脚本如何与另一个源的资源进行交互。这是浏览器的一个用于隔离潜在恶意文件的重要的安全机制。同源指的是:协议、端口号、域名必须一致。

同源政策主要限制了三个方面:
● 当前域下的 js 脚本不能够访问其他域下的 cookie、localStorage 和 indexDB。
● 当前域下的 js 脚本不能够操作访问操作其他域下的 DOM。
● 当前域下 ajax 无法发送跨域请求。

同源政策的目的主要是为了保证用户的信息安全,它只是对 js 脚本的一种限制,并不是对浏览器的限制,对于一般的 img、或者script 脚本请求都不会有跨域的限制,这是因为这些操作都不会通过响应结果来进行可能出现安全问题的操作。

2、如何解决跨域问题

1、CORS

跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain)上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域HTTP 请求。

CORS需要浏览器和服务器同时支持,整个CORS过程都是浏览器完成的,无需用户参与。因此实现CORS的关键就是服务器,只要服务器实现了CORS请求,就可以跨源通信了。

浏览器将CORS分为简单请求和非简单请求

若该请求满足以下两个条件,就可以看作是简单请求:
1)请求方法是以下三种方法之一:
● HEAD
● GET
● POST
2)HTTP的头信息不超出以下几种字段:
● Accept
● Accept-Language
● Content-Language
● Last-Event-ID
● Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

(1)简单请求过程:
对于简单请求,浏览器会直接发出CORS请求,它会在请求的头信息中增加一个Orign字段,该字段用来说明本次请求来自哪个源(协议+端口+域名),服务器会根据这个值来决定是否同意这次请求。如果Orign指定的域名在许可范围之内,服务器返回的响应就会多出以下信息头

Access-Control-Allow-Origin: http://api.bob.com  // 和Orign一直
Access-Control-Allow-Credentials: true   // 表示是否允许发送Cookie
Access-Control-Expose-Headers: FooBar   // 指定返回其他字段的值
Content-Type: text/html; charset=utf-8   // 表示文档类型

如果origin域名不在许可范围之内,返回一个正常的http响应,浏览器发现没有上面的Access-Control-Allow-Origin头部信息,就知道出错了。这个错误无法通过状态码识别,因为返回的状态码可能是200。

(2)非简单请求
非简单请求是对服务器有特殊要求的请求,比如请求方法为DELETE或者PUT等。非简单请求的CORS请求会在正式通信之前进行一次HTTP查询请求,称为预检请求。

浏览器会询问服务器,当前所在的网页是否在服务器允许访问的范围内,以及可以使用哪些HTTP请求方式和头信息字段,只有得到肯定的回复,才会进行正式的HTTP请求,否则就会报错。

预检请求使用的请求方法是OPTIONS,表示这个请求是来询问的。他的头信息中的关键字段是Orign,表示请求来自哪个源。除此之外,头信息中还包括两个字段:

  • Access-Control-Request-Method,列出浏览器的cors请求会用到哪些http方法
  • Access-Control-Request-Header:指定浏览器CORS请求会额外发送的头信息字段。

服务器在收到浏览器的预检请求之后,会根据头信息的三个字段来进行判断,如果返回的头信息在中有Access-Control-Allow-Origin这个字段就是允许跨域请求,如果没有,就是不同意这个预检请求,就会报错。

Access-Control-Allow-Origin: http://api.bob.com  // 允许跨域的源地址
Access-Control-Allow-Methods: GET, POST, PUT // 服务器支持的所有跨域请求的方法
Access-Control-Allow-Headers: X-Custom-Header  // 服务器支持的所有头信息字段
Access-Control-Allow-Credentials: true   // 表示是否允许发送Cookie
Access-Control-Max-Age: 1728000  // 用来指定本次预检请求的有效期,单位为秒

只要服务器通过了预检请求,在以后每次的CORS请求都会自带一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。

因此在非简单请求下,至少需要设置以下字段:

  • Access-Control-Allow-Origin
  • Access-Control-Allow-Methods’
  • Access-Control-Allow-Headers’

减少OPTIONS请求次数
OPTIONS 请求次数过多就会损耗页面加载的性能,降低用户体验度。所以尽量要减少OPTIONS请求次数,可以后端在请求的返回头部添加:Access-Control-Max-Age:number。它表示预检请求的返回结果可以被缓存多久,单位是秒。该字段只对完全一样的URL的缓存设置生效,所以设置了缓存时间,在这个时间范围内,再次发送请求就不需要进行预检请求了。

2、JSONP

jsonp的原理就是利用

//y原生实现,JSONPvar script = document.createElement('script');
script.type = 'text/javascript';script.src = 'http://www.domain2.com:8080/login?user=admin&callback=handleCallback'
document.head.appendChild(script);function handleCallback(res){alert(JSON.stringify(res));
}

JSONP的缺点:
● 具有局限性, 仅支持get方法
● 不安全,可能会遭受XSS攻击

3、postMessage跨域

postMessage是HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,它可用于解决以下方面的问题:

● 页面和其打开的新窗口的数据传递
● 多窗口之间消息传递
● 页面与嵌套的iframe消息传递
● 上面三个场景的跨域数据传递

用法:postMessage(data,origin)方法接受两个参数:
● data: html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用JSON.stringify()序列化。
● origin: 协议+主机+端口号,也可以设置为"*“,表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为”/"。

<iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe>
<script>       var iframe = document.getElementById('iframe');iframe.onload = function() {var data = {name: 'aym'};// 向domain2传送跨域数据iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.domain2.com');};// 接受domain2返回数据window.addEventListener('message', function(e) {alert('data from domain2 ---> ' + e.data);}, false);
</script>

4、nginx代理跨域

nginx代理跨域,实质和CORS跨域原理一样,通过配置文件设置请求响应头Access-Control-Allow-Origin…等字段。

5、nodejs 中间件代理跨域

node中间件实现跨域代理,原理大致与nginx相同,都是通过启一个代理服务器,实现数据的转发,也可以通过设置cookieDomainRewrite参数修改响应头中cookie中域名,实现当前域的cookie写入,方便接口登录认证。

6、document.domain + iframe跨域

此方案仅限主域相同,子域不同的跨域应用场景。实现原理:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。

7、WebSocket协议跨域

WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。

八、浏览器事件机制

1、事件是什么?事件模型?

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

事件是用户操作网页时发生的交互动作或者网页本身的一些操作,现代浏览器一共有三种事件模型:

  • DOM0 级事件模型,这种模型不会传播,所以没有事件流的概念,但是现在有的浏览器支持以冒泡的方式实现,它可以在网页中直接定义监听函数,也可以通过 js 属性来指定监听函数。所有浏览器都兼容这种方式。直接在dom对象上注册事件名称,就是DOM0写法。

  • IE 事件模型,在该事件模型中,一次事件共有两个过程,事件处理阶段和事件冒泡阶段。事件处理阶段会首先执行目标元素绑定的监听事件。然后是事件冒泡阶段,冒泡指的是事件从目标元素冒泡到 document,依次检查经过的节点是否绑定了事件监听函数,如果有则执行。这种模型通过attachEvent 来添加监听函数,可以添加多个监听函数,会按顺序依次执行。

  • DOM2 级事件模型,在该事件模型中,一次事件共有三个过程,第一个过程是事件捕获阶段。捕获指的是事件从 document 一直向下传播到目标元素,依次检查经过的节点是否绑定了事件监听函数,如果有则执行。后面两个阶段和 IE 事件模型的两个阶段相同。这种事件模型,事件绑定的函数是addEventListener,其中第三个参数可以指定事件是否在捕获阶段执行。

2、如何阻止事件冒泡

● 普通浏览器使用:event.stopPropagation()
● IE浏览器使用:event.cancelBubble = true;

3、对事件委托的理解

事件委托本质上是利用了浏览器事件冒泡的机制。因为事件在冒泡过程中会上传到父节点,父节点可以通过事件对象获取到目标节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件,这种方式称为事件委托(事件代理)。

使用事件委托可以不必要为每一个子元素都绑定一个监听事件,这样减少了内存上的消耗。并且使用事件代理还可以实现事件的动态绑定,比如说新增了一个子节点,并不需要单独地为它添加一个监听事件,它绑定的事件会交给父元素中的监听函数来处理。

事件委托的特点:

减少内存消耗

3)局限性
当然,事件委托也是有局限的。比如 focus、blur 之类的事件没有事件冒泡机制,所以无法实现事件委托;mousemove、mouseout 这样的事件,虽然有事件冒泡,但是只能不断通过位置去计算定位,对性能消耗高,因此也是不适合于事件委托的。

5、同步和异步的区别

同步指的是当一个进程在执行某个请求时,如果这个请求需要等待一段时间才能返回,那么这个进程会一直等待下去,直到消息返回为止再继续向下执行。

异步指的是当一个进程在执行某个请求时,如果这个请求需要等待一段时间才能返回,这个时候进程会继续往下执行,不会阻塞等待消息的返回,当消息返回时系统再通知进程进行处理。

6、对事件循环的理解

因为 js 是单线程运行的,在代码执行时,通过将不同函数的执行上下文压入执行栈中来保证代码的有序执行。在执行同步代码时,如果遇到异步事件,js 引擎并不会一直等待其返回结果,而是会将这个事件挂起,继续执行执行栈中的其他任务。当异步事件执行完毕后,再将异步事件对应的回调加入到一个任务队列中等待执行。任务队列可以分为宏任务队列和微任务队列,当当前执行栈中的事件执行完毕后,js 引擎首先会判断微任务队列中是否有任务可以执行,如果有就将微任务队首的事件压入栈中执行。当微任务队列中的任务都执行完成后再去执行宏任务队列中的任务。

Event Loop 执行顺序如下所示:
● 首先执行同步代码,这属于宏任务
● 当执行完所有同步代码后,执行栈为空,查询是否有异步代码需要执行
● 执行所有微任务
● 当执行完所有微任务后,如有必要会渲染页面
● 然后开始下一轮 Event Loop,执行宏任务中的异步代码

7、微任务与宏仁务

  • 微任务包括: promise 的回调、node 中的 process.nextTick 、对 Dom 变化监听的 MutationObserver。
  • 宏任务包括: script 脚本的执行、setTimeout ,setInterval ,setImmediate 一类的定时事件,还有如 I/O 操作、UI 渲染等。

8、什么是执行栈

可以把执行栈认为是一个存储函数调用的栈结构,遵循先进后出的原则。

九、浏览器的垃圾回收

1、标记清除
2、引用计数

  1. 哪些操作会造成内存泄漏?
    ● 第一种情况是由于使用未声明的变量,而意外的创建了一个全局变量,而使这个变量一直留在内存中无法被回收。
    ● 第二种情况是设置了 setInterval 定时器,而忘记取消它,如果循环函数有对外部变量的引用的话,那么这个变量会被一直留在内存中,而无法被回收。
    ● 第三种情况是获取一个 DOM 元素的引用,而后面这个元素被删除,由于我们一直保留了对这个元素的引用,所以它也无法被回收。
    ● 第四种情况是不合理

【浏览器】浏览器原理及常见考点相关推荐

  1. 【技术干货】浏览器工作原理和常见WEB攻击 (下)

    本文作者:上海驻云开发总监 陈昂 上篇给大家带来的是关于浏览器基本工作原理的总结和介绍,这篇文章重点给大家说明有哪些常见WEB攻击. 常见WEB攻击 互联网是个面向全世界的开放平台,越是开放的东西漏洞 ...

  2. 《浏览器工作原理与实践》学习笔记

    浏览器原理 前言 本文是学习李兵老师的<浏览器工作原理与实践>过程中记录笔记,详细链接见文末 进程vs线程 进程:一个应用程序的运行实例就是一个进程,详细来说就是:启动一个应用程序的时候, ...

  3. 浏览器工作原理探究详解

    浏览器工作原理探究 标签: 浏览器工作原理 / web性能优化 引言 最近对web的性能优化比较感兴趣,而前端代码主要在浏览器工作的.如果对浏览器的工作原理了解清楚,可以为web性能优化提供方向以及理 ...

  4. 详解浏览器渲染原理及流程

    今天来分享一下浏览器的渲染原理及流程. 前言   先来看看 Chrome 浏览器的多进程架构:   通常,我们打包出来的 HTML.CSS.JavaScript 等文件,经过浏览器运行之后就会显示出页 ...

  5. 图解浏览器渲染原理及流程

    本文约 8500 字,预计阅读需要 30 分钟. 今天来分享一下浏览器的渲染原理及流程. 前言 先来看看 Chrome 浏览器的多进程架构: 通常,我们打包出来的 HTML.CSS.JavaScrip ...

  6. 浏览器渲染原理-通俗易懂版本

    文章目录 浏览器渲染原理 前言 1. 网页的解析过程 2. 浏览器的功能与组成 2.1 浏览器内核 2.2 进程与线程 3. 浏览器渲染流程 3.1 渲染引擎解析过程 3.2 渲染引擎主要模块 4. ...

  7. 浏览器工作原理及相关内核、技术介绍

    BY XIAOMING · 2014 年 1 月 16 日. 正文开始: 一.浏览器工作原理(简化版) 1.浏览器用来干什么用 浏览器的主要功能是将用户请求访问的web资源呈现出来,它需要从服务器请求 ...

  8. 一文看懂Chrome浏览器工作原理

    转自:https://juejin.im/post/5e182a47e51d453cee48c752 本文是笔者对Mario Kosaka写的inside look at modern web bro ...

  9. 单文件浏览器_图文并茂深度解析浏览器渲染原理,包看懂超值得收藏

    在我们面试过程中,面试官经常会问到这么一个问题,那就是从在浏览器地址栏中输入URL到页面显示,浏览器到底发生了什么?这个问题看起来是老生常谈,但是这个问题回答的好坏,确实可以很好的反映出面试者知识的广 ...

最新文章

  1. 《转》IIS中配置通配符应用程序映射
  2. 直播预告 | 第四范式2021发布会技术分论坛报名开启,6月23日线上见
  3. 日志-周报-月报(2019年2月)
  4. 返回值是内置类型 不能更改_选择通过更改内容类型返回的详细程度,第二部分...
  5. 前端学习(2487):在VUE中使用element-ui的el-select组件时出现该报错
  6. pmp知识点详解-项目大牛整理_PMP核心知识点—第四章:项目整合管理(一)
  7. 《软件需求分析(第二版)》第 16 章——需求链中的联系链 重点部分总结
  8. Elementary Methods in Number Theory Exercise 1.2.31
  9. 谈谈对水晶报表的看法
  10. 《人月神话》阅读笔记一
  11. thinkphp group count连用
  12. MindMap学习使用
  13. DocSearcher:文档搜索引擎
  14. Pulling 1 repository Exception caught during execution of merge command
  15. POI EXCEL修改图表
  16. 大文件上传的解决方法
  17. Sentinel第二篇:降级
  18. 送给所有程序员的新年祝福新年愿望
  19. fre740变频器参数_三菱(F700)FR-F740系列变频器常用参数
  20. AAAI2020/风格迁移:Ultrafast Photorealistic Style Transfer via Neural Architecture基于神经结构搜索的超快逼真风格转移

热门文章

  1. 10年前25岁的我意气风发,10年后面对中年危机,房贷、车贷把我压得喘不过气...
  2. jquery 实现上拉加载功能
  3. 【保姆级】FastDFS+Nginx搭建图片文件服务器,SpringBoot实现本地上传
  4. 中国有什么食品科学与工程方面比较有含金量的证书
  5. IOS8发布,原来苹果6的出现只是一件小事!
  6. 知道创宇2018年度网络安全(云安全)态势报告
  7. 香港中文大学深圳(CUHKSZ)2020fall数据科学硕士申请及笔面经
  8. Unity3D_WebGL_手机和其他浏览器警告处理
  9. conda install 卡住
  10. 了解Sidecar模式