关于前端:2023前端一面vue面试题合集

64次阅读

共计 13432 个字符,预计需要花费 34 分钟才能阅读完成。

函数式组件劣势和原理

函数组件的特点

  1. 函数式组件须要在申明组件是指定 functional:true
  2. 不须要实例化,所以没有 this,this 通过 render 函数的第二个参数 context 来代替
  3. 没有生命周期钩子函数,不能应用计算属性,watch
  4. 不能通过 $emit 对外裸露事件,调用事件只能通过context.listeners.click 的形式调用内部传入的事件
  5. 因为函数式组件是没有实例化的,所以在内部通过 ref 去援用组件时,理论援用的是HTMLElement
  6. 函数式组件的 props 能够不必显示申明,所以没有在 props 外面申明的属性都会被主动隐式解析为 prop, 而一般组件所有未声明的属性都解析到$attrs 外面,并主动挂载到组件根元素下面 (能够通过inheritAttrs 属性禁止)

长处

  1. 因为函数式组件不须要实例化,无状态,没有生命周期,所以渲染性能要好于一般组件
  2. 函数式组件构造比较简单,代码构造更清晰

应用场景:

  • 一个简略的展现组件,作为容器组件应用 比方 router-view 就是一个函数式组件
  • “高阶组件”——用于接管一个组件作为参数,返回一个被包装过的组件

例子

Vue.component('functional',{ // 构造函数产生虚构节点的
    functional:true, // 函数式组件 // data={attrs:{}}
    render(h){return h('div','test')
    }
})
const vm = new Vue({el: '#app'})

源码相干

// functional component
if (isTrue(Ctor.options.functional)) { // 带有 functional 的属性的就是函数式组件
  return createFunctionalComponent(Ctor, propsData, data, context, children)
}

// extract listeners, since these needs to be treated as
// child component listeners instead of DOM listeners
const listeners = data.on // 处理事件
// replace with listeners with .native modifier
// so it gets processed during parent component patch.
data.on = data.nativeOn // 解决原生事件

// install component management hooks onto the placeholder node
installComponentHooks(data) // 装置组件相干钩子(函数式组件没有调用此办法,从而性能高于一般组件)

双向数据绑定的原理

Vue.js 是采纳 数据劫持 联合 发布者 - 订阅者模式 的形式,通过 Object.defineProperty()来劫持各个属性的 setter,getter,在数据变动时公布音讯给订阅者,触发相应的监听回调。次要分为以下几个步骤:

  1. 须要 observe 的数据对象进行递归遍历,包含子属性对象的属性,都加上 setter 和 getter 这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变动
  2. compile 解析模板指令,将模板中的变量替换成数据,而后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,增加监听数据的订阅者,一旦数据有变动,收到告诉,更新视图
  3. Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁,次要做的事件是: ①在本身实例化时往属性订阅器 (dep) 外面增加本人 ②本身必须有一个 update()办法 ③待属性变动 dep.notice()告诉时,能调用本身的 update()办法,并触发 Compile 中绑定的回调,则功成身退。
  4. MVVM 作为数据绑定的入口,整合 Observer、Compile 和 Watcher 三者,通过 Observer 来监听本人的 model 数据变动,通过 Compile 来解析编译模板指令,最终利用 Watcher 搭起 Observer 和 Compile 之间的通信桥梁,达到数据变动 -> 视图更新;视图交互变动(input) -> 数据 model 变更的双向绑定成果。

Vue 组件如何通信?

Vue 组件通信的办法如下:

  • props/$emit+v-on: 通过 props 将数据自上而下传递,而通过 $emit 和 v -on 来向上传递信息。
  • EventBus: 通过 EventBus 进行信息的公布与订阅
  • vuex: 是全局数据管理库,能够通过 vuex 治理全局的数据流
  • $attrs/$listeners: Vue2.4 中退出的 $attrs/$listeners 能够进行跨级的组件通信
  • provide/inject:以容许一个先人组件向其所有子孙后代注入一个依赖,不管组件档次有多深,并在起上下游关系成立的工夫里始终失效,这成为了跨组件通信的根底

