Vue深入学习4—指令和生命周期

news2024/9/26 5:21:01

1.Vue是怎么识别 v- 指令的?

首先将HTML结构解析成属性列表,存入到数组中,接着遍历数组中的每一个节点,获取到不同指令对应的方法。

// 将HTML看作真正的属性列表
var ndoeAttrs = node.attributes;
var self = this;
// 类数组对象变为数组,一层一层的遍历节点
[].slice.call(nodeAttes).forEach(attr => {
	// 这里开始分析指令
	var attrName = attr.name;
	var value = attr.value;
	// 指令都是 v- 开头的
	var dir = attrName.substring(2);
	if(attrName.indexOf('v-') == 0){
		// v-下不同的指令
		if(dir == 'model'){
			// console.log('发现了model指令',value);
			// 添加Watcher
			new Watcher(self.$vue, value, value => {
				node.value = value;
			});
			// 得到 v 的值
			var v = self.getVueVal(self.$vue, value);
			// 显示 v 的值
			node.value = v;
			// 添加监听事件,基本实现双向绑定
			node.addEventListener('input', e => {
				var newVal = e.target.value;
				self.setVueVal(self.$vue, value, newVal);
				v = newVal;
			});
		}else if(dir == 'if'){
			// console.log('发现了if指令',value);
		}
	} 
})

2.v-model底层是怎么实现的?

v-model 会把关联的相应式数据(info.message),动态的绑定到表单元素的value属性上,然后监听 input 事件;

v-model 绑定的相应数据发生变化时,表单元素的value值也会随之变化。

<template>
  <div>
    <div class="message">{{ info.message }}</div>
    <div><input v-model="info.message" type="text"></div>
    <button @click="change">click</button>
  </div>
</template>

<script>
  export default {
    data () {
      return {
        info: {}
      }
    },
    methods: {
      change () {
        this.info.message = 'hello world'
      }
    }
  }
</script>

面试题:v-for 和 v-if为什么不能一起用?

涉及到一个优先级的问题,v-for 比 v-if优先执行,如果一起使用,循环出来的每一项都会去判断一下v-if是否为true或者false,这样就会照成资源的浪费!

3.生命周期

image-20210725222206025

  • beforeCreate & created

    属于实例化阶段,在_init 方法内,DOM 被挂载时执行,两个函数都不能获取到 prop、data 中定义的值,也不能调用 methods 中定义的函数。

Vue.prototype._init = function (options?: Object) {
  // ...
  initLifecycle(vm)
  initEvents(vm)
  initRender(vm)
  callHook(vm, 'beforeCreate')
  initInjections(vm) // 在prop、data 之前解决注入
  initState(vm)
  initProvide(vm) // 解决初始化之后的prop、data
  callHook(vm, 'created')
  // ...
}
  • beforeMount &mounted

    属于挂载阶段,在mountComponent方法内,响应数据被修改时执行,对于同步渲染的子组件而言,mounted 钩子函数的执行顺序是先子后父

export function mountComponent (
  vm: Component,
  el: ?Element,
  hydrating?: boolean
): Component {
  vm.$el = el
  callHook(vm, 'beforeMount')
  
  let updateComponent
 
  if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
    updateComponent = () => {
      const name = vm._name
      const id = vm._uid
      const startTag = `vue-perf-start:${id}`
      const endTag = `vue-perf-end:${id}`

      mark(startTag)
      const vnode = vm._render()
      mark(endTag)
      measure(`vue ${name} render`, startTag, endTag)

      mark(startTag)
      // 执行vm._update 把 VNode 渲染到 真实 DOM 
      vm._update(vnode, hydrating)
      mark(endTag)
      measure(`vue ${name} patch`, startTag, endTag)
    }
  } else {
    updateComponent = () => {
      vm._update(vm._render(), hydrating)
    }
  }
  // 把它设为vm._watcher 在watcher的构造函数中.
  // 因为观察者的初始补丁可能会调用$forceUpdate(例如:inside child . exe)
  // 组件的挂载钩子),依赖于vm._watcher已经定义.
  new Watcher(vm, updateComponent, noop, {
    before () {
      if (vm._isMounted) {
        callHook(vm, 'beforeUpdate')
      }
    }
  }, true /* isRenderWatcher */)
  hydrating = false
  // 手动挂载实例,调用挂载在self上
  // 挂载在其插入的钩子中为渲染创建的子组件调用
  if (vm.$vnode == null) {
    vm._isMounted = true
    callHook(vm, 'mounted')
  }
  • beforeUpdate & updated

    属于更新阶段,在渲染 Watcher 的 before 函数内,元素被销毁之前执行,在 callUpdatedHooks 函数中,等 vm._watcher 的回调执行完毕后,才能执行 update 函数。

