前端知识一

防抖节流

优化高频率事件 onscroll oninput resize onkeyup keydown.... 降低代码执行频率
前端知识一
文章图片
1.jpg
  • js动画/往页面里添加一些dom元素
  • style确定每个dom应该用什么样式规则
  • Layout布局,计算最终显示的位置和大小
  • Paint绘制dom,在不同的层上绘制
  • Composite渲染层合并
用户scroll和resize行为会导致页面不断的重新渲染,如果在绑定的回调函数中大量操作dom也会出现页面卡顿
优化方案:

前端知识一
文章图片
2.jpg 前端知识一
文章图片
3.jpg 函数节流 节流就是保证一段时间内,核心代码只执行一次
打个比方:水滴积攒到一定重量才会下落
简易节流函数
Document - 锐客网

防抖 防抖就是一段时间结束后,才触发一次事件,如果一段时间未结束再次触发事件,就会重新开始计算时间
打个比方:电梯中,门快要关了,突然游刃准备上来,电梯并没有改变楼层,而是再次打开电梯门。电梯延迟了改变楼层的功能,但是优化了资源。
简易防抖代码
Document - 锐客网

requestAnimationFrame 编写动画循环的关键是要知道延迟时间多长合适,如果时间过长会导致动画补流畅,时间过短会造成过度的绘制。
requestAnimationFrame采用系统时间间隔,保持最佳绘制效率。此方法是用来在页面重绘之前,
通知浏览器调用一个指定的函数,被调用的频率是约每秒60次,在运行时浏览器会自动优化方法的调用.
重点:这个函数的核心就是浏览器可以根据不同PC性能算出最佳绘制时间,以实现最佳显示效果
Document - 锐客网

