前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Vue 开发技巧总结

Vue 开发技巧总结

原创
作者头像
Krry
修改2020-08-28 10:11:59
5870
修改2020-08-28 10:11:59
举报
文章被收录于专栏:KrryblogKrryblog

博客地址:https://ainyi.com/95

本人玩了 Vue 两年多,在此总结一下开发时的一些技巧和方法

自定义组件 v-model

v-model 是 Vue 提供的一个语法糖,它本质上是由 value 属性 + input 事件组成的(都是原生的默认属性)

自定义组件中,可以通过传递 value 属性并监听 input 事件来实现数据的双向绑定

自定义组件

代码语言:txt
复制
<template>
  <div>
    <input :value="value" @input="$_handleInput" />
  </div>
</template>

<script>
export default {
  props: {
    value: {
      type: String,
      default: ''
    }
  },
  data() {
    return {}
  },
  methods: {
    $_handleInput(e) {
      this.$emit('input', e.target.value)
    }
  }
}
</script>

父组件调用

代码语言:txt
复制
<template>
  <div class="home">
    <krry-input v-model="say"></krry-input>
  </div>
</template>

<script>
export default {
  name: 'Home',
  components: {
    KrryInput: () => import('@/components/KrryInput')
  },
  data() {
    return {
      say: 'haha'
    }
  }
}
</script>

函数式组件

简单说一下函数式组件

函数式组件就是函数是组件。使用过 React 的同学,应该不会对函数式组件感到陌生

函数式组件,我们可以理解为==没有内部状态==,==没有生命周期钩子函数==,==没有 this==(不需要实例化的组件)

由于它像函数一样轻巧,没有实例引用,所以渲染性能提高了不少

在日常开发中,经常会开发一些纯展示性的业务组件,比如一些详情页面,列表界面等,它们有一个共同的特点是只需要将外部传入的数据进行展现,不需要有内部状态,不需要在生命周期钩子函数里面做处理,这时候你就可以考虑使用函数式组件

代码语言:txt
复制
export default {
  // 通过配置 functional 属性指定组件为函数式组件
  functional: true,
  // 组件接收的外部属性,也可无需显式声明 props
  props: {
    avatar: {
      type: String
    }
  },
  /**
   * 渲染函数
   * @param {*} h
   * @param {*} context 函数式组件没有 this, props, slots 等,都在 context 上面挂着
   */
  render(h, context) {
    const { props } = context
    if (props.avatar) {
      return <img src={props.avatar}></img>
    }
    return <img src="default-avatar.png"></img>
  }
}

使用函数式组件的原因:

  1. 最主要最关键的原因是函数式组件不需要实例化,无状态,没有生命周期,所以渲染性能要好于普通组件
  2. 函数式组件结构比较简单,代码结构更清晰

函数式组件与普通组件的区别

  1. 函数式组件需要在组件上声明==functional==
  2. 函数式组件不需要实例化,所以没有 this,==this==通过==render==函数的第二个参数来代替
  3. 函数式组件没有生命周期钩子函数,不能使用计算属性、watch 等等
  4. 函数式组件不能通过 $emit 对外暴露事件,调用事件只能通过==context.listeners.click==的方式调用外部传入的事件
  5. 因为函数式组件是没有实例化的,所以在外部通过==ref==去引用组件时,实际引用的是 HTMLElement
  6. 函数式组件的==props==可以不用显式声明,所以没有在==props==里面声明的属性都会被自动隐式解析为 prop,而普通组件所有未声明的属性都被解析到 $attrs 里面,并自动挂载到组件根元素上面(可以通过 inheritAttrs 属性禁止)

模板语法声明函数式组件

在 Vue2.5 之前,使用函数式组件只能通过 JSX 的方式,在之后可以通过模板语法来声明函数式组件

代码语言:txt
复制
<!-- 在 template 上面添加 functional 属性 -->
<template functional>
  <img :src="props.avatar" />
</template>
<!-- 上面第 6 点,可不用显示声明 props -->

事件参数 $event

$event 是事件对象的一个特殊变量。它在某些场景下为复杂的功能提供了更多的可选参数

代码语言:txt
复制
<template>
  <img src="text.jpg" @click="handleClick($event)" />
</template>

<script>
export default {
  methods: {
    handleClick (e) {
      console.log(e)
    }
  }
}
</script>

EventBus

声明一个全局 Vue 实例变量 EventBus,把所有的通信数据、事件监听都存储到这个变量上

类似于 Vuex,但这种方式一般适用于小的项目

原理就是利用 on、emit 并实例化一个全局 vue 实现数据共享

可以实现平级、嵌套组件传值;但是对应的事件名 eventTarget 必须是全局唯一的

代码语言:txt
复制
// 在 main.js
Vue.prototype.$eventBus = new Vue()

// 传值组件
this.$eventBus.$emit('eventTarget','这是eventTarget传过来的值')

// 接收组件
this.$eventBus.$on('eventTarget', v => {
  console.log('eventTarget', v)
})

也可以新建一个 bus.js 文件

代码语言:txt
复制
import Vue from 'vue'
export default new Vue()

在要通信的组件导入此文件,进行监听或发送

代码语言:txt
复制
import Bus from '@/bus'

// 组件1
Bus.$emit('operateMusic', id)

// 组件2
Bus.$on('operateMusic', id => {})

Mixin 混入

一般在 src 定义一个 mixins 文件夹,里面存放每个 mixin,用 index.js 文件汇总导出

