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的思想,主要是为了让我们开发更方便,因为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>