关于 Vue 的使用中的一些注意事项和技巧

1. 简单传递多个 props 的方法

当一个组件需要分多个参数传递 props 值的时候,而这些参数又刚好是某个对象的属性

<template>
  <User
    :name="user.name"
    :avatar="user.avatar"
    :email="user.email"
    />
</template>

这个时候可以直接通过v-bind绑定对象来将它的属性值作为 props 自动传递给组件

<template>
  <User v-bind="user"/>
</template>
export default {
  setup() {
    return {
      user: {
        name: 'Timfan',
        avatar: 'my-profile.jpg',
        email: 'fants0230@sina.com'
      }
    }
  }
};

这种用法同样也适用于通过 v-on 来绑定多个事件监听器

<template>
  <User v-on="userEventHandlers"/>
  <!-- 等效于
    <User
      @updateName="userEventHandlers.updateName"
      @deleteUser="userEventHandlers.deleteUser"
      @addFriend="userEventHandlers.addFriend"
    />
  -->
</template>
export default {
  setup() {
    return {
      userEventHandlers: {
        updateName(newName) {
          // ...
        },
        deleteUser() {
          // ...
        },
        addFriend(friend) {
          // ...
        }
      }
    }
  }
};

2. 监听数组和对象的变化

不知道你是否有这样的疑惑,当你在使用侦听器 watch的时候,发现侦听的对象变化并没有正确地触发回调函数,通常这是因为侦听的对象是一个数组或者对象,但是没有设置deep的值为true造成的。

export default {
  name: 'ColourChange',
  props: {
    colours: {
      type: Array,
      required: true,
    },
  },
  watch: {
    // Use the object syntax instead of just a method
    colours: {
      // This will let Vue know to look inside the array
      deep: true,
      // We have to move our method to a handler field
      handler(oldVal, newVal)
        console.log('The list of colours has changed!');
        }
    }
  }
}

同理在 Vue 3 中使用的是响应式 API 来实现的,方式如下

watch(
  colours,
  () => {
    console.log('The list of colours has changed!');
  },
  {
    deep: true,
  }
);

详细用法可以参考官方文档 https://cn.vuejs.org/guide/essentials/watchers.html

3. 给 Prop 值添加校验

我们可以在 Props 的定义项里通过validator属性来传递一个回调来为它的值做校验

export default {
  name: 'Image',
  props: {
    src: {
      type: String,
    },
    style: {
      type: String,
      validator: s => ['square', 'rounded'].includes(s)
    }
  }
};

这个校验函数根据值是否有效,返回true或者false两种值。

通常按钮组件或者提示组件的类型(诸如:”info”, “success”, “danger”, “warning”)需要使用到该种用法作为有效值校验。这只是其中一种,还有更多的使用场景。

4. 全局组件

当你注册一个全局组件,你就不再需要进行第二次导入就可以在 template中使用:

/ Vue 3
import { createApp } from 'vue';
import GlobalComponent from './GlobalComponent.vue';
const app = createApp({})
app.component('GlobalComponent', GlobalComponent);

在 Vue 2 中采用如下方案

// Vue 2
import Vue from 'vue';
import GlobalComponent from './GlobalComponent.vue';
Vue.component('GlobalComponent', GlobalComponent);

现在你可以在项目中的任何 template 中使用 GlobalComponent全局组件,不需要进行额外的处理。当然全局组件和全局变量有着同样的利弊,请谨慎使用。

5. 监听嵌套的值需要带上引号

也许你之前没见过,但是确实可以直接通过引号括起来监听嵌套的值,特别在深层嵌套的情况下,特别方便。

watch: {
  '$route.query.id'() {
  // ...
  }
}

6. 如何监听组件中任何你想监听的对象

事实上组件中的任何响应式的变量都是可以通过watch来监听的:

export default {
  computed: {
    someComputedProperty() {
      // Update the computed prop
    },
  },
  watch: {
    someComputedProperty() {
      // Do something when the computed prop is updated
    }
  }
};

