params和query的区别

用法:query要用path来引入,params要用name来引入,接管参数都是相似的,别离是 this.$route.query.namethis.$route.params.name

url地址显示:query更加相似于ajax中get传参,params则相似于post,说的再简略一点,前者在浏览器地址栏中显示参数,后者则不显示

留神:query刷新不会失落query外面的数据 params刷新会失落 params外面的数据。

为什么Vue采纳异步渲染呢?

Vue 是组件级更新,如果不采纳异步更新,那么每次更新数据都会对以后组件进行从新渲染,所以为了性能, Vue 会在本轮数据更新后,在异步更新视图。核心思想 nextTick

dep.notify() 告诉 watcher进行更新, subs[i].update 顺次调用 watcher 的 update queueWatcher 将watcher 去重放入队列, nextTick( flushSchedulerQueue )在下一tick中刷新watcher队列(异步)。

Vue的生命周期办法有哪些

  1. Vue 实例有一个残缺的生命周期,也就是从开始创立初始化数据编译模版挂载Dom -> 渲染更新 -> 渲染卸载等一系列过程,咱们称这是Vue的生命周期
  2. Vue生命周期总共分为8个阶段创立前/后载入前/后更新前/后销毁前/后
beforeCreate => created => beforeMount => Mounted => beforeUpdate => updated => beforeDestroy => destroyedkeep-alive下:activated deactivated
生命周期vue2生命周期vue3形容
beforeCreatebeforeCreate在实例初始化之后,数据观测(data observer) 之前被调用。
createdcreated实例曾经创立实现之后被调用。在这一步,实例已实现以下的配置:数据观测(data observer),属性和办法的运算, watch/event 事件回调。这里没有$el
beforeMountbeforeMount在挂载开始之前被调用:相干的 render 函数首次被调用
mountedmountedel 被新创建的 vm.$el 替换,并挂载到实例下来之后调用该钩子
beforeUpdatebeforeUpdate组件数据更新之前调用,产生在虚构 DOM 打补丁之前
updatedupdated因为数据更改导致的虚构 DOM 从新渲染和打补丁,在这之后会调用该钩子
beforeDestroybeforeUnmount实例销毁之前调用。在这一步,实例依然齐全可用
destroyedunmounted实例销毁后调用。调用后, Vue 实例批示的所有货色都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。 该钩子在服务器端渲染期间不被调用。

其余几个生命周期

生命周期vue2生命周期vue3形容
activatedactivatedkeep-alive专属,组件被激活时调用
deactivateddeactivatedkeep-alive专属,组件被销毁时调用
errorCapturederrorCaptured捕捉一个来自子孙组件的谬误时被调用
-renderTracked调试钩子,响应式依赖被收集时调用
-renderTriggered调试钩子,响应式依赖被触发时调用
-serverPrefetchssr 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() 外部调用生命周期钩子:

选项式 APIHook inside setup
beforeCreate不须要*
created不须要*
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeUnmountonBeforeUnmount
unmountedonUnmounted
errorCapturedonErrorCaptured
renderTrackedonRenderTracked
renderTriggeredonRenderTriggered
因为 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 中 computed 和 watch 有什么区别? **计算属性 computed**:     (1)**反对缓存**,只有依赖数据发生变化时,才会从新进行计算函数;     (2)计算属性内**不反对异步操作**;     (3)计算属性的函数中**都有一个 get**(默认具备,获取计算属性)**和 set**(手动增加,设置计算属性)办法;     (4)计算属性是主动监听依赖值的变动,从而动静返回内容。  **侦听属性 watch**:     (1)**不反对缓存**,只有数据发生变化,就会执行侦听函数;     (2)侦听属性内**反对异步操作**;     (3)侦听属性的值**能够是一个对象,接管 handler 回调,deep,immediate 三个属性**;     (3)监听是一个过程,在监听的值变动时,能够触发一个回调,并**做一些其余事件**。参考:[前端vue面试题具体解答](https://kc7474.com/archives/1333?url=vue)### Vue template 到 render 的过程vue的模版编译过程次要如下:**template -> ast -> render函数**vue 在模版编译版本的码中会执行 compileToFunctions 将template转化为render函数:

// 将模板编译为render函数const { render, staticRenderFns } = compileToFunctions(template,options//省略}, this)

CompileToFunctions中的次要逻辑如下∶ **(1)调用parse办法将template转化为ast(形象语法树)**

constast = parse(template.trim(), options)

- **parse的指标**:把tamplate转换为AST树,它是一种用 JavaScript对象的模式来形容整个模板。- **解析过程**:利用正则表达式程序解析模板,当解析到开始标签、闭合标签、文本的时候都会别离执行对应的 回调函数,来达到结构AST树的目标。AST元素节点总共三种类型:type为1示意一般元素、2为表达式、3为纯文本**(2)对动态节点做优化**

optimize(ast,options)

这个过程次要剖析出哪些是动态节点,给其打一个标记,为后续更新渲染能够间接跳过动态节点做优化深度遍历AST,查看每个子树的节点元素是否为动态节点或者动态节点根。如果为动态节点,他们生成的DOM永远不会扭转,这对运行时模板更新起到了极大的优化作用。**(3)生成代码**

const code = generate(ast, options)

generate将ast形象语法树编译成 render字符串并将动态局部放到 staticRenderFns 中,最初通过 `new Function(`` render``)` 生成render函数。### action 与 mutation 的区别- ` mutation`  是同步更新,` $watch` 严格模式下会报错- ` action`  是异步操作,能够获取数据后调用` mutation` 提交最终数据### assets和static的区别**相同点:** `assets` 和 `static` 两个都是寄存动态资源文件。我的项目中所须要的资源文件图片,字体图标,款式文件等都能够放在这两个文件下,这是相同点**不相同点:**`assets` 中寄存的动态资源文件在我的项目打包时,也就是运行 `npm run build` 时会将 `assets` 中搁置的动态资源文件进行打包上传,所谓打包简略点能够了解为压缩体积,代码格式化。而压缩后的动态资源文件最终也都会搁置在 `static` 文件中跟着 `index.html` 一起上传至服务器。`static` 中搁置的动态资源文件就不会要走打包压缩格式化等流程,而是间接进入打包好的目录,间接上传至服务器。因为防止了压缩间接进行上传,在打包时会进步肯定的效率,然而 `static` 中的资源文件因为没有进行压缩等操作,所以文件的体积也就绝对于 `assets` 中打包后的文件提交较大点。在服务器中就会占据更大的空间。**倡议:** 将我的项目中 `template`须要的款式文件js文件等都能够搁置在 `assets` 中,走打包这一流程。缩小体积。而我的项目中引入的第三方的资源文件如`iconfoont.css` 等文件能够搁置在 `static` 中,因为这些引入的第三方文件曾经通过解决,不再须要解决,间接上传。### 双向数据绑定的原理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变更的双向绑定成果。### Computed 和 Methods 的区别能够将同一函数定义为一个 method 或者一个计算属性。对于最终的后果,两种形式是雷同的**不同点:**- computed: 计算属性是基于它们的依赖进行缓存的,只有在它的相干依赖产生扭转时才会从新求值;- method 调用总会执行该函数。### 什么是 mixin ?- Mixin 使咱们可能为 Vue 组件编写可插拔和可重用的性能。- 如果心愿在多个组件之间重用一组组件选项,例如生命周期 hook、 办法等,则能够将其编写为 mixin,并在组件中简略的援用它。- 而后将 mixin 的内容合并到组件中。如果你要在 mixin 中定义生命周期 hook,那么它在执行时将优化于组件自已的 hook。## 了解Vue运行机制全局概览#### 全局概览首先咱们来看一下笔者画的外部流程图。![](https://img-blog.csdnimg.cn/img_convert/00c11ba8ff00da643b2b2c2ae04048c0.png)大家第一次看到这个图肯定是一头雾水的,没有关系,咱们来一一讲一下这些模块的作用以及调用关系。置信讲完之后大家对`Vue.js`外部运行机制会有一个大略的意识。#### 初始化及挂载![](https://img-blog.csdnimg.cn/img_convert/38254a061430cd441351f83339654679.png)> 在 `new Vue()` 之后。 Vue 会调用 `_init` 函数进行初始化,也就是这里的 `init` 过程,它会初始化生命周期、事件、 props、 methods、 data、 computed 与 watch 等。其中最重要的是通过 `Object.defineProperty` 设置 `setter` 与 `getter` 函数,用来实现「 **响应式** 」以及「 **依赖收集** 」,前面会具体讲到,这里只有有一个印象即可。> 初始化之后调用 `$mount` 会挂载组件,如果是运行时编译,即不存在 render function 然而存在 template 的状况,须要进行「 **编译** 」步骤。#### 编译compile编译能够分成 `parse`、`optimize` 与 `generate` 三个阶段,最终须要失去 render function。![](https://img-blog.csdnimg.cn/img_convert/2801d1d3c31ac8dcf0e68a751fb0e970.png)**1. parse**`parse` 会用正则等形式解析 template 模板中的指令、class、style等数据,造成AST。**2. optimize**> `optimize` 的次要作用是标记 static 动态节点,这是 Vue 在编译过程中的一处优化,前面当 `update` 更新界面时,会有一个 `patch` 的过程, diff 算法会间接跳过动态节点,从而缩小了比拟的过程,优化了 `patch` 的性能。**3. generate**> `generate` 是将 AST 转化成 `render function`字符串的过程,失去后果是 `render` 的字符串以及 staticRenderFns 字符串。* 在经验过 `parse`、`optimize` 与 `generate` 这三个阶段当前,组件中就会存在渲染 `VNode` 所需的 `render function` 了。#### 响应式接下来也就是 Vue.js 响应式外围局部。![](https://img-blog.csdnimg.cn/img_convert/0815dfa005d37e7e6deffdbf1ee46a09.png)> 这里的 `getter` 跟 `setter` 曾经在之前介绍过了,在 `init` 的时候通过 `Object.defineProperty` 进行了绑定,它使得当被设置的对象被读取的时候会执行 `getter` 函数,而在当被赋值的时候会执行 `setter` 函数。* 当 `render function` 被渲染的时候,因为会读取所需对象的值,所以会触发 `getter` 函数进行「 **依赖收集** 」,「 **依赖收集** 」的目标是将观察者 `Watcher` 对象寄存到以后闭包中的订阅者 `Dep` 的 `subs` 中。造成如下所示的这样一个关系。![](https://img-blog.csdnimg.cn/img_convert/e237f6910a6a0198208852715051ab8f.png)> 在批改对象的值的时候,会触发对应的 `setter`, `setter` 告诉之前「 **依赖收集** 」失去的 Dep 中的每一个 Watcher,通知它们本人的值扭转了,须要从新渲染视图。这时候这些 Watcher 就会开始调用 `update` 来更新视图,当然这两头还有一个 `patch` 的过程以及应用队列来异步更新的策略,这个咱们前面再讲。#### Virtual DOM> 咱们晓得,`render function` 会被转化成 `VNode` 节点。`Virtual DOM` 其实就是一棵以 JavaScript 对象( VNode 节点)作为根底的树,用对象属性来形容节点,实际上它只是一层对实在 DOM 的形象。最终能够通过一系列操作使这棵树映射到实在环境上。因为 Virtual DOM 是以 JavaScript 对象为根底而不依赖实在平台环境,所以使它具备了跨平台的能力,比如说浏览器平台、Weex、Node 等。比如说上面这样一个例子:

{

tag: 'div',                 /*阐明这是一个div标签*/children: [                 /*寄存该标签的子节点*/    {        tag: 'a',           /*阐明这是一个a标签*/        text: 'click me'    /*标签的内容*/    }]

}

渲染后能够失去

<div>

<a>click me</a>

</div>

> 这只是一个简略的例子,实际上的节点有更多的属性来标记节点,比方 isStatic (代表是否为动态节点)、 isComment (代表是否为正文节点)等。#### 更新视图![](https://img-blog.csdnimg.cn/img_convert/9fe665ec341361c3d1f1b1c12be61922.png)* 后面咱们说到,在批改一个对象值的时候,会通过 `setter -> Watcher -> update` 的流程来批改对应的视图,那么最终是如何更新视图的呢?* 当数据变动后,执行 render function 就能够失去一个新的 VNode 节点,咱们如果想要失去新的视图,最简略粗犷的办法就是间接解析这个新的 `VNode` 节点,而后用 `innerHTML` 间接全副渲染到实在 `DOM` 中。然而其实咱们只对其中的一小块内容进行了批改,这样做仿佛有些「 **节约** 」。* 那么咱们为什么不能只批改那些「扭转了的中央」呢?这个时候就要介绍咱们的「 **`patch`** 」了。咱们会将新的 `VNode` 与旧的 `VNode` 一起传入 `patch` 进行比拟,通过 diff 算法得出它们的「 **差别** 」。最初咱们只须要将这些「 **差别** 」的对应 DOM 进行批改即可。#### 再看全局![](https://img-blog.csdnimg.cn/img_convert/9d6d5e9ca39bd34cb49e343db6bf4ffb.png)回过头再来看看这张图,是不是大脑中曾经有一个大略的脉络了呢?### keep-alive 中的生命周期哪些keep-alive是 Vue 提供的一个内置组件,用来对组件进行缓存——在组件切换过程中将状态保留在内存中,避免反复渲染DOM。如果为一个组件包裹了 keep-alive,那么它会多出两个生命周期:deactivated、activated。同时,beforeDestroy 和 destroyed 就不会再被触发了,因为组件不会被真正销毁。当组件被换掉时,会被缓存到内存中、触发 deactivated 生命周期;当组件被切回来时,再去缓存里找这个组件、触发 activated钩子函数。### 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)" >
})

