本文的Demo和源代码已放到GitHub,如果您觉得本篇内容不错,请点个赞,或在GitHub上加个星星!
https://github.com/zwl-jasmine95/Vue_test
以下所有知识都是基于vue.js 2.0版本
一、简介
Vue.js(读音 /vjuː/,类似于 view) 是一套构建用户界面的渐进式框架。与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计。Vue 的核心库只关注视图层,它不仅易于上手,还便于与第三方库或既有项目整合。
另一方面,当与单文件组件和 Vue 生态系统支持的库结合使用时,Vue 也完全能够为复杂的单页应用程序提供驱动。
Vue.js是当下很火的一个JavaScript MVVM库,它是以数据驱动和组件化的思想构建的。相比于Angular.js,Vue.js提供了更加简洁、更易于理解的API,使得我们能够快速地上手并使用Vue.js。
MVVM:
DOM Listeners和Data Bindings两个工具,它们是实现双向绑定的关键。
从View侧看,ViewModel中的DOM Listeners工具会帮我们监测页面上DOM元素的变化,如果有变化,则更改Model中的数据;
从Model侧看,当我们更新Model中的数据时,Data Bindings工具会帮我们更新页面中的DOM元素。
二、vue.js基础指令知识
Vue.js提供了一些常用的内置指令:
- v-if指令
- v-show指令
- v-else指令
- v-for指令
- v-bind指令
- v-on指令
1.声明式渲染
Vue.js 的核心是一个允许采用简洁的模板语法来声明式的将数据渲染进 DOM。 个人感觉实现原理还是和angularJS很像的。
我们试一下写一个简单的hello world!:
1 <body> 2 <div id="app"> 3 {{message}} 4 </div> 5 6 <script type="text/javascript" src="lib/js/vue.js"></script> 7 <script type="text/javascript"> 8 /** 9 * 采用简洁的模板语法来声明式的将数据渲染进 DOM 10 */ 11 var app = new Vue({ 12 el:'#app', 13 data:{ 14 message:'Hello World!' 15 } 16 }); 17 </script> 18 </body>
可以通过app.message来查看数据值。但是在写代码的过程中需要注意很重要的两点:
1)我们编写的js代码一定要放在body标签的最下面,不然会报错‘没有找到#app’,因为js代码放在head标签里时页面DOM结果还没加载完。
2){{message}}这里其实相当于一个变量,可以任意命名,只要和js里对应即可。
数据和 DOM 已经被绑定在一起,一切都是响应式的。可以通过app.message改变数据值。
______________ 除了文本插值,我们还可以采用指令的方式绑定 DOM 元素属性,指令带有前缀 v-
,以表示它们是 Vue 提供的特殊属性。
v-bind
指令可以在其名称后面带一个参数,中间放一个冒号隔开,这个参数通常是HTML元素的特性(attribute),例如:v-bind:class
v-bind:argument="expression"
1 <body> 2 <div id="app-2"> 3 <span v-bind:title="message"> 4 鼠标悬停几秒钟查看此处动态绑定的提示信息! 5 </span> 6 </div> 7 8 <script type="text/javascript" src="lib/js/vue.js"></script> 9 <script type="text/javascript"> 10 /** 11 * 采用简洁的模板语法来声明式的将数据渲染进 DOM 12 */ 13 var app2 = new Vue({ 14 el: '#app-2', 15 data: { 16 message: '页面加载于 ' + new Date() 17 } 18 }) 19 </script> 20 </body>
这里该指令的作用是:“将这个元素节点的 title
属性和 Vue 实例的 message
属性保持一致”。
使用Vue的过程就是定义MVVM各个组成部分的过程的过程。
- 定义View
- 定义Model
- 创建一个Vue实例或"ViewModel",它用于连接View和Model
在创建Vue实例时,需要传入一个选项对象,选项对象可以包含数据、挂载元素、方法、模生命周期钩子等等。
在这个示例中,选项对象的el属性指向View,el: '#app'
表示该Vue实例将挂载到<div id="app">...</div>
这个元素;data属性指向Model,data: message
表示我们的Model是message对象。
Vue.js有多种数据绑定的语法,最基础的形式是文本插值,使用一对大括号语法,在运行时{{ message }}
会被数据对象的message属性替换,所以页面上会输出"Hello World!"。
双大括号以及v-text会将数据解释为纯文本,而非 HTML 。为了输出真正的 HTML ,需要使用 v-html
指令。
2、条件与循环
1)v-if
v-if是条件渲染指令,根据条件表达式的值来执行元素的插入或者删除行为。
v-if="expression"
expression是一个返回bool值的表达式,表达式可以是一个bool属性,也可以是一个返回bool的运算式。
1 <div class="app"> 2 <span v-if="true">你好!vue.js!</span> 3 </div>
这一段代码可以还用js来实现:
<body> <div class="app"> <!--这里不一定要用id,class也行的,只要js里的选择器对应--> <span v-if="tag">你好!vue.js!</span> </div> <script type="text/javascript" src="lib/js/vue.js"></script> <script type="text/javascript"> var app = new Vue({ el:'.app', //注意选择器 data:{ tag:true } }); </script> </body>
尽管用了class而不是id,但是在控制台也是可以通过app.tag来查看变量的:
拓展:
v-show
也是条件渲染指令,和v-if指令不同的是,使用v-show
指令的元素始终会被渲染到HTML,它只是简单地为元素设置CSS的style属性。
还可以用v-else
指令为v-if
或v-show
添加一个“else块”。v-else
元素必须立即跟在v-if
或v-show
元素的后面——否则它不能被识别。
(v-else
元素是否渲染在HTML中,取决于前面使用的是v-if
还是v-show
指令。)
1 <body> 2 <div id="app"> 3 <p v-if="tag">你好!vue.js!</p> 4 <p v-else>这里是例外情况</p> 5 </div> 6 7 <script type="text/javascript" src="lib/js/vue.js"></script> 8 <script type="text/javascript"> 9 var app = new Vue({ 10 el:'#app', //注意选择器 11 data:{ 12 tag:true 13 } 14 }); 15 </script> 16 </body>
在控制台中改变tag的值:
2)v-for
v-for
指令基于一个数组渲染一个列表,它和JavaScript的遍历语法相似:
v-for="item in items"
items是一个数组,item是当前被遍历的数组元素。
v-for
指令可以绑定数组的数据来渲染一个项目列表:
1 <body> 2 <div class="app"> 3 <p>前端学习之路:</p> 4 <ol> 5 <li v-for="item in items">{{item.text}}</li> 6 </ol> 7 </div> 8 9 <script type="text/javascript" src="lib/js/vue.js"></script> 10 <script type="text/javascript"> 11 var app = new Vue({ 12 el:'.app', 13 data:{ 14 items:[ 15 {text:'HTML'}, 16 {text:'CSS'}, 17 {text:'JavaScript'}, 18 {text:'jQuery'} 19 ] 20 } 21 }); 22 </script> 23 </body>
在控制台里,输入 app.items.push({ text: '新项目' })
,你会发现列表中添加了一个新项:
另外:v-for还有一种特殊写法可以获取索引值:
index可以直接用作索引值
3、处理用户输入
1)v-on
v-on
指令用于给监听DOM事件,它的用语法和v-bind是类似的,例如监听<a>元素的点击事件:
<a v-on:click="doSomething">
有两种形式调用方法:绑定一个方法(让事件指向方法的引用),或者使用内联语句。
1 <body> 2 <div id="app"> 3 <p>{{ message }}</p> 4 <button v-on:click="reverseMessage">逆转消息</button> 5 </div> 6 7 <script type="text/javascript" src="lib/js/vue.js"></script> 8 <script type="text/javascript"> 9 var app = new Vue({ 10 el: '#app', 11 data: { 12 message: 'Hello Vue.js!' 13 }, 14 // 在 `methods` 对象中定义方法 15 methods: { 16 reverseMessage: function () { 17 //reverse() 方法用于颠倒数组中元素的顺序。 18 //方法中的this指向app 19 this.message = this.message.split('').reverse().join('') 20 } 21 } 22 }) 23 </script> 24 </body>
——————>
Vue.js为最常用的两个指令v-bind
和v-on
提供了缩写方式。v-bind指令可以缩写为一个冒号,v-on指令可以缩写为@符号。
2)v-model
Vue 提供了 v-model
指令,使表单输入和应用状态间的双向绑定变得轻而易举。
1 <body> 2 <div id="app"> 3 <p>{{ message }}</p> 4 <input type="text" v-model="message"> 5 <button v-on:click="alertMessage">弹出输入框内容</button> 6 </div> 7 8 <script type="text/javascript" src="lib/js/vue.js"></script> 9 <script type="text/javascript"> 10 var app = new Vue({ 11 el: '#app', 12 data: { 13 message: 'Hello Vue.js!' 14 }, 15 // 在 `methods` 对象中定义方法 16 methods: { 17 alertMessage: function () { 18 19 //方法中的this指向app 20 alert(this.message); 21 } 22 } 23 }) 24 </script> 25 </body>
改变输入框的值,会发现上面的p标签的值也改变了。然后点击按钮,弹框中会显示改变的输入框值,这就是双向数据绑定。(也可以通过控制台来改变输入框以及p标签的值)
三、vue.js基础用法知识
一开始就学习vue.js的内置指令,估计有些人对vue.js的执行还是有点不理解,下面做简单的讲解。
1、构造器
每个 Vue.js 应用都是通过构造函数 Vue
创建一个 Vue 的根实例 启动的:
1 var vm = new Vue({ 2 // 选项 3 })
虽然没有完全遵循 MVVM 模式, Vue 的设计无疑受到了它的启发。因此在文档中经常会使用 vm
(ViewModel 的简称) 这个变量名表示 Vue 实例。
在实例化 Vue 时,需要传入一个选项对象,它可以包含数据、模板、挂载元素、方法、生命周期钩子等选项。
(这是我用思维导图工具xMind画的,后面三个就不具体列出来了,更加具体的内容可以在官方API查看。)
new一个vue对象的时候可以设置属性,其中最重要的包括三个,分别是data、methods、watch。
其中data代表vue对象的数据,methods代表vue对象的方法,watch设置了对象监听的方法。如下:
在第二节的内置指令中已经用到了data和methods。,其他的后面用到了再系统讲解。
另外可以扩展Vue构造器,从而用预定义选项创建可复用的组件构造器:
1 var MyComponent = Vue.extend({ 2 // 扩展选项 3 }) 4 // 所有的 `MyComponent` 实例都将以预定义的扩展选项被创建 5 var myComponentInstance = new MyComponent()
所有的 Vue.js 组件其实都是被扩展的 Vue 实例。
2、属性与方法
每个 Vue 实例都会代理其 data
对象里所有的属性:
1 var data = { a: 1 } 2 var vm = new Vue({ 3 data: data 4 }) 5 vm.a === data.a // -> true 6 // 设置属性也会影响到原始数据 7 vm.a = 2 8 data.a // -> 2 9 // ... 反之亦然 10 data.a = 3 11 vm.a // -> 3
注意只有这些被代理的属性是响应的。如果在实例创建之后添加新的属性到实例上,它不会触发视图更新。
除了 data 属性, Vue 实例暴露了一些有用的实例属性与方法。这些属性与方法都有前缀 $
,以便与代理的 data 属性区分。
1 var data = { a: 1 } 2 var vm = new Vue({ 3 el: '#example', 4 data: data 5 }) 6 vm.$data === data // -> true 7 vm.$el === document.getElementById('example') // -> true 8 // $watch 是一个实例方法 9 vm.$watch('a', function (newVal, oldVal) { 10 // 这个回调将在 `vm.a` 改变后调用 11 })
3、实例生命周期
每个 Vue 实例在被创建之前都要经过一系列的初始化过程。例如,实例需要配置数据观测(data observer)、编译模版、挂载实例到 DOM ,然后在数据变化时更新 DOM 。在这个过程中,实例也会调用一些 生命周期钩子 ,这就给我们提供了执行自定义逻辑的机会。例如,created
这个钩子在实例被创建之后被调用:
var vm = new Vue({ data: { a: 1 }, created: function () { // `this` 指向 vm 实例 console.log('a is: ' + this.a) } }) // -> "a is: 1"
也有一些其它的钩子,在实例生命周期的不同阶段调用,如 mounted
、 updated
、destroyed
。钩子的 this
指向调用它的 Vue 实例。一些用户可能会问 Vue.js 是否有“控制器”的概念?答案是,没有。组件的自定义逻辑可以分布在这些钩子中。
生命周期图示:
四、实践案例
1、表格
主要是利用v-for来实现的:(源代码: github上 demo1/table.html)
html:
1 <table id="myTable" class="table table-striped"> 2 <thead> 3 <tr> 4 <th>姓名</th> 5 <th>性别</th> 6 <th>年龄</th> 7 </tr> 8 </thead> 9 <tbody> 10 <tr v-for="item in items"> 11 <td>{{item.name}}</td> 12 <td>{{item.sex}}</td> 13 <td>{{item.age}}</td> 14 </tr> 15 </tbody> 16 </table>
js:
var vm = new Vue({ el:'#myTable', data:{ items:[ { name:'张姗', sex:'女', age:'42' }, { name:'李四', sex:'男', age:'33' }, { name:'王五', sex:'男', age:'58' } ] } });
将v-for嵌套在tr标签中,这样就可以生成下面的表格了。案例比较简单。
2、实时表格编辑
(源代码: github上 demo1/editTable.html)
首先我们先确定一下需要做什么:
我们可以在上面的表单中输入要添加的表格行的具体内容,然后点击添加按钮即可在表格中增加一行。
点击表格每一行后面的删除按钮,即可删除该表格行。
1)布局
在实现功能之前,现将表单模块和表格模块利用HTML实现,这里为了节省时间,直接是使用bootstrap框架。
1 <form class="form-horizontal" role="form"> 2 <div class="form-group"> 3 <label for="name" class="col-sm-2 control-label">姓名</label> 4 <div class="col-sm-10"> 5 <input type="text" class="form-control" id="name" placeholder="请输入名字"> 6 </div> 7 </div> 8 <div class="form-group"> 9 <label for="age" class="col-sm-2 control-label">年龄</label> 10 <div class="col-sm-10"> 11 <input type="text" class="form-control" id="age" placeholder="请输入年龄"> 12 </div> 13 </div> 14 <div class="form-group"> 15 <label for="sex" class="col-sm-2 control-label">性别</label> 16 <div class="col-sm-10"> 17 <select name="" id="sex" class="form-control"> 18 <option value="男">男</option> 19 <option value="女">女</option> 20 </select> 21 </div> 22 </div> 23 <div class="form-group"> 24 <div class="col-sm-offset-2 col-sm-10"> 25 <button type="button" class="btn btn-primary">添加</button> 26 </div> 27 </div> 28 </form> 29 30 <table id="editTable" class="table table-striped"> 31 <thead> 32 <tr> 33 <th>姓名</th> 34 <th>年龄</th> 35 <th>性别</th> 36 <th>操作</th> 37 </tr> 38 </thead> 39 <tbody> 40 <tr v-for="item in items"> 41 <td>{{item.name}}</td> 42 <td>{{item.age}}</td> 43 <td>{{item.sex}}</td> 44 <td><button type="button" class="btn btn-primary">删除</button></td> 45 </tr> 46 </tbody> 47 </table>
2)数据绑定
接下来我们使用v-model将表单输入框绑定到数据层。
先在data里创建一个新的对象-newItem,用来存放新增的表格行:
然后将数据绑定到输入框,例如:
3)添加新增按钮事件
定义名为createItem的函数:
1 methods:{ 2 creatItem:function () { 3 this.items.push(this.newItem); //this指向data 4 // 添加完newItem对象后,重置newItem对象 5 this.newItem = {name: '', age: 0, sex: '女'} 6 } 7 }
4)添加删除按钮事件
定义名为deleteItem的函数,并将该表格行的索引作为参数传入;
1 //事件处理 2 methods:{ 3 /** 4 * 根据表格行的索引删除该表格行 5 * @param {[number]} index [表格行的索引] 6 * @return 7 */ 8 deleteItem:function (index) { 9 this.items.splice(index,1); 10 } 11 }
完整代码:
https://github.com/zwl-jasmine95/Vue_test/blob/master/demo1/editTable.html
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>实时表格编辑</title> 6 <link rel="stylesheet" href="../lib/css/bootstrap.min.css"> 7 <style> 8 body{ 9 padding:50px; 10 } 11 </style> 12 </head> 13 <body> 14 <div id="editTable"> 15 <form class="form-horizontal" role="form"> 16 <div class="form-group"> 17 <label for="name" class="col-sm-2 control-label">姓名</label> 18 <div class="col-sm-10"> 19 <input type="text" class="form-control" id="name" placeholder="请输入名字" v-model="newItem.name"> 20 </div> 21 </div> 22 <div class="form-group"> 23 <label for="age" class="col-sm-2 control-label">年龄</label> 24 <div class="col-sm-10"> 25 <input type="text" class="form-control" id="age" placeholder="请输入年龄" v-model="newItem.age"> 26 </div> 27 </div> 28 <div class="form-group"> 29 <label for="sex" class="col-sm-2 control-label">性别</label> 30 <div class="col-sm-10"> 31 <select name="" id="sex" class="form-control" v-model="newItem.sex"> 32 <option value="男">男</option> 33 <option value="女">女</option> 34 </select> 35 </div> 36 </div> 37 <div class="form-group"> 38 <div class="col-sm-offset-2 col-sm-10"> 39 <button type="button" class="btn btn-primary" v-on:click="creatItem">添加</button> 40 </div> 41 </div> 42 </form> 43 44 <table class="table table-striped"> 45 <thead> 46 <tr> 47 <th>姓名</th> 48 <th>年龄</th> 49 <th>性别</th> 50 <th>操作</th> 51 </tr> 52 </thead> 53 <tbody> 54 <tr v-for="(item,index) in items"> 55 <td>{{item.name}}</td> 56 <td>{{item.age}}</td> 57 <td>{{item.sex}}</td> 58 <td><button type="button" class="btn btn-primary" v-on:click="deleteItem(index)">删除</button></td> 59 </tr> 60 </tbody> 61 </table> 62 </div> 63 64 <script type="text/javascript" src="../lib/js/vue.js"></script> 65 <script type="text/javascript"> 66 var vm = new Vue({ 67 el:'#editTable', 68 data:{ 69 newItem:{ 70 name:'', 71 sex:'女', 72 age:'0' 73 }, 74 items:[ 75 { 76 name:'张姗', 77 sex:'女', 78 age:'42' 79 }, 80 { 81 name:'李四', 82 sex:'男', 83 age:'33' 84 }, 85 { 86 name:'王五', 87 sex:'男', 88 age:'58' 89 } 90 ] 91 }, 92 //事件处理 93 methods:{ 94 creatItem:function () { 95 this.items.push(this.newItem); //this指向data 96 // 添加完newItem对象后,重置newItem对象 97 this.newItem = {name: '', age: 0, sex: '女'} 98 }, 99 100 /** 101 * 根据表格行的索引删除该表格行 102 * @param {[number]} index [表格行的索引] 103 * @return 104 */ 105 deleteItem:function (index) { 106 this.items.splice(index,1); 107 } 108 } 109 110 }); 111 </script> 112 </body> 113 </html>