VueJS 基础
首先,你可以在这里下载本文使用到的vue.js文件,使用的是v2.6.10
开发版本。
MVC与MVVM
Vue只关注视图层,是一套构建用户界面的框架。
app.js
:项目的入口模块,一切的请求,都要先进入这里进行处理。(注意:app.js并没有路由分发功能,需要调用router.js
模块进行路由的分发处理)router.js
:这是路由分发处理模块:为了保证路由模型的只能单一,router.js只负责分发路由,不负责具体业务逻辑的处理(如果涉及到业务逻辑处理操作,router.js就无能为力了,只能调用controller模块
进行业务逻辑处理)Controller
:这是业务逻辑处理层,在这个模块中,封装了一些具体业务逻辑处理的逻辑代码,但是,也是为了保证职能单一,此模块只负责处理业务,不负责处理数据的CRUD,如果涉及到了数据的CRUD,需要调用Model层
Model层
:职能单一,只负责操作数据库,执行对应的Sql语句,进行数据的CRUD。View视图层
:每当用户操作了界面,如果需要业务的处理,都会通过网络请求,去请求后端服务,此时,这个请求就会被后端的app.js监听到
。
- MVVM:是前端
视图层的分层开发思想
,主要把每个页面分成了M、V和VM。其中,VM是MVVM的思想核心,因为VM是M和V之间的调度者。 - M:保存每个页面中单独的数据;(如:ajax请求返回的数据)
- VM:它是一个调度者,分割了M和V。(每当M要想V渲染,或V改动后要同步到M时,VM负责这部分中间的处理工作。)
- V:视图,每个页面中的html代码;(M中的数据渲染到V中)
前端页面使用MVVM的思想,主要是为了让我们开发更方便,因为MVVM提供了数据的双向绑定。(注意:数据的双向绑定是由VM提供的。)
Vue基础代码
- 基础代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="./lib/vue.js"></script> </head> <body> <!-- 将来new的vue实例会控制这个元素中的所有内容 --> <!-- vue实例所控制的这个元素区域,就是我们的V --> <div id="app"> <p>{{msg}}</p> </div> <script> //创建一个Vue实例 //当导入vue.js的包之后,在浏览器的内存中,就多了一个Vue构造函数 //注意:我们new 出来的这个vm对象,就是我们MVVM中的VM调度者 var vm = new Vue({ el: '#app', //表示当前我们new的这个vue实例,要控制页面上的哪个区域 //这里的data就是MVVM中的M,专门用来保存每个页面的数据 data: { //data属性中,存放的是el中要用到的数据 msg: '欢迎学习Vue' //通过vue提供的指令,很方便的把数据渲染到页面上,程序员不再手动操作DOM元素了 } }) </script> </body> </html>
- 基础指令
- v-cloak:解决插值表达式闪烁问题
- v-text:解析文本
- v-html:解析html
- v-bind:提供属性绑定机制,缩写是 “:”
- v-on:绑定事件,绑定浏览器常见事件,缩写是 “@”
- 示例代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> [v-cloak]{display: none;} </style> </head> <body> <div id="app"> <!-- v-cloak能够解决 插值表达式 闪烁的问题 --> <!-- 默认 v-text 是没有闪烁问题的 --> <!-- 插值表达式,可以再其前后放置任何内容,而v-text会覆盖元素中原本的内容,但是 插值表达式 只会替换自己的占位符,不会吧整个元素替换 --> <p v-cloak>{{msg}}</p> <h4 v-text="msg"></h4> <!-- v-html 输出html格式 --> <div v-html="msg2"></div> <!-- v-bind: 绑定属性的指令 v-bing会把mytitle当作一个js去解析,所以,v-bind中可以写合法的js表达式--> <!-- 简写 “:”等同“v-bing:” --> <input type="button" value="按钮" v-bind:title="mytitle + '123'"> <input type="button" value="按钮" :title="mytitle + '567'"> <!-- v-on 绑定事件--> <input type="button" value="按钮" :title="mytitle" v-on:click="hello"> <input type="button" value="按钮" :title="mytitle" @mouseover="hello"> </div> <script src="./lib/vue.js"></script> <script> var vm = new Vue({ el: '#app', data: { msg: '123', msg2: '<h1>我是H1</h1>', mytitle: '这个一个自定义的title' }, methods: { // methods属性中定义了当前Vue实例中所有可能的方法 hello: function() { alert('hello'); } } }) </script> </body> </html>
实例:跑马灯
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="app"> <input type="button" value="浪起来" @click="lang"> <input type="button" value="别浪" @click="dontLang"> <h4>{{ msg }}</h4> </div> <script src="./lib/vue.js"></script> <script> //注意:在vm实例中,如果想要获取data上的数据,或者想要调用methods中方法,必须通过t his.数据属性名 或 this.方法名 来进行访问,就表示我们 new 出来的vm实例对象 var vm = new Vue({ el: '#app', data: { msg: '猥琐发育,别浪!', timer: null, //在data上定义定时器ID }, methods: { lang() { if(this.timer === null) { this.timer = setInterval(()=>{ var start = this.msg.substring(0,1);//获取第一个字符串 var end = this.msg.substring(1);//获取到第一个字符后面的所有字符 this.msg = end + start;//重新凭借得到新的字符串,并赋值给 this.msg //注意: VM实例,会自动监听data中所有数据的改变,只要数据发送变化,就会把最新的数据,从data同步到页面中去。【好处:程序员只需要关系操作数据,不需要考虑如何重新渲染到页面】 },300); }else { return; } }, dontLang() { clearInterval(this.timer); //每当清除了定时器后,重新把timer置为null this.timer = null; } } }) //分析: //1.给 【浪起来】按钮,绑定一个点击事件 v-on 或 @ //2. 在按钮的事件处理函数中,写相关的业务逻辑代码:拿到msg字符串,然后调用字符串的substring方法,来进行字符串截取操作,把第一个字符串截取出来, //放到最后一个位置,即可。 //3.为了实现点击下按钮,自动截取的功能,需要把2步骤的代码,放到一个定时器中 </script> </body> </html>
事件修饰符
.stop
阻止冒泡.prevent
阻止默认事件.capture
添加事件侦听器时使用事件捕获模式.self
只当事件在该元素本身(比如:不是子元素)触发时触发回调.once
事件只触发一次
.self
和.stop
的区别:
.self
只阻止自身元素上的其他默认行为,不会阻止其他元素的默认冒泡行为.stop
阻止包含自身以及其他元素的默认冒泡行为
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="./lib/vue.js"></script> <style> .inner { height: 100px; background-color: darkcyan; } .outer{ padding: 10px; background-color: green; } </style> </head> <body> <div id="app"> <!-- 1.使用 .stop 阻止冒泡 --> <!-- <div class="inner" @click="divHandler"> <input type="button" value="click me" @click.stop="btnHandler"> </div> --> <!-- 2.使用 .capture 添加事件侦听器时使用事件捕获模式 --> <!-- <div class="inner" @click.capture="divHandler"> <input type="button" value="click me" @click="btnHandler"> </div> --> <!-- 3.使用 .self 实现只有点击当前元素时,才会触发事件处理函数 --> <!-- <div class="inner" @click.self="divSelfHandler"> <input type="button" value="click me" @click="btnHandler"> </div> --> <!-- 4.使用 .prevent 阻止默认行为 --> <!-- <a href="http://www.baidu.com" @click.prevent="linkHandler">百度一下</a> --> <!-- 5.使用 .once 只触发一次事件处理函数 --> <!-- <a href="http://www.baidu.com" @click.prevent.once="linkHandler">百度一下</a> --> <!-- 6.演示 .stop和 .self 的区别 --> <!-- <div class="outer" @click="divouterHandler"> <div class="inner" @click="divHandler"> <input type="button" value="click me" @click.stop="btnHandler"> </div> </div> --> <!-- .self 只会阻止自己身上的冒泡行为的触发,并不会真正阻止其他元素的冒泡行为 --> <!-- <div class="outer" @click="divouterHandler"> <div class="inner" @click.self="divHandler"> <input type="button" value="click me" @click="btnHandler"> </div> </div> --> </div> <script> var vm = new Vue({ el: '#app', data: { }, methods: { divHandler() { console.log('inner div click event') }, btnHandler() { console.log('button click event') }, linkHandler() { console.log('a link click event') }, divSelfHandler() { console.log('inner self click event') }, divouterHandler() { console.log('outer click event') } } }) </script> </body> </html>
v-model :唯一一个实现双向数据绑定的指令
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="./lib/vue.js"></script> </head> <body> <div id="app"> <h4>{{ msg }}</h4> <!-- 1. v-bind 只能实现数据的单向绑定:从M自动绑定到V,无法实现数据的双向绑定 --> <!-- <input type="text" v-bind:value="msg"> --> <!-- 2. v-model 唯一可以实现表单元素和Model中数据现双向绑定的指令 --> <!-- 注意: v-model 只能运用在表单元素中 --> <!-- 如:input(radio,text,address,email...) select checkbox textarea等--> <input type="text" v-model="msg"> </div> <script> var vm = new Vue({ el: '#app', data: { msg: 'HELLO world' }, methods: { } }) </script> </body> </html>
简易计算器
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="./lib/vue.js"></script> </head> <body> <div id="app"> <input type="text" name="" v-model="n1"> <select v-model="opt"> <option value="+">+</option> <option value="-">-</option> <option value="*">*</option> <option value="/">/</option> </select> <input type="text" v-model="n2"> <input type="button" value="=" @click="calc"> <input type="text" v-model="result"> </div> <script> var vm = new Vue({ el: '#app', data: { n1: 0, n2: 0, result: 0, opt: '+' }, methods: { calc() { //不推荐该方式,开发中最好不用 let codeStr = `parseInt(this.n1)${this.opt}parseInt(this.n2)`; this.result = eval(codeStr); } } }) </script> </body> </html>
在Vue 中使用样式
使用class样式 :class
- 数组方式
<h1 :class="['red','thin']">啧啧啧啧啧啧</h1>
- 数组中使用三元表达式(要在data中添加isactive)
<h1 :class="['red','thin',isactive?'active':'']"></h1>
- 数组中嵌套对象
<h1 :class="['red','thin',{'active':isactive}]">啧啧啧啧啧啧</h1>
- 直接使用对象
<h1 :class="{red:true,'thin':true,italic:true,active:true}">啧啧啧啧啧啧</h1>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="./lib/vue.js"></script> <style> .red { color: red; } .thin { font-weight: 200; } .italic { font-style: italic; } .active { letter-spacing: 0.5em; } </style> </head> <body> <div id="app"> <!-- 1. 直接传递一个数组,这里的class需要使用 v-bind做数据绑定 --> <!-- <h1 :class="['red','thin']">啧啧啧啧啧啧</h1> --> <!-- 2. 在数组中使用三元表达式--> <!-- <h1 :class="['red','thin',isactive?'active':'']">啧啧啧啧啧啧</h1> --> <!-- 3. 数组中嵌套对象 ,提高代码可读性--> <!-- <h1 :class="['red','thin',{'active':isactive}]">啧啧啧啧啧啧</h1> --> <!-- 4. 直接使用对象:在为class使用v-bind绑定对象的时候,对象的属性是类名,由于对象的属性可带引号,也可不带引号,所以这里可加可不加引号;属性的值是一个标识符 --> <h1 :class="classObj">啧啧啧啧啧啧</h1> </div> <script> var vm = new Vue({ el: '#app', data: { classObj: {red:true,'thin':true,italic:true,active:false} }, methods: { } }) </script> </body> </html>
使用内联样式 :style
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="./lib/vue.js"></script> </head> <body> <div id="app"> <!-- 1. 对象就是无序键值对的集合 --> <h1 :style="styleObj1">啧啧啧啧啧啧</h1> <!-- 2. :style中通过数组,引用对个data上的样式对象--> <h1 :style="[styleObj1,styleObj2]">啧啧啧啧啧啧</h1> </div> <script> var vm = new Vue({ el: '#app', data: { styleObj1: {color: 'red','font-weight': 400}, styleObj2: {'font-style':'italic','letter-spacing':'0.5em'} }, methods: { } }) </script> </body> </html>
v-for 循环
循环普通数组
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="./lib/vue.js"></script> </head> <body> <div id="app"> <ul> <li v-for="(item,i) in list">第{{i+1}}项:{{item}}</li> </ul> </div> <script> var vm = new Vue({ el: '#app', data: { list: [1,2,3,4,5,6] }, methods: { } }) </script> </body> </html>
循环对象数组
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="./lib/vue.js"></script> </head> <body> <div id="app"> <ul> <li v-for="(item,i) in list">id:{{item.id}}---{{item.name}}</li> </ul> </div> <script> var vm = new Vue({ el: '#app', data: { list: [ {id:1,name:'zzz1'}, {id:2,name:'zzz2'}, {id:3,name:'zzz3'}, {id:4,name:'zzz4'}, {id:5,name:'zzz5'}, ] }, methods: { } }) </script> </body> </html>
循环对象
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="./lib/vue.js"></script> </head> <body> <div id="app"> <!-- 注意:在遍历对象身上的键值对的时候,除了有 val , key 之外,在第三个位置还有一个索引 --> <p v-for="(val,key,i) in user">{{ key }} --- {{ val }} --- 索引:{{i}}</p> </div> <script> var vm = new Vue({ el: '#app', data: { user: { id: 1, name: 'zzzz', gender: '男' } }, methods: { } }) </script> </body> </html>
迭代数字
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="./lib/vue.js"></script> </head> <body> <div id="app"> <!-- in 后面可以放 普通数组,对象数组,对象,数字 --> <!-- v-for迭代数字时,count 从1开始--> <p v-for="count in 10">第{{count}}次循环</p> </div> <script> var vm = new Vue({ el: '#app', data: { }, methods: { } }) </script> </body> </html>
v-for中key属性的使用注意事项
v2.2.0+的版本里,每次
for
循环时,通过key属性标识当前循环的唯一身份。
- 注意: v-for循环的时候,key属性只能使用number获取string
- 注意: key 在使用的时候,必须使用v-bind 属性绑定的形式,指定key的值
- 在组件中,使用v-for循环的时候,或者在一些特殊情况中,如果v-for有问题,必须在使用v-for的同时指定唯一的字符串/数字类型的:key值
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="./lib/vue.js"></script> </head> <body> <div id="app"> <div> <label> ID: <input type="text" v-model="id"> </label> <label> name: <input type="text" v-model="name"> </label> <input type="button" value="添加" @click="add"> </div> <!-- 注意: v-for循环的时候,key属性只能使用number或者string --> <!-- 注意: key 在使用的时候,必须使用v-bind 属性绑定的形式,指定key的值 --> <!-- 在组件中,使用v-for循环的时候,或者在一些特殊情况中,如果v-for有问题,必须在使用v-for的同时指定唯一的字符串/数字类型的:key值 --> <p v-for="item in list" :key="item.id"> <input type="checkbox"> {{item.id}}--- {{item.name}} </p> </div> <script> var vm = new Vue({ el: '#app', data: { id: '', name: '', list: [ {id:1,name: 'xxx'}, {id:2,name: 'yyy'}, {id:3,name: 'zzz'}, {id:4,name: 'www'} ] }, methods: { add() { this.list.unshift({id: this.id,name:this.name}); } } }) </script> </body> </html>
v-if 和 v-show
v-if
:每次都会重新删除或创建DOM(v-if会消耗较高的切换性能 ,如果该DOM涉及到频繁的切换,最好不要用到v-if
,这时推荐使用v-show
)v-show
:每次不会重新进行DOM的删除或创建操作,只是切换了DOM的display:none
样式。(如果这个DOM从来不会被进行显示,那么v-show有较高的初始渲染消耗,这时推荐使用v-if
)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="./lib/vue.js"></script> </head> <body> <div id="app"> <input type="button" value="toggle" @click="flag=!flag"> <!-- v-if会消耗较高的切换性能 ,如果该DOM涉及到频繁的切换,最好不要用到v-if --> <h3 v-if="flag">v-if控制的DOM</h3> <!-- v-show有较高的初始渲染消耗 --> <h3 v-show="flag">v-show控制的DOM</h3> </div> <script> var vm = new Vue({ el: '#app', data: { flag: true }, methods: { } }) </script> </body> </html>
实例-商品列表
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet"> <script src="../lib/vue.js"></script> </head> <body> <div id="app"> <div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title">添加品牌</h3> </div> <div class="panel-body form-inline"> <label for=""> id: <input type="text" class="form-control" v-model="id"> </label> <label for=""> name: <input type="text" class="form-control" v-model="name"> </label> <!-- 在Vue中,时使用事件绑定机制,为元素指定处理函数的时候,如果加了小括号,就可以给函数传参了 --> <input type="button" value="添加" class="btn btn-primary" @click="add"> <label for=""> 搜索关键字: <input type="text" value="搜索" v-model="keywords"> </label> </div> </div> <table class="table table-bordered table-hover table-striped"> <thead> <tr> <th>id</th><th>Name</th><th>Ctime</th><th>Operation</th> </tr> </thead> <tbody> <!-- 之前,v-for中的数据都是直接从data上的list中直接渲染过来的 --> <!-- 现在,我们自定义了一个search方法,同时,把所有的关键字通过传参的形式,传递给了search方法 --> <!-- 在search方法内部,通过执行for循环,把所有符合搜索关键字的数据,保存到一个新数组中,返回 --> <tr v-for="item in search(keywords)" :key="item.id"> <td v-text="item.id"></td> <td v-text="item.name"></td> <td v-text="item.ctime"></td> <td> <input type="button" value="删除" @click.prevent="del(item.id)"> </td> </tr> </tbody> </table> </div> <script> var vm = new Vue({ el: '#app', data: { id: '', name: '', keywords: '', list: [ {id:1,name:'科比',ctime: new Date()}, {id:2,name:'詹姆斯',ctime: new Date()} ] }, methods: { add() { //分析: //1.获取到id和name,直接从data上获取 //2.组织处一个对象 //3.把这个对象,调用数组的相关方法,添加到当前data上的list中 //4.注意:在Vue中,已经实现数据的双向绑定,每当我们修改了data中的数据,Vue会默认监听到数据的改动,自动把最新的数据,应用到页面上 let data = {id:this.id,name:this.name,ctime:new Date}; this.list.push(data) this.id = this.name = '';//清空 }, del(id) { // this.list.some((item,i)=>{ // if(item.id === id) { // this.list.splice(i,1); // return true; // } // }) let index = this.list.findIndex(item => { if(item.id === id) { return true; } }); this.list.splice(index,1); }, search(keywords) {//根据关键字进行数据的搜索 // let newList = []; // this.list.forEach(item=>{ // if(item.name.indexOf(keywords) !== -1) { // newList.push(item); // } // }) // if(keywords.trim() === '') { // return this.list; // }else { // return newList; // } return this.list.filter(item => { return item.name.includes(keywords) === true ? item : false; }) } } }) </script> </body> </html>