一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情。
我们平时在vue项目的开发中如果可以用到下面这几个小技巧,可能会达到事半功倍的效果
可以去繁从简的template–render函数
有时候项目中template里存在一值多判断,如果按照下方代码书写业务逻辑,代码冗余且杂乱。
<template>
<div>
<h1 v-if="level === 1">
<slot></slot>
</h1>
<h2 v-else-if="level === 2">
<slot></slot>
</h2>
<h3 v-else-if="level === 3">
<slot></slot>
</h3>
<h4 v-else-if="level === 4">
<slot></slot>
</h4>
<h5 v-else-if="level === 5">
<slot></slot>
</h5>
<h6 v-else-if="level === 6">
<slot></slot>
</h6>
</div>
</template>
<script>
export default {
data() {
return {}
},
props: {
level: {
type: Number,
required: true,
},
},
}
</script>
现在使用 render 函数重写上面的例子:
<script>
export default {
props: {
level: {
require: true,
type: Number,
}
},
render(createElement) {
return createElement('h' + this.level, this.$slots.default);
}
};
</script>
灵活的组件注册方式
组件使用前,需要引入后再注册:
import BaseButton from './baseButton'
import BaseIcon from './baseIcon'
import BaseInput from './baseInput'
export default {
components: {
BaseButton,
BaseIcon,
BaseInput
}
}
现在 BaseButton、 BaseIcon和BaseInput都可以在模板中使用了:
<BaseInput
v-model="searchText"
@keydown.enter="search"
/>
<BaseButton @click="search">
<BaseIcon name="search"/>
</BaseButton>
但如果组件多了后,每次都要先导入每个你想使用的组件,然后再注册组件,便会新增很多代码量,这时我们就需要借助一下webpack的require.context() 方法来创建自己的(模块)上下文,从而实现自动动态require组件。这个方法需要3个参数:要搜索的文件夹目录,是否还应该搜索它的子目录,以及一个匹配文件的正则表达式。 我们先在components文件夹(这里面都是些高频组件)添加一个叫global.js的文件,在这个文件里使用require.context 动态将需要的高频组件统统打包进来。然后在main.js文件中引入global.js的文件。
import Vue from 'vue'
function changeStr (str) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
const requireComponent = require.context('./', false, /\.vue$/)
// 查找同级目录下以vue结尾的组件
const install = () => {
requireComponent.keys().forEach(fileName => {
let config = requireComponent(fileName)
console.log(config) // ./child1.vue 然后用正则拿到child1
let componentName = changeStr(
fileName.replace(/^\.\//, '').replace(/\.\w+$/, '')
)
Vue.component(componentName, config.default || config)
})
}
export default {
install // 对外暴露install方法
}
// main.js
import index from './components/global.js'
Vue.use(index)
最后我们就可以随时随地在页面中使用这些高频组件,无需再手动一个个引入了。
隐藏的大招–hook
开发过程中我们有时候要创建一个定时器,在组件被销毁之前,这个定时器也要销毁。代码如下:
mounted() {
// 创建一个定时器
this.timer = setInterval(() => {
// ......
}, 500);
},
// 销毁这个定时器。
beforeDestroy() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
}
这种写法有个很明显的弊端:定时器timer的创建和清理并不是在一个地方,这样很容易导致忘记去清理! 我们可以借助hook对代码整合,这样代码也更容易维护了:
mounted() {
let timer = setInterval(() => {
// ......
}, 500);
this.$once("hook:beforeDestroy", function() {
if (timer) {
clearInterval(timer);
timer = null;
}
});
}
在Vue组件中,可以用过once去监听所有的生命周期钩子函数,如监听组件的updated钩子函数可以写成 this.$on(‘hook:updated’, () => {})。 hook除了上面的运用外,还可以外部监听组件的生命周期函数。在某些情况下,我们需要在父组件中了解一个子组件何时被创建、挂载或更新。 比如,如果你要在第三方组件 CustomSelect 渲染时监听其 updated 钩子,可以通过@hook:updated来实现:
<template>
<!--通过@hook:updated监听组件的updated生命钩子函数-->
<!--组件的所有生命周期钩子都可以通过@hook:钩子函数名 来监听触发-->
<custom-select @hook:updated="doSomething" />
</template>
<script>
import CustomSelect from "../components/custom-select";
export default {
components: {
CustomSelect
},
methods: {
doSomething() {
console.log("custom-select组件的updated钩子函数被触发");
}
}
};
</script>
简单暴力的router key
我们在项目开发时,可能会遇到这样问题:当页面切换到同一个路由但不同参数地址时,比如/detail/1,跳转到/detail/2,页面跳转后数据竟然没更新?路由配置如下:
{
path: "/detail/:id",
name:"detail",
component: Detail
}
这是因为vue-router会识别出两个路由使用的是同一个组件从而进行复用,并不会重新创建组件,而且组件的生命周期钩子自然也不会被触发,导致跳转后数据没有更新。那我们如何解决这个问题呢?我们可以为router-view组件添加属性key,例子如下:
<router-view :key="$route.fullpath"></router-view>
这种办法主要是利用虚拟DOM在渲染时候通过key来对比两个节点是否相同,如果key不相同,就会判定router-view组件是一个新节点,从而先销毁组件,然后再重新创建新组件,这样组件内的生命周期会重新触发。
动态指令参数
Vue的最酷功能之一是可以将指令参数动态传递给组件。我们可以用方括号括起来的 JavaScript 表达式作为一个指令的参数:
<a v-bind:[attributeName]="url"> 这是个链接 </a>
这里的 attributeName 会被作为一个 JavaScript 表达式进行动态求值,求得的值将会作为最终的参数来使用。同样地,你可以使用动态参数为一个动态的事件名绑定处理函数:
<a v-on:[eventName]="doSomething"> 这是个链接 </a>
接下来我们看个例子:假设你有一个按钮,在某些情况下想监听单击事件,在某些情况下想监听双击事件。这时动态指令参数派上用场:
<template>
<div>
<aButton @[someEvent]="handleSomeEvent()" />
</div>
</template>
<script>
export default {
data () {
return {
someEvent: someCondition ? "click" : "dbclick"
}
},
methods: {
handleSomeEvent () {
// handle some event
}
}
}
</script>