Redux 和 Vuex 有什么区别,它们的独特思维

(1)Redux 和 Vuex区别

  • Vuex改良了Redux中的Action和Reducer函数,以mutations变动函数取代Reducer,无需switch,只需在对应的mutation函数里扭转state值即可
  • Vuex因为Vue主动从新渲染的个性,无需订阅从新渲染函数,只有生成新的State即可
  • Vuex数据流的程序是∶View调用store.commit提交对应的申请到Store中对应的mutation函数->store扭转(vue检测到数据变动主动渲染)

艰深点了解就是,vuex 弱化 dispatch,通过commit进行 store状态的一次更变;勾销了action概念,不用传入特定的 action模式进行指定变更;弱化reducer,基于commit参数间接对数据进行转变,使得框架更加繁难;

(2)独特思维

  • 单—的数据源
  • 变动能够预测

实质上:redux与vuex都是对mvvm思维的服务,将数据从视图中抽离的一种计划;
模式上:vuex借鉴了redux,将store作为全局的数据中心,进行mode治理;

v-model 能够被用在自定义组件上吗?如果能够,如何应用?

能够。v-model 实际上是一个语法糖,如:

<input v-model="searchText">

实际上相当于:

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

用在自定义组件上也是同理:

<custom-input v-model="searchText">

相当于:

<custom-input  v-bind:value="searchText"  v-on:input="searchText = $event"></custom-input>

显然,custom-input 与父组件的交互如下:

  1. 父组件将searchText变量传入custom-input 组件,应用的 prop 名为value
  2. custom-input 组件向父组件传出名为input的事件,父组件将接管到的值赋值给searchText

所以,custom-input 组件的实现应该相似于这样:

Vue.component('custom-input', {  props: ['value'],  template: `    <input      v-bind:value="value"      v-on:input="$emit('input', $event.target.value)"    >  `})

MVC 和 MVVM 区别

MVC

MVC 全名是 Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计榜样

  • Model(模型):是应用程序中用于解决应用程序数据逻辑的局部。通常模型对象负责在数据库中存取数据
  • View(视图):是应用程序中解决数据显示的局部。通常视图是根据模型数据创立的
  • Controller(控制器):是应用程序中解决用户交互的局部。通常控制器负责从视图读取数据,管制用户输出,并向模型发送数据

MVC 的思维:一句话形容就是 Controller 负责将 Model 的数据用 View 显示进去,换句话说就是在 Controller 外面把 Model 的数据赋值给 View。

MVVM

MVVM 新增了 VM 类

  • ViewModel 层:做了两件事达到了数据的双向绑定 一是将【模型】转化成【视图】,行将后端传递的数据转化成所看到的页面。实现的形式是:数据绑定。二是将【视图】转化成【模型】,行将所看到的页面转化成后端的数据。实现的形式是:DOM 事件监听。

MVVM 与 MVC 最大的区别就是:它实现了 View 和 Model 的主动同步,也就是当 Model 的属性扭转时,咱们不必再本人手动操作 Dom 元素,来扭转 View 的显示,而是扭转属性后该属性对应 View 层显示会主动扭转(对应Vue数据驱动的思维)

整体看来,MVVM 比 MVC 精简很多,不仅简化了业务与界面的依赖,还解决了数据频繁更新的问题,不必再用选择器操作 DOM 元素。因为在 MVVM 中,View 不晓得 Model 的存在,Model 和 ViewModel 也察看不到 View,这种低耦合模式进步代码的可重用性

留神:Vue 并没有齐全遵循 MVVM 的思维 这一点官网本人也有阐明

那么问题来了 为什么官网要说 Vue 没有齐全遵循 MVVM 思维呢?

  • 严格的 MVVM 要求 View 不能和 Model 间接通信,而 Vue 提供了$refs 这个属性,让 Model 能够间接操作 View,违反了这一规定,所以说 Vue 没有齐全遵循 MVVM。

computed 的实现原理

computed 实质是一个惰性求值的观察者。

computed 外部实现了一个惰性的 watcher,也就是 computed watcher,computed watcher 不会立即求值,同时持有一个 dep 实例。

其外部通过 this.dirty 属性标记计算属性是否须要从新求值。

当 computed 的依赖状态产生扭转时,就会告诉这个惰性的 watcher,

