DOMContentLoaded与load简介

DOMContentLoaded事件在页面文档加载解析完毕之后马上执行。而不会等待图片文件和子框架页面的加载

load事件会在页面所有资源被加载进来之后才会触发load事件。load时间在DOMContentLoaded事件触发之后

页面的加载顺序:

1.从上往下解析html节点,生成dom树和css树

2.如果遇到js或css文件,则加载js脚本文件和css等

3.js脚本如果是内联,则会执行脚本,否则继续解析

4.在页面能显示内容的时候,会触发domcontentloaded,不会等待图片等资源加载

5.在图片等资源都加载完成后,才会触发load

输入URl后发生的事

http://www.dailichun.com/2018/03/12/whenyouenteraurl.html

  1. 从浏览器接收url到开启网络请求线程(这一部分可以展开浏览器的机制以及进程与线程之间的关系)
  2. 开启网络线程到发出一个完整的http请求(这一部分涉及到dns查询,tcp/ip请求,五层因特网协议栈等知识)
  3. 从服务器接收到请求到对应后台接收到请求(这一部分可能涉及到负载均衡,安全拦截以及后台内部的处理等等)
  4. 后台和前台的http交互(这一部分包括http头部、响应码、报文结构、cookie等知识,可以提下静态资源的cookie优化,以及编码解码,如gzip压缩等)
  5. 单独拎出来的缓存问题,http的缓存(这部分包括http缓存头部,etag,catch-control等)
  6. 浏览器接收到http数据包后的解析流程(解析html-词法分析然后解析成dom树、解析css生成css规则树、合并成render树,然后layout、painting渲染、复合图层的合成、GPU绘制、外链资源的处理、loaded和domcontentloaded等)
  7. CSS的可视化格式模型(元素的渲染规则,如包含块,控制框,BFC,IFC等概念)
  8. JS引擎解析过程(JS的解释阶段,预处理阶段,执行阶段生成执行上下文,VO,作用域链、回收机制等等)
  9. 其它(可以拓展不同的知识模块,如跨域,web安全,hybrid模式等等内容)

Dom树 CSS树 渲染树(render树) 规则、原理

浏览器的进程和线程

浏览器阻塞问题

在没有js的情况下,dom树和cssom会好好的并行解析,但是如果dom解析的时候遇到了link,也就是外链样式而且在这个样式后紧跟着的是js对cssom的查询操作的话,css就会阻塞脚本,从而阻止了dom(webkit内核优化过,如果没操作dom的就不会去等cssom,不会阻塞cssom),等cssom完成了再去执行脚本最后才继续dom解析。

如果有外部样式和外部脚本同时引用的时候,两者都会并行加载,谁先加载好谁就开始解析,不过只有当外部样式解析完生成cssom树以后。

JS文件不止会阻塞DOM的构建,也会导致CSSOM的构建。不完整的CSSOM是无法使用的,JavaScript想要访问CSSOM并更改它,就必须得到完整的CSSOM。所以导致浏览器在未完成CSSOM的构建的时候想要运行JavaScript。

这种情况下,浏览器会先下载和构建CSSOM,然后再执行JavaScript,最后再继续dom解析构建。

预解析

浏览器有个预解析阶段,就是在浏览器收到了http返回的代码之后,在执行脚本时阻塞时会先开启新线程预解析文档其余部分收集需要资源的url,然后再调用放进网络请求的线程里,同步并行请求那些资源,即使其他阻塞了也会一直保持加载资源,最后再将返回资源交给渲染进程进行渲染。

重绘和回流

减少重绘和回流措施

  • 使用transfrom代替top
  • 使用visibility代替display: none(前者引起重绘,后者引起回流)
  • 不要把节点的属性值放在一个循环里当成循环的变量
  • 不要使用table布局(小改动可能造成整个table重新布局)
  • CSS选择符从右往左匹配查找,避免节点层级过多
  • 动画实现的速度的选择,动画速度越快,回流次数越多,或者选择使用requestAnimationFrame
  • 将频繁重绘或回流的节点设置为图层,图层能够阻止该节点影响到别的节点。

