自学内容网 自学内容网

vue深入理解(1)

本文章内容主要来源于《vue.js设计与实现》

视图层框架设计

命令式和声明式

范式上,视图层框架通常分为命令式和范式

JQuery就是典型的命令式框架,命令式框架的一大特点就是关注过程

例子:

$('#app') // 获取app
.text('hello world') // 设置文本内容
.on('click', () => { alert('ok') })

原生JS实现例子:

const div = document.querySelector('#app')
div.innerText = 'hello world'
div.addEventListener('click', () => { alert('ok') })

声明式框架更加关注结果

vue.js例子:

<div @click="() => alert('ok')">
  hello world
</div>

可以看出在vue.js实现就是提供的是一个“结果”,怎么去实现的这个过程我们并不关心这是由vue.js帮我们完成的

也就是Vue封装了这个过程

  • vue的内部实现一定是命令式的,而暴露给用户的则更加声明式

两者性能与可维护性比较

命令式代码的性能优于声明式的性能

若要实现修改文本内容如上面代码,只需要直接调用相关命令即可

div.innerText = 'nihao'

理论上命令式代码可以做到极致的代码优化(因为明确知道哪些发生更改,只需要做必要的修改

对于框架而言,为了实现最优的更新性能,需要找到前后差异并只更新变化的地方

把直接修改消耗的性能定义为A,把找出差异的性能消耗定义为B

  • 命令式代码的更新性能消耗=A
  • 声明式代码的更新性能消耗=B + A

声明式代码的可维护性更强(命令式代码需要维护实现目标的整个过程)

虚拟DOM性能

该部分主要是初步介绍虚拟DOM,主要讲述weishe

由于声明式代码的更新性能消耗=B + A

为了最小化找出差异的性能消耗,所以让声明式代码的性能无限接近于命令式消耗 -------- 虚拟DOM就是为了最小化找出差异这一步的性能消耗

理论上采用虚拟DOM的更新技术的性能不可能比原生JS操作DOM更高

虚拟DOM在vue中使用就是为了写声明式代码,还能够保证应用程序的性能下限

以上说的原生JS指的是类似于document.creatElement之类的DOM操作,并不包含innerHTML

innerHTML操作页面

使用innerHTML创建页面需要构建一段HTML字符串,然后将字符串赋值给DOM元素的innerHTML属性

  • 但深层并非这句话这么简单,为了渲染出页面,首先要把字符串解析成DOM树(这是一个DOM层面的计算

涉及DOM的计算要远比JS层面的计算性能差,纯JS层面的操作比DOM操作快得多,甚至不在一个数量级

请添加图片描述

使用innerHTML更新页面的过程是重新构建HTML字符串,再重新设置DOM元素的innerHTML属性(相当于销毁所有旧的DOM元素,再 全量 创建新的DOM元素

虚拟DOM操作页面

使用虚拟DOM创建页面首先创建JS对象(该对象为真实DOM的映照),然后递归地遍历DOM树并创建真实DOM

使用虚拟DOM更新页面需要重新创建虚拟DOM树,然后比较新旧虚拟DOM,找到变化地元素并更新它

两者相比

虚拟DOM在 JS层面的计算 要比创建页面是多出一个Diff的性能消耗,但不会产生数量级的差异 ;然后DOM层面的计算,虚拟DOM在更新页面时只会更新必要的元素,但innerHTML要全量更新

性能总结

请添加图片描述

运行时、编译时

编译时(Compile Time)和运行时(Runtime)指的是程序在不同的阶段进行的不同类型的处理。

js 编译是什么呢?
现代前端开发,我们都是使用 react、vue、webpack 等工具,其实就是把这些框架代码编译成了 js 代码,那么编译时其实是指的,webpack 等工具把 react、vue 编译为 js 的过程。运行时自然不用说,就是浏览器解释 js 代码的过程。

纯运行时框架例子

// 渲染 dom
function render (node, root) {
  const el = document.createElement(node.tag)
  if (typeof node.children === 'string') {
    const text = document.createTextNode(node.children)
    el.appendChild(text)
  }
  if (Array.isArray(node.children)) {
    node.children.forEach(child => render(child, el)) // 递归地处理节点的渲染
  }
  root.appendChild(el)
}

const node = {
  tag: 'div', // tag代表标签名称
  children: [ // children可以是一个数组,代表子节点
    {
      tag: 'h1',
      children: 'hello' // children也可以是一段文本,代表文本子节点
    }
  ]
}

render(node, document.body)

用户在使用render函数渲染内容时,直接为render函数提供了一个树型结构的数据对象

但若是用户需求是手写树型结构的数据对象太麻烦了,而且不直观,想要支持用类似HTML的方式描述树型结构的数据对象,所以引入编译的手段,把HTML标签编译成树型结构的数据对象

纯编译时框架例子

<div>
  <h1>hello</h1>
</div>

经过编译

const div = document.createElement('div')
const h1 = document.createElement('h1')
h1.innerText = 'hello'
div.appendChild(h1)
document.body.appendChild(div)

直接将HTML字符串编译成命令式代码过程

编译时+运行时框架例子

编写一个Compiler的程序,作用是把HTML字符串编译成树型结构的数据对象

const html = '
<div>
  <span>hello</span>
</div>
'
// 调用Compiler编译得到树形结构的数据对象
const obj = Compiler(html)
// 再调用render进行渲染
render(obj, document.bod)

原文地址:https://blog.csdn.net/2301_79980806/article/details/144330549

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!