关于Vue.js 3.x的拾遗

341 阅读1分钟

1、Vue.js里面的变量如何让它只生效一次?

有一个v-once指令,用它就可以了。

在 Vue 中渲染纯 HTML 元素的速度非常快,但有时你可能有一个包含很多静态内容的组件。在这些情况下,可以通过向根元素添加 v-once 指令来确保只对其求值一次,然后进行缓存,如下所示:

app.component('terms-of-service', {
  template: `
    <div v-once>
      <h1>Terms of Service</h1>
      ... a lot of static content ...
    </div>
  `
})

不过要注意:

不要过度使用这种模式。虽然在需要渲染大量静态内容的极少数情况下使用这种模式会很方便,但除非你注意到先前的渲染速度很慢,否则就没有必要这样做。另外,过度使用这种模式可能会在以后引起很多混乱。例如,假设另一个开发人员不熟悉 v-once 或者只是在模板中遗漏了它。他们可能会花上几个小时来弄清楚为什么模板没有正确更新。

对于下面代码片段,如果你在控制台中通过vm.$data.msg = 'This is a new string!'来更新msg,界面是不会发生重新渲染的。当去掉其中的v-once则可以。

src\main.js

import { createApp } from 'vue'
import App from './App.vue'

window.vm = createApp(App).mount('#app')

src\App.vue

<template>
  <h1 v-once>{{ msg }}</h1>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      msg: 'Vue.js 3.x'
    };
  }
}
</script>

2、怎么绑定动态的事件和属性?

其实Vue.js是支持绑定动态的事件和属性的。

<template>
  <h1 v-once>{{ msg }}</h1>
  <button @[eventName]="eventHandler" :[propName]="propValue">按钮</button>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      msg: 'Vue.js 3.x',
      eventName: 'click',
      propName: 'title',
      propValue: 'This is a title',
    };
  },
  setup() {
    return {
      eventHandler() {
        alert('hahaha');
      }
    }
  }
}
</script>

这个代码片段运行后按钮点击会弹出'hahaha',并且给按钮绑定上了title属性,鼠标悬停在按钮上可以看到title的信息。

3、Vue.js 3.x的生命周期钩子函数到底有几个?

这个问题再简单不过了。但似乎还不少人只了解前8个,有的培训课程老师也说是只有8个,大概是剩下那几个用到的时候少吧。实际上一共有13个。

选项式 APIHook inside setup功能
beforeCreateNot needed*实例生成之前自动执行的函数
createdNot needed*实例生成之后自动执行的函数
beforeMountonBeforeMount组件内容被渲染到页面之前自动执行的函数
mountedonMounted组件内容被渲染到页面之后自动执行的函数
beforeUpdateonBeforeUpdate数据发生变化时会立即执行的函数
updatedonUpdated数据发生变化,页面重新渲染后会自动执行的函数
beforeUnmountonBeforeUnmount当组件销毁之前,自动执行的函数
unmountedonUnmounted当组件销毁之后,自动执行的函数
errorCapturedonErrorCaptured当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。
renderTrackedonRenderTracked跟踪虚拟 DOM 重新渲染时调用。钩子接收 DebuggerEventt 作为参数。此事件告诉你哪个操作跟踪了组件以及该操作的目标对象和键。
renderTriggeredonRenderTriggered当虚拟 DOM 重新渲染被触发时调用。和 renderTracked 类似,接收 DebuggerEventt 作为参数。此事件告诉你是什么操作触发了重新渲染,以及该操作的目标对象和键。
activatedonActivated被 keep-alive 缓存的组件激活时调用。该钩子在服务器端渲染期间不被调用。
deactivatedonDeactivated被 keep-alive 缓存的组件停用时调用。该钩子在服务器端渲染期间不被调用。

注意:

<keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 <transition> 相似,<keep-alive> 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在组件的父组件链中。

当组件在 <keep-alive> 内被切换时,它的 mounted 和 unmounted 生命周期钩子不会被调用,取而代之的是 activated 和 deactivated。(这会运用在 <keep-alive> 的直接子节点及其所有子孙节点。)

你可以通过运行如下代码片段,点击其中的toggle按钮,去查看控制台生命周期钩子的log,感知<keep-alive>对于其内部组件生命周期的影响:

src\App.vue

<template>
  <button @click="toggle">toggle</button>
  <keep-alive>
    <component :is="currentComponent"></component>
  </keep-alive>
</template>

<script>
import Foo from './components/Foo.vue';
import Bar from './components/Bar.vue';