还有一些用 solt 插槽或者 ref 实例进行通信的,应用场景过于无限就不赘述了。

Vue.extend 作用和原理

官网解释:Vue.extend 应用根底 Vue 结构器,创立一个“子类”。参数是一个蕴含组件选项的对象。

其实就是一个子类结构器 是 Vue 组件的外围 api 实现思路就是应用原型继承的办法返回了 Vue 的子类 并且利用 mergeOptions 把传入组件的 options 和父类的 options 进行了合并

虚构 DOM 实现原理?

  • 虚构 DOM 实质上是 JavaScript 对象, 是对实在 DOM 的形象
  • 状态变更时,记录新树和旧树的差别
  • 最初把差别更新到真正的 dom 中

说说 Vue 的生命周期吧

什么时候被调用?

  • beforeCreate:实例初始化之后,数据观测之前调用
  • created:实例创立万之后调用。实例实现:数据观测、属性和办法的运算、 watch/event 事件回调。无 $el .
  • beforeMount:在挂载之前调用,相干 render 函数首次被调用
  • mounted:了被新创建的 vm.$el 替换,并挂载到实例下来之后调用改钩子。
  • beforeUpdate:数据更新前调用,产生在虚构 DOM 从新渲染和打补丁,在这之后会调用改钩子。
  • updated:因为数据更改导致的虚构 DOM 从新渲染和打补丁,在这之后会调用改钩子。
  • beforeDestroy:实例销毁前调用,实例依然可用。
  • destroyed:实例销毁之后调用,调用后,Vue 实例批示的所有货色都会解绑,所有事件监听器和所有子实例都会被移除

每个生命周期外部能够做什么?

  • created:实例曾经创立实现,因为他是最早触发的,所以能够进行一些数据、资源的申请。
  • mounted:实例曾经挂载实现,能够进行一些 DOM 操作。
  • beforeUpdate:能够在这个钩子中进一步的更改状态,不会触发重渲染。
  • updated:能够执行依赖于 DOM 的操作,然而要防止更改状态,可能会导致更新无线循环。
  • destroyed:能够执行一些优化操作,清空计时器,解除绑定事件。

ajax 放在哪个生命周期?:个别放在 mounted 中,保障逻辑统一性,因为生命周期是同步执行的, ajax 是异步执行的。复数服务端渲染 ssr 同一放在 created 中,因为服务端渲染不反对 mounted 办法。 什么时候应用 beforeDestroy?:以后页面应用 $on,须要解绑事件。分明定时器。解除事件绑定, scroll mousemove

参考 前端进阶面试题具体解答

$nextTick 是什么?

Vue 实现响应式并不是在数据产生后立刻更新 DOM,应用 vm.$nextTick 是在下次 DOM 更新循环完结之后立刻执行提早回调。在批改数据之后应用,则 能够在回调中获取更新后的 DOM

diff 算法

<details open=””><summary>答案 </summary>
<p>
</p><p> 工夫复杂度: 个树的齐全 diff 算法是一个工夫复杂度为 O(n*3),vue 进行优化转化成 O(n)。</p>
<p> 了解:</p>
<ul>
<li>
<p> 最小量更新, key 很重要。这个能够是这个节点的惟一标识,通知 diff 算法,在更改前后它们是同一个 DOM 节点 </p>
<ul>
<li> 扩大 v-for 为什么要有 key,没有 key 会暴力复用,举例子的话轻易说一个比方挪动节点或者减少节点(批改 DOM),加 key 只会挪动缩小操作 DOM。</li>
</ul>
</li>
<li>
<p> 只有是同一个虚构节点才会进行精细化比拟,否则就是暴力删除旧的,插入新的。</p>
</li>
<li>
<p> 只进行同层比拟,不会进行跨层比拟。</p>
</li>
</ul>
<p>diff 算法的优化策略:四种命中查找,四个指针 </p>
<ol>
<li>
<p> 旧前与新前(先比结尾,后插入和删除节点的这种状况)</p>
</li>
<li>
<p> 旧后与新后(比结尾,前插入或删除的状况)</p>
</li>
<li>
<p> 旧前与新后(头与尾比,此种产生了,波及挪动节点,那么新前指向的节点,挪动到旧后之后)</p>
</li>
<li>
<p> 旧后与新前(尾与头比,此种产生了,波及挪动节点,那么新前指向的节点,挪动到旧前之前)</p>
</li>
</ol>
<p></p>
</details>

