一.一个完整的URL 解析过程
DNS解析过程
1 用户输入 URL 地址。
2 如果输入的是域名,对 URL 地址进行 DNS 域名解获得IP地址。
3 建立 TCP 连接(三次握手)。
4 浏览器向 web 服务器发送一个 HTTP 请求报文。
5 服务器返回 HTTP 响应报文给客户端。
6 关闭 TCP 连接(四次挥手)。
7 浏览器解析文档资源并渲染页面。
解析过程:浏览器解析的资源(html,svg,Xhtml等),解析完成后都会生成一个完整的DOM Tree ,css资源则会解析成CSS Rule Tree,生成之后进行浏览器渲染,保证脚本执行前已完成DOM渲染会放在body标签结束之后。
二.EventLoop机制:Event Loop即事件循环,是指浏览器或Node的一种解决javaScript单线程运行时不会阻塞的一种机制,也就是我们经常使用异步的原理。
为什么JS是单线程?
JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?产生了冲突。
单线程执行机制:
1)所有同步任务都在主线程上执行,形成一个[执行栈]execution context stack。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。
avaScript 把异步任务又做了进一步的划分,异步任务又分为两类,分别是:
① 宏任务(macrotask)
- 异步 Ajax 请求、
- setTimeout、setInterval、
- 文件操作(I/O)
- 其它宏任务
② 微任务(microtask)
- Promise.then、.catch 和 .finally
- process.nextTick
- Object.observe
- MutationObserver
- 其它微任务
宏任务和微任务的执行顺序:
宏任务与微任务.png- 每一个宏任务执行完之后,都会检查是否存在待执行的微任务,
- 如果有,则执行完所有微任务之后,再继续执行下一个宏任务。
注意:promise本身是同步的,promise的回调then是异步的。
三.MVC与MVVM的区别
3.1mvc
mvc
- View 传送指令到 Controller
- Controller 完成业务逻辑后,要求 Model 改变状态
- Model 将新的数据发送到 View,用户得到反馈
mvc中的所有通信都是单向的
3.2mvp
MVP 模式将 Controller 改名为 Presenter,同时改变了通信方向。
1. 各部分之间的通信,都是双向的。
2. View 与 Model 不发生联系,都通过 Presenter 传递。
3. View 非常薄,不部署任何业务逻辑,称为"被动视图"(Passive View),即没有任何主动性,而 Presenter非常厚,所有逻辑都部署在那里。
3.3mvvm
MVVM 模式将 Presenter 改名为 ViewModel,基本上与 MVP 模式完全一致。
唯一的区别是,它采用双向绑定(data-binding):View的变动,自动反映在 ViewModel,反之亦然。Angular 和 Ember 都采用这种模式。
四:Vue数据双向绑定原理
①Vue2.X的双向绑定响应式原理
什么是响应式,也即是说,数据发生改变的时候,视图会重新渲染,匹配更新为最新的值。
Object.defineProperty 为对象中的每一个属性,设置 get 和 set 方法,每个声明的属性,都会有一个 专属的依赖收集器 subs,当页面使用到 某个属性时,触发 ObjectdefineProperty - get函数,页面的 watcher 就会被放到属性的依赖收集器 subs 中,在数据变化时,通知更新;
当数据改变的时候,会触发Object.defineProperty - set函数,数据会遍历自己的依赖收集器 subs,逐个通知 watcher,视图开始更新;
②Vue3.x双向绑定响应式数据原理
Vue3.x改用Proxy替代Object.defineProperty。
因为Proxy可以直接监听对象和数组的变化,并且有多达13种拦截方法。并且作为新标准将受到浏览器厂商重点持续的性能优化。
Proxy只会代理对象的第一层,Vue3是怎样处理这个问题的呢?
判断当前Reflect.get的返回值是否为Object,如果是则再通过reactive方法做代理, 这样就实现了深度观测。
监测数组的时候可能触发多次get/set,那么如何防止触发多次呢?我们可以判断key是否为当前被代理对象target自身属性,也可以判断旧值与新值是否相等,只有满足以上两个条件之一时,才有可能执行trigger。
③Vue3 里为什么要用 Proxy API替代 defineProperty API?
defineProperty API 的局限性最大原因是它只能针对单例属性做监听。
Vue2.x中的响应式实现正是基于defineProperty中的descriptor,对 data 中的属性做了遍历 + 递归,为每个属性设置了 getter、setter。这也就是为什么 Vue 只能对 data 中预定义过的属性做出响应的原因。
Proxy API的监听是针对一个对象的,那么对这个对象的所有操作会进入监听操作, 这就完全可以代理所有属性,将会带来很大的性能提升和更优的代码。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
响应式是惰性的。
在 Vue.js 2.x 中,对于一个深层属性嵌套的对象,要劫持它内部深层次的变化,就需要递归遍历这个对象,执行 Object.defineProperty 把每一层对象数据都变成响应式的,这无疑会有很大的性能消耗。
在 Vue.js 3.0 中,使用 Proxy API 并不能监听到对象内部深层次的属性变化,因此它的处理方式是在 getter 中去递归响应式,这样的好处是真正访问到的内部属性才会变成响应式,简单的可以说是按需实现响应式,减少性能消耗。
五.什么是虚拟DOM
virtual DOM 虚拟DOM,用普通js对象来描述DOM结构,因为不是真实DOM,所以称之为虚拟DOM。
目的是为了减少回流和重绘,提升性能。
虚拟DOM基本步骤
1.页面初始加载解析DOM树,将DOM解析为JS对象
[最终还需要将其映射成真实DOM,渲染到页面]
2.DOM结构要发生变化,生成一个新的JS对象
3.DIFF比较,比较同级对象
4.将不同之处写入到patch对象中
5.渲染到页面
那么为什么用虚拟dom呢?
Web界面由DOM树(树的意思是数据结构)来构建,当其中一部分发生变化时,其实就是对应某个DOM节点发生了变化,
虚拟DOM就是为了解决浏览器性能问题而被设计出来的。若一次操作中有10次更新DOM的动作,虚拟DOM不会立即操作DOM,而是将这10次更新的diff内容保存到本地一个JS对象中,最终将这个JS对象一次性attch到DOM树上,再进行后续操作,避免大量无谓的计算量。所以,用JS对象模拟DOM节点的好处是,页面的更新可以先全部反映在JS对象(虚拟DOM)上,操作内存中的JS对象的速度显然要更快,等更新完成后,再将最终的JS对象映射成真实的DOM,交由浏览器去绘制。
大白话:就是为了提高代码效率,渲染效果,就如往常的js jquery这些使用dom操作时都是一段复杂的过程,整个过程要遍历属性,标签啦,就是在你的代码上一遍遍寻找大半天还不知道有没有收获,很慢。而且做多件事情的时候只能一件件去做,浪费很多时间,而我们的虚拟dom呢,就是,把你想要做的事情都用小本子记起来(记在本地js对象上)在虚拟dom上更新完成后,再拿到真是的dom上去渲染,然后交给浏览器绘制页面呀。从头到末尾,人家知道每一步要做什么,而且可以同时做,还不会等到你的页面渲染完成后才出发,人家可是早早出发了,
真实DOM和虚拟DOM的区别
那么说回到真实DOM,虚拟DOM和真实DOM又有什么区别呢?
我想,应该会有一下几点:
1.虚拟DOM不会进行排版与重绘操作
2.真实DOM频繁排版与重绘的效率是相当低的
3.虚拟DOM进行频繁修改,然后一次性比较并修改真实DOM中需要改的部分,最后并在真实DOM中进行排版与重绘,减少过多DOM节点排版与重绘损耗
4.虚拟DOM有效降低大面积(真实DOM节点)的重绘与排版,因为最终与真实DOM比较差异,可以只渲染局部
DIFF算法
vue diff算法
六.Diff算法步骤
用js对象结构(虚拟DOM)表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中
当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
把所记录的差异应用到所构建的真正的DOM树上,视图就更新了
Diff 算法: 仅在同级的vnode间做diff,递归地进行同级vnode的diff,不仅仅是同层级对比,在diff算法中也会进行同key值对比和同组件对比
Diff 过程整体策略:深度优先,同层比较
key的作用
在使用v-forj添加唯一的key的作用标识,Diff算法就可以正确的识别此节点,找到正确的位置区更新新的节点,高效的更新虚拟DOM。
八:Vue的生命周期:
- beforecreated :表示刚创建了一个空的Vue实例,此时对象身上只有生命周期钩子函数和事件,其他的东西还未创建,data,method等都还未初始化
- created:data和methods等已被初始化,之后开始编译模板
- beforeMount:完成了模板($el)的编译,还未挂载到页面上去
- mounted:将已经编译好的模板(mount)
- beforeUpdate:此时data中的数据是最新的,但是view还没有被更新
- updated:date和view都是最新的
- beforeDestroy: 实例销毁之前,在当前阶段实例完全可以被使用(data,methods,指令等),我们可以在这时进行善后收尾工作,比如清除计时器
- destroy:组件已经被彻底销毁,实例不可用
- activited keep-alive 专属 , 组件被激活时调用
- deactivated keep-alive 专属 , 组件被销毁时调用
九:Vue组件通信
1.父组件向子组件传递数据可以通过 props;
2.子组件向父组件是通过 on事件;
3.还可以通过 parent、root(根组件)和refs.(ref值) 获取到的是dom元素
②ref 加在子组件上,用this.refs.(ref值).方法() 就可以使用了。
4.跨级通信: eventbus ;Vuex;provide / inject;
十:Vue项目优化
项目优化
网友评论