export default {
  name: 'App',
  components: {
    Foo,
    Bar,
  },
  data() {
    return {
      toggleCount: 1,
      currentComponent: Foo,
    };
  },
  methods: {
    toggle() {
      const arr = [Foo, Bar];
      this.toggleCount += 1;
      this.currentComponent = arr[this.toggleCount % 2];
    },
  },
  beforeCreate() {
    console.log('beforeCreate');
  },
  created() {
    console.log('created');
  },
  beforeMount() {
    console.log('beforeMount');
    console.log(document.getElementById('app').innerHTML);
  },
  mounted() {
    console.log('mounted');
    console.log(document.getElementById('app').innerHTML);
  },
  beforeUpdate() {
    console.log('beforeUpdate');
    console.log(document.getElementById('app').innerHTML);
  },
  updated() {
    console.log('updated');
    console.log(document.getElementById('app').innerHTML);
  },
  beforeUnmount() {
    console.log('beforeUnmount');
    console.log(document.getElementById('app').innerHTML);
  },
  unmounted() {
    console.log('unmounted');
    console.log(document.getElementById('app').innerHTML);
  },
}
</script>

src\components\Foo.vue

<template>
  <h2>foo</h2>
</template>

<script>
export default {
  beforeUnmount() {
    console.log('beforeUnmount');
    console.log(document.getElementById('app').innerHTML);
  },
  unmounted() {
    console.log('unmounted');
    console.log(document.getElementById('app').innerHTML);
  },
  activated() {
    console.log('activated');
    console.log(document.getElementById('app').innerHTML);
  },
  deactivated() {
    console.log('deactivated');
    console.log(document.getElementById('app').innerHTML);
  },
}
</script>

src\components\Bar.vue

<template>
  <h2>bar</h2>
</template>

对于另外,errorCaptured钩子是在钩子,其传播规则为:

错误传播规则

  • 默认情况下,如果全局的 config.errorHandler 被定义,所有的错误仍会发送它,因此这些错误仍然会向单一的分析服务的地方进行汇报。
  • 如果一个组件的继承或父级从属链路中存在多个 errorCaptured 钩子,则它们将会被相同的错误逐个唤起。
  • 如果此 errorCaptured 钩子自身抛出了一个错误,则这个新错误和原本被捕获的错误都会发送给全局的 config.errorHandler
  • 一个 errorCaptured 钩子能够返回 false 以阻止错误继续向上传播。本质上是说“这个错误已经被搞定了且应该被忽略”。它会阻止其它任何会被这个错误唤起的 errorCaptured 钩子和全局的 config.errorHandler

另外,errorCaptured钩子是在当捕获一个来自子孙组件的错误时被调用,但对于当前组件自身抛出的错误,是不会触发当前组件的errorCaptured钩子的。

此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。

可通过如下代码片段感受errorCaptured钩子的使用:

src\App.vue

<template>
  <Foo></Foo>
</template>

<script>
import Foo from './components/Foo.vue';

export default {
  name: 'App',
  components: {
    Foo,
  },
  errorCaptured(...arg) {
    console.log('errorCaptured', arg);
  }
}
</script>

src\components\Foo.vue

<template>
  <h2>foo</h2>
</template>

<script>
export default {
  mounted() {
    throw Error();
  },
}
</script>

4、计算属性和method都可以实现计算功能,有什么区别?

如下代码片段,当通过控制台里输入vm.$vm.msg = 'new string'去改变msg变量时,computedVal的内容不会被重新刷新。而通过method获取的值会刷新。

计算属性只有在它所依赖的响应式变量变化时才会重新计算,比如这里的this.count被修改时。

而method的计算只要组件更新,就会重新计算。比如,vm.$vm.msg = 'new string'去改变msg变量时,会触发组件更新,method也会再计算一遍,虽然这个计算表达式并不依赖于msg变量。

src\App.vue

<template>
  <h1>{{ msg }}</h1>
  <button @[eventName]="eventHandler" :[propName]="propValue">按钮</button>
  <div>计算属性:{{ computedVal }}</div>
  <div>methods获取:{{ caculateMethod() }}</div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      msg: 'Vue.js 3.x',
      eventName: 'click',
      propName: 'title',
      propValue: 'This is a title',
      count: 1,
    };
  },
  computed: {
    computedVal() {
      return this.count + new Date().getTime();
    },
  },
  setup() {
    return {
      eventHandler() {
        alert('hahaha');
      },
    }
  },
  methods: {
    caculateMethod() {
      return this.count + new Date().getTime();
    },
  },
}
</script>