— 问完下面这些如果都能很分明的话,根本 O 了 —

以下的这些简略的概念,你必定也是没有问题的啦😉

vue 中应用了哪些设计模式

1. 工厂模式 – 传入参数即可创立实例

虚构 DOM 依据参数的不同返回根底标签的 Vnode 和组件 Vnode

2. 单例模式 – 整个程序有且仅有一个实例

vuex 和 vue-router 的插件注册办法 install 判断如果零碎存在实例就间接返回掉

3. 公布 - 订阅模式 (vue 事件机制)

4. 观察者模式 (响应式数据原理)

5. 装璜模式: (@装璜器的用法)

6. 策略模式 策略模式指对象有某个行为, 然而在不同的场景中, 该行为有不同的实现计划 - 比方选项的合并策略

… 其余模式欢送补充

keep-alive 应用场景和原理

keep-alive 是 Vue 内置的一个组件,能够实现组件缓存,当组件切换时不会对以后组件进行卸载。

  • 罕用的两个属性 include/exclude,容许组件有条件的进行缓存。
  • 两个生命周期 activated/deactivated,用来得悉以后组件是否处于沉闷状态。
  • keep-alive 的中还使用了 LRU(最近起码应用) 算法,抉择最近最久未应用的组件予以淘汰。

你有对 Vue 我的项目进行哪些优化?

(1)代码层面的优化

  • v-if 和 v-show 辨别应用场景
  • computed 和 watch 辨别应用场景
  • v-for 遍历必须为 item 增加 key,且防止同时应用 v-if
  • 长列表性能优化
  • 事件的销毁
  • 图片资源懒加载
  • 路由懒加载
  • 第三方插件的按需引入
  • 优化有限列表性能
  • 服务端渲染 SSR or 预渲染

(2)Webpack 层面的优化

  • Webpack 对图片进行压缩
  • 缩小 ES6 转为 ES5 的冗余代码
  • 提取公共代码
  • 模板预编译
  • 提取组件的 CSS
  • 优化 SourceMap
  • 构建后果输入剖析
  • Vue 我的项目的编译优化

(3)根底的 Web 技术的优化

  • 开启 gzip 压缩
  • 浏览器缓存
  • CDN 的应用
  • 应用 Chrome Performance 查找性能瓶颈

理解 nextTick 吗?

异步办法,异步渲染最初一步,与 JS 事件循环分割严密。次要应用了宏工作微工作(setTimeoutpromise那些),定义了一个异步办法,屡次调用 nextTick 会将办法存入队列,通过异步办法清空以后队列。

谈谈 Vue 和 React 组件化的思维

  • 1. 咱们在各个页面开发的时候,会产生很多反复的性能,比方 element 中的 xxxx。像这种纯正非页面的 UI,便成为咱们罕用的 UI 组件,最后的前端组件也就仅仅指的是 UI 组件
  • 2. 随着业务逻辑变得越来多是,咱们就想要咱们的组件能够解决很多事,这就是咱们常说的组件化,这个组件就不是 UI 组件了,而是包具体业务的业务组件
  • 3. 这种开发思维就是分而治之。最大水平的升高开发难度和保护老本的成果。并且能够多人合作,每个人写不同的组件,最初像撘积木一样的把它形成一个页面

Vue3.0 和 2.0 的响应式原理区别

