提升效率的vue小技巧

97 阅读3分钟

1. 将 prop 限制为类型列表

在 prop 定义中使用 validator 选项,你可以将 prop 限制为一组特定的值:

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

这个 validator 函数接受一个 prop,返回 true 或 false。当你需要比布尔值允许的更多选项时,也可以使用它。按钮类型或警报类型(信息、成功、危险、警告)是一些比较常见的用途。

2. 使用引号观察嵌套值

你可能不知道这一点:只需使用引号就可以轻松地直接查看嵌套值:

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

这对于处理深度嵌套的对象非常有用

4. 将本地和全局风格混合在一起

通常在使用样式时我们希望它们被限定为单个组件:

<style scoped>
  .component {
    background: green;
  }
</style>

如果你需要还可以添加一个非作用域样式块来添加全局样式:

<style>
  /*全局应用*/
  .component p {
    margin-bottom: 16px;
  }
</style>

<style scoped>
  /*范围限定于此特定组件*/
  .component {
    background: green;
  }
</style>

5. 覆盖子组件的样式——正确的方法

Scoped CSS 比较容易保持整洁,并且不会意外地将样式渗入应用程序的其他部分。但有时你需要覆盖子组件的样式,并突破该范围。Vue 有一个deep专门用于此的选择器:

<style scoped>
/* 覆盖子组件的 CSS,同时保持样式范围*/
.my-component >>> .child-component {
  font-size: 24px;
}
</style>

注意:如果你使用的是 SCSS 之类的 CSS 预处理器,则可能需要改用/deep/

6. 如何使在 Vue 之外创建的变量具有响应性?

如果你从 Vue 外部获得一个变量,那么能够使其具有响应性就很好。这样你就可以在计算道具、观察者和其他任何地方使用它,它就像 Vue 中的任何其他状态一样工作。

当你正在使用 options API,你只需将它放在data组件的部分中:

const externalVariable = getValue();

export default {
  data() {
    return {
      reactiveVariable: externalVariable,
    };
  }
};

当你在 Vue 3 中使用组合 API,则可以使用refreactive这样:

import { ref } from 'vue';

// 可以完全在 Vue 组件之外完成
const externalVariable = getValue();
const reactiveVariable = ref(externalVariable);

// 使用 .value 访问
console.log(reactiveVariable.value);

使用reactive来代替:

import { reactive } from 'vue';

// 可以完全在 Vue 组件之外完成
const externalVariable = getValue();
// Reactive 仅适用于对象和数组
const anotherReactiveVariable = reactive(externalVariable);

// 直接访问
console.log(anotherReactiveVariable);

如果你仍在使用 Vue 2(就像我们中的许多人一样),你可以使用observable而不是reactive获得完全相同的结果。

7. 在 v-for 中解构

你知道你可以在 v-for 中解构吗?

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

众所周知,你可以使用这样的元组从 v-for 中获取索引:

<li v-for="(value, key) in [  'Hai Yong',  'Frozen',  'Web Beginner']">
  {{ index + 1 }} - {{ value }}
</li>

使用对象时,你还可以抓住key:

<li v-for="(value, key) in {
  name: 'Hai Yong',
  released: 2021,
  director: 'A blogger',
}">
  {{ key }}: {{ value }}
</li>

也可以结合这两种方法,获取属性的键和索引:

<li v-for="(value, key, index) in {
  name: 'Hai Yong',
  released: 2021,
  director: 'A blogger',
}">
  #{{ index + 1 }}. {{ key }}: {{ value }}
</li>

8. 在 Vue 中循环一个范围

v-for指令允许我们遍历一个数组,但它也让我们遍历一个范围:

<template>
  <ul>
    <li v-for="n in 5">项目#{{ n }}</li>
  </ul>
</template>

显示效果:

  • 项目#1
  • 项目#2
  • 项目#3
  • 项目#4
  • 项目#5

