2、vue改良的五个方法

1. 化繁为简的Watchers

created(){
  this.fetchPostList()
},
watch: {
  searchInputValue(){
    this.fetchPostList()
  }
}

组件创建的时候我们获取⼀次列表,同时监听input框,每当发⽣变化的时候重新获取⼀次筛选后的列表这个场景很常见,有没有办法优化⼀下呢?
招式解析:
⾸先,在watchers中,可以直接使⽤函数的字⾯量名称;其次,声明immediate:true表⽰创建组件时⽴马执⾏⼀次。

watch: {
  searchInputValue:{
    handler: 'fetchPostList',
    immediate: true
  }
}

2. 一劳永逸的组件注册

场景还原:

<BaseInput
  v-model="searchText"
  @keydown.enter="search"
/>
<BaseButton @click="search">
  <BaseIcon name="search"/>
</BaseButton>

<script>
import BaseButton from './baseButton'
import BaseIcon from './baseIcon'
import BaseInput from './baseInput'
export default {
  components: {
    BaseButton,
    BaseIcon,
    BaseInput
  }
}
</script>

我们写了⼀堆基础UI组件,然后每次我们需要使⽤这些组件的时候,都得先import,然后声明components,很繁琐!秉持能偷懒就偷懒的原则,我们要想办法优化!

招式解析:
我们需要借助⼀下神器webpack,使⽤ require.context()⽅法来创建⾃⼰的(模块)上下⽂,从⽽实现⾃动动态require组件。
这个⽅法需要3个参数:要搜索的⽂件夹⽬录、是否还应该搜索它的⼦⽬录、以及⼀个匹配⽂件的正则表达式。
我们在components⽂件夹添加⼀个叫global.js的⽂件,在这个⽂件⾥借助webpack动态将需要的基础组件统统打包进来。

import Vue from 'vue'
function capitalizeFirstLetter(string) {
  return string.charAt(0).toUpperCase() + string.slice(1)
}
const requireComponent = require.context(
  '.', false, /\.vue$/
   //找到components⽂件夹下以.vue命名的⽂件
)
requireComponent.keys().forEach(fileName => {
  const componentConfig = requireComponent(fileName)
  const componentName = capitalizeFirstLetter(
    fileName.replace(/^\.\//, '').replace(/\.\w+$/, '')
    //因为得到的filename格式是: './baseButton.vue', 所以这⾥我们去掉头和尾,只保留真正的⽂件名
  )
  Vue.component(componentName, componentConfig.default || componentConfig)
})

最后我们在main.jsimport 'components/global.js',然后我们就可以随时随地使⽤这些基础组件,⽆需⼿动引⼊了。

3. 釜底抽薪的 router Key

场景还原:
下⾯这个场景真的是伤透了很多程序员的⼼…先默认⼤家⽤的是Vue-router来实现路由的控制。
假设我们在写⼀个博客⽹站,需求是从/post-page/a,跳转到/post-page/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){
  }
}

bug是解决了,可每次这么写也太不优雅了吧?秉持着能偷懒则偷懒的原则,我们希望代码这样写:

data() {
  return {
    loading: false,
    error: null,
    post: null
  }
},
created () {
  this.getPost(this.$route.params.id)
},
methods () {
  getPost(postId) {
    // ...
  }
}

招式解析:
那要怎么样才能实现这样的效果呢,答案是给router-view添加⼀个unique的key,这样即使是公⽤组件,只要url变化了,就⼀定会重新创建这个组件。(虽然损失了⼀丢丢性能,但避免了⽆限的bug)。
同时,注意我将key直接设置为路由的完整路径,⼀举两得。

4. 无所不能的render函数

场景还原:
vue要求每⼀个组件都只能有⼀个根元素,当你有多个根元素时,vue就会给你报错

<template>
  <li
    v-for="route in routes"
    :key="route.name"
  >
    <router-link :to="route">
      {{ route.title }}
    </router-link>
  </li>
</template>

ERROR - Component template should contain exactly one root element.
If you are using v-if on multiple elements, use v-else-if to chain them instead.

招式解析:
那有没有办法化解呢,答案是有的,只不过这时候我们需要使⽤render()函数来创建HTML,⽽不是template
其实⽤js来⽣成html的好处就是极度的灵活功能强⼤,⽽且你不需要去学习使⽤vue的那些功能有限的指令API,⽐如v-for, v-if。(reactjs就完全丢弃了template)

functional: true,
render(h, { props }) {
  return props.routes.map(route =>
    <li key={route.name}>
      <router-link to={route}>
        {route.title}
      </router-link>
    </li>
  )
}

5. 无招胜有招的高阶组件

划重点:这⼀招威⼒⽆穷,请务必掌握
当我们写组件的时候,通常我们都需要从⽗组件传递⼀系列的props到⼦组件,同时⽗组件监听⼦组件emit过来的⼀系列事件。举例⼦:

//⽗组件
<BaseInput 
    :value="value"
    label="密码" 
    placeholder="请填写密码"
    @input="handleInput"
    @focus="handleFocus>
</BaseInput>
//⼦组件
<template>
  <label>
    {{ label }}
    <input
      :value="value"
      :placeholder="placeholder"
      @focus=$emit('focus', $event)"
      @input="$emit('input', $event.target.value)"
    >
  </label>
</template>

有下⾯⼏个优化点:

  1. 每⼀个从⽗组件传到⼦组件的props,我们都得在⼦组件的Props中显式的声明才能使⽤。这样⼀来,我们的⼦组件每次都需要申明⼀⼤堆props,
    ⽽类似placeholer这种dom原⽣的property我们其实完全可以直接从⽗传到⼦,⽆需声明。⽅法如下:
<input
  :value="value"
  v-bind="$attrs"
  @input="$emit('input', $event.target.value)"
>

$attrs包含了⽗作⽤域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。
当⼀个组件没有声明任何 prop 时,这⾥会包含所有⽗作⽤域的绑定,并且可以通过 v-bind="$attrs" 传⼊内部组件——在创建更⾼层次的组件时⾮常有⽤。

  1. 注意到⼦组件的@focus="$emit('focus', $event)"
    其实什么都没做,只是把event传回给⽗组件⽽已,那其实和上⾯类似,我完全没必要显式地申明:
<input
  :value="value"
  v-bind="$attrs"
  v-on="listeners"
>
<script>
export default {
  computed: {
    listeners() {
      return {
        ...this.$listeners,
        input: event => 
          this.$emit('input', event.target.value)
      }
    }
  }
}
</script>

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

  1. 需要注意的是,由于我们input并不是BaseInput这个组件的根节点,⽽默认情况下⽗作⽤域的不被认作 props 的特性绑定将会“回退”且作为普通的 HTML 特性应⽤在⼦组件的根元素上。
    所以我们需要设置inheritAttrs:false,这些默认⾏为将会被去掉, 以上两点的优化才能成功。

作者:云梦来了资料集合
链接:https···
来源:百度文库
仅作学习使用,侵删

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值