什么是vue?
Vue是“渐进式框架”:vue.min.js只包含了vue最核心的内容「例如:options api、数据处理、template模板视图的解析等操作」;真实项目中我们还会根据需求,导入
- + vuex 实现公共状态管理
- + vue-router 实现SPA(single page application)单页面应用
- + element-ui/antdv/iview/vant/cube... 基于这些UI组件库快速创建项目
- + axios 实现数据通信
- + @vue/cli 基于vue的脚手架创建工程化项目
- + ...
Vue是基于MVVM模式构建的框架,它本身实现了viewModel层去监听数据/视图的变化,从而去渲染视图/修改数据,所以我们学习vue主要考虑两条主线
+ Model数据层 + View视图层 最后学习vue的原理,把两条主线关联在一起
- ==MVVM:(Model-View-Viewmodel):数据-视图-视图模型==
- - M(Model):数据层->后台,对于前段来说就是后端提供的一个API接口
- - V(View):视图层->浏览器
- - VM(Viewmodel):视图模型层-> 一个同步View和Model的对象
`ViewModel`通过双向数据绑定把`View`层和`Model`层连接了起来,而View和Model之间的同步工作完全是自动的,无需人为干涉,因此开发者只需要关注业务逻辑,不需要手动操作DOM,不需要关注数据状态的同步问题,复杂的数据状态维护完全由MVVM来统一管理。
+ Model数据层
如何判断数据是否为响应式?
响应式数据 new Vue的时候,在data中指定的数据,vue默认就会对其做劫持处理,所以这些数据是响应式的;
vm.arr=100 这是修改arr的值,触发重新渲染
vm.arr[0]=100 这是修改arr堆内存中的信息,不属于修改arr本身,不会触发重新渲染
vm.arr.splice(0, 1, 100); 基于重写的7个方法操作数据每一项,会重新渲染「推荐」
非响应式数据: 自己直接挂在到实例上的是非响应式的数据;
- Vue.prototype
- + vm.$forceUpdate() 强制让视图重新渲染
- + vm.$set([object],[key],[value]) 针对于data中的某个对象,开始没有初始化某个属性,后期才新加的(这样这个属性默认是非响应式的,改值视图也不会重新渲染);$set可以在后续把对象中的这个属性,设置为响应式的;
响应式数据 VS 非响应式数据
- + 都可以修改值
- + 响应式数据在值修改之后,会通知视图重新渲染;因为其基于Object.defineProperty对其做了get/set劫持;当我们需改响应式数据值,会触发set函数,在set函数中一方面把值修改,一方面会通知视图重新渲染!!
- 后期在vue的项目中,我们只需要展开实例,看哪些数据做了get/set,做了劫持的数据就是响应式的!!
vm使用规范
vm的options API对象中的属性
let vm = new Vue({
//指定视图,将html中的视图元素与vm绑定
el: "#app",
//构建数据模型,挂载需要在视图中渲染的数据,这里的数据都是==响应式数据==
data: {
msg: '<a href="">你好,世界</a>',
num: 8,
arr: [{
id: 1,
title: "今天天气很好"
}, {
id: 2,
title: "很适合学习"
}]
},
/* methods编写普通方法 */
// 1.这样写方法,不论方法咋执行的,也不论在哪执行的,方法中的this是vue的实例「vue内部做了处理」;所以我们不会把其设置为箭头函数(this->window)!!
// 2.methods编写的方法会直接挂载到实例上(不做get/set劫持),所以可以直接在视图中使用
methods: {
hadle(e) {
console.log('handle', ev.keyCode);
},
submit() {
console.log('submit');
},
fn() {
console.log("fn");
}
},
// 计算属性:依赖于某些数据值,计算出新的值 【多个值变化,影响到一个结果值变化】
**计算**:以函数的形式定义 - **属性**:以属性的形式使用
//【==缓存效果==:依赖的值没变,会用之前计算的值,只有依赖的值变了,才会重新计算】
// - ==这里的this也指向vm==
computed:{
fun1(){}
fun1(){}
},
// ==watch:侦听属性【一个值变化,影响多个结果值变换】==
- **侦听**:watch中的定义的属性名就是我们监控的vm里的私有属性名,当这个私有属性发生变化是,会自动执行watch中对应方法:可以看作是`computed`和`methods`的结合体
**特点**:`watch能够监控路由`,如果不需要监控路由,==我们尽可能的用computed的set方法来代替watch==,原因是computed有缓存
- **注意**:我们监控的对象必须vm里已经存在该私有属性
})
computed、watch、methods的区别
- computed是属性调用,而methods是函数调用
- - computed带有缓存功能,而watch没有
- - watch可以监控对象,而computed、methods不能
computed用法
computed是依赖已有的变量来计算一个目标变量,大多情况下都是多个变量凑在一起,计算出一个变量,并且computed具有缓存机制,依赖值不变的情况下,其会直接读取缓存进行复用.computed不能进行异步操作 多对一
<div>{{funllname}}</div>
<script>
let vm=new Vue({
computed:{
// 以函数的形式定义
fullname(){
return this.first+"-"+this.last;
}
}
})
对象定义方式**:computed中的计算属性本身其实存在两个方法,只是默认获取,`get()和set`方法,原理:使用了Object.defineProperty()中的get/set劫持
computed:{
fullname:{
// 对fullname属性获取时拦截,也是函数定义形式使用的默认方法
get(){
//实现多个值变换,改变一个结果值
return this.first+"-"+this.last;
},
//对fullname属性赋值时拦截
set(val){//val是想赋给fullname的值,也可以说是心值
let arr=val.split("-");
//实现一个值变化,改变多个结果只
this.first=arr[0];
this.last=arr[1];
}
}
}
watch的用法
监听,某一个变量的变化,并执行响应的回调函数,通常是一个变量的变化决定对个变量的变化,watch可以进行异步操作 一对多
watch:{
//fullname就是我们监控的vm实例中的私有属性,存在两个值
//函数式写法
"obj"(newVal,oldVal){
let arr=newVal.split("-");
//实现一个值变化,改变多个结果值
this.first=arr[0];
this.last=arr[1];
},
// 对象式写法
"obj":{
// 这是函数式写法默认用的方法
// handler(newVal,oldVal){...}
//设置对obj的属性是否进行深侦听
deep:true,
// 设置对obj的属性是否在第一次渲染页面时立即侦听一次
immediate:true
}
}
vm实例的方法
- `vm.$forceUpdate()`:强制让vm视图渲染
- - `vm.$set([obj],[key],[value])`:将obj对象中[key]属性的值改为[value],并且把[key]属性转换为响应式数据
- - 针对于data中的某个对象,开始没有初始化某个属性,后期才新加的(这样的属性默认是非响应式的:改值视图不会重新渲染):$set可以在后续把对象只能够的这个属性,设置为响应式的。
指定视图的方法4种
let vm=new Vue({
//方案一:指定页面中存在的DOM元素作为视图
el:`#box`,
//方案二:创建视图
template:`<div>
//最后把数据渲染在这个视图中
</div>`,
//方案四:render渲染视图
render:h=>{
...
}
})
//方案三:挂载视图
vm.$mount(('#app'));
View视图层
Vue构建视图是基于“template”模板语法:template中定义了很多vue内置的语法规范,这些规范不能被浏览器直接识别,需要基于vue进行渲染「流程:根据数据和template视图,把其识别为一个虚拟DOM对象(vnode) -> DOM-DIFF -> 把vnode虚拟DOM变为真实DOM」
思维导图
vue内部指令
vue修饰符
普通指令:v-xxx
1. 渲染数据的属性`v-text/v-html:[string]`:把数据渲染到指定的容器中
- - `<h2 v-html="msg"></h2>`:把msg数据渲染到h2标签中,如果数据内容是html字符串【带标签】,会自动进行识别渲染
- - `<h2 v-text="msg"></h2>`:把msg数据渲染到h2标签中,如果数据内容是html字符串【带标签】,不会进行识别渲染,与`{{}}`小胡子语法相同
<h2 v-text="msg"></h2>//带标签
<h2 v-html="msg"></h2>//不带标签
<h2>{{msg}}</h2>//小胡子语法{{}}与v-text相同
2. v-show:[boolen]控制元素的显示隐藏{原理:控制display='none/block'};所以这种方式,不论结果是TRUE还是FALSE,元素都渲染出来了,只是display值不同而已!!
<h2 v-show="num>15">{{msg}}</h2>
3. v-if/v-else-if/v-else
v-if:[boolean]`:控制元素的显示隐藏,但是和v-show不一样,如果值是false,元素是不进行渲染的(页面中没有这个元素),只有结果是true,元素才会渲染!!
- ==两者应该分场景使用:==
- - 如果元素切换显示隐藏频率不高的情况下,使用`v-if`性能更好
- - 如果元素切换显示隐藏频率比较高的情况下,`v-if`会使元素一直销毁和重新渲染,此时使用`v-show`更好
- `v-else-if:[boolean]/v-else`:与`v-if`联合使用,如果三个组合用,用的标签必须挨着,否则无法生效
- - `v-else-if:[boolean]`:if条件不成立则判断这个条件是否成立
- - `v-else`: if或else-if条件不成立,则判断这个条件
<button v-if='num>10'>红色</button>
<button v-else-if="num<10">蓝色</button>
<button v-else="num<20">绿色</button>
4. v-for
v-for="(item,index) in xxx" :key="index"`:循环创建元素,实现内容绑定
- 想让哪个元素创建多个,就给哪个元素设置v-for
- - 循环的元素要设置唯一值key【**最好不要使用索引作为key?**】
- - xxx可以是数字、字符串、数组类型
:xxx是数字,先隐式转换为数组`[1,2,3,4,5]`,再进行逐项循环渲染
<ul>
<li v-for="(item,index) in 5">
{{item}}--{{index}}
</li>
xxx是字符串,先隐式转为数组`[s,t,r]`,再逐项循环渲染
<li v-for="(item,index) in 'str'">
{{item}}--{{index}}
</li>
xxx是数组类型,直接逐项循环渲染
<li v-for="(item,index) in arr" :k='item.id'>
{{item.title}}
</li>
</ul>
为什么在v-for中必须有:key="xxx"属性,并且不能用index?
- 当在组件中使用 v-for 时,key 现在是必须的
- 当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用 “**就地复用**” 策略。
- 如果数据项的顺序被改变,Vue将**不是移动 DOM 元素来匹配数据项的顺序**, 而是**简单复用此处每个元素**,并且确保它在特定索引下显示已被渲染过的每个元素。
比如我们用多选框选中了数组中的第二个数据:
- - 如果没有存key值:则浏览器默认认为index=2的数据被选中了,当我们在数组最前面插入数据项,则后面的数据位置都发生了改变,而系统仍然认为index=2的数据项被选中了,这时正确被选中的应该是第三项猜对
- - 如果有:key值,但是:key="index":浏览器回去找:key=2的数据选中,但是当在最前面插入数据项时,后面所有数据的key值,都会跟着改变,结果仍然是不正确的
所以我们为了保证Vue能够跟踪每个节点的身份,从而重用和重新排序现有的元素,==我们应该给每一项提供一个唯一的key属性,且这个key属性不会被改变。
.template与div用v-for的区别
v-for的优先级高于v-if那么就会出现一个问题
<div v-for="(item,index) in arr" v-if=" item>10">{{item}}</div>
以上标签在渲染时,会首先通过`v-for`循环创建3个div标签,再通过`v-if`判断是否符合条件,不符合条件的再被销毁,但这里有item=10的标签并不需要渲染创建再销毁,这样是消耗性能的
//-----------------------------解决方法-----------------------------------------
- template标签:==template标签不会被渲染到页面上,
template里面的div在渲染时已经知道了item值,所以不符合规则的div不会再无用的生成再销毁一次,优化了性能
<template v-for="(item,index) in arr">
<div v-if="item>10">{{item}}</div>
</template>
5.v-on
`v-on:[事件]="要执行的方法"`:缩写为`@事件="要执行的方法"
原理基于DOM2事件绑定实现的 addEventLinstener冒泡事件==【好处是,后期获取到这个DOM元素对象,再做事件绑定,也不会和@click绑定的方法冲突】
*以下写法都是点击的时候才会把handle执行
- - `@click="handle"=>handle(ev){...}`
- - `@click="handle(10,20)"=>handle(10,20){...}`:也不是把handle立即执行,而是点击才执行,只不过点击的时候,会把10/20传递给handle
原生中
button.addEventListener('click',handle(10,20)) 不等点击 handle就已经执行了
button.addEventListener('click',handle.bind(null,10,20)); 这样才是基于bind预先把handle中的this和值处理好,点击的时候,才会执行handle
- - `@click="handle(10,20,$event)"=>handle(10,20,ev){...}`:视图中出现的`$event`代表是事件对象
<button @click="handle">按钮</button>
<button @click="handle(10,20)">按钮</button>
<button @click="handle(10,20,$event)">按钮</button>
点击事件的修饰符
-
.stop`@click.stop:阻止事件的冒泡传播=>同理于:`e.stopPropergation()`
-
.prevent `@click.prevent`:阻止事件的默认行为=>同理于:`e.preventDefault()`
-
.capture @click.capture`:将事件变为==捕获事件===>同理于:`dom.addEventListener([event],[fun],true)`,[**默认都是在冒泡阶段触发**]
-
.self `@click.self`:只有点击的事件源就是当前元素本身,绑定的方法才会执行{**但是没有阻止冒泡传播**}=>如同在函数执行之前做了校验:`if(e.target==e)=true`时,才会执行
-
.once`@click.once`:只触发执行一次,然后把事件绑定的方法一吃掉=>相当于执行一次后会默认执行`dom.removeEventListener([event],fn)`
-
-..passive` `@click.passive`:当我们在监听元素滚动事件的时候,会一直触发onscroll事件,在pc端没有啥问题,但是在移动端会让网页变卡,因此我们使用这个修饰符的时候想到相当于给onscroll事件整了一个lazy修饰符
键盘事件的修饰符
@keydown/@keyup/@keypress
自定义按键修饰符
- `Vue.config.keyCodes.a=65;`
- `@keydown.a='xxx'`
- `@keydown.65='xxx'`
组合按键
@keydown.ctrl.65="xxx"`:按下ctrl+a就会触发
- 默认情况下,按下的按键只要包含组合键(==哪怕还按了其他键)==也会触发,但是我们可以基于.exact修饰符,设置精准匹配(只有按下规定的键,其他键不按,才会触发)
- `@keydown.ctrl.65.exact`:只有按下ctrl+a才会触发
- `@click.exact`:只有只点击了单击按键时触发
6. v-bind
给元素的attribute动态绑定值
`v-bind:xxx="xxx"`:简写为`:xxx="xxx"`给元素的attribute动态绑定值
- - 但凡属性值是一个变量的值或者是一个表达式计算的结果,都需要给属性`v-bind`一下
- - 基于属性给子组件传递信息的时候,如果想让属性值是字符串以外的类型,也需要`v-bind`一下
动态绑定class
-
对象语法:`:class="{box:true,active:true/false}"`:active是class样式类名,它的值true/false决定元素是否拥有这个样式类名
-
- 数组语法:`:class="['box',num>10?'active':'']"`:box是肯定有的类名,根据num条件决定是否有active类名
动态绑定style:
样式需要用驼峰命名法
- 对象语法:`:style="{color:'red',fontSize:num>10?'20px':'12px'}"`:元素颜色静态为红色,字体大小根据num决定
- - 数组语法:`:style="[color,fontSize]"`:每个数组中的数据项都是一个对象,存在样式名和值
修饰符
..camel``:sup-name.camel`:把设定的`sup-name`自动转为camel规范=>`supName`
.prop `:supName.prop`
- 默认情况下,给元素设置的属性都会呈现在元素的结构上,我们可以基
getAttribute/setAttribute来设置获取;
- 但是设置了prop修饰符,设置的自定义属性不在结构上,不再html结构中显示,而是放到了DOM对象的堆内存中
- 给元素设置自定义属性有两种方式
- - 方式一:写在结构上,`<div index="8"></div>`,用`setAttribute、getAttribute`
- - 方式二:写在堆内存中,box{index:8},用`box.index=8、box.index`
.sync:supName.sync
性能优化指令
7. v-pre让元素及其后代元素跳过编译
- `v-pre`:在视图编译渲染的时候,跳过拥有这个指令的元素及其后代元素[不进行任何的语法的编译,你写的是啥,呈现在视图上就是啥];
- - 真实项目中,我们完全可以把一些“静态内容”不进行任何的编译,以此来优化视图编译渲染的速度!!
8. v-once控制元素或组件的编译次数
- `v-once`:让元素或者组件只在视图第一次渲染的时候编译一次,当视图重新渲染的时候,这部分内容就不再重新编译了;
- - 适用于第一次动态绑定完成后,后期不会再更新的内容
9. v-clock延迟显示元素
- - `v-clock`:在非工程化的vue项目中,在js没有加载出来之前,页面会呈现出原始的{{xxx}}这种内容,只有js加载出来并处理完成,才能把template语法编译为真实DOM渲染到页面中。
- - 为了防止这种“闪”的效果,我们在js没有加载完成之前让容器隐藏,加载完成之后再让它显示。
-
用法:
<style>
[v-clock]{
display:none;
}
</style>
<div v-clock></div>
10. v-model
v-model`:实现==视图更新控制状态修改==
- 实现原理:
- - @1把状态值赋值给文本框的value属性
- - @2基于==input==事件监听文本框内容的改变,当内容改变的时候,去修改对应的状态值
修饰符
.lazy把监听事件从input换成change*
- input事件:只要正在输入就触发,不一定输入的内容进入到文本框中(例如:中文输入框)
- change事件:内容已经进入到文本框,摁下enter键时,(value值确认改变时),才会触发
- - 所以change比input性能消耗低、但是流畅度也比较低
.number`规定输入的内容必须是数字格式
文本框中默认输入的内容都是字符串格式,设置了这个修饰符,vue内部会把输入的内容自动转换为数字(如果出现非有效数字字符,则会把非有效字符及以后的内容干掉)
.trim自动去除输入内容的首尾空格
能够用v-model的修饰符的标签类型有:`input/textarea、select(option)、input[type='radio']、input[type='checkbox']`
select下拉框
- - 1、拿city状态值和option中的value值做比较,和谁一样,谁默认选中
- - 2、监听下拉框的change事件,选中的是谁,就把那个选项的value赋值给city状态
- - 3、select标签加`mutiple`属性表示可多选,则==状态值需要是一个数组==
<select v-model="city">
<option value="">请选择</option>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="shenzhen">深圳</option>
</select>
<script>
let vm = new Vue({
data: {
city: 'beijing'
}
})
vm.$mount('#app')
</script>
在radio点选框中
- - 1、会把`v-model中的sex`状态值相同的作为同一组,一组中只能选中一个【也可以自己设置name】
- - 2、根据sex状态值和value值做对比,相同的则默认被选中
- - 3、监听每一个radio的change事件,把选中radio的value值赋值给sex状态值
<input type="radio" name="sex" :value="0" v-model="sex">男
<input type="radio" name="sex" :value="1" v-model="sex">女
let vm = new Vue({
data: {
sex: 0
}
})
vm.$mount('#app')
在checkbox多选框中
- - 1、会把`v-model中的hobby`状态值相同的作为同一组,一组中可以选中多个,所以==hobby状态值必须是一个数组==
- - 2、监听每一个`checkbox`的change事件,只要有选项被选中或取消选中,都会把value值赋值给hobby状态值
<input type="checkbox" name="hobby" value="jump" v-model="hobby">音乐
<input type="checkbox" name="hobby" value="movie" v-model="hobby">电影
<input type="checkbox" name="hobby" value="music" v-model="hobby">跳舞
<input type="checkbox" name="hobby" value="read" v-model="hobby">阅读
let vm = new Vue({
data: {
hobby: ['jump', 'read']
}
})
vm.$mount('#app')
vue2.0中如何使用过滤器?
vue1.0中存在自带的过滤器
vue2.0中需要在vm实例options API的filters里定义局部过滤器,也可以用Vue.filter定义全局过滤器
- 能够用filter过滤器的有`{{}}(小胡子语法)`和`v-bind指令`
Vue.filter("uppercase", function (value) {
console.log(value); //msg abc
return value.toUpperCase();
})