10个Vue开发技巧「实践」

10个Vue开发技巧「实践」函数式组件是无状态 它无法实例化 没有任何的生命周期和方法 吃透 Vue 项目开发实践 16 个方面深入前端工程化开发技巧 上

欢迎大家来到IT世界,在知识的湖畔探索吧!

10个Vue开发技巧「实践」



欢迎大家来到IT世界,在知识的湖畔探索吧!

作者:WahFung

转发链接:
https://juejin.im/post/5e8a9b1ae51dbdfa

路由参数解耦

一般在组件内使用路由参数,大多数人会这样做:

export default { methods: { getParamsId() { return this.$route.params.id } } } 复制代码

欢迎大家来到IT世界,在知识的湖畔探索吧!

在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。

正确的做法是通过 props 解耦

欢迎大家来到IT世界,在知识的湖畔探索吧!const router = new VueRouter({ routes: [{ path: '/user/:id', component: User, props: true }] }) 复制代码

将路由的 props 属性设置为 true 后,组件内可通过 props 接收到 params 参数

export default { props: ['id'], methods: { getParamsId() { return this.id } } } 复制代码

另外你还可以通过函数模式来返回 props

欢迎大家来到IT世界,在知识的湖畔探索吧!const router = new VueRouter({ routes: [{ path: '/user/:id', component: User, props: (route) => ({ id: route.query.id }) }] }) 复制代码

文档:
https://router.vuejs.org/zh/guide/essentials/passing-props.html

函数式组件

函数式组件是无状态,它无法实例化,没有任何的生命周期和方法。创建函数式组件也很简单,只需要在模板添加 functional 声明即可。一般适合只依赖于外部数据的变化而变化的组件,因其轻量,渲染性能也会有所提高。

组件需要的一切都是通过 context 参数传递。它是一个上下文对象,具体属性查看文档。这里 props 是一个包含所有绑定属性的对象。

函数式组件

 
   复制代码

父组件使用

欢迎大家来到IT世界,在知识的湖畔探索吧! 
   复制代码
import List from '@/components/List.vue' export default { components: { List }, data() { return { list: [{ title: 'title', content: 'content' }], currentItem: '' } } } 复制代码

文档:
https://cn.vuejs.org/v2/guide/render-function.html

样式穿透

在开发中修改第三方组件样式是很常见,但由于 scoped 属性的样式隔离,可能需要去除 scoped 或是另起一个 style 。这些做法都会带来副作用(组件样式污染、不够优雅),样式穿透在css预处理器中使用才生效。

我们可以使用 >>> 或 /deep/ 解决这一问题:

欢迎大家来到IT世界,在知识的湖畔探索吧! 复制代码
 复制代码

watch高阶使用

立即执行

watch 是在监听属性改变时才会触发,有些时候,我们希望在组件创建后 watch 能够立即执行

可能想到的的方法就是在 create 生命周期中调用一次,但这样的写法不优雅,或许我们可以使用这样的方法

欢迎大家来到IT世界,在知识的湖畔探索吧!export default { data() { return { name: 'Joe' } }, watch: { name: { handler: 'sayName', immediate: true } }, methods: { sayName() { console.log(this.name) } } } 复制代码

深度监听

在监听对象时,对象内部的属性被改变时无法触发 watch ,我们可以为其设置深度监听

export default { data: { studen: { name: 'Joe', skill: { run: { speed: 'fast' } } } }, watch: { studen: { handler: 'sayName', deep: true } }, methods: { sayName() { console.log(this.studen) } } } 复制代码

触发监听执行多个方法

使用数组可以设置多项,形式包括字符串、函数、对象

欢迎大家来到IT世界,在知识的湖畔探索吧!export default { data: { name: 'Joe' }, watch: { name: [ 'sayName1', function(newVal, oldVal) { this.sayName2() }, { handler: 'sayName3', immaediate: true } ] }, methods: { sayName1() { console.log('sayName1==>', this.name) }, sayName2() { console.log('sayName2==>', this.name) }, sayName3() { console.log('sayName3==>', this.name) } } } 复制代码