computed watcher 通过 this.dep.subs.length 判断有没有订阅者,

有的话,会从新计算,而后比照新旧值,如果变动了,会从新渲染。 (Vue 想确保不仅仅是计算属性依赖的值发生变化,而是当计算属性最终计算的值发生变化时才会触发渲染 watcher 从新渲染,实质上是一种优化。)

没有的话,仅仅把 this.dirty = true。 (当计算属性依赖于其余数据时,属性并不会立刻从新计算,只有之后其余中央须要读取属性的时候,它才会真正计算,即具备 lazy(懒计算)个性。)

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 组件间通信有哪几种形式?

Vue 组件间通信是面试常考的知识点之一,这题有点相似于凋谢题,你答复出越多办法当然越加分,表明你对 Vue 把握的越纯熟。Vue 组件间通信只有指以下 3 类通信:父子组件通信、隔代组件通信、兄弟组件通信,上面咱们别离介绍每种通信形式且会阐明此种办法可实用于哪类组件间通信。

(1)props / $emit 实用 父子组件通信

这种办法是 Vue 组件的根底,置信大部分同学耳闻能详,所以此处就不举例开展介绍。

(2)ref$parent / $children 实用 父子组件通信

  • ref:如果在一般的 DOM 元素上应用,援用指向的就是 DOM 元素;如果用在子组件上,援用就指向组件实例
  • $parent / $children:拜访父 / 子实例

(3)EventBus ($emit / $on) 实用于 父子、隔代、兄弟组件通信

这种办法通过一个空的 Vue 实例作为地方事件总线(事件核心),用它来触发事件和监听事件,从而实现任何组件间的通信,包含父子、隔代、兄弟组件。

(4)$attrs/$listeners 实用于 隔代组件通信

  • $attrs:蕴含了父作用域中不被 prop 所辨认 (且获取) 的个性绑定 ( class 和 style 除外 )。当一个组件没有申明任何 prop 时,这里会蕴含所有父作用域的绑定 ( class 和 style 除外 ),并且能够通过 v-bind="$attrs" 传入外部组件。通常配合 inheritAttrs 选项一起应用。
  • $listeners:蕴含了父作用域中的 (不含 .native 润饰器的) v-on 事件监听器。它能够通过 v-on="$listeners" 传入外部组件

(5)provide / inject 实用于 隔代组件通信

先人组件中通过 provider 来提供变量,而后在子孙组件中通过 inject 来注入变量。 provide / inject API 次要解决了跨级组件间的通信问题,不过它的应用场景,次要是子组件获取下级组件的状态,跨级组件间建设了一种被动提供与依赖注入的关系。

(6)Vuex 实用于 父子、隔代、兄弟组件通信

Vuex 是一个专为 Vue.js 利用程序开发的状态管理模式。每一个 Vuex 利用的外围就是 store(仓库)。“store” 基本上就是一个容器,它蕴含着你的利用中大部分的状态 ( state )。

  • Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地失去高效更新。
  • 扭转 store 中的状态的惟一路径就是显式地提交 (commit) mutation。这样使得咱们能够不便地跟踪每一个状态的变动。

Vue 组件通信有哪几种形式

  1. props 和$emit 父组件向子组件传递数据是通过 prop 传递的,子组件传递数据给父组件是通过$emit 触发事件来做到的
  2. $parent,$children 获取以后组件的父组件和以后组件的子组件
  3. $attrs 和$listeners A->B->C。Vue 2.4 开始提供了$attrs 和$listeners 来解决这个问题
  4. 父组件中通过 provide 来提供变量,而后在子组件中通过 inject 来注入变量。(官网不举荐在理论业务中应用,然而写组件库时很罕用)
  5. $refs 获取组件实例
  6. envetBus 兄弟组件数据传递 这种状况下能够应用事件总线的形式
  7. vuex 状态治理

computed和watch区别

  1. 当页面中有某些数据依赖其余数据进行变动的时候,能够应用计算属性computed
Computed实质是一个具备缓存的watcher,依赖的属性发生变化就会更新视图。 实用于计算比拟耗费性能的计算场景。当表达式过于简单时,在模板中放入过多逻辑会让模板难以保护,能够将简单的逻辑放入计算属性中解决

