Vue在页面右上角实现可悬浮/隐藏的系统菜单
这是个大多数网站很常见的功能,点击页面右上角头像显示一个悬浮菜单,点击页面其他位置或点击头像则菜单隐藏。
作为一个jQuery前端攻城狮实现这个功能可以说是很easy了,对只刚粗看了一遍vue文档的菜鸟来说,坑还是要亲自踩过才算圆满。
知识点
- 组件及组件间通信
- 计算属性
正文
1. 父组件
这里暂时只涉及系统菜单这一个功能,路由暂未涉及。
基本思路是通过props将showCancel这个Boolean值传递到子组件,对父子组件分别绑定事件,来控制这个系统菜单的显示状态。其中在父组件的绑定click事件中,将传入子组件的showCancel值重置。
这里就涉及第一个小知识点——子组件调用
写好等待被子组件渲染的自定义元素
<t-header :showCancel=showCancel></t-header>
接着import写好的子组件
import THeader from "./ponents/t-header/t-header";
然后在组件中注册子组件
ponents: { THeader }
到这里,新入坑的同学可能会比较疑惑这几行代码是怎样把子组件对应到<t-header>标签的,官方文档是这样说的
当注册组件 (或者 prop) 时,可以使用 kebab-case (短横线分隔命名)、camelCase (驼峰式命名) 或 PascalCase (单词首字母大写命名);
在 HTML 模板中,请使用 kebab-case;
我的理解是(举例),当自定义元素为<t-header>时,注册组件名可以有三种写法t-header、tHeader和THeader,在这种情况下注册的组件会自动识别并渲染到<t-header>。
需要注意的是,以上使用的是HTML 模板,是在单文件组件里用<template><template/>指定的模板;存在一种字符串模板,是用在组件选项里用template: "" 指定的模板。当使用字符串模板时,自定义标签可以用三种写法,具体情况请移步官方文档 组件命名约定。
这样父组件的雏形就诞生了
<template> <div id="app" @click="hideCancel"> <t-header :showCancel=showCancel></t-header> <!-- <router-view/> --> </div> </template> <script> import THeader from "./ponents/t-header/t-header"; export default { name: "app", ponents: { THeader }, data() { return { showCancel: false }; }, methods: { hideCancel() { this.showCancel = false; } } }; </script>
2. 子组件
子组件中.cancel为打开系统菜单的按钮,.cancel-div为系统菜单,最开始是这个样子
<template> <div class="header-wrapper"> /这里是logo和title/ ... /这里是用户名和按钮/ <div class="info-wrapper"> <span class="username">你好,管理员!</span> <span class="cancel" @click.s="switchCancelBoard"> <div class="cancel-div" v-show="showCancel"> <ul> <li @click.s="doSomething" title="用户设置">设置 </li> <li @click.s="doSomething" title="退出登录">退出 </li> </ul> </div> </span> </div> </div> </template>
按照踩坑之前的思路,在子组件接到showCancel值后用v-show控制显示隐藏,那么在父子组件的绑定click事件中只需要根据情况更改showCancel值就可以了,只要注意对系统菜单内几个选项的绑定事件不要触发父子组件上的绑定事件——总不能一点菜单它就没了,所以在绑定事件中用到了.s,即
@click.s="doSomething"
于是万事大吉,也就是像这样
<script> export default { props: { showCancel: { type: Boolean } }, methods: { doSomething() {}, switchCancelBoard() { this.showCancel = !this.showCancel; } }, puted: { ifShowCancel() { return this.showCancel; } } }; </script>
第一波踩坑之后一起表明显然我还是太年轻。狼蚁网站SEO优化是一些不好的示范
prop来的showCancel值的确可以用,点击子组件按钮的时候,
this.showCancel=!this.showCancel
实现了菜单的显示/隐藏,一打开控制台,每次点击贡献了一条报错
vue.esm.js?efeb:578 [Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent ponent re-renders. Instead, use a data or puted property based on the prop's value.
意思是避免修改prop值,因为父组件一旦re-render,这个值就会被覆盖;
,尽管在这个按钮上实现了显示状态的切换,点击其他区域的时候,并不会隐藏它,原因是子组件prop值的变化并没有影响到父组件,showCancel的值一直保持初始值没有变化,而只有在这个值被更新时才会触发子组件中相关值的更新。
——好吧,那么老老实实的用一个计算属性接收showCancel值,这样实现点击子组件控制系统菜单的状态切换;
获得了计算属性ifShowCancel,组件相应的变成了v-show="ifShowCancel",我试图在绑定事件里通过this.ifShowCancel=!this.ifShowCancel切换菜单状态,报错,得到报错信息Computed property "ifShowCancel" was assigned to but it has no setter;
明白了,要以直接赋值的形式改变计算属性ifShowCancel的值,需要一个setter函数,setter函数中无法修改prop值,在getter中也就无法通过return this.showCancel来更新这个计算属性,所以这个方法貌似也行不通;
到此为止,好像路都成了堵死状态prop值不能改->要用计算属性;计算属性不能改->需要setter;而写入了getter、setter,计算属性的值依赖于prop值->prop值不能改。——一个堪称完美的闭环诞生了!
走投无路之际我想起了$emit和$on这一对。
3. 父子互相通信
前边的prop实现了从父到子的单向通信,而通过$emit和$on,就可以实现从子组件到父组件的通信这不能直接修改父组件的属性,但却可以触发父组件的指定绑定事件,并将一个值传入父组件。
在这一步我摒弃了点击按钮时的去操作子组件内属性的想法,既然计算属性ifShowCancel依赖于prop值,那么就在点击按钮时,通过$emit触发父组件的事件,并将需要修改的属性值传入父组件,于是
/父组件自定义元素绑定switch-show事件/ <t-header :showCancel=showCancel @switch-show="switchShow"></t-header> // 父组件js methods: { //会被子组件$emit触发的方法 switchShow(val) { this.showCancel = val; } } // 子组件js methods: { //按钮上的绑定click事件 switchCancelBoard() { this.$emit("switch-show", this.ifShowCancel); } }
这样处理流程就变成了点击按钮->作为计算属性的ifShowCancel值传入父组件并触发父组件事件,对showCancel赋值->父组件属性更新->触发子组件prop更新->触发重新pute,更新ifShowCancel值->v-show起作用。
在点击其他区域时,通过父组件绑定的click事件,就可以重置showCancel值,进而隐藏掉出现的系统菜单。
下边放出这个功能的完整代码。
4. 完整代码
/父组件/ <template> <div id="app" @click="hideCancel"> <t-header :showCancel=showCancel @switch-show="switchShow"></t-header> <!-- <router-view/> --> </div> </template> <script> import THeader from "./ponents/t-header/t-header"; export default { name: "app", ponents: { THeader }, data() { return { showCancel: false }; }, methods: { hideCancel() { this.showCancel = false; }, switchShow(val) { this.showCancel = val; } } }; </script> <style scope lang="stylus"> </style> /子组件/ <template> <div class="header-wrapper"> <div class="title-wrapper"> <div class="logo"></div> <h2 class="title">Title</h2> </div> <div class="info-wrapper"> <span class="username">你好,管理员!</span> <span class="cancel" @click.s="switchCancelBoard"> <div class="cancel-div" v-show="ifShowCancel"> <ul> <li @click.s="doSomething" title="用户设置">设置 </li> <li @click.s="doSomething" title="退出登录">退出 </li> </ul> </div> </span> </div> </div> </template> <script> export default { props: { showCancel: { type: Boolean } }, methods: { doSomething() {}, switchCancelBoard() { // this.ifShowCancel = !this.showCancel; this.$emit("switch-show", !this.ifShowCancel); } }, puted: { ifShowCancel() { return this.showCancel; } } }; </script> <style lang="stylus" rel="stylesheet/stylus" scoped> .header-wrapper background: #1C60D1 color: #fff width: 100% height: 50px line-height: 50px position: fixed : 0px left: 0px font-size: 0 .title-wrapper display: block position: relative float: left height: 50px .logo display: inline-block background-image: url('./logo.png') background-size: 30px 30px background-repeat: no-repeat width: 30px height: 30px margin-: 10px .title display: inline-block font-size: 16px height: 50px line-height: 50px margin: 0px auto 0px 16px font-weight: normal vertical-align: .info-wrapper display: block position: relative float: right height: 50px width: 160px font-size: 0 .username display: inline-block height: 50px line-height: 50px font-size: 14px vertical-align: .cancel display: inline-block vertical-align: middle background-image: url('./cancel.png') background-size: 32px 32px cursor: pointer background-repeat: no-repeat width: 32px height: 32px .cancel-div position: absolute display: block width: 60px height: 80px background: #fff z-index: 50 : 40px right: 16px font-size: 14px color: #646464 box-shadow: 1px 1px 8px rgba(0, 0, 0, 0.4) ul padding-left: 0px margin: 0px li width: 100% height: 40px line-height: 40px text-align: center list-style-type: none &:hover background-color: #eaeaea </style>
以上所述是长沙网络推广给大家介绍的Vue在页面右上角实现可悬浮/隐藏的系统菜单,希望对大家有所帮助,如果大家有任何疑问请给我留言,长沙网络推广会及时回复大家的。在此也非常感谢大家对狼蚁SEO网站的支持!
编程语言
- 如何快速学会编程 如何快速学会ug编程
- 免费学编程的app 推荐12个免费学编程的好网站
- 电脑怎么编程:电脑怎么编程网咯游戏菜单图标
- 如何写代码新手教学 如何写代码新手教学手机
- 基础编程入门教程视频 基础编程入门教程视频华
- 编程演示:编程演示浦丰投针过程
- 乐高编程加盟 乐高积木编程加盟
- 跟我学plc编程 plc编程自学入门视频教程
- ug编程成航林总 ug编程实战视频
- 孩子学编程的好处和坏处
- 初学者学编程该从哪里开始 新手学编程从哪里入
- 慢走丝编程 慢走丝编程难学吗
- 国内十强少儿编程机构 中国少儿编程机构十强有
- 成人计算机速成培训班 成人计算机速成培训班办
- 孩子学编程网上课程哪家好 儿童学编程比较好的
- 代码编程教学入门软件 代码编程教程