vue2杂记-关于vue2组件的更新和生命周期函数

213 阅读1分钟

生命周期函数顺序测试

created, mounted

  1. 父组件created
  2. 子组件created
  3. 子组件mounted
  4. 父组件mounted

updated

无论是通过v-model在子组件更新父组件的数据,或直接在父组件修改传递给了子组件用的值,update的执行顺序都是

  1. 子组件updated
  2. 父组件updated

重现代码

App.vue 这是父组件

<script>
import ShowComp01 from 'components/ShowComp01.vue'
export default {
  name: 'App',
  components: { ShowComp01 },
  data() {
    return {
      akName: '张三',
      age: '12',
    }
  },
  methods: {
    onChangeAge() {
      this.age = '33'
    },
  },
  created() {
    console.log('>>>>>>>>>>>>>>>>App created')
    console.log(this)
  },
  mounted() {
    console.log('************App mounted')
  },
  updated() {
    console.log('App更新')
  },
}
</script>

<template>
  <div>
    <div
      style="
        border-bottom: 1px solid #333;
        margin-bottom: 10px;
        padding-bottom: 10px;
      "
    >
      akName:{{ akName }}

      <input type="text" v-model="age" />
      <button @click="onChangeAge">修改age</button>
    </div>
    <ShowComp01 v-model="age"></ShowComp01>
  </div>
</template>

<style lang="scss" scoped></style>

ShowComp01.vue 这是子组件

<template>
  <div>
    ShowComp01组件
    <br />
    age:{{ age }}
    <input type="text" :value="age" @change="onChange" />
  </div>
</template>

<script>
export default {
  name: 'ShowComp01',
  model: {
    prop: 'age',
    event: 'change',
  },
  props: {
    age: {
      type: String,
    },
  },
  methods: {
    onChange(val) {
      this.$emit('change', val.target.value)
    },
  },
  created() {
    console.log('>>>>>>>>>>>>>>>>ShowComp created')
  },
  mounted() {
    console.log('************ShowComp mounted')
  },
  updated() {
    console.log('ShowComp更新')
  },
}
</script>

<style scoped></style>