<template>{{fullName}}</template>export default {    data(){        return {            firstName: 'zhang',            lastName: 'san',        }    },    computed:{        fullName: function(){            return this.firstName + ' ' + this.lastName        }    }}
  1. watch用于察看和监听页面上的vue实例,如果要在数据变动的同时进行异步操作或者是比拟大的开销,那么watch为最佳抉择
Watch没有缓存性,更多的是察看的作用,能够监听某些数据执行回调。当咱们须要深度监听对象中的属性时,能够关上deep:true选项,这样便会对对象中的每一项进行监听。这样会带来性能问题,优化的话能够应用字符串模式监听,如果没有写到组件中,不要遗记应用unWatch手动登记

<template>{{fullName}}</template>export default {    data(){        return {            firstName: 'zhang',            lastName: 'san',            fullName: 'zhang san'        }    },    watch:{        firstName(val) {            this.fullName = val + ' ' + this.lastName        },        lastName(val) {            this.fullName = this.firstName + ' ' + val        }    }}

computed:

  • computed是计算属性,也就是计算值,它更多用于计算值的场景
  • computed具备缓存性,computed的值在getter执行后是会缓存的,只有在它依赖的属性值扭转之后,下一次获取computed的值时才会从新调用对应的getter来计算
  • computed实用于计算比拟耗费性能的计算场景

watch:

  • 更多的是「察看」的作用,相似于某些数据的监听回调,用于察看props $emit或者本组件的值,当数据变动时来执行回调进行后续操作
  • 无缓存性,页面从新渲染时值不变动也会执行

小结:

  • computedwatch都是基于watcher来实现的
  • computed属性是具备缓存的,依赖的值不发生变化,对其取值时计算属性办法不会从新执行
  • watch是监控值的变动,当值发生变化时调用其对应的回调函数
  • 当咱们要进行数值计算,而且依赖于其余数据,那么把这个数据设计为computed
  • 如果你须要在某个数据变动时做一些事件,应用watch来察看这个数据变动

答复范例

思路剖析

  • 先看computed, watch两者定义,列举应用上的差别
  • 列举应用场景上的差别,如何抉择
  • 应用细节、注意事项
  • vue3变动

computed特点:具备响应式的返回值

const count = ref(1)const plusOne = computed(() => count.value + 1)

watch特点:侦测变动,执行回调

const state = reactive({ count: 0 })watch(  () => state.count,  (count, prevCount) => {    /* ... */  })

答复范例

  1. 计算属性能够从组件数据派生出新数据,最常见的应用形式是设置一个函数,返回计算之后的后果,computedmethods的差别是它具备缓存性,如果依赖项不变时不会从新计算。侦听器能够侦测某个响应式数据的变动并执行副作用,常见用法是传递一个函数,执行副作用,watch没有返回值,但能够执行异步操作等简单逻辑
  2. 计算属性罕用场景是简化行内模板中的简单表达式,模板中呈现太多逻辑会是模板变得臃肿不易保护。侦听器罕用场景是状态变动之后做一些额定的DOM操作或者异步操作。抉择采纳何用计划时首先看是否须要派生出新值,根本能用计算属性实现的形式首选计算属性.
  3. 应用过程中有一些细节,比方计算属性也是能够传递对象,成为既可读又可写的计算属性。watch能够传递对象,设置deepimmediate等选项
  4. vue3watch选项产生了一些变动,例如不再能侦测一个点操作符之外的字符串模式的表达式; reactivity API中新呈现了watchwatchEffect能够齐全代替目前的watch选项,且性能更加弱小

根本应用

// src/core/observer:45;// 渲染watcher  /  computed watcher  /  watchconst vm = new Vue({    el: '#app',    data: {        firstname:'张',        lastname:'三'    },    computed:{ // watcher  =>   firstname lastname        // computed 只有取值时才执行        // Object.defineProperty .get        fullName(){ // firstName lastName 会收集fullName计算属性            return this.firstname + this.lastname        }    },    watch:{        firstname(newVal,oldVal){            console.log(newVal)        }    }});setTimeout(() => {    debugger;    vm.firstname = '赵'}, 1000);

相干源码

