15个 Vue 技巧,开发有段时间了,才知道还能这么用(你所知道真的包括这些吗?)

目录

前言

1、将一个 prop 限制在一个类型的列表中

2、使用引号来监听嵌套属性

3、知道何时使用 v-if

4、单个作用域插槽的简写

5、将局部和全局的 style 混合在一起

6、重写子组件的样式

7、如何在 Vue 之外创建一个具有响应性的变量

8、v-for 中的解构

9、在指定范围内循环

10、窃取 prop 类型

11、检测元素外部(或内部)的单击

12、从组件外部调用一个方法

13、监听数组和对象

14、template 标签的另一个用途

15、处理错误(和警告)的更好方法


前言

学习成为一个更好的 Vue 开发者,并不总是关于那些需要花时间和精力才能掌握的大概念。

掌握一些技巧和窍门,可以让我们的编程生活变得更容易–没有大量重复的工作。

在使用 Vue 开发的这几年里,掌握一些有用的技巧,使用一些更高级的技术点,总会有用的。

1、将一个 prop 限制在一个类型的列表中

我们在使用 prop 时,可能会有时候需要判断该 prop 是否在我们规定的范围内(或者说规定的值内),此时可以使用 prop 定义中的 validator 选项,将一个 prop 类型限制在一组特定的值中。

export?default?{
??name:?'Test',
??props:?{
????name:?{
??????type:?String,
????},
????size:?{
??????type:?String,
??????validator:?s?=>?['small',?'middle'].includes(s)
????}
??}
};

这个验证函数接受一个 prop,如果 prop 有效或无效,则返回 true 或 false。当单单传入的 true 或 false 来控制某些条件不能满足需求时,我通常使用这个方法来做。

2、使用引号来监听嵌套属性

你可能使用过 watch 来监听过某个属性,但是你可能不知道这一点,我们可以通过使用引号轻松地直接监听嵌套值

watch?{
??'obj.message.name'()?{
????//?...
??}
}

3、知道何时使用 v-if

有时候使用 v-if,往往使用 v-show 来代替,会有更高的性能。

<TableList v-show="isShow" />

当 v-if 被打开或关闭时,它将创建并完全销毁该元素。相反,v-show 将创建该元素并将其留在那里,通过设置其样式为 display: none 来隐藏它,如果你要切换的组件的渲染成本很高,那么这样做会更有效率,特别是对于结构复杂的组件或页面来说,更推荐使用v-show。

反过来说,如果不需要立即执行昂贵的组件,可以使用 v-if,这样它就会跳过渲染它,而使页面的加载速度更快一些。

4、单个作用域插槽的简写

限定范围的插槽非常有趣,但为了使用它们,您还必须使用许多模板标记。幸运的是,有一个简写可以让我们摆脱它,但只有在我们使用单个作用域槽的情况下。

你或许这样使用过

<TableList>
??<template?#header="attrList">
????<TableHeader?v-bind="attrList"?/>
??</template>
</TableList>

不使用 template

<TableList #header="attrList">
??<TableHeader?v-bind="attrList"?/>
</TableList>

简单、直截了当,令人赞叹不已。

5、将局部和全局的 style 混合在一起

通常情况下,在处理样式时,我们希望它们只在当前组件内起作用

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

不过,如果需要的话,也可以添加一个非作用域样式块来添加全局样式

<style>
??/*?全局?*/
??.content?p?{
????font-size:?12px;
??}
</style>
 
<style?scoped>
??/*?在该组件内有效?*/
??.content?{
????background:?green;
??}
</style>

需要注意,全局样式是危险的,难以追踪。但有时,它们是一个完美的逃生舱口,正是你所需要的。

6、重写子组件的样式

Scoped CSS 在保持内容整洁方面非常棒,而且不会将样式引入应用的其他组件中。但有时我们需要覆盖一个子组件的样式,并跳出这个作用域。

Vue 有一个 deep 选择器可以完成以上功能