临时性死区

  • let、const不存在变量提升,在声明前使用会报错:Uncaught ReferenceError,var会进行变量提升,在var声明之前就访问对应的变量,则会得到undefined。
  • TDZ(临时性死区):流程在进入作用域创建变量,到变量开始可被访问访问之前的一段时间,称为临时性死区

flex布局

父元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
display: flex;

/* 决定主轴的方向(即项目的排列方向) */
flex-direction: row | row-reverse | column | column-reverse

/* 决定容器内项目是否可换行 */
flex-wrap: nowrap | wrap | wrap-reverse

/* 两个属性的拼接 */
flex-flow: <flex-direction> || <flex-wrap>

/* 定义了项目在主轴的对齐方式 */
justify-content: flex-start | flex-end | center | space-between | space-around

/* 定义了项目在交叉轴上的对齐方式 */
align-items: flex-start | flex-end | center | baseline | stretch

/* 定义了多根轴线的对齐方式,如果项目只有一根轴线,那么该属性将不起作用 */
align-content: flex-start | flex-end | center | space-between | space-around | stretch

子元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 /* 定义项目在容器中的排列顺序,数值越小,排列越靠前,默认值为 0  */ 
order

/* 定义了在分配多余空间之前,项目占据的主轴空间,浏览器根据这个属性,计算主轴是否有多余空间 */
flex-basis: <length> | auto

/* 定义项目的放大比例,为0时有剩余空间不会增长 */
flex-grow: <number>

/* 定义了项目的缩小比例,为0时无剩余空间也不会压缩 */
flex-shrink: <number>

/* * flex-grow, flex-shrink 和 flex-basis的简写 */
flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]

/* * 允许单个项目有与其他项目不一样的对齐方式 */
align-self: auto | flex-start | flex-end | center | baseline | stretch

参考

flex: initial = flex:0 1 auto (初始值)

flex:none = flex: 0 0 auto

flex: 1 = flex: 1 1 0%

css优先级

内联样式 > ID 选择器 > 类选择器 = 属性选择器 = 伪类选择器 > 标签选择器 = 伪元素选择器

伪类和伪元素

伪类与伪元素的区别在于:有没有创建一个文档树之外的元素。它们是否创造了新的元素。

1、伪类的操作对象是文档树中已有的元素;而伪元素则是创建的文档树外的元素。

2、css3中,单冒号代表伪类,双冒号表示伪元素。

css3规范中要求使用双冒号(::)表示伪元素,以此来区分伪类和伪元素,比如::before和::after等伪元素使用双冒号(::),:hover和:active伪类使用单冒号(:)。除了一些低于IE8版本的浏览器外,大部分浏览器都支持伪元素的双冒号(::)表示方法。

进程和线程

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

做个简单的比喻:进程=火车,线程=车厢

  • 线程在进程下行进(单纯的车厢无法运行)
  • 一个进程可以包含多个线程(一辆火车可以有多个车厢)
  • 不同进程间数据很难共享(一辆火车上的乘客很难换到另外一辆火车,比如站点换乘)
  • 同一进程下不同线程间数据很易共享(A车厢换到B车厢很容易)
  • 进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更耗资源)
  • 进程间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响到另外一列火车,但是如果一列火车上中间的一节车厢着火了,将影响到所有车厢)
  • 进程可以拓展到多机,进程最多适合多核(不同火车可以开在多个轨道上,同一火车的车厢不能在行进的不同的轨道上)
  • 进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。(比如火车上的洗手间)-”互斥锁”
  • 进程使用的内存地址可以限定使用量(比如火车上的餐厅,最多只允许多少人进入,如果满了需要在门口等,等有人出来了才能进去)-“信号量”

v-if和v-show