Vue3.x 改用 Proxy 代替 Object.defineProperty。因为 Proxy 能够间接监听对象和数组的变动,并且有多达 13 种拦挡办法。

相干代码如下

import {mutableHandlers} from "./baseHandlers"; // 代理相干逻辑
import {isObject} from "./util"; // 工具办法

export function reactive(target) {
  // 依据不同参数创立不同响应式对象
  return createReactiveObject(target, mutableHandlers);
}
function createReactiveObject(target, baseHandler) {if (!isObject(target)) {return target;}
  const observed = new Proxy(target, baseHandler);
  return observed;
}

const get = createGetter();
const set = createSetter();

function createGetter() {return function get(target, key, receiver) {
    // 对获取的值进行喷射
    const res = Reflect.get(target, key, receiver);
    console.log("属性获取", key);
    if (isObject(res)) {
      // 如果获取的值是对象类型,则返回以后对象的代理对象
      return reactive(res);
    }
    return res;
  };
}
function createSetter() {return function set(target, key, value, receiver) {const oldValue = target[key];
    const hadKey = hasOwn(target, key);
    const result = Reflect.set(target, key, value, receiver);
    if (!hadKey) {console.log("属性新增", key, value);
    } else if (hasChanged(value, oldValue)) {console.log("属性值被批改", key, value);
    }
    return result;
  };
}
export const mutableHandlers = {
  get, // 当获取属性时调用此办法
  set, // 当批改属性时调用此办法
};

v-model 的原理?

咱们在 vue 我的项目中次要应用 v-model 指令在表单 input、textarea、select 等元素上创立双向数据绑定,咱们晓得 v-model 实质上不过是语法糖,v-model 在外部为不同的输出元素应用不同的属性并抛出不同的事件:

  • text 和 textarea 元素应用 value 属性和 input 事件;
  • checkbox 和 radio 应用 checked 属性和 change 事件;
  • select 字段将 value 作为 prop 并将 change 作为事件。

以 input 表单元素为例:

<input v-model='something'>

相当于

<input v-bind:value="something" v-on:input="something = $event.target.value">

如果在自定义组件中,v-model 默认会利用名为 value 的 prop 和名为 input 的事件,如下所示:

父组件:<ModelChild v-model="message"></ModelChild>

子组件:<div>{{value}}</div>

props:{value: String},
methods: {test1(){this.$emit('input', '小红')
  },
},

Vue 的生命周期办法有哪些

  1. Vue 实例有一个残缺的生命周期,也就是从 开始创立 初始化数据 编译模版 挂载 Dom -> 渲染 更新 -> 渲染 卸载 等一系列过程,咱们称这是 Vue 的生命周期
  2. Vue生命周期总共分为 8 个阶段 创立前 / 后 载入前 / 后 更新前 / 后 销毁前 / 后

beforeCreate => created => beforeMount => Mounted => beforeUpdate => updated => beforeDestroy => destroyedkeep-alive下:activated deactivated

生命周期 vue2 生命周期 vue3 形容
beforeCreate beforeCreate 在实例初始化之后,数据观测(data observer) 之前被调用。
created created 实例曾经创立实现之后被调用。在这一步,实例已实现以下的配置:数据观测(data observer),属性和办法的运算,watch/event 事件回调。这里没有$el
beforeMount beforeMount 在挂载开始之前被调用:相干的 render 函数首次被调用
mounted mounted el 被新创建的 vm.$el 替换,并挂载到实例下来之后调用该钩子
beforeUpdate beforeUpdate 组件数据更新之前调用,产生在虚构 DOM 打补丁之前
updated updated 因为数据更改导致的虚构 DOM 从新渲染和打补丁,在这之后会调用该钩子
beforeDestroy beforeUnmount 实例销毁之前调用。在这一步,实例依然齐全可用
destroyed unmounted 实例销毁后调用。调用后,Vue 实例批示的所有货色都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。

其余几个生命周期

