本章概要
- 实例
- 通过指令实现下拉菜单
- 使用自定义指令实现随机背景色
5.3 实例
5.3.1 通过指令实现下拉菜单
当鼠标移动到毛哥菜单上时会弹出一个子菜单列表,每个子菜单单项都是可以单击的,当鼠标移除整个菜单列表区域,子菜单列表隐藏。
此处按照菜单的层级关系,通过嵌套的 v-for 指令循环输出。
在 Vue 实例的数据属性中定义一个 menus 数组,将各个顶层菜单定义为一个对象,作为数组中的元素,子菜单作为顶层菜单对象的属性嵌套定义。如下:
data : {
menus: [
{
name: '我的淘宝', url: '#', show: false, subMenus: [
{name: '已买到的宝贝', url: '#'},
{name: '已卖出的宝贝', url: '#'}
]
},
{
name: '收藏夹', url: '#', show: false, subMenus: [
{name: '收藏的宝贝', url: '#'},
{name: '收藏的店铺', url: '#'}
]
}
]
};
然后为每一个顶层菜单定义一个 show 属性,初始值为 false ,该属性主要用于控制其下的子菜单是否显示。
下拉菜单的子菜单初始是不显示的,只有当鼠标移动到顶层菜单时才会显示,当鼠标移动到菜单列表外边,子菜单列表要隐藏。这里定义的 show 属性就是用来标记这两种情况的,当需要显示时,将 show 设置为 true;当需要隐藏时,将 show 设置为 false。
接下来就该使用 v-for 指令循环输出菜单了。
对于这种有多个菜单的,采用 v-for 指令进行渲染时非常方便的,菜单可能有 二级菜单、三级菜单,这都没关系,无非就是使用多个 v-for 指令。
另外,子菜单的显示是鼠标移动到顶层菜单上,隐藏是鼠标移动到顶层菜单外部,因此还需要为所有的顶层菜单绑定两个鼠标事件:mouseover 和 mouseout。如下:
<div id = "app" v-cloak>
<li v-for="menu in menus" @mouseover="menu.show = !menu.show" @mouseout="menu.show = !menu.show">
<a :href="menu.url" >
{{menu.name}}
</a>
<ul v-show="menu.show">
<li v-for="subMenu in menu.subMenus">
<a :href="subMenu.url">{{subMenu.name}}</a>
</li>
</ul>
</li>
</div>
说明:
- 在 div 元素中使用了 v-cloak 指令避免页面加载时的闪烁问题,当然,这需要和 CSS 样式规则 [v-cloak]{display:none} 一起使用。
- 绑定 mouseover 和 mouseout 事件时采用了 v-on 指令的简写语法,menu.show 初始值为 false,因此 @mouseover 的表达式计算结果是将 menu.show 设为 true,而 @mouseout 表达式的计算结果是将 menu.show 设为 false。
- 子菜单放在一个 ul 元素内部,在该元素上使用 v-show 指令,根据表达式 menu.show 的值动态控制子菜单的显示和隐藏。这里不适合使用 v-if 指令,因为子菜单的显示和隐藏可能会频繁切换。
本例主要的逻辑代码就是以上内容,剩下的主要是 CSS 样式的设置。完整代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
body {
width: 600px;
}
a {
text-decoration: none;
display: block;
color: #fff;
width: 120px;
height: 40px;
line-height: 40px;
border: 1px solid #fff;
border-width: 1px 1px 0 0;
background: #255f9e;
}
li {
list-style-type: none;
}
#app > li {
list-style-type: none;
float: left;
text-align: center;
position: relative;
}
#app li a:hover {
color: #fff;
background: #ffb100;
}
#app li ul {
position: absolute;
left: -40px;
top: 40px;
margin-top: 1px;
font-size: 12px;
}
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id = "app" v-cloak>
<li v-for="menu in menus" @mouseover="menu.show = !menu.show" @mouseout="menu.show = !menu.show">
<a :href="menu.url" >
{{menu.name}}
</a>
<ul v-show="menu.show">
<li v-for="subMenu in menu.subMenus">
<a :href="subMenu.url">{{subMenu.name}}</a>
</li>
</ul>
</li>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const data = {
menus: [
{
name: '我的淘宝', url: '#', show: false, subMenus: [
{name: '已买到的宝贝', url: '#'},
{name: '已卖出的宝贝', url: '#'}
]
},
{
name: '收藏夹', url: '#', show: false, subMenus: [
{name: '收藏的宝贝', url: '#'},
{name: '收藏的店铺', url: '#'}
]
}
]
};
const vm = Vue.createApp({
data() {
return data;
}
}).mount('#app');
</script>
</body>
</html>
运行结果如下:
5.3.2 使用自定义指令实现随机背景色
有时候会使用一张图作为网页中某个元素的背景图,当网络状况不好时,或者图片本身较大时,图片的加载会比较慢。
在这种情况下,可以先在该元素的区域用随机的背景色填充,等图片加载完成后,再把元素背景替换为图片。如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
div{
width: 567px;
height: 567px;
}
</style>
</head>
<body>
<div id="app">
<div v-img="'images/bg.jpg'"></div>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({});
app.directive('img', {
mounted: function(el, binding){
let color = Math.floor(Math.random() * 1000000);
el.style.backgroundColor = '#' + color;
let img = new Image();
img.src = binding.value;
img.onload = function(){
el.style.backgroundImage = 'url(' + binding.value + ')';
}
}
})
app.mount('#app');
</script>
</body>
</html>