// 初始化statefunction initState (vm: Component) {  vm._watchers = []  const opts = vm.$options  if (opts.props) initProps(vm, opts.props)  if (opts.methods) initMethods(vm, opts.methods)  if (opts.data) {    initData(vm)  } else {    observe(vm._data = {}, true /* asRootData */)  }  // 初始化计算属性  if (opts.computed) initComputed(vm, opts.computed)   // 初始化watch  if (opts.watch && opts.watch !== nativeWatch) {     initWatch(vm, opts.watch)  }}// 计算属性取值函数function createComputedGetter (key) {  return function computedGetter () {    const watcher = this._computedWatchers && this._computedWatchers[key]    if (watcher) {      if (watcher.dirty) { // 如果值依赖的值发生变化,就会进行从新求值        watcher.evaluate(); // this.firstname lastname      }      if (Dep.target) { // 让计算属性所依赖的属性 收集渲染watcher        watcher.depend()      }      return watcher.value    }  }}// watch的实现Vue.prototype.$watch = function (    expOrFn: string | Function,    cb: any,    options?: Object  ): Function {    const vm: Component = this    debugger;    if (isPlainObject(cb)) {      return createWatcher(vm, expOrFn, cb, options)    }    options = options || {}    options.user = true    const watcher = new Watcher(vm, expOrFn, cb, options) // 创立watcher,数据更新调用cb    if (options.immediate) {      try {        cb.call(vm, watcher.value)      } catch (error) {        handleError(error, vm, `callback for immediate watcher "${watcher.expression}"`)      }    }    return function unwatchFn () {      watcher.teardown()    }}

能说下 vue-router 中罕用的 hash 和 history 路由模式实现原理吗?

(1)hash 模式的实现原理

晚期的前端路由的实现就是基于 location.hash 来实现的。其实现原理很简略,location.hash 的值就是 URL 中 # 前面的内容。比方上面这个网站,它的 location.hash 的值为 '#search':

https://www.word.com#search

hash 路由模式的实现次要是基于上面几个个性:

  • URL 中 hash 值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash 局部不会被发送;

hash 值的扭转,都会在浏览器的拜访历史中减少一个记录。因而咱们能通过浏览器的回退、后退按钮管制hash 的切换;

  • 能够通过 a 标签,并设置 href 属性,当用户点击这个标签后,URL 的 hash 值会产生扭转;或者应用 JavaScript 来对 loaction.hash 进行赋值,扭转 URL 的 hash 值;
  • 咱们能够应用 hashchange 事件来监听 hash 值的变动,从而对页面进行跳转(渲染)。

(2)history 模式的实现原理

HTML5 提供了 History API 来实现 URL 的变动。其中做最次要的 API 有以下两个:history.pushState() 和 history.repalceState()。这两个 API 能够在不进行刷新的状况下,操作浏览器的历史纪录。惟一不同的是,前者是新增一个历史记录,后者是间接替换以后的历史记录,如下所示:

window.history.pushState(null, null, path);window.history.replaceState(null, null, path);

history 路由模式的实现次要基于存在上面几个个性:

  • pushState 和 repalceState 两个 API 来操作实现 URL 的变动 ;
  • 咱们能够应用 popstate 事件来监听 url 的变动,从而对页面进行跳转(渲染);
  • history.pushState() 或 history.replaceState() 不会触发 popstate 事件,这时咱们须要手动触发页面跳转(渲染)。

vue 中应用了哪些设计模式

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

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

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

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

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

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

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

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

vue如何监听对象或者数组某个属性的变动

当在我的项目中间接设置数组的某一项的值,或者间接设置对象的某个属性值,这个时候,你会发现页面并没有更新。这是因为Object.defineProperty()限度,监听不到变动。

解决形式:

  • this.$set(你要扭转的数组/对象,你要扭转的地位/key,你要改成什么value)
this.$set(this.arr, 0, "OBKoro1"); // 扭转数组this.$set(this.obj, "c", "OBKoro1"); // 扭转对象
  • 调用以下几个数组的办法
splice()、 push()、pop()、shift()、unshift()、sort()、reverse()

vue源码里缓存了array的原型链,而后重写了这几个办法,触发这几个办法的时候会observer数据,意思是应用这些办法不必再进行额定的操作,视图主动进行更新。 举荐应用splice办法会比拟好自定义,因为splice能够在数组的任何地位进行删除/增加操作