### 子组件能够间接扭转父组件的数据吗?子组件不能够间接扭转父组件的数据。这样做次要是为了保护父子组件的单向数据流。每次父级组件产生更新时,子组件中所有的 prop 都将会刷新为最新的值。如果这样做了,Vue 会在浏览器的控制台中收回正告。Vue提倡单向数据流,即父级 props 的更新会流向子组件,然而反过来则不行。这是为了避免意外的扭转父组件状态,使得利用的数据流变得难以了解,导致数据流凌乱。如果毁坏了单向数据流,当利用简单时,debug 的老本会十分高。**只能通过 **`$emit`** 派发一个自定义事件,父组件接管到后,由父组件批改。**### 应用 Object.defineProperty() 来进行数据劫持有什么毛病?在对一些属性进行操作时,应用这种办法无奈拦挡,比方通过下标形式批改数组数据或者给对象新增属性,这都不能触发组件的从新渲染,因为 Object.defineProperty 不能拦挡到这些操作。更准确的来说,对于数组而言,大部分操作都是拦挡不到的,只是 Vue 外部通过重写函数的形式解决了这个问题。在 Vue3.0 中曾经不应用这种形式了,而是通过应用 Proxy 对对象进行代理,从而实现数据劫持。应用Proxy 的益处是它能够完满的监听到任何形式的数据扭转,惟一的毛病是兼容性的问题,因为 Proxy 是 ES6 的语法。## Vue2.x 响应式数据原理整体思路是数据劫持+观察者模式对象外部通过 `defineReactive` 办法,应用 `Object.defineProperty` 来劫持各个属性的 `setter`、`getter`(只会劫持曾经存在的属性),数组则是通过`重写数组7个办法`来实现。当页面应用对应属性时,每个属性都领有本人的 `dep` 属性,寄存他所依赖的 `watcher`(依赖收集),当属性变动后会告诉本人对应的 `watcher` 去更新(派发更新)**Object.defineProperty根本应用**

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) {

observer(value);Object.defineProperty(obj, key, {    get() { // 收集对应的key 在哪个办法(组件)中被应用        return value;    },    set(newValue) {        if (newValue !== value) {            observer(newValue);            value = newValue; // 让key对应的办法(组件从新渲染)从新执行        }    }})

}
let obj1 = { school: { name: 'poetry', age: 20 } };
observer(obj1);
console.log(obj1)

**源码剖析**![](https://img-blog.csdnimg.cn/img_convert/2d663b454ecd8ab745557e44ee28cd10.png)

class Observer {
// 观测值
constructor(value) {

this.walk(value);

}
walk(data) {

// 对象上的所有属性顺次进行观测let keys = Object.keys(data);for (let i = 0; i < keys.length; i++) {  let key = keys[i];  let value = data[key];  defineReactive(data, key, value);}

}
}
// Object.defineProperty数据劫持外围 兼容性在ie9以及以上
function defineReactive(data, key, value) {
observe(value); // 递归要害
// --如果value还是一个对象会持续走一遍odefineReactive 层层遍历始终到value不是对象才进行
// 思考?如果Vue数据嵌套层级过深 >>性能会受影响
Object.defineProperty(data, key, {

get() {  console.log("获取值");  //须要做依赖收集过程 这里代码没写进去  return value;},set(newValue) {  if (newValue === value) return;  console.log("设置值");  //须要做派发更新过程 这里代码没写进去  value = newValue;},

});
}
export function observe(value) {
// 如果传过来的是对象或者数组 进行属性劫持
if (

Object.prototype.toString.call(value) === "[object Object]" ||Array.isArray(value)

) {

return new Observer(value);

}
}

说一说你对vue响应式了解答复范例* 所谓数据响应式就是**可能使数据变动能够被检测并对这种变动做出响应的机制*** `MVVM`框架中要解决的一个外围问题是连贯数据层和视图层,通过**数据驱动**利用,数据变动,视图更新,要做到这点的就须要对数据做响应式解决,这样一旦数据发生变化就能够立刻做出更新解决* 以`vue`为例阐明,通过数据响应式加上虚构`DOM`和`patch`算法,开发人员只须要操作数据,关怀业务,齐全不必接触繁琐的DOM操作,从而大大晋升开发效率,升高开发难度* `vue2`中的数据响应式会依据数据类型来做不同解决,如果是 **对象则采纳`Object.defineProperty()`的形式定义数据拦挡,当数据被拜访或发生变化时,咱们感知并作出响应;如果是数组则通过笼罩数组对象原型的7个变更办法** ,使这些办法能够额定的做更新告诉,从而作出响应。这种机制很好的解决了数据响应化的问题,但在理论应用中也存在一些毛病:比方初始化时的递归遍历会造成性能损失;新增或删除属性时须要用户应用`Vue.set/delete`这样非凡的`api`能力失效;对于`es6`中新产生的`Map`、`Set`这些数据结构不反对等问题* 为了解决这些问题,`vue3`从新编写了这一部分的实现:利用`ES6`的`Proxy`代理要响应化的数据,它有很多益处,编程体验是统一的,不须要应用非凡`api`,初始化性能和内存耗费都失去了大幅改善;另外因为响应化的实现代码抽取为独立的`reactivity`包,使得咱们能够更灵便的应用它,第三方的扩大开发起来更加灵便了### Vue 的父子组件生命周期钩子函数执行程序* **渲染程序** :先父后子,实现程序:先子后父* **更新程序** :父更新导致子更新,子更新实现后父* **销毁程序** :先父后子,实现程序:先子后父**加载渲染过程**父 `beforeCreate`->父 `created`->父 `beforeMount`->子 `beforeCreate`->子 `created`->子 `beforeMount`->子 `mounted`->父 `mounted`。**子组件先挂载,而后到父组件****子组件更新过程**父 `beforeUpdate`->子 `beforeUpdate`->子 `updated`->父 `updated`**父组件更新过程**父 `beforeUpdate`->父 `updated`**销毁过程**父 `beforeDestroy`->子 `beforeDestroy`->子 `destroyed`->父 `destroyed`> 之所以会这样是因为`Vue`创立过程是一个递归过程,先创立父组件,有子组件就会创立子组件,因而创立时先有父组件再有子组件;子组件首次创立时会增加`mounted`钩子到队列,等到`patch`完结再执行它们,可见子组件的`mounted`钩子是先进入到队列中的,因而等到`patch`完结执行这些钩子时也先执行。![](https://img-blog.csdnimg.cn/img_convert/553b51ad2a8a00396f1834281603e16f.png)

function patch (oldVnode, vnode, hydrating, removeOnly) {

if (isUndef(vnode)) {   if (isDef(oldVnode)) invokeDestroyHook(oldVnode) return }let isInitialPatch = false const insertedVnodeQueue = [] // 定义收集所有组件的insert hook办法的数组 // somthing ... createElm(     vnode,     insertedVnodeQueue, oldElm._leaveCb ? null : parentElm,     nodeOps.nextSibling(oldElm) )// somthing... // 最终会顺次调用收集的insert hook invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch);return vnode.elm

}

function createElm ( vnode, insertedVnodeQueue, parentElm, refElm, nested, ownerArray, index ) {

// createChildren 会递归创立儿子组件 createChildren(vnode, children, insertedVnodeQueue) // something... 

}
// 将组件的vnode插入到数组中
function invokeCreateHooks (vnode, insertedVnodeQueue) {

for (let i = 0; i < cbs.create.length; ++i) {     cbs.create[i](emptyNode, vnode) }i = vnode.data.hook // Reuse variable if (isDef(i)) {     if (isDef(i.create)) i.create(emptyNode, vnode)     if (isDef(i.insert)) insertedVnodeQueue.push(vnode) } 

}
// insert办法中会顺次调用mounted办法
insert (vnode: MountedComponentVNode) {

const { context, componentInstance } = vnode if (!componentInstance._isMounted) {     componentInstance._isMounted = true     callHook(componentInstance, 'mounted') } 

}
function invokeInsertHook (vnode, queue, initial) {

// delay insert hooks for component root nodes, invoke them after the // element is really inserted if (isTrue(initial) && isDef(vnode.parent)) {     vnode.parent.data.pendingInsert = queue } else {     for (let i = 0; i < queue.length; ++i) {         queue[i].data.hook.insert(queue[i]); // 调用insert办法     } } 

}

Vue.prototype.$destroy = function () {

callHook(vm, 'beforeDestroy') // invoke destroy hooks on current rendered tree vm.__patch__(vm._vnode, null) // 先销毁儿子 // fire destroyed hook callHook(vm, 'destroyed') 

}

### Vue中封装的数组办法有哪些,其如何实现页面更新在Vue中,对响应式解决利用的是Object.defineProperty对数据进行拦挡,而这个办法并不能监听到数组外部变动,数组长度变动,数组的截取变动等,所以须要对这些操作进行hack,让Vue能监听到其中的变动。  那Vue是如何实现让这些数组办法实现元素的实时更新的呢,上面是Vue中对这些办法的封装:

// 缓存数组原型
const arrayProto = Array.prototype;
// 实现 arrayMethods.__proto__ === Array.prototype
export const arrayMethods = Object.create(arrayProto);
// 须要进行性能拓展的办法
const methodsToPatch = [
"push",
"pop",
"shift",
"unshift",
"splice",
"sort",
"reverse"
];

/* Intercept mutating methods and emit events */
methodsToPatch.forEach(function(method) {
// 缓存原生数组办法
const original = arrayProto[method];
def(arrayMethods, method, function mutator(...args) {

// 执行并缓存原生数组性能const result = original.apply(this, args);// 响应式解决const ob = this.__ob__;let inserted;switch (method) {// push、unshift会新增索引,所以要手动observer  case "push":  case "unshift":    inserted = args;    break;  // splice办法,如果传入了第三个参数,也会有索引退出,也要手动observer。  case "splice":    inserted = args.slice(2);    break;}// if (inserted) ob.observeArray(inserted);// 获取插入的值,并设置响应式监听// notify changeob.dep.notify();// 告诉依赖更新// 返回原生数组办法的执行后果return result;

});
});

简略来说就是,重写了数组中的那些原生办法,首先获取到这个数组的__ob__,也就是它的Observer对象,如果有新的值,就调用observeArray持续对新的值察看变动(也就是通过`target__proto__ == arrayMethods`来扭转了数组实例的型),而后手动调用notify,告诉渲染watcher,执行update。### v-model 是如何实现的,语法糖理论是什么?**(1)作用在表单元素上** 动静绑定了 input 的 value 指向了 messgae 变量,并且在触发 input 事件的时候去动静把 message设置为目标值:

<input v-model="sth" />
// 等同于
<input v-bind:value="message" v-on:input="message=$event.target.value"

//$event 指代以后触发的事件对象;//$event.target 指代以后触发的事件对象的dom;//$event.target.value 就是以后dom的value值;//在@input办法中,value => sth;//在:value中,sth => value;
**(2)作用在组件上** 在自定义组件中,v-model 默认会利用名为 value 的 prop和名为 input 的事件**实质是一个父子组件通信的语法糖,通过prop和$.emit实现。** 因而父组件 v-model 语法糖实质上能够批改为:

<child :value="message" @input="function(e){message = e}"></child>

在组件的实现中,能够通过 v-model属性来配置子组件接管的prop名称,以及派发的事件名称。例子:

// 父组件
<aa-input v-model="aa"></aa-input>
// 等价于
<aa-input v-bind:value="aa" v-on:input="aa=$event.target.value"></aa-input>

// 子组件:
<input v-bind:value="aa" v-on:input="onmessage"></aa-input>

props:{value:aa,}
methods:{

onmessage(e){    $emit('input',e.target.value)}

}

默认状况下,一个组件上的v-model 会把 value 用作 prop且把 input 用作 event。然而一些输出类型比方单选框和复选框按钮可能想应用 value prop 来达到不同的目标。应用 model 选项能够回避这些状况产生的抵触。js 监听input 输入框输出数据扭转,用oninput,数据扭转当前就会立即登程这个事件。通过input事件把数据$emit 进来,在父组件承受。父组件设置v-model的值为input `$emit`过去的值。### delete和Vue.delete删除数组的区别- `delete` 只是被删除的元素变成了 `empty/undefined` 其余的元素的键值还是不变。- `Vue.delete` 间接删除了数组 扭转了数组的键值。