1.善用watch的immediate属性
通常在项目中有的需求是这样子的,项目初始化的时候执行一次,然后监听它的变化,在执行
created (){
this.fetchPostList()
},
watch: {
searchInputValue(){
this.fetchPostList()
}
}
上面的代码我们可以简写如下:
watch: {
searchInputValue:{
handler: 'fetchPostList',
immediate: true
}
}
2.router key组件刷新
我们在使用vue-router来实现路由的控制,假设我们的需求是从/post-haorooms/a跳转到/post-haorooms/b,发现页面跳转后数据没有更新,原因是vue-router发现这是同一个组件,然后会复用这个组件,所以created函数里写的方法压根就没有执行。
通常解决方案是监听$route的变化来初始化数据,如下
data () {
return {
loading: false,
error: null,
post: null
}
},
watch: {
'$route': {
handler: 'resetData',
immediate: true
}
},
methods: {
resetData () {
this.loading = false
this.error = null
this.post = null
this.getPost(this.$route.params.id)
},
getPost (id) {
}
}
问题是解决了,但是每次这样子写很不优雅,我们希望代码是这样子的:
data () {
return {
loading: false,
error: null,
post: null
}
},
created () {
this.getPost(this.$route.params.id)
},
methods () {
getPost (postId) {
// ...
}
}
解决方案:给router-view添加一个唯一的key,即使是公用的组件,只要url变化了,就一定会重新创建这个组件
<router-view :key="$route.fullpath" />
注:这个一般是应用在子路由里面,避免大量重绘,假设app.vue根目录添加这个属性,每次点击改变地址都会重绘,得不偿失。
3.唯一根元素
模板中只能有一个根元素,如果是两个的话,就会报错,示例:
<template>
<li
v-for="route in routes"
:key="route.name"
>
<router-link :to="route">
</router-link>
</li>
</template>
我们通常是使用一个div或者其他元素,将要的元素包裹起来,避免这种问题
<template>
<ul>
<li
v-for="route in routes"
:key="route.name"
>
<router-link :to="route">
</router-link>
</li>
</ul>
</template>
但是页面上会多一个 div dom节点,这不是我们想要的,其实我们可以用render函数去渲染:
functional: true,
render(h, { props }) {
return props.routes.map(route =>
<li key={route.name}>
<router-link to={route}>
{route.title}
</router-link>
</li>
)
}
4.组件包装、事件属性穿透问题
当我们在写组件的时候,通常我们都需要从父组件传递一系列的props到子组件,同时父组件监听子组件emit过来的一系列事件。举例:
//父组件
<BaseInput
:value="value"
label="密码"
placeholder="请填写密码"
@input="handleInput"
@focus="handleFocus>
</BaseInput>
//子组件
<template>
<label>
<input
:value="value"
:placeholder="placeholder"
@focus=$emit('focus', $event)"
@input="$emit('input', $event.target.value)"
>
</label>
</template>
这样子写很不精简,很多属性和事件都需要手动定义,我们可以这样写:
<input
:value="value"
v-bind="$attrs"
v-on="listeners"
>
computed: {
listeners() {
return {
...this.$listeners,
input: event =>
this.$emit('input', event.target.value)
}
}
}
attrs"传入内部组件。
listeners" 传入内部组件
5.css深度选择器
如果子组件的css样式中,使用 scoped 属性的话,父组件是无法更改其样式的,通常是这样子操作:
<style>
.parent {
color: #fff;
}
</style>
<style>
.parent .child {
color:#ccc
}
</style>
上面这样子操作是可以解决问题,但是如果class类名一样,是会影响到全局的,可以通过css的深度选择器去解决
<style>
.parent >>> .child {
color:#ccc
}
</style>
这样子的话 .child 就不会被添加上属性了,我们就可以修改其css样式
如果你使用了预处理器,如less 或者 sass,可以这样子去写
<style>
.parent /deep/ .child {
color:#ccc
}
</style>
6.清除定时器的方法
通常我们写个定时器,都是在组件销毁的生命周期里面清除
beforeDestroy () {
clearInterval(this.timer);
this.timer = null
}
可以使用$once优化,可以少声明data的属性
const timer = setInterval(() =>{
// 某些定时器操作
}, 500)
// 通过$once来监听定时器,在beforeDestroy钩子可以被清除。
this.$once('hook:beforeDestroy', () => {
clearInterval(timer)
})
7.data初始化
因为 props 要比 data先完成初始化,所以可以利用这一点给 data 初始化一些数据进去:
export default {
data () {
return {
buttonSize: this.size
}
},
props: {
size: String
}
}
除了以上,子组件的 data 函数也可以有参数,且该参数是当前的实例对象。所以我们可以利用这一点,改写上面的代码:
export default {
data (vm) {
return {
buttonSize: vm.size
}
},
props: {
size: String
}
}
8.Lifecycle hook
生命周期可以是一个数组的类型,且数组中的函数会依次执行。
export default {
...
created: [
function one () {
console.log(1)
},
function two () {
console.log(2)
}
]
...
}
生命周期钩子还可以作用于DOM元素上,利用这一点,我们可以用父组件中的方法来初始化子组件的生命周期钩子:
<!-- Child.vue -->
<template>
<h3>I'm child!</h3>
</template>
<!-- Parent.vue -->
<template>
<child @hook:created="handleChildCreated"></child>
</template>
<script>
import Child from './child.vue'
export default {
components: [ Child ],
methods: {
handleChildCreated () {
console.log('handle child created...')
}
}
}
</script>