v-if

“真正“的条件渲染,为假时一直不渲染,为真时才渲染

v-show

不管初始条件如何都会被渲染,只是进行简单的基于css的切换(display)。

一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。

v-ifv-for 一起使用时,v-for 具有比 v-if 更高的优先级。

js的数据类型

**值类型(基本类型)**:字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol、BigInt。

引用数据类型:对象(Object)、数组(Array)、函数(Function)。

两者区别

  • 基本数据类型是按值访问的

  • 引用数据类型是保存在堆内存中的对象。

    与其他语言的不同是,你不可以直接访问堆内存空间中的位置和操作堆内存空间。只能操作对象在栈内存中的引用地址。

判断数据的方法

typeof

一般用来判断基本数据类型,typeof 目前能返回string,number,boolean,symbol,bigint,undefined,object,function这八种判断类型

遇到null返回object

instanceof

一般用来判断引用数据类型的判断,通过比较右边变量的prototype是否在左边变量的原型链上即可。

Object.prototype.toString(这个是判断类型最准的方法)

toString是Object原型对象上的一个方法,该方法默认返回其调用者的具体类型,更严格的讲,是 toString运行时this指向的对象类型。

1
Object.prototype.toString.call(undefined); // [object Undefined]

constructor

当一个函数F被定义时,JS引擎会为F添加prototype原型,然后再在prototype上添加一个constructor属性,并让其指向F的引用。

细节问题:
1.null和undefined是无效的对象,因此是不会有constructor存在的,这两种类型的数据可以通过第四种方法来判断。
2.JS对象的constructor是不稳定的,这个主要体现在自定义对象上,当开发者重写prototype后,原有的constructor会丢失,constructor会默认为Object。

js的内存泄漏和垃圾回收

内存泄露

程序的运行需要内存,只要程序提出要求,操作系统或者运行是就必须供给内存。

对于持续运行的服务进程,必须及时释放内存,否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。

不再用到的内存,没有及时释放,就叫做内存泄漏。

有些语言(比如c语言)必须手动释放内存,程序员负责内存管理。

这很麻烦,所以大多数语言提供自动内存管理,减轻程序员的负担,这被称为”垃圾回收机制”。

垃圾回收机制原理

解决内存的泄露,垃圾回收机制会定期(周期性)找出那些不再用到的内存(变量),然后释放其内存。

现在各大浏览器通常采用的垃圾回收机制有两种方法:标记清除,引用计数。

标记清除:

js中最常用的垃圾回收方式就是标记清除。当变量进入环境时,例如,在一个函数中声明一个变量,就将这个变量标记为”进入环境”,从逻辑上讲,永远不能释放进入环境变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为”离开环境”。

1
2
3
4
5
function test(){
var a = 10; //被标记"进入环境"
var b = "hello"; //被标记"进入环境"
}
test(); //执行完毕后之后,a和b又被标记"离开环境",被回收

垃圾回收机制在运行的时候会给存储在内存中的所有变量都加上标记(可以是任何标记方式),然后,它会去掉处在环境中的变量及被环境中的变量引用的变量标记(闭包)。而在此之后剩下的带有标记的变量被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后垃圾回收机制到下一个周期运行时,将释放这些变量的内存,回收它们所占用的空间。

引用计数:

语言引擎有一张”引用表”,保存了内存里面所有资源(通常是各种值)的引用次数。如果一个值的引用次数是0,就表示这个值不再用到了,因此可以将这块内存释放。

如果一个值不再需要了,引用数却不为0,垃圾回收机制无法释放这块内存,从而导致内存泄漏。

js的内存泄露与垃圾回收

vue生命周期

每个组件相互独立,都有其自己的生命周期,包括了一个组件的创建、数据初始化、挂载、更新、销毁。

