Vue基础入门
什么是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默认就会对其做劫持处理,所以这些数据是响应式的;
-
- 在vue开发中,我们把需要的数据一般都写在data中(哪怕现在用不着,我们也先在data中初始化一下),目的是让这些数据变为响应式数据
- 而且对data中所有层级做深层次监听劫持
- 特殊 :对于数据来讲,数组本身会做劫持;但是数据中的每一个索引项并没有做劫持;这样是不好的,vue为了解决这个问题,把数组中的7个方法(push/pop/shift/unshift/splice/sort/reverse)进行了重写,后期我们操作这个7个方法,不仅可以修改数据中某一项的内容,而且也会触发视图的重新渲染;
vm.arr=100 这是修改arr的值,触发重新渲染
vm.arr[0]=100 这是修改arr堆内存中的信息,不属于修改arr本身,不会触发重新渲染
vm.arr.splice(0, 1, 100); 基于重写的7个方法操作数据每一项,会重新渲染「推荐」
非响应式数据: 自己直接挂在到实例上的是非响应式的数据;
- Vue.prototype
-
- vm.$forceUpdate() 强制让视图重新渲染
-
- vm.set可以在后续把对象中的这个属性,设置为响应式的;
响应式数据 VS 非响应式数据
-
- 都可以修改值
-
- 响应式数据在值修改之后,会通知视图重新渲染;因为其基于Object.defineProperty对其做了get/set劫持;当我们需改响应式数据值,会触发set函数,在set函数中一方面把值修改,一方面会通知视图重新渲染!!
- 后期在vue的项目中,我们只需要展开实例,看哪些数据做了get/set,做了劫持的数据就是响应式的!!
vm使用规范
vm的optionsAPI对象中的属性
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修饰符
- ..passive
键盘事件的修饰符
@keydown/@keyup/@keypress
@keydown.13:直接使用键码也可以,【所有的键码】-
@keydown.enter:只有按下enter键时会触发事件
-
@keydown.tab:按下tab键时会触发
-
.delete/esc/space/up/down/left/right
-
ctrl/alt/shift/meta
left/right/middle鼠标左中右按键
自定义按键修饰符
- `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]":每个数组中的数据项都是一个对象,存在样式名和值
- 数组语法:
修饰符
..came l``: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属性表示可多选,则==状态值需要是一个数组==
- 3、select标签加
<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】
- 1、会把
-
- 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状态值必须是一个数组==
- 1、会把
-
- 2、监听每一个
checkbox的change事件,只要有选项被选中或取消选中,都会把value值赋值给hobby状态值
- 2、监听每一个
<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();
})