21

10个简单的技巧让你的 vue.js 代码更优雅

 3 years ago
source link: https://mp.weixin.qq.com/s?__biz=Mzg5NDAyNjc2MQ%3D%3D&%3Bmid=2247486468&%3Bidx=1&%3Bsn=78a28ab00e43ca89baf7a3615f94f967
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

首先送上一个福利,公众号后台回复" 微信状态 "即可获得 程序员专属状态。

前言:tangerine:

作为深度代码洁癖,我们都希望能写出简单高效的代码,让我们的代码看起来更加优雅,让我们抛弃繁杂的代码,一起开启简单的旅程~~

slots 新语法向 3.0 看齐

使用带有“#”的新命名插槽缩写语法,在Vue 2.6.0+中可用:+1:

yqEjmyV.png!mobile

举个例子:

构建插槽时,最好规划一下布局。这就是我的文章布局。 mIfAV32.png!mobile 构建插槽与构建组件没有什么不同。本质上,插槽是具有超强功能的组件,让我们细分一下上面的布局,组件的外观如下:

<!-- TidbitPage.vue -->
<template>
<article-layout>

<template #articleHeader>
<h1>I am the header</h1>
</template>

<template #articleContent>
<p>I am the content</p>
</template>

<template #articleFooter>
<footer>I am the footer</footer>
</template>

<template #side>
<aside>I am the side stuff</aside>
</template>

<template #banner>
<div>I am the banner</div>
</template>

</article-layout>
<template>

动态指令参数