当我们使用v-for范围时,它将从 1 开始并以我们指定的数字结束。

9. 观察组件中的任何内容

你的组件中的任何响应都可以被观察到:

export default {
  computed: {
    someComputedProperty() {
      // 更新计算道具
    },
  },
  watch: {
    someComputedProperty() {
      // 当计算的 prop 更新时做一些事情
    }
  }
};

你可以看:

  • 计算道具
  • 道具
  • 嵌套值

如果你使用组合 API,只要它是一个refreactive对象就可以监视任何值,。

10. 检测元素外部(或内部)的点击

有时我们需要检测点击是发生在特定元素el内部还是外部。这是我们通常使用的方法:

window.addEventListener('mousedown', e => {
  // 获取被点击的元素
  const clickedEl = e.target;

  // `el` 是你正在检测外部点击的元素
  if (el.contains(clickedEl)) {
    // 单击“el”内部
  } else {
    // 在`el`之外点击
  }
});

11. 组件元数据

并不是你添加到组件的每一点信息都是状态。有时你需要添加一些元数据来为其他组件提供更多信息。

例如,如果你要为 Google Analytics 等分析仪表板构建一堆不同的小部件:

image.png

如果你希望布局知道每个小部件应占用多少列,你可以将其作为元数据直接添加到组件上:

export default {
  name: 'LiveUsersWidget',
  // 👇 只需将其添加为额外属性
  columns: 3,
  props: {
    // ...
  },
  data() {
    return {
      //...
    };
  },
};

你会发现此元数据是组件上的一个属性:

import LiveUsersWidget from './LiveUsersWidget.vue';
const { columns } = LiveUsersWidget;

你还可以通过特殊$options属性从组件内部访问元数据:

export default {
  name: 'LiveUsersWidget',
  columns: 3,
  created() {
    // 👇 `$options` 包含组件的所有元数据
    console.log(`Using ${this.$options.metadata} columns`);
  },
};

请记住,此元数据对于组件的每个实例都是相同的,并且不是响应式的。

其他用途包括(但不限于):

  • 保留各个组件的版本号
  • 用于构建工具的自定义标志以区别对待组件
  • 向组件添加自定义功能,超出计算道具、数据、观察者等。

12. 从组件外部调用方法

你可以通过给它一个从组件外部调用方法ref

<!-- Parent.vue -->
<template>
  <ChildComponent ref="child" />
</template>
// Parent.vue 中的某个地方
this.$refs.child.method();

通常,我们使用道具和事件在组件之间进行通信。道具被发送到子组件,事件被发送回父组件。

<template>
  <ChildComponent
    :tell-me-what-to-do="someInstructions"
    @something-happened="hereIWillHelpYouWithThat"
  />
</template>

但有时你可能会遇到需要父组件触发子组件中的方法的情况。这是只有向下传递道具不起作用的地方。可以向下传递一个布尔值并让子组件监视它:

<!-- Parent.vue -->
<template>
  <ChildComponent :trigger="shouldCallMethod" />
</template>
// Child.vue
export default {
  props: ['trigger'],
  watch: {
    shouldCallMethod(newVal) {
      if (newVal) {
        // 当触发器设置为 `true` 时调用该方法
        this.method();
      }
    }
  }
}

这工作正常,但仅限于第一次调用。如果你需要多次触发此操作,则必须清理并重置状态。然后逻辑看起来像这样:

  1. Parent 组件传递truetriggerprop
  2. Watch被触发,Child组件调用方法
  3. Child 组件发出一个事件告诉 Parent 组件该方法已成功触发
  4. Parent 组件重置triggerfalse,因此我们可以再次执行此操作

啊。

相反,如果我们在子组件上设置ref ,我们可以直接调用该方法:

<!-- Parent.vue -->
<template>
  <ChildComponent ref="child" />
</template>
// Parent.vue 中的某个地方
this.$refs.child.method();