依赖defineProperty和观察者模式, vue是以组件为单位的精确更新

  • 组件渲染顺序是从上至下依次渲染的
  • 修改了仅在父组件中用的数据,子组件和兄弟组件是不会更新的
  • 无论是在父组件修改了在子组件用到的数据,还是子组件通过v-model方式修改了父组件顺序,updated的执行顺序都是先子组件updated,再父组件updated
  • 即时父组件将某个属性传递给了子组件,只要子组件的界面未使用该属性,那么父组件更新该值时,子组件都时不会重新渲染的
  • 只要界面上用到的数据发生了改变,那么就会触发组件的rerender,否则不会(注意:是界面上用到的即template块中用到的

随便更新一个input的值,加入断点,会发现是input所在组件发起的update操作

image.png

image.png

这里的prevVnodevnode,表示的是DemoOne这个组件的虚拟DOM, prevVnode表示的是旧的虚拟DOM, 表示的是当前的虚拟DOM

__patch__方法内部会通过diff算法对比两个虚拟DOM的不同,然后做精确更新

看到这里的,你可能会有疑问,既然VUE每次都是精确更新,那为什么我项目里有些界面还是有卡顿?你是吹嘘了一个假的精确更新吧!

不要慌,请继续往下看,注意VUE是精确更新没错,但他是通过diff算法,筛选出了,仅哪些虚拟DOM对应的真实DOM,需要进行更新(注意:这里的更新不仅是指更新还包括删除dom和添加dom),diff算法也是要消耗时间的,越是复杂的界面,diff算法执行的时间越长

这里的children就是子虚拟DOM

image.png

重现代码

根组件

<script>
import DemoOne from 'components/DemoOne.vue'
import BrotherComp from 'components/BrotherComp.vue'
export default {
  name: 'App',
  components: { DemoOne, BrotherComp },
  data() {
    return {}
  },
  methods: {},
  created() {
    console.log('>>>>>>>>>>>>>>>>App created')
    // console.log(this)
  },
  mounted() {
    console.log('************App mounted')
  },
  updated() {
    console.log('App更新')
  },
}
</script>

<template>
  <div>
    <BrotherComp></BrotherComp>
    <DemoOne></DemoOne>
  </div>
</template>

<style lang="scss" scoped></style>

BrotherComp.vue兄弟组件

<template>
  <p>兄弟组件</p>
</template>

<script>
export default {
  name: 'BrotherComp',
  created() {
    console.log('>>>>>>>>>>>>>>>>兄弟组件 created')
  },
  mounted() {
    console.log('>>>>>>>>>>>>>>>>兄弟组件 mounted')
  },
  updated() {
    console.log('>>>>>>>>>>>>>>>>兄弟组件 updated')
  },
}
</script>

<style scoped></style>

DemoOne.vue父组件

<script>
import ShowComp from 'components/ShowComp.vue'
export default {
  name: 'DemoOne',
  components: { ShowComp },
  data() {
    // console.log(p)
    debugger
    return {
      akName: '张三',
      age: '12',
      info: {
        country: '中国',
        male: '男',
      },
      other: {
        story: '有故事的人',
        hobby: '玩',
      },
      parentName: {
        father: '大张三',
        mother: '张三之母',
      },
    }
  },
  method: {},
  created() {
    console.log('>>>>>>>>>>>>>>>>父组件 created')
    // console.log(this)
  },
  mounted() {
    console.log('************父组件 mounted')
  },
  updated() {
    console.log('父组件更新')
  },
}
</script>

<template>
  <div>
    <div
      style="
        border-bottom: 1px solid #333;
        margin-bottom: 10px;
        padding-bottom: 10px;
      "
    >
      父组件
      <br />
      akName:{{ akName }}
      <input v-model="akName" />
      age:{{ age }}<input v-model="age" />
      <br />
      info:{{ info }}<input v-model="info.country" /><input
        v-model="info.male"
      />
      <input v-model="info.country" />
      <br />
      other {{ other }}<input v-model="other.story" />
      <input v-model="other.hobby" />
      <br />
      parentName<input v-model="parentName.father" />
      <input v-model="parentName.mother" />
    </div>
    <ShowComp
      :age="age"
      v-model="info"
      :story="other.story"
      :parentName="parentName"
      :motherName="parentName.mother"
    ></ShowComp>
  </div>
</template>

<style lang="scss" scoped></style>

ShowComp.vue子组件

<template>
  <div>
    子组件
    <br />
    age:{{ age }}
    <br />
    info:{{ info }} <input :value="info.male" @change="onChange" />
    <br />
    story:{{ story }}
    <br />
    parentName:{{ parentName.father }}
  </div>
</template>

<script>
export default {
  name: 'ShowComp',
  model: {
    prop: 'info',
    event: 'change',
  },
  props: {
    story: {
      type: String,
    },
    age: {
      type: String,
    },
    info: {
      type: Object,
    },
    parentName: {
      type: Object,
    },
    motherName: {
      type: String,
    },
  },
  methods: {
    onChange(val) {
      this.$emit('change', { ...this.info, male: val.target.value })
    },
  },
  watch: {
    motherName(newVal, oldVal) {
      console.log('motherName监听', newVal, oldVal)
    },
  },
  created() {
    console.log('>>>>>>>>>>>>>>>>子组件 created')
  },
  mounted() {
    console.log('************子组件 mounted')
  },
  updated() {
    console.log('子组件更新')
  },
}
</script>

<style scoped></style>

为什么写这个?

是因为看了 阿里的Formily 提到了精确渲染的概念,一看之下觉得牛逼哄哄,精确渲染,只渲染变动的部分,性能极佳

image.png

然后回想起我做过的项目,确实有一些表单场景是比较卡顿的,以前也没仔细了解过,为什么会卡顿,也没有好的解决方案,所以我就想,是不是以前那些界面卡顿,也是因为没有精确渲染的原因?

但貌似我想岔了,vue本身就是精确更新的。Formily貌似主要解决的是React 组件的渲染性能

最后虽然没有达到我预期的目的,但对于vue开发我又有了更近一步的了解

同时我也看到了一个比较有启发的文章: 一顿操作,我把 Table 组件性能提升了十倍