一、认识Vue
1、vue特点
命令式编码:每一步都要写正确
声明式编码:提高代码复用率
2、搭建vue2环境
(1)下载开发版本的vue.js
(2)安装chrome的vue调试工具vue_devtools
(3)关闭开发者提示,Vue.config.productionTip = false
3、创建vue实例
<div id="root"> <h1>hello,{{name}}</h1> <a v-bind:href=url>hi</a> </div> <script type="text/javascript"> const x = new Vue({ el: '#root', data: { name: "ct", p=7&spm_id_from=pageDriver&vd_source=155d951c4687163925c57298113b2636' } }) </script>
4、插值语法
(1) 功能: 用于解析标签体内容
(2)语法: {{xxx}} ,xxxx 会作为 js
5、指令语法
(1)功能: 解析标签属性、解析标签体内容、绑定事件
(2)举例:v-bind:href = 'xxxx' ,xxxx 会作为 js 表达式被解析
(3)说明:Vue 中有有很多的指令,此处只是用 v-bind 举个例子
6、数据绑定
(1)单向绑定
- 语法:v-bind:href ="xxx" 或简写为 :href
- 特点:数据只能从 data 流向页面
(2)双向绑定
- 语法:v-mode:value="xxx" 或简写为 v-model="xxx"
- 特点:数据不仅能从 data 流向页面,还能从页面流向 data
- 只能给可以和用户产生互动的标签使用
7、el的两种写法
(1)传统挂载:在实例中用el属性挂载
(2)组件式写法——更灵活
利用v实例化对象的$mount属性
const v = new Vue({ //el: '#root', data: { name: "ct", url: 'xxx' } }) v.$mount('#root')
8、data的两种写法
(1)传统写法:对象式
const x = new Vue({ el: '#root', data: { name: "ct", url: 'xxx' } })
(2)函数式
学习组件时用该种写法,并且data函数不能写箭头函数(不然this不指向vue实例对象,而是window)
const x = new Vue({ el: '#root', /* data: { name: "ct", url: 'xxx' } */ //也可写成 data(){}函数格式 data:function(){ return{ name:"ct" } } })
二、数据代理
1、MVVM模型
(1)data中所有的属性,最后都出现在vm上
(2)vm上的所有属性及vue原型上所有属性,在vue模板中都可以直接使用
2、Object.defineProperty方法
(1)语法
Object.defineProperty(对象名,'属性名',配置项)
(2)配置项的设置:
可以枚举、可以被修改、可以被删除的属性值设置
get()函数和set()函数——即getter、setter
<script type="text/javascript" > let number = 18 let person = { name:'张三', sex:'男', } Object.defineProperty(person,'age',{ // value:18, // enumerable:true, //控制属性是否可以枚举,默认值是false // writable:true, //控制属性是否可以被修改,默认值是false // configurable:true //控制属性是否可以被删除,默认值是false //当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值 get(){ console.log('有人读取age属性了') return number }, //当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值 set(value){ console.log('有人修改了age属性,且值是',value) number = value } }) // console.log(Object.keys(person)) console.log(person) </script>
3、数据代理
通过一个对象对另一个对象中属性的操作(读/写)
4、Vue中的数据代理
(1)通过vm对象来代理data对象中属性的操作(读/写)
(2)好处:更加方便的操作data中的数据
(3)基本原理
- 通过Object.defineProperty把data对象中所有属性添加到vm上
- 为每一个添加到vm上的属性,都指定一个getter和setter
- 在getter和setter内部去操作(读/写)data中对应的属性
三、事件处理
1、事件使用
(1)使用v-on:xxx或@xxx绑定事件,其中xxx是事件名
(2)事件的回调需要配置在methods对象中,最终会在vm上
(3)methods中配置的函数,不需要用箭头函数!否则this就不是vm了
(4)methods中配置的函数,都是被Vue所管理的函数,this的指向是vm或组件实例对象
(5)@click='demo'和@click="demo($event)"效果一致,但后者可以传参
2、事件修饰符
<a href="#" @click.prevent="showInfo"></a>
(1).prevent(常用):阻止事件的默认行为 event.preventDefault()
(2).stop(常用):停止事件冒泡 event.stopPropagation((3).once(常用):事件只触发一次(常用)
(4).capture:使用事件的捕获模式
(5).self:只有event.target是当前操作的元素才是触发事件
(6).passive:事件的默认行为立即执行,无需等待事件回调执行完毕
以上效果可连续写。e.g @click.prevent.stop="showInfo"
3、键盘事件
<input type="text" placeholder="按下回车提示输入" @keyup.enter="showInfo">
- (1)vue中常用的键盘别名
- 回车=>enter
- 删除=>delete(捕获“删除”和“退格”键)
- 退出=>esc
- 空格=>space
- 换行=>tab
- 上=>up
- 下=>down
- 左=>left
- 右=>right
- (2)vue未提供按键
- 通过原始的key值去绑定,但注意要转为kebab-case(短横线命名)
- (3)系统修饰键(用法特殊):ctrl、alt、shift、meta
- 配合keyup使用:按下修饰键的提示,再按下其他键,随后释放其他键。事件才触发。
- 配合keydown使用:正常触发事件
- (4)也可以使用keyCode去指定具体的按键(不推荐)
- (5)Vue.config.keyCodes.自定义键名=键码,可以去定制案件别名
Vue.config.keyCodes.huiche=13
四、计算属性
1、计算属性 computed
- 定义:要用的属性不存在,要通过已有的属性计算得来
- 原理:底层借助了Object.defineproperty方法提供的getter和setter
2、特点
- 和data、methods并列,与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
- 计算属性最终会出现在vm上,直接读取使用既可
- 如果计算属性要被修改,那必须写set函数去响应修改,且set要引起计算时依赖的数据发生变化。
3、get什么时候调用
(1)初次读取fullName时
(2)所依赖的数据发生变化时
4、简写
只读不改(只有get)
<!-- 准备好一个容器--> <div id="root"> 姓:<input type="text" v-model="firstName"> <br/><br/> 名:<input type="text" v-model="lastName"> <br/><br/> 全名:<span>{{fullName}}</span> <br/><br/> //这里调用computed不需要加括号 </div> const vm = new Vue({ el:'#root', data:{ firstName:'张', lastName:'三', }, computed:{ //完整写法 /* fullName:{ get(){ console.log('get被调用了') return this.firstName + '-' + this.lastName }, 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 } } })
五、监视属性
1、 监视属性watch
(1)当被监视的属性发生变化时,回调函数自动调用,机械能相关操作
(2)监视的属性必须存在,才能进行监视
2、监视的两种写法
(1)创建实例时就监视
通过.new Vue时传入watch配置
(2)用户交互时监视
const vm = new Vue({ el:'#root', data:{ isHot:true, }, computed:{ info(){ return this.isHot ? '炎热' : '凉爽' } }, methods: { changeWeather(){ this.isHot = !this.isHot } }, /* watch:{ isHot:{ immediate:true, //初始化时让handler调用一下 //handler什么时候调用?当isHot发生改变时。 handler(newValue,oldValue){ console.log('isHot被修改了',newValue,oldValue) } } } */ }) vm.$watch('isHot',{ immediate:true, //初始化时让handler调用一下 //handler什么时候调用?当isHot发生改变时。 handler(newValue,oldValue){ console.log('isHot被修改了',newValue,oldValue) } })
3、深度监视
(1)Vue中的watch默认不监测对象内部值的改变(一层)。
(2)配置deep:true可以监测对象内部值改变(多层)。
- 备注:
(1)Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以!
(2)使用watch时根据数据的具体结构,决定是否采用深度监视。4、监视的简写形式
const vm = new Vue({ el:'#root', data:{ isHot:true, }, computed:{ info(){ return this.isHot ? '炎热' : '凉爽' } }, methods: { changeWeather(){ this.isHot = !this.isHot } }, watch:{ //正常写法 /* isHot:{ // immediate:true, //初始化时让handler调用一下 // deep:true,//深度监视 handler(newValue,oldValue){ console.log('isHot被修改了',newValue,oldValue) } }, */ //简写 /* isHot(newValue,oldValue){ console.log('isHot被修改了',newValue,oldValue,this) } */ } }) //正常写法 /* vm.$watch('isHot',{ immediate:true, //初始化时让handler调用一下 deep:true,//深度监视 handler(newValue,oldValue){ console.log('isHot被修改了',newValue,oldValue) } }) */ //简写 /* vm.$watch('isHot',(newValue,oldValue)=>{ console.log('isHot被修改了',newValue,oldValue,this) }) */
5、computed和watch的区别
(1)computed能完成的功能,watch都可以完成。
(2)watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。
- 两个重要的小原则:
(1)所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。
(2)所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,这样this的指向才是vm 或 组件实例对象。
六、绑定样式
1、绑定class样式方法
(1):class='xxx'
(2)表达式是字符串: 'classA'
<!-- 绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定 --> <div class="basic" :class="mood" @click="changeMood">{{name}}</div> <br/><br/>
(3)表达式是对象: {classA:isA, classB: isB}
<!-- 绑定class样式--对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 --> <div class="basic" :class="classObj">{{name}}</div> <br/><br/>
(4)表达式是数组: ['classA', 'classB']
<!-- 绑定class样式--数组写法,适用于:要绑定的样式个数不确定、名字也不确定 --> <div class="basic" :class="classArr">{{name}}</div> <br/><br/>
2、绑定style样式方法
(1):style="{ color: activeColor, fontSize: fontSize + 'px' }"
(2) 其中 activeColor/fontSize 是 data 属性
<!-- 绑定style样式--对象写法 (常用)--> <div class="basic" :style="styleObj">{{name}}</div> <br/><br/> <!-- 绑定style样式--数组写法(少,不推荐) --> <div class="basic" :style="styleArr">{{name}}</div>
上述的vue实例部分
const vm = new Vue({ el:'#root', data:{ name:'尚硅谷', mood:'normal', classArr:['atguigu1','atguigu2','atguigu3'], classObj:{ atguigu1:false, atguigu2:false, }, styleObj:{ fontSize: '40px', color:'red', }, styleObj2:{ backgroundColor:'orange' }, styleArr:[ { fontSize: '40px', color:'blue', }, { backgroundColor:'gray' } ] }, methods: { changeMood(){ const arr = ['happy','sad','normal'] const index = Math.floor(Math.random()*3) this.mood = arr[index] } }, })
七、条件渲染
1、v-if
写法:
(1)v-if="表达式"
(2)v-else-if="表达式"
(3)v-else="表达式"
适用于:切换频率较低的场景。
特点:不展示的DOM元素直接被移除。
注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”。可以配合template实现批量相同条件渲染
<!-- v-if与template的配合使用 --> <template v-if="n === 1"> <h2>你好</h2> <h2>尚硅谷</h2> <h2>北京</h2> </template>
2、v-show
写法:v-show="表达式"
适用于:切换频率较高的场景。
特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉备注:使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到。
八、列表渲染
1、v-for
(1)用于展示列表数据
(2)语法:v-for="(item, index) in xxx" :key="yyy"(:key属性是指属性唯一标识index/key/index)
(3)可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)2、key作用与原理
!!!如果用v-for遍历未写key,则默认将index作为key的值
初始数据→生成虚拟DOM→虚拟DOM转为真实DOM
数据变化→生成虚拟DOM→虚拟DOM对比算法→将虚拟DOM转换为真实DOM
3、面试题:react、vue中的key有什么作用?(key的内部原理)
(1)虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】, 随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:(2)对比规则:
- 旧虚拟DOM中找到了与新虚拟DOM相同的key:
- ①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!
- ②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
- 旧虚拟DOM中未找到与新虚拟DOM相同的key
- 创建新的真实DOM,随后渲染到到页面。
(3)用index作为key可能会引发的问题:
- 若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
- 如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题。
(4)开发中如何选择key?:
- 最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
- 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。
4、列表过滤
案例:模糊搜索
(1)用watch实现
//用watch实现 //#region /* new Vue({ el:'#root', data:{ keyWord:'', persons:[ {id:'001',name:'马冬梅',age:19,sex:'女'}, {id:'002',name:'周冬雨',age:20,sex:'女'}, {id:'003',name:'周杰伦',age:21,sex:'男'}, {id:'004',name:'温兆伦',age:22,sex:'男'} ], filPerons:[] }, watch:{ keyWord:{ immediate:true, handler(val){ this.filPerons = this.persons.filter((p)=>{ return p.name.indexOf(val) !== -1 }) } } } }) */ //#endregion
(2)用computed实现
//用computed实现 new Vue({ el:'#root', data:{ keyWord:'', persons:[ {id:'001',name:'马冬梅',age:19,sex:'女'}, {id:'002',name:'周冬雨',age:20,sex:'女'}, {id:'003',name:'周杰伦',age:21,sex:'男'}, {id:'004',name:'温兆伦',age:22,sex:'男'} ] }, computed:{ filPerons(){ return this.persons.filter((p)=>{ return p.name.indexOf(this.keyWord) !== -1 }) } } })
5、列表排序
利用computed属性,配置数组sort函数进行编码
new Vue({ el:'#root', data:{ keyWord:'', sortType:0, //0原顺序 1降序 2升序 persons:[ {id:'001',name:'马冬梅',age:30,sex:'女'}, {id:'002',name:'周冬雨',age:31,sex:'女'}, {id:'003',name:'周杰伦',age:18,sex:'男'}, {id:'004',name:'温兆伦',age:19,sex:'男'} ] }, computed:{ filPerons(){ const arr = this.persons.filter((p)=>{ return p.name.indexOf(this.keyWord) !== -1 }) //判断一下是否需要排序 if(this.sortType){ arr.sort((p1,p2)=>{ return this.sortType === 1 ? p2.age-p1.age : p1.age-p2.age }) } return arr } } })
九、Vue监测数据改变的原理
1、Vue检测对象的改变
模拟一个vue数据监测对象
let data = { name:'尚硅谷', address:'北京', } //创建一个监视的实例对象,用于监视data中属性的变化 const obs = new Observer(data) console.log(obs) //准备一个vm实例对象 let vm = {} vm._data = data = obs function Observer(obj){ //汇总对象中所有的属性形成一个数组 const keys = Object.keys(obj) //遍历 keys.forEach((k)=>{ Object.defineProperty(this,k,{ get(){ return obj[k] }, set(val){ console.log(`${k}被改了,我要去解析模板,生成虚拟DOM.....我要开始忙了`) obj[k] = val } }) }) }
2、Vue检测数组的改变
用操作数组的7个方法vue才能监测到数组变化
- 怎么监测到使用了方法?vue把数组的原型对象进行封装
3、Vue监视数据的原理
- (1)vue会监视data中所有层次的数据。
- (2)如何监测对象中的数据?——通过setter实现监视,且要在new Vue时就传入要监测的数据。
- vm.$set(target,propertyName/index,value)
- Vue.set(target,propertyName/index,value) 或
- ii.如需给后添加的属性做响应式,请使用如下API:
- i.对象中后追加的属性,Vue默认不做响应式处理
- (3)如何监测数组中的数据?——通过包裹数组更新元素的方法实现,本质就是做了两件事:
- i.调用原生对应的方法对数组进行更新。
- ii.重新解析模板,进而更新页面。
- (4)在Vue修改数组中的某个元素一定要用如下方法:
- i.使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
- ii.Vue.set() 或 vm.$set()
特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性!!!
十、 收集表单数据
1、收集表单数据:
- 若:<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属性:
- i.v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
- ii.v-model的初始值是数组,那么收集的的就是value组成的数组
2、备注:v-model的三个修饰符:
- lazy:失去焦点再收集数据
- number:输入字符串转为有效的数字
- trim:输入首尾空格过滤
3、可以将vue的_data中数据转换为JSON格式
methods: { demo(){ console.log(JSON.stringify(this._data)) } }
十一、过滤器
1、过滤器定义
对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。
2、语法:
(1)注册过滤器:Vue.filter(name,callback) 或 new Vue{filters:{}}
(2)使用过滤器:{{ xxx | 过滤器名}} (推荐)或 v-bind:属性 = "xxx | 过滤器名"(少)
3、备注:
(1)过滤器也可以接收额外参数、多个过滤器也可以串联
(2)并没有改变原本的数据, 是产生新的对应的数据4、实例
(1)html引入过滤器模板
<!-- 过滤器实现 --> <h3>现在是:{{time | timeFormater}}</h3> <!-- 过滤器实现(传参) --> <h3>现在是:{{time | timeFormater('YYYY_MM_DD') | mySlice}}</h3> <h3 :x="msg | mySlice">尚硅谷</h3>
(2)配置项名:filters
new Vue({ el:'#root', data:{ time:1621561377603, //时间戳 msg:'你好,尚硅谷' }, //局部过滤器 filters:{ timeFormater(value,str='YYYY年MM月DD日 HH:mm:ss'){ // console.log('@',value) return dayjs(value).format(str) } } })
5、全局过滤器
<div id="root2"> <h2>{{msg | mySlice}}</h2> </div> //全局过滤器 Vue.filter('mySlice',function(value){ return value.slice(0,4) }) new Vue...
十二、Vue内置指令
1、指令复习
v-bind : 单向绑定解析表达式, 可简写为 :xxx
v-model : 双向数据绑定
v-for : 遍历数组/对象/字符串
v-on : 绑定事件监听, 可简写为@
v-if : 条件渲染(动态控制节点是否存存在)
v-else : 条件渲染(动态控制节点是否存存在)
v-show : 条件渲染 (动态控制节点是否展示)
2、v-text指令
(1)作用:向其所在的节点中渲染文本内容。
(2)与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会。3、v-html指令
(1)作用:向指定节点中渲染包含html结构的内容。
(2)与插值语法的区别:
- v-html会替换掉节点中所有的内容,{{xx}}则不会。
- v-html可以识别html结构。
(3)严重注意:v-html有安全性问题!!!!
- 在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。
- 一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!
(4)cookie
有cookie就相当于获取了账户密码
不允许跨浏览器使用cookie
4、v-cloak指令
(1)本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。
(2)使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题。5、v-once指令
(1)v-once所在节点在初次动态渲染后,就视为静态内容了。
(2)以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能.<!-- 准备好一个容器--> <div id="root"> <h2 v-once>初始化的n值是:{{n}}</h2> <h2>当前的n值是:{{n}}</h2> <button @click="n++">点我n+1</button> </div> <script type="text/javascript"> Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。 new Vue({ el:'#root', data:{ n:1 } }) </script>
6、v-pre指令
(1)跳过其所在节点的编译过程。
(2)可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。
十三、自定义指令
1、定义语法
(1)局部指令:
new Vue({ directives{指令名:回调函数} })
new Vue({ directives:{指令名:配置对象} })
(2)全局指令:
Vue.directive(指令名,配置对象) 或 Vue.directive(指令名,回调函数)2、配置对象中常用的3个回调
(1)bind:指令与元素成功绑定时调用。
(2)inserted:指令所在元素被插入页面时调用。
(3)update:指令所在模板结构被重新解析时调用。3、备注
(1)指令定义时不加v-,但使用时要加v-;
(2)指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。
十四、vue生命周期
1、vue生命周期
(1)又名:生命周期回调函数、生命周期函数、生命周期钩子。
(2)是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数。(3)生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。
(4)生命周期函数中的this指向是vm 或 组件实例对象。
2、 生命周期原理图
8个周期(4对函数)
3个场景:挂载流程→更新流程→销毁流程
4、总结
(1)常用的生命周期钩子:
- mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。
- beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。
(2)关于销毁Vue实例
- 销毁后借助Vue开发者工具看不到任何信息。
- 销毁后自定义事件会失效,但原生DOM事件依然有效。
- 一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了。