我们打破了“props down, events up”规则,打破了封装,但它更清晰、更容易理解值得这样做!

有时,“最佳”解决方案最终会成为最差的解决方案。

13. 观察数组和对象

使用观察者最棘手的部分是有时候它似乎不能正确触发。一般都是因为你试图查看一个数组或一个对象,但没有设置deeptrue

export default {
  name: 'ColourChange',
  props: {
    colours: {
      type: Array,
      required: true,
    },
  },
  watch: {
    // 使用对象语法而不仅仅是方法
    colours: {
      // 这将让 Vue 知道查看数组内部
      deep: true,

      // 我们必须将我们的方法移动到处理程序字段
      handler()
        console.log('颜色列表已更改!');
      }
    }
  }
}

使用 Vue 3 的反应式 API 看起来像这样:

watch(
  colours,
  () => {
    console.log('颜色列表已更改!');
  },
  {
    deep: true,
  }
);

如果你想了解更多信息,可以参阅Vue 3Vue 2的文档。

14. 与 Vue Router 深度链接

你可以在 URL 中存储(一些)状态,允许你直接跳转到页面上的特定状态。

比如你可以加载一个已选择日期范围过滤器的页面:

someurl.com/edit?date-range=last-week

这对于用户可能共享大量链接的应用程序部分、服务器呈现的应用程序或在两个独立应用程序之间传递比常规链接通常提供的信息更多的信息非常有用。

你可以存储过滤器、搜索值、模式是打开还是关闭,或者我们滚动到的列表中的位置——非常适合无限分页。

使用vue-router这样的方式获取查询(这也适用于 Nuxt 和 Vuepress 等大多数 Vue 框架):

const dateRange = this.$route.query.dateRange;
复制代码

要更改它,我们使用RouterLink组件并更新query

<RouterLink :to="{
  query: {
    dateRange: newDateRange
  }
}">
复制代码

15. 模板标签的另一种用途

template标签可以在模板内的任何地方使用,以更好地组织代码。

我喜欢用它来简化v-if逻辑,有时v-for也是。

在这个例子中,我们有几个元素都使用相同的v-if条件:

<template>
  <div class="card">
    <img src="imgPath" />
    <h3>
      {{ title }}
    </h3>
    <h4 v-if="expanded">
      {{ subheading }}
    </h4>
    <div v-if="expanded" class="card-content">
      <slot/>
    </div>
    <SocialShare v-if="expanded" />
  </div>
</template>
复制代码

这有点笨拙,一开始并不明显,一堆这些元素被显示和隐藏在一起。在更大、更复杂的组件上,这可能是更糟糕的情况!

但我们可以解决这个问题。

我们可以使用template标签对这些元素进行分组,并将其提升v-iftemplate标签本身:

<template>
  <div class="card">
    <img src="imgPath" />
    <h3>
      {{ title }}
    </h3>
    <template v-if="expanded">
      <h4>
        {{ subheading }}
      </h4>
      <div class="card-content">
        <slot/>
      </div>
      <SocialShare/>
    </template>
  </div>
</template>

16. 处理错误(和警告)的更好方法

你可以为 Vue 中的错误和警告提供自定义处理程序:

// Vue 3
const app = createApp(App);
app.config.errorHandler = (err) => {
  alert(err);
};

// Vue 2
Vue.config.errorHandler = (err) => {
  alert(err);
};

Bugsnag 和 Rollbar 等错误跟踪服务挂接到这些处理程序中以记录错误,但你也可以使用它们来更优雅地处理错误以获得更好的用户体验。

例如,如果错误未得到处理,应用程序不仅会崩溃,还可以显示整页错误屏幕并让用户刷新或尝试其他操作。

在 Vue 3 中,错误处理程序仅适用于模板和观察程序错误,但 Vue 2 错误处理程序几乎可以捕获所有内容。两个版本中的警告处理程序仅适用于开发。