[TOC]
>[success] # 了解虚拟dom
~~~
1.vue 无论是模板通过AST 转换成render形式也好,或者直接使用render的形式也罢,其实都是为
接下来转换'虚拟DOM' 做准备,在这之前需要了解什么是虚拟dom
~~~
>[info] ## 虚拟dom 是什么
~~~
1.是由普通的 JS 对象来描述 DOM 对象,因为不是真实的 DOM 对象,所以叫 Virtual DOM
~~~
>[danger] ##### 真实dom是什么
~~~
1.既然有了虚拟dom,那么真实dom是什么?把下面的代码在浏览器中运行
let div = document.createElement('div')
let str = ''
for (const key in div) {
str += key + ''
}
console.log(str)
2.上面代码运行后,可以发现输出了很多的属性,可以发现'一个真正的 DOM 元素是非常庞大',这些对象
属性并不是实际开发过程都会用上的如果有一个用js表现的dom的结构,再将这个结构渲染会成为dom可以
让开发阶段更关心数据
~~~
>[danger] ##### 为什么使用虚拟dom
~~~
1.Vue是数据驱动视图的,数据发生变化视图就要随之更新,在更新视图的时候难免要操作DOM,
而操作真实DOM又是非常耗费性能的
2.手动操作DOM时代 ,即使有了jq帮助我们对dom做了简化的操作,但随着项目的复杂DOM操作也变得
复杂起来
3.后来也出现了一种产物模板方式进行dom操作,就例如'art-template',但依旧不能解决跟踪状态变化
4.开始思考,想跟踪变化,就需要进行比较,比较后知道哪里改变了,去改变对应位置思路就诞生了,但是
如果说维护两个dom,每次两个dom进行比较,这原本一个dom就相对比较浪费,现在又用两个,这个肯定
不行,如果我们用js形式记录两个数组,每次比较两个数组那个位置变化了,再去操作对应变化的局部的dom
是不是会更好(两个dom 比较不好的主要原因通过打印dom 属性看到的数量上,这么多属性每次都要最对比是个开销,并且并不是每个属性都用了)
5.Virtual DOM 的好处是当状态改变时不需要立即更新 DOM,只需要创建一个虚拟树来描述
DOM, Virtual DOM 内部将弄清楚如何有效(diff)的更新 DOM
~~~
* 虚拟dom 用js表现形式
~~~
{
sel: "div",
data: {},
children: undefined,
text: "Hello Virtual DOM",
elm: undefined,
key: undefined
}
~~~
>[danger] ##### 虚拟dom 作用
~~~
1.维护视图和状态关系
2.复杂视图情况下提高性能,如果说页面只是一个很简单的视图状态,额外去多维护一个虚拟dom也就是
js的话其实很不值当
3.还可以实现 SSR(Nuxt.js/Next.js)、原生应用(Weex/React Native)、小程序(mpvue/uni-app)
~~~
>[danger] ##### 常见的操作虚拟dom 的库
~~~
1.'Snabbdom' Vue 2.x 内部使用的 Virtual DOM 就是改造的 Snabbdom
2.'virtual-dom',其实进入'virtual-dom'github在项目介绍中也有一段对虚拟dom的产生的动机描述
~~~
* 谷歌翻译动机描述
~~~
手动DOM操作比较麻烦,并且很难跟踪以前的DOM状态。解决此问题的方法是编写代码,
就像在状态更改时重新创建整个DOM一样。当然,如果您每次更改应用程序状态时实际上都重新创建
了整个DOM,则应用程序将非常缓慢,并且输入字段将失去焦点。
virtual-dom是模块的集合,旨在提供声明性的方式来表示应用程序的DOM。因此,您不必在应用程序
状态更改时更新DOM,而只需创建一个虚拟树或VTree,它看起来像您想要的DOM状态。virtual-dom
然后,您将了解如何有效地使DOM看起来像这样,而无需重新创建所有DOM节点。
virtual-dom通过创建完整VTree的视图,然后有效地修补DOM以使其完全符合您的描述,您可以在状态
变化时更新视图。这样可以避免在应用程序代码中进行手动DOM操作和先前状态跟踪,从而为Web应用
程序提供了清晰且可维护的呈现逻辑。
~~~
>[danger] ##### 虚拟dom 真的能提高性能么?
[本段内容来自拉勾前端高手进阶](https://kaiwu.lagou.com/course/courseInfo.htm?courseId=180#/detail/pc?id=3190)
~~~
1.优化性能 。DOM 操作是比较耗时的,对于大量、频繁的 DOM 操作,如果先在 JavaScript 中模拟进行,
然后再通过计算比对,找到真正需要更新的节点,这样就有可能减少不必要的 DOM 操作,从而提升渲染
性能。但并不是所有的 DOM 操作都能通过虚拟 DOM 提升性能,比如单次删除某个节点,直接操作 DOM
肯定比虚拟 DOM 计算比对之后再删除要快。总体而言, 虚拟 DOM 提升了 DOM 操作的性能下限,降低了
DOM 操作的性能上限。 所以会看到一些对渲染性能要求比较高的场景,比如在线文档、表格编辑,还是会
使用原生 DOM 操作。
2.跨平台 。由于虚拟 DOM 以 JavaScript 对象为基础,所以可根据不同的运行环境进行代码转换
(比如浏览器、服务端、原生应用等),这使得它具有了跨平台的能力。
~~~
>[danger] ##### 总结
~~~
1.要分清虚拟dom 和直接操作dom本质上的区别,在'jq'的时代需要开发既要关心数据,又要对对应的'dom'进行
操作,例如新增数据的时候需要开发对所在新增数据'dom'区域进行新增数据的'dom'创建并且插入。那是否可以
让'jq' 也变成只关心数据'dom'直接改变?这里提出一个假设将对应的数据要渲染的'js模板'写好,当数据变化的时候
整个该数据地方的dom全部重新渲染。和之前相比原本100条数据新增一条我们只需要创建一条新增数据的dom
插入这100条即可,现在为了减少dom操作需要重新渲染101条数据'dom'并且重新插入,如果现在用虚拟dom来解决
这个问题我们每次会存储新老的dom结构,注意这里存储的不是webapi 提供的dom对象结构,而是将其抽象化
用js数据格式表现的数据结构成为虚拟dom,每次数据改变生成的新虚拟dom 结构和老的虚拟dom 结构进行比较
哪里不一样重新渲染哪里(这是理想状态下)
2.通过用 JavaScript 对象来表示 DOM 元素的方式,该对象仅包括常用的这些属性方法和节点关系,这样就可以大大降低对象内存、虚拟 DOM 差异对比的计算量等
~~~
* jq直接操作数据
~~~
1.正常的jq代码以我的理解我是不会去像下面那么写,更多的是新增创建一个对应的新增dom,插入到他的父节点
,下面案例站在的角度是vue这类不操组dom 直接数据变化影响视图用jq强行实现,就出现了已经被渲染的dom
还需要重新创建重新渲染,虚拟dom出现就是既可以实现数据操作又满足不创建之前存在的dom节点
~~~
~~~
let heroes = [
{ name: "剑圣", age: 80, offsetTop: 0, elmHeight: 20 },
{ name: "盲僧", age: 30, offsetTop: 0, elmHeight: 20 },
{ name: "暗夜猎手", age: 50, offsetTop: 0, elmHeight: 20 },
{ name: "寒冰射手", age: 20, offsetTop: 0, elmHeight: 20 },
{ name: "赏金猎人", age: 40, offsetTop: 0, elmHeight: 20 }
];
function render() {
let html = "";
heroes.forEach(hero => {
html += `
<li class="hero" style="opacity: 0; transform: translateX(0px) translateY(${
hero.offsetTop
}px)">
<div>
<span class="left">姓名:${hero.name}</span>
<span class="left l30">年龄:${hero.age}</span>
<span class="right close">x</span>
</div>
</li>
`;
});
$(".content > ul").html(html);
$(".content > ul > li").each((index, li) => {
heroes[index].elmHeight = $(li).height();
});
heroes = heroes.reduce((arr, hero) => {
let last = arr[arr.length - 1];
hero.offsetTop = last ? last.elmHeight + last.offsetTop + 10 : 10;
return arr.concat(hero);
}, []);
~~~
[jq案例链接](https://codesandbox.io/s/jq-demo-5i7qp?file=/src/index.js:25-1034)
[虚拟dom链接](https://codesandbox.io/s/snabbdom-demo-forked-8lsvb?file=/src/index.js:769-944)
>[danger] ##### 文章参考来源
[参考内容来源](https://github.com/lagoufed/vuejs-enhancement)
- Vue--基础篇章
- Vue -- 介绍
- Vue -- MVVM
- Vue -- 创建Vue实例
- Vue -- 模板语法
- Vue -- 指令用法
- v-cloak -- 遮盖
- v-bind -- 标签属性动态绑定
- v-on -- 绑定事件
- v-model -- 双向数据绑定
- v-for -- 只是循环没那么简单
- 小知识点 -- 计划内属性
- key -- 属性为什么要加
- 案例说明
- v-if/v-show -- 显示隐藏
- v-for 和 v-if 同时使用
- v-pre -- 不渲染大大胡语法
- v-once -- 只渲染一次
- Vue -- class和style绑定
- Vue -- filter 过滤器
- Vue--watch/computed/fun
- watch -- 巧妙利用watch思想
- Vue -- 自定义指令
- Vue -- $方法
- Vue--生命周期
- Vue -- 专属ajax
- Vue -- transition过渡动画
- 前面章节的案例
- 案例 -- 跑马灯效果
- 案例 -- 选项卡内容切换
- 案例-- 筛选商品
- 案例 -- 搜索/删除/更改
- 案例 -- 用computed做多选
- 案例 -- checked 多选
- Vue--组件篇章
- component -- 介绍
- component -- 使用全局组件
- component -- 使用局部组件
- component -- 组件深入
- component -- 组件传值父传子
- component -- 组件传值子传父
- component -- 子传父语法糖拆解
- component -- 父组件操作子组件
- component -- is 动态切换组件
- component -- 用v-if/v-show控制子组件
- component -- 组件切换的动画效果
- component -- slot 插槽
- component -- 插槽2.6
- component -- 组件的生命周期
- component -- 基础组件全局注册
- VueRouter--获取路由参数
- VueRouter -- 介绍路由
- VueRouter -- 安装
- VueRouter -- 使用
- VueRouter--router-link简单参数
- VueRouter--router-link样式问题
- VueRouter--router-view动画效果
- VueRouter -- 匹配优先级
- vueRouter -- 动态路由
- VueRouter -- 命名路由
- VueRouter -- 命名视图
- VueRouter--$router 获取函数
- VueRouter--$route获取参数
- VueRouter--路由嵌套
- VueRouter -- 导航守卫
- VueRouter -- 写在最后
- Vue--模块化方式结构
- webpack--自定义配置
- webpack -- 自定义Vue操作
- VueCli -- 3.0可视化配置
- VueCli -- 3.0 项目目录
- Vue -- 组件升级篇
- Vue -- 组件种类与组件组成
- Vue -- 组件prop、event、slot 技巧
- Vue -- 组件通信(一)
- Vue -- 组件通信(二)
- Vue -- 组件通信(三)
- Vue -- 组件通信(四)
- Vue -- 组件通信(五)
- Vue -- 组件通信(六)
- Vue -- bus非父子组件通信
- Vue -- 封装js插件成vue组件
- vue组件分装 -- 进阶篇
- Vue -- 组件封装splitpane(分割面板)
- UI -- 正式封装
- Vue -- iview 可编辑表格案例
- Ui -- iview 可以同时编辑多行
- Vue -- 了解递归组件
- UI -- 正式使用递归菜单
- Vue -- iview Tree组件
- Vue -- 利用通信仿写一个form验证
- Vue -- 使用自己的Form
- Vue -- Checkbox 组件
- Vue -- CheckboxGroup.vue
- Vue -- Alert 组件
- Vue -- 手动挂载组件
- Vue -- Alert开始封装
- Vue -- 动态表单组件
- Vue -- Vuex组件的状态管理
- Vuex -- 参数使用理解
- Vuex -- state扩展
- Vuex -- getters扩展
- Vuex--mutations扩展
- Vuex -- Action 异步
- Vuex -- plugins插件
- Vuex -- v-model写法
- Vuex -- 更多
- VueCli -- 技巧总结篇
- CLI -- 路由基础
- CLI -- 路由升级篇
- CLI --异步axios
- axios -- 封装axios
- CLI -- 登录写法
- CLI -- 权限
- CLI -- 简单权限
- CLI -- 动态路由加载
- CLI -- 数据性能优化
- ES6 -- 类的概念
- ES6类 -- 基础
- ES6 -- 继承
- ES6 -- 工作实战用类数据管理
- JS -- 适配器模式
- ES7 -- 装饰器(Decorator)
- 装饰器 -- 装饰器修饰类
- 装饰器--修饰类方法(知识扩展)
- 装饰器 -- 装饰器修饰类中的方法
- 装饰器 -- 执行顺序
- Reflect -- es6 自带版本
- Reflect -- reflect-metadata 版本
- 实战 -- 验证篇章(基础)
- 验证篇章 -- 搭建和目录
- 验证篇章 -- 创建基本模板
- 验证篇章 -- 使用
- 实战 -- 更新模型(为了迎合ui升级)
- 实战 -- 模型与接口对接
- TypeSprict -- 基础篇章
- TS-- 搭建(一)webpack版本
- TS -- 搭建(二)直接使用
- TS -- 基础类型
- TS -- 枚举类型
- TS -- Symbol
- TS -- interface 接口
- TS -- 函数
- TS -- 泛型
- TS -- 类
- TS -- 类型推论和兼容
- TS -- 高级类型(一)
- TS -- 高级类型(二)
- TS -- 关于模块解析
- TS -- 声明合并
- TS -- 混入
- Vue -- TS项目模拟
- TS -- vue和以前代码对比
- TS -- vue简单案例上手
- Vue -- 简单弄懂VueRouter过程
- VueRouter -- 实现简单Router
- Vue-- 原理2.x源码简单理解
- 了解 -- 简单的响应式工作原理
- 准备工作 -- 了解发布订阅和观察者模式
- 了解 -- 响应式工作原理(一)
- 了解 -- 响应式工作原理(二)
- 手写 -- 简单的vue数据响应(一)
- 手写 -- 简单的vue数据响应(二)
- 模板引擎可以做的
- 了解 -- 虚拟DOM
- 虚拟dom -- 使用Snabbdom
- 阅读 -- Snabbdom
- 分析snabbdom源码 -- h函数
- 分析snabbdom -- init 方法
- init 方法 -- patch方法分析(一)
- init 方法 -- patch方法分析(二)
- init方法 -- patch方法分析(三)
- 手写 -- 简单的虚拟dom渲染
- 函数表达解析 - h 和 create-element
- dom操作 -- patch.js
- Vue -- 完成一个minVue
- minVue -- 打包入口
- Vue -- new实例做了什么
- Vue -- $mount 模板编译阶段
- 模板编译 -- 分析入口
- 模板编译 -- 分析模板转译
- Vue -- mountComponent 挂载阶段
- 挂载阶段 -- vm._render()
- 挂载阶段 -- vnode
- 备份章节
- Vue -- Nuxt.js
- Vue3 -- 学习
- Vue3.x --基本功能快速预览
- Vue3.x -- createApp
- Vue3.x -- 生命周期
- Vue3.x -- 组件
- vue3.x -- 异步组件???
- vue3.x -- Teleport???
- vue3.x -- 动画章节 ??
- vue3.x -- 自定义指令 ???
- 深入响应性原理 ???
- vue3.x -- Option API VS Composition API
- Vue3.x -- 使用set up
- Vue3.x -- 响应性API
- 其他 Api 使用
- 计算属性和监听属性
- 生命周期
- 小的案例(一)
- 小的案例(二)-- 泛型
- Vue2.x => Vue3.x 导读
- v-for 中的 Ref 数组 -- 非兼容
- 异步组件
- attribute 强制行为 -- 非兼容
- $attrs 包括 class & style -- 非兼容
- $children -- 移除
- 自定义指令 -- 非兼容
- 自定义元素交互 -- 非兼容
- Data选项 -- 非兼容
- emits Option -- 新增
- 事件 API -- 非兼容
- 过滤器 -- 移除
- 片段 -- 新增
- 函数式组件 -- 非兼容
- 全局 API -- 非兼容
- 全局 API Treeshaking -- 非兼容
- 内联模板 Attribute -- 非兼容
- key attribute -- 非兼容
- 按键修饰符 -- 非兼容
- 移除 $listeners 和 v-on.native -- 非兼容
- 在 prop 的默认函数中访问 this -- ??
- 组件使用 v-model -- 非兼容
- 渲染函数 API -- ??
- Slot 统一 ??
- 过渡的 class 名更改 ???
- Transition Group 根元素 -- ??
- v-if 与 v-for 的优先级对比 -- 非兼容
- v-bind 合并行为 非兼容
- 监听数组 -- 非兼容