一、MVC模式和MVVM模式的区别
由于vue框架采用的是mvvm模式的开发,这里把MVC模式和MVVM两种模式做一个详细的解释说明。
在解释两种模式之前,首先要先理解一个概念,MVC是一种设计模式,这个模式既可以用在前端也可以用在后端,基于这个模式衍生出许多mvc框架,比如SpringMVC、Struts2、Tapestry、JSF等。但是千万不要把mvc错误认为就是SpringMVC,模式是模式,模式是可以复用到很多地方的,框架是框架,框架的出现是为了解决某一特定的问题的。比如springmvc就是为了解决网站应用程序或者服务web开发——URL路由、Session、模板引擎、静态Web资源等这些问题而出现的一个MVC框架。
MVC模式:  mvc即Model、View、Controller,从上图来理解就是,用户对View进行的操作(这个操作很多比如提交了表单,input框输入了内容等)交给了Controller来处理,控制器Controller调用方法去执行操作相应的Model,一旦Model发生变化后View立即做出相应。
如果前端没有框架,我们可以简单去这么理解,html就是View,js相当于Controller用来监听view的操作(比如点击,输入等),把ajax当成Model层,js就是对Model进行操作,并完成Model与View的同步。当然这个解释多少是有点牵强了,但目的是为了让我们更简单去从前端的层面理解这个MVC模式的应用场景。
MVC模式的应用在前端看似是OK的,但是随着时间的发展,前端的业务逻辑随之也变得十分的复杂,这种在前端使用的MVC模式就暴露出了几个问题:
- 开发者在前端代码中大量调用相同DOM API ,处理繁琐,操作冗余。
- 大量的DOM操作使得页面渲染性能降低,加载速度变慢。
- 最重要的一点,当Model发生变化,开发中需要主动更新到View,而View发生变化,开发者又要将变化的数据同步到Model中,这样不仅工作繁琐而且对一些复杂的数据很难维护。
而MVVM模式的出现,完美的解决了上面这三个问题。
MVVM模式:  MVVM模式与MVC模式最大的区别就是它实现了View和Model的自动同步,也就是常说的双向绑定。当Model改变时View也会随之变化,而不用我们手动去操作dom来达到改变View的操作了,反之亦然。目前实现MVVM模式的框架有Vue、AngularJS等。
MVVM即Model、View、ViewModel,其中Model和View没什么说的和mvc中的一样,关键是这个ViewModel层。 ViewModel 是由前端开发人员组织生成和维护的视图数据层。在这一层,前端开发者对从后端获取的Model 数据进行转换处理,做二次封装,以生成符合 View 层使用预期的视图数据模型。
ViewModel所封装出来的数据模型包括视图的状态和行为两部分
- 视图状态:比如页面的这一块展示什么,那一块展示什么这些都属于视图状态
- 视图行为:页面加载进来时发生什么,点击这一块发生什么,这一块滚动时发生什么这些都属于视图行为(交互)
视图状态和行为都封装在了 ViewModel 里。这样的封装使得 ViewModel 可以完整地去描述 View 层。由于实现了双向绑定,ViewModel 的内容会实时展现在 View 层,这是激动人心的,因为前端开发者再也不必低效又麻烦地通过操纵 DOM 去更新视图(MVVM帮我们做好了这块,而这块也恰恰是最繁琐和重复的一块内容)。
简单来说,View层展示的是ViewModel中封装好的数据,ViewModel层用来和Model层交互,通过ViewModel也就完全解耦了View层和Model层。
总结: MVC模式和MVVM模式都是为了解决现有的问题而出现,基于这两种模式可以开发出相应的框架来解决具体的问题,值得注意的一点是模式是模式,框架是框架,两者的概念不能搞混。.
再一个,有时候我们摸不清分层的界限。比如MVC模式中,到底哪一块是Model,哪一块是Controller的代码,又或者MVVM模式中,Model和ViewModel的区分严格界限到底在哪里,其实这些界限并不重要,重要的是这两种模式的基本思想,根据这中模式的思想设计出相应的框架来解决实际开发中的问题这个才是最重要的。
二、Vue概述
Vue (读音 /vju?/,类似于 view) 是一套用于构建用户界面的渐进式JavaScript框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用(SPA)提供驱动。
为什么要使用Vue.js?
- 轻量级,体积小,Vue.js压缩后只有22kb左右(Angular 压缩后 56kb+,React压缩后 44kb+)
- 移动优先。更适合移动端,比如移动端的 Touch 事件
- 吸取了 Angular(模块化)和 React(虚拟 DOM)的长处,并拥有自己独特的功能,如:计算属性
- 易上手,文档全,开源,活跃度高等
第一个Vue程序,Hello,Vue !
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-if="http://www.w3.org/1999/xhtml"
xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>HelloVue</title>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
</head>
<body>
<div id="app" >
{{message}} // 双括号用来获取数据
</div>
</body>
<script>
var vm = new Vue({
el:"#app",
data:{
message:"hello vue!"
}
});
</script>
</html>
三、Vue.js基础语法
每一个前端的框架肯定都有自己的基础语法,这些语法说白了就是实现元素赋值,循环,判断,以及事件响应即可!
v-bind、v-on、v-if、v-for
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-if="http://www.w3.org/1999/xhtml"
xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>vue基本语法</title>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
</head>
<body>
<div id="app" v-bind:title="message">
<span :title="message">aaa</span>
{{message}}
<button v-on:click="sayHello">点击测试</button>
<button @click="sayHello">点击测试</button>
<li v-if="ok">123</li>
<li v-if="type=='A'">A</li>
<li v-else-if="type=='B'">B</li>
<li v-else>C</li>
<li v-for="m in messageList">
{{m.num}}
</li>
</div>
</body>
<script>
var vm = new Vue({
el:"#app",
data:{
message:"hello vue!",
ok:false,
type:"A",
messageList:[{num:"1"},{num:"2"},{num:"3"}]
},
methods: {
sayHello: function () {
alert(this.message);
}
}
});
</script>
</html>
v-model:双向绑定,MVVM的实现之处,上面的v-bind可以看出数据发生改变后视图就会相应改变,v-model一般用在input、textarea、select上,即视图View发生改变后,Model数据也会立即变化。
<!DOCTYPE html>
<html lang="en" xmlns:v-model="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>vue双向绑定</title>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
</head>
<body>
<div id="app">
输入的文本 <input type="text" v-model="message"> <br>
展示的文本:{{message}}<br>
<select v-model="msg">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
选中了:{{msg}}
</div>
</body>
<script>
var vm = new Vue({
el:"#app",
data:{
message:"123",
msg:''
},
})
</script>
</html>
四、组件
组件是Vue.js中最强大的功能之一,组件可以扩展html元素,封装可重用的代码。组件说白了就是一组可重复使用的模板。  若还是不太理解,可以观察上图,一个页面可能包含多个模块,例如页面头部、侧边栏、内容区等多个模块,这每一个模块对应的就是一个组件,每一个大的组件下又可以有很多小的组件例如导航链接、博文之类的小组件。
在vue中的所有页面都是一个个的组件拼接出来的,它不像我们之前的有很多.jsp,.html文件,每一个页面对应一个文件,在vue中是很多很多的.vue组件,组件之间的相互引用组成了一个个的页面。  如上图,在实际开发中组件是最重要的(或者说你可以把组件当前原来的一个个的模板页面jsp或thymeleaf),页面只有一个。
下面使用简单的示例,来解释组件的应用,当然在开发中肯定不是下面这么用的,下面这个示例只是为了让我们更简单的去理解什么是组件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>组件</title>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
</head>
<body>
<div id="app">
<hhl v-for="item in items" v-bind:da="item"></hhl>
<hhl v-bind:da="123456"></hhl>
</div>
</body>
<script>
Vue.component("hhl",{
props:['da'],
template:'<li>{{da}}</li>'
});
var vm = new Vue({
el:"#app",
data:{
items:['a','b','c']
}
});
</script>
</html>
五、插槽
案例:比如准备制作一个待办事项组件(todo),该组件由待办标题(todo-title)和待办内容(todo-items)组成,但这三个组件又是相互独立的,该如何操作呢? 我们的目的是为了让这三个组件可以嵌套组合,也就是说todo里面放入todo-title和todo-items。这就用到了插槽。
在 Vue 中我们使用 元素,作为承载分发内容的出口,作者称其为 插槽,可以应用在组合组件的场景中。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>插槽</title>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
</head>
<body>
<div id="app">
<todo>
<todo-title slot="todo-title" title="NBA季后赛"></todo-title>
<todo-items slot="todo-items" v-for="(item,idx) in todoItems"
v-bind:index="idx" v-bind:item="item"
v-on:remove="removeItems(idx)"></todo-items>
</todo>
</div>
</body>
<script>
Vue.component('todo',{
template: `<div>
<slot name="todo-title"></slot>
<ul>
<slot name="todo-items"></slot>
</ul>
</div>`
});
Vue.component('todo-title',{
props: ['title'],
template: '<div>{{title}}</div>'
});
Vue.component('todo-items',{
props: ['index','item'],
template: '<li>{{index+1}}.{{item}}<button @click="removeabc" >删除</button></li>',
methods: {
removeabc: function (index) {
this.$emit('remove',index);
}
}
});
var vm = new Vue({
el:"#app",
data: {
todoItems: ['篮网','雄鹿','太阳']
},
methods:{
removeItems: function (i) {
alert("删除了"+this.todoItems[i]+"OK");
this.todoItems.splice(i,1);
}
}
});
</script>
</html>
六、计算属性
计算属性的重点突出在 属性 两个字上(属性是名词),首先它是个 属性 其次这个属性有 计 算 的能力(计算是动词),这里的 计算 就是个函数;简单点说,它就是一个能够将计算结果缓存起来的属性(将行为转化成了静态的属性),仅此而已;可以想象为缓存!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>计算属性</title>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
</head>
<body>
<div id="app">
<p>currentTime01 : {{currentTime01()}} </p>
<p>currentTime02 : {{currentTime02}} </p>
</div>
</body>
<script>
var vm = new Vue({
el:"#app",
data:{
msg:"Hello computed!"
},
methods:{
currentTime01:function () {
return Date.now();
}
},
computed:{
currentTime02:function () {
console.log(this.msg);
return Date.now();
}
}
})
</script>
</html>
运行上面的代码,控制台出入一下命令 
计算属性的主要特性就是为了将不经常变化的计算结果进行缓存,以节约我们的系统开销
七、Axios
Axios 是一个开源的可以用在浏览器端和 NodeJS 的异步通信框架,它的主要作用就是实现 ajax 异步通信。
由于Vue.js是一个视图层框架,vue本身并不包含ajax通信功能,所以要使用axios框架来进行通信。(虽然jquery也可以做ajax通信,但是jquery操作dom太频繁,所以不推荐使用jquery)
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>axios</title>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="app">
<li>姓名:{{info.name}}</li>
<li>年龄:{{info.age}}</li>
<li>省:{{info.address.province}}</li>
<li>市:{{info.address.city}}</li>
<a v-bind:href="info.url" >点击跳转页面</a>
</div>
</body>
<script>
var vm = new Vue({
el:"#app",
data(){
return{
info:{
name:null,
age:null,
address:{
province:null,
city:null
},
url:null
}
}
},
mounted(){
axios.get("data.json").then(response=>this.info=response.data)
}
})
</script>
</html>
八、路由Vue Router
在解释路由之前,首先来了解一个概念,单页面应用SPA
所谓单页面应用,就是指整个前端只有一个页面的Web应用,一开始只需加载一次js、css等相关资源(这些资源一般都被webpack打包为一个文件了),后面的所有请求,都不会再重新加载页面或者刷新跳转了,而是利用 JavaScript 动态的变换 HTML(采用的是 div 切换显示和隐藏),从而实现UI与用户的交互。在 SPA 应用中,应用加载之后就不会再有整页刷新。 
 一般来说之前的开发以多页面应用MPA为主,一个url就对应一个页面,每次请求都会重新加载新的页面。而单页面就是那种只有一个页面,每次请只是去该这个页面对应的html内容,而不会重新去加载页面。 
