1、Vue的特点
<script>
// 1、Vue的特点
// 1.1、采用组件化模式,提高代码的复用率、而且DOM、CSS、JS都是分离的,代码更好维护
// 1.2、声明式编码,让编码人员无需直接操作DOM,提高开发效率,数据控制DOM,不用像之前写一堆模板字符串
// 1.3、使用虚拟DOM+优秀的Diff算法,尽量减少复用DOM节点(这个很关键)
</script>
2、指令和修饰符
<script>
// 常用的指令
// 1、v-bind:动态绑定数据
// 简写: <div :name="name"></div>
// 2、v-model:数据的双向绑定
// 注意:v-model只能应用在表单类元素,其他元素用会报错比如H标签用不了v-model
// 还有一个需要注意的是,v-model自动绑定到表单元素的value属性上
// 简写:<input v-model="searchValue"></input>
// 3、v-on:事件处理
// 简写: <div :name="name" @click="sayHello"></div>
// 可以传事件本身,但是需要用$占位符,$event放前放后都是一样的
// <div :name="name" @click="sayHello($event, '你好')"></div>
// 4、v-once: 只会执行一次渲染,当数据发生改变时,不会再变化
// <p v-once>{{ 绑定的字段 }}</p>
// 下面看下区别:什么叫只渲染一次(初始渲染)
// <div id="root">
// <h2>初始值是:{{n}}</h2> // 你会发现这个n永远是初始值
// <h2>当前值是:{{n}}</h2> // 而这里的值会随着点击button不断增加
// <button @click="n++">点我n+1</button>
// </div>
// 总结:v-once所在节点在初次动态渲染后就视为静态内容了,以后数据的改变不会引起更新,优化性能用的
// 5、v-show: 你懂的
// 以下这三个必须紧紧挨着使用
// 6、v-if:
// 7、v-else:
// 8、v-else-if:
// <div>
// <span v-if="score >= 90">优秀</span>
// <span v-else-if="score >= 80">良好</span>
// <span v-else-if="score >= 60">及格</span>
// <span v-else>不及格</span>
// </div>
// 9、v-for: 你懂的
// 10、v-html:
// 11、v-text: 向所在标签插入文本内容,覆盖所有的内容
// v-text是渲染字符串,会覆盖原先的字符串;v-html是渲染为html。{{}}双大括号和v-text都是输出为文本
// 那如果想输出为html。使用v-html,如下例子
// const innerHtml = '<button>一个按钮</button>';
// <div v-text="innerHtml"></div> //文本
// <div v-html="innerHtml"></div> // 会生成一个按钮
// 12、v-cloak: 在加载很慢的时候,vue.js文件没有加载完成时,在页面上上会出现 ‘{{message}}’的字样,
// 等到vue创建实例、编译模板时,DOM就会被替换掉,在这整个过程中屏幕上会出现闪动一下。
// <div id="app" v-cloak>{{message}}</div>,并且需要搭配CSS使用
// [v-cloak]{
// display:none;
// }
// 在Vue解析模版前,模版是不显示的display: none嘛,当模版解析完成后,Vue自己会把v-cloak指令清除
// v-cloak指令可以解决初始化慢而导致页面闪动的一个方案,如果是在有工程化的项目里面,项目的HTML结构中就
// 只有一个空的DIV元素,其他的都是让路由去挂载不同组件来完成的,就不需要v-cloak指令的了
// 13、v-pre: 加了这个v-pre就是程序员写什么样页面显示就是什么样,它不会再进行解析,不解析插值语法
// 所以不要在,有插值语句,或者点击事件或者添加了标签属性的元素上加pre等等
// 要在像第一行只有文本文字的情况下去加
// <h2 v-pre>Vue其实很简单</h2> // 显示:Vue其实很简单
// <h2 v-pre>{{n}}}</h2> // 显示:{{n}}}
// 常用的修饰符
// 先看事件修饰符
// 1、@click.prevent:阻止默认事件,比如阻止a标签的跳转
// 2、@click.stop:阻止事件冒泡
// 3、@click.once:事件只触发一次(常用),以后再点击就不行了
// 4、@click.capture:阻止事件捕获
// 5、@click.self:只有event.target是当前操作的元素时才触发事件
// 这个要有意思很多,event.target就是触发事件的元素(标签),在事件冒泡和事件捕获的过程中,event.target是不会变的
// 所以.self其实也是一种阻止事件冒泡和捕获的方式,因为事件触发前会判别,当前这个标签是不是event.target
// 不过这个用的不多,因为他是用在你不想触发事件的标签的,比如下面的就会放在div上,而不是放在button上,当我们点击button时
// 就变相的不会事件冒泡了
// <div @click.self="sayHello">
// <button @click="sayHello">我是button</button>
// </div>
// 6、@click.passive:事件的默认行为立即执行,无需等待事件回调执行完毕
// 这里加一个解释:@scroll:滚动条滚动事件 和 @wheel:滚轮滚动事件,当我们滚到底部的时候,发现滚动条
// 不动了,而滚轮还是可以动的,这个时候滚轮事件继续触发,而滚动条事件不会再触发了,这就是区别
// 好了接下来解释这个修饰符是干嘛的:给一个div加一个滚动事件(@scroll="sayHello"),我们滚滚轮的是时候
// 滚动条是一起在动的,为什么呢,因为滚动条的滚动是滚轮滚动的默认事件,如果我们给sayHello写一个回调执行体
// 是100万次的循环,滚动条滚动会等循环执行完才会滚动
// 但是如果我们开启了passive,滚动条不会等执行体执行完,而是立即执行默认事件,也就是滚动条滚动
// 当然如果你直接绑定@scroll滚动条滚动事件,就没这个情况了
// 7、@click.native:我们知道在自定义组件上,只能监听自定义事件,一些原生事件(比如click)是没有办法直接触发的,
// 但是使用.native修饰符可以帮我们办到这点,比如自定义组件没有click事件,加了native就好了
// <NativeCustom @click.native="onClick" />
// 键盘按键修饰符
// 7、enter
// 用法:@keyup.enter="sayHello";当光标在表单元素内,我们按下回车键,回车键上升的时候触发,下面的全都一样
// 8、delete
// 9、esc
// 10、space
// 11、tab:这个按键比较特殊,因为它本身有一个功能就是把光标焦点切换走,不等tab键抬起来,光标已经切走了
// 所以tab键不适合keyup,只能用keydown使用
// 12、up
// 13、down
// 鼠标修饰符
// 14、left:鼠标左键
// 15、right:鼠标右键
// 15、middle:鼠标中键
// 系统修饰符
// 16、这几个也特殊:ctrl、alt、shift、win;他们是系统修饰键,在配合keyup使用时
// 要按下修饰键同时按下其他键,随后释放其他键,事件才会被触发;在配合keydown使用时
// 正常触发事件不用其他键
// 如果只想实现ctrl + y触发事件这样写
// <input type="text" @keyup.crtl.y="sayHello">
// v-bind的修饰符:
// 17、.sync <hello :data.sync="data":></hello>
// 当我们想要在父组件和子组件之间对某个属性值进行双向绑定时,有什么便捷的方式?
// 是的只要.sync修饰符即可办到;
// 18、.camel <svg :view-box.camel="viewBox"></svg>
// .camel修饰符允许在使用 DOM 模板时将 v-bind property 名称驼峰化
// 19、.prop :帮助我们用来处理自定义属性的
// <div :my-name="myName"></div> 最终变成了 <div my-name="myName的实际值"></div>
// <div :my-name.prop="myName"></div> 最终变成了<div></div>,不会把自定义属性显示出来,这里用getAttribute也访问不到my-name属性
// 关于.prop修饰符官网只有这句话 .prop 作为一个 DOM property 绑定而不是作为 attribute 绑定
// 通过自定义属性存储变量,避免暴露数据;防止污染 HTML 结构
// 表单修饰符
// 20、.trim :对于输入的内容,希望可以过滤首尾空格应该怎么做呢
// <input type="text" v-model.trim="name">
// 21、.lazy v-model大家都很熟悉,默认情况下,每次input事件触发的时候都会将输入框的值与其绑定
// 的数据进行实时同步。但是如果想要实现光标离开的时候再更新数据如何实现呢
// <input type="text" v-model.lazy="text2">
// 22、.number: 我们知道input输入框的type哪怕是number得到的值的类型也是string,如果我们想直接
// 拿到number类型的数据,又不想麻烦的手动转换应该怎么办呢
// 需要注意的是.number经常都是和表单元素type=number一起使用的
// <input type="number" v-model.number="number2">
// 还有需要注意的是,这里type=number的作用是你输入不了字符串(字母和其他符号),只能输入数字
// 注意!!!!!!!!!!!!
// 总结一下:修饰符可以连着写
// @click.stop.prevent
</script>
3、数据代理:Object.defineProperty
<script>
// 1、数据代理:Object.defineProperty
// 这个方法就是给对象添加(定义)属性用的
const person = {
name: 'jjwu',
}
//person.age = 18; 这种写法,age属性可以枚举、修改、删除(delete)
// 很关键的一个地方Object.defineProperty添加的属性是可以被实时监听到的
// 而直接添加的属性不行,比如
let number = 10;
person.ha = number;
number = 20;
console.log(person);// 你发现ha没有变成20,但是用Object.defineProperty添加的属性就可以
Object.defineProperty(person, 'age', {
/* value: number,
enumerable: true, //开启之后可以枚举,默认是false
writable: true, // 控制属性值是否可以被修改,默认是false
configurable: true, // 控制属性是否可以被删除,默认是false */
get: function() {
return number;
},
set: function(val) {
number = val;
}
});
number = 30;
console.log(person);
// 这种添加属性的方法,在浏览器开发者工具里看,属性是不可枚举的(颜色比name属性要淡一点)
// 颜色淡一点就是不可枚举,说白了就是不可遍历,比如for in 遍历不到
// get函数也叫getter属性,它将添加的属性变成了动态的,Vue能监听到的
// 我们修改了number之后再访问age属性,你发现值也变了,因为它是现用现取,靠get取
// 这样的话我们每次读取age的时候,返回的都是最新的正确的值了
// 并且当我们去改age的值的时候,会同时把number也改掉
// 总结:!!!!!!!!!!!!
// 你一定要有一个体会,直接添加的属性,number和person没有产生任何关联
// 但是用Object.defineProperty就能让number和person产生关联
</script>
<script>
// Object.defineProperty在vue中的体现比如一个Vue实例是这样的
const vm = new Vue({
el: '.p',
data: function() {
return {
name: 'jjwu',
age: 18
}
}
})
// 你注意看data中的name和age属性
// 我们在创建vm实例的时候会给vm传配置项,而vm会将配置项里的data里所有东西放在自己的_data属性上,
// 之后vm再给自己添加所有_data里的属性,这里就是name和data这两个,
// 而这些添加的属性(从_data到vm自身)都有getter和setter属性,都是可以被监听的,是实时的,动态的,现取现用
// 取的就是实例对象里_data中的name和age值,修改的时候会修改_data里的name和age值
// 总结一下,所谓的Vue的数据代理,代理的就是data中的数据,这个data对象在创建实例的时候会把他放在vm的一个属性上_data
// 所有代理的数据就在他自己本身的_data属性上,而一旦_data中的数据变化了,页面就会自动更新
// 为什么_data中的数据发生变化,页面会自动更新呢,这个你就要知道什么是数据劫持了
// 数据代理加上数据劫持就是我们Vue的响应式操作了
</script>
4、计算属性
<body>
<div>{{fullName}}</div>
<div>{{fullName}}</div>
<div>{{fullName}}</div>
<div>{{fullName}}</div>
<div>{{fullName()}}</div>
<div>{{fullName()}}</div>
<div>{{fullName()}}</div>
<div>{{fullName()}}</div>
<script>
// 计算属性
new Vue({
el: '#root',
data: {
firstName: 'jjwu',
lastName: '27'
},
methods: {
fullName() {
console.log('我是fullName');
return this.firstName + '-' + this.lastName;
}
},
computed: {
fullName: {
get() {
console.log('我是fullName')
return this.firstName + '-' + this.lastName;
},
set(val) {
// 根据实际情况处理,不用太想理解
// 很多情况下不需要写set
}
},
// 只考虑读取,不考虑修改,当不用set的时候,计算属性是可以简写的
fullName2() {
return this.firstName + '-' + this.lastName;
}
}
})
// 我的理解计算属性本质上就是typescript的get和set获取值
// 但是除此之外呢,计算属性还有缓存,我们的四个div调用了四次fullName,但是
// console.log,只会打印一次说明只访问了一次
// 所以get只有初始访问以及所依赖的数据(firstName和lastName)发生改变的时候才会被调用
// methods没有缓存而是调用四次
</script>
</body>
5、监视属性
<body>
<script>
// 计算属性
const vm = new Vue({
el: '#root',
data: {
isHot: true,
isCold: false,
numbers: {
a: 1,
b: 2
}
},
watch: {
// 正常写法
isHot: {
immediate: true, //初始化时调用一下handler
handler(newValue, oldValue) {
console.log('我监听到你啦!');
}
},
// 监视多级结构中的单个属性
'numbers.a': {
handler(newValue, oldValue) {
console.log('我监听到你啦!');
}
},
// 深度监视
// 监视多级结构中所有属性的变化
numbers: {
deep: true, // 加了他只要a或者b发生了改变那么numbers就能被监听到,否则a、b改变是监听不到
handler() {
console.log('numbers改变了')
}
},
// 当你不写任何配置项的时候可以简写
numbers2(newValue, oldValue) {
console.log('numbers2改变了')
}
}
})
// 监视的另一种写法
vm.$watch('isCold', {
immediate: true,
handler(newValue, oldValue) {
console.log('我监听到你啦!');
}
})
// 简写,注意回调函数不能写成箭头函数
vm.$watch('isCold', function(newValue, oldValue){
console.log('我监听到你啦!');
})
// 注意: 计算属性也是可以监视的
</script>
</body>
6、补充一个this指向的问题
<body>
<script>
// 计时器中this指向问题
const obj = {
sayHello() {
// 回调写成箭头函数
setTimeout(() => {
console.log(this); //obj
})
},
sayHi() {
// 回调写成普通函数
setTimeout(function() {
console.log(this); // window
})
}
}
obj.sayHello();
obj.sayHi();
// 自我理解:箭头函数没有this,只能向上一层级去找,找到了obj
// 而普通函数的this就是谁调用指向谁,setTimeout是谁调用的,是window嘛
// 另一种解释:定时器是在obj的两个方法中开启的,但是定时器的回调不受obj控制
// 它是受浏览器定时器管理模块控制的,定时器到点了,也是JS引擎帮你调的
// 所以JS引擎帮你调用的时候this已经给你指向window了,所以一般我们用计时器都用箭头函数
// 20240816 补充gpt的回答
// 箭头函数不会创建自己的 this,它会继承来自包含它的上下文的 this。在你的代码中,箭头函数的包含上下文是 sayHello 方法,而 sayHello 方法中的 this 是 obj,因此箭头函数内部的 this 就是 obj。
// 普通函数在调用时的 this 值是由调用位置决定的。在 setTimeout 中执行的函数(即普通函数)默认会在全局上下文中执行,因此它的 this 值是 window(在浏览器环境中)或 global(在 Node.js 环境中)。
</script>
</body>