目录
1. vue中的数据绑定
vue中有两种数据绑定的方式:
- 单向数据绑定(v-bind):数据只能从data流向页面
- 双向数据绑定(v-model):数据不仅能从data流向页面,也能从页面流向data
<body>
<div id="root">
单向数据绑定:<input type="text" :value="name"><br/>
双向数据绑定:<input type="text" v-model="name"><br/>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el:'#root',
data:{
name:'尚硅谷'
}
})
</script>
在上面文本框中输入值,不会影响下面文本框的值,而在下面文本框中输入值,上面文本框的值也会跟着一起改变,即为双向数据绑定,页面上的name改变了,data中的name也随之改变。
2. MVVM模型
- M:模型(Model),data中的数据
- V:视图(View),模板代码
- VM:视图模型(ViewModel),Vue实例
- data中的所有属性,都会在vue实例(即vm)身上,vm身上的所有属性及Vue原型上的所有属性,在Vue模板中都可以直接使用。
3. 数据代理
- Object.defineProperty()方式
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 } })
用Object.defineProperty()方法定义的属性,默认是不可枚举、不可修改、不可删除的。
- 何为数据代理
<!-- 数据代理:通过一个对象代理对另一个对象中属性的操作(读/写)--> <script type="text/javascript" > let obj = {x:100} let obj2 = {y:200} Object.defineProperty(obj2,'x',{ get(){ return obj.x }, set(value){ obj.x = value } }) </script>
- Vue中的数据代理
(1) vm.name,实际读取的是data中的name,set vm.name时,data.name也会修改,即通过vm对象代理对data对象中属性的操作,即为数据代理; (2) vm.name的来源:首先创建vm的时候,就会把配置中data中的数据放一份在vm里面,名为_data。然后再把_data中的数据拿一份到vm中。(3) vm._data里面不仅有data配置里面的属性,还有相应属性的get和set方法,目的是为了vue能够实现响应式操作,使得vue能够监测到数据的改变,从而更新页面。
4. 事件处理methods
-
事件的基本使用
<body>
<!--
事件的基本使用:
1.使用v-on:xxx 或 @xxx 绑定事件,其中xxx是事件名;
2.事件的回调需要配置在methods对象中,最终会在vm上;
3.methods中配置的函数,不要用箭头函数!否则this就不是vm了;
4.methods中配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例对象;
5.@click="demo" 和 @click="demo($event)" 效果一致,但后者可以传参;
-->
<div id="root">
<button @click="showInfo1">点我提示信息1(不传参)</button>
<button @click="showInfo2($event,66)">点我提示信息2(传参)</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
name:'尚硅谷',
},
methods:{
showInfo1(event){
// console.log(event.target) --> <button>点我提示信息1(不传参)</button>
// console.log(this) //此处的this是vm
},
showInfo2(event,number){
console.log(event,number)
}
}
})
</script>
- 事件修饰符
1. prevent:阻止默认事件(常用); 2. stop:阻止事件冒泡(常用);3. once:事件只触发一次(常用); 4. capture:使用事件的捕获模式; 5. self:只有event.target是当前操作的元素时才会触发;6. passive:事件的默认行为立即执行,无需等待事件回调执行完毕。
- 键盘事件
5. 计算属性Computed
- 计算属性的原理:底层借助Object.defineProperty()的getter和setter方法来实现;get函数的执行时机:1. 初次读取的时候会执行一次;2. 当依赖的数据改变时会再次执行。
- 计算属性和methods方法的区别:计算属性内部有缓存机制,效率更高,调试方便。
- 计算属性最终会出现在vm上,可以直接读取;如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变(即要想页面动态响应计算属性的改变,需要有set方法)
<body>
<div id="root">
全名:<span>{{fullName}}</span>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三'
},
computed:{
//完整写法
fullName:{
//get有什么作用?当有人读取fullName时,get就会被调用,且返回值就作为fullName的值
get(){
console.log('get被调用了')
// console.log(this) //此处的this是vm
return this.firstName + '-' + this.lastName
},
//set什么时候调用? 当fullName被修改时。
set(value){
const arr = value.split('-')
this.firstName = arr[0]
this.lastName = arr[1]
}
}
//简写
//fullName(){
// return this.firstName + '-' + this.lastName
//}
}
})
</script>
- 计算属性的简写只适用于不会修改计算属性值的时候,即不调用set方法的时候。
6. 监视属性watch
- 当被监视的属性变化时,回调函数会自动调用,进行相关操作。且只有当监视的属性存在的时候,才能进行监视。
- 监视的两种写法:1. new Vue时传入watch配置;2. 通过vm.$watch监视
//写法1 /* watch:{ isHot:{ immediate:true, //初始化时让handler调用一下 //handler什么时候调用?当isHot发生改变时。 handler(newValue,oldValue){ console.log('isHot被修改了',newValue,oldValue) } } //简写 /* isHot(newValue,oldValue){ console.log('isHot被修改了',newValue,oldValue,this) } */ } */ //写法2 vm.$watch('isHot',{ immediate:true, //初始化时让handler调用一下 //handler什么时候调用?当isHot发生改变时。 handler(newValue,oldValue){ console.log('isHot被修改了',newValue,oldValue) } }) //简写 /* vm.$watch('isHot',(newValue,oldValue)=>{ console.log('isHot被修改了',newValue,oldValue,this) }) */
- computed和watch之间的区别:computed能完成的功能,watch都能完成;而watch能完成的功能,computed不一定能够完成,例如:watch可以进行异步操作
watch:{ firstName(val){ setTimeout(()=>{ console.log(this) this.fullName = val + '-' + this.lastName },1000); } }
- 注意点: 被Vue管理的函数,比如methods方法等最好写成普通函数,所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数、Promise的回调函数等)最好写成箭头函数,这样this的指向才是vm或组件实例对象
7. 条件渲染v-if和v-show
- v-if写法:v-if=“表达式”,v-else-if=“表达式”,v-else=“表达式” ;v-show写法:v-show=“表达式”
- v-if和v-show的区别: 使用v-if时,不展示的DOM元素直接被移除,适用于切换频率较低的场景;而使用v-show时。不展示的DOM元素未被移除,仅仅是使用样式隐藏掉,适用于切换频率较高的场景。因此,使用v-if时,元素可能无法获取到,而使用v-show则一定可以获取到。
8. 列表渲染
- 基本使用:v-for="(item, index) in arr"
<ul> <li v-for="(p,index) of persons" :key="index"> {{p.name}}-{{p.age}} </li> </ul>
- key的内部原理(有相同父元素的子元素必须有独特的key)
- 虚拟DOM中key的作用:key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据新数据生成新的虚拟DOM,随后Vue进行新虚拟DOM和旧虚拟DOM的差异比较
- 对比规则:(1)旧虚拟DOM中找到了与新虚拟DOM相同的key:若虚拟DOM中内容没有变化,则直接使用之前的真实DOM;若虚拟DOM中的内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM。(2)旧虚拟DOM中未找到和新虚拟DOM中相同的key:则创建新的真实DOM,随后渲染到页面。
- 用index作为key可能引发的问题:(1)若对数据进行逆序添加、逆序删除等破坏顺序操作,则会产生没必要的真实DOM更新,界面效果没问题,但效率低。(2)若结构中还包含输入类的DOM,则会产生错误的DOM更新,界面也会有问题。
-
开发中如何选择key:(1)最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。(2)如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。
- Vue监测数据改变的原理
- vue会监视data中所有层次的数据。
- 如何监测对象中的数据变化:通过setter实现监视,且要在new Vue时就传入要监测的数据,调用set方法之后,就会重新解析模板,随后生成新的虚拟DOM。对象后追加的属性,Vue默认不做响应式处理,如果需要给后添加的属性做响应式,可以使用:Vue.set(target, propertyName, value)或者vm.$set(target, propertyName, value)
- 如何监测数组中的数据变化:通过包裹数组更新元素的方法实现,本质是:(1)调用原生对应的方法对数组进行更新;(2)重新解析模板,进而更新页面。在vue中修改数组中的某个元素一定要使用如下方法:(1)API:push(),pop(),shift(),unshift(),splice(),sort(),reverse();(2)Vue.set()或vm.$set()
- 注:Vue.set()或vm.$set()不能给vm或vm的根数据对象添加属性。
<script type="text/javascript"> Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。 const vm = new Vue({ el:'#root', data:{ student:{ name:'tom', age:18, hobby:['抽烟','喝酒','烫头'], friends:[ {name:'jerry',age:35}, {name:'tony',age:36} ] } }, methods: { addSex(){ // Vue.set(this.student,'sex','男') this.$set(this.student,'sex','男') }, } }) </script>
9. 收集表单数据
- <input type="text "/>,则v-model收集的是value值,用户输入的就是value值;
- <input type="radio"/>,则v-model收集的是value值,且要配置value值;
- <input type="checkbox"/>
- 没有配置value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)
- 配置了value属性:(1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值);(2)v-model的初始值是数组,那么收集的的就是value组成的数组。
- v-model的修饰符:
- lazy:失去焦点再收集数据
- number:输入字符串转为有效的数字
- trim:输入首尾空格过滤
账号:<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">
10. 过滤器
- 定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。
- 语法:注册过滤器:Vue.filter(name,callback) 或 new Vue{filters:{}};使用过滤器:{{ xxx | 过滤器名}} 或 v-bind:属性 = "xxx | 过滤器名"
- 备注:过滤器也可以接收额外参数、多个过滤器也可以串联;过滤器并没有改变原本的数据, 是产生新的对应的数据。
<body> <div id="root"> <!-- 过滤器实现 --> <h3>现在是:{{time | timeFormater}}</h3> <!-- 过滤器实现(传参) --> <h3>现在是:{{time | timeFormater('YYYY_MM_DD') | mySlice}}</h3> <h3 :x="msg | mySlice">尚硅谷</h3> </div> <div id="root2"> <h2>{{msg | mySlice}}</h2> </div> </body> <script type="text/javascript"> Vue.config.productionTip = false //全局过滤器 Vue.filter('mySlice',function(value){ return value.slice(0,4) }) new Vue({ el:'#root', data:{ time:1621561377603, //时间戳 msg:'你好,尚硅谷' }, //局部过滤器 filters:{ timeFormater(value,str='YYYY年MM月DD日 HH:mm:ss'){ return dayjs(value).format(str) } } }) new Vue({ el:'#root2', data:{ msg:'hello,atguigu!' } }) </script>
11. 内置指令
- v-text:向其所在的节点中渲染文本内容,与插值语法的区别:v-text会替换掉节点中的内容,而插值语法不会。语法:v-text="xxx"。
- v-html:向指定节点中渲染包含html结构的内容,与插值语法的区别:(1).v-html会替换掉节点中所有的内容,{{xx}}则不会;(2).v-html可以识别html结构。v-html有安全性问题,在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击,一定要在可信的内容上使用v-html,不要用在用户提交的内容上。
- v-cloak:没有值,是一个属性,在Vue实例创建完毕并接管容器后,就会删掉v-cloak属性,使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题。
<html> <head> <meta charset="UTF-8" /> <title>v-cloak指令</title> <style> [v-cloak]{ display:none; } </style> </head> <body> <div id="root"> <h2 v-cloak>{{name}}</h2> </div> <script type="text/javascript" src="http://localhost:8080/resource/5s/vue.js"></script> </body> <script type="text/javascript"> Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。 new Vue({ el:'#root', data:{ name:'尚硅谷' } }) </script> </html>
- v-once:v-once所在节点在初次动态渲染后,即被视为静态内容,以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。
- v-pre:可跳过其所在节点的编译过程。可利用该指令跳过没有使用指令语法、没有使用插值语法的节点,会加快编译。
12. 自定义指令
- 语法一:回调函数式 new Vue({directives:{指令名:回调函数}})(局部指令);Vue.directive(指令名:回调函数)(全局指令)。调用时机:1. 指令与元素成功绑定时(一上来);2. 指令所在的模板被重新解析时。
<h2>放大10倍后的n值是:<span v-big="n"></span> </h2> new Vue({ el:'#root', data:{ name:'尚硅谷', n:1 }, directives:{ big(element,binding){ console.log('big',this) //注意此处的this是window element.innerText = binding.value * 10 }, } })
- 语法二: 对象式 new Vue({directives:{指令名:配置对象}})(局部指令);Vue.directive(指令名:配置对象)(全局指令)
<input type="text" v-fbind:value="n"> new Vue({ el:'#root', data:{ name:'尚硅谷', n:1 }, directives:{ fbind:{ //指令与元素成功绑定时(一上来) bind(element,binding){ element.value = binding.value }, //指令所在元素被插入页面时 inserted(element,binding){ element.focus() }, //指令所在的模板被重新解析时 update(element,binding){ element.value = binding.value } } } }) //定义全局指令 /* Vue.directive('fbind',{ bind(element,binding){ element.value = binding.value }, inserted(element,binding){ element.focus() }, update(element,binding){ element.value = binding.value } }) */
- 备注: 1. 指令定义时不加v-,但使用时要加v-;2. 指令名如果是多个单词,要使用kebab-case命名方式,不要要camelCase命名。