用vue写的项目是单页应用,刷新页面会请求一个HTML文件,切换页面的时候,并不会发起新的请求一个HTML文件,只是页面内容发生了变化
Vue-Router概念:路由就是SPA(single page application单页应用)的路径管理器。vue的单页面应用是基于路由和组件的,路由的作用就是用于设定访问路径,并将路径和组件映射起来,路由模块的本质就是建立起url和页面之间的映射关系。
简单来理解就是,vue项目是单页面应用,里面主要是有很多组件在不停的切换,而这些组件如何切换,就是根据路由,每一个路由对应一个url并绑定一个组件,这样通过路由和组件就可以实现单页面应用程序的开发了。
从上面的解释来看,我们可以发现路由的作用其实就相当于a标签一样,但是为什么我们不直接使用a标签? 因为a标签是从一个页面跳转到另一个页面,页面会重新加载,相当于打开了一个新的网页。而这显然与vue的单页面应用冲突了,在vue项目中我们需要的不是跳转页面,而是对相应的组件进行渲染。
下面是vue-router的代码实现:
- 安装路由,因为vue-router是一个插件包
npm install vue-router --save-dev
- 先看一下项目大致内容
 - 定义一些组件,Content.vue
<template>
<div>
<h1>用户id:{{id}}</h1>
<h1>用户name:{{name}}</h1>
<h1>用户name:{{age}}</h1>
</div>
</template>
<script>
export default {
props: ['id','name','age'],
name: "Content",
beforeRouteEnter:(to,from,next)=>{
console.log("进入路由之前"),
next(vm => {
vm.getData();
});
},
beforeRouteLeave:(to,from,next)=>{
console.log("进入路由之后");
next();
},
methods:{
getData:function () {
this.axios({
method: 'get',
url: 'http://localhost:8080/static/mock/data.json',
}).then(function (response) {
console.log(response.data);
})
}
}
}
</script>
<style scoped>
</style>
- 定义路由配置文件
import Vue from 'vue'
import Router from 'vue-router'
import Content from '../components/Content'
import Main from '../components/Main'
import NotFound from '../components/404'
Vue.use(Router);
export default new Router({
mode: 'history',
routes: [
{
path: '/content/:id/:name/:age',
name: 'content',
component: Content,
props: true
},
{
path: '/main',
name: 'main',
component: Main
},
{
path: '*',
component: NotFound
},
{
path: '/goHome',
redirect: '/main'
}
]
})
- APP.vue
<template>
<div id="app">
<router-link to="/main">首页</router-link>
<router-link :to="{name:'content',params:{id: 1,name:'詹姆斯',age: 34}}">用户页</router-link>
<router-link to="/goHome">goHome</router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
components: {
}
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
- main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(VueAxios,axios)
Vue.config.productionTip = false
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
Content.vue中有路由钩子函数,类似java中的拦截器。404组件、重定向的配置在index.js中。
九、Element-ui
element-ui 是饿了么前端出品的基于 Vue.js的 后台组件库,方便程序员进行页面快速布局和构建。Element-ui官网
安装Element-ui
npm i element-ui -S
然后按照官网的教程,引入相应的组件就可以用了。
|