vue组件化开发
新接触
最近接触了一下vue.js
,这个框架非常强大,它跟以往我们前端的常规开发很不一样,它基于node.js,使用数据驱动的方式,避免直接去操作DOM,使用它来构建移动端的单页应用十分方便。项目中我采用vue.js + vuex + vue-router + vue-resource + vux+ webpack,来实现一个完整应用的创建,幸好的是,我所说到的这些,都是为了服务于vue.js的,所以它们与vue.js搭配起来用真的十分方便,简化了许多繁琐的DOM操作。
关于vue.js
vue.js它是一个MVVM
的框架,即Model-View-ViewModel
,引用官方的一张图:
vue.js是一个构建数据驱动的web界面的库。Vue.js的目标是通过尽可能简单的API实现响应的数据绑定
和组合的视图组件
。它意味着我们在普通 HTML 模板中使用特殊的语法将 DOM “绑定”到底层数据。一旦创建了绑定,DOM 将与数据保持同步。每当修改了数据,DOM 便相应地更新。这样我们应用中的逻辑就几乎都是直接修改数据了,不必与DOM更新搅在一起。这让我们的代码更容易撰写、理解与维护。这与Angular.js有点类似。
安装
如果是急需创建一个大型应用的话,使用命令行vue-cli
十分方便,它直接创建好了一个基于vue的框架
有两个极力推荐的参考项目:
cnodejs-vue
vue-zhihu-daily
Vue.js 提供一个官方命令行工具,可用于快速搭建大型单页应用。该工具提供开箱即用的构建工具配置,带来现代化的前端开发流程。只需一分钟即可启动带热重载、保存时静态检查以及可用于生产环境的构建配置的项目:vue官网
1
2
3
4
5
6
7
8 # 全局安装 vue-cli
$ npm install -g vue-cli
# 创建一个基于 "webpack" 模板的新项目
$ vue init webpack my-project
# 安装依赖,走你
$ cd my-project
$ npm install
$ npm run dev(需要一直开着)
执行完最后一步后,使用npm run build
来打包项目,然后在默认的http://localhost:8080/就可以看到项目主页了。
接下来,你可以参考其他的文档安装配套使用的库:1
2
3
4
5
6
7
8
9
10
11
12
13安装vue-resource:
$ npm install vue-resource
安装vue-router:
$ npm install vue-router
安装vuex:
$ npm install --save vuex
安装vux样式库:
npm install vux
#安装less-loader, vuejs-templates模板默认不安装less less-loader
npm install less less-loader --save-dev
一些说明
可能很多人在开始使用vue的时候都一脸懵逼,因为这和我们平常做的常规项目确实是很不一样,踩坑是很正常的事情,那么我想分享几点可能大家在开始上手的时候遇到的疑惑点。
安装的这些库分别是干什么?
首先讲vuex,它是用来进行状态管理的,将store里的数据和页面绑定起来,并通过action进来重新分发到页面,实现数据实时改变。参考官方的图:
Vuex 的四个核心概念分别是:
The state tree:Vuex 使用单一状态树,用一个对象就包含了全部的应用层级状态。至此它便作为一个『唯一数据源(SSOT)』而存在。这也意味着,每个应用将仅仅包含一个 store 实例。单状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。
Getters:用来从 store 获取 Vue 组件数据。
Mutators:事件处理器用来驱动状态的变化。
Actions:可以给组件使用的函数,以此用来驱动事件处理器 mutationsvue-router
顾名思义,主要用来路由页面使用,通过配置map函数,来定义单页应用路由的地址,避免了单页应用去到某一个页面的时候刷新而回到首页的问题,这个对于单页应用是个很大的优化。- vue-resource
这个类似于JQuery中的$.ajax
,用来请求服务器端的数据。 - vux
Vux = Vue + WeUI + A Bunch of Components,这个库是基于vue和微信样式开发的,所以使用起来很搭配。
vue的小分享
1.vue组件的特点是可插拔、独立作用域、观察者模式、完整的生命周期。
在Vue.js中,父子之间的通信主要通过事件来完成,这种就是我们熟悉的观察者模式(或叫订阅-发布模式),很多框架也都使用了这种设计模式,比如Angular。父组件通过Vue内置的$broadcast()向下广播事件和传递数据,子组件通过$dispatch()向上派发事件和传递数据,双方都可以在events对象内接收自定义事件,并且处理各自的业务逻辑。
引用官方模板:
子组件传给父组件: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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50<!-- 子组件模板 -->
<template id="child-template">
<input v-model="msg">
<button v-on:click="notify">Dispatch Event</button>
</template>
<!-- 父组件模板 -->
<div id="events-example">
<p>Messages: {{ messages | json }}</p>
<child></child>
</div>
-----------------------------------------------
// 注册子组件
// 将当前消息派发出去
Vue.component('child', {
template: '#child-template',
data: function () {
return { msg: 'hello' }
},
methods: {
notify: function () {
if (this.msg.trim()) {
this.$dispatch('child-msg', this.msg)
this.msg = ''
}
}
}
})
// 初始化父组件
// 将收到消息时将事件推入一个数组
var parent = new Vue({
el: '#events-example',
data: {
messages: []
},
// 在创建实例时 `events` 选项简单地调用 `$on`
events: {
'child-msg': function (msg) {
// 事件回调内的 `this` 自动绑定到注册它的实例上
this.messages.push(msg)
}
}
})
或者父组件使用:
<child v-on:child-msg="handleIt"></child>
再在methods中标明handleIt函数。
父组件传给子组件:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<!-- 默认为单向绑定 -->
<child :msg="parentMsg"></child>
<!-- 双向绑定 -->
<child :msg.sync="parentMsg"></child>
<!-- 单次绑定 -->
<child :msg.once="parentMsg"></child>
子组件接受参数:
Vue.component('example', {
props: {
msg: {
type: String,
required: false
}
}
})
2.关于样式:
我们给按钮定义了一些基本的样式,但是我们用的css选择器就是一个.button类名,可能会和别的组件中的.button样式冲突,所以我们加入了一个scoped属性,让App.vue中的style样式只作用于这个组件内部。
注意:scoped并不会影响css的作用优先级,使用scoped不代表不会被外部样式表覆盖1
2
3<style scpped>
...
</style>
3.vue动态组件
例如我们要实现一个tab切换的效果,我们设计两个tab,每点击一个tab是切换一个子组件,然后再把事件添加到两个tab上;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
30
31
32
33
34
35
36
37
38
39
40
41//父组件
<nav>
<ul class="nav nav-pills pull-right">
<li role="presentation">
<a href="#" @click="currentView='manage-posts'">Manage Posts</a>
</li>
<li role="presentation">
<a href="#" @click="currentView='create-post'">Create Post</a>
</li>
</ul>
</nav>
<div class="container">
<!-- render the currently active component/page here -->
<component :is="currentView" keep-alive></component>
</div>
Vue.component('manage-posts', {
template: '#manage-template',
data: function() {
return {
posts: [
'Vue.js: The Basics',
'Vue.js Components',
'Server Side Rendering with Vue',
'Vue + Firebase'
]
}
}
})
Vue.component('create-post', {
template: '#create-template'
})
new Vue({
el: 'body',
data: {
currentView: 'manage-posts'
}
})
如果把切换出去的组件保留在内存中,可以保留它的状态或避免重新渲染。为此可以添加一个 keep-alive
指令参数
4.不要将HTML的Attributes和Vue的Model混用
比如最终要实现的代码是:1
2
3<img src=123.jpg>
<a href="index.html?id=123"></a>
<div id="id-123"></div>
比如Vue实例为:1
2
3
4
5
6new Vue({
el: 'body',
data: {
id: 123
}
})
错误 的使用是:1
2
3<img src={{ id }}.jpg>
<a href="index.html?id={{ id }}"></a>
<div id="id-{{ id }}"></div>
正确 的使用是:1
2
3<img :src=id + '.jpg'>
<a :href="'index.html?id=' + id"></a>
<div :id="'id-' + id "></div>
5.使用组件的时候传的参数要用小写不能用大小写混合,或者用横杠-连接:userinfo=”userinfo”1
2
3
4<div>
<cmp-account :userinfo="userinfo"></cmp-account>
<cmp-task :task="task"></cmp-task>
</div>
6.理解异步更新
默认情况下, Vue 的 DOM 更新是异步执行的。理解这一点非常重要。当侦测到数据变化时, Vue 会打开一个队列,然后把在同一个事件循环 (event loop) 当中观察到数据变化的 watcher 推送进这个队列。假如一个 watcher 在一个事件循环中被触发了多次,它只会被推送到队列中一次。然后,在进入下一次的事件循环时, Vue 会清空队列并进行必要的 DOM 更新。在内部,Vue 会使用 MutationObserver 来实现队列的异步处理,如果不支持则会回退到 setTimeout(fn, 0)。
举例来说,当你设置 vm.someData = ‘new value’,DOM 并不会马上更新,而是在异步队列被清除,也就是下一个事件循环开始时执行更新。如果你想要根据更新的 DOM 状态去做某些事情,就必须要留意这个细节。尽管 Vue.js 鼓励开发者用 “数据驱动” 的方式想问题,避免直接操作 DOM ,但有时候你可能就是想要使用某个熟悉的 jQuery 插件。这种情况下怎么办呢?你可以在数据改变后,立刻调用 Vue.nextTick(callback),并把你要做的事情放到回调函数里面。当Vue.nextTick 的回调函数执行时,DOM 将会已经是更新后的状态了。
示例:1
2
3
4
5
6
7
8
9
10
11
12
13<div id="example">{{msg}}</div>
var vm = new Vue({
el: '#example',
data: {
msg: '123'
}
})
vm.msg = 'new message' // change data
vm.$el.textContent === 'new message' // false
Vue.nextTick(function () {
vm.$el.textContent === 'new message' // true
})
除此之外,也有一个实例方法 vm.$nextTick()。这个方法和全局的 Vue.nextTick 功能一样,但更方便在组件内部使用,因为它不需要全局的 Vue 变量,另外它的回调函数的 this 上下文会自动绑定到调用它的 Vue 实例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17Vue.component('example', {
template: '<span>{{msg}}</span>',
data: function () {
return {
msg: 'not updated'
}
},
methods: {
updateMessage: function () {
this.msg = 'updated'
console.log(this.$el.textContent) // => 'not updated'
this.$nextTick(function () {
console.log(this.$el.textContent) // => 'updated'
})
}
}
})
总结
相对于react native来说的话,vue.js是比较轻量级的,而且相对比较容易上手,对于单页应用来说无疑是带来了福音,做完项目后也是学到了很多知识,得到了很多经验,对于组件化的开发又有了深刻的认识,站在巨人的肩膀上又看远了那么一点。