本系列文章是对vue2.x的知识进行系统梳理,也算是一个回顾。
本系列只针对基础知识,难度较浅,如有错误,欢迎大佬指正,不胜感激!!!
第一个vue例子
- 想让Vue工作,首先要创建一个Vue实例,并且传入一个配置对象;
- root容器里的代码叫做
Vue模版
,包含一些特殊的Vue语法; - Vue实例和容器是一一对应的;
{{xx}}
中的xxx要写js表达式,且xxx可以自动读取到data
中的所有属性;- 一旦
data
中的数据发生改变,那么页面中用到该数据的地方也会自动更新;
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>第一个vue例子</title>
<!-- 引入Vue -->
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="root">
<h1>{{msg1}}</h1>
<h1>{{msg2}}</h1>
</div>
<script type="text/javascript" >
//创建Vue实例
new Vue({
// el用于指定当前Vue实例为哪个容器服务,值通常为css选择器字符串。
el:'#root',
// data中用于存储数据,数据供el所指定的容器去使用,值暂时先写成一个对象。
data (){
return {
msg1: 'Hello Vue!!!',
msg2: 'This is my first Vue Demo'
}
}
})
</script>
</body>
</html>
模版语法
- 插值语法:
- 功能:用于解析标签体内容。
- 写法:
{{xxx}}
,xxx是js表达式,可以直接读取到data中的所有属性。
- 指令语法:
- 功能:用于解析标签(包括:标签属性、标签体内容、绑定事件.....)。
- 举例:
v-bind:href="xxx"
或 简写为:href="xxx"
,xxx同样要写js表达式,同样可以直接读取到data中的所有属性。 - 备注:Vue中有很多的指令,且形式都是:
v-??
。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>模板语法</title>
<!-- 引入Vue -->
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h1>插值语法</h1>
<h3>姓名:{{name}}</h3>
<h3>职业:{{job}}</h3>
<hr/>
<h1>指令语法</h1>
<a v-bind:href="address" x="hello">点我去{{target}}冒险</a>
<a :href="address" x="hello">点我去{{address}}冒险</a>
</div>
</body>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
name:'伍六七',
job: '理发师',
target: '玄武国',
address: 'https://movie.douban.com/subject/35161255/'
}
})
</script>
</html>
数据绑定
Vue中有2种数据绑定的方式:
- 单向绑定(v-bind):数据只能从data流向页面。
- 双向绑定(v-model):数据能从data流向页面,也可以从页面流向data。
- 双向绑定一般都应用在表单类元素上(如:input、select等)。
v-model:value
可以简写为v-model
,因为v-model
默认收集的就是value值。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>数据绑定</title>
<!-- 引入Vue -->
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<!-- 普通写法 -->
单向数据绑定(普通写法):<input type="text" v-bind:value="name"><br/>
双向数据绑定(普通写法):<input type="text" v-model:value="name"><br/>
<!-- 简写 -->
单向数据绑定(简写):<input type="text" :value="name"><br/>
双向数据绑定(简写):<input type="text" v-model="name"><br/>
<!-- 不能这么写,因为v-model只能应用在表单类元素上 -->
<!-- <h2 v-model:x="name">我错了</h2> -->
</div>
</body>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
name:'伍六七'
}
})
</script>
</html>
data与el
data与el的2种写法
- el的2种写法
- new Vue时候配置el属性。
- 先创建Vue实例,随后再通过vm.$mount('#root')指定el的值
- data的2种写法
- 对象式(目前这种写法可以用对象式,后面用到组件就必须用函数式)
- 函数式
- 注意:由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>data与el的两种写法</title>
<!-- 引入Vue -->
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h1>姓名:{{name}}</h1>
</div>
</body>
<script type="text/javascript">
// el的两种写法
// 第一种写法
/* const v = new Vue({
el:'#root',
data:{
name:'伍六七'
}
})
*/
// 第二种写法
/* const v = new Vue({
data:{
name:'伍六七'
}
})
v.$mount('#root')
*/
// data的两种写法
new Vue({
el:'#root',
// 第一种写法:对象式
/*
data:{
name:'伍六七'
}
*/
// 第二种写法:函数式
data(){
console.log('this:',this) //此处的this是Vue实例对象
return{
name:'伍六七'
}
}
})
</script>
</html>
数据代理
- 实现:通过
vm
对象来代理data对象中属性的读写操作。 - 原理:通过
Object.defineProperty()
把data对象中所有属性添加到vm
上。同时为每一个添加到vm
上的属性,都指定一个getter/setter
方法,并在getter/setter
内部去操作data中对应的属性(比如页面渲染等伴随操作)。
数据监听
Vue监视数据的原理:
- vue会监视data中所有层次的数据。
- 如何监测对象中的数据?通过setter实现监视,且要在new Vue时就传入要监测的数据。
- 对象中后追加的属性,Vue默认不做响应式处理
- 如需给后添加的属性做响应式,请使用如下API:
Vue.set(target,propertyName/index,value)
vm.$set(target,propertyName/index,value)
- 如何监测数组中的数据?通过包裹数组更新元素的方法实现,本质就是做了两件事:
- (1)调用原生对应的方法对数组进行更新。
- (2)重新解析模板,进而更新页面。
- 注意,在Vue修改数组中的某个元素一定要用如下方法:
- 使用这些API:
push()、pop()、shift()、unshift()、splice()、sort()、reverse()
Vue.set()
或vm.$set()
- 使用这些API:
- 特别注意:
Vue.set()
和vm.$set()
不能给 vm 或 vm的根数据对象 添加属性!!!
事件处理
基本使用
- 使用
v-on:xxx
或@xxx
绑定事件,其中xxx是事件名; - 事件的回调需要配置在methods对象中,最终会在
vm
上; - methods中配置的函数,不要用箭头函数!否则this就不是
vm
了; - methods中配置的函数,都是被Vue所管理的函数,this的指向是
vm
或组件实例对象
; @click="demo"
和@click="demo($event)"
效果一致,但后者可以传参;
事件描述符
- prevent:阻止默认事件(常用);
- stop:阻止事件冒泡(常用);
- once:事件只触发一次(常用);
- capture:使用事件的捕获模式;
- 冒泡是从里往外冒,捕获是从外往里捕。
- 当捕获存在时,先从外到里的捕获,剩下的从里到外的冒泡输出。
- self:只有event.target是当前操作的元素时才触发事件;
- passive:事件的默认行为立即执行,无需等待事件回调执行完毕;
计算属性
- 定义:要用的属性不存在,要通过已有属性计算得来。
- 原理:底层借助了
Objcet.defineproperty
方法提供的getter和setter。 - get函数什么时候执行?
- 初次读取时会执行一次。
- 当依赖的数据发生改变时会被再次调用。
- 优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
- 备注:
- 计算属性最终会出现在vm上,直接读取使用即可。
- 如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>计算属性</title>
<!-- 引入Vue -->
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
姓:<input type="text" v-model="firstName"> <br/><br/>
名:<input type="text" v-model="lastName"> <br/><br/>
全名:<span>{{fullName}}</span> <br/><br/>
</div>
</body>
<script type="text/javascript">
const vm = new Vue({
el:'#root',
data(){
return {
firstName:'伍',
lastName:'六七',
}
},
computed:{
// 完整写法
fullName:{
// get有什么作用?当有人读取fullName时,get就会被调用,且返回值就作为fullName的值
// get什么时候调用?1.初次读取fullName时。2.所依赖的数据发生变化时。
get(){
console.log('get被调用了')
return this.firstName + '-' + this.lastName
},
// set什么时候调用? 当fullName被修改时。
set(value){
console.log('set被调用了',value)
const arr = value.split('-')
this.firstName = arr[0]
this.lastName = arr[1]
}
}
//简写
/*
fullName(){
console.log('get被调用了')
return this.firstName + '-' + this.lastName
}
*/
}
})
</script>
</html>
监视属性
基本使用
- 当被监视的属性变化时, 回调函数自动调用, 进行相关操作
- 监视的属性必须存在,才能进行监视
- 监视的两种写法:
- new Vue时传入watch配置
- 通过vm.$watch监视
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>监视属性</title>
<!-- 引入Vue -->
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2>小鸡岛的天气真:{{info}}</h2>
<button @click="changeInfo">变天</button>
</div>
</body>
<script type="text/javascript">
const vm = new Vue({
el:'#root',
data:{
isHot: true,
},
computed:{
info(){
return this.isHot ? '热啊' : '凉快'
}
},
methods: {
changeInfo(){
this.isHot = !this.isHot
}
},
// 第一种写法
/*
watch:{
isHot:{
// 初始化时让handler调用一下
immediate: true,
// 当isHot发生改变时调用。
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
}
}
*/
// 第一种写法:简写
/*
isHot(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue,this)
}
*/
})
// 第二种写法
vm.$watch('isHot',{
// 初始化时让handler调用一下
immediate:true,
// 当isHot发生改变时调用。
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
})
// 第二种写法:简写
/*
vm.$watch('isHot',(newValue,oldValue)=>{
console.log('isHot被修改了',newValue,oldValue,this)
})
*/
</script>
</html>
深度监视
- Vue中的
watch
默认不监测对象内部值的改变。 - 配置
deep:true
可以监测对象内部值改变。
样式绑定
class样式,写法:class="xxx"
xxx可以是字符串、对象、数组。
- 字符串写法适用于:类名不确定,要动态获取。
- 对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。
- 数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。
style样式,写法
:style="{fontSize: xxx}"
,其中xxx是动态值。:style="[a,b]"
其中a、b是样式对象。
条件渲染
v-if
- 写法:
v-if="表达式"
、v-else-if="表达式"
、v-else="表达式"
- 适用于切换频率较低的场景。
- 特点:不展示的DOM元素直接被移除。
v-show
- 写法:
v-show="表达式"
- 适用于切换频率较高的场景。
- 特点:不展示的DOM元素未被移除,仅仅是使用
display
样式隐藏掉 使用v-if时,元素可能无法获取到,而使用v-show一定可以获取到。
列表渲染
- 用于展示列表数据
- 语法:
v-for="(item, index) in list" :key="id"
key的原理
- key的作用:key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】, 随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
- 旧虚拟DOM中找到了与新虚拟DOM相同的key:
- ①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!
- ②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
- 旧虚拟DOM中未找到与新虚拟DOM相同的key
- 创建新的真实DOM,随后渲染到到页面。
- 旧虚拟DOM中找到了与新虚拟DOM相同的key:
- 用index作为key可能会引发的问题:
- 若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
- 如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题。
- 开发中如何选择key:
- 1.最好使用每条数据的唯一标识作为key, 比如id、手机号等唯一值。
- 2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。
收集表单数据
- 若:
<input type="text"/>
,则v-model收集的是value值,用户输入的就是value值。 - 若:
<input type="radio"/>
,则v-model收集的是value值,且要给标签配置value值。 - 若:
<input type="checkbox"/>
- 1.没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)
- 2.配置input的value属性:
- v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
- v-model的初始值是数组,那么收集的的就是value组成的数组
- v-model的三个修饰符:
- lazy:失去焦点再收集数据
- number:输入字符串转为有效的数字
- trim:输入首尾空格过滤
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>收集表单数据</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<form @submit.prevent="demo">
账号:<input type="text" v-model.trim="userInfo.account"> <br/><br/>
密码:<input type="password" v-model="userInfo.password"> <br/><br/>
年龄:<input type="number" v-model.number="userInfo.age"> <br/><br/>
性别:
男<input type="radio" name="sex" v-model="userInfo.sex" value="male">
女<input type="radio" name="sex" v-model="userInfo.sex" value="female"> <br/><br/>
爱好:
学习<input type="checkbox" v-model="userInfo.hobby" value="study">
打游戏<input type="checkbox" v-model="userInfo.hobby" value="game">
吃饭<input type="checkbox" v-model="userInfo.hobby" value="eat">
<br/><br/>
所属校区
<select v-model="userInfo.city">
<option value="">请选择校区</option>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="shenzhen">深圳</option>
<option value="wuhan">武汉</option>
</select>
<br/><br/>
其他信息:
<textarea v-model.lazy="userInfo.other"></textarea> <br/><br/>
<input type="checkbox" v-model="userInfo.agree">阅读并接受<a href="http://www.atguigu.com">《用户协议》</a>
<button>提交</button>
</form>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
userInfo:{
account:'',
password:'',
age:18,
sex:'female',
hobby:[],
city:'beijing',
other:'',
agree:''
}
},
methods: {
demo(){
console.log(JSON.stringify(this.userInfo))
}
}
})
</script>
</html>
过滤器
定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。 语法:
- 注册过滤器:
Vue.filter(name,callback)
或new Vue{filters:{}}
- 使用过滤器:
{{ xxx | 过滤器名}}
或v-bind:属性 = "xxx | 过滤器名"
注意: - 过滤器也可以接收额外参数、多个过滤器也可以串联
- 并没有改变原本的数据, 是产生新的对应的数据
内置指令
常用指令
v-bind
: 单向绑定解析表达式, 可简写为:xxx
v-model
: 双向数据绑定v-for
: 遍历数组/对象/字符串v-on
: 绑定事件监听, 可简写为@
v-if
v-else
: 条件渲染v-show
: 条件渲染
v-text指令:
- 作用是向其所在的节点中渲染文本内容,相当于原生js中的innerText。
- 与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会。
v-html指令:
- 作用是向指定节点中渲染包含html结构的内容,相当于原生js中的innerHTML。
- 与插值语法的区别:
- (1).v-html会替换掉节点中所有的内容,{{xx}}则不会。
- (2).v-html可以识别html结构。
- 注意:v-html有安全性问题
- 在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。
- 一定要在可信的内容上使用v-html,永不要用在用户提交的内容上
v-cloak指令(没有值):
- 本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。
- 使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题。
v-once指令:
v-once
所在节点在初次动态渲染后,就视为静态内容了。- 以后数据的改变不会引起
v-once
所在结构的更新,可以用于优化性能。
v-pre指令:
- 跳过其所在节点的编译过程。
- 可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。
自定义指令
- 定义语法:
- 局部指令:
new Vue({directives:{指令名:配置对象}})
或new Vue({directives{指令名:回调函数}})
- 全局指令:
Vue.directive(指令名,配置对象)
或Vue.directive(指令名,回调函数)
- 局部指令:
- 配置对象中常用的3个回调:
- (1).bind:指令与元素成功绑定时调用。
- (2).inserted:指令所在元素被插入页面时调用。
- (3).update:指令所在模板结构被重新解析时调用。
- 备注:
- 指令定义时不加v-,但使用时要加v-;
- 指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。