Vue3 你可能忽略的 v-model 的巧用
前言:目前已经使用 Vue3 开发已经四个多月了,常用的一些机制自己确信已经玩的很明白了,但是在这周里看到同事的一个组件设计,用到了 V-model这个机制,给我一种恍然大悟,相见恨晚的感觉,为什么自己之前没早点了解到还可以这样用...
感觉自己忽略了 Vue 官方文档的很重要一些知识,于是重新对着官方文档大致看了一遍。
特来分享一下自己学习到的新的知识。
本文新手向,并不是单纯讲解用法,所以我不会直接就开始讲 v-model 的用法,接下来我会把我在项目中最真实的思路过程讲述出来,可能附加内容有点多。如果你只想看到 v-model 的用法,请直接跳转到《标题四》
<hr/>
一. 需求背景
- 我们公司主要项目是移动端开发,使用移动端框架是 ionic-Vue。做过移动端开发的应该都知道,移动端页面的 Header(手机顶部) ,Content(内容区域),和Footer 都是非常固定的结构。大致结构基本上和微信这样的布局结构十分相似。这里相似是指的这三个部分的高度是不会轻易调整的,不然就会造成页面切换的时候页面整体因为高度的变化而造成的“抖动”。用户体验就会变得非常不舒服,这是我们需要避免的情况。
- 周四我在写 UI 提供的一个稿子的时候。简单来说需求就是类似于 QQ 界面的右上角的加号,然后出来一个小弹窗的效果。如下图所示:
二. 实现思路
- 由于这个“加号➕”是位于 Header 部分的,但是显示区域位于 Content 区域。最一开始的想法就是将这个弹窗部分使用绝对定位来实现,使它和 “加号➕” 位于同一个组件。很遗憾的是,Ionic 所封装的 Header 组件的高度是写死的,如果把这个弹出框写在和
<Ionic-Header>
组件里的话就会出现如下的效果。
上图是我理想达到的效果(这里是为了方便演示,故意超出了一部分)
然而结果却是这样的。
由于<ion-Header>
高度固定,并且设定了 overflow-hidden 属性,那么你的选项框是无法显示在 Header 部分的。并且其实这个设计思路在最开始就是错的,这个弹出框位于的区域就应该是 Content 区域,而就不应该是位于在 Header 里的。
tips: 在这里想给和我一样的新手开发者说一句,拿到需求切忌马上敲代码而忽略组件设计思路的思考 - 于是转变了想法,采用 React 状态提升的思路。将 Header 和 Content 一起装在了同一个父亲组件里,将弹窗部分写在父组件里,并由父组件来提供改变状态的方法。
于是我的代码由下图的设计思路:
转变为了: - 这里稍等一下,由于我们的 header 是一个通用的组件,所以它在实际项目中会是一个单独的通用组件的形式,这里我们把它抽离出去。所以项目的结构大概是这样样子的。
- 这里稍微调整一下样式,如下所示:(这里解释一下,这里使用的是 tailwindCss 使用自己的语法将样式样式内嵌在 class 属性内,和传统写在 style 里没任何区别)
5.这时候就很简单了,我设定一个属性值,在点击按钮的时候弹出选项框,再次点击按钮,关闭选项框即可。默认状态下为关闭即可。
三. 使用 Props 的方法
- 接下来就得想办法将这个属性传递给
<Header/>
组件了。非常简单,我们直接使用 props 即可。
然后我还是按照 "React 后遗症" 的写法,父组件还需要再提供一个改变这个属性值的方法。 - 接下来要去子组件接受传递过来的 Props 。
- 嗯,看起来功能是正常的。最开始我也是这样想的,但是 leader 说,现在需要点击
<Content/>
区域也可以关闭这个弹出框。我心想,这还不简单啊,我直接给<Content>
也加上这个点击事件不就行了吗?
效果如下:
是不是乍一看好像没什么问题,但是我发现如果简单这样的话有个很严重的 BUG ,我点击 <Content/>
区域这个选项框也会出现了。
- 于是我就改变了
changeShow
这个函数的逻辑。我把它拆分成了两份。 - 同样的,我们
<Header/>
组件也得接收两个函数。 - 我们的
<Header>
组件内部也变成了这样子
然后把Content
的点击事件更换为closeOptions
函数,我们试一下效果。 - 🤔写到这里的时候,我就开始思考,这样虽然可以实现功能。这个场景仅仅是改变了一个属性值,我就需要传递三个 props 并且这三个属性值仅仅是为了服务一个 boolean 值属性。于是我想到了之前好像看同事开发过一个组件,是使用 v-bind 完成的,于是我转头过去研究了一下他的代码,随后又去查阅了官网。
四. 通过 v-model 实现
- 我们先看官网的简介。
说实话,我使用 Vue 几个月以来,一直想不起来使用这个 v-model 的主要原因是就是因为 Vue 官方的这句话。
仅限这几个标签去使用,我是真的没看到这个没有高亮效果的 components,所以一开始我也没理解这个 v-model 的真正强大之处。 - v-model 的真正用法应该点击这里进入去查阅。
- 这里我们需要重点去理解这段话的意思:
看到 v-model 展开的样子你发现了什么?对没错,就是一个普普通通的 porps 和一个 emit 自定义事件而已。
(这里官网写的不是特别清楚,导致我最开始没看懂 modelValue 是啥意思。这里的意思应该是,如果你不给 v-model 起一个名字,那么它就会给你起一个默认的名字,叫做 modelValue,对应的,因为它仅仅是一个 props ,所以子组件去也是需要提前定义一个名叫 modelValue 的 props, 子组件才能接收使用)
我们简单验证一下,我在子组件的 props 名称改一下,随之 Vue 就会提示你错误。 - 如果想要改名字,也非常简单,只需要在
v-model
加一个冒号然后后面跟上名字即可。 - 搞明白了这个,那么接下来的这个自定义时间也就非常好理解了。
- 首先父组件注册了一个自定义事件就叫做
update:modelValue
,这里需要注意的是update:
冒号后面跟着的名字和modelValue
是保持一致的。但是前缀一定有update:
这个关键单词。举个例子,如果是下面的写法:
那么它最终会被展开写成
再举个极端的例子,这下应该可以明白了吧。如下: - 我发现还有人不太明白官方的这个写法,
其实这不就是一个普通的箭头函数吗...
你不喜欢你可以自己写一个函数放这里也可以呀~
(Tips:但是要记住,你重新声明的这个@update:isShow=theFunc
会把默认的(newValue)=>isShow=newValue
顶替掉, v-model 仅仅就是上面的一个简写而已。只不过如果你重新设定了这个自定义事件的话,就和你自己传递一个 props 然后传递一个自定义事件没区别了,你就失去了使用 v-model 的意义了,你想使用 v-model 的目的不就是就是想简化一下代码吗) - 重点: 父组件注册好了
update:isShow
这个自定义事件,那么现在就剩子组件传递信息过来了。非常简单,在子组件中emit
即可。
这里使用的是 Vue3 的写法,主要是Ts
的语法,其实和你直接这样写是一样的const emit=defineEmits(['update:isShow'])
- 效果和上面通过 props 传递两个改变状态的方法是一模一样的。
五. 总结
v-model 双向绑定其实就是一个看作传递 props
和设定自定义事件的语法糖。没有什么很特别的地方。但是如果熟练使用起来的话,在开发通用组件的时候会更近得心应手。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。