[Vue.js启航]——主从结构应用构建

Vue.js启航——英雄编辑器(二)

简介

 上一篇关于英雄编辑器的文章写到了简单的双向数据绑定和简单的组件式开发。这一篇文章将会更加深入的使用组件和指令。

主从结构的英雄编辑器

 主从结构在当前应用领域是非常流行的,常常用于数据索引和数据查询,让我们先看看主从结构的英雄编辑器的实现效果

主从结构的英雄编辑器

从实现效果可以看出整个应用使用了列表渲染,条件渲染,双向绑定,样式绑定等,双向绑定都很熟悉,那什么是列表渲染,条件渲染以及样式绑定呢
- 列表渲染:用 v-for 指令根据一组数组的选项列表进行渲染。
- 条件渲染:用 v-if 指令组根据条件来选择渲染,v-if 指令组包括 v-if , v-else , v-else-if (2.1.0新增)
- 样式绑定:用 v-bind 将class和样式进行绑定,使其能够根据数据显示不同的样式

模版

 先来讲解一下模版

<div>
<h1>{{title}}</h1>
<h2>My Heroes</h2>
<ul class="heroes">
    <li v-for="hero in heroes" v-on:click="onSelect(hero)"
        v-bind:class="{selected:hero===selectedHero}">
        <span class="badge">{{hero.id}}</span>{{hero.name}}
    </li>
</ul>
<template v-if="selectedHero">
    <h2>{{selectedHero.name}} details</h2>
    <div><label>id: </label>{{selectedHero.id}}</div>
    <div>
        <label>name: </label>
        <input v-model="selectedHero.name" placeholder="name"/>
    </div>
</template>
</div>

这个模版分了两个部分,一个是英雄列表的显示,一个是上一篇文章写的简单的编辑。在英雄列表显示这一部分上使用了列表渲染,具体的代码是

<li v-for="hero in heroes" ...>....</li>

主要的意思是遍历 heroes 集合里的所有元素,并显示出来。具体的 heroes 就通过指定实例时使用 v-bind:heroes 来绑定。之后就可以在遍历标签内使用 hero 具体代表 heroes集合内的元素。如:

<li v-for='hero in heroes'>
    <span>{{hero.id}}</span>{{hero.name}}
</li>

然后就是简单的编辑,这次加入了条件渲染,也就是 v-if 指令,下面代码的意思是,在 selectedHero 这个对象值不为 false 以及解释为 false 的取值时,就会渲染出来,否则就不渲染。实现出来的效果是,当一开始打开页面的时候,这一部分并没有出现,只有等到你点击了某个英雄 selectedHero 被赋值时,这一部分的代码才会被渲染出来。

<template v-if="selectedHero">
    ...
</template>

有一点要注意的是 <template></template> 标签只是一个渲染容器,在渲染后会去掉,也就是在你看到的网页源码中不会出现 <template></template> 标签
在模版里还是用了样式绑定这一技巧,那么什么是样式绑定呢,我们在编写css文件的时候会使用选择器来选择元素进行样式编写,在这个模版的样式中我们使用了 class 来编辑样式,在编写传统的html/css/js网页时我们也会改变标签的 className 属性来改变样式,而使用Vue.js的实现就是通过 v-bind:class 指令来实现,看一下模版里的有关样式绑定的代码

v-bind:class="{selected:hero===selectedHero}"

这行代码的含义是当 hero===selectedHero 表达式为 true 时,绑定 .selected 样式,在传统的js改变样式是改变标签属性的值,而到了Vue.js就是改变表达式的真值,这样我们就可以很方便的为这个标签指定不同的样式样式,例如,我们在 active1 条件下激活 class1 样式,在 active2 条件下激活 class2 样式,写法为

<div v-bind:class=[{class1:activ1},{class2:active2}]></div>

传统的js还有直接改变标签的 style 属性的值来改变样式,而Vue.js当然也是可以的,通过 v-bind:style 指令就可以实现,如

<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>

