- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
1、虚拟DOM:
因为DOM操作非常 消耗性能 ,在操作DOM时,会出现DOM的 回流(Reflow:元素大小或者位置发生改变) 与 重绘(元素样式的改变) 使DOM重新渲染.
现在的框架Vue和React很少直接操作DOM,因为两者都是数据驱动视图,只会对数据进行增删改的操作 。
因此,二者使用虚拟DOM(vdom)来解决控制DOM操作的问题:
原理: 使用Js模拟DOM结构 ,把DOM的计算转移为Js的计算,使用diff算法计算出最小的变更,然后根据变更操作DOM 。
学习diff算法需要借助snabbdom这个vdom库的源码,vue也是参考它实现的 。
import { init, classModule, propsModule, styleModule, eventListenersModule, h, } from "snabbdom" ; const patch = init([ // Init patch function with chosen modules classModule, // makes it easy to toggle classes propsModule, // for setting properties on DOM elements styleModule, // handles styling on elements with support for animations eventListenersModule, // attaches event listeners ]); const container = document.getElementById("container" ); const vnode = h("div#container.two.classes", { on: { click: someFn } }, [ h("span", { style: { fontWeight: "bold" } }, "This is bold"), " and this is just normal text", h("a", { props: { href: "/foo" } }, "I'll take you places!"), ]); // Patch into empty DOM element – this modifies the DOM as a side effect patch(container, vnode); const newVnode = h( "div#container.two.classes", { on: { click: anotherEventHandler } }, [ h( "span", { style: { fontWeight: "normal", fontStyle: "italic" } }, "This is now italic type" ), " and this is still just normal text", h("a", { props: { href: "/bar" } }, "I'll take you places!"), ] ); // Second ` patch ` invocation patch(vnode, newVnode) ; // Snabbdom efficiently updates the old view to the new state
其中有两个关键函数:
2、Diff算法:
diff 比对两个新旧vnode的过程主要是在 patch 函数 (patchVnode函数) 中进行 。
。
正常情况下两棵树之间作比对,那么第一遍历tree1,第二遍历tree2,第三排序,三次遍历,时间复杂度为 O(n ^ 3) 节点太多,算法就不可用 。
框架中diff算法的优化:
h 函数用来生成vnode,vnode函数如下:
返回一个js对象结构的虚拟DOM(vnode):
1.children和text是不能共存的,要么里面是纯text文本,要么是子元素 。
2.elm 就是vnode对应的那个DOM元素 。
3.key 就相当于 v-for 里面的 key,是我们在使用 v-for 的时候需要自己手动加上 。
初始化: 第一次执行patch,patch(container,vnode),创建空的vnode,关联传入的dom 。
更新: 判断是否是相同的vnode, tag(sel选择器)和key相同 ,则认为是相同节点,执行 patchVnode函数进行比对 。
否则删除重建,不做深度比对 。
。
。
原理:
针对新旧 children 定义四个index, oldStartIdx , oldEndIdx , newStartIdx , newEndIdx ,然后进行一个循环,在循环过程中 。
idx会一边累加或者一边累减,startIdx会累加,endIdx会累减,在这个过程中,指针会慢慢地往中间去移动,当指针重合的时候,说明遍历结束了,循环结束.
在每一轮循环过程中的具体的对比过程是:
如果出现下面情况中的一种: 开始和开始节点去对比,结束和结束节点对比,结束和开始节点对比 ,那么就执行 patchVnode() 函数,进行 递归 比较, 。
并且指针累加或者累减,往中间移动。 进行下一轮循环的时候,指针就指到下一个了 children 。
key
。
sel
是否相等,如果 sel
不相等,那还是没对应上,说明节点是新的,那也找地方插入新的。 sel
相等, key
相等,那么继续对这两个相同的节点执行 patchVnode
方法,递归比较。 。
如果检测出新节点中的 key 在旧节点上有对应的 key ,在进行交换位置的操作时,就没有必要销毁,由此提升性能 。
3、模板编译 。
零、前置知识点:JS的 with 语法 。
with 语法:改变 {} 内自由变量的查找规则,,将 {} 内自由变量,当作 obj 的属性来查找 如果找不到匹配的 obj 属性,就会报错 with 要慎用, 它打破了作用域的规则,易读性变差 。
vue模板编译成什么?
模板不是html , 有指令、插值、JS 表达式,能实现判断、循环 。
html是标签语言,只有JS才能实现判断、循环(图灵完备的) 。
因此,模板一定是转换为某种JS代码,模板怎么转成js代码的过程就是模板编译 。
安装 vue template complier 这个库,查看编译输出值:
// 引入
const compiler = require( ' vue-template-compiler ' ) // 插值 // const template = ` <p>{{message}}</p> ` // 编译 const res = compiler.compile(template) console.log(res. render )
打印结果:
with( this ){ return _c( ' p ' ,[_v(_s(message))])}
其中 this 在vue中就是 vm 实例,所以 _c、_v、_s 就是vue源码中的一些函数 。
// 从 vue 源码中找到缩写函数的含义 function installRenderHelpers (target) { target._c = createElement // 创建vnode target._o = markOnce; target._n = toNumber; target._s = toString; target._l = renderList; target._t = renderSlot; target._q = looseEqual; target._i = looseIndexOf; target._m = renderStatic; target._f = resolveFilter; target._k = checkKeyCodes; target._b = bindObjectProps; target._v = createTextVNode; target._e = createEmptyVNode; target._u = resolveScopedSlots; target._g = bindObjectListeners; target._d = bindDynamicKeys; target._p = prependModifier; }
转化后:createElement 函数的作用是创建一个 vnode 。
with( this ){ return createElement( ' p ' ,[createTextVNode(toString(message))])}
const template = ` <p>{{flag ? message : ' no message found ' }}</p> ` with( this ){ return _c( ' p ' ,[_v(_s(flag ? message : ' no message found ' ))])}
const template = ` <div id= " div1 " class = " container " > <img :src= " imgUrl " /> </div> ` with( this ){ return _c( ' div ' , {staticClass: " container " ,attrs:{ " id " : " div1 " }}, [_c( ' img ' ,{attrs:{ " src " :imgUrl}})])
}
// 条件 const template = ` <div> <p v- if = " flag === 'a' " >A</p> <p v- else >B</p> </div> ` with( this ){ return _c( ' div ' ,[(flag === ' a ' )?_c( ' p ' ,[_v( " A " )]):_c( ' p ' ,[_v( " B " )])])}
_l
( renderList
)函数,传入数组或者对象,即可返回列表vnode // 循环 const template = ` <ul> <li v- for = " item in list " :key= " item.id " >{{item.title}}</li> </ul> ` with( this ){ return _c( ' ul ' ,_l((list),function(item){ return _c( ' li ' ,{key:item.id},[_v(_s(item.title))])}), 0 )}
// 事件 const template = ` <button @click= " clickHandler " >submit</button> ` with( this ){ return _c( ' button ' ,{on:{ " click " :clickHandler}},[_v( " submit " )])}
value
的 attr
加 input
事件监听的语法糖 最后执行 render
函数,生成vnode // v-model const template = ` <input type= " text " v-model= " name " > ` // 主要看 input 事件 with( this ){ return _c( ' input ' ,{directives:[{name: " model " ,rawName: " v-model " ,value:(name),expression: " name " }],attrs:{ " type " : " text " },domProps:{ " value " :(name)},on:{ " input " :function($ event ){ if ($ event .target.composing) return ;name=$ event .target.value}}})}
模板编译的过程 :模板编译为render函数,执行render函数后返回vnode 。
之后再基于 vnode 执行 patch 和 diff 算法 。
注意:使用webpack,vue-loader,会在开发环境编译模板,所以最后打包出来产生的代码就没有模板代码,全部都是 render 函数形式 。
4、初次渲染与更新过程 。
5、异步渲染--this$nextTick() 。
vue组件是异步渲染的。代码没执行完,DOM不会立即渲染。this.$nextTick 会在DOM渲染完成时回调 。
页面渲染时会将 data 的修改做一个整合,多次 data 的修改 最后只会渲染一个最终值 。
6、组件化 。
对于双向绑定的理解, 就是用户更新了View,Model的数据也自动被更新了 ,这种情况就是双向绑定.
再说细点,就是在单向绑定的基础上给可输入元素input、textare等添加了change(input)事件,(change事件触发,View的状态就被更新了)来动态修改model.
MVC和MVVM的区别并不是VM完全取代了C, ViewModel存在目的在于抽离Controller中展示的业务逻辑 , 而不是替代Controller 。
其它视图操作业务等还是应该放在Controller中实现。也就是说 MVVM实现的是业务逻辑组件的重用 .
MVC中Controller演变成MVVM中的ViewModel 。
MVVM通过数据来显示视图层而不是节点操作 。
MVVM主要解决了MVC中大量的dom操作使页面渲染性能降低,加载速度变慢,影响用户体验等问题.
7、响应式 。
。
。
引用: 。
https://www.shouxicto.com/article/3298.html 。
https://juejin.cn/post/6995232345749979172#heading-2 。
https://juejin.cn/post/6995204870114377741 。
https://blog.csdn.net/gxll499294075/article/details/123667632 。
最后此篇关于vue原理:diff、模板编译、渲染过程等的文章就讲到这里了,如果你想了解更多关于vue原理:diff、模板编译、渲染过程等的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我在我们的系统中使用了多种不同的查询,我们想要获取分页的“总”记录。 我所遵循的所有地方都遵循这种结构 - var query1 = "select SQL_CALC_FOUND_ROWS ...."
我是 diff 的新手。我过去用过它。但我想知道,是否可以使用 diff 来跟踪希伯来语中两个文件之间的差异?我想比较具有元音点和重音符号的圣经希伯来文文件。 最佳答案 我不知道有任何“差异”(文件比
在Word中,有一个选项可以比较两个文档并找出两个文档之间的差异。我正在寻找一个类似的功能来比较两个Powerpoint文档。理想情况下,它将列出不同的单个幻灯片,每个幻灯片之间版本之间的差异以及进行
Bizzaro-Diff!!! 有没有办法做一个只显示一组文件中相同部分的 bizzaro/inverse-diff? (即远超过 three files ) 奇怪的问题,我知道...但我正在将某人的
我将 git 与默认的 Ubuntu 12.04 软件包一起使用: git --version git version 1.7.9.5 我找不到任何可以让 git diff 打开外部差异工具的机制,它
这是我的树 ├── test │ ├── dir1 │ └── dir2 │ ├── file │ └── file2 └── test2 └── dir2
我在 source forge ( cocoa.diff ) 上找到了这个补丁,这意味着我可以使用 cocoa.diff 文件进行补丁。但是,我似乎无法弄清楚如何使用 .diff 文件。 谢谢你的帮助
我修改了某个文件的第 494 行,并使用 cvs diff -u4 来查看我修改的内容,cvs 输出如下内容: @@ -490,9 +490,9 @@ if (!(hPtr->hSta
我需要知道这两个补丁是否有效相同。 我有一个旧补丁文件和使用 unix diff 命令创建的新补丁文件。由于补丁创建时的时间戳,只是比较补丁会报告差异。 有没有办法(有差异?)可以可靠地告诉我这两个补
我有这样的东西 src/sim/simulate.cc 41d40 public: > 61,62c60,61 a.patch 用于创建补丁文件,尽管也可能会抛出一些其他开关(-N?)
我想知道是否有工具可以显示与 debian 打包相关的补丁中使用的 *.diff 文件。我需要从该工具中获得的是它可以只读取 diff 文件并显示随更改的行更改的实际文件,就像 kdiff 或 mel
主要有3个git diff版本: git diff - difference between WORKING DIRECTORY & STAGE git diff --staged - differe
根据 Ender 的 Applied Econometric Time Series ,变量 y 的二阶差分定义为: Pandas 提供了 diff 函数,它接收“periods”作为参数。尽管如此,
这是我的差异的开始部分。 #!/usr/bin/env python import fileinput import difflib import subprocess import sys # fo
有没有办法在 svn diff 或任何其他工具(基于 linux)到 仅显示空格/制表符更改 ?. 目的,我不希望 checkin 这些差异。如果工具可以捕获这些差异,我可以在 checkin 之前将
我正在尝试比较忽略回车符的文件 - diff -b 在任何其他 unix 上的表现令人钦佩。但是在这个 AIX 5.3 机器上: tst1:tst2$ od -c testfile 00000
这个问题已经有答案了: What does the “@@…@@” meta line with at signs in svn diff or git diff mean? (3 个回答) 已关闭
我在使用 git diff --word-diff 时遇到了问题。问题是当 diff 获取没有换行符的文件(单行文件)时,它会逐行区分。我想逐字区分。 以下是当我在没有换行符的情况下比较文件时发生的情
这个问题在这里已经有了答案: Highlight changed lines and changed bytes in each changed line (12 个答案) 关闭 7 年前。 给定一
我正在尝试使用两个大文件夹(〜7GB)创建一个补丁。 这是我的做法: $ diff -Naurbw . ../other-folder > file.patch 但可能由于文件大小而导致未创建补丁并给
我是一名优秀的程序员,十分优秀!