【Vue五分钟】 五分钟了解Vue绑定、虚拟DOM、侦听器和声明周期

简介: 在我们的vue里面,双向绑定和单向绑定是不冲突

1.双向绑定和单向绑定

在我们的vue里面,双向绑定和单向绑定是不冲突;

2345_image_file_copy_388.jpg

2345_image_file_copy_389.jpg

2345_image_file_copy_390.jpg

Vue 是 如 何 是 进 行 双 向 绑 定 的 ? 其 实 并 不 是 通 过 defineProperty 这个 API;如下带代码同样的内容,第一种就是所谓的双向绑定,第二种是手写实现的双向绑定也是 v-model 最终编译出来的样子,所以 v-model 仅仅是一个语法糖而已。

<template>
<div>
  <PersonalInfo v-model=""phoneInfo" :zip-code.sync="zipCode"/>
<PersonalInfo
  : phone-info="phoneInfo"
  :zip-code=""zipCode"
  @change="val => (phoneInfo = val)"
  @update:zipCode="val =>(zipCode = val)"/>
phaneInfo: {{phoneInfo }}
<br/>
zipcode: {{ zipCode }}
</div>
</template>
<script>
import PersonalInfo from "./PersonalInfo":
export default {
  components: {
   PersonalInfo
},
 data() {
   return {
   phoneInfo:{
 areaCode: "+86”",phone:""
,
zipCode: ""
};
}};
</script>

当然有时候组件可能需要多个属性的双向绑定则可以通过.sync 对其它属性做双向绑定,编译后就是@update 去改变值即可。 personalInfo 组件内部;

<script>
  export default {
   name:"PersonalInfo",
  model: {
   prop: "phoncInfo",//默认 value
   event:"change"//默认input
},
props: {
phoneInfo: 0bject,
zipcode: String
},
methods:{
handleAreaCodeChange(e) {
  this.$emit("change", {
  ...this.phoneInfo,
 areaCode: e.target.value
}};
},
handlePhoneChange(e) {
  this.$emit("change",{
  ...this.phoneInfo,
  phone: e.target.value
}};
},
handleZipCodeChange(e){
this.$emit("update:zipcode",e.target.value);
}
}
};
</script>

我们发现为什么绑定的名字不一样,其实是因为我们组件内部是用修改了其名字(props 和 event 那个)。最终语法糖的形式(v-model 对应前两个,.sync 对应后两个);

//语法糖
<PersonalInfo
v-mode l=" phoneInfo"
: zip-code.sync=" zipCode"
/>
<PersonalInfo
:phone- info="phoneInfo"
@change="val => (phoneInfo = val)"
:zip-code=" zipCode"
@update: zipCode="val => (zipCode = val)"
/>

2.理解虚拟 DOM 及 key 属性的作用

事件操作 DOM

2345_image_file_copy_391.jpg

但是事件太多的话:

2345_image_file_copy_392.jpg

2345_image_file_copy_393.jpg

Vue 的话是通过引入一个数据中间层,然后事件不再直接操作 DOM 而是改变数据再映射到 DOM 上。当然由于操作 DOM 非常耗费性能,所以这里是希望尽可能的复用之前的 DOM,于是引入虚拟 DOM。

2345_image_file_copy_394.jpg

查找两棵树的时间复杂度是 O(n3),很差,所以只是比较相同层级的 DOM 节点。

2345_image_file_copy_395.jpg

2345_image_file_copy_396.jpg

2345_image_file_copy_397.jpg

场景 2 比较到第二层节点的时候算法(同层节点比较,其实已经是比较好的了,虽然理想是直接移动 C 节点)是将 C 节点删除而不是移动,第三层的时候新建一个 C 节点,第四层新建 EF,所以说 CEF 都是不能复用的。

2345_image_file_copy_398.jpg

场景 4 要注意,B 是属于同一组件,而且理想状态下是直接移动 B1B2,但是这个时候其实是不知道我们要更新还是移动 B1B2 所以只是把 B1 更新成 B2,B2 更新成 B1,EF 就无法复用了,只能新建。所以我们可以加上 key 来优化(与场景 1 相同):

2345_image_file_copy_399.jpg

场景四:更新删除新建(没有key)

2345_image_file_copy_400.jpg

场景五:移动(有key)

2345_image_file_copy_401.jpg

场景六:插入(有key)

场景 6 也是需要注意,如果无 key 的话则可能是 B2 更新 B4,B3 更新成 B4,新建 B3,但是有的话就只需要插入 B4 就行。


3.如何触发组件的更新

2345_image_file_copy_402.jpg

任何直接更改DOM的行为都是在作死】

2345_image_file_copy_403.jpg

状态data VS属性props;

●状态是组件自身的数据;

●属性是来自父组件的数据;

●状态的改变末必会触发更新;

●属性的改变未必会触发更新。

<script>
import PropsAndData from "./PropsAndData";
let name = "world";
export default {
  components: {
   PropsAndData
},
data() {
this.nane = name;
return {
info:{},
list: []
};
},
methods:{
handleNaneChange() {
  this.nane = "vue" + Date.now();
  console.log("this.nane发生了变化,但是并没有触发子组件更新”,this.nane);
},
handleInfoChange() {
this.info.number = 1;
console.log("this.info发生了变化,但是并没有触发子组件更新”,this.info);
}, 
handleListChange() {
this.list.push(1, 2, 3);
console.log("this.list并没有发生变化,但是触发了子组件更新”,this.list);
}}
);
</script>

上图可以看到前两个方法点击了是改变数据但是不会触发组件的变化,因为 name 没有做响应式,而 info 下面的数据并没有做响应式,所以改变 info 下面的字段是没有用(除非是下面的字段有定义在 data 里面就是响应式了)。

这个时候 name 和 number 在 vue 实例化的时候就有响应式了。这个 list 要注意,其实我们

push 的话是没有改变 list 的(因为没有 this.list 等于改变后的数组),但是却能更新组件。

<template>
  <div>
<p>props.info: {{ info }}</p>
<p>props.name: {{ nane }}</p>
<p>props.list: {{ list }}</p>
<p>data.a:{{ a }}</p>
<p>
  <button @click="handleBChange">change data. b</button>
</p>
</div>
</template>
<script>
export default { 
 name: "PropsAndData",
 props: {
  info: Object,
  nane: String,
  list: Array
},
datal() {
  return
  a: "hello",
  b: "world"
};
},
updated() {
console.log("触发PropsAndData updated");
},
methods: {
handleBChange() { 
this.b = "vue" + Date.now();
console.log("data.b发生了变化。但是并没有触发组件更新",this.b);
}
}
};
</script>

触发点击事件的时候更改 b 但是组件并没有更新,那是因为组件没有用到 b,所以不会提示组件更新。

2345_image_file_copy_404.jpg

Vue 如何做响应式更新,哪些需要依赖收集哪些不需要。Vue实例化的时候会对 data 下面的数据做一个 getter 和 setter 的转化也就是对数据做一个中间的代理层,不管是做什么操作都会经过这个代理层再去进行相应的操作(可以在代理层做任何事情)。而组件在渲染的时候即 render,如果需要用到data 里面的一些数据则把这些数据放到 watcher 里面,没有用到就不会放进去(所以 b 没有用到,更新了不会去通知watcher 也不会去更新组件).

4.合理应用计算属性和侦听器

2345_image_file_copy_405.jpg

计算属性就是可以在里面写一些诸如计算的逻辑等等,同时如果数据没有变化它也不会触发是为缓存,当然这个数据必须是放到 data 里面也就是响应式数据而不能是传来的全局数据!!数据量巨大的时候可以很好的提高性能,毕竟变化时才会计算。

<div>
   <p>Reversed message1: "{{ reversedHessage1 }}"</p>
   <p>Reversed ressage2: "{{ reversedMessage2() }}"</p>
<p>{{ now }}</p>
<button @click="() => $forceUpdate()">forceUpdate</button>
<br/>
 <input v-model="message"/>
</div>
</template>
<script>
export default {
  data() {
   return {
    message: "hello vue"
};
},
computed: {
// 计算属性的getter
 reversedMessage1: function() {
  console.log("执行reversedHessage1");
  return this.message
.split("")
.reverse()
.join(""); 
now: function() { 
return Date.now();
}
},
methods: {
reversedMessage2: function() {
  console.log("执行reversedMessage2");
  return this.message
 .splt("")
 .reverse()
 . join("");
}}

对比上面的例子可以看到,如果值发生变化计算属性和方法都会调用,如果值没有变化但是强制刷新则方法会被调用.

侦听器就是用来监听数据变化的,可以看看某个数据是否改变。

export default {
  data: function() { 
   return {
    a: 1,
    b:{c:2,d:3),
    e:{
     f: {
     g: 4
}
},
h: []
};
},
watch: {
  a: function(val, oldval) {
   this.b.c +=1;
   console.log("new: %s, old: %s", val, oldVal);
},
"b.c": function(val, oldVal) {
this.b.d += 1;
console.log("new:%s,old: %s". val, oldVal);
},
"b.d": function(val, oldVal) {
this.e.f.g += 1;
console.log("new:%s, old: %s", val, oldVal);
},
e:{
  handler: function(val, oldVal) {
  this.h. push(" ");
  console.log("new: %s,old: %s", val, oldVal);
},
deep: true
},
h(val, oldVal) 
console. Log("new: %s, old:%s" val, oldVal);

注意这里面是采用了深度监听(deep:true),不管是 f 还是 g 发生变化都会触发 e 的 handler,这里诸如 this.b.c 的值改变恰恰是为了触发下一个监听“b.c”。这里就是触发了一系

列的更新。虽然啥都能做但是最好还是不要操作 DOM。

2345_image_file_copy_406.jpg

2345_image_file_copy_407.jpg

2345_image_file_copy_408.jpg

注意两个例子,如果是侦听器的话两个都需要加逻辑执行,相对于计算属性是比较冗余,所以先计算属性,实在不行就采用比较底层的 watch(当然也不是一定要这样)。

5.生命周期的应用场景和函数式组件

每一个 vue 组件被创建的时候都会经过一系列的初始化过程,更新的时候也会经过一系列的钩子函数,方便执行一些我们自己的业务逻辑.

更新阶段执行多次,创建和销毁阶段只是执行一次。

2345_image_file_copy_409.jpg

生命周期

2345_image_file_copy_410.jpg

创建阶段

数据做响应式化处理就是数据观测。直接写 render 的话是跳过模板编译阶段的,一般是写 template 的。Render 会生成虚拟 DOM,然后再去挂载我们的真实的 DOM,挂载完才是 mounted(到了这个阶段也不能保证就是把我们操作子组件DOM 的行为挂载到 DOM 上,有时候还需要 nexttick 将操作DOM 丢到回调函数).

2345_image_file_copy_411.jpg

事件监听器一般比较少用,除非是做通用组件库什么的,Updated 阶段也是不一样,不承诺子组件已经更新完,操作DOM 依然需要添加到 nexttick。更改依赖就会导致死循环不 断的触发更新

2345_image_file_copy_412.jpg

销毁阶段

反正哪里的 render 都是渲染虚拟 DOM 挂载真实 DOM;

2345_image_file_copy_413.jpg

只是一个简单的方法,只需要添加 functional:true 即可声明, 一般是展示所用。很经常用到在模板作临时变量,这个很有必要(避免多次重复的逻辑计算),计算属性避免了,但是 不是响应式数据的就不得行(如 v-for 和 v-if、全局数据),还得靠函数式组件。

2345_image_file_copy_414.jpg

很简单,只需要 render 的时候把传递的属性返回给调用方就行了。

2345_image_file_copy_415.jpg

传完之后通过 template 作用域插槽(v-slot)拿到返回出来的 值即可在 template 里面随时使用了。

相关文章
|
28天前
|
JavaScript
Vue中如何实现兄弟组件之间的通信
在Vue中,兄弟组件可通过父组件中转、事件总线、Vuex/Pinia或provide/inject实现通信。小型项目推荐父组件中转或事件总线,大型项目建议使用Pinia等状态管理工具,确保数据流清晰可控,避免内存泄漏。
182 2
|
4月前
|
人工智能 JavaScript 算法
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
626 0
|
4月前
|
JavaScript UED
用组件懒加载优化Vue应用性能
用组件懒加载优化Vue应用性能
|
2月前
|
缓存 前端开发 大数据
虚拟列表在Vue3中的具体应用场景有哪些?
虚拟列表在 Vue3 中通过仅渲染可视区域内容,显著提升大数据列表性能,适用于 ERP 表格、聊天界面、社交媒体、阅读器、日历及树形结构等场景,结合 `vue-virtual-scroller` 等工具可实现高效滚动与交互体验。
321 1
|
3月前
|
JavaScript 安全
在 Vue 中,如何在回调函数中正确使用 this?
在 Vue 中,如何在回调函数中正确使用 this?
138 0
|
3月前
|
人工智能 JSON JavaScript
VTJ.PRO 首发 MasterGo 设计智能识别引擎,秒级生成 Vue 代码
VTJ.PRO发布「AI MasterGo设计稿识别引擎」,成为全球首个支持解析MasterGo原生JSON文件并自动生成Vue组件的AI工具。通过双引擎架构,实现设计到代码全流程自动化,效率提升300%,助力企业降本增效,引领“设计即生产”新时代。
289 1
|
JavaScript
DOM 节点列表长度(Node List Length)
DOM 节点列表长度(Node List Length)
|
12月前
|
JavaScript
HTML DOM 节点树
HTML DOM 节点是指在 HTML 文档对象模型中,文档中的所有内容都被视为节点。整个文档是一个文档节点,每个 HTML 元素是元素节点,元素内的文本是文本节点,属性是属性节点,注释是注释节点。DOM 将文档表示为节点树,节点之间有父子和同胞关系。
|
12月前
|
JavaScript
HTML DOM 节点
HTML DOM(文档对象模型)将HTML文档视为节点树,其中每个部分都是节点:文档本身是文档节点,HTML元素是元素节点,元素内的文本是文本节点,属性是属性节点,注释是注释节点。节点间存在父子及同胞关系,形成层次结构。
|
JavaScript
DOM 节点列表长度(Node List Length)
DOM 节点列表长度(Node List Length)