当然也是可以引用实例的 data 选项的值来改变,如下所示就是将 style 的值设置为 color:'red', fontSize:'13px'

<div v-bind:style="styleObject"></div>

data: {
    styleObject: {
    color: 'red',
    fontSize: '13px'
    }
}

接着是DOM事件处理部分,这个模版中有一处DOM事件处理,在Vue.js中事件处理一般是使用 v-on 指令,下面处理得是 click 时间所以使用 v-on:click, onSelect(hero) 是事件处理的方法,具体如何定义将会在数据传递以及数据处理那一部分讲解。

<li ... v-on:click="onSelect(hero)">...</li>

到这里我们的模版也就解释完了,下一部分就解释组件中的 propsdata 以及 methods,这三个选项主要进行组件的数据传递以及数据处理的

数据传递以及数据处理

 在这里我们先理清,组件的内部数据以及组件的外部数据,也称为父组件的数据以及子组件的数据。我的理解就是父组件的数据就是要通过 props 传递进去组件的数据,而子组件的数据就是 data 引入的数据。那么父组件的数据和子组件的数据的处理有什么不同吗,暂时我遇到的第一个对于数据传递的坑就是Vue.js对于父组件数据的直接修改会警告。Vue.js的数据流使用的是单向数据流,具体就是,父组件的数据变化会导致子组件相应数据的变化,而子组件改变了传入的父组件的数据,父组件的数据不会发生改变。那么究竟如何改变父数据的值才不会警告呢,官方文档给了两种方法

  1. 定义一个局部变量,并用 prop 的值初始化它:
  2. 定义一个计算属性,处理 prop 的值并返回

说了一堆理论的,看一下我的英雄编辑器是怎么处理数据传递以及处理的,首先外部数据我使用了两个,分别是 titleheroestitle 就是网页的标题, heroes 就是英雄列表,这两个数据至少在这篇文章的主从结构的英雄编辑器中是不会有改动的,很好处理,因为根本不用处理,内部数据只有 selectedHero ,这是标识选中的英雄,所以是经常会发生改变的,使用内部数据会使得改变数据更加灵活,使用data 选项时要注意,data 选项必须是函数,所以我在定义 selectedHero 时的定义方法是:

data:function(){
    return {selectedHero:""}
}

关于数据处理,我们可以使用 methods 选项,也可以使用 computed 选项,关于这两者的区别以后会讲到。这一次我使用的是最好理解的 methods 选项,在上面我们说到事件处理方法 onSelect(hero) 的定义就是在 methods 选项中定义的,具体的定义方式为:

methods:{
    onSelect:function(hero){
        this.selectedHero=hero
    }
}

上面代码与 v-on:click="onSelect(hero) 结合起来就是当点击一个 hero 时,就会把 selectedHero 改为对应的 hero ,接下来就会发生,样式改变,英雄编辑器数据的改变等连锁反应。

到这里我们这个主从结构的英雄编辑器就差不多做好了,接下来就是创建一个Vue实例,使用 v-bind 指令将外部数据传入就可以了。

总结

 在五一假期的最后一天写完了这篇文章,感觉很不错。在实现这个主从结构效果的时候确实遇到不少坑,首先,一开始没有理解内部数据以及外部数据的区别,selectedHero 使用 props 选项传入,然后直接使用 this.selectedHero=hero 来进行修改。其实运行还是可以的,但是Vue会发出警告,而且这确实是个不好的习惯,要从源头开始改正。

直接修改外部数据警告

 然后就是摸着石头过河,根本不知道哪些数据需要外部传入,哪些数据只要内部处理。一开始不仅 selectedHero 是通过 props 传入,甚至连 onSelect(hero)selected 等,反正是所有要使用到的数据都通过 props 传入。经过好几次的尝试和不断的查找资料修改才得出最终的结果,可能这也不是最好的结果,毕竟只是尝试了一个下午。况且当他是一个下午出来的最好的结果吧。
&nbps;还有就是 onSelect(hero) 应该是定义在实例中呢,还是定义在组件中呢,一开始也是没有个概念,也是通过查找资料才确定的。onSelect(hero) 主要是对组件的内部数据进行处理的,所以在没弄清楚内部数据和外部数据的时候根本是不可能确定这个方法在哪里定义,当明确了内部数据和外部数据的时候,方法的定义就迎刃而解了。
总结一下知识点:

  • 使用 v-bind:classv-bind:style 进行样式的绑定
  • 使用 v-if 指令族进行条件渲染
  • 使用 v-for 指令进行列表渲染
  • 组件中 propsdata 的区别与使用方法
  • 组件中 methods 选项的定义和使用
  • <template></template>作为渲染容器的作用

可运行代码

hero.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <link rel="stylesheet" href="hero.css">
</head>
<body>
    <div id="host-guest-app">
        <host-guest v-bind:title="title"
            v-bind:heroes="heroes"
            ></host-guest>
    </div>    
</body>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="hero.js"></script>
</html>

hero.js

//十位英雄数据
const HEROES=[
{ id: 11, name: 'Mr. Nice' },
{ id: 12, name: 'Narco' },
{ id: 13, name: 'Bombasto' },
{ id: 14, name: 'Celeritas' },
{ id: 15, name: 'Magneta' },
{ id: 16, name: 'RubberMan' },
{ id: 17, name: 'Dynama' },
{ id: 18, name: 'Dr IQ' },
{ id: 19, name: 'Magma' },
{ id: 20, name: 'Tornado' }
];


//主从结构组件
Vue.component("host-guest",{
    props:['title','heroes'],
    template:'\
    <div>\
    <h1>{{title}}</h1>\
    <h2>My Heroes</h2>\
    <ul class="heroes">\
        <li v-for="hero in heroes" v-on:click="onSelect(hero)"\
            v-bind:class="{selected:hero===selectedHero}">\
            <span class="badge">{{hero.id}}</span>{{hero.name}}\
        </li>\
    </ul>\
    <template v-if="selectedHero">\
        <h2>{{selectedHero.name}} details</h2>\
        <div><label>id: </label>{{selectedHero.id}}</div>\
        <div>\
            <label>name: </label>\
            <input v-model="selectedHero.name" placeholder="name"/>\
        </div>\
    </template>\
    </div>',
    data:function(){
        return {selectedHero:""}
    },
    methods:{
        onSelect:function(hero){
            this.selectedHero=hero
        }
    },

})

var app_host_guest=new Vue({
    el:"#host-guest-app",
    data:{
        title:"Tour of Heroes",
        heroes:HEROES,
    },
})

hero.css

h1 {
color: #369;
font-family: Arial, Helvetica, sans-serif;
font-size: 250%;
}
h2, h3 {
color: #444;
font-family: Arial, Helvetica, sans-serif;
font-weight: lighter;
}.selected {
    background-color: #CFD8DC !important;
    color: white;
}
body {
    margin: 2em;
}
body, input[text] {
    color: #888;
    font-family: Cambria, Georgia;
}
.heroes {
    margin: 0 0 2em 0;
    list-style-type: none;
    padding: 0;
    width: 15em;
}
.heroes li {
    cursor: pointer;
    position: relative;
    left: 0;
    background-color: #EEE;
    margin: .5em;
    padding: .3em 0;
    height: 1.6em;
    border-radius: 4px;
}
.heroes li.selected:hover {
    background-color: #BBD8DC !important;
    color: white;
}
.heroes li:hover {
    color: #607D8B;
    background-color: #DDD;
    left: .1em;
}
.heroes .text {
    position: relative;
    top: -3px;
}
.heroes .badge {
    display: inline-block;
    font-size: small;
    color: white;
    padding: 0.8em 0.7em 0 0.7em;
    background-color: #607D8B;
    line-height: 1em;
    position: relative;
    left: -1px;
    top: -4px;
    height: 1.8em;
    margin-right: .8em;
    border-radius: 4px 0 0 4px;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

若即

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值