橘子园

种橘子的地方

0%

监听内容

1 静态资源404
2 异步接口错误的上报
3 vue error错误上报
4 promise 捕获

原理

  1. vue error 监听
    • vue捕获了 vue 运行中产生的错误,故无法直接使用全局错误监听的方式监听此类错误,需使用 vue 提供的errorHandler 接口来实现对错误的监听。
  2. js error及 静态资源错误
    • 均使用 addeventlistener 函数在 window 上添加 error 事件监听。
    • 对于静态资源错误,不会冒泡,故上述监听函数应使用捕获模式。
    • 静态资源的网络相关错误信息(如错误码)无法通过常规方式得到,故需在监听到静态资源错误的时候,对发生错误的链接发送一个 HEAD 的请求,以获取对应的网络相关错误信息。
    • HEAD方法跟GET方法相同,只不过服务器响应时不会返回消息体。一个HEAD请求的响应中,HTTP头中包含的元信息应该和一个GET请求的响应消息相同。这种方法可以用来获取请求中隐含的元信息,而不用传输实体本身。也经常用来测试超链接的有效性、可用性和最近的修改。
  3. 异步接口错误
    • 重写XMLHttpRequest类的 send 和 open 方法,记录相关请求参数。
    • 监听readystatechange事件,当 status 为表示错误的状态码(即大于等于400)时,上报错误
  4. promise 捕获
     当我们希望在浏览器上捕获全局错误时会监听 window 对象的 error 事件,可是 error 事件只是捕获错误而已,对于 Promise 的 reject 未处理是不会触发的。如今在业务代码中我们越来越多地使用 Promise,导致很多被放在 Promise 中处理的逻辑错误仅仅是被 reject 没有触发 error。
      通过 unhandledrejection 事件,我们就可以捕获未处理的 reject 。
      因为 unhandledrejection 事件的接口中不包含 error 相关的信息,故我们无法获取堆栈信息,除非在 reject 的时候入参设置为一个 error 对象(如 reject(new Error('error')

三种错误类型

JavaScript错误

  • 即一切跟 JavaScript 相关的错误,包括
    • JavaScript运行时错误
    • JavaScript语法错误
    • Promise reject 未处理
    • vue 运行时错误
    • 用户自己抛出的错误。
  • 这些错误的共同特点是有堆栈信息,且堆栈信息有意义。

    静态资源错误

  • 即静态资源加载错误,如图片链接404等。
  • 这些错误的共同特点是,有 DOM 信息。

    AJAX错误

  • 即 AJAX 请求时发生的错误,如服务器返回400,500等。
  • 这些错误的共同特点不言而喻,都是 AJAX 请求。
  • 后续考虑升级成对 HTTP 请求的监听

压缩

使用lz-string

压缩效率

原大小的35.63%,减少了64.37%

压缩方式

gzip

gzip与deflate对比

gzip的基础是DEFLATE,DEFLATE是LZ77与哈夫曼编码的一个组合体,二者区别主要有:

  1. 两者都是使用Gzip压缩算法
  2. deflate压缩速度略快,gzip压缩比略高。默认情况下,gzip会比deflate多压4%-6%
  3. gzip对CPU占用要高一些,deflate是专门为保护性能的压缩模块,它仅需很小的资源来压缩文件
    综上,我们更关注压缩比,故选择 gzip

错误合并逻辑

javascriptError

所在文件 类型 信息共5个因素一致即为同一错误

httpError

url status statusText 3个因素一致即为同一错误

resourceError

url status statusText xpath 四个因素一致即为同一错误

定义

.offset()方法返回对应元素边框盒(包括外边距)基于文档的坐标值。
The .offset() method allows us to retrieve the current position of an element (specifically its border box, which excludes margins) relative to the document.

核心源码

1
2
3
4
5
6
7
// Get document-relative position by adding viewport scroll to viewport-relative gBCR
rect = elem.getBoundingClientRect();
win = elem.ownerDocument.defaultView;
return {
top: rect.top + win.pageYOffset,
left: rect.left + win.pageXOffset
};

getBoundingClientRect返回的是基于视口的坐标。
如果不希望属性值随视口变化,那么只要给top、left属性值加上当前的滚动位置(通过window.scrollX和window.scrollY),这样就可以获取与当前的滚动位置无关的(基于文档的)常量值。

视口,窗口,文档

  • 视口(viewport):当前浏览器可视区域
  • 窗口(window):当前浏览器
  • 文档(document):当前文档

Chrome DevTools 重要快捷键(Mac)

访问 DevTools

打开/切换检查元素模式和浏览器窗口 Cmd + Shift + C
打开 Developer Tools 并聚焦到控制台 Cmd + Opt + J

全局

显示设置 ?
上/下一个面板 Cmd + [/]
更改停靠位置 Cmd + Shift + D
Device Mode Cmd + Shift + M
切换控制台 ESC
在所有源中搜索文本 Cmd + Opt + F
按文件名搜索(除了在 Timeline 上) Cmd + O、Cmd + P

Elements

展开/折叠节点及其所有子节点 Opt + 点击箭头图标
隐藏元素 H
切换为以 HTML 形式编辑 F2

style

转到源中样式规则属性声明行 Cmd + 点击属性
在颜色定义值之间循环 Shift + 点击颜色选取器框
编辑下一个/上一个属性 Tab、Shift + Tab
以 10 为增量增大/减小值 Shift + Up、Shift + Down PgUp、PgDown
以 100 为增量增大/减小值 Shift + PgUp、Shift + PgDown
以 0.1 为增量增大/减小值 Opt + 向上键、Opt + 向下键

Sources

暂停/继续脚本执行 F8、Cmd + \
越过下一个函数调用 F10、Cmd + ‘
进入下一个函数调用 F11、Cmd + ;
跳出当前函数 Shift + F11、Cmd + Shift + ;
转到行 Ctrl + G
转到成员 Cmd + Shift + O

复盘-fix不同node_modules引起的bug

一份代码,线上的node_modules包是已有的,线下的node_modules包是我近期通过package.json自行yarn得来的.
当我把线下的node_modules包替换到线上时,线上报错,此时线下正常.
顺着错误信息一番debug,乱七八糟的代码使人眼晕,也查不出什么所以然.
此时冷静分析一下:同一份代码,线下正常,线上出错,二者有何区别?

  • 环境
  • 构建方式
    环境不去说他,小概率事件,构建方式有何不同呢,线下是develop构建,而线上是deploy,
    二者的差别是,deploy的gulp流程多了rev,compress两个步骤,分而治之,证明是compress出了问题.
    compress中的流程继续分而治之,是gulp-uglify这个包除了问题,对比一下,果然,旧的node_modules里gulp-uglify版本为2.0.0,新的为2.1.2,替换之,遂解决.
    具体的原因日后再更新.

事件

HTML事件处理程序

即通过与事件处理程序同名的HTML特性来实现.
<input type="button" onclick="alert('HTML')">

  • 有权访问全局作用域中任意代码.
  • this值为事件的目标元素.
  • 作用域扩展至document以及本元素本身,即相当于with(this).
  • 如果当前元素是一个表单输入元素,作用域扩展至整个表单元素,即相当于with(this.form).
  • 生成event对象,不必自己定义,亦不必从函数参数列表中获取.
  • 存在时差问题,当HTML加载时,对应的JavaScript可能没有加载.
  • 前述提到的作用域扩展在浏览器间行为并不一致.
  • 可以通过设置当前元素DOM的对应事件处理属性为null的方式销毁事件.
  • 显然应该在事件到达元素本身的时候处理.

    DOM0级事件处理程序

    DOM0指第四代浏览器(IE4.0, Netscape4.0)最初支持的DOM规则,并不是事实存在的标准.
    将一个函数赋值给一个事件处理程序属性,首先必须取得一个要操作对象的引用.
    let btn = document.getElementById('btn'); btn.onclick = () => { alert('DOM0'); }
  • 简单,跨浏览器兼容.
  • this引用当前元素.
  • 在冒泡阶段被处理
  • 销毁时设置对应属性为null btn.onclick = null;

    DOM2级事件处理程序

    addEventListener removeEventListener
    接受三个参数,要处理的事件名,事件处理函数,布尔值(true,捕获阶段处理事件;false,冒泡阶段处理事件)
  • this引用当前元素
  • 按顺序添加的事件按顺序执行
  • addEventListener添加的事件只能由removeEventListener移除,移除时需参数相同,这意味着匿名事件处理函数将无法移除.
  • 捕获浏览器兼容性较差

《你不知道的JavaScript》(上卷)读书笔记

第一部分

语言

事实上,JavaScript是一门编译语言,但与传统的编译语言不同,他不是提前编译的,编译结果也不能在分布式系统中进行移植.

JavaScript引擎进行编译的步骤与传统语言非常相似,包括分词/词法分析,解析/语法分析,代码生成.

比起那些编译过程只有三个步骤的语言的编译器,JavaScript在语法分析和代码生成阶段有特定的步骤对运行性能进行优化,包括对冗余元素进行优化等

LHS与RHS

  • LHS:左值查询,试图找到变量的容器本身,从而可以对其赋值.
  • RHS:右值查询,与简单地查找某个变量的值别无二致.
  • 从概念上讲,LHS查找赋值的目标,RHS查找赋值的源头.

    词法作用域

    是定义在词法阶段的作用域,换句话说,是由你在写代码时将变量和块作用域写在哪里决定的,区别于动态作用域.

    欺骗词法作用域

    eval
  • 接受一个字符串为参数,并将其中的内容视为好像在书写时就存在于程序中的这个位置的代码.
  • 严格模式下,eval有自己的词法作用域.
  • JavaScript中还有一些类似的功能效果,例如setTimeout以及setInterval第一个参数(本质上还是调用了eval),以及new Function的最后一个参数.
    with
  • 尽管with块可以将一个对象处理为词法作用域,但是这个块内部正常的var声明比并不会被限制在这个块的作用域中,而是被添加到with所处的函数作用域中.
  • 严格模式下被禁用了.
    性能问题
    编译阶段的很多优化没法做了.

    函数作用域

    函数声明与函数表达式

    区分函数声明和表达式最简单的方法是看function关键字出现在生命中的位置。如果function是声明中的第一个词,那么就是一个函数声明,否则就是一个函数表达式。

    匿名与具名

    始终给函数表达式命名是一个最佳实践。

    未完待续

px在CSS中指CSS像素,是相对于设备像素相对值.
在W3C的Web Style Sheets CSS tips & tricks中提到

In fact, CSS requires that 1px must be exactly 1/96th of an inch in all printed output.

所以这就很明白了.
比如iPhone 5使用的是Retina视网膜屏幕,使用2px x 2px的 device pixel 代表 1px x 1px 的 css pixel,所以设备像素数为640 x 1136px,而CSS逻辑像素数为320 x 568px。
需要用的时候,注意换算像素比就是了.

区别

根源上来说,参看RFC2616

GET

The GET method means retrieve whatever information (in the form of an
entity) is identified by the Request-URI. If the Request-URI refers
to a data-producing process, it is the produced data which shall be
returned as the entity in the response and not the source text of the
process, unless that text happens to be the output of the process.

The semantics of the GET method change to a “conditional GET” if the
request message includes an If-Modified-Since, If-Unmodified-Since,
If-Match, If-None-Match, or If-Range header field. A conditional GET
method requests that the entity be transferred only under the
circumstances described by the conditional header field(s). The
conditional GET method is intended to reduce unnecessary network
usage by allowing cached entities to be refreshed without requiring
multiple requests or transferring data already held by the client.

The semantics of the GET method change to a “partial GET” if the
request message includes a Range header field. A partial GET requests
that only part of the entity be transferred, as described in section
14.35. The partial GET method is intended to reduce unnecessary
network usage by allowing partially-retrieved entities to be
completed without transferring data already held by the client.

The response to a GET request is cacheable if and only if it meets
the requirements for HTTP caching described in section 13.

See section 15.1.3 for security considerations when used for forms.

POST

The POST method is used to request that the origin server accept the
entity enclosed in the request as a new subordinate of the resource
identified by the Request-URI in the Request-Line. POST is designed
to allow a uniform method to cover the following functions:

- Annotation of existing resources;

- Posting a message to a bulletin board, newsgroup, mailing list,
  or similar group of articles;

- Providing a block of data, such as the result of submitting a
  form, to a data-handling process;

- Extending a database through an append operation.

The actual function performed by the POST method is determined by the
server and is usually dependent on the Request-URI. The posted entity
is subordinate to that URI in the same way that a file is subordinate
to a directory containing it, a news article is subordinate to a
newsgroup to which it is posted, or a record is subordinate to a
database.

The action performed by the POST method might not result in a
resource that can be identified by a URI. In this case, either 200
(OK) or 204 (No Content) is the appropriate response status,
depending on whether or not the response includes an entity that
describes the result.

If a resource has been created on the origin server, the response
SHOULD be 201 (Created) and contain an entity which describes the
status of the request and refers to the new resource, and a Location
header (see section 14.30).

Responses to this method are not cacheable, unless the response
includes appropriate Cache-Control or Expires header fields. However,
the 303 (See Other) response can be used to direct the user agent to
retrieve a cacheable resource.

POST requests MUST obey the message transmission requirements set out
in section 8.2.

See section 15.1.3 for security considerations.

可知,二者最本质的区别在于:

- GET 方法意思是获取被请求 URI(Request-URI)指定的信息(以实体的格式).
- POST 方法被用于请求源服务器接受请求中的实体作为请求资源的一个新的从属物.

除此之外,二者较为本质的区别还有:

- GET幂等,POST不幂等.
- GET强制服务器支持,而POST在规范(HTTP/1.1)中为可选支持.
- GET请求的相应是可缓存的,POST 方法的响应是不可缓存的。除非响应里有合适的 Cache-Control 或者 Expires 头域。然而,303响应能被用户代理利用去获得可缓存的响应

我们再去看一下目前充斥于搜索引擎搜索结果的所谓二者的区别:

- 后退/刷新时,get无害(相当于重新获取),post会重新提交(重新修改):基于二者**本质区别**
- GET 请求可被缓存,POST不可以:正确.
- GET 请求参数保留在浏览器历史记录中,而POST不可以:因为GET参数就在url中,显然.
- GET 请求可被收藏为书签,POST不可以:同上,显然.
- GET 请求不应在处理敏感数据时使用:显然.
- GET 请求有长度限制:错误,GET请求长度体现在url,而http相关规范**从来没有**规定过url的长度限制,所谓的限制只是**特定浏览器**的限制.多为2048B或者1024B
- GET 请求只应当用于取回数据
- GET编码类型限定为application/x-www-form-urlencoded,POST有多种类型(下面会总结一些常用的类型)
- GET只允许ASCII数据类型,POST无限制:大多数浏览器服务器如此实现
- GET方法产生一个TCP数据包;POST方法产生两个TCP数据包:同上,比如Firefox就不是这么玩的.
- 安全性,呵呵呵呵呵
- 可见性,同上.

一言以蔽之,除了上述的本质区别,较为本质的区别,其他GET和POST的玩法都是浏览器,服务器的规定而已,我们可以给POST用url传参,也可以给GET加上报文体,毕竟HTTP规范只在语义上规定了二者嘛.

POST的content-type

比较常见的

  • application/x-www-form-urlencoded 在报文体里以和GET相同的格式传输数据
  • application/json 消息主体是序列化的JSON字符串
  • multipart/form-data 传文件

    表单的content-type

    正常情况下,默认application/x-www-form-urlencoded,可换成multipart/form-data,具体参见W3C相关资料

部分信息为道听途说,未经验证,如有谬误,欢迎指正.

理解

可以理解为”版本号“,即父元素为主版本,子元素为次要版本,由此来确立层叠顺序.

父子

父子元素下,只要父元素设置了z-index值,无论子元素如何设置,其都在其父元素之上显示,但是,如果父元素没有设置,或设置为默认值,当子组件设置z-index为负值时,子组件会放置于父组件之下.

默认值

z-index的默认值auto的意思为,不新建堆叠上下文,元素所在的堆叠上下文和其父元素相同.

问题

基于以上规则,子元素作为次要”版本号”无法堆叠在其父版本的兄弟版本上时,解决思路有可以通过移除不同级别的菜单之间的重叠,或者使用ID选择器指定独立的(不同的)z-index值,或者减少HTML的层级。

有这样一个需求,各个标签用行内元素(例如span)包裹(这样做往往是考虑到HTML语义),当容器宽度不足时,完整换行,即当行内剩余空间不足展示一个标签时,换行,而非标签内分断.
稍有常识的人一定知道,直接用span是不行的.
解决的方案当然也是十分简单,设置这些span的display属性为inline-block就可以了.
值得吐槽的是,chrome显示的行内元素的padding区域在出现换行的时候,会在每一行都显示,这样就会出现除最后一行外,其他行的内容会在”padding区域”(实际上并不是)显示,可能会对一些新手造成困扰.
Firefox显示正常.


2020-5-18

设置这些span的display属性为inline-block怎么就可以了呢?
是因为设置为inline-block的元素等同行内替换元素,无换行,就好比一个图片,图片怎么可能换行呢?当然这里说的换行不是指其内容不可换行,而是指其自身不可换行(比如匿名文本元素和普通的行内非替换元素自身就可以换行)

图画里 龙不吟虎不啸 小小书童可笑可笑