lanbos'blog

js的mvvm(2)

vue处理事件

当用户提交表单时,传统的做法是响应onsubmit事件,用jQuery获取表单内容,检查输入是否有效,最后提交表单,或者用AJAX提交表单。
现在,获取表单内容已经不需要了,因为双向绑定直接让我们获得了表单内容,并且获得了合适的数据类型。
响应onsubmit事件也可以放到VM中。我们在< form >元素上使用指令:

1
<form id="vm" v-on:submit.prevent="register">

其中,v-on:sumbit=”register”指令就会自动监听表单的submit事件,并调用register方法处理该事件。使用.prevent表示阻止事件冒泡,这样,浏览器不再处理FORM的submit事件。
因为我们指定了事件处理函数是register,所以需要在创建VM时添加一个register函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
var vm = new Vue({
el: '#vm',
data: {
...
},
methods: {
register: function () {
// 显示JSON格式的Model:
alert(JSON.stringify(this.$data));
// TODO: AJAX POST...
}
}
});

点击事件、鼠标经过事件等与其都类似。

模板循环判断等

模板最重要和最常用的功能就是条件渲染和循环渲染列表,mvvm框架的view层也都提供了类似的用法:

条件渲染

与常见的字符串模板不同,mvvm框架的条件循环是借助元素的指令完成的,所以通常会借助一个空标签template来完成,最终的渲染结果不包含此空标签:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div id="vm">
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
</div>
<script type="text/javascript">
$(function() {
var vm = new Vue({
el: '#vm',
data: {
ok: true
}
})
});
</script>

列表循环渲染

列表循环的方式和条件循环类似,最好通过空标签template进行渲染:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<ul id="vm">
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider"></li>
</template>
</ul>
<script type="text/javascript">
$(function() {
var vm = new Vue({
el: '#vm',
data: {
items: [
{ msg: 'Foo' },
{ msg: 'Bar' }
]
}
})
});

</script>

当然也可以直接把指令挂在需要循环的元素上,循环渲染。需要注意的是,Vue之所以能够监听Model状态的变化,是因为JavaScript语言本身提供了Proxy或者Object.observe()机制来监听对象状态的变化。但是,对于数组元素的赋值,却没有办法直接监听,因此,如果我们直接对数组元素赋值:

1
2
3
vm.items[0] = {
msg: 'New name',
};

会导致Vue无法更新View。
正确的方法是不要对数组元素赋值,而是更新:

1
vm.items[0].msg='New name';

vue插件的简单介绍

vue-resource ajax请求插件

可以直接通过script标签引入,或者其他自动化工具。

1
<script src="https://cdn.jsdelivr.net/vue.resource/1.0.3/vue-resource.min.js"></script>

我们给VM增加一个init()方法,读取后台发送的列表:

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
var vm = new Vue({
el: '#vm',
data: {
title: 'TODO List',
todos: []
},
created: function () {
this.init();
},
methods: {
init: function () {
var that = this;
that.$http.get('/api/todos').then(function (resp) {
// 调用API成功时调用json()异步返回结果:
resp.json().then(function (result) {
// 更新VM的todos:
that.todos = result.todos;
});
}, function (resp) {
// 调用API失败:
alert('error');
});
}
}
});

jsonp示例:

1
2
3
4
this.$http.jsonp(jsonpUrl,{jsonpCallback:jsonpName}).then(function(rep){
rep=rep.body;
console.log(rep);
})

注意到创建VM时,created指定了当VM初始化成功后的回调函数,这样,init()方法会被自动调用。
类似的,对于添加、修改、删除的操作,我们也需要往VM中添加对应的函数。以添加为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var vm = new Vue({
...
methods: {
...
create: function (todo) {
var that = this;
that.$resource('/api/todos').save(todo).then(function (resp) {
resp.json().then(function (result) {
that.todos.push(result);
});
}, showError);
},
update: function (todo, prop, e) {
...
},
remove: function (todo) {
...
}
}
});

vue-router 2

前后分离的开发方式把很多后端逻辑放到了前端来进行实现,spa单页面网站的流行,更是将路由和部分其他中间件交给了前端来实现,mvvm框架的流行很大一部分也是基于spa的兴起。vue拥有官方路由插件vue-router,用 Vue.js + vue-router 创建单页应用,是非常简单的。使用 Vue.js 时,我们就已经把组件组合成一个应用了,当你要把 vue-router 加进来,只需要配置组件和路由映射,然后告诉 vue-router 在哪里渲染它们。下面是个基本例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script src="https://cdn.bootcss.com/vue-router/2.0.0/vue-router.js"></script>
<div id="app">
<h1>Hello App!</h1>
<p>
<!-- 使用 router-link 组件来导航. -->
<!-- 通过传入 `to` 属性指定链接. -->
<!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
</div>

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
// 0. 如果使用模块化机制编程, 要调用 Vue.use(VueRouter)

// 1. 定义(路由)组件。
// 可以从其他文件 import 进来
var Foo = { template: '<div>foo</div>' }
var Bar = { template: '<div>bar</div>' }

// 2. 定义路由
// 每个路由应该映射一个组件。 其中"component" 可以是
// 通过 Vue.extend() 创建的组件构造器,
// 或者,只是一个组件配置对象。
// 我们晚点在讨论嵌套路由。
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]

// 3. 创建 router 实例,然后传 `routes` 配置
// 你还可以传别的配置参数, 不过先这么简单着吧。
var router = new VueRouter({
routes // (缩写)相当于 routes: routes
})

// 4. 创建和挂载根实例。
// 记得要通过 router 配置参数注入路由,
// 从而让整个应用都有路由功能
var app = new Vue({
router
}).$mount('#app')

// 现在,应用已经启动了!

总结

从几个例子我们可以看到,MVVM最大的优势是编写前端逻辑非常复杂的页面,尤其是需要大量DOM操作的逻辑,利用MVVM可以极大地简化前端页面的逻辑。
但是MVVM不是万能的,它的目的是为了解决复杂的前端逻辑。对于以展示逻辑为主的页面,例如,新闻,博客、文档等,不能使用MVVM展示数据,因为这些页面需要被搜索引擎索引,而搜索引擎无法获取使用MVVM并通过API加载的数据。
所以,需要SEO(Search Engine Optimization)的页面,不能使用MVVM展示数据。不需要SEO的页面,如果前端逻辑复杂,就适合使用MVVM展示数据,例如,工具类页面,复杂的表单页面,用户登录后才能操作的页面等等。