Vue 实例从创建到销毁的过程,就是生命周期。
1.beforeCreate:存在于实例初始化之后,刚new出来vue实例对象。
2.created(重要):实例创建完成之后,在这一步,实例完成数据观测,property和方法的运算,以及watch的回调——此时是获取服务器数据的最佳时期
3.beforeMount:视图挂载之前,此时数据和视图还没有进行联系。
4.mounted(重要):在视图被挂载之后调用——此时是获取视图或者修改DOM的最佳时期。
5.beforeUpdate:数据更新完成之后,此时视图还没有更新
6.update:数据更新完成之后,此时视图也液晶更新完成。
7.beforeDestroy:实例销毁之前调用。但是在这一步,实例仍然可用。
8.destroyed:实例销毁之后调用,该钩子被调用后,对应的Vue实例所有的指令都会被解绑,所有的事件监听器都会被移除,所有的子实例也会被销毁。

父子组件的生命周期

  • 加载渲染过程父组件挂载前加载子组件

->父beforeCreate -> 父created -> 父beforeMount

->子beforeCreate -> 子created -> 子beforeMount -> 子mounted

-> 父mounted

  • 子组件更新过程

->父beforeUpdate

-> 子beforeUpdate -> 子updated

-> 父updated

  • 父组件更新过程

父beforeUpdate -> 父updated

  • 销毁过程

-> 父beforeDestroy

-> 子beforeDestroy -> 子destroyed

-> 父destroyed

vue3特性

性能

双向响应原理由Object.defineProperty改为基于ES6的Proxy,使其颗粒度更大,速度更快,且消除了之前存在的警告;
重写了 Vdom ,突破了 Vdom 的性能瓶颈
进行了模板编译的优化
进行了更加高效的组件初始化

Tree-Shaking 的支持

支持了 tree-shaking (剪枝):像修剪树叶一样把不需要的东西给修剪掉,使 Vue3 的体积更小。

需要的模块才会打入到包里,优化后的 Vue3.0 的打包体积只有原来的一半(13kb)。哪怕把所有的功能都引入进来也只有23kb,依然比 Vue2.x 更小。像 keep-alive 、 transition 甚至 v-for 等功能都可以按需引入。

Composition API

composition-api 是一个 Vue3 中新增的功能,它的灵感来自于 React Hooks ,是比 mixin 更强大的存在。

composition-api 可以提高代码逻辑的可复用性,从而实现与模板的无关性;同时使代码的可压缩性更强。另外,把 Reactivity 模块独立开来,意味着 Vue3.0 的响应式模块可以与其他框架相组合。

Fragments

不再限制 template 只有一个根节点。
render函数也可以返回数组了,有点像 React.Fragments

Better TypeScript Support

更好的类型推导,使得 Vue3 把 TypeScript 支持得非常好

Custom Renderer API

实现用DOM的方式进行 WebGL 编程

ES6新特性

  • const、let

  • 模板字符量

    • ${}
  • 解构

    • 在ES6中,可以使用解构从数组和对象提取值并赋值给独特的变量

      解构数组的值:

      1
      2
      3
      const point = [10, 25, -34];
      const [x, y, z] = point;
      console.log(x, y, z);
  • 对象字面量简写法

  • for…of循环

  • 展开运算符

  • ES6箭头函数

    • 新的书写方式

      this 的改变

      不能当构造函数

      没有 prototype 属性

      没有 arguments 对象

  • 函数数默认值

  • promise

数据库事务的特性、脏读、不可重复读和幻读的现象

事物特性:原子性、一致性、持久性、隔离性;

脏读:没有提交的数据;

不可重复读:一个事务先读取某条记录,但在两次读取之间该记录被其他另一个事务给修改了,就造成了两次两次读取的数据不同;

幻读:幻读就是一个事务按照查询条件查询以前检索过的数据可是发现该数据被其他事务插入了满足其查询条件的新数据的现象。

一些函数

forEach()

forEach只是简单的将数组遍历,类似于军人接受检阅,但是检阅结束并不会返回任何东西,也不会改变原数组,forEach的返回永远是undefind

