畅聊V2与V3,卷不动?还得练

主题

最近有感于各种面经理论体系的惊吓,信奉能动手绝不系统学习理论原则的我,慌慌张张的去又瞅了眼V3的源码,顺带看文档的时候发现了挺有意思的解释,结合实践和源码的观看,简短的做个摘要点。

什么是 Vue?

是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。

声明式的、组件化的编程模型,用于构建用户界面

  • 声明式渲染:Vue 基于标准 HTML 拓展了一套模板语法,使得我们可以声明式地描述最终输出的 HTML 和 JavaScript 状态之间的关系。

  • 响应性:Vue 会自动跟踪 JavaScript 状态并在其发生变化时响应式地更新 DOM。

  • 渐进式框架:设计非常注重灵活性和"可以被逐步集成"。

    这个点有槽点,我理解这个是个过渡阶段,从15年Vue刚进入视野,我的观感是有点儿类似于模板引擎-EJS,Handlebarsjs 等等,多了个响应式,我隐约记得好像当时的提法是双向绑定,改变对象触发dom改变,改变dom触发对象改变、在非工程化的时期,相较于React的原始引入写法,Vue更容易被接受和融合,但放到工程化的天下,貌似这个渐进式的提法有点儿站不住,特别V2和V3的差异化之后,但不得不承认,相较于React的体验上,我确实觉得用着比较轻盈,就此打住,不争辩,只是个人使用体验。

什么是响应性(V3)?

响应性是一种可以使我们声明式地处理变化的编程范式。 一个经常被拿来当作典型例子的用例即是 Excel 表格:

A B C
0 1
1 2
2 3

这里单元格 A2 中的值是通过公式 = A0 + A1 来定义的 (你可以在 A2 上点击来查看或编辑该公式),因此最终得到的值为 3,正如所料。但如果你试着更改 A0 或 A1,你会注意到 A2 也随即自动更新了。

而 JavaScript 默认并不是这样的。如果我们用 JavaScript 写类似的逻辑:

js 复制代码
let A0 = 1
let A1 = 2
let A2 = A0 + A1

console.log(A2) // 3

A0 = 2
console.log(A2) // 仍然是 3

当我们更改 A0 后,A2 不会自动更新。

那么我们如何在 JavaScript 中做到这一点呢?首先,为了能重新运行计算的代码来更新 A2,我们需要将其包装为一个函数:

js 复制代码
let A2

function update() {
  A2 = A0 + A1
}

然后,我们需要定义几个术语:

  • 这个 update() 函数会产生一个副作用 ,或者就简称为作用 (effect),因为它会更改程序里的状态。
  • A0A1 被视为这个作用的依赖 (dependency),因为它们的值被用来执行这个作用。因此这次作用也可以说是一个它依赖的订阅者 (subscriber)。

我们需要一个魔法函数,能够在 A0A1 (这两个依赖 ) 变化时调用 update() (产生作用)。

js 复制代码
whenDepsChange(update)

这个 whenDepsChange() 函数有如下的任务:

  1. 当一个变量被读取时进行追踪。例如我们执行了表达式 A0 + A1 的计算,则 A0A1 都被读取到了。
  2. 如果一个变量在当前运行的副作用中被读取了,就将该副作用设为此变量的一个订阅者。例如由于 A0A1update() 执行时被访问到了,则 update() 需要在第一次调用之后成为 A0A1 的订阅者。
  3. 探测一个变量的变化。例如当我们给 A0 赋了一个新的值后,应该通知其所有订阅了的副作用重新执行。

Vue3 中的响应性是如何工作的

我们无法直接追踪对上述示例中局部变量的读写,原生 JavaScript 没有提供任何机制能做到这一点。但是 ,我们是可以追踪对象属性的读写的。

在 JavaScript 中有两种劫持 property 访问的方式:getter / settersProxies。Vue 2 使用 getter / setters 完全是出于支持旧版本浏览器的限制。而在 Vue 3 中则使用了 Proxy 来创建响应式对象,仅将 getter / setter 用于 ref。下面的伪代码将会说明它们是如何工作的:

js 复制代码
function reactive(obj) {
  return new Proxy(obj, {
    get(target, key) {
      track(target, key)
      return target[key]
    },
    set(target, key, value) {
      target[key] = value
      trigger(target, key)
    }
  })
}

function ref(value) {
  const refObject = {
    get value() {
      track(refObject, 'value')
      return value
    },
    set value(newValue) {
      value = newValue
      trigger(refObject, 'value')
    }
  }
  return refObject
}

常见的响应式副作用的用例是什么呢?自然是更新 DOM!我们可以像下面这样实现一个简单的"响应式渲染":