生命周期 vue2 生命周期 vue3 形容
activated activated keep-alive专属,组件被激活时调用
deactivated deactivated keep-alive专属,组件被销毁时调用
errorCaptured errorCaptured 捕捉一个来自子孙组件的谬误时被调用
renderTracked 调试钩子,响应式依赖被收集时调用
renderTriggered 调试钩子,响应式依赖被触发时调用
serverPrefetch ssr only,组件实例在服务器上被渲染前调用
  1. 要把握每个生命周期外部能够做什么事
  2. beforeCreate 初始化 vue 实例,进行数据观测。执行时组件实例还未创立,通常用于插件开发中执行一些初始化工作
  3. created 组件初始化结束,能够拜访各种数据,获取接口数据等
  4. beforeMount 此阶段 vm.el 虽已实现 DOM 初始化,但并未挂载在 el 选项上
  5. mounted 实例曾经挂载实现,能够进行一些 DOM 操作
  6. beforeUpdate 更新前,可用于获取更新前各种状态。此时 view 层还未更新,可用于获取更新前各种状态。能够在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。
  7. updated 实现 view 层的更新,更新后,所有状态已是最新。能够执行依赖于 DOM 的操作。然而在大多数状况下,你应该防止在此期间更改状态,因为这可能会导致更新有限循环。该钩子在服务器端渲染期间不被调用。
  8. destroyed 能够执行一些优化操作, 清空定时器,解除绑定事件
  9. vue3 beforeunmount:实例被销毁前调用,可用于一些定时器或订阅的勾销
  10. vue3 unmounted:销毁一个实例。可清理它与其它实例的连贯,解绑它的全副指令及事件监听器
<div id="app">{{name}}</div>
<script>
    const vm = new Vue({data(){return {name:'poetries'}
        },
        el: '#app',
        beforeCreate(){// 数据观测(data observer) 和 event/watcher 事件配置之前被调用。console.log('beforeCreate');
        },
        created(){
            // 属性和办法的运算,watch/event 事件回调。这里没有 $el
            console.log('created')
        },
        beforeMount(){
            // 相干的 render 函数首次被调用。console.log('beforeMount')
        },
        mounted(){
            // 被新创建的 vm.$el 替换
            console.log('mounted')
        },
        beforeUpdate(){
            //  数据更新时调用,产生在虚构 DOM 从新渲染和打补丁之前。console.log('beforeUpdate')
        },
        updated(){
            //  因为数据更改导致的虚构 DOM 从新渲染和打补丁,在这之后会调用该钩子。console.log('updated')
        },
        beforeDestroy(){
            // 实例销毁之前调用 实例依然齐全可用
            console.log('beforeDestroy')
        },
        destroyed(){ 
            // 所有货色都会解绑定,所有的事件监听器会被移除
            console.log('destroyed')
        }
    });
    setTimeout(() => {
        vm.name = 'poetry';
        setTimeout(() => {vm.$destroy()  
        }, 1000);
    }, 1000);
</script>
  1. 组合式 API 生命周期钩子

你能够通过在生命周期钩子后面加上“on”来拜访组件的生命周期钩子。

下表蕴含如何在 setup() 外部调用生命周期钩子:

选项式 API Hook inside setup
beforeCreate 不须要 *
created 不须要 *
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeUnmount onBeforeUnmount
unmounted onUnmounted
errorCaptured onErrorCaptured
renderTracked onRenderTracked
renderTriggered onRenderTriggered

因为 setup 是围绕 beforeCreatecreated 生命周期钩子运行的,所以不须要显式地定义它们。换句话说,在这些钩子中编写的任何代码都应该间接在 setup 函数中编写

export default {setup() {
    // mounted
    onMounted(() => {console.log('Component is mounted!')
    })
  }
}

setupcreated 谁先执行?

  • beforeCreate: 组件被创立进去,组件的 methodsdata还没初始化好
  • setup:在 beforeCreatecreated之间执行
  • created: 组件被创立进去,组件的 methodsdata曾经初始化好了