vm.$set 的实现原理是:

  • 如果指标是数组,间接应用数组的 splice 办法触发相应式;
  • 如果指标是对象,会先判读属性是否存在、对象是否是响应式,最终如果要对属性进行响应式解决,则是通过调用 defineReactive 办法进行响应式解决( defineReactive 办法就是 Vue 在初始化对象时,给对象属性采纳 Object.defineProperty 动静增加 getter 和 setter 的性能所调用的办法)

v-if、v-show、v-html 的原理

  • v-if会调用addIfCondition办法,生成vnode的时候会疏忽对应节点,render的时候就不会渲染;
  • v-show会生成vnode,render的时候也会渲染成实在节点,只是在render过程中会在节点的属性中批改show属性值,也就是常说的display;
  • v-html会先移除节点下的所有节点,调用html办法,通过addProp增加innerHTML属性,归根结底还是设置innerHTML为v-html的值。

如何了解Vue中模板编译原理

Vue 的编译过程就是将 template 转化为 render 函数的过程
  • 解析生成AST树template模板转化成AST语法树,应用大量的正则表达式对模板进行解析,遇到标签、文本的时候都会执行对应的钩子进行相干解决
  • 标记优化 对动态语法做动态标记 markup(动态节点如div下有p标签内容不会变动) diff来做优化 动态节点跳过diff操作

    • Vue的数据是响应式的,但其实模板中并不是所有的数据都是响应式的。有一些数据首次渲染后就不会再变动,对应的DOM也不会变动。那么优化过程就是深度遍历AST树,依照相干条件对树节点进行标记。这些被标记的节点(动态节点)咱们就能够跳过对它们的比对,对运行时的模板起到很大的优化作用
    • 期待后续节点更新,如果是动态的,不会在比拟children
  • 代码生成 编译的最初一步是将优化后的AST树转换为可执行的代码

答复范例

思路

  • 引入vue编译器概念
  • 阐明编译器的必要性
  • 论述编译器工作流程

答复范例

  1. Vue中有个独特的编译器模块,称为compiler,它的次要作用是将用户编写的template编译为js中可执行的render函数。
  2. 之所以须要这个编译过程是为了便于前端能高效的编写视图模板。相比而言,咱们还是更违心用HTML来编写视图,直观且高效。手写render函数不仅效率底下,而且失去了编译期的优化能力。
  3. Vue中编译器会先对template进行解析,这一步称为parse,完结之后会失去一个JS对象,咱们称为 形象语法树AST ,而后是对AST进行深加工的转换过程,这一步成为transform,最初将后面失去的AST生成为JS代码,也就是render函数

可能的诘问

  1. Vue中编译器何时执行?

new Vue()之后。 Vue 会调用 _init 函数进行初始化,也就是这里的 init 过程,它会初始化生命周期、事件、 propsmethodsdatacomputedwatch等。其中最重要的是通过 Object.defineProperty 设置 settergetter 函数,用来实现「响应式」以及「依赖收集」
  • 初始化之后调用 $mount 会挂载组件,如果是运行时编译,即不存在 render function 然而存在 template 的状况,须要进行「编译」步骤
  • compile编译能够分成 parseoptimizegenerate 三个阶段,最终须要失去 render function
  • React有没有编译器?

react 应用babelJSX语法解析

<div id="app"></div><script>    let vm = new Vue({        el: '#app',        template: `<div>            // <span>hello world</span> 是动态节点            <span>hello world</span>                // <p>{{name}}</p> 是动静节点            <p>{{name}}</p>        </div>`,        data() {          return { name: 'test' }        }    });</script>

源码剖析

