注:本教程所使用的vue版本为 2.5.16
MVC与MVVM
MVC(Model-View-Controller):javascript
M指的是从后台获取到的数据, V指的是显示动态数据的html页面, C是指响应用户操做、通过业务逻辑处理后去更新视图的过程,在此过程当中会致使对view层的引用。
这里咱们发现咱们网站的大量代码都被放在Controller,致使Controller代码臃肿;并且不利于单元测试,由于业务逻辑处理和视图更新操做会混杂在一块儿。css
MVVM (Model-View-ViewModel):
MVVM是MVC的一个衍生模型,这里的 ViewModel 把业务逻辑处理、用户输入验证等跟视图更新操做分离开了。MVVM是数据驱动的,咱们只须要关心数据的处理逻辑便可,它会经过模板渲染去单独处理视图的更新而不须要咱们亲自去操做Dom元素。html
实例化Vue对象
<div id="app"> </div> <script src="js/vue.min.js"></script> <script> //调用Vue的构造函数建立实例对象 var vm = new Vue({ //根节点选择器 el: '#app', //数据对象 data: { message: 'hello world', }, //渲染模板 template: '<div>message: {{ message }}</div>' }) </script>
Vue会将渲染模板结合数据对象生成的html结构替换掉根节点,只要数据对象上的message发生改变,插值处的内容就会跟着改变,上述例子的实际效果以下:vue
<body> <div>message: hello world</div> </body>
模板语法
上面的双大括号绑定是vue最经常使用的数据绑定方式,除了双大括号还可使用v-text属性进行绑定java
<div>message: <span v-text="message"></span></div>
若是要绑定html结构的话,须要使用到v-html指令,不然vue会把这段html代码当作字符串直接绑定到对应位置数组
new Vue({ el: '#app', data:{ message: 'hello world', html: '<span>hello world</span>' }, template: '<div>message: <span v-html="html"></span></div>' })
须要绑定html元素特性的时候须要使用v-bind指令,v-bind能够省略app
<div v-bind:id="id"></div> <div :id="id"></div>
双大括号的插值方法还可使用js表达式,这些表达式会在所属 Vue 实例的数据做用域下被解析dom
{{ number + 1 }} {{ boolean ? 'true' : 'false' }} {{ message.split('').reverse().join('') }} <div :id="'is' + id"></div>
注意,这里的javascript语句只能是单个表达式,其余的声明变量、流程控制语法都不会生效函数
事件绑定
除了数据绑定外,vue还帮咱们优化了事件绑定流程,指令为v-on,可缩写为@,后面是事件名称单元测试
<a v-on:click="console.log(1)">打印1</a> <a @click="console.log(1)">打印1</a>
仅仅一句js表达式是不够支撑咱们的平常开发的,因此vue给咱们提供了自定义事件处理方法
new Vue({ el: '#app', data: { message: 'hello world' }, //log为methods中定义的函数名,vue会默认把原生DOM事件对象当作参数传处处理函数中 template: '<a @click="log">Click Me</a>', methods: { log: function(event){ console.log(this.message); event.stopPropagation(); } } });
除了直接绑定到一个方法,也能够在内联 JavaScript 语句中调用方法
<!-- 使用内联语法后,原生DOM对象须要手动传入(变量名变动为 $event ) --> <a @click="log(1, $event)">Click Me</a>
计算属性 computed
模板内的表达式很是便利,可是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板太重且难以维护。例如这个字符串反转:
<div>{{ message.split('').reverse().join('') }}</div>
这种状况咱们能够用 computed 解决
new Vue({ el: '#app', data: { message: 'hello' }, template: '<div>{{ reversedMessage }}</div>', //olleh computed: { reversedMessage: function(){ return this.message.split('').reverse().join(''); } } });
这里看渲染模板就直观多了
监听属性 watch
Vue 提供了 watch 这种通用的方式来观察和响应 Vue 实例上的数据变更
<div id="app"> <p>问题: <input v-model="question"></p> <p>{{ answer }}</p> </div> <script src="js/vue.min.js"></script> <script> var vm = new Vue({ el: '#app', data: { question: '', answer: '请先输入问题', timer: undefined, }, watch: { question: function(newVal, oldVal){ if(this.timer){ clearTimeout(this.timer); } var _this = this; this.answer = '等待中止输入...'; setTimeout(function(){ _this.answer = 'Yes!!!'; }, 1000); } } }); </script>
表单绑定,指令为 v-model
v-model 指令在表单 <input> 及 <textarea> 元素上建立双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。v-model 会忽略全部表单元素的 value、checked、selected 特性的初始值而老是将 Vue 实例的数据做为数据来源。
<div id="app"> <input v-model="message" placeholder="写点什么。。"> <!-- 经过v-model绑定message后, 在input输入的文字会实时更新到数据对象上, 从而自动渲染在下面的p标签上 --> <p>Message: {{ message }}</p> </div> <script src="js/vue.min.js"></script> <script> var vm = new Vue({ el: '#app', data: { message: '' } }) </script>
单个复选框
<input type="checkbox" id="checkbox" v-model="checked"> <label for="checkbox">{{ checked }}</label> data: { checked: true }
多个复选框的状况下,把v-model绑定到同一个数组便可:
<input type="checkbox" value="blue" id="blue" v-model="checkedColor"/> <label for="blue">blue</label> <input type="checkbox" value="red" id="red" v-model="checkedColor"/> <label for="red">red</label> data: { checkedColor: [] }
单选按钮
<input type="checkbox" value="true" id="true" v-model="checkedRadio"/> <label for="true">true</label> <input type="checkbox" value="false" id="false" v-model="checkedRadio"/> <label for="false">false</label> data: { checkedRadio: '' }
条件渲染
在javascript语法中有if-else等流程语句让程序执行不一样的代码块,在vue中一样有 v-if
、v-else-if
、v-else
这些指令控制某些节点的显示与否
<div id="app"> <div v-if="isShow"> <label>用户名</label> <input type="text" placeholder="请输入用户名" /> </div> <!-- v-else 元素必须紧跟在带 v-if 或者 v-else-if 的元素的后面,不然它将不会被识别。 --> <div v-else> <label>邮箱</label> <input type="text" placeholder="请输入邮箱" /> </div> <button @click="switchDom">切换输入框类型</button> </div> <script src="js/vue.min.js"></script> <script> var vm = new Vue({ el: '#app', data: { isShow: true }, methods: { switchDom: function(){ this.isShow = !this.isShow; } } }) </script>
在上述例子中,咱们点击 button 会显示不一样的dom,可是若是咱们在input里面输入文字再进行切换的时候会发现,输入的文字并不会被清除,这是由于vue的 就地复用 策略致使的。vue为了尽量高效地渲染dom元素,一般会复用已有元素而不是从头开始渲染,若是不想vue复用这些元素,咱们能够添加一个具备惟一值的 key 属性
<input type="text" placeholder="请输入用户" key="username"/> <input type="text" placeholder="请输入邮箱" key="email"/>
另外一个用于根据条件展现元素的选项是 v-show 指令。用法大体同样:
<p v-show="show">Hello!</p>
v-if
与 v-show
区别:v-if
是惰性渲染,在初始渲染时条件为假,什么也不作——直到条件第一次变为真时,才会开始渲染条件块;在切换过程当中条件块内的事件监听器和子组件会被销毁和重建。v-show
无论初始条件是什么,元素总会被渲染,切换的只是css的display属性
列表渲染
咱们用 v-for
指令根据一组数据表进行列表渲染。v-for
指令须要使用 item in list
的语法,list指的是原数据数组,item指的是迭代的数组元素。v-for
指令还支持一个可选的表示当前迭代元素索引的第二个参数 (item, index) in list
<div id="app"> <ul> <li v-for="item in list"> <!-- 效果同样 --> <!-- <li v-for="item of list"> --> {{ item.text }} </li> </ul> </div> <script src="js/vue.min.js"></script> <script> var vm = new Vue({ el: '#app', data: { list: [ {text: 'blue'}, {text: 'red'}, {text: 'yellow'}, ] } }) </script>
除了数组,v-for
指令还能够经过一个对象的属性来迭代,v-for
指令最多能够支持3个参数,第二第三个可选。
<li v-for="(value, key, index) in object"> {{ index }}. {{ key }}: {{ value }} </li> data:{ object: { firstName: 'John', lastName: 'Doe', age: 26 } } //结果 0. firstName: John 1. lastName: Doe 2. age: 26
Vue的生命周期
根据上图作了一个测试例子,列出了每一个生命周期对应的不一样属性的状态
<div id="app"> {{message}} </div> <script src="js/vue.min.js"></script> <script> var vm = new Vue({ el: '#app', data: { message : "hello" }, beforeCreate: function () { console.group('beforeCreate 建立前状态===============》'); console.log("%c%s", "color:red" , "el : " + this.$el); //undefined console.log("%c%s", "color:red","data : " + this.$data); //undefined console.log("%c%s", "color:red","message: " + this.message) }, created: function () { console.group('created 建立完毕状态===============》'); console.log("%c%s", "color:red","el : " + this.$el); //undefined console.log("%c%s", "color:red","data : " + this.$data); //已被初始化 console.log("%c%s", "color:red","message: " + this.message); //已被初始化 }, beforeMount: function () { console.group('beforeMount 挂载前状态===============》'); console.log("%c%s", "color:red","el : " + (this.$el)); //已被初始化 console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); //已被初始化 console.log("%c%s", "color:red","message: " + this.message); //已被初始化 }, mounted: function () { console.group('mounted 挂载结束状态===============》'); console.log("%c%s", "color:red","el : " + this.$el); //已被初始化 console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); //已被初始化 console.log("%c%s", "color:red","message: " + this.message); //已被初始化 var _this = this; setTimeout(function(){ _this.message = 'hello world!!'; }, 3000); }, beforeUpdate: function () { console.group('beforeUpdate 更新前状态===============》'); console.log("%c%s", "color:red","el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); console.log("%c%s", "color:red","message: " + this.message); }, updated: function () { console.group('updated 更新完成状态===============》'); console.log("%c%s", "color:red","el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); console.log("%c%s", "color:red","message: " + this.message); }, beforeDestroy: function () { console.group('beforeDestroy 销毁前状态===============》'); console.log("%c%s", "color:red","el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); console.log("%c%s", "color:red","message: " + this.message); }, destroyed: function () { console.group('destroyed 销毁完成状态===============》'); console.log("%c%s", "color:red","el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red","data : " + this.$data); console.log("%c%s", "color:red","message: " + this.message) } }) </script>