因为在执行 setup 的时候,created还没有创立好,所以在 setup 函数内咱们是无奈应用 datamethods的。所以 vue 为了让咱们防止谬误的应用,间接将 setup 函数内的 this 执行指向undefined

import {ref} from "vue"
export default {// setup 函数是组合 api 的入口函数,留神在组合 api 中定义的变量或者办法,要在 template 响应式须要 return{}进来
  setup(){let count = ref(1)
    function myFn(){count.value +=1}
    return {count,myFn}
  },

}
  1. 其余问题
  2. 什么是 vue 生命周期? Vue 实例从创立到销毁的过程,就是生命周期。从开始创立、初始化数据、编译模板、挂载 Dom→渲染、更新→渲染、销毁等一系列过程,称之为 Vue 的生命周期。
  • vue 生命周期的作用是什么? 它的生命周期中有多个事件钩子,让咱们在管制整个 Vue 实例的过程时更容易造成好的逻辑。
  • vue 生命周期总共有几个阶段? 它能够总共分为 8 个阶段:创立前 / 后、载入前 / 后、更新前 / 后、销毁前 / 销毁后。
  • 第一次页面加载会触发哪几个钩子? 会触发上面这几个beforeCreatecreatedbeforeMountmounted
  • 你的接口申请个别放在哪个生命周期中? 接口申请个别放在 mounted 中,但须要留神的是服务端渲染时不反对 mounted,须要放到created
  • DOM 渲染在哪个周期中就曾经实现?mounted 中,

    • 留神 mounted 不会承诺所有的子组件也都一起被挂载。如果你心愿等到整个视图都渲染结束,能够用 vm.$nextTick 替换掉 mounted
    mounted: function () {this.$nextTick(function () {
            // Code that will run only after the
            // entire view has been rendered
        })
      }


### Vue 模版编译原理

vue 中的模板 template 无奈被浏览器解析并渲染,因为这不属于浏览器的规范,不是正确的 HTML 语法,所有须要将 template 转化成一个 JavaScript 函数,这样浏览器就能够执行这一个函数并渲染出对应的 HTML 元素,就能够让视图跑起来了,这一个转化的过程,就成为模板编译。模板编译又分三个阶段,解析 parse,优化 optimize,生成 generate,最终生成可执行函数 render。- ** 解析阶段 **:应用大量的正则表达式对 template 字符串进行解析,将标签、指令、属性等转化为形象语法树 AST。- ** 优化阶段 **:遍历 AST,找到其中的一些动态节点并进行标记,不便在页面重渲染的时候进行 diff 比拟时,间接跳过这一些动态节点,优化 runtime 的性能。- ** 生成阶段 **:将最终的 AST 转化为 render 函数字符串。### Vue 中给 data 中的对象属性增加一个新的属性时会产生什么?如何解决?

<template>
<div>

  <ul>
     <li v-for="value in obj" :key="value"> {{value}} </li>       </ul>       <button @click="addObjB"> 增加 obj.b</button>    </div>

</template>

<script>

export default {data () {return {               obj: {                   a: 'obj.a'}           }        },       methods: {addObjB () {this.obj.b = 'obj.b'               console.log(this.obj)           }       }   }

</script>


点击 button 会发现,obj.b 曾经胜利增加,然而视图并未刷新。这是因为在 Vue 实例创立时,obj.b 并未申明,因而就没有被 Vue 转换为响应式的属性,天然就不会触发视图的更新,这时就须要应用 Vue 的全局 api **$set():**

addObjB () (
this.$set(this.obj, ‘b’, ‘obj.b’)
console.log(this.obj)
}


$set()办法相当于手动的去把 obj.b 解决成一个响应式的属性,此时视图也会跟着扭转了。### Vue 怎么用 vm.$set() 解决对象新增属性不能响应的问题?受古代 JavaScript 的限度,Vue ** 无奈检测到对象属性的增加或删除 **。因为 Vue 会在初始化实例时对属性执行 getter/setter 转化,所以属性必须在 data 对象上存在能力让 Vue 将它转换为响应式的。然而 Vue 提供了 `Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value)`  来实现为对象增加响应式属性,那框架自身是如何实现的呢?咱们查看对应的 Vue 源码:`vue/src/core/instance/index.js`

