Artikel ini membawakan anda dua puluh lima petua apabila menggunakan Vue Terdapat banyak petua berguna, ada yang rumit, ada yang digunakan hampir setiap hari, dan ada yang lebih maju – tetapi semuanya berguna. Semoga ia membantu semua orang.
Belajar untuk menjadi pembangun Vue yang lebih baik tidak semestinya mengenai konsep besar yang memerlukan masa dan usaha untuk dikuasai. Mengetahui beberapa petua dan kiat boleh menjadikan kehidupan pengaturcaraan anda lebih mudah – tanpa banyak kerja berulang.
Dalam beberapa tahun yang lalu membangun dengan Vue, saya telah mempelajari banyak teknik yang berguna. Ada yang rumit, ada yang anda gunakan hampir setiap hari, dan ada yang lebih maju - tetapi semuanya berfungsi.
Gunakan pilihan validator
dalam definisi prop untuk mengehadkan jenis prop kepada set tertentu dalam nilai.
export default { name: 'Image', props: { src: { type: String, }, style: { type: String, validator: s => ['square', 'rounded'].includes(s) } } };
Fungsi pengesahan ini menerima prop dan mengembalikan true
atau false
jika prop itu sah atau tidak sah.
Apabila hanya menghantar benar atau salah untuk mengawal keadaan tertentu tidak dapat memenuhi keperluan, saya biasanya menggunakan kaedah ini.
Jenis butang atau jenis amaran (maklumat, kejayaan, bahaya, amaran) ialah penggunaan yang paling biasa. Warna juga bagus untuk digunakan.
Slot dalam Vue boleh mempunyai kandungan lalai, yang membolehkan kami membuat komponen yang lebih mudah digunakan.
<button> <slot> <!-- Used if no slot is provided --> Click me </slot> </button>
Cara kegemaran saya untuk menggunakan slot lalai ialah menggunakannya untuk membuat titik sambungan.
Kita boleh mengambil mana-mana bahagian komponen, merangkumkannya dalam slot, dan di luar kita boleh menulis ganti bahagian komponen itu dengan apa sahaja yang kita mahu. Secara lalai ia masih akan berfungsi dengan cara yang sama, tetapi berbuat demikian akan memberi anda lebih banyak pilihan
<template> <button> <slot> <p> {{ text }} </p> </slot> </button> </template>
Kini kita boleh menggunakan komponen ini dalam pelbagai cara. Cara mudah, lalai, atau cara tersuai.
<!-- Uses default functionality of the component --> <buttonwithextensionpoint></buttonwithextensionpoint> <buttonwithextensionpoint> <p> Do something a little different here </p> </buttonwithextensionpoint>
Anda mungkin tidak tahu perkara ini, kami boleh mendengar nilai bersarang secara langsung dengan menggunakan petikan:
watch { '$route.query.id'() { // ... } }
v-if
(dan bila untuk mengelakkannya) Daripada menggunakan v-if
, kadangkala gunakan v-show
sebaliknya , akan ada prestasi yang lebih tinggi.
<complicatedchart></complicatedchart>
Apabila v-if
dibuka atau ditutup, ia mencipta dan memusnahkan elemen sepenuhnya. Sebaliknya, v-show
akan mencipta elemen dan meninggalkannya di sana, menyembunyikannya dengan menetapkan gayanya kepada display: none
.
Ini lebih cekap jika komponen yang anda tukar mempunyai kos pemaparan yang tinggi.
Sebaliknya, jika anda tidak perlu melaksanakan komponen yang mahal dengan segera, anda boleh menggunakan v-if
supaya ia melangkau memaparkannya dan menjadikan halaman dimuatkan dengan lebih cepat sedikit.
Slot berskop sangat menarik, tetapi untuk menggunakannya, anda juga mesti menggunakan Banyak templat tag.
Nasib baik, terdapat trengkas yang membolehkan kita menjauhinya, tetapi hanya jika kita menggunakan satu slot skop.
Tulisan biasa:
<datatable> <template> <tableheader></tableheader> </template> </datatable>
Tidak digunakan template
:
<datatable> <tableheader></tableheader> </datatable>
Mudah, lugas dan menakjubkan.
Mari lihat dahulu cara melakukannya, kemudian bincangkan mengapa anda mahu menyembunyikan slot itu.
Setiap komponen Vue mempunyai objek $slots
khas yang memegang semua slot anda. Kekunci slot lalai ialah default
, mana-mana slot bernama menggunakan namanya sebagai kunci.
const $slots = { default: <default>, icon: <icon>, button: <button>, };</button></icon></default>
Tetapi objek $slots
ini hanya mempunyai slot yang boleh digunakan untuk komponen ini, bukan setiap slot yang ditentukan.
Ambil komponen ini yang mentakrifkan beberapa slot, termasuk beberapa slot bernama.
<!-- Slots.vue --> <template> <p> </p> <h2>Here are some slots</h2> <slot></slot> <slot></slot> <slot></slot> </template>
Jika kami menggunakan hanya satu slot pada komponen, hanya slot itu akan dipaparkan dalam objek $slots
kami.
<template> <slots> <template> This will be applied to the second slot. </template> </slots> </template>
$slots = { second: <vnode> }</vnode>
Kami boleh menggunakannya dalam komponen kami untuk mengesan slot yang telah digunakan pada komponen, contohnya, dengan menyembunyikan slot dalam elemen pembalut.
<template> <p> </p> <h2>A wrapped slot</h2> <p> <slot></slot> </p> </template>
Kini, pembalut yang menggunakan gaya p
hanya akan dipaparkan jika kita mengisi slot ini dengan sesuatu.
Jika anda tidak menggunakan v-if
, anda akan mendapat kosong yang tidak diperlukan p
jika tiada slot. Bergantung pada gaya p
, ini mungkin mengacaukan reka letak kami dan menjadikan antara muka kelihatan pelik.
Terdapat tiga sebab utama untuk menggunakan slot bersyarat:
p
berbalut untuk menambah gaya lalai Sebagai contoh, apabila kami menambah gaya lalai, kami menambah p
:
<template> <p> </p> <h2>This is a pretty great component, amirite?</h2> <p> <slot> </slot></p> <button>Click me!</button> </template>
kosong pada halaman tersebut. p
<p> </p><h2>This is a pretty great component, amirite?</h2> <p> <!-- 槽中没有内容,但这个p 仍然被渲染。糟糕 --> </p> <button>Click me!</button>
有时我们需要知道插槽内的内容何时发生了变化。
<!-- 可惜这个事件不存在 --> <slot></slot>
不幸的是,Vue没有内置的方法让我们检测这一点。
然而,我的朋友Austin想出了一个非常干净的方法,使用MutationObserver来做这件事。
MutationObserver接口提供了监视对DOM树所做更改的能力。它被设计为旧的Mutation Events功能的替代品,该功能是DOM3 Events规范的一部分。
export default { mounted() { // 当有变化时调用`update` const observer = new MutationObserver(this.update); // 监听此组件的变化 observer.observe(this.$el, { childList: true, subtree: true }); } };
这个涉及的内容还是很多的,后面会单独出一篇文章来讲,记得关注刷碗智的公众号 哦!
style
混合在一起通常情况下,在处理样式时,我们希望它们能被划分到一个单独的组件中。
<style> .component { background: green; } </style>
不过,如果需要的话,也可以添加一个非作用域样式块来添加全局样式
<style> /* 全局 */ .component p { margin-bottom: 16px; } </style> <style> /* 在该组件内有效 */ .component { background: green; } </style>
但要小心,全局样式是危险的,难以追踪。但有时,它们是一个完美的逃生舱口,正是你所需要的。
Scoped CSS在保持内容整洁方面非常棒,而且不会将样式引入应用的其他组件中。
但有时你需要覆盖一个子组件的样式,并跳出这个作用域。
Vue有一个 deep
选择器:
<style> .my-component >>> .child-component { font-size: 24px; } </style>
注意:如果你使用像SCSS这样的CSS预处理器,你可能需要使用/deep/
来代替。
**上下文感知组件(context-aware)**是“魔法的”,它们自动适应周围发生的事情,处理边缘情况、状态共享等等。
有3种主要的 context-aware
,但 Configuration
是我最感兴趣的一种。
当你把一个大的组件分解成多个小的组件时,它们往往仍然需要共享状态。
我们可以在 "幕后 "做这些工作,而不是把这些工作推给使用者。
我们一般会把 Dropdown
组件分解成 Select
和 Option
组件,这样会获得更多的灵活性。但是为了方便使用,Select
和Option
组件彼此共享 selected
状态。
<!-- 为简单起见,作为一个单一组件使用 --> <dropdown></dropdown> <!-- 分多个组件,更灵活 --> <select> <option>Mustard</option> <option>Ketchup</option> <p> <option>Relish</option> </p> </select>
有时,一个组件的行为需要根据应用程序的其他部分的情况来改变。这通常是为了自动处理边缘情况,否则处理起来会很烦人。
一个 Popup
或 Tooltip
应该重新定位,以便它不会溢出页面。但是,如果该组件是在一个modal 内,它应该重新定位,以便它不会溢出 modal。
如果Tooltip
知道它是在一个模态里面,这可以自动完成。
创建了 context-aware
的CSS,根据父级或同级元素的情况应用不同的样式。
.statistic { color: black; font-size: 24px; font-weight: bold; } .statistic + .statistic { margin-left: 10px; }
CSS变量让我们更进一步,允许我们在一个页面的不同部分设置不同的值。
如果你从Vue之外得到一个变量,让它具有反应性是很好的。
这样,我们就可以在computed props
、watch
和其他任何地方使用它,它的工作方式就像Vue中的任何其他状态一样。
如果我们使用的选项API,需要的只是将其放在组件的数据部分中:
const externalVariable = getValue(); export default { data() { return { reactiveVariable: externalVariable, }; } };
如果使用Vue3的组合API,可以直接使用ref
或reactive
。
import { ref } from 'vue'; // 可以完全在Vue组件之外完成 const externalVariable = getValue(); const reactiveVariable = ref(externalVariable); console.log(reactiveVariable.value);
使用 reactive
代替:
import { reactive } from 'vue'; // 可以完全在Vue组件之外完成 const externalVariable = getValue(); // reactive 只对对象和数组起作用 const anotherReactiveVariable = reactive(externalVariable); // Access directly console.log(anotherReactiveVariable);
如果你还在使用 Vue2,你可以使用observable
而不是reactive
来实现完全相同的结果。
你知道可以在-vfor
中使用解构吗?
更广为人知的是,可以通过使用这样的元组从v-for
中取出索引。
当使用一个对象时,可以这样使用 key
:
也可以将这两种方法结合起来,获取key
以及属性的 index
。
v-for
指令允许我们遍历数组,但它也允许我们遍历一个范围
<template> <ul> <li>Item #{{ n }}</li> </ul> </template>
渲染结果:
Item #1 Item #2 Item #3 Item #4 Item #5
当我们使用带范围的v-for
时,它将从1
开始,以我们指定的数字结束。
export default { computed: { someComputedProperty() { // Update the computed prop }, }, watch: { someComputedProperty() { // Do something when the computed prop is updated } } };
我们可以监听:
如果你使用组合API,任何值都可以被监视,只要它是一个ref
或reactive
对象。
我从一个子组件中复制 prop 类型,只是为了在一个父组件中使用它们。但我发现,偷取这些 prop 类型要比仅仅复制它们好得多。
例如,我们在这个组件中使用了一个Icon
组件。
<template> <p> </p> <h2>{{ heading }}</h2> <icon></icon> </template>
为了让它工作,我们需要添加正确的 prop 类型,从``Icon`组件复制。
import Icon from './Icon'; export default { components: { Icon }, props: { iconType: { type: String, required: true, }, iconSize: { type: String, default: 'medium', validator: size => [ 'small', 'medium', 'large', 'x-large' ].includes(size), }, iconColour: { type: String, default: 'black', }, heading: { type: String, required: true, }, }, };
多么痛苦啊。
当 Icon
组件的 prop
类型被更新时,我们肯定会忘记返回这个组件并更新它们。随着时间的推移,当该组件的 prop
类型开始偏离Icon
组件中的 prop 类型时,就会引入错误。
因此,这就是为什么我们要窃取组件的 prop
类型:
import Icon from './Icon'; export default { components: { Icon }, props: { ...Icon.props, heading: { type: String, required: true, }, }, };
不需要再复杂了。
除了在我们的例子中,我们把 icon
加在每个 prop
名称的开头。所以我们必须做一些额外的工作来实现这一点。
import Icon from './Icon'; const iconProps = {}; Object.entries(Icon.props).forEach((key, val) => { iconProps[`icon${key.toUpperCase()}`] = val; }); export default { components: { Icon }, props: { ...iconProps, heading: { type: String, required: true, }, }, };
现在,如果Icon
组件中的 prop
类型被修改,我们的组件将保持最新状态。
但是,如果一个 prop
类型从 Icon
组件中被添加或删除了呢?为了应对这些情况,我们可以使用v-bind
和一个计算的 prop
来保持动态。
有时我需要检测一个点击是发生在一个特定元素el
的内部还是外部。这就是我通常使用的方法。
window.addEventListener('mousedown', e => { // 获取被点击的元素 const clickedEl = e.target; if (el.contains(clickedEl)) { //在 "el "里面点击了 } else { //在 "el "外点击了 } });
有一次,我决定看看我是否可以只用模板来做一个v-for
组件。在这个过程中,我也发现了如何递归地使用槽。
<!-- VFor.vue --> <template> <p> <!-- 渲染第一项 --> {{ list[0] }} <!-- 如果我们有更多的项目,继续!但是不要使用我们刚刚渲染的项 --> <v-for> 1" :list="list.slice(1)" /> </v-for></p> </template>
如果你想用作用域插槽来做这件事,只是需要一些调整
<template> <p> <!-- Pass the item into the slot to be rendered --> <slot> <!-- Default --> {{ list[0] }} </slot> <v-for> 1" :list="list.slice(1)" > <!-- Recursively pass down scoped slot --> <template> <slot></slot> </template> </v-for> </p> </template>
下面是这个组件的使用方法。
<template> <p> <!-- 常规列表 --> <v-for></v-for> <!-- 加粗的项目列表 --> <v-for> <template> <strong>{{ item }}</strong> </template> </v-for> </p> </template>
并不是添加到一个组件的每一点信息都是状态。有时我们需要添加一些元数据,给其他组件提供更多信息。
例如,如果正在为谷歌 analytics这样的分析仪表:
如果你想让布局知道每个小组件应该占多少列,你可以直接在组件上添加元数据。
export default { name: 'LiveUsersWidget', // 只需将其作为一个额外的属性添加 columns: 3, props: { // ... }, data() { return { //... }; }, };
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` contains all the metadata for a component console.log(`Using ${this.$options.metadata} columns`); }, };
只要记住,这个元数据对组件的每个实例都是一样的,而且不是响应式的。
这方面的其他用途包括(但不限于):
这是**SFC(单文件组件)**的一点已知功能。
可以像常规HTML文件一样导入文件:
<template></template> <script></script> <style></style>
如果你需要分享样式、文件或其他任何东西,这可能会非常方便。
可重复使用的组件不一定是大的或复杂的东西。
我经常让小的和短的组件可以重复使用。
因为我没有到处重写这段代码,所以更新它变得更加容易,而且我可以确保每个OverflowMenu
的外观和工作方式都完全一样–因为它们是一样的!"。
<!-- OverflowMenu.vue --> <template> <menu> <!-- 添加一个自定义按钮来触发我们的菜单 --> <template> <!-- 使用bind来传递click处理程序、a11y 属性等 --> <button> <template> <svg></svg> </template> </button> </template> </menu> </template>
在这里,我们采用了一个菜单组件,但在触发它的按钮上添加了一个 ellipsis 图标
。(省略号)的图标来触发它的打开。
这似乎不值得把它做成一个可重复使用的组件,因为它只有几行。难道我们就不能在每次要使用这样的菜单时添加图标吗?
但是这个OverflowMenu将被使用几十次,现在如果我们想更新图标或它的行为,我们可以非常容易地做到。而且,使用它也更简单了。
我们可以从一个组件的外部通过给它一个 ref
用来调用一个方法。
<!-- Parent.vue --> <template> <childcomponent></childcomponent> </template>
// Somewhere in Parent.vue this.$refs.child.method();
再解释一下这个问题。
有时候,“最佳实践”并不适用于你正在做的事情,你需要一个像这样的逃生口。
通常情况下,我们使用 props 和 events 在组件之间进行交流。props 被下发到子组件中,而events 被上发到父组件中。
<template> <childcomponent></childcomponent> </template>
// Child.vue export default { props: ['trigger'], watch: { shouldCallMethod(newVal) { if (newVal) { // Call the method when the trigger is set to `true` this.method(); } } } }
这可以正常工作,但只能在第一次调用时使用。如果您需要多次触发此操作,则必须进行清理并重置状态。逻辑是这样的
true
传递给触发器 prop
trigger
重置为 false
,所以我们可以从头再来一次相反,如果我们在子组件上设置一个 ref
,我们可以直接调用该方法:
<!-- Parent.vue --> <template> <childcomponent></childcomponent> </template>
// Somewhere in Parent.vue this.$refs.child.method();
是的,我们打破了 “props down, events up"” 的规则,我们打破了封装,但是这样做更清晰,更容易理解,所以是值得的
有时,"最好的 "解决方案最终会成为最糟糕的解决方案。
使用 watcher
最棘手的部分是,有时它似乎不能正确触发。
通常,这是因为我们试图监听数组或对象,但没有将deep
设置为true
export default { name: 'ColourChange', props: { colours: { type: Array, required: true, }, }, watch: { // 使用对象语法,而不仅仅是方法 colours: { // 这将让Vue知道要在数组内部寻找 deep: true, handler() console.log('The list of colours has changed!'); } } } }
使用Vue 3的API会是这样的:
watch( colours, () => { console.log('The list of colours has changed!'); }, { deep: true, } );
我们可以在URL中存储(一点)状态,允许我们直接跳到页面上的一个特定状态。
例如,你加载一个已经选择了日期范围过滤器的页面:
someurl.com/edit?date-range=last-week
这对于应用中用户可能共享大量链接的部分来说是非常棒的,对于服务器渲染的应用,或者在两个独立的应用之间通信的信息比普通链接通常提供的更多。
我们可以存储过滤器、搜索值、模态框是打开还是关闭,或者在列表的哪个位置滚动以完美地实现无限分页。
使用 vue-router
获取查询参数是这样工作的(这也适用于大多数Vue框架,如Nuxt和Vuepress):
const dateRange = this.$route.query.dateRange;
为了改变它,我们使用 RouterLink
组件并更新 query
。
<routerlink></routerlink>
template
标签的另一个用途template
标签可以在模板中的任何地方使用,以更好地组织代码。
我喜欢用它来简化v-if
逻辑,有时也用v-for
。
在这个例子中,我们有几个元素都使用同一个v-if
条件。
<template> <p> <img alt="Ringkasan terperinci 25 kemahiran Vue yang mesti anda gunakan!" > </p> <h3> {{ title }} </h3> <h4> {{ subheading }} </h4> <p> <slot></slot> </p> <socialshare></socialshare> </template>
这有点笨拙,而且一开始并不明显,一堆这样的元素被显示和隐藏在一起。在一个更大、更复杂的组件上,这可能是一个更糟糕的情况
但我们能优化它。
我们可以使用 template
标签来分组这些元素,并将 v-if
提升到模板 template
本身。
<template> <p> <img alt="Ringkasan terperinci 25 kemahiran Vue yang mesti anda gunakan!" > </p> <h3> {{ title }} </h3> <template> <h4> {{ subheading }} </h4> <p> <slot></slot> </p> <socialshare></socialshare> </template> </template>
现在看起来就更容易理解,而且它在做什么,一目了然。
我们可以为Vue中的错误和警告提供一个自定义处理程序。
// Vue 3 const app = createApp(App); app.config.errorHandler = (err) => { alert(err); }; // Vue 2 Vue.config.errorHandler = (err) => { alert(err); };
像 Bugsnag 和 Rollbar 这样的错误跟踪服务,可以钩住这些处理程序来记录错误,但你也可以用它们来更优雅地处理错误,以获得更好的用户体验。
例如,如果一个错误未被处理,应用程序不会直接崩溃,你可以显示一个完整的错误屏幕,让用户刷新或尝试其他东西。
在 Vue3 中,错误处理程序只能处理 template
和 watcher
错误,但是 Vue2
的错误处理程序可以捕获几乎所有错误。这两个版本中的警告处理程序只在开发阶段有效。
代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug。
【相关推荐:《vue.js教程》】
Atas ialah kandungan terperinci Ringkasan terperinci 25 kemahiran Vue yang mesti anda gunakan!. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!