export function mountComponent (
  vm: Component,
  el: ?Element,
  hydrating?: boolean
): Component {
  // ...

  // 把它设为vm._watcher 在watcher的构造函数中.
  // 因为观察者的初始补丁可能会调用$forceUpdate(例如:inside child . exe)
  // 组件的挂载钩子),依赖于vm。_watcher已经定义.
  new Watcher(vm, updateComponent, noop, {
    before () {
      if (vm._isMounted) {
        callHook(vm, 'beforeUpdate')
      }
    }
  }, true /* isRenderWatcher */)
  // ...
}

  • beforeDestory & destroyed (3.x中更名为 beforeUnmount & unmounted

    属于销毁阶段,在 $destroy 函数前执行,从 parent$children 中删掉自身,删除 watcher

Vue.prototype.$destroy = function () {
    const vm: Component = this
    if (vm._isBeingDestroyed) {
      return
    }
    callHook(vm, 'beforeDestroy')
    vm._isBeingDestroyed = true
    // 将self从父节点移除
    const parent = vm.$parent
    if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {
      remove(parent.$children, vm)
    }
    // 卸载 watchers
    if (vm._watcher) {
      vm._watcher.teardown()
    }
    let i = vm._watchers.length
    while (i--) {
      vm._watchers[i].teardown()
    }
    // 从数据ob中移除引用
    // frozen object 没有观察者。
    if (vm._data.__ob__) {
      vm._data.__ob__.vmCount--
    }
    // 调用最后一个钩子
    vm._isDestroyed = true
    // 在当前redered 树上调用销毁钩子
    vm.__patch__(vm._vnode, null)
    // 销毁钩子函数
    callHook(vm, 'destroyed')
    // 关闭所有实例侦听器。
    vm.$off()
    // 删除vue reference
    if (vm.$el) {
      vm.$el.__vue__ = null
    }
    // 发布循环引用
    if (vm.$vnode) {
      vm.$vnode.parent = null
    }
  }

renderTracked & renderTriggered(3.x新增)

都是跟踪虚拟DOM 重新渲染时调用,接收 debugger event 参数;

renderTracked() : 此事件告诉你哪个操作跟踪组件,以及该操作的目标对象和键。

renderTriggered() : 此事件告诉你哪个操作触发重新渲染,以及该操作的目标对象和键。

<div id="app">
  <button v-on:click="addToCart">Add to cart</button>
  <p>Cart({{ cart }})</p>
</div>
const app = Vue.vreateApp({
    data(){
        return{
			cart: 0
        }
    },
    // cart 操作*跟踪*了 组件
    renderTracked({ key, target, type }){
        console.log({ key, target, type })
        /*{ key: "cart", target:{cart: 0}, type: "get" }*/
    },
    // cart 操作*触发*了 重新渲染
    renderTriggered({ key, target, type }) {
  		console.log({ key, target, type })
  	},
    methods: {
        addToCart(){
            this.cart += 1
            /*{ key: "cart", target:{cart:1}, type: "set" }*/
        }
    }
})
app.mount('#app')

总结: Vue生命周期函数就是在初始化,及数据更新过程各个阶段执行不同的钩子函数;在created钩子函数中可以访问到数据,在mounted钩子函数中可以访问到DOM,在destroyed 钩子函数中可以做一些定时器销毁工作!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1414813.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++核心编程:C++ 中的引用 笔记

2.引用 2.1 引用的基本使用 - 作用&#xff1a;给变量起别名 - 语法&#xff1a;数据类型 &别名 原名 #include<iostream> using namespace std; int main() {// 引用基本语法// 数据类型 &别名 原名int a 10;// 创建引用int &ref_a a;cout<<&qu…

RTP工具改进(五)--使用qt

前篇 第四篇 RTP工具改进(四) - rtmp协议推送 前面使用的工具一直为mfc&#xff0c;今天将使用qt 来做界面&#xff0c;使用qt 来进行程序和协议的编写&#xff0c;qt部分目前还不包括rtp ps流和rtmp&#xff0c;暂时只有rtp 直接传输&#xff0c;关于rtmp协议和ps流协议&…

josef约瑟 电流继电器JL8-12 0.02~9.99A DC220V 板内安装

JL-8B电流继电器 系列型号 JL-8B/11电流继电器&#xff1b;JL-8B/12电流继电器&#xff1b; JL-8B/13电流继电器&#xff1b;JL-8B/14电流继电器&#xff1b; JL-8B/21电流继电器&#xff1b;JL-8B/22电流继电器&#xff1b; JL-8B/23电流继电器&#xff1b;JL-8B/24电流继电…

力扣面试题 16.06. 最小差

Problem: 面试题 16.06. 最小差 文章目录 题目描述思路即解法复杂度Code 题目描述 思路即解法 注意本题目的数据范围!!! 1.对数组a与数组b进行排序;获取a与b的数组长度aLen,bLen&#xff0c;定义一个long类型的变量min&#xff1b; 2.分别让两个指针i&#xff0c;j指向数组的开…

【STM32】STM32学习笔记-SPI通信协议(36)

00. 目录 文章目录 00. 目录01. SPI简介02. SPI特征03. SPI通信04. 硬件电路05. 移位示意图06. SPI时序基本单元07. SPI时序08. 附录 01. SPI简介 在大容量产品和互联型产品上&#xff0c;SPI接口可以配置为支持SPI协议或者支持I 2 S音频协议。SPI接口默认工作在SPI方式&#…

【STM32】STM32学习笔记-Unix时间戳(41)

00. 目录 文章目录 00. 目录01. Unix时间戳02. UTC/GMT03. 时间戳转换04. C 标准库 <time.h>05. 时间相关函数示例5.1 time函数5.2 gmtime函数5.3 localtime函数5.4 mktime函数5.5 ctime函数5.6 asctime函数5.7 strftime函数 06. 预留07. 附录 01. Unix时间戳 •Unix 时…

SU-03T语音控制模块详解

当我们谈到智能家居时&#xff0c;经常会通过语音来控制我们的家电&#xff0c;将「懒」发挥到极致。语音模块结合了语音识别和控制技术&#xff0c;使得我们可以通过简单的口令来轻松操控灯光等设备&#xff0c;实现更智能化的生活体验。 在本文中&#xff0c;我们将探讨如何…

2024年最新版快手直播推流码获取工具

快手平台的直播推流码在2023年9月份之前可以通过快手云直播平台获取&#xff0c;但是在此之后快手平台关闭了个人用户的直播推流码功能&#xff0c;导致很多主播都不能再使用OBS或者第三方直播编码器与直播软件进行推流直播。 目前&#xff0c;我们经过多年研发&#xff0c;开…

hive面试题

0. 思维导图 1. 简述Hive♥♥ 我理解的&#xff0c;hive就是一款构建数据仓库的工具&#xff0c;它可以就结构化的数据映射为一张表&#xff0c;并且可以通过SQL语句进行查询分析。本质上是将SQL转换为MapReduce或者spark来进行计算&#xff0c;数据是存储在hdfs上&#xff0c;…

【常用工具】7-Zip 解/压缩软件——基本使用方法

在实际日常工作或项目中&#xff0c;经常会遇到需要在window操作系统上压缩文件&#xff0c;在Linux操作系统上解压缩的场景&#xff0c;一款实用的压缩软件迫在眉睫&#xff0c;经过实际使用总结&#xff0c;7-Zip可以很好的解决很多压缩和解压缩问题&#xff0c;其基本使用方…

FreeRtos Queue (二)

本篇主要讲Queue的prvLockQueue和prvUnlockQueue 一、前言 1、prvLockQueue和prvUnlockQueue是FreeRtos内核函数&#xff0c;只能供内核调用&#xff0c;应用层无法call。 2、cTxLock和cRxLock为中断上锁计数器&#xff0c;cTxLock记录了队列上锁期间在中断里入队的数量&#…

CodeGPT--(Visual )

GitCode - 开发者的代码家园 gitcode.com/ inscode.csdn.net/liujiaping/java_1706242128563/edit?openFileMain.java&editTypelite marketplace.visualstudio.com/items?itemNameCSDN.csdn-codegpt&spm1018.2226.3001.9836&extra%5Butm_source%5Dvip_chatgpt_c…

利用aiohttp异步爬虫实现网站数据高效抓取

前言 大数据时代&#xff0c;网站数据的高效抓取对于众多应用程序和服务来说至关重要。传统的同步爬虫技术在面对大规模数据抓取时往往效率低下&#xff0c;而异步爬虫技术的出现为解决这一问题提供了新的思路。本文将介绍如何利用aiohttp异步爬虫技术实现网站数据抓取&#x…

燃烧的指针(三)

&#x1f308;个人主页&#xff1a;小田爱学编程 &#x1f525; 系列专栏&#xff1a;c语言从基础到进阶 &#x1f3c6;&#x1f3c6;关注博主&#xff0c;随时获取更多关于c语言的优质内容&#xff01;&#x1f3c6;&#x1f3c6; &#x1f600;欢迎来到小田代码世界~ &#x…

DMA 和 零拷贝技术 到 网络大文件传输优化

文章目录 DMA 控制器的发展无 DMA 控制器 IO 过程DMA 控制器 传统文件传输性能有多糟糕&#xff1f;如何优化文件传输性能零拷贝技术mmap writesendfileSG-DMA&#xff08;The Scatter-Gather Direct Memory Access&#xff09; 零拷贝技术的应用 大文件传输应该用什么方式Pag…

C# 使用 SapNwRfc 调用SAP RFC

好久没写过相关代码&#xff0c;今天又来贡献一篇 C# 使用 SapNwRfc 调用SAP RFC。用VS2022的WINFORM应用程序&#xff0c;使用NuGet中的SapNwRfc类库&#xff0c;call SAP系统中的RFC&#xff0c;传入7个参数&#xff0c;得到RFC返回的2张表的数据。 一、VS2022中新建WINFORM…

三数之和----双指针

https://leetcode.cn/problems/3sum/description/?envType=study-plan-v2&envId=top-100-liked “三数之和”在某些人的口中被叫做“程序员之梦破碎的地方”。既然如此,这个题肯定是有难度的,尤其是其中的细节,很多,很细。 其中nums代表给定的数组,numsSize代表给定数…

短视频矩阵系统软件(源头独立开发)技术php7.40版本开发

短视频矩阵功能构建&#xff1a; 1. 关键词批量比距生成&#xff08;区域词行业词产品词&#xff09; 2. 多平台多账号一站式运营管理 3. 视频内容批量复制生成 4. 视频内容批量多平台投放 5. 视频数据分析及粉丝画像分布统计 6. 智能客服响应 7. 智能私域化线索收集 功…

第七篇【传奇开心果】beeware的toga开发移动应用示例:gui工具包介绍和常用组件使用方法示例

传奇开心果博文系列 系列博文目录beeware的toga开发移动应用示例系列博文目录一、beeware和toga介绍二、Toga常用组件使用方法示例三、归纳总结系列博文目录 beeware的toga开发移动应用示例系列 博文目录 一、beeware和toga介绍 1.BeeWare介绍 BeeWare是一个可以让Python开…

SPA单页面的讲解(超级详细)

目录 一、什么是SPA 二、SPA和MPA的区别 单页应用与多页应用的区别 单页应用优缺点 三、实现一个SPA 原理 实现 hash 模式 history模式 四、题外话&#xff1a;如何给SPA做SEO SSR服务端渲染 静态化 使用Phantomjs针对爬虫处理 一、什么是SPA SPA&#xff08;sin…