js 复制代码
import { ref, watchEffect } from 'vue'

const count = ref(0)

watchEffect(() => {
  document.body.innerHTML = `计数:${count.value}`
})

// 更新 DOM
count.value++

实际上,这与 Vue 组件保持状态和 DOM 同步的方式非常接近------每个组件实例创建一个响应式副作用来渲染和更新 DOM。

关于ref这个槽点,作者给出的解释是这样的,很多其他框架已经引入了与 Vue 组合式 API 中的 ref 类似的响应性基础类型,并称之为"信号"

与 Vue 的 ref 相比,Solid 和 Angular 基于 getter 的 API 风格在 Vue 组件中使用时提供了一些有趣的权衡:

  • ().value 略微省事,但更新值却更冗长;
  • 没有 ref 解包:总是需要通过 () 来访问值。这使得值的访问在任何地方都是一致的。这也意味着你可以将原始信号作为组件的参数传递下去。

这些 API 风格是否适合你,在某种程度上是主观的。我们在这里的目标是展示这些不同的 API 设计之间的基本相似性和取舍。我们还想说明 Vue 是灵活的:你并没有真正被限定在现有的 API 中。如有必要,你可以创建你自己的响应性基础 API,以满足更多的具体需求。

补充个渲染机制和虚拟dom

Vue2 响应式原理

检测变化的注意事项

由于 JavaScript 的限制,Vue 不能检测数组和对象的变化。尽管如此我们还是有一些办法来回避这些限制并保证它们的响应性。

对于对象

ue 无法检测 property 的添加或移除。由于 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的。例如:

js 复制代码
var vm = new Vue({  
data:{  
a:1  
}  
})  
  
// `vm.a` 是响应式的  
  
vm.b = 2  
// `vm.b` 是非响应式的

对于已经创建的实例,Vue 不允许动态添加根级别的响应式 property。但是,可以使用 Vue.set(object, propertyName, value) 方法向嵌套对象添加响应式 property。例如,对于:

js 复制代码
Vue.set(vm.someObject, 'b', 2)

对于数组

Vue 不能检测以下数组的变动:

  1. 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
  2. 当你修改数组的长度时,例如:vm.items.length = newLength

举个例子:

js 复制代码
var vm = new Vue({  
data: {  
items: ['a', 'b', 'c']  
}  
})  
vm.items[1] = 'x' // 不是响应性的  
vm.items.length = 2 // 不是响应性的

解决办法同样是Vue.set

js 复制代码
// Vue.set  
Vue.set(vm.items, indexOfItem, newValue)

好吧,这些注意点,基本上我在最初好像都犯了,但貌似之前好像没补充这些点,后来总结属于?

生命周期

Vue3的生命周期 Vue2的生命周期

胡乱抒发一下

各种框架升级的事儿经历了不少,都属于是箭在弦上,逼到墙角、所以对待升级的事儿基本保持能不升级尽量不升级,但有新尝试,必然会积极的去应用新特性的心态,毕竟,框架升级肯定是朝着便捷性去的,与此同时带来的学习成本和易读性肯定会增加,但这玩意儿咋说呢,总会面对各种奇奇怪怪的诉求,逃避和拒绝往往坑害的是自己,工作里面的现学现卖我基本还没这个胆量,属于尝遍坑点后,才会系统性的学习应用。

至于各种变态的面经八股文 ,咋说呢, "磨刀不误砍柴工" 、 这里的磨刀过程在我的认知里面,有点儿 "纸上谈兵" 的感觉,这可能跟个人的学习思路有关系,遇到问题,解决问题,而不是系统性的学习,然后规避问题、我觉得前者的学习成本要低很多,如果你对编程语言持以工具的态度,那可能 "一通百通" 的思路会比较认可,毕竟我们解决现实问题的场景和思路总有规律,针对性的用工具,也就是个过程了。

PS

这里也不针对性的贴过程,再回过头来看,官方的文档的思路搞的很贴切,摘的没停住,见谅勿怪~。

相关推荐
一颗花生米。2 小时前
深入理解JavaScript 的原型继承
java·开发语言·javascript·原型模式
学习使我快乐012 小时前
JS进阶 3——深入面向对象、原型
开发语言·前端·javascript
bobostudio19952 小时前
TypeScript 设计模式之【策略模式】
前端·javascript·设计模式·typescript·策略模式
勿语&3 小时前
Element-UI Plus 暗黑主题切换及自定义主题色
开发语言·javascript·ui
黄尚圈圈3 小时前
Vue 中引入 ECharts 的详细步骤与示例
前端·vue.js·echarts
浮华似水4 小时前
简洁之道 - React Hook Form
前端
正小安7 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
_.Switch8 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光8 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   8 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发