# 谈谈 vue3 的 ref 和 reactive

# vue 的组件传值如何实现

# vue 的响应式原理

# event bus 如何实现

# vue 声明周期做的事情

# beforeCreate

实例、组件通过 new Vue() 在内存中创建出来之后会初始化事件和生命周期,然后就会执行 beforeCreate 钩子函数,这个时候,数据还没有挂载,只是一个空壳,无法访问到数据和真实的 dom;只包含一些自带的生命周期函数;

# created

进行挂载数据以及绑定事件等;比如处理 inject 、props 、methods 、data 、computed 和 watch 的初始化处理;

这个时候可以获取到数据,也可以更改数据,在这里更改数据不会触发updated 函数,不会触发其他的钩子函数,一般可以在这里做初始数据的获取;

# beforeMount

这个阶段将编译模板为虚拟 dom 放入到 render (渲染)函数中准备渲染,然后执行 beforeMount 钩子函数;

在这个函数中虚拟 dom 已经创建完成,马上就要渲染,在这里也可以更改数据,不会触发 updated,在这里可以在渲染前最后一次更改数据的机会,不会触发其他的钩子函数,一般可以在这里做初始数据的获取;

# mounted

接下来开始 render,渲染出真实 dom,然后执行 mounted (安装好的)钩子函数;此时,组件已经出现在页面中,数据、真实 dom 都已经处理好了,事件都已经挂载好了,可以在这里操作真实 dom 等事情;

# beforeUpdate

当组件或实例的数据更改之后,会立即执行 beforeUpdate,(更新之前)然后 vue 的虚拟 dom 机制会重新构建虚拟 dom与上一次的虚拟 dom 树利用 diff 算法进行对比之后重新渲染,一般不做什么事儿;需要注意的是界面中的数据还是旧的,但是 data 数据已经更新,页面中和 data 还没有同步;

# updated

当更新完成后,执行 updated,数据已经更改完成,dom 也重新 render 完成,可以操作更新后的虚拟 dom;此时页面中的数据和 data 保持一致;

# beforeDestroy

执行该方法的时候,Vue 的生命周期已经进入销毁阶段,但是实例上的各种数据还出于可用状态;一般在这里做一些善后工作,例如清除计时器、清除非指令绑定的事件、websocket 服务关闭等等;

# destroyed

组件已经全部销毁,Vue 实例已经被销毁,Vue 中的任何数据都不可用只剩下 dom 空壳,这个时候,执行 destroyed,在这里做善后工作也可以;

# provide/inject 实现响应式数据更新

官网 api 说到:

提示:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。

如果我们传入简单的属性并不是可响应的;实现响应式数据更新很简单,就是在 provide 中返回一个函数,这个函数去引用需要监听更新的数据;实际上这个函数存储了父组件实例的引用,所以每次子组件都能获取到最新的数据。如下代码:

父组件:

<template>
    <div class="parent-container">
      Parent组件
      <br/>
      <button type="button" @click="changeName">改变name</button>
      <br/>
      Parent 组件中 name的值: {{name}}
      <Child  v-bind="{name: 'k3vvvv'}" />
    </div>
</template>
<script>
import Child from './Child'
export default {
  name: 'Parent',
  components: {
    Child
  },
  data () {
    return {
      name: 'Kevin'
    }
  },
  methods: {
    changeName (val) {
      this.name = 'parent - 1'
    }
  },
  provide () {
    return {
      getReaciveNameFromParent: () => this.name
    }
  }
}
</script>

子组件中:

<template>
  <div class="child-container">
    Child组件
    <br/>
    <GrandSon />
  </div>
</template>
<script>
import GrandSon from './GrandSon'
export default {
  components: {
    GrandSon
  }
}
</script>

GrandSon 组件:

<template>
  <div class="grandson-container">
    Grandson组件
    <br/>
    {{reactiveNameFromParent}}
  </div>
</template>
<script>
export default {
  inject: ['getReaciveNameFromParent'],
  computed: {
    reactiveNameFromParent () {
      return this.getReaciveNameFromParent()
    }
  },
  watch: {
    'reactiveNameFromParent': function (val) {
      console.log('来自Parent组件的name值发生了变化', val)
    }
  }
}
</script>

# vue 如何实现 v-model

首先 v-model 的本质就是语法糖;如下:

<input type="text" v-model="name">

相当于:

<input :value="name" @input="name = $event.target.vaule" >

如下例子:

<template>
  <div>
    <custom-text-input 
      v-model='value' 
    />
    <p> Value: {{ value }} </p>
  </div>
</template>

<script>
import CustomTextInput from './CustomTextInput.vue'

export default {
  components: {
    CustomTextInput,
  },
  data() {
    return {
      value: 'Matt',
    }
  }
}
</script>

然后,我们内部的子组件:

<template>
  <input type="text" :value="value" @input="$emit('input', $event.target.value)">
</template>
<script>
export default {
  name: 'vmodel',
  props: {
    value: String
  }
}
</script>
<style scoped type="less">
</style>

参考:https://segmentfault.com/a/1190000039219503

# 什么是虚拟 DOM

首先需要知道为何使用虚拟 DOM ,因为我们直接通过 JS 进行 DOM 操作是非常 “昂贵” 的,很消耗性能,浏览器会从构建 DOM 树开始从头到尾执行一遍流程;频繁操作还是会出现页面卡顿,影响用户体验。

虚拟 DOM 就是为了解决浏览器性能问题而被设计出来的。比如一次操作中有 10 次更新 DOM 的动作,虚拟 DOM 不会立即操作 DOM,而是将这 10 次更新的 diff 内容保存到本地一个 JS 对象中,最终将这个 JS 对象一次性 attch 到 DOM 树上,再进行后续操作,避免大量无谓的计算量。所以,用 JS 对象模拟 DOM 节点的好处是,页面的更新可以先全部反映在 JS 对象(虚拟 DOM )上,操作内存中的 JS 对象的速度显然要更快,等更新完成后,再将最终的 JS 对象映射成真实的 DOM,交由浏览器去绘制。

# vuex 实现原理以及使用

# vuex 如何实现持久性存储

# v-if、v-show 实现原理;

# vue-router 的实现原理;各个模式

# nexttrick 如何实现

# vue 整个数据更新流程;

# object.defineproperty 注意。对数组有什么影响,vue2 为何不使用;proxy API;

# 更新策略。以及虚拟 dom 等

# 与 react 相比,数据流向以及更新策略有何不同

# Vue 中父组件能否监听子组件的生命周期

方法一:

// Parent.vue
<Child @mounted="doSomething"/>
    
// Child.vue
mounted() {
  this.$emit("mounted");
}

方法二:

使用 @hook

//  Parent.vue
<Child @hook:mounted="doSomething" ></Child>

doSomething() {
  console.log('父组件监听到 mounted 钩子函数 ...')
}
    
//  Child.vue
mounted(){
  console.log('子组件触发 mounted 钩子函数 ...')
} 
    
// 以上输出顺序为:
// 子组件触发 mounted 钩子函数 ...
// 父组件监听到 mounted 钩子函数 ...     

# Vue 父组件与子组件声明周期的执行顺序

Vue 的父组件和子组件生命周期钩子函数执行顺序可以归类为以下 4 部分:

  • 加载渲染过程

父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted

  • 子组件更新过程

父 beforeUpdate -> 子 beforeUpdate -> 子 update -> 父 updated

  • 父组件更新过程

父 beforeUpdate -> 父 updated

  • 销毁过程

父 beforeDestory -> 子 beforeDestory -> 子 destoryed -> 父 destoryed

更新: 7/11/2021, 5:37:23 PM