文档:
https://cn.vuejs.org/v2/api/#watch

watch监听多个变量

watch本身无法监听多个变量。但我们可以将需要监听的多个变量通过计算属性返回对象,再监听这个对象来实现“监听多个变量”

export default { data() { return { msg1: 'apple', msg2: 'banana' } }, compouted: { msgObj() { const { msg1, msg2 } = this return { msg1, msg2 } } }, watch: { msgObj: { handler(newVal, oldVal) { if (newVal.msg1 != oldVal.msg1) { console.log('msg1 is change') } if (newVal.msg2 != oldVal.msg2) { console.log('msg2 is change') } }, deep: true } } } 复制代码

事件参数$event

$event 是事件对象的特殊变量,在一些场景能给我们实现复杂功能提供更多可用的参数

原生事件

在原生事件中表现和默认的事件对象相同

欢迎大家来到IT世界,在知识的湖畔探索吧! 
   复制代码
export default { methods: { inputHandler(msg, e) { console.log(e.target.value) } } } 复制代码

自定义事件

在自定义事件中表现为捕获从子组件抛出的值

my-item.vue :

欢迎大家来到IT世界,在知识的湖畔探索吧!export default { methods: { customEvent() { this.$emit('custom-event', 'some value') } } } 复制代码

App.vue

 
   复制代码