<style?scoped>
.my-content?>>>?.child-component?{
??font-size:?16px;
}
</style>

注意:如果你使用像 LESS这样的 CSS 预处理器,你可能需要使用 /deep/ 来代替

<style lang="less"?scoped>
.my-content?/deep/?.child-component?{
??font-size:?16px;
}
</style>

7、如何在 Vue 之外创建一个具有响应性的变量

如果你从 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);   //使用ref使其具有响应性

使用 reactive 代替

import?{?reactive?}?from?'vue';
 
//??可以完全在 Vue 组件之外完成
const?externalVariable?=?getValue();
//?reactive?只对对象和数组起作用
const?anotherReactiveVariable?=?reactive(externalVariable);

如果你还在使用 Vue2,你可以使用 observable 而不是 reactive 来实现完全相同的结果

import Vue from 'vue'

//??可以完全在 Vue 组件之外完成
const?externalVariable?=?getValue();
const?anotherReactiveVariable?=?Vue.observable(externalVariable);

8、v-for 中的解构

你是否在v-for 中使用解构?

// users: [{name:'leo',id:'1'},{name:'lion',id:'2'}]
<li
??v-for="{?name,?id?}?in?users"
??:key="id"
>
??{{?name?}}
</li>

更广为人知的是,可以通过使用这样的元组从 v-for 中取出索引

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

当使用一个对象时,可以这样使用 key

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

也可以将这两种方法结合起来,获取 key 以及属性的 index

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

9、在指定范围内循环

v-for 允许我们遍历数组,也允许我们遍历一个范围

<template>
??<ul>
????<li?v-for="num?in?6">Item?{{?num?}}</li>
??</ul>
</template>

渲染结果

Item?1
Item?2
Item?3
Item?4
Item?5
Item?6

这里有个注意点,当我们使用 v-for 遍历一个范围时,它将从 1 开始,以我们指定的数字结束。

10、窃取 prop 类型

我们从一个子组件中复制 prop 类型,只是为了在一个父组件中使用它们。但是往往偷取这些 prop 类型要比仅仅复制它们好得多,什么意思?

举个例子,我们在某个组件中使用了一个 List组件

<template>
??<div>
????<h2>{{?title?}}</h2>
????<List
??????:type="listType"
??????:size="listSize"
      :name="listName"
????/>
??</div>
</template>

为了让它正常工作,我们需要添加正确的 prop 类型,从 List 组件复制

import?List?from?'./List';
export?default?{
??components:?{?List?},
??props:?{
????listType:?{
??????type:?String,
??????required:?true,
????},
????listSize:?{
??????type:?String,
??????default:?'medium',
??????validator:?size?=>?[
????????'small',
????????'medium',
????????'large',
????????'x-large'
??????].includes(size),
????},
????listName:?{
??????type:?String,
??????default:?'list',
????},
????title:?{
??????type:?String,
??????required:?true,
????},
??},
};

看下来,有没有觉得很麻烦?

当 List组件的 prop 类型被更新时,我们肯定会忘记返回这个组件并更新它们。随着时间的推移,当该组件的 prop 类型开始偏离 List组件中的 prop 类型时,就会引入错误。

因此,这就是为什么我们要窃取组件的 prop 类型,具体如下

import?List?from?'./List';
export?default?{
??components:?{?List?},
??props:?{
????...List.props,  // 窃取List的prop,不需要再重复累赘地一一列出
????title:?{
??????type:?String,
??????required:?true,
????},
??},
};

这样是不是简便了很多?

除了在上面的例子中,我们把 List加在每个 prop 名称的开头。所以我们必须做一些额外的工作来实现这一点

import?List?from?'./List';
 
const?listProps?=?{};
 
Object.entries(List.props).forEach((key,?val)?=>?{
??listProps[`list${key.toUpperCase()}`]?=?val;
});
 