export function set (target: Array<any> | Object, key: any, val: any): any {
// target 为数组
if (Array.isArray(target) && isValidArrayIndex(key)) {

// 批改数组的长度, 防止索引 > 数组长度导致 splcie()执行有误
target.length = Math.max(target.length, key)
// 利用数组的 splice 变异办法触发响应式  
target.splice(key, 1, val)
return val

}
// key 曾经存在,间接批改属性值
if (key in target && !(key in Object.prototype)) {

target[key] = val
return val

}
const ob = (target: any).__ob__
// target 自身就不是响应式数据, 间接赋值
if (!ob) {

target[key] = val
return val

}
// 对属性进行响应式解决
defineReactive(ob.value, key, val)
ob.dep.notify()
return val
}


咱们浏览以上源码可知,vm.$set 的实现原理是:- 如果指标是数组,间接应用数组的 splice 办法触发相应式;- 如果指标是对象,会先判读属性是否存在、对象是否是响应式,最终如果要对属性进行响应式解决,则是通过调用   defineReactive 办法进行响应式解决(defineReactive 办法就是  Vue 在初始化对象时,给对象属性采纳 Object.defineProperty 动静增加 getter 和 setter 的性能所调用的办法)## Vue 中如何进行依赖收集?* 每个属性都有本人的 `dep` 属性,寄存他所依赖的 `watcher`,当属性变动之后会告诉本人对应的 `watcher` 去更新
* 默认会在初始化时调用 `render` 函数,此时会触发属性依赖收集 `dep.depend`
* 当属性产生批改时会触发 `watcher` 更新 `dep.notify()`

![](https://img-blog.csdnimg.cn/img_convert/2585adec162987c743fbaa9085084f65.png)

** 依赖收集简版 **

let obj = {name: ‘poetry’, age: 20};

class Dep {

constructor() {this.subs = [] // subs [watcher]
}
depend() {this.subs.push(Dep.target)
}
notify() {this.subs.forEach(watcher => watcher.update())
}

}
Dep.target = null;
observer(obj); // 响应式属性劫持

// 依赖收集 所有属性都会减少一个 dep 属性,
// 当渲染的时候取值了,这个 dep 属性 就会将渲染的 watcher 收集起来
// 数据更新 会让 watcher 从新执行

// 观察者模式

// 渲染组件时 会创立 watcher
class Watcher {

constructor(render) {this.get();
}
get() {
  Dep.target = this;
  render(); // 执行 render
  Dep.target = null;
}
update() {this.get();
}

}
const render = () => {

console.log(obj.name); // obj.name => get 办法

}

// 组件是 watcher、计算属性是 watcher
new Watcher(render);

function observer(value) {// proxy reflect

if (typeof value === 'object' && typeof value !== null)
for (let key in value) {defineReactive(value, key, value[key]);
}

}
function defineReactive(obj, key, value) {

// 创立一个 dep
let dep = new Dep();

// 递归察看子属性
observer(value);

Object.defineProperty(obj, key, {get() { // 收集对应的 key 在哪个办法(组件)中被应用
        if (Dep.target) { // watcher
            dep.depend(); // 这里会建设 dep 和 watcher 的关系}
        return value;
    },
    set(newValue) {if (newValue !== value) {observer(newValue);
            value = newValue; // 让 key 对应的办法(组件从新渲染)从新执行
            dep.notify()}
    }
})

}

// 模仿数据获取,触发 getter
obj.name = ‘poetries’

// 一个属性一个 dep,一个属性能够对应多个 watcher(一个属性能够在任何组件中应用、在多个组件中应用)
// 一个 dep 对应多个 watcher
// 一个 watcher 对应多个 dep(一个视图对应多个属性)
// dep 和 watcher 是多对多的关系

正文完
 0