欢迎大家来到IT世界,在知识的湖畔探索吧!export default { methods: { customEvent(index, e) { console.log(e) // 'some value' } } } 复制代码

文档:
https://cn.vuejs.org/v2/guide/events.html

https://cn.vuejs.org/v2/guide/components.html

自定义组件双向绑定

组件 model 选项:

允许一个自定义组件在使用 v-model 时定制 prop 和 event。默认情况下,一个组件上的 v-model 会把 value 用作 prop 且把 input 用作 event,但是一些输入类型比如单选框和复选框按钮可能想使用 value prop 来达到不同的目的。使用 model 选项可以回避这些情况产生的冲突。

input 默认作为双向绑定的更新事件,通过 $emit 可以更新绑定的值

 
   复制代码
欢迎大家来到IT世界,在知识的湖畔探索吧!export default { props: { value: { type: Boolean, default: false } }, methods: { switchChange(val) { this.$emit('input', val) } } } 复制代码

修改组件的 model 选项,自定义绑定的变量和事件

 
   复制代码
欢迎大家来到IT世界,在知识的湖畔探索吧!export default { model: { prop: 'num', event: 'update' }, props: { value: { type: String, default: '' }, num: { type: Number, default: 0 } }, methods: { numChange() { this.$emit('update', this.num++) } } } 复制代码

文档:
https://cn.vuejs.org/v2/api/#model

监听组件生命周期

通常我们监听组件生命周期会使用 $emit ,父组件接收事件来进行通知

子组件

export default { mounted() { this.$emit('listenMounted') } } 复制代码

父组件

欢迎大家来到IT世界,在知识的湖畔探索吧! 
   复制代码

其实还有一种简洁的方法,使用 @hook 即可监听组件生命周期,组件内无需做任何改变。同样的, created 、 updated 等也可以使用此方法。

 
   复制代码

程序化的事件侦听器

比如,在页面挂载时定义计时器,需要在页面销毁时清除定时器。这看起来没什么问题。但仔细一看 this.timer 唯一的作用只是为了能够在 beforeDestroy 内取到计时器序号,除此之外没有任何用处。

欢迎大家来到IT世界,在知识的湖畔探索吧!export default { mounted() { this.timer = setInterval(() => { console.log(Date.now()) }, 1000) }, beforeDestroy() { clearInterval(this.timer) } } 复制代码

如果可以的话最好只有生命周期钩子可以访问到它。这并不算严重的问题,但是它可以被视为杂物。

我们可以通过 $on 或 $once 监听页面生命周期销毁来解决这个问题:

export default { mounted() { this.creatInterval('hello') this.creatInterval('world') }, creatInterval(msg) { let timer = setInterval(() => { console.log(msg) }, 1000) this.$once('hook:beforeDestroy', function() { clearInterval(timer) }) } } 复制代码

使用这个方法后,即使我们同时创建多个计时器,也不影响效果。因为它们会在页面销毁后程序化的自主清除。

文档:
https://cn.vuejs.org/v2/guide/components-edge-cases.html

手动挂载组件

在一些需求中,手动挂载组件能够让我们实现起来更加优雅。比如一个弹窗组件,最理想的用法是通过命令式调用,就像 elementUI 的 this.$message 。而不是在模板中通过状态切换,这种实现真的很糟糕。

先来个最简单的例子:

欢迎大家来到IT世界,在知识的湖畔探索吧!import Vue from 'vue' import Message from './Message.vue' // 构造子类 let MessageConstructor = Vue.extend(Message) // 实例化组件 let messageInstance = new MessageConstructor() // $mount可以传入选择器字符串,表示挂载到该选择器 // 如果不传入选择器,将渲染为文档之外的的元素,你可以想象成 document.createElement()在内存中生成dom messageInstance.$mount() // messageInstance.$el获取的是dom元素 document.body.appendChild(messageInstance.$el) 复制代码

下面实现一个简易的 message 弹窗组件

Message/index.vue

 
   复制代码
欢迎大家来到IT世界,在知识的湖畔探索吧!// 默认选项 const DefaultOptions = { duration: 1500, type: 'info', content: '这是一条提示信息!', } let mid = 0 export default { data() { return { notices: [] } }, methods: { add(notice = {}) { // name标识 用于移除弹窗 let _name = this.getName() // 合并选项 notice = Object.assign({ _name }, DefaultOptions, notice) this.notices.push(notice) setTimeout(() => { this.removeNotice(_name) }, notice.duration) }, getName() { return 'msg_' + (mid++) }, removeNotice(_name) { let index = this.notices.findIndex(item => item._name === _name) this.notices.splice(index, 1) } } } 复制代码
.wrap { position: fixed; top: 50px; left: 50%; display: flex; flex-direction: column; align-items: center; transform: translateX(-50%); } .message { --borderWidth: 3px; min-width: 240px; max-width: 500px; margin-bottom: 10px; border-radius: 3px; box-shadow: 0 0 8px #ddd; overflow: hidden; } .content { padding: 8px; line-height: 1.3; } .message.info { border-left: var(--borderWidth) solid #; background: #F4F4F5; } .message.success { border-left: var(--borderWidth) solid #67C23A; background: #F0F9EB; } .message.error { border-left: var(--borderWidth) solid #F56C6C; background: #FEF0F0; } .message.warning { border-left: var(--borderWidth) solid #E6A23C; background: #FDF6EC; } 复制代码

Message/index.js

欢迎大家来到IT世界,在知识的湖畔探索吧!import Vue from 'vue' import Index from './index.vue' let messageInstance = null let MessageConstructor = Vue.extend(Index) let init = () => { messageInstance = new MessageConstructor() messageInstance.$mount() document.body.appendChild(messageInstance.$el) } let caller = (options) => { if (!messageInstance) { init(options) } messageInstance.add(options) } export default { // 返回 install 函数 用于 Vue.use 注册 install(vue) { vue.prototype.$message = caller } } 复制代码

main.js

import Message from '@/components/Message/index.js' Vue.use(Message) 复制代码

使用

欢迎大家来到IT世界,在知识的湖畔探索吧!this.$message({ type: 'success', content: '成功信息提示', duration: 3000 }) 复制代码

文档:
https://cn.vuejs.org/v2/api/#vm-mount

推荐Vue学习资料文章:

《是什么导致尤大大选择放弃Webpack?【vite 原理解析】》

《带你了解 vue-next(Vue 3.0)之 小试牛刀【实践】》

《带你了解 vue-next(Vue 3.0)之 初入茅庐【实践】》

《实践Vue 3.0做JSX(TSX)风格的组件开发》

《一篇文章教你并列比较React.js和Vue.js的语法【实践】》

《手拉手带你开启Vue3世界的鬼斧神工【实践】》

《深入浅出通过vue-cli3构建一个SSR应用程序【实践】》

《怎样为你的 Vue.js 单页应用提速》

《聊聊昨晚尤雨溪现场针对Vue3.0 Beta版本新特性知识点汇总》

《【新消息】Vue 3.0 Beta 版本发布,你还学的动么?》

《Vue真是太好了 壹万多字的Vue知识点 超详细!》

《Vue + Koa从零打造一个H5页面可视化编辑器——Quark-h5》

《深入浅出Vue3 跟着尤雨溪学 TypeScript 之 Ref 【实践】》

《手把手教你深入浅出vue-cli3升级vue-cli4的方法》

《Vue 3.0 Beta 和React 开发者分别杠上了》

《手把手教你用vue drag chart 实现一个可以拖动 / 缩放的图表组件》

《Vue3 尝鲜》

《总结Vue组件的通信》

《手把手让你成为更好的Vue.js开发人员的12个技巧和窍门【实践】》

《Vue 开源项目 TOP45》

《2020 年,Vue 受欢迎程度是否会超过 React?》

《尤雨溪:Vue 3.0的设计原则》

《使用vue实现HTML页面生成图片》

《实现全栈收银系统(Node+Vue)(上)》

《实现全栈收银系统(Node+Vue)(下)》

《vue引入原生高德地图》

《Vue合理配置WebSocket并实现群聊》

《多年vue项目实战经验汇总》

《vue之将echart封装为组件》

《基于 Vue 的两层吸顶踩坑总结》

《Vue插件总结【前端开发必备】》

《Vue 开发必须知道的 36 个技巧【近1W字】》

《构建大型 Vue.js 项目的10条建议》

《深入理解vue中的slot与slot-scope》

《手把手教你Vue解析pdf(base64)转图片【实践】》

《使用vue+node搭建前端异常监控系统》

《推荐 8 个漂亮的 vue.js 进度条组件》

《基于Vue实现拖拽升级(九宫格拖拽)》

《手摸手,带你用vue撸后台 系列二(登录权限篇)》

《手摸手,带你用vue撸后台 系列三(实战篇)》

《前端框架用vue还是react?清晰对比两者差异》

《Vue组件间通信几种方式,你用哪种?【实践】》

《浅析 React / Vue 跨端渲染原理与实现》

《10个Vue开发技巧助力成为更好的工程师》

《手把手教你Vue之父子组件间通信实践讲解【props、$ref 、$emit】》

《1W字长文+多图,带你了解vue的双向数据绑定源码实现》

《深入浅出Vue3 的响应式和以前的区别到底在哪里?【实践】》

《干货满满!如何优雅简洁地实现时钟翻牌器(支持JS/Vue/React)》

《基于Vue/VueRouter/Vuex/Axios登录路由和接口级拦截原理与实现》

《手把手教你D3.js 实现数据可视化极速上手到Vue应用》

《吃透 Vue 项目开发实践|16个方面深入前端工程化开发技巧【上】》

《吃透 Vue 项目开发实践|16个方面深入前端工程化开发技巧【中】》

《吃透 Vue 项目开发实践|16个方面深入前端工程化开发技巧【下】》

《Vue3.0权限管理实现流程【实践】》

《后台管理系统,前端Vue根据角色动态设置菜单栏和路由》

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/114342.html

(0)
上一篇 31分钟前
下一篇 6分钟前

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们YX

mu99908888

在线咨询: 微信交谈

邮件:itzsgw@126.com

工作时间:时刻准备着!

关注微信