export function compileToFunctions(template) {  // 咱们须要把html字符串变成render函数  // 1.把html代码转成ast语法树  ast用来形容代码自身造成树结构 不仅能够形容html 也能形容css以及js语法  // 很多库都使用到了ast 比方 webpack babel eslint等等  let ast = parse(template);  // 2.优化动态节点:对ast树进行标记,标记动态节点    if (options.optimize !== false) {      optimize(ast, options);    }  // 3.通过ast 从新生成代码  // 咱们最初生成的代码须要和render函数一样  // 相似_c('div',{id:"app"},_c('div',undefined,_v("hello"+_s(name)),_c('span',undefined,_v("world"))))  // _c代表创立元素 _v代表创立文本 _s代表文Json.stringify--把对象解析成文本  let code = generate(ast);  //   应用with语法扭转作用域为this  之后调用render函数能够应用call扭转this 不便code外面的变量取值  let renderFn = new Function(`with(this){return ${code}}`);  return renderFn;}

说一下Vue的生命周期

Vue 实例有⼀个残缺的⽣命周期,也就是从开始创立、初始化数据、编译模版、挂载Dom -> 渲染、更新 -> 渲染、卸载 等⼀系列过程,称这是Vue的⽣命周期。

  1. beforeCreate(创立前):数据观测和初始化事件还未开始,此时 data 的响应式追踪、event/watcher 都还没有被设置,也就是说不能拜访到data、computed、watch、methods上的办法和数据。
  2. created(创立后) :实例创立实现,实例上配置的 options 包含 data、computed、watch、methods 等都配置实现,然而此时渲染得节点还未挂载到 DOM,所以不能拜访到 $el 属性。
  3. beforeMount(挂载前):在挂载开始之前被调用,相干的render函数首次被调用。实例已实现以下的配置:编译模板,把data外面的数据和模板生成html。此时还没有挂载html到页面上。
  4. mounted(挂载后):在el被新创建的 vm.$el 替换,并挂载到实例下来之后调用。实例已实现以下的配置:用下面编译好的html内容替换el属性指向的DOM对象。实现模板中的html渲染到html 页面中。此过程中进行ajax交互。
  5. beforeUpdate(更新前):响应式数据更新时调用,此时尽管响应式数据更新了,然而对应的实在 DOM 还没有被渲染。
  6. updated(更新后) :在因为数据更改导致的虚构DOM从新渲染和打补丁之后调用。此时 DOM 曾经依据响应式数据的变动更新了。调用时,组件 DOM曾经更新,所以能够执行依赖于DOM的操作。然而在大多数状况下,应该防止在此期间更改状态,因为这可能会导致更新有限循环。该钩子在服务器端渲染期间不被调用。
  7. beforeDestroy(销毁前):实例销毁之前调用。这一步,实例依然齐全可用,this 仍能获取到实例。
  8. destroyed(销毁后):实例销毁后调用,调用后,Vue 实例批示的所有货色都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务端渲染期间不被调用。

另外还有 keep-alive 独有的生命周期,别离为 activateddeactivated 。用 keep-alive 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 deactivated 钩子函数,命中缓存渲染后会执行 activated 钩子函数。

Vue模版编译原理晓得吗,能简略说一下吗?

简略说,Vue的编译过程就是将template转化为render函数的过程。会经验以下阶段:

  • 生成AST树
  • 优化
  • codegen

首先解析模版,生成AST语法树(一种用JavaScript对象的模式来形容整个模板)。 应用大量的正则表达式对模板进行解析,遇到标签、文本的时候都会执行对应的钩子进行相干解决。

Vue的数据是响应式的,但其实模板中并不是所有的数据都是响应式的。有一些数据首次渲染后就不会再变动,对应的DOM也不会变动。那么优化过程就是深度遍历AST树,依照相干条件对树节点进行标记。这些被标记的节点(动态节点)咱们就能够跳过对它们的比对,对运行时的模板起到很大的优化作用。

编译的最初一步是将优化后的AST树转换为可执行的代码

computed 的实现原理

computed 实质是一个惰性求值的观察者。

computed 外部实现了一个惰性的 watcher,也就是 computed watcher,computed watcher 不会立即求值,同时持有一个 dep 实例。

其外部通过 this.dirty 属性标记计算属性是否须要从新求值。

当 computed 的依赖状态产生扭转时,就会告诉这个惰性的 watcher,

computed watcher 通过 this.dep.subs.length 判断有没有订阅者,

有的话,会从新计算,而后比照新旧值,如果变动了,会从新渲染。 (Vue 想确保不仅仅是计算属性依赖的值发生变化,而是当计算属性最终计算的值发生变化时才会触发渲染 watcher 从新渲染,实质上是一种优化。)

没有的话,仅仅把 this.dirty = true。 (当计算属性依赖于其余数据时,属性并不会立刻从新计算,只有之后其余中央须要读取属性的时候,它才会真正计算,即具备 lazy(懒计算)个性。)

Vue 子组件和父组件执行程序

加载渲染过程:

  1. 父组件 beforeCreate
  2. 父组件 created
  3. 父组件 beforeMount
  4. 子组件 beforeCreate
  5. 子组件 created
  6. 子组件 beforeMount
  7. 子组件 mounted
  8. 父组件 mounted

更新过程:

  1. 父组件 beforeUpdate
  2. 子组件 beforeUpdate
  3. 子组件 updated
  4. 父组件 updated

销毁过程:

  1. 父组件 beforeDestroy
  2. 子组件 beforeDestroy
  3. 子组件 destroyed
  4. 父组件 destoryed

Vue中如何进行依赖收集?

  • 每个属性都有本人的dep属性,寄存他所依赖的watcher,当属性变动之后会告诉本人对应的watcher去更新
  • 默认会在初始化时调用render函数,此时会触发属性依赖收集 dep.depend
  • 当属性产生批改时会触发watcher更新dep.notify()

依赖收集简版

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从新执行// 观察者模式// 渲染组件时 会创立watcherclass 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、计算属性是watchernew 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()            }        }    })}// 模仿数据获取,触发getterobj.name = 'poetries'// 一个属性一个dep,一个属性能够对应多个watcher(一个属性能够在任何组件中应用、在多个组件中应用)// 一个dep 对应多个watcher // 一个watcher 对应多个dep (一个视图对应多个属性)// dep 和 watcher是多对多的关系

Vue中如何检测数组变动

前言

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

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

Vue 提供了以下操作方法

// Vue.setVue.set(vm.items, indexOfItem, newValue)// vm.$set,Vue.set的一个别名vm.$set(vm.items, indexOfItem, newValue)// Array.prototype.splicevm.items.splice(indexOfItem, 1, newValue)

剖析

数组思考性能起因没有用 defineProperty 对数组的每一项进行拦挡,而是抉择对 7 种数组(push,shift,pop,splice,unshift,sort,reverse)办法进行重写(AOP 切片思维)

所以在 Vue 中批改数组的索引和长度是无奈监控到的。须要通过以上 7 种变异办法批改数组才会触发数组对应的 watcher 进行更新

  • 用函数劫持的形式,重写了数组办法,具体呢就是更改了数组的原型,更改成本人的,用户调数组的一些办法的时候,走的就是本人的办法,而后告诉视图去更新
  • 数组里每一项可能是对象,那么我就是会对数组的每一项进行观测,(且只有数组里的对象能力进行观测,观测过的也不会进行观测)

原理

Vuedata 中的数组,进行了原型链重写。指向了本人定义的数组原型办法,这样当调用数组api 时,能够告诉依赖更新,如果数组中蕴含着援用类型。会对数组中的援用类型再次进行监控。

手写简版剖析

let oldArray = Object.create(Array.prototype);['shift', 'unshift', 'push', 'pop', 'reverse','sort'].forEach(method => {    oldArray[method] = function() { // 这里能够触发页面更新逻辑        console.log('method', method)        Array.prototype[method].call(this,...arguments);    }});let arr = [1,2,3];arr.__proto__ = oldArray;arr.unshift(4);

源码剖析

// 拿到数组原型拷贝一份const arrayProto = Array.prototype // 而后将arrayMethods继承自数组原型// 这里是面向切片编程思维(AOP)--不毁坏封装的前提下,动静的扩大性能export const arrayMethods = Object.create(arrayProto) const methodsToPatch = [ 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse' ]methodsToPatch.forEach(function (method) { // 重写原型办法     const original = arrayProto[method] // 调用原数组的办法     def(arrayMethods, method, function mutator (...args) {         // 这里保留原型办法的执行后果        const result = original.apply(this, args)         // 这句话是要害        // this代表的就是数据自身 比方数据是{a:[1,2,3]} 那么咱们应用a.push(4)  this就是a  ob就是a.__ob__ 这个属性就是上段代码减少的 代表的是该数据曾经被响应式察看过了指向Observer实例        const ob = this.__ob__         // 这里的标记就是代表数组有新增操作        let inserted        switch (method) {             case 'push':             case 'unshift':                 inserted = args                 break             case 'splice':                 inserted = args.slice(2)                 break         }        // 如果有新增的元素 inserted是一个数组 调用Observer实例的observeArray对数组每一项进行观测        if (inserted) ob.observeArray(inserted)         ob.dep.notify() // 当调用数组办法后,手动告诉视图更新         return result     }) })this.observeArray(value) // 进行深度监控
vue3:改用 proxy ,可间接监听对象数组的变动