草庐IT

Vue最佳实践和实用技巧

CRMEB众邦科技 2023-03-28 原文

1.props限制和透传

​​​内自定义限制

props: {
size: {
// 自定义验证函数
validator: (val) => {
return ["small", "medium", "large"].includes(val);
},
}
}
复制代码
这个验证函数接受一个 prop值,验证并返回 true 或 false
透传属性

<!-- 此组件为二次封装的中间组件 -->
<template>
<MyComponent v-bind="$attrs"/>
</template>

<script>
export default {
// $attrs 中的所有属性不自动继承到组件的根元素上
inheritAttrs: false,
}
</script>
复制代码
  • $attrs包含所有透传过来的对象,除显式声明接受的props、emits、slots
  • 如不希望透传下去某些属性,可使用useAttrs()
const attrs = useAttrs();
const filteredAttrs = computed(() => {
return { ...attrs, style: undefined };
});
复制代码
​$attrs​​还可与listeners`包含了父组件传递的事件(不包含.native修饰器),它可以通过v-on="$listeners"转发传入内部组件,进行对事件的监听处理

<MyComponent v-bind="$attrs" v-on="$listeners" />
复制代码
注意: attrs属性上
单个slot透传

<About>
<template #about>
<slot name="about" />
</template>
</About>
复制代码
多个slot透传

<template #[slotName] v-for="(slot, slotName) in $slots" >
<slot :name="slotName"/>
</template>
复制代码
多个slot透传作用域插槽

<template #[slotName]="slotProps" v-for="(slot, slotName) in $slots" >
<slot :name="slotName" v-bind="slotProps"/>
</template>
<!-- Vue2则需要将v-for里面循环的$slots改成$scopedSlots -->
复制代码

2.require.context()和import.meta.glob()批量引入文件

  • webpack统一导入相同路径下的多个组件的方法
const path = require("path");
// 参数一:说明需要检索的目录,参数二:是否检索子目录,参数三::指定匹配文件名的正则表达式
const files = require.context("./components", false, /\.vue$/);
const modules = {};
files.keys().forEach((key) => {const name = path.basename(key, ".vue");modules[name] = files(key).default || files(key);
});
复制代码
  • Vite 支持使用特殊的 import.meta.glob 函数从文件系统导入多个模块:
const modules = import.meta.glob('./src/*.js');

// vite 转译上面后生成的代码
const modules = {
'./src/foo.js': () => import('./src/foo.js'),
'./src/bar.js': () => import('./src/bar.js')
}
复制代码

3.有条件的渲染slot

  • 组件都有一个特殊的$slots对象,包含所有的插槽对象,结构如下:
const $slots = {"default": [{...}],"slotA": [{...}],"slotB": [{...}]
}
复制代码
我们可以使用v-if有条件的渲染slot更加合理,并且我们封装通用组件的时候最好预留个slot更好扩展

<template>
<div>
<div v-if="$slots.default">
<slot />
</div>
<div v-if="$slots.slotA">
<slot name="slotA"/>
</div>
</div>
</template>
复制代码

4.检测点击是否发生在元素内部

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

// `targetEl` 为检测的元素
if (targetEl.contains(clickedEl)) {
// 在"targetEl"内部点击
} else {
// 在"targetEl"之外点击
}
});
复制代码

5.动态组件和递归组件

动态组件:tab切换的时候可使用动态组件动态加载并缓存提供动效

<transition>
<keep-alive>
<!-- :is值必须是全局或者局部注册过的组件 -->
<component :is="currentTab"></component>
</keep-alive>
</transition>
复制代码
递归组件:模板里面自己自己,注意需要设置条件退出,不然会无限渲染,适合嵌套菜单,树形控件等

<div v-if="item.children">
{{ tree.label }}
<!-- 递归调用自身 -->
<tree v-for="(item, index) in tree.children" :tree="item" :key="index"></tree>
</div>
<script>
export default {
// 定义name,以使组件内部递归调用
name: 'tree',
// 接收外部传入的值
props: {
tree: {
type:Array,
default: () => []
}
}
}
</script>
复制代码

6.nextTick

  • 在下次 DOM 更新循环结束之后执行延迟回调
  • 我们可以在修改数据后立即使用此方法获取最新的DOM
mounted(){
this.$nextTick(() => {
this.$refs.inputs.focus(); //通过 $refs 获取dom 并绑定 focus 方法
})
}
复制代码

7.简化 :class 和 v-if 与逻辑

<div :class="$route.name === 'Home' ||$route.name === 'Gallery' ||$route.name === 'Profile'? 'classOnlyOnThesePages': ''"></div>
复制代码
直接最佳写法如下:

<div :class="{classOnlyOnThesePages: ['Home', 'Gallery', 'Profile'].includes($route.name),}"></div>
复制代码

8.全局重用方法

  • 遇到全局可重用的工具方法,例如
class Utils {// 复制一段文字到剪切板copyToClipboard(text) {let copyText = document.createElement("input");document.body.appendChild(copyText);copyText.value = text;copyText.select();document.execCommand("copy");document.body.removeChild(copyText);}
}

export default new Utils();
复制代码
我们可以抽离出来放在整个应用程序都能访问的地方
Vue2:

import Utils from "./utils/utils.js";
// 设置全局方法
Vue.prototype.$utils = Utils;
复制代码
Vue3:

import Utils from "./utils/utils.js";
const app = createApp(App);
// 设置全局方法
app.config.globalProperties.$utils = Utils;
app.mount("#app");
复制代码
接下来任何地方都能愉快的访问啦
this.$utils.copyToClipboard(text);

// Vue3 setup
const { proxy } = getCurrentInstance();
proxy.$utils.copyToClipboard(text);
复制代码

9.局部组件刷新

  • 使用 v-if 方法来控制 router-view 的显示隐藏
<template><div id="app"><router-view v-if="isActive" /></div>
</template>

<script>
export default {name: "App",
// provider给下层组件重刷的方法provide() {return {reload: this.reload,};},data: {
isActive: true,},method: {reload() {this.isActive = false;this.$nextTick(() => {this.isActive = true;});},},
};
</script>
复制代码
需要的页面可注入该方法使用

<script>
export default {inject: ["reload"],methods: {refresh() {this.reload();},},
};
</script>
复制代码
或者直接使用v-if操作该组件

<template><div v-if="isShow"></div>
</template>
<script>
export default {data() {return {isShow: true,};},method: {refresh() {this.isShow = false;this.$nextTick(() => {this.isShow = true;});},},
};
</script>
复制代码
或者借助Vue的diff算法,我们给元素设置一个唯一的Key值然后去改变它

<template><div :key="keyValue"></div>
</template>
<script>
export default {data() {return {keyValue: 0,};},method: {refresh() {this.keyValue++;},},
};
</script>
复制代码

10.组件封装原则

Vue组件的API主要包含三部分:props、event、slot

  • props 为组件接收的参数,最好用对象的写法,可更好设置类型默认值和自定义校验
  • event用于子组件向父组件传递消息
  • slot可以给组件动态插入一些内容或组件,是实现高阶组件的重要途径
组件封装最好还应遵循单向数据流,传递的props仅仅做展示,如需修改,则应该重新初始化一份全新的响应式数据并将props深拷贝后作为初始值

11.错误(警告)处理

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

// Vue 3
const app = createApp(App);
app.config.errorHandler = (err) => {
console.error(err);
};
// Vue 2
Vue.config.errorHandler = (err) => {
console.error(err);
};
复制代码

12.使用template标签分组

template 标签可以在模板内的任何地方使用,可以减少实际嵌套层级,更简化代码逻辑

<template>
<div class="card">
<h3>
{{ title }}
</h3>
<h4 v-if="isShow" class="card-desc">
{{ description }}
</h4>
<div v-if="isShow">
<slot />
</div>
<Home v-if="isShow" />
</div>
</template>
复制代码
上面代码v-if逻辑分组后:

<template>
<div class="card">
<h3>
{{ title }}
</h3>
<template v-if="isShow">
<h4 class="card-desc">
{{ description }}
</h4>
<div>
<slot />
</div>
<Home />
</template>
</div>
</template>
复制代码

13.在 v-for 中解构

在模板中使用 v-for 遍历输出数据,可以使用解构语法

<divv-for="{ id, user } in [{ id: 1, user: 'yun' },{ id: 2, user: 'mu' },]":key="id">{{ user }}
</div>
复制代码

14.全局和局部style混合及样式穿透

<style>
/* 全局有效 */
.content p {
font-size: 12px;
}
</style>

<style scoped>
/* 只在该组件内有效 */
.content {
background: green;
}
</style>
复制代码
有时候我们想跳出scoped这个限定作用域,更改子组件的样式但不会影响全局样式,我们就可以使用深度选择器来完成

<style scoped>
:deep(.ant-card-head-title){
background: green;
}
</style>
复制代码
上面代码会被解析为

[data-v-e44d851a] .ant-card-head-title {
background: green;
}
复制代码
注意:vue版本和预处理不同,深度选择器的写法也会不同,灵活使用即可
15.借用props类型
当我们当前组件使用到了Card组件

<Card :type="Mytype" :color="Mycolor">
复制代码
其中的Mytype和Mycolor是我们通过props接收而来的

import Card from './Card.vue';
export default {
components: { Card },
props: {
Mytype: {
type: String,
required: true,
},
Mycolor: {
type: String,
default: "green",
}
},
};
复制代码
我们可以简写为

import Card from './Card.vue';

const cardProps = {};

Object.entries(Card.props).forEach(([key, value]) => {cardProps[`My${key}`] = value;
});

export default {
components: { Card },
props: {
...cardProps
},
};
完整附件​​点此下载​

有关Vue最佳实践和实用技巧的更多相关文章

  1. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby - 在 Ruby 中编写命令行实用程序 - 2

    我想用ruby​​编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序

  4. 叮咚买菜基于 Apache Doris 统一 OLAP 引擎的应用实践 - 2

    导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵

  5. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  6. 动漫制作技巧如何制作动漫视频 - 2

    动漫制作技巧是很多新人想了解的问题,今天小编就来解答与大家分享一下动漫制作流程,为了帮助有兴趣的同学理解,大多数人会选择动漫培训机构,那么今天小编就带大家来看看动漫制作要掌握哪些技巧?一、动漫作品首先完成草图设计和原型制作。设计草图要有目的、有对象、有步骤、要形象、要简单、符合实际。设计图要一致性,以保证制作的顺利进行。二、原型制作是根据设计图纸和制作材料,可以是手绘也可以是3d软件创建。在此步骤中,要注意的问题是色彩和平面布局。三、动漫制作制作完成后,加工成型。完成不同的表现形式后,就要对设计稿进行加工处理,使加工的难易度降低,并得到一些基本准确的概念,以便于后续的大样、准确的尺寸制定。四、

  7. ruby-on-rails - Rails 中同一个类的多个关联的最佳实践? - 2

    我认为我的问题最好用一个例子来描述。假设我有一个名为“Thing”的简单模型,它有一些简单数据类型的属性。像...Thing-foo:string-goo:string-bar:int这并不难。数据库表将包含具有这三个属性的三列,我可以使用@thing.foo或@thing.bar之类的东西访问它们。但我要解决的问题是当“foo”或“goo”不再包含在简单数据类型中时会发生什么?假设foo和goo代表相同类型的对象。也就是说,它们都是“Whazit”的实例,只是数据不同。所以现在事情可能看起来像这样......Thing-bar:int但是现在有一个新的模型叫做“Whazit”,看起来

  8. ruby-on-rails - 向 Rails 3 添加 Ruby 扩展方法的最佳实践? - 2

    我有一个要在我的Rails3项目中使用的数组扩展方法。它应该住在哪里?我有一个应用程序/类,我最初把它放在(array_extensions.rb)中,在我的config/application.rb中我加载路径:config.autoload_paths+=%W(#{Rails.root}/应用程序/类)。但是,当我转到railsconsole时,未加载扩展。是否有一个预定义的位置可以放置我的Rails3扩展方法?或者,一种预先定义的方式来添加它们?我知道Rails有自己的数组扩展方法。我应该将我的添加到active_support/core_ext/array/conversion

  9. ruby - 最佳原则中的原则 - 2

    我似乎经常遇到一些设计问题,但我不知道是什么是真的很合适。一方面我经常听到我应该限制耦合和坚持单一职责,但当我这样做时,我常常发现它很困难到在需要时将信息获取到程序的一部分。为了例如,classSingerdefinitialize(name)@name=nameendattr:nameend那么Song应该是:classSongdefnew(singer)@singer=singerendend或classSongdefnew(singer_name)@singer_name=singer_nameendend后者耦合性小,按道理应该用。但如果我以后发现宋有什么需要了解更多歌手,我的

  10. ruby-on-rails - 与 ActiveMerchant 一起使用的最佳支付网关是什么? - 2

    我需要使用ActiveMerchant库在我们的一个Rails应用程序中设置支付解决方案。尽管这个问题非常主观,但人们对主要网关(BrainTree、Authorize.net等)的体验如何?它必须:处理定期付款。有能力记入个人帐户。能够取消付款。有办法存储用户的付款详细信息(例如Authotize.netsCIM)。干杯 最佳答案 ActiveMerchant很棒,但在过去一年左右的时间里,我在使用它时发现了一些问题。首先,虽然某些网关可能会得到“支持”——但并非所有功能都包含在内。查看功能矩阵以确保完全支持您选择的网关-http

随机推荐