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

函数式组件劣势和原理

函数组件的特点

  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是多对多的关系

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理