只要这个值是refreactive转化的响应式对象,或者是computed实现的计算属性值,你都可以通过watch来监听它的变化。

7. hRender 函数

当我们使用render函数来代替template模板的时候,通常是通过h函数来实现的:

<script setup>
import { h } from 'vue';
const render = () => h('div', {}, 'Hello Wurld');
</script>

它的作用是创建了一个虚拟节点,一个 Vue 内部跟踪变化的对象和最终渲染到页面中的文本。

第一个参数也就是这个虚拟节点可以是任意的 HTML 元素,也可以是封装好的可以调用的 Vue 组件

<script setup>
import { h } from 'vue';
import MyComponent from './MyComponent.vue';
const render = () => h(MyComponent, {}, []);
</script>

第二个参数可以是一系列的props值,自身的属性,或者事件监听器

<script setup>
import { h } from 'vue';
import MyComponent from './MyComponent.vue';
const render = () => h(MyComponent, {
class: 'text-blue-400',
title: 'This component is the greatest',
onClick() {
console.log('Clicked!');
},
}, []);
</script>

第三个参数可以是一串文本,一个子节点组成的数组,或者用来定义slot的对象

h函数的使用为我们带来通过 javaScript 复杂的逻辑创建 html 的便利性。

8. 通过 Vue 路由参数携带状态

在开发中我们可以在页面路由中携带参数,这对于我们进入到某个页面预设一些操作是很有用的,比如进入到某个有默认选项的页面或者需要滚动到某个位置,都可以通过获取到路由中的参数值来达到。

const dateRange = this.$route.query.dateRange;

这对于分离的两个不同的应用之间的通信也是通过该原理实现的。

9. 在 Render 函数中使用自定义指令

我们很熟悉在 template 模板中使用自定义指令的方式,直接通过 v-指令名,而 Vue 提供了resolveDirectivewithDirectives两个接口,前者可以获取到自定义的指令添加到第二个接口的回调参数中

<script setup>
import { resolveDirective, withDirectives, h } from 'vue';
// Find the already registered directive by name
const focusDirective = resolveDirective('focus');
// Wrap the button with the directive
const render = () => withDirectives(
h('button', {}, []),
// An array of directives to apply
[
[focusDirective]
]
);
</script>

10. Vue 中定义 Web Component 的方式

我们如何在 Vue 应用中自定义在 html 中可以使用的元素,可以分三步完成:

第一步:通过 Vue 提供的接口defineCustomElement

import { defineCustomElement } from 'vue';
import MyVueComponent from './MyVueComponent.vue';
const customElement = defineCustomElement(MyVueComponent);

第二步:注册自定义元素到 DOM 中:

customElements.define('my-vue-component', customElement);

第三步:在 HTML 结构中直接使用该自定义元素

<html>
  <head></head>
  <body>
    <my-vue-component></my-vue-component>
  </body>
</html>

这样就实现了一个不需要任何框架就可以在浏览器中运行的自定义组件( 也就是通常说的 Web Component)。

11. 在 script setup 中访问内部属性和方法

当我们通过$ref来访问某个组件的时候,我们是无法直接访问组件内部的方法或者属性变量的

export default {
  expose: ['makeItPublic'],
  data() {
    return {
      privateData: 'Keep me a secret!',
    };
  },
  computed: {
    makeItPublic() {
      return this.privateData.toUpperCase();
    },
  },
};

但是 Vue 提供的 expose 可以将内部的属性或方法暴露出来供外部使用,这里可以访问makeItPublic方法

this.$refs.component.makeItPublic()

在 Vue3 中当我们使用 <script setup> 里面的是完全封闭状态,如何你要暴露一个值只能这么做

<script setup>
import { ref, computed } from 'vue';
const privateData = ref('Keep me a secret!');
const makeItPublic = computed(
() => privateData.value.toUpperCase()
);
// We don't need to import this because it's a compiler macro
defineExpose({
makeItPublic
});
</script>

注意:这里的 defineExpose是一个编译宏,不是某个具体的函数,不用导入即可使用。

12. Vue 中特殊的几个 CSS 伪选择器