柯里化 函数柯里化,是固定部分参数,返回一个接受剩余参数的函数,也称为部分计算函数,目的是为了缩小适用范围,创建一个针对性更强的函数。
其实质就是预先处理机制,核心就是利用闭包实现
(function () { function myBind(context=window,...outerArg) { //此处的this是fn,因为是fn.myBind let _this=this; return function(...innerArg) { //此处就相当于fn.call // _this.call(context,...outerArg.concat(innerArg)); //innerArg:就是自动传递的event对象 _this.apply (context,outerArg.concat(innerArg)); } } Function.prototype.myBind = myBind; })(); let obj = { name: "OBJ" } function fn(...args) { console.log(this, args); } /** 自定义实现了bind机制,而这个核心其实就是预先合并参数 利用这个机制可以实现下面案例的题目 */ document.body.onclick=fn.myBind(obj,100,200); //ev浏览器会自动追加在最后面传递,bind其实返回就是匿名函数,和下面的等效 //document.body.onclick=fn.bind(obj,100,200); //其实bind函数内部就是这么实现的,可以避免立即执行 // document.body.onclick = function (ev) { //fn.call(obj, 100, 200, ev); // }

假如需求是固定三层相加需求,但是每层参数不固定,可以如下实现
function add(...A) { return function(...B) { return function(...C) { return eval([...A,...B,...C].join('+')) } } } //也可以(1,2,4)(3)(5)这种层数固定但是每层参数不固定 add(1)(2)(3)

下面实现核心逻辑
/**请实现一个add函数,满足以下功能 * add(1); 1 * add(1)(2); 3 * add(1)(2)(3)6 * add(1)(2)(3)(4) 10 * add(1)(2,3)6 * add(1,2)(3) 6 * add(1,2,3)6 */function currying(fn, length) { length = length || fn.length; return function (...args) { if (args.length >= length) { return fn(...args); } //传入null/undefined的时候将执行js全局对象浏览器中是window,其他环境是global return currying(fn.bind(null, ...args), length - args.length); } } // function $add(n1, n2, n3, n4) { //return n1 + n2 + n3 + n4; // } // let add = currying($add, 4); // console.log(add(1)(2)(3)(4)); // console.log(add(1, 2, 3, 4)); //$add.bind(null,1).bind(null,2).bind(null,3)(4); //联系之前柯里化的案例 //function any1(...innerArg){$add.call(null,...[1,...innerArg])}这样一层层关联预处理了参数 //function any2(...innerArg){any1.call(null,...[2,...innerArg])} //function any3(...innerArg){any2.call(null,...[3,...innerArg])} //any3(4) //any2.call(null,3,4) //any1.call(null,2,3,4) //$add.call(null,1,2,3,4)let add=currying((...arg)=>eval(arg.join('+')),5); console.log(add(1,2,3,4,5));

总结:currying传递的第二参数就是实际真实计算是几个数据,一定要对应。
而从currying内部逻辑可发现,其实是递归实现,内部也有闭包,而这种需求核心其实就是不论多少层每层多少参数都要可以计算,参考前面的柯里化思想,每层递归其实类似于call这种绑定关联。
而在最终递归(预先处理)结束开始计算的时候,实际就是参数的合并逻辑。
【前端知识一】参考视频:https://www.bilibili.com/video/BV1aE411C7pt?p=27
反柯里化 从字面讲,意义和用法跟函数柯里化相比正好相反,扩大适用范围,创建一个应用范围更广的函数。使本来只有特定对象才适用的方法,扩展到更多的对象。
Function.prototype.uncurrying=function() { return str=>{ // Object.prototype.toString.call('str') //this就是 Object.prototype.toString //Object.prototype.toString.call(参数)就是输出类型 return this.call(str); //这只是简单案例,其实可以添加很多自己的逻辑 } } let toString=Object.prototype.toString.uncurrying(); //扩展了函数的功能 console.log(toString('hello')); //[object String]//Object.prototype.toString本身就是一个函数,函数自然就有Function.prototype上面的属性

总结:柯里化和反柯里化是一个思路,理解即可,不需要关心N多的具体实现,没有实际意义
SourceMap 说起sourceMap大家肯定都不陌生,随着前端工程化的演进,我们打包出来的代码都是混淆压缩过的,当源代码经过转换后,调试就成了一个问题。在浏览器中调试时,如何判断原始代码的位置?
为了解决这个问题,google 提出了sourceMap 的想法,并在chorme上最先支持sourceMap的使用。sourceMap 由于包含许多信息,前期也经过多版的编码算法优化,最后在2011年探索出了Source Map Revision 3.0 ,这个版本也就是我们现在一直在使用的sourceMap版本。这一版本的mapping信息使用Base64 VLQ 编码,大大缩小了.map文件的体积。
sourceMap可以帮我们直接定位到编译前代码的特定位置,接下来我们直接拿个sourceMap文件来看看它包含了一些什么信息:

前端知识一
文章图片
image.png 上面可以看到,sourceMap其实就是就是一段维护了前后代码映射关系的json描述文件,包含了以下一些信息:
  • version:sourcemap版本(现在都是v3)
  • file:转换后的文件名。
  • sourceRoot:转换前的文件所在的目录。如果与转换前的文件在同一目录,该项为空。
  • sources:转换前的文件。该项是一个数组,表示可能存在多个文件合并。
  • names:转换前的所有变量名和属性名。
  • mappings:记录位置信息的字符串。
mappings 信息是关键,它使用Base64 VLQ 编码,包含了源代码与生成代码的位置映射信息。mappings的编码原理详解可见:http://www.ruanyifeng.com/blog/2013/01/javascript_source_map.html,这里就不再详述。
webpack中的sourceMap配置
webpack 给出了多种sourceMap配置方式,相信很多人第一眼看到的时候和我一样,疑惑这些都有啥区别

前端知识一
文章图片
image.png 其实不难发现这么多配置,这些就是source-map和eval、inline、cheap、module 的自由组合。所以我们来拆开看下每项配置。
为了方便演示,这里的源代码只包含了一行代码
console.log( hello world );
最原始的只设置’source-map’配置,可以看到输出了两个文件,其中包含一个map文件

前端知识一
文章图片
image.png
  • eval
    每个模块用eval()包裹执行。
    • devtool: eval
      我们先看看单独的eval配置,这个配置相对于其他会特殊一点 。因为配置里没有sourceMap,实际上它也会生出map,只是它映射的是转换后的代码,而不是映射到原始代码。

      前端知识一
      文章图片
      image.png
    • 2)devtool: eval-source-map
      所以eval-source-map就会带上源码的sourceMap,打包结果如下:

      前端知识一
      文章图片
      image.png
值得注意的是加了eval的配置生成的sourceMap会作为DataURI嵌入,不单独生成.map文件。
对于eval的构建模式,我们可以看看官方的描述:可以看出官方是比较推荐开发场景下使用的,因为它能cache sourceMap,从而rebuild的速度会比较快。
  • inline
    inline配置想必大家肯定已经能猜到了,就是将map作为DataURI嵌入,不单独生成.map文件。
    devtool: inline-source-map构建出来的文件如下, 这个比较好理解,就不多说了

    前端知识一
    文章图片
    image.png
  • cheap
    这是 “cheap(低开销)” 的 source map,因为它没有生成列映射(column mapping),只是映射行数 。
    为了方便演示,我们在代码加一行错误抛出:
console.log('hello'); throw new Error('this is test');

可以看到错误信息只有行映射,但实际上开发时我们有行映射也基本足够了,所以开发场景下完全可以使用cheap 模式 ,来节省sourceMap的开销

前端知识一
文章图片
image.png
  • module
    Webpack会利用loader将所有非js模块转化为webpack可处理的js模块,而增加上面的cheap配置后也不会有loader模块之间对应的sourceMap。
    什么是模块之间的sourceMap呢?比如jsx文件会经历loader处理成js文件再混淆压缩, 如果没有loader之间的sourceMap,那么在debug的时候定义到上图中的压缩前的js处,而不能追踪到jsx中。
    所以为了映射到loader处理前的代码,我们一般也会加上module配置
总结
1、开发环境
综上所述,考虑到我们在开发环境对sourceMap的要求是:快(eval),信息全(module),且由于此时代码未压缩,我们并不那么在意代码列信息(cheap),所以开发环境比较推荐配置:devtool: cheap-module-eval-source-map
2、生产环境
一般情况下,我们并不希望任何人都可以在浏览器直接看到我们未编译的源码,所以我们不应该直接提供sourceMap给浏览器。但我们又需要sourceMap来定位我们的错误信息, 这时我们可以设置hidden-source-map:
一方面webpack会生成sourcemap文件以提供给错误收集工具比如sentry,另一方面又不会为 bundle 添加引用注释,以避免浏览器使用。
当然如果没有这一类的错误处理工具,可以看看webpack推荐的其他配置:
https://www.webpackjs.com/configuration/devtool/
CSS sourceMap
说起sourceMap我们第一反应通常是JavaScript的sourceMap,实际上现在css也可以使用sourceMap。因为sourceMap本质只是一个json,里面包含了源码的映射信息。所以其实只要了解sourcemap的编码规范,我们可以对任何我们想要的资源生成sourceMap,当然sourceMap 的支持也还是要取决于浏览器的支持。
现在,对于css我们也有同样诉求,比如我现在打开调试器看到的样式配置没有任何源信息。如果想像js一样,知道这个css样式是在哪个文件需要怎么弄呢?
前端知识一
文章图片
image.png 上面讲解的配置其实都是针对js的sourceMap,配置后webpack会自动帮我们生成各类js sourceMap。因为本质上webpack只处理js,对于webpack来说,css是否有sourceMap依赖于对css处理的loader是否有sourceMap输出,所以loader需要开启并传递sourceMap,这样最后生成的css才会带上sourceMap 。
目前使用的css-loader,sass-loader都已经提供了生成sourceMap的能力,只需要我们加上配置即可。
需要注意的是,这里如果要拿到sass编译前的源码信息,那么sourceMap一定要从sass-loader一直传递到css-loader,中间如有其他loader处理,也要透传sourceMap
前端知识一
文章图片
image.png 可以看到,加了sourceMap 配置后,sourceMap会被内联在css代码里(这一层是css-loader处理的,与你是否使用min-extract-css-plugin抽出css无关)
前端知识一
文章图片
image.png 加了css sourceMap后,我们可以很轻松的定位到sass编译前的源码路径了。
前端知识一
文章图片
image.png 通过debug,打印出生成的css sourceMap,和js sourceMap对比并无他样:
前端知识一
文章图片
image.png 利用css sourceMap 解决css url resolve的问题
如果大家用了sass的话,很可能会遇到一个css url resolve的问题,在之前的一篇讲webpack 配置的文章里我也提到过:
前端知识一
文章图片
image.png 实际上,利用css sourceMap这个问题便可以在不改变源码的情况下就可以完美解决。
这里会增加一个loader去处理,loader处理流程主要分为二步:
1、根据sourceMap的sourcesContent和url内容进行匹配,然后从sources定位到原有的css资源路径
2、将传递给下个loader的url内容替换成绝对路径
代码如下:
module.exports = function (content, map) { const res = content.replace(/url((?: |")?((./|../)+([^ ")]*))( |")?)/g, (str, img, p2, imgPath) => { let index = -1; const {sourcesContent = [], sources = [], sourceRoot = []} = map || {}; sourcesContent.some((item, i)=> { if (item.indexOf(img) !== -1) { index = i; return true; } }); if (index !== -1) { const dir = path.dirname(sources[index]); // 获取文件所在目录 str = str.replace(img, `~${path.join(dir, img)}`); } return str; }); this.callback(null, res, map); return; }

因为依赖sass-loader 处理之后的sourceMap, 所以@tencent/im-resolve-url-loader应配置在sass-loader 前面,配置如下:
前端知识一
文章图片
image.png 说明:sourcemap部分内容,完全复制前端Q公众号文章:hSourceMap知多少:介绍与实践
SVG矢量图操作 概述 svg是有一种基于xml语法的图像格式,全称是可缩放矢量图。其他图像格式都是基于像素处理的,svg则是属于对图像的形状描述,所以它本质上是文本文件,体积比较小,且不管放多少倍都不会失真。
SVG文件可以直接插入网页,成为DOM的一部分,然后js和css进行操作。
Document - 锐客网

上面是svg代码直接插入网页的例子。
SVG代码也可以写在一个独立文件中,然后使用img/object/iframe/embed等标签插入网页。
前端知识一
文章图片

案例一


前端知识一
文章图片
image.png 案例二
SVG绘制条形图
- 锐客网.axis{ stroke: #999; stroke-width: 2px; } 时间 时间 订单

前端知识一
文章图片
image.png CSS相关 自定义css属性
即css变量,这样可以做到类似于less的效果,一改全改
Document - 锐客网:root { --div-color: red; }#d1 { width: 100px; height: 100px; background-color: var(--div-color); }#d2 { width: 200px; height: 200px; background-color: var(--div-color); }d1d2

注意:自定义css属性名称的时候,必须--开头,必须两个,这是规范
H5属性的操作 预定义属性
h5提供了classList属性获取class的属性值,没有这个api之前,需要一点点获取attribute属性然后通过复杂匹配获取。

输出结果 前端知识一
文章图片
图一.png 自定义属性
h5之前,都是通过setattribute操作自定义属性

H5案例:
如果是data-开头的自定义属性,可以通过

H5可编辑属性
可编辑属性

前端知识一
文章图片
可编辑属性.png H5语义化标签 hgroup
代表网页或者section的标题,当元素有多个层级时,该元素可以将h1到h6元素放在其内,譬如文章主标题和副标题的组合。
使用细节:
如果只有一个h1-h6标签不要使用hgroup
如果有连续多个h1-h6标签就用hgroup
如果有连续多个标题和其他文章数据,h1-h6标签就用hgroup标签包裹住,然后和其他文章元素一起放进header标签
header
代表网页或secction的页眉,通常包含h1-h6或hgroup
使用细节:
可以是"网页"或者任意"section"的头部部分
没有个数限制
如果hgroup或h1-h6自己就能工作很好,不要使用header
nav
代表页面的导航链接区域,用于定义页面的主要导航部分
用在整个页面的主导航部分,不适合不要用nav元素
section
代表文档中的节或段,段可以是指一篇文章里按照字体的分段,节可以指一个页面的分组
使用细节:
section不是一般意义上的容器元素,如果想作为样式展示和脚本的便利,可以用div。
article、nav、aside可以理解为特殊的section,
所以如果可以用article、nav、aside就不要用section,没实际意义的就用div
section是啥?关于sectionsection的介绍
关于其他
关于其他section的介绍

article
article元素最容易跟section和div容易混淆,其实article代表一个在文档,页面或者网站中自成一体的内容
使用细节:
独立文章:用article
单独的模块:用section
没有语义的:用div
一篇文章文章内容..
版权:html5jscss网所属,作者:damu

aside
aside元素被包含在article元素中作为主要内容的附属信息部分,其中的内容可以是与当前文章有关的相关资料、标签、名次解释等.
在article元素之外使用作为页面或站点全局的附属信息部分。最典型的是侧边栏,其中的内容可以是日志串连,其他组的导航,甚至广告,这些内容相关的页面。
使用细节:
aside在article内表示主要内容的附属信息,
在article之外则可做侧边栏
如果是广告,其他日志链接或者其他分类导航也可以用
内容
作者简介小北,前端一枚

footer
footer元素代表 网页 或 section 的页脚,通常含有该节的一些基本信息,譬如:作者,相关文档链接,版权资料。
使用细节:
footer使用注意:
可以是 网页 或任意 section 的底部部分;
没有个数限制,除了包裹的内容不一样,其他跟header类似。
COPYRIGHT@damu

Prop&&Attr checkbox

说明:多选框中的checked属性除非不写,不然不论赋什么值,还是不写值,都代表选中。
  • attribute
html的预定义属性和自定义属性
input标签:针对的html
所以如上:checked是input标签的attribute
  • property
js原生对象的直接属性
每一个预定义的attribute都会有一个property与之对应
input节点:针对的是js
所以如上:checked是input节点的property
  • 布尔值属性和非布尔值属性

  • 属性使用attr还是prop的区分图

    前端知识一
    文章图片
    prop&attr.png
能使用attribute尽量使用attribute,性能高。
基本案例:
此时只有在第一次点击全选才有效,如果一旦第一次点击操作过property则全选无效,例如此时的水果三个全部手动选中,都是操作property,则在点击全选无效。如果在html全部给水果加上checked属性就相当于操作了property,此时即使第一次点击全选也无效。
H5 - 锐客网 苹果 栗子 香蕉

对比案例:
checked属性是布尔值属性,如果使用操作property则一直有效
H5 - 锐客网 苹果 栗子 香蕉

Web Workers
  • Worker:构造函数,加载分线程执行的js文件
  • Worker.prototype.onmessage: 用于接收另一个线程的回调函数
  • Worker.prototype.postMessage:向另一个线程发送消息
  • 缺点:worker内代码不能更新UI,不能跨域加载js,浏览器支持问题
说明:必须在服务器环境下,不能本地file,加载的js不能跨域,使用webworker的代码不要使用
箭头函数,let等新特性。
案例
html中的js var worker = new Worker('a.js'); worker.postMessage(8); worker.onmessage = function (event) { console.log("接收的数据",event.data); } a.js // 斐波那契数列 function fibonacci(n){ return n<=2?1:fibonacci(n-1)+fibonacci(n-2); } var onmessage = function(event) { let res=fibonacci(event.data); postMessage(res); } 说明:之所以webworker代码中不能更新UI,是因为this指向是[object DedicatedWorkerGlobalScope], 即a.js中上下文不是window,没有UI更新相关的接口。

浏览器渲染机制 浏览器,再内核控制下相互配合以保持同步。它至少三个常驻的线程,JS引擎线程,GUI渲染线程,浏览器事件触发线程。
  1. js引擎是基于事件驱动单线程执行的
  2. 渲染线程负责渲染浏览器界面,但是GUI渲染线程与JS引擎互斥的,当JS引擎执行时GUI会被挂起;
    GUI的更新也会被保存在一个队列中,等到JS引擎空闲时候才有机会被执行。这就是JS阻塞页面加载
  3. 事件触发线程,当一个事件被触发时候该线程会把事件添加到任务队列的对尾,等待JS引擎的处理

    前端知识一
    文章图片
    1.png
浏览器内核
  • Trident内核:IE
  • Webkit内核:Chrome,Safari
  • Gecko内核:FireFox
流程
  • 引擎一开始会从网络获取请求文档的内容,然后进行如下所示的基本流程:

    前端知识一
    文章图片
    2.png
    呈现引擎将开始解析HTML文档,并将各标记逐个转化成内容树上的DOM节点。同时也会将解析外部CSS文件以及样式元素种的样式数据。
    HTML种这些带有视觉指令的样式信息将用于创建另一个树结构:呈现树。
呈现树包含多个带有视觉属性(如颜色和尺寸)的矩形。这些矩形的排列顺序就是它们将在屏幕上显示的顺序。
呈现树构建完毕之后,进入"布局"处理阶段,也就是为每个节点分配一个应出现在屏幕上的确切坐标。下一个阶段是绘制-呈现引擎会遍历呈现树,由用户界面后端层将每个节点绘制出来。
  • 当用户访问页面时,浏览器需要获取用户请求内容,这个过程主要涉及浏览器网络模块:
  1. 用户在地址栏输入域名,如baidu.com,DNS服务器根据输入的域名查找对应的IP,然后向该IP地址发起请求
  2. 浏览器获取并解析服务器的返回内容
  3. 浏览器加载HTML文件及文件内包含的外部引用文件以及图片,多媒体等资源
这是一个渐进的过程。为了达到更好的用户体验,呈现引擎会力求尽快将内容显示在屏幕上。它不必等到整个HTML文档解析完毕之后,就会开始构建呈现树和设置布局。在不断接收和处理来自网络的其余内容的同时,呈现引擎会将部分内容解析并显示出来。

前端知识一
文章图片
3.png 浏览器渲染过程
前端知识一
文章图片
image.png 下半部分是上面的拆解;最后dispaly是显示;
简化版的渲染过程
前端知识一
文章图片
image.png 重排重绘
重点:其中重排也就是回流;注意:修改颜色(样式变化)等,会引起重绘;而修改大小(几何变化)等会引起重排
前端知识一
文章图片
image.png 从chrome性能监控上面可以发现,layout消耗了大量的CPU时间片。而Paint次之;所以能尽量避免重排就已经可以提供性能了;而下方会说明通过css3实现GPU加速避免重排重绘,提高性能,出让CPU,而使用GPU渲染。
Composite 合成线程
  • Paint阶段就是绘制,但并不是把页面绘制到显示器上。绘制的本质是填充像素的过程,包括绘制文字、颜色、图像、边框、阴影等效果,也就是一个DOM元素的所有可视效果。
  • 而且,绘制过程一般是在多个图层上完成的,这些图层我们称之为渲染层。渲染层将保证页面中的元素以正确的顺序堆叠排列。
  • 也就是说,我们的页面不是2D平面的,而是三维立体的!
  • 前端所说的层叠上下文,其实正是因为页面的3D特性。
  • Composite阶段就是负责把所有图层,按照合理的顺序合并成一个图层。对于有元素位置重叠的页面,这个过程尤其重要。因为一旦图层的合并顺序出错,将会导致元素显示异常。
  • 这个模型类似PhotoShop的图层模型。在PhotoShop中,每个设计元素都是一个独立的图层,多个图层以前挡的顺序在Z轴空间上叠加,最终构成完整的设计图。
合成层
合成层是一种特殊的渲染层,这也是我们讨论的重点。
Chrome浏览器提供了查看图层的工具:Layers,虽然很难用,但也可以看出其中的层级。
先来看看淘宝的渲染层和合成层
CSS3 跳过重排重绘,避开CPU,直接进入GPU硬件加速,执行动画,正是在合成层完成的。不影响其他渲染层。
淘宝首页的banner提升到合成层:transform: translate3d()
提升合成层的因素有很多,这里我们只演示 CSS3 transform
代码案例~~~~~ 常用的还有will-change #target { will-change: transform, opacity; }

渲染层自动提升合成层
合成层并不是一定要通过手动开启的,有些原因也会自动提升合成层,其中,重叠原因是最常见的。

前端知识一
文章图片
image.png 层爆炸与层压缩
  • 天下没有白吃的午餐!合成层虽好,但也是需要付出代价的!
  • 过多的合成层,意味着更复杂的图层管理,意味着对资源的消耗。
  • 这就是层爆炸。
  • 而且,GPU是显卡的核心部件,显卡的配置往往是比较低的,内存有限,很容易因为层爆炸而崩溃。
  • 当然,浏览器也想到了这一点,因此有了层压缩。
  • 当多个渲染层与同一个合成层重叠时,这些渲染会被压缩到一个图层上。
  • 但是,层压缩机制也不是万能的,也有很多原因不能进行层压缩,比如,不能进行会打破渲染顺序的压缩。
css3直接使用GPU加速小结
  • 在实际开发中,我们常见一些CSS基础库会使用:box-sizing: border-box; 原因就是为了固定盒子大小,尽可能减少重排。
  • 尽量使用CSS3动画替代JS模拟动画,不仅可以利用硬件加速,减少重排,还不会占用JS主线程。而且浏览器会对CSS动画做优化。
  • transform: translateZ(0); 常用来欺骗浏览器,提升合成层,利用GPU硬件加速。
  • 性能优化并没有所谓的“银弹”,transform: translateZ(0) 不是,本文列出的优化建议也不是。
  • 抛开了对页面的具体分析,任何的性能优化都是站不住脚的,盲目的使用一些优化措施,结果可能会适得其反。
  • 因此切实的去分析页面的实际性能表现,不断的改进测试,才是正确的优化途径。
什么是html的解析?
解析html/css文档是指将文档转化成为有意义的结构,也就是可让代码理解和使用的结构。
解析得到的结果通常是表示了文档结构的节点树,它被称为解析树或者语法树。
解析的过程可以分为两个子过程:词法分析和语法分析
  • 词法分析
    词法分析是将输入内容分割成大量标记的过程。
    其实html本质上就是字符串,;
    需要在词法分析过程转变成一个个的词

-语法分析
应用语言的语法规则的过程。在认类语言中,相当于字典中的单词。
根据(html or css等)语言的语法规则分析文档的结构,从而构建解析树。
例子:2+3-1,分析过程:

前端知识一
文章图片
4.png
这种方式被称为, 移位规约解析器,因为输入在向右移动(设想有一个指针从输入内容的开头移动到结尾)(这之前是词法分析), 并且逐渐归约到语法规则上。
  • HTML解析器
    任务是将HTML标记解析成解析树。
    HTML的定义采用DTD的格式。此格式可用于定义SGML族的语言。它包括所有允许使用的元素及其属性和层次结构的定义。
    现在都是使用html5的规范来解析了,DTD不使用了。
  • DOM解析器
    作用是,输出"解析树"。是由DOM元素和属性节点构成的树结构。
Document - 锐客网

首先浏览器从上到下依次解析文档构建的DOM树

前端知识一
文章图片
5.png
  • CSS例子:

    前端知识一
    文章图片
    6.png
前端知识一
文章图片
7.png
在上面的过程主要有两个阶段:
  1. 标记化
  2. 树的构建:
标记化是词法分析的过程,将输入内容解析程多个标记。例如:HTML标记包含起始标记,结束标记,属性名称和属性值。不断的解析,直到加载网页结束。
树的构建:在创建解析器的同时,也会创建Document对象。在树的构建阶段,以Document为根节点的DOM树也会不断进行修改,向其中添加各种元素。标记生成器发送的每个节点都会由树构建器进行处理。
这些标记元素不仅会添加到DOM树种,还会添加到开放元素的堆栈种。此堆栈用于纠正嵌套错误和处理未关闭的标记。
其算法也可以用状态机来描述,这些状态成为"插入模式"。
  • Dom树和渲染树
    每一个渲染对象都对应着DOM节点

    前端知识一
    文章图片
    8.png
创建渲染树之后,下一步就是布局(Layout)这个过程就是通过渲染树中渲染对象的信息,计算出每一个渲染对象的位置和尺寸,将其安置在浏览器窗口的正确位置。
有些时候我们会在文档布局完成之后对DOM进行修改,这时候可能需要重新进行布局,也可以称其为回流(重绘)。
根据需要,或是全局重绘,或是局部重绘,最终触发"重新渲染页面"。
Object.create原理
Bell.prototyp=Object.create(EventEmitter.prototype); //等效于下面三行 function Temp() {} Temp.prototype=EventEmitter.prototype; Bell.prototype=new Temp(); //等效于下面 let bell=new Bell(); bell.__proto__=EventEmitter.prototype;

事件捕获和冒泡 dom标准事件流的触发的先后顺序为:先捕获再冒泡。即当触发dom事件时,会先进行事件捕获,捕获到事件源之后通过事件传播进行事件冒泡。
前端知识一
文章图片
1.png
  • addEventListener的第三个参数
element.addEventListener(event, function, useCapture);

第三个参数默认值是false,表示在事件冒泡阶段调用事件处理函数; 如果参数为true,则表示在事件捕获阶段调用处理函数。
  • 事件冒泡
父元素子元素

前端知识一
文章图片
2.png 事件触发顺序是由内到外的,这就是事件冒泡。如果点击子元素不想触发父元素的事件,可使用event.stopPropagation(); 方法:
child.addEventListener("click",function(e){ console.log("click-child"); //该句是事件是否继续冒泡 e.stopPropagation(); },false); //false指的是在冒泡阶段执行,效果不同,配合起来使用可以精确控制

  • 事件捕获
var parent = document.getElementById("parent"); var child = document.getElementById("child"); document.body.addEventListener("click",function(e){ console.log("click-body"); },false); parent.addEventListener("click",function(e){ console.log("click-parent---事件传播"); },false); //新增事件捕获事件代码 parent.addEventListener("click",function(e){ console.log("click-parent--事件捕获"); },true); child.addEventListener("click",function(e){ console.log("click-child"); },false);

前端知识一
文章图片
3.png
父元素通过事件捕获的方式注册了click事件,所以在事件捕获阶段就会触发,然后到了目标阶段,即事件源,之后进行事件冒泡,parent同时也用冒泡方式注册了click事件,所以这里会触发冒泡事件,最后到根节点(body)。这就是整个事件流程。

    推荐阅读