笔记的后面部分转用notion来记录了,时间关系写得都比较简易,就不发在这里了。
vid4-6 hello world案例
<body>
<!-- 容器里的代码被称为vue模版 -->
<!-- 流程 -->
<!-- Vue实例开始工作时, 去找el中对应的容器,然后开始进行解析,扫描模版内是否有vue的特殊语法-->
<!-- 例如发现要用name,会用name中的内容替换{{name}}, 然后生成一个全新div,再把解析完的div重新放到容器里-->
<!-- 所有容器的作用有两个: (1)为vue提供模版,(2)把vue的工作成果提供可放置的容器 -->
<div id="root">
<!-- 插值语法 , 与解构对象什么的无关 -->
<h1>hello, {{name}}</h1>
</div>
</body>
<script>
Vue.config.productionTip = false
// const vm定义不需要。直接去掉即可
const vm = new Vue({ // 构造函数参数只有一个,也就是配置对象
el: '#root', // 用于指定当前Vue实例为哪个容器服务。这里用document.getElement.byId()也ok
data: { // 存储供指定容器使用的data
name: 'Alex'
}
})
</script>
vid7/8 模板语法 数据绑定
两大类:
- 插值语法(用于标签体内)
- 指令语法(用于标签属性) 例如: v-bind(可以简写为:)、v-model(双向数据绑定)
vid9 el与data的两种写法
const v = new Vue({
// el: "#root",
// data: {
// name: 'Alex'
// }
data() { // 组件data必须用函数式
console.log(this) // Vue实例对象
return {
name: 'Alex'
}
}
})
v.$mount("#root")
data最终会出现于Vue实例对象上。Vue实例对象及其原型上的属性都可以在模版中获取。
10 MVVM模型
M: 对应data中的数据 V: view VM: view modal, Vue实例对象
VM可以理解为是V和M之间的桥梁
11 数据代理
Object.defineProperty()
用该方法定义属性时与字面量定义的主要区别是:
(1)前者定义的属性默认情况下是无法被枚举的,在console中输出可以看到,hobby属性和其他属性颜色是不同的。
// 以下结果中均没有hobby属性
console.log(Object.keys(myFriend))
for (key in myFriend) {
console.log(myFriend[key])
}
如果要想被枚举,可以在配置对象中加上enumerable: true
Object.defineProperty(myFriend, 'hobby', {
value: 'archery',
enumerable: true
})
(2)前者默认情况下无法通过xx.xx的方法去修改属性,可以配置writable:true来允许修改
(3) 前者默认情况下无法通过delete xx.xx方法删除,可配置configurale:true
(4)
let age = 11
let p = {
name: 'alex',
age: age
}
age = 13
console.log(p.age) //11 ,因为p的定义语句只会执行一次,所以只会取到一开始11的值。
而如果使用Object.defineProperty()的话,我们可以用get来设定每次读取属性值时都会值进行更新
还可以去set来进行p.age = xx的修改绑定
let num = 15
let p = {
name: 'alex'
}
Object.defineProperty(p, 'age', {
get: function() {
return num
},
set(value) {
num = 13
console.log(p)
}
})
console.log(p)
用defineProperty实现简单的数据代理
数据代理定义: 通过一个对象代理对另一个对象中属性的操作(读写)
let p1 = {
name: 'alex',
age: 5
}
let p2 = {
name: 'louie'
}
Object.defineProperty(p2, 'age', {
get() {
return p1.age
},
set(value) {
p1.age = value
}
})
vue中的数据代理
在下面的例子中,打印出vm,它的两个data是...状态,鼠标放上去会出现invoke property getter,所以它本质上调用的就是 Object.defineProperty中的get。
如果再往下看,会发现有这两个数据的getter和setter
所以vm和我们写的data构成了一个数据代理。当我们获取vm中的hobby时,其实调用了getter来获取data中的hobby。它的基本原理就是:通过Object.defineProperty把data中的属性都添加到vm上,并且都指定getter、setter。
如果我们让vm.hobby ='xx', 会发现页面上的hobby已变化。
data实际上是存储在vm.__data中的,两个是全等的。那为什么不直接到__data中获取数据,而是用进行数据代理呢?
这是因为我们插值表达式中要写vm的属性名,如果从___data中获取数值,我们需要写{{__data.name}}。所以数据代理的好处就是操作data数据更方便。
事件修饰符
- prevent
- stop
- once: 事件只触发一次
- capture: 在捕获模式处理事件
- self:只有event.target是当前操作的元素时才触发事件。 例如:
<div class="container" @click.self="containerSayHello">
<button @click="buttonSayHello">click me</button>
</div>
在上面情况下,只有点击的是container(不含button),才会触发containerSayHello事件。self实际上也能起到阻止冒泡的作用。
- passive:事件的默认行为立即执行,无需等待cb执行完毕(用很少,这里暂时先跳过)。
12. v-for
- vue内部会用key来标识每个v-for生成的内容,在浏览器中标签上是看不到key这个属性置的。
- 当不写key时,如果每个item内部还有输入框的话,在最上方新增一个item会出现错位的情况。但这时即使用index,也还是会错位,用唯一标识(例如id)则不会出现这个问题。
key 的工作原理
- vue根据初始数据生成虚拟dom,这些虚拟dom是在内存中的,页面上什么也没有。
-
把虚拟dom转化为真实dom,然后渲染到页面上。当用户在input中输入数据时,数据是残留在真实dom中。
-
此时数组第一项新增数据后,vue根据新数据生成一个新的虚拟dom。此时第一项的key是0。
-
此时vue要用到虚拟dom的对比算法。
在对比时,它会根据key去对比,新增的第一项key是0,所以它会与左边第一项key是0 的项进行对比。 它会先去分析:第一项有两个节点,一个是文本节点,一个是input标签节点。一一对比之后,发现只有第一个文本节点有不同
- 此时vue开始将第一项转化真实dom,但因为第一项中只有第一个文本节点有不同,input没有不同,所以input会被重复利用。但之前用户输入的文字还残留在里面。
对比第二项时,会在左右两边去找id为1的项目,两边input没变,但是文本节点已经不同了,所以文本节点部分也会被重新生成为真实dom,而input则重复利用...一直到最后一项,因为左边已经没有id为3的项目,所以只能直接生成真实dom。
可以看出,在这个过程中,除了错位外,之前已经生成过的一些文本节点也不会被再次利用,所以效率比较低。
总结:
- key是虚拟dom对象的标识,当数据发生变化的时,会根据新数据生成新虚拟dom,而会根据key来将新旧讯息dom进行比较。
比较过程中如果找到了key相同的项目,则会更新其中不同的部分,如果没有找到,则直接生成新的真实dom。
-
用index作为key可能会引发的问题: 对数据进行破坏顺序的操作时,有可能出现错位等问题,而且效率比较低。
-
开发中最好用唯一标识作为key,但如果不存在破坏顺序的操作,仅仅用于展示,则用index作为key也可以。