当你仅需要对插槽中内容中含有的标签进行样式设置时,你可以使用 :slotted伪选择器:

<style scoped>
/* Add margin to <p> tags within the slot */
:slotted(p) {
  margin: 15px 5px;
}
</style>

你也可以使用:global伪选择器进行全局范围的样式设置,即使在<style scoped>样式块中也生效

<style scoped>
:global(body) {
margin: 0;
padding: 0;
font-family: sans-serif;
}
</style>

当然,你也可以很方便地通过添加第二个<style>样式块,剔除scoped的限制,作用于全局样式中

<style scoped>
/* Add margin to <p> tags within the slot */
:slotted(p) {
margin: 15px 5px;
}
</style>
<style>
body {
margin: 0;
padding: 0;
font-family: sans-serif;
}
</style>

13. 实现自定义指令

在 Vue 中有一系列的预设指令,比如经常使用的v-forv-if,除此之外,我们还可以自定义一些指令:

<template>
  <h1 v-bg-colour="'skyblue'">This background is blue</h1>
</template>
import { createApp } from "vue";
const app = createApp({});
app.directive("bg-colour", {
  mounted(el, { value }) {
    // Update the background colour
    el.style.background = value;
  }
});

这个指令的作用是实现对指定标签中文本颜色的设置

你甚至还可以指定特定参数进行设置:

<template>
<h1 v-bg-colour:colour="'skyblue'">
This background is blue
</h1>
</template>
app.directive("bg-colour", {
  mounted(el, { value, arg }) {
    // Update the background colour
    el.style.background = value;
    console.log(arg); // "colour"
  }
});

这里可能和你预想的不一样,当多个参数时 Vue 会把它们当不同的指令来处理:

<template>
  <!-- Two directives will be mounted -->
  <h1
    v-bg-colour:colour="'skyblue'"
    v-bg-colour:animate="true"
    >
    This background is blue
  </h1>
</template>

索性这里的值最终会被评估为 javaScript 表达式,我们可以把它当做选项对象来传值:

<template>
<!-- Two directives will be mounted -->
<h1
	v-bg-colour="{
		colour: 'skyblue',
		animate: true,
	}"
>
This background is blue
</h1>
</template>
app.directive("bg-colour", {
  mounted(el, { value }) {
    // Update the background colour
    el.style.background = value.colour;
    // Do something cool with value.animate
  }
});

14. nextTick: 等到 DOM 更新完再执行

Vue 提供了很方便的途径帮助我们等待 DOM 完成更新在执行事件

// Do something that will cause Vue to re-render
changeTheDOM();
// Wait for the re-render to finish
await nextTick();
// Now we can check out the freshly minted DOM
inspectTheDOM();

或者在选项式 API 中:

await this.$nextTick();

一个tick即为一个页面渲染周期,Vue 监听所有响应式变化然后一次性完成 DOM 的更新。然后再进行下一次周期。

当你需要在数据更新,页面完成渲染后再触发某个动作,它能保证你在下一个tick执行之前完成这个动作。

15. 对v-for中的变量进行解构

你知道在v-for中就可以直接进行解构操作吗?

<li
  v-for="{ name, id } in users"
  :key="id"
  >
  {{ name }}
</li>

或者你对数组解构出index这种方式更熟悉

<li v-for="(movie, index) in [
'Lion King',
'Frozen',
'The Princess Bride'
]">
{{ index + 1 }} - {{ movie }}
</li>

当需要对对象解构出属性和值时

<li v-for="(value, key) in {
name: 'Lion King',
released: 2019,
director: 'Jon Favreau',
}">
{{ key }}: {{ value }}
</li>

你甚至可以同时解构出属性和index

<li v-for="(value, key, index) in {
name: 'Lion King',
released: 2019,
director: 'Jon Favreau',
}">
#{{ index + 1 }}. {{ key }}: {{ value }}
</li>

评论

还没有评论...留下你的评论!

发表评论

电子邮件地址不会被公开。 必填项已用*标注

Sidebar