export?default?{
??components:?{?List?},
??props:?{
????...listProps,
????title:?{
??????type:?String,
??????required:?true,
????},
??},
};

现在,如果 List组件中的 prop 类型被修改,我们的组件将保持最新状态。但是,如果一个 prop 类型从 List组件中被添加或删除了呢?为了应对这些情况,我们可以使用 v-bind 和一个计算的 prop 来保持动态。

11、检测元素外部(或内部)的单击

当我们需要检测一个点击是发生在一个特定元素 el 的内部还是外部,通常使用的方法

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

??// 检测在el元素的内部还是外部
??if?(el.contains(currtentEl))?{
???// 在el里面点击了
??}?else?{
???// 在el外面点击了
??}
});

12、从组件外部调用一个方法

我们可以从一个组件的外部通过给它一个 ref 用来调用一个方法

<!--?Parent.vue?-->
<template>
??<Child?ref="child"?/>
</template>

//?在该组件调用子组件的方法
this.$refs.child.method();

通常情况下,我们使用 props和 events 在组件之间进行交流。props 被下发到子组件中,而 events 被上发到父组件中

<template>
??<Child
????:tell-me-what-to-do="someInstructions"
????@something-happened="hereIWillHelpYouWithThat"
??/>
</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
  • Watch 被触发,然后 Child 组件调用该方法
  • 子组件发出一个事件,告诉父组件该方法已被成功触发
  • Parent 组件将 trigger 重置为 false,所以我们可以从头再来一次

相反,如果我们在子组件上设置一个 ref,我们可以直接调用该方法(如最开始的方式),我们打破了 “props down, events up” 的规则,我们打破了封装,但是这样做更清晰,更容易理解。

13、监听数组和对象

有时候使用 watcher 不能正确触发,很多情况下是因为我们试图监听数组或对象,但没有将 deep 设置为 true

export?default?{
??name:?'namesChange',
??props:?{
????names:?{
??????type:?Array,
??????required:?true,
????},
??},
??watch:?{
????names:?{
??????//?这将让 Vue 知道要在数组内部寻找,如不添加,则不能进行深度监听
??????deep:?true,
 
??????handler(newVal,oldVal)
????????console.log('The?list?of?names?has?changed!');
??????}
????}
??}
}

在vue3中使用

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

14、template 标签的另一个用途

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-if 条件一致的元素),并将 v-if 提升到模板 template本身,达到优化的目的

<template>
??<div?class="card">
????<img?src="imgPath"?/>
????<h3>
??????{{?title?}}
????</h3>
    <!-- 将 v-if 提升到分组上 -->
????<template?v-if="expanded">
??????<h4>
????????{{?subheading?}}
??????</h4>
??????<div?class="card-content">
????????<slot?/>
??????</div>
??????<SocialShare?/>
????</template>
??</div>
</template>

现在看起来清晰了许多,也更容易理解,而且它在做什么,一目了然。

15、处理错误(和警告)的更好方法

我们可以为 Vue 中的错误和警告提供一个全局的自定义处理程序

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

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

当然你也可以用它们来更优雅地处理错误,以获得更好的用户体验。例如,如果一个错误未被处理,应用程序不会直接崩溃,而是显示一个完整的错误屏幕,提示用户刷新或尝试其他操作。

看到这里,千万不要吝啬你的小手指喔,一键三连走起来~~~

总结

写到这里也结束了,在文章最后放上一个小小的福利,以下为小编自己在学习过程中整理出的一个关于 前端开发 的学习思路及方向。从事互联网开发,最主要的是要学好技术,而学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯,更加需要准确的学习方向达到有效的学习效果。

由于内容较多就只放上一个大概的大纲,需要更及详细的学习思维导图的 点击我的GitHub免费获取
还有免费的 高级web全套视频教程 前端架构 H5 vue node 小程序 视频+资料+代码+面试题!

全方面的web前端进阶实践技术资料,并且还有技术大牛一起讨论交流解决问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值