vue的实用技巧

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)
    }
  }
}

attrspropclassstylepropvbind="attrs包含了父作用域中不作为prop被识别(且获取)的特性绑定(class和style除外)。当一个组件没有声明任何prop时,这里会包含所有父作用域的绑定,并且可以通过v-bind="attrs"传入内部组件。

listeners.nativevon von="listeners包含父作用域中的(不含.native修饰器的)v-on 事件监听器。它可以通过 v-on="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>


文章作者: 徐辉波
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 徐辉波 !
评论
 上一篇
每日一题 每日一题
第一题: ['1', '2', '3'].map(parseInt) 解析 执行结果:[1, NaN, NaN],此题需要注意以下两点 array.map(function(currentValue,index,arr), thisV
2019年04月21日
下一篇 
Vue之keep-alive页面前进刷新后退缓存 Vue之keep-alive页面前进刷新后退缓存
需求:现有四个页面:分类列表页A,表格列表页B,列表搜索页C,详情页D,需要实现以下链路前进刷新后退缓存 A—>B—>C B刷新 C—>B—>A B缓存 A—>B—>D B刷新 D—>B—
2019年04月20日
  目录