Vue 开发技巧总结
博客地址:https://ainyi.com/95
本人玩了 Vue 两年多,在此总结一下开发时的一些技巧和方法
自定义组件 v-model
v-model 是 Vue 提供的一个语法糖,它本质上是由 value 属性 + input 事件组成的(都是原生的默认属性)
自定义组件中,可以通过传递 value 属性并监听 input 事件来实现数据的双向绑定
自定义组件
<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>
父组件调用
<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(不需要实例化的组件)
由于它像函数一样轻巧,没有实例引用,所以渲染性能提高了不少
在日常开发中,经常会开发一些纯展示性的业务组件,比如一些详情页面,列表界面等,它们有一个共同的特点是只需要将外部传入的数据进行展现,不需要有内部状态,不需要在生命周期钩子函数里面做处理,这时候你就可以考虑使用函数式组件
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>
}
}
使用函数式组件的原因:
- 最主要最关键的原因是函数式组件不需要实例化,无状态,没有生命周期,所以渲染性能要好于普通组件
- 函数式组件结构比较简单,代码结构更清晰
函数式组件与普通组件的区别
- 函数式组件需要在组件上声明functional
- 函数式组件不需要实例化,所以没有 this,this通过render函数的第二个参数来代替
- 函数式组件没有生命周期钩子函数,不能使用计算属性、watch 等等
- 函数式组件不能通过 $emit 对外暴露事件,调用事件只能通过context.listeners.click的方式调用外部传入的事件
- 因为函数式组件是没有实例化的,所以在外部通过ref去引用组件时,实际引用的是 HTMLElement
- 函数式组件的props可以不用显式声明,所以没有在props里面声明的属性都会被自动隐式解析为 prop,而普通组件所有未声明的属性都被解析到 $attrs 里面,并自动挂载到组件根元素上面(可以通过 inheritAttrs 属性禁止)
模板语法声明函数式组件
在 Vue2.5 之前,使用函数式组件只能通过 JSX 的方式,在之后可以通过模板语法来声明函数式组件
<!-- 在 template 上面添加 functional 属性 -->
<template functional>
<img :src="props.avatar" />
</template>
<!-- 上面第 6 点,可不用显示声明 props -->
事件参数 $event
$event 是事件对象的一个特殊变量。它在某些场景下为复杂的功能提供了更多的可选参数
<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 必须是全局唯一的
// 在 main.js
Vue.prototype.$eventBus = new Vue()
// 传值组件
this.$eventBus.$emit('eventTarget','这是eventTarget传过来的值')
// 接收组件
this.$eventBus.$on('eventTarget', v => {
console.log('eventTarget', v)
})
也可以新建一个 bus.js 文件
import Vue from 'vue'
export default new Vue()
在要通信的组件导入此文件,进行监听或发送
import Bus from '@/bus'
// 组件1
Bus.$emit('operateMusic', id)
// 组件2
Bus.$on('operateMusic', id => {})
Mixin 混入
一般在 src 定义一个 mixins 文件夹,里面存放每个 mixin,用 index.js 文件汇总导出
index.js
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],
// }
主要说说冲突问题
当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”
比如,数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先
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" }
}
})
同名钩子函数将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用
let mixin = {
created: function () {
console.log('混入对象的钩子被调用')
}
}
new Vue({
mixins: [mixin],
created: function () {
console.log('组件钩子被调用')
}
})
// => "混入对象的钩子被调用"
// => "组件钩子被调用"
值为对象的选项,例如 methods、components 和 directives,将被合并为同一个对象。两个对象键名冲突时,取组件对象的键值对
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() 也使用同样的策略进行合并
路由参数解耦
相信这是大多数人处理组件中路由参数的方式:
export default {
computed: {
paramsId() {
return this.$route.params.id
}
}
}
在组件内部使用 $route 会对某个URL产生强耦合,这限制了组件的灵活性
正确的解决方案是向路由器添加 props
const router = new VueRouter({
routes: [{
path: '/:id',
component: Component,
props: true
}]
})
这样,组件可以直接从 props 获取 params
export default {
props: ['id'],
computed: {
paramsId() {
return this.id
}
}
}
此外,你还可以传入函数以返回自定义 props
const router = new VueRouter({
routes: [{
path: '/:id',
component: Component,
props: router => ({ id: route.query.id })
}]
})
hook 妙用
如果在页面挂载时增加一个定时器,但销毁时需要清除定时器
一般想法是在 beforeDestroy 中使用 clearInterval(this.timer) 来清除
export default {
data () {
return {
timer: null
}
},
mounted () {
this.timer = setInterval(() => {
console.log(Date.now())
}, 1000)
},
beforeDestroy () {
clearInterval(this.timer)
}
}
还有种更方便的方法,使用 $once 监听 hook 函数
export default {
mounted () {
let timer = null
timer = setInterval(() => {
console.log(Date.now())
}, 1000)
this.$once('hook:beforeDestroy', () => {
clearInterval(timer)
})
}
}
监听子组件生命周期 Hook
通常,可以像这样监听子组件的生命周期(例如 mounted)
<!-- Child -->
<script>
export default {
mounted () {
this.$emit('onMounted')
}
}
</script>
<!-- Parent -->
<template>
<Child @onMounted="handleOnMounted" />
</template>
还有另一种简单的解决方案,可以改用 @hook:mounted 在父组件直接监听
<!-- Parent -->
<template>
<Child @hook:mounted="handleOnMounted" />
</template>
挂载全局变量
Vue.prototype.$lang = Lang
// 可以在任何一个组件使用 this.$lang
Watcher 技巧
watch 的深度监听deep: true 和 立即触发immediate: true 就不多说了
需要注意的是深度监听deep: true 只能监听原有属性的变化,不能监听新增、删除的属性
还有一个有趣的特性,随时监听,随时取消,$watch
const unwatch = this.$watch('say', curVal => {
console.log('数据发生了变化', curVal)
}, {
deep: true,
immediate: true // 是否第一次触发
})
setTimeout(() => {
unwatch()
}, 3000)
this.$watch 的返回值 unwatch 是个方法,执行后就可以取消监听
传送门
关于 Vue 不能 watch 数组 和 对象变化的解决方案
博客地址:https://ainyi.com/95
Vue 开发技巧总结的更多相关文章
- Vue 开发技巧或者说Vue知识点梳理(转,自个学习)
Vue 组件通讯 ——常见使用场景可以分为三类: 父子通信: 父向子传递数据是通过 props,子向父是通过 events($emit):通过父链 / 子链也可以通信($parent / $child ...
- 10个Vue开发技巧助力成为更好的工程师·二
优雅更新props 更新 prop 在业务中是很常见的需求,但在子组件中不允许直接修改 prop,因为这种做法不符合单向数据流的原则,在开发模式下还会报出警告.因此大多数人会通过 $emit 触发自定 ...
- vue -- 7 个 有用的 Vue 开发技巧
1 状态共享 随着组件的细化,就会遇到多组件状态共享的情况, Vuex当然可以解决这类问题,不过就像 Vuex官方文档所说的,如果应用不够大,为避免代码繁琐冗余,最好不要使用它,今天我们介绍的是 vu ...
- vue开发必须知道的小技巧
近年来,vue越来越火,使用它的人也越来越多.vue基本用法很容易上手,但是还有很多优化的写法你就不一定知道了.本文列举了一些vue常用的开发技巧.require.context() 在实际开发中,绝 ...
- 微信小程序入门与实战 常用组件API开发技巧项目实战*全
第1章 什么是微信小程序? 第2章 小程序环境搭建与开发工具介绍 第3章 从一个简单的“欢迎“页面开始小程序之旅 第4章 第二个页面:新闻阅读列表 第5章 小程序的模板化与模块化 第6章 构建新闻详情 ...
- SQL开发技巧(二)
本系列文章旨在收集在开发过程中遇到的一些常用的SQL语句,然后整理归档,本系列文章基于SQLServer系列,且版本为SQLServer2005及以上-- 文章系列目录 SQL开发技巧(一) SQL开 ...
- DelphiXE2 DataSnap开发技巧收集
DelphiXE2 DataSnap开发技巧收集 作者: 2012-08-07 09:12:52 分类:Delphi 标签: 作为DelphiXE2 DataSnap开发的私家锦囊, ...
- delphi XE5下安卓开发技巧
delphi XE5下安卓开发技巧 一.手机快捷方式显示中文名称 project->options->Version Info-label(改成需要显示的中文名即可),但是需要安装到安卓手 ...
- 经典收藏 50个jQuery Mobile开发技巧集萃
http://www.cnblogs.com/chu888chu888/archive/2011/11/10/2244181.html 1.Backbone移动实例 这是在Safari中运行的一款Ba ...
随机推荐
- PHP simplexml_import_dom() 函数
实例 获取 DOM 文档节点并转换为 SimpleXML 节点: <?php$dom=new domDocument;高佣联盟 www.cgewang.com$dom->loadXML(& ...
- Sharding-JDBC实现读写分离
参考资料:猿天地 https://mp.weixin.qq.com/s/kp2lJHpTMz4bDWkJYjVbOQ 作者:尹吉欢 技术选型:SpringBoot + Sharding-JDBC ...
- Centos8最小化部署安装OpenStack Ussuri
#!/bin/bash #Centos8最小化部署安装OpenStack Ussuri #共两台主机,分别是一台控制节点,一台计算节点 #.控制节点内存4096M.双网卡,分别为eth0:10.0.0 ...
- spring data jpa 代码生成!!(精华帖)
通过数据库动态生成自己想要生成的各种模板,需要了解grovery. view -> Tool Windows -> Database + -> Data source -> M ...
- SpringBoot实现发送邮件
1.QQ邮箱发送邮件设置 首先登录QQ邮箱>>>登录成功后找到设置>>>然后找到邮箱设置>>>点击账户>>>找到POP3|SMT ...
- SCOI2020后摸鱼实况记录
6.27:回家之后摸摸摸,等辉夜更新辉夜真好看. 6.28:口胡了一场比赛,发现原题大战,感觉很有毒.然后不知道干了啥,一天就结束了.晚上发现兰斯10居然汉化了,马上跑去白嫖下载,waiting... ...
- Weighted-Residual-Connections
- data argumentation 数据增强汇总
几何变换 flip:水平翻转,也叫镜像:垂直翻转 rotation:图片旋转一定的角度,这个可以通过opencv来操作,各个框架也有自己的算子 crop:随机裁剪,比如说,在ImageNet中可以将输 ...
- ROS 八叉树地图构建 - 安装 octomap 和 octomap_server 建图包!
项目要用到八叉树库 Octomap 来构建地图,这里记录下安装.可视化,并启用带颜色的 Octomap 的过程. 一.Apt 安装 Octomap 库 如果你不需要修改源码,可以直接安装编译好的 oc ...
- java Format
DecimalFormat函数语法: DecimalFormat 是 NumberFormat 的一个具体子类,用于格式化十进制数字. DecimalFormat 包含一个模式 和一组符号 符号含义: ...