1
2
3
4
5
6
7
8
9
let total = null
let arr = [1,2,3]
let result = arr.forEach(a => {
total += a
return a + 1
})
console.log(result) //undefind
console.log(total) //6
console.log(arr) //[1,2,3]

Map()

处理数据,有返回值,返回一个新的数组,每个元素为调用func的结果。

1
2
3
4
5
6
let list = [1, 2, 3, 4, 5];
let other = list.map((d, i) => {
return d * 2;
});
console.log(other);
// print: [2, 4, 6, 8, 10]

Vue.nextTick()

因为vue更新数据是异步的,因此有了这个方法:

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

你在Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中。

因为是在created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js代码放进Vue.nextTick()的回调函数中。

与之对应的就是mounted钩子函数,因为该钩子函数执行时所有的DOM挂载已完成。使用时机:el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子函数,此时页面并未全部渲染。

在某个动作有可能改变DOM元素结构的时候,对DOM一系列的js操作都要放进Vue.nextTick()的回调函数中

vue的异步请求

得看实际情况;

一般在 created 里面就可以,如果涉及到需要页面加载完成之后的操作话就用 mounted;

  • created 阶段的优势是:请求时间比较早,页面 loading 时间相对较短;(调用异步请求最佳,用户就越早感知页面的已加载)

  • mounted 阶段的优势是:页面已经渲染完成,如果想请求之后进行 DOM 操作的话,最好在 mounted 阶段发起请求

js中的集合对象

js中的集合对象(Array、map、Set)及类数组对象的使用与对比

检测或遍历属性在实例中还是在构造函数的prototype属性对象中

hasOwnProperty()方法

判断对象是否包含特定的自身(非继承)属性。即这个属性是否在该实例上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Fn(name){
this.name=name;
}
Fn.prototype.say="hello";
var user1=new Fn('app');
console.log(user1.hasOwnProperty('say')) //false 原型链上的属性不是对象自身的属性返回false
console.log(user1.hasOwnProperty('age')) //true 对象自身的属性返回true

// 只遍历对象自身的属性
for(var p in pro1){
if (pro1.hasOwnProperty(p)) {
console.log(p); //这样,打印的属性就只包含对象自己的了
}
}

in操作符

判断对象的属性是自由属性还是原型链上的属性,如果对象的自由属性或者继承属性中包含这个属性则返回true。

1
2
3
4
5
6
7
function Fn(name){
this.name=name;
}
Fn.prototype.say="hello";
var user1=new Fn('app');
console.log('name' in user1) //true 对象自身的属性返回true
console.log('say' in user1) //true 对象原型上的属性返回true

isPrototypeOf

检测一个对象是否是另一个对象的原型。或者说一个对象是否被包含在另一个对象的原型链中

1
2
3
4
5
6
7
8
function Fn(name){
this.name=name;
}
var user=Object.create(Fn);//Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__(即原型指向这个对象)。
console.log(Fn.isPrototypeOf(user)) //true

var user1=new Fn('app');
console.log(Fn.prototype.isPrototypeOf(user1)) //true

扩展运算符和object.assign

扩展运算符(object spread)

扩展运算符(Object spread)不复制继承的属性或类的属性,但是它会复制ES6的 symbols 属性

object.assign()

object spread spec 明确指出{… obj}等同于Object.assign({},obj)。

Object.assign()修改了一个对象,因此它可以触发 ES6 setter。如果你更喜欢使用immutable技术,那么 Object spread 操作符就是你更好的选择。使用 Object.assign(),你必须确保始终将空对象{}作为第一个参数传递。

当一个 Object 使用了 Object.defineProperty 修改了 set 方法,因为调用 Object.assign 会触发 setter 方法,会触发意想不到的错误。

关于这两个对于只有一层的数据来说是深拷贝,但是如果是多层数据就只会拷贝第一层,但是不是深拷贝。