index.js

代码语言:txt
复制
import serviceMixinsModule from './service-mixins'
import tableListMixinsModule from './tableList-mixins'

export const serviceMixins = serviceMixinsModule
export const tableListMixins = tableListMixinsModule

// 组件中使用
// import { serviceMixins, tableListMixins } from '@/mixins'
// export default {
//   mixins: [serviceMixins, tableListMixins],
// }

主要说说冲突问题

当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”

比如,数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先

代码语言:txt
复制
let mixin = {
  data: function () {
    return {
      message: 'hello',
      foo: 'abc'
    }
  }
}

new Vue({
  mixins: [mixin],
  data: function () {
    return {
      message: 'goodbye',
      bar: 'def'
    }
  },
  created: function () {
    console.log(this.$data)
    // => { message: "goodbye", foo: "abc", bar: "def" }
  }
})

同名钩子函数将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用

代码语言:txt
复制
let mixin = {
  created: function () {
    console.log('混入对象的钩子被调用')
  }
}

new Vue({
  mixins: [mixin],
  created: function () {
    console.log('组件钩子被调用')
  }
})

// => "混入对象的钩子被调用"
// => "组件钩子被调用"

值为对象的选项,例如 methods、components 和 directives,将被合并为同一个对象。两个对象键名冲突时,取组件对象的键值对

代码语言:txt
复制
let mixin = {
  methods: {
    foo: function () {
      console.log('foo')
    },
    conflicting: function () {
      console.log('from mixin')
    }
  }
}

let vm = new Vue({
  mixins: [mixin],
  methods: {
    bar: function () {
      console.log('bar')
    },
    conflicting: function () {
      console.log('from self')
    }
  }
})

vm.foo() // => "foo"
vm.bar() // => "bar"
vm.conflicting() // => "from self"

注意:Vue.extend() 也使用同样的策略进行合并

路由参数解耦

相信这是大多数人处理组件中路由参数的方式:

代码语言:txt
复制
export default {
  computed: {
    paramsId() {
      return this.$route.params.id
    }
  }
}

在组件内部使用 $route 会对某个URL产生强耦合,这限制了组件的灵活性

正确的解决方案是向路由器添加 props

代码语言:txt
复制
const router = new VueRouter({
  routes: [{
    path: '/:id',
    component: Component,
    props: true
  }]
})

这样,组件可以直接从 props 获取 params

代码语言:txt
复制
export default {
  props: ['id'],
  computed: {
    paramsId() {
      return this.id
    }
  }
}

此外,你还可以传入函数以返回自定义 props

代码语言:txt
复制
const router = new VueRouter({
  routes: [{
    path: '/:id',
    component: Component,
    props: router => ({ id: route.query.id })
  }]
})

hook 妙用

如果在页面挂载时增加一个定时器,但销毁时需要清除定时器

一般想法是在 beforeDestroy 中使用 clearInterval(this.timer) 来清除

代码语言:txt
复制
export default {
  data () {
    return {
      timer: null
    }
  },
  mounted () {
    this.timer = setInterval(() => {
      console.log(Date.now())
    }, 1000)
  },
  beforeDestroy () {
    clearInterval(this.timer)
  }
}

还有种更方便的方法,使用 $once 监听 hook 函数

代码语言:txt
复制
export default {
  mounted () {
    let timer = null
    timer = setInterval(() => {
      console.log(Date.now())
    }, 1000)
    this.$once('hook:beforeDestroy', () => {
      clearInterval(timer)
    })
  }
}

监听子组件生命周期 Hook

通常,可以像这样监听子组件的生命周期(例如 mounted)

代码语言:txt
复制
<!-- Child -->
<script>
export default {
  mounted () {
    this.$emit('onMounted')
  }
}
</script>

<!-- Parent -->
<template>
  <Child @onMounted="handleOnMounted" />
</template>

还有另一种简单的解决方案,可以改用 @hook:mounted 在父组件直接监听

代码语言:txt
复制
<!-- Parent -->
<template>
  <Child @hook:mounted="handleOnMounted" />
</template>

挂载全局变量

代码语言:txt
复制
Vue.prototype.$lang = Lang

// 可以在任何一个组件使用 this.$lang

Watcher 技巧

watch 的深度监听==deep: true== 和 立即触发==immediate: true== 就不多说了

需要注意的是深度监听==deep: true== 只能监听原有属性的变化,不能监听新增、删除的属性

还有一个有趣的特性,随时监听,随时取消,$watch

代码语言:txt
复制
const unwatch = this.$watch('say', curVal => {
  console.log('数据发生了变化', curVal)
}, {
  deep: true,
  immediate: true // 是否第一次触发
})
setTimeout(() => {
  unwatch()
}, 3000)

this.$watch 的返回值 unwatch 是个方法,执行后就可以取消监听

传送门

Vue 自定义指令

Vue3 为何使用 Proxy 实现数据监听

Vue JSX、自定义 v-model

Vue.nextTick 的应用解析

Vue 路由模块化配置

关于 Vue 不能 watch 数组 和 对象变化的解决方案

Vue.mixin

Vuex 相关理解

Vue 知识总结

博客地址:https://ainyi.com/95

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 自定义组件 v-model
  • 函数式组件
  • 事件参数 $event
  • EventBus
  • Mixin 混入
  • 路由参数解耦
  • hook 妙用
  • 挂载全局变量
  • Watcher 技巧
  • 传送门
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档