指令参数现在可以接受动态JavaScript表达式 动态参数值应该是字符串,但允许 null 作为一个明确指示应该删除绑定的特殊值,那将会很方便。任何其他非字符串值都可能出错,并会触发警告。(仅适用于 v-bind和v-on

<div v-bind:[attr]="attributeName"></div>
//简写
<div :[attr]="attributeName"></div>

这里的 attributeName 会被作为一个JavaScript表达式进行动态求值,求得的值将会作为最终的参数来使用。例如,如果你的 Vue 实例有一个 data 属性 attributeName ,其值为 href ,那么这个绑定将等价于 v-bind:href

同样地,你可以使用动态参数为一个动态的事件名绑定处理函数:

<button v-on:[eventName]="handler"></button>
//简写
<button @[eventName]="handler"></button>

eventName 的值为 focus 时, v-on:[eventName] 将等价于 v-on:focus

同样可以适用于插槽绑定:

<my-component>
<template v-slot:[slotName]>
Dynamic slot name
</template>
</my-component>
//简写
<foo>
<template #[name]>
Default slot
</template>
</foo>

动态参数预期会求出一个字符串,异常情况下值为 null。这个特殊的 null 值可以被显性地用于移除绑定。任何其它非字符串类型的值都将会触发一个警告。

<!-- 这会触发一个编译警告 且 无效 -->
<a v-bind:['foo' + bar]="value"> ... </a>

变通的办法是使用没有空格或引号的表达式,或用计算属性替代这种复杂表达式。

@hook那些事

处理组件内定时器的步骤。通常我们一般都是这样操作的:

<script>
export default {
mounted() {
this.timer = setInterval(() => { ... }, 1000);
},
beforeDestroy() {
clearInterval(this.timer);
}
};
</script>

但是其实更好的做法是:

<script>
export default {
mounted() {
const timer = setInterval(() => { ... }, 1000);
this.$once('hook:beforeDestroy', () => clearInterval(timer);)
}
};
</script>

设想一个场景 如果我们需要在数据渲染到页面的之前让页面 loadingmounted 之后停止 loadingbeforeUpdata 时开始 loadingupdatad 之后停止 loading

最简单的方法就是改写组件的生命周期函数,使其在 mounted/beforeUpdata /updatad 时通知父组件显示或者隐藏 loading。

这样做显示不好,因为侵入了自组件的逻辑,增加的逻辑也和组件本身的功能好不关联。最好的办法就是使用 v-on="hook:xxx" 的方式:

<v-chart
@hook:mounted="loading = false"
@hook:beforeUpdated="loading = true"
@hook:updated="loading = false"
:data="data"
/>

这样,就实现了对子组件生命周期的监听。对任意的组件都有效果,包括引入的第三方组件。

vue中的 $props$attrs$listeners (可用来二次封装组件)

$props:当前组件接收到的 props 对象。Vue 实例代理了对其 props 对象属性的访问。

假如有个input输入框。我们有很多的原生属性,比如:name、placeholder、disabled等等。我们如果都定义在props显示接收,未免太过繁琐。所以官网出现了: v-bind="$props" 这样的操作。如果父组件传递很多的原生属性,那么我们在子组件中直接可以:

//good
<input v-bind="$props">

//bad
//而不是下面这样,如果很多的属性就特别繁琐
<input :name="name" :placeholder="placeholder" :disabled="disabled">

我们可以利用以下方式 $attrs 将原生属性直接传递给子组件,这是Vue在2.4.0新增的属性,包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。

<input
v-bind="$attrs"
:value="value"
@focus=$emit('focus', $event)"
@input="$emit('input', $event.target.value)"
>

$listeners :包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用。

如果子组件不在父组件的根目录下,则可以将所有事件侦听器从父组件传递到子组件,如下所示:

<template>
<div>
...
<childComponent v-on="$listeners" />...
</div>
</template>

响应式数据(2.6.0新增)

我们习惯于用Vuex去解决状态的共享问题,但是在小项目中使用就会有增大代码体积和将代码复杂化的烦恼,所以在后来的版本中Vue新增

Vue.observable( object ) 让一个对象可响应,Vue 内部会用它来处理 data 函数返回的对象。

返回的对象可以直接用于渲染函数和 计算属性 内,并且会在发生改变时触发相应的更新。也可以作为最小化的跨组件状态存储器,用于简单的场景:

官方示例:

const state = Vue.observable({ count: 0 })

const Demo = {
render(h) {
return h('button', {
on: { click: () => { state.count++ }}
}, `count is: ${state.count}`)
}
}

jsx模板组件

以下面的一组状态判断按钮为例,我们很容易就下意识地在模板内写下这种代码

<button v-if="status === 1" class="btn1" :class="status === 1" @click="">未开始</button>
<button v-if="status === 2" class="btn2" :class="status === 2" @click="">进行中</button>
<button v-if="status === 3" class="btn3" :class="status === 3" @click="">可领取</button>
<button v-if="status === 4" class="btn4" :class="status === 4" @click="">已领取</button>

但是如果我们利用渲染函数可以将上面的代码抽取成优雅的使用组件

<!DOCTYPE html>
<html lang="en">

<body>
<div id="app">
<child :status="status"></child>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('child', {
props: {
status: {
type: Number,
required: true
}
},
render(createElement) {
const innerHTML = ['未开始', '进行中', '可领取', '已领取'][this.status]
return createElement('button', {
class: {
active: this.status
},
attrs: {
id: 'btn'
},
domProps: {
innerHTML
},
on: {
click: () => console.log(this.status)
}
})
}
})
var app = new Vue({
el: '#app',
data: {
status: 0
}
})
</script>
</body>

</html>

我们将所有的逻辑封装进渲染函数内,外部只需要传递一个状态参数即可改变

<child :status="status"></child>

动态组件

通过 Vue 的 元素加一个特殊的 is attribute 可以实现动态组件的效果

如图,这是一个v-for渲染的列表(只是目前这个版块才刚开始做,目前只有一个),圆圈内的就是一个组件,也就是要v-for动态组件

emU3Yve.png!mobile

实际使用

一开始就是基本的组件引入了

import ColorIn from '@/components/Magic/ColorIn.vue'
import LineIn from "@/components/Magic/LineIn.vue";
import Header from "@/components/Magic/Header.vue";
import Footer from "@/components/Magic/Footer.vue";

export default{
components:{
ColorIn,
LineIn,
Header,
Footer
}
}

接下来就是动态v-for动态组件的使用, componentList:['ColorIn','LineIn','Header','Footer'] 使用下面的代码即可将代码依次循环

<component v-for="(item,index) in componentList" :key="index" :is="item"></component>

编译以后的效果就是

<ColorIn></ColorIn>
<LineIn></LineIn>
<Header></Header>
<Footer></Footer>

Vue.filter

简单介绍一下过滤器,顾名思义,过滤就是一个数据经过了这个过滤之后出来另一样东西,可以是从中取得你想要的,或者给那个数据添加点什么装饰,那么过滤器则是过滤的工具。例如,从['abc','abd','ade']数组中取得包含‘ab’的值,那么可通过过滤器筛选出来‘abc’和‘abd’;把‘Hello’变成‘Hello World’,那么可用过滤器给值‘Hello’后面添加上‘ World’;或者把时间节点改为时间戳等等都可以使用过滤器。

场景:时间戳转化成年月日这是一个公共方法,所以可以抽离成过滤器使用

// 使用
// 在双花括号中
{{ message | capitalize }}

// 在 `v-bind` 中
<div v-bind:id="rawId | formatId"></div>

// 全局注册
Vue.filter('stampToYYMMDD', (value) =>{
// 处理逻辑
})

// 局部注册
filters: {
stampToYYMMDD: (value)=> {
// 处理逻辑
}
}

// 多个过滤器全局注册
// /src/common/filters.js
let dateServer = value => value.replace(/(\d{4})(\d{2})(\d{2})/g, '$1-$2-$3')
export { dateServer }
// /src/main.js
import * as custom from './common/filters/custom'
Object.keys(custom).forEach(key => Vue.filter(key, custom[key]))

.sync 语法糖

sync 就是为了实现prop 进行“双向绑定”仅此而已(父对子,子对父,来回传)

当你有需要在子组件修改父组件值的时候这个方法很好用,它的实现机制和v-model是一样的。

VbmeAzY.png!mobile

利用 object.freeze 提升性能

Object.freeze() 方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。 freeze() 返回和传入的参数相同的对象。

比方我们需要渲染一个非常大的数组对象,例如用户列表,对象列表,文章列表等等。

export default {
data: () => ({
users: {}
}),
async created() {
const users = await axios.get("/api/users");
this.users = users;
}
};

vue 会将 data 对象中的所有的属性加入到 vue 的响应式系统中,当这些属性的值发生改变时,视图将会产生 响应,若对象的体积比较大,会消耗很多浏览器解析时间。

所以我们可以通过减少数据的响应式转换来提供前端的性能。

export default {
data: () => ({
users: {}
}),
async created() {
const users = await axios.get("/api/users");
this.users = Object.freeze(users);
}
};

:heart: 交流讨论

欢迎关注公众号  秋风的笔记 ,主要记录日常中觉得有意思的工具以及分享开发实践,保持深度和专注度。

最近整理了一些大厂的相关内推信息,基本上都是部门直招,包含 美团、字节、阿里飞猪、腾讯、快手、58集团、哈喽出行、京东 等多个公司,有兴趣的可以公众号后台回复 "面试" ,加入群聊,一起交流面试学习~

Avi6Vj6.jpg!mobile

点赞、在看、分享 是对作者最大的支持:heart:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK