撸一遍vue的基础特性

撸一遍vue的基础特性

Vue.js无疑是最近最火的一套渐进式前端框架。Vue的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。其轻量级,渐进式的优点对开发者的吸引力毋庸置疑。我使用过两年的Angular,对Vue也充满了好奇,于是也想认识一下Vue。

Vue.js安装

主流的安装方式当然是使用vue-cli工具,这跟angular挺像的。但是作为刚入门的开发者,官方比较推荐的安装方式是直接引入script。vue脚本也分为开发版vue.js和生产版vue.min.js,开发环境下推荐使用vue.js,可以在开发过程中看到常见错误相关的警告。你可以选择使用CDN服务,直接引入这个脚本即可进行开发。

1
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.14/dist/vue.js"></script>

我还是习惯直接把脚本下载到项目文件夹中。

1
<script src="./lib/vue.js"></script>

学习Vue

学习Vue.js最好的方法当然是把代码都撸一遍,这样印象会比较深刻。

v-if

这个指令类似于angular的*ngIf,作为一个结构性指令,用来控制元素的插入和移除。当v-if=”true”时,元素正常显示;反之元素则被移除。

v-show

这个指令达到的效果看起来与v-if是一样的,但是从本质上来讲是不同的。v-show其实仅仅从样式上来控制显示和隐藏,改变的是display属性。与jQuery的show()和hide()方法有异曲同工之妙。

插值表达式

1
<span>{{ content }}</span>

这个是数据绑定的标准写法,与angular是一样的。content变量的值改变时,页面渲染也随之变化。插值表达式中支持js表达式。

v-html

从字面意思能看出来,该指令绑定的值会输出html内容,类似于jQuery的html()方法。在angular中好像没有看到这个指令,我是自定义了一个directive来实现的。

属性绑定v-bind

这个指令用来绑定元素属性,通常用于向子组件传递props。比如子组件studentList内部需要属性id来做逻辑判断,这个id可以由父组件来传递,这里就可以使用到v-bind。

1
<student-list v-bind:id="somevalue"></student-list>

v-bind:可以缩写为:

1
<student-list :id="somevalue"></student-list>

事件绑定v-on

用来绑定事件,比如鼠标点击事件,可以这样写:

1
<button v-on:click="toggleVueIf">Toggle VUE IF</button>

v-bind:可以缩写为@:

1
<button @click="toggleVueIf">Toggle VUE IF</button>

v-for

v-for用来遍历数组或对象
(1)数组,接收的参数上可以是两个,顺序是value, index

1
2
3
<ul v-for="(value, index) in vueForArray">
<li>{{ index }}: {{ value }}</li>
</ul>

(2)对象,接收的参数上可以是三个,顺序是value, key, index

1
2
3
<ul v-for="(value, key, index) in vueForObject">
<li>{{ index }}. {{ key }} : {{ value }}</li>
</ul>

计算属性computed

对于插值表达式中涉及到的复杂计算,建议用计算属性computed来替代。在new Vue时可以使用computed属性。

1
2
3
4
5
6
7
8
// html中
{{ reversedMessage }}
// vue实例中
computed: {
reversedMessage: function() {
return this.testMessage.split('').reverse().join('');
}
}

这里只是一个倒序的算法,不算很复杂,只是举例说明。

watch属性

我们可以使用watch来进行侦听,来响应数据的变化。上面计算属性的应用,我们也可以改用watch来写:

1
2
3
4
5
watch: {
testMessage: function() {
this.reversedMessage = this.testMessage.split('').reverse().join('');
}
}

这里,我们通过侦听testMessage的变化来做出响应,用于改变reversedMessage的值。

ps:那么computed和watch的应用场景怎么区分?
个人认为,computed更适合做一些数据值的计算,而涉及到侦听某变量需要做一些业务处理时,建议使用watch。

class绑定

1
2
<!-- 类似这种形式,与属性绑定大同小异。class的绑定值可以是键值对,也可以是数组。 -->
<div :class="{ category: isCategory, red: isRed }"></div>

style绑定

与class绑定类似,支持键值对语法(css键值对),也支持数组(多个样式对象同时作用)。

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

双向绑定v-model

这一特性与angular的[(ngModel)]是一致的。通常用于表单元素。

1
2
<input v-model="message" placeholder="edit me">
<p>Message is: {{ message }}</p>

不过v-model还支持很多修饰符,如
(1).lazy

1
2
<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="msg" >

(2).number
自动将用户的输入值转为数值类型
(3).trim
自动过滤用户输入的首尾空白字符

.sync

看名字就知道了,同步的意思,如果绑定的属性加上这个修饰符,就实现了双向绑定。.sync破坏了单向数据流,于2.0 中移除,但又在 2.3.0 版本后以语法糖形式重新引入。

1
<comp :foo.sync="bar"></comp>

父子组件通信

父组件通过属性传递数据给子组件props,而子组件以this.$emit(eventname, data)的方式发事件给父组件,父组件模板中需要绑定对应事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 父组件模板,通过绑定greet传递数据给子组件,@replay接收来自子组件的事件。
<child :greet="greetMsg" @reply="showReply"></child>
// 子组件props属性接收
props: ['greet']
// 子组件模板
<p>Greet from Father Component: {{greet}}</p>
<button @click="replyToFather">Reply</button>
// 点击button时调用replyToFather方法,通过$emit发送事件给父组件。
methods: {
replyToFather: function() {
this.$emit('reply', this.replyInfo);
}
}

slot

类似angular的ng-content。父组件可以将想要的内容插在子组件的插槽中。子组件slot标签通过name属性区分插槽,如name=”slot1”,不写name属性的就是默认插槽了;而父组件要在传递给插槽的标签中加上slot属性来区分插在哪个槽中,如slot=”slot1”,如果不加slot属性,则自动插在默认插槽中。slot标签中的内容都被视为备用内容,如果父组件不给子组件传入内容,则会显示子组件slot标签中的备用内容。使用如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- 父组件模板 -->
<child>
<!-- 没有指定slot属性,所以插在子组件中没有name属性的slot位置 -->
<p>Message From Father</p>
<!-- 指定了slot属性的值为head,所以插在子组件中name属性值为head的slot位置 -->
<p slot="head">For Head</p>
<!-- 指定了slot属性的值为foot,所以插在子组件中name属性值为foot的slot位置 -->
<p slot="foot">For Foot</p>
<!-- 没有指定slot属性,所以插在子组件中没有name属性的slot位置 -->
<p>Other information...</p>
<child>
<!-- 子组件模板 -->
<div>
<slot name="head">head content...</slot>
<slot>Backup content...</slot>
<slot name="foot">foot content...</slot>
</div>

作用域插槽

官方提供的说明我不太看得懂,根据我个人的理解,一个很重要的应用场景就是用来抽象列表式组件。比如一个展示性的列表组件,但是具体展示图文,还是其他的,需要由其父组件来指定。我这里写了一个demo。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 父组件模板 -->
<my-list :items="movies">
<template slot-scope="props">
<li>
<img class="poster" :src="props.item.posterUrl"/>
<div>{{ props.item.moviename }}</div>
</li>
<!-- 我可以在这里调用各种组件,来适应各种需要,比如我可以做海报展示,那么我只要一个<img>;比如我只要电影名称的列表,那么就只要用到电影名称 -->
</template>
</my-list>
<!-- 子组件模板,模板中的items是由props接收的 -->
<ul>
<span class="hot-movie">近期热映</span>
<slot name="slot-item" v-for="item in items" :item="item"></slot>
</ul>

使用要点:
(1)父组件通过属性绑定将数据(一般是数组,毕竟是用于列表展示嘛)传递给子组件。

1
<my-list :items="movies"></my-list>

(2)父组件传递给子组件的内容用template标签包裹,template标签具备slot-scope=”props”,props来源于子组件slot标签绑定的属性集,而本例中slot标签绑定了item属性,所以template中的内容可以调用props.item下的属性。

动态组件

利用component标签及其is属性可以动态切换组件。curComponent的值为组件名。

1
<component :is="curComponent"></component>

keep-alive标签

利用keep-alive标签包裹component标签可以让切换组件保留在内存中,可以保留它的状态,避免重新渲染。

1
2
3
<keep-alive>
<component :is="curComponent"></component>
</keep-alive>

ref属性

ref属性实现了组件的引用,不过应当避免使用ref。

1
2
3
<ref-comp ref="refComp"></ref-comp>
<!-- 我们可以在父组件中直接改变子组件中的refValue属性 -->
this.$refs.refComp.refValue += 'a';

transition过渡

在组件外包裹一层transition标签,name标识transition,即可为组件加上过渡效果。如:

1
2
3
<transition name="fade">
<p v-show="showFade">Fade</p>
</transition>

在进入/离开的过渡中,会有 6 个 class 切换,默认 class 名称格式如下。

  • fade-enter // 进入过渡开始,一般在这个class下定义-过渡开始时的css状态
  • fade-enter-active // 进入过渡活跃状态,一般在这个-class下定义进入过渡过程中的动画
  • fade-enter-to // 进入过渡结束,一般在这个class下定义过渡结束时的css状态。ps:离开过渡不再说明,类似开始过渡。
  • fade-leave // 离开过渡开始
  • fade-leave-active // 离开过渡活跃状态
  • fade-leave-to // 离开过渡结束

我们也可以自定义过渡类名,需要增加transition标签的属性,这样就可以使用第三方的动画库了。用法如下:

1
2
3
<transition name="fade" enter-active-class="animated tada" leave-active-class="animated bounceOutRight">
<p v-show="showFade">Fade</p>
</transition>

过渡效果主要的应用场景有:

  • v-if
  • v-show
  • 动态组件

transition有着一系列javascript钩子函数,从字面意思可以与过渡的class对应起来,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<transition
:css="false"
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
@enter-cancelled="enterCancelled"

@before-leave="beforeLeave"
@leave="leave"
@after-leave="afterLeave"
@leave-cancelled="leaveCancelled"
>
......
</transition>

钩子函数都有参数el,用于引用添加了transition包裹的元素。enter和leave钩子函数中额外有一个参数done,done是一个回调函数,是必须在最后调用的。否则,它们会被同步调用,过渡会立即完成。

1
2
3
4
enter: function (el, done) {
// ...
done();
}

有了js钩子函数,那么我就可以不使用class来定义过渡的效果了,我可以使用js动画库,如velocity.js,这个时候最好添加v-bind:css=”false”,Vue 会跳过 CSS 的检测。这也可以避免过渡过程中 CSS 的影响。

我们可以给元素设置初始渲染的过渡效果。

1
2
3
<transition appear>
<!-- ... -->
</transition>

appear拥有相同的class钩子和js钩子,也可以自定义类名。

多个元素的过渡

transition中多个元素过渡时,可以这样写,给每个元素加上key属性来作为唯一标识:

1
2
3
4
5
6
7
8
<transition>
<button v-if="isEditing" key="save">
Save
</button>
<button v-else key="edit">
Edit
</button>
</transition>

上述代码也可以简写为绑定key的形式,而不再需要if,else:

1
2
3
4
5
<transition>
<button :key="isEditing">
{{ isEditing ? 'Save' : 'Edit' }}
</button>
</transition>

其中的isEditing ? ‘Save’ : ‘Edit’也可以抽象为一个computed属性。

除了多个元素的过渡,类似的还有动态组件的过渡。

1
2
3
<transition name="fade-dynamic" mode="out-in">
<component :is="curComponent"></component>
</transition>

mode属性定义了过渡模式,防止当前元素的离开与下一个元素的进入产生重叠现象:

  • in-out:新元素先进行过渡,完成之后当前元素过渡离开。
  • out-in:当前元素先进行过渡,完成之后新元素过渡进入。

列表过渡

列表过渡用到的组件是transition-group。transition-group会以真实的元素存在于html文档中,而transition是不存在于html文档中的。transition-group存在的形式由tag属性指定,如

1
2
3
4
5
<transition-group name="group-list" tag="div">
<div v-for="item in groups" :key="item.id" class="group-item">
<img class="group-item-img" :src="item.picUrl"/>
</div>
</transition-group>

为了让group-list的过渡效果平滑,有一个v-move属性,也是通过绑定class实现

1
2
3
.group-list-move {
transition: transform 1s;
}

另一种实现方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
.group-item {
margin: 10px;
display: inline-block;
width: 100px;
height: 60px;
transition: all 1s;
}
.group-item-img {
width: 100%;
height: 100%;
}
/* 这个作用是让元素在离开的过程中不占位,使过渡看起来十分平滑 */
.group-list-leave-active {
position: absolute;
}
/* .group-list-enter-active, .group-list-leave-active {
transition: all 1s;
} */
.group-list-enter, .group-list-leave-to {
opacity: 0;
transform: translateY(30px);
}
/* move属性作用于元素的改变定位的过程中,这里会让transform有一个过渡效果;
如果需要让列表整体有一个整体性过渡,那么可以在group-item类中加上transition属性,
那么也就不需要group-list-enter-active和group-list-leave-active类中有transition属性了。
*/
/* .group-list-move {
transition: transform 1s;
} */

mixin

mixin是一种提高编写效率的写法,详情请参考mixin

vue自定义指令

一个指令定义对象可以提供如下几个钩子函数。

  • bind:只调用一次,指令第一次绑定到元素时调用。
  • inserted:被绑定元素插入父节点时调用
  • update
  • componentUpdated
  • unbind

目前未对指令进行深究,只写了一个简单的指令。

1
2
3
4
5
6
7
directives: {
autoClick: {
inserted: function(el) {
el.click();
}
}
}

vue组件生命周期

参考:
(1)Vue2.0 探索之路——生命周期和钩子函数的一些理解
(2)API


扫一扫下方小程序码或搜索Tusi博客,即刻阅读最新文章!

Tusi博客

You forgot to set the qrcode for Alipay. Please set it in _config.yml.
You forgot to set the qrcode for Wechat. Please set it in _config.yml.
You forgot to set the business and currency_code for Paypal. Please set it in _config.yml.
You forgot to set the url Patreon. Please set it in _config.yml.
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×