Vue

217 阅读4分钟

前:Vue和React的区别

lq782655835.github.io/blogs/vue/d…

1. vue需要背指令,React更像原生JavaScript

image.png

2.响应式原理不同

image.png

3.事件机制不同

image.png

1.computed和watch的选择

一般首选computed。有异步操作的时候,不能用computed,因为计算属性必须有返回值。

image.png

2.指令相关

v-pre

不会被编译,比如{{msg}}就直接展现出先来了。

image.png

自定义指令

第一个参数element是绑定的dom,第二个参数是绑定关系(包含value)。

image.png

对象式定义,可以指定触发时机。若函数式,默认是在绑定时和update时触发。

image.png

vue2和vue3区别

响应式处理

www.bilibili.com/video/BV17h…

definepropty只能拦截set和get,因此,当我们给对象新增属性或者删除属性,它无法拦截。

vue3为什么用Reflect

juejin.cn/post/722246…

可以发现,不用Reflect也行。但是,如果target对象中有 访问器属性(getter),那么get内部的this不是代理对象,我们需要将他设置为代理对象的话,就必须用Reflect。

    const tempObj1 = {
      a: 1,
      get value() {
        console.log("this是不是proxyObj", this === proxyObj); // false
        return this.a;
      },
    };
    const handler = {
      get: function (obj, prop, receiver) {
        // return obj[prop];
        return Reflect.get(obj, prop, receiver);
      },
      set: function (obj, prop, value, receiver) {
        obj[prop] = value;
        return true;
      },
    };
    const proxyObj = new Proxy(tempObj1, handler);
    proxyObj.value; // 1

Vue有哪些生命周期

image.png

执行顺序如下:

image.png

image.png


自定义事件

发送方:context.emit()

image.png

接收方:注意,如果是子组件A emit的事件,那么@event就得绑定在A组件身上(绑在其他组件身上无效)

image.png

事件总线

封装事件总线:

image.png

发送方:

image.png

接收方: 在onMounted()声命周期中开始监听

image.png

解除事件绑定:

image.png

计算属性

完整写法如下,如果简写,直接在computed里写回调即可。

image.png

侦听/监视属性

直接包裹在watch里面:

image.png

image.png

Vue 响应式

juejin.cn/post/693265…

observe: 循环遍历 data 对象,对于 data 的每个属性,使用 defineReactive 处理。

defineReactive: 使用 Object.defineProperty() ,访问或修改属性会触发 get 或者 set。每个属性有一个 dep 数组,数组里的每个元素都是一个watcher对象。

get:首次渲染的时候触发 get ,会将当前的 watcher 添加到 dep 数组中。

set:每次修改 data 的属性时,触发 set 。在 set 中,遍历该属性对应的 dep 数组,每遍历到一个 watcher 对象,就调用 watcher.update() ,更新视图。

可能的追问:

1.defineReactive 的属性也是对象怎么办?递归调用 observe ,继续循环遍历它的每个属性。

2.update如何改变视图?

例如更改某个属性:

let dom = document.getElementById('1');
dom.setAttribute('attr', 'diy');

www.bilibili.com/video/BV1UW…

image.png

3.视图改变了(比如input框),data的值如何改变?

遍历所有 DOM,如果某个 DOM 有 v-model属性,则给他 addEventListener(),修改 Vue 实例上的属性。

image.png

虚拟DOM

1.本质是JavaScript对象,一般包含 tag ,props,children

image.png

优势:

无需手动操作 DOM: 我们不再需要手动去操作 DOM(比如先 getElement,然后修改 innerHTML ),只需要写好 View-Model 的代码逻辑,框架会根据虚拟 DOM 和 数据双向绑定,帮我们以可预期的方式更新视图,极大提高我们的开发效率。

跨平台能力:虚拟DOM是与平台无关的中间层,使得Vue可以在不同的平台上运行,例如浏览器、移动端、服务器等。通过将虚拟DOM渲染成不同平台的实际DOM,实现了跨平台的能力。

数据改变引起虚拟DOM更新,虚拟DOM更新引起真实 DOM 更新。通过 Diff 算法找出最佳更新方法。

缺点:

首次渲染比较耗时:由于虚拟DOM需要将数据映射到虚拟DOM树并进行比对才能更新实际的DOM,因此初始渲染可能会比直接操作实际DOM略慢一些。这是因为需要进行额外的计算和比对过程。

简而言之:先构建虚拟DOM(Javasctipt对象),再生成真实DOM。

Diff算法

特点:只会同层比较,不会跨层比较,比如图中只会比较相同颜色的部分:

image.png

同一层级 比较 标签名、key值,如果两个值都一样,则认为是同一个节点。以此类推比较下一层级。发现有不同,则进行更新属性。

创建新真实DOM

image.png

如何调用 createElement():patch函数内调用

image.png

更新子节点

上面说的是新旧虚拟DOM不同,因此删除旧的真实DOM,创建新的真实DOM。那么,如果新旧虚拟DOM相同,只需要更新 children 应该怎么做呢?

命中是什么意思?命中的意思是,key 相同,标签名 也相同。新前和旧前如果命中,都是后移;新后和旧后如果命中,都是前移。循环条件是(新前 <= 新后 && 旧前 <= 旧后)。

需要注意的是,命中之后,先执行 patch(),再移动指针。

image.png

针对第三种和第四种情况的特殊说明:如何移动真实 DOM

image.png

image.png

如果四种策略都没命中,则 for 循环旧的节点列表(寻找新前)。找到后将该节点移动到新的位置(旧前之前)。

  • 如果旧的节点先循环结束,那么新前和新后之间剩余的部分就是需要新增的节点,我们应该创建相应的真实DOM。 创建完之后,插入在哪?见代码。

image.png

  • 如果新的节点先循环结束,那么旧前和旧后之间剩余的部分就是需要删除的节点,我们应该删除相应的真实DOM

image.png

nextTick

在下例中,message已经改变了,为什么dom.innerHTML仍然是旧值?因为DOM更新是异步操作,我们希望在下一次 dom更新结束后执行其指定的回调,就必须这么写。

<template>
  <div>
    <button @click="updateData">更新数据</button>
    <p>{{ message }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: "初始值",
    };
  },
  methods: {
    updateData() {
      this.message = "更新后的值";
      //   this.$nextTick(() => {
      //     // DOM已经更新完成,可以获取到正确的结果
      //     const updatedValue = this.$el.querySelector("p").textContent;
      //     console.log(updatedValue); // 输出:更新后的值
      //   });
      const updatedValue = this.$el.querySelector("p").textContent;
      console.log(updatedValue); // 输出:更新后的值
    },
  },
};
</script>