当前位置: 首页 > news >正文

【Vue2.0源码学习】生命周期篇-初始化阶段(initEvents)

文章目录

    • 1. 前言
    • 2. 解析事件
    • 3. initEvents函数分析
    • 4. 总结

1. 前言

本篇文章介绍生命周期初始化阶段所调用的第二个初始化函数——initEvents。从函数名字上来看,这个初始化函数是初始化实例的事件系统。我们知道,在Vue中,当我们在父组件中使用子组件时可以给子组件上注册一些事件,这些事件即包括使用v-on@注册的自定义事件,也包括注册的浏览器原生事件(需要加 .native 修饰符),如下:

<child @select="selectHandler" 	@click.native="clickHandler"></child>

不管是什么事件,当子组件(即实例)在初始化的时候都需要进行一定的初始化,那么本篇文章就来看看实例上的事件都是如何进行初始化的。

2. 解析事件

我们先从解析事件开始说起,回顾之前的模板编译解析中,当遇到开始标签的时候,除了会解析开始标签,还会调用processAttrs 方法解析标签中的属性,processAttrs 方法位于源码的 src/compiler/parser/index.js中, 如下:

export const onRE = /^@|^v-on:/
export const dirRE = /^v-|^@|^:/function processAttrs (el) {const list = el.attrsListlet i, l, name, value, modifiersfor (i = 0, l = list.length; i < l; i++) {name  = list[i].namevalue = list[i].valueif (dirRE.test(name)) {// 解析修饰符modifiers = parseModifiers(name)if (modifiers) {name = name.replace(modifierRE, '')}if (onRE.test(name)) { // v-onname = name.replace(onRE, '')addHandler(el, name, value, modifiers, false, warn)}}}
}

从上述代码中可以看到,在对标签属性进行解析时,判断如果属性是指令,首先通过 parseModifiers 解析出属性的修饰符,然后判断如果是事件的指令,则执行 addHandler(el, name, value, modifiers, false, warn) 方法, 该方法定义在 src/compiler/helpers.js 中,如下:

export function addHandler (el,name,value,modifiers) {modifiers = modifiers || emptyObject// check capture modifier 判断是否有capture修饰符if (modifiers.capture) {delete modifiers.capturename = '!' + name // 给事件名前加'!'用以标记capture修饰符}// 判断是否有once修饰符if (modifiers.once) {delete modifiers.oncename = '~' + name // 给事件名前加'~'用以标记once修饰符}// 判断是否有passive修饰符if (modifiers.passive) {delete modifiers.passivename = '&' + name // 给事件名前加'&'用以标记passive修饰符}let eventsif (modifiers.native) {delete modifiers.nativeevents = el.nativeEvents || (el.nativeEvents = {})} else {events = el.events || (el.events = {})}const newHandler: any = {value: value.trim()}if (modifiers !== emptyObject) {newHandler.modifiers = modifiers}const handlers = events[name]if (Array.isArray(handlers)) {handlers.push(newHandler)} else if (handlers) {events[name] = [handlers, newHandler]} else {events[name] = newHandler}el.plain = false
}

addHandler 函数里做了 3 件事情,首先根据 modifier 修饰符对事件名 name 做处理,接着根据 modifier.native 判断事件是一个浏览器原生事件还是自定义事件,分别对应 el.nativeEventsel.events,最后按照 name 对事件做归类,并把回调函数的字符串保留到对应的事件中。

在前言中的例子中,父组件的 child 节点生成的 el.eventsel.nativeEvents 如下:

el.events = {select: {value: 'selectHandler'}
}el.nativeEvents = {click: {value: 'clickHandler'}
}

然后在模板编译的代码生成阶段,会在 genData 函数中根据 AST 元素节点上的 eventsnativeEvents 生成_c(tagName,data,children)函数中所需要的 data 数据,它的定义在 src/compiler/codegen/index.js 中:

export function genData (el state) {let data = '{'// ...if (el.events) {data += `${genHandlers(el.events, false,state.warn)},`}if (el.nativeEvents) {data += `${genHandlers(el.nativeEvents, true, state.warn)},`}// ...return data
}

生成的data数据如下:

{// ...on: {"select": selectHandler},nativeOn: {"click": function($event) {return clickHandler($event)}}// ...
}

可以看到,最开始的模板中标签上注册的事件最终会被解析成用于创建元素型VNode_c(tagName,data,children)函数中data数据中的两个对象,自定义事件对象on,浏览器原生事件nativeOn

在前面的文章中我们说过,模板编译的最终目的是创建render函数供挂载的时候调用生成虚拟DOM,那么在挂载阶段, 如果被挂载的节点是一个组件节点,则通过 createComponent 函数创建一个组件 vnode,该函数位于源码的 src/core/vdom/create-component.js 中, 如下:

export function createComponent (Ctor: Class<Component> | Function | Object | void,data: ?VNodeData,context: Component,children: ?Array<VNode>,tag?: string
): VNode | Array<VNode> | void {// ...const listeners = data.ondata.on = data.nativeOn// ...const name = Ctor.options.name || tagconst vnode = new VNode(`vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,data, undefined, undefined, undefined, context,{ Ctor, propsData, listeners, tag, children },asyncFactory)return vnode
}

可以看到,把 自定义事件data.on 赋值给了 listeners,把浏览器原生事件 data.nativeOn 赋值给了 data.on,这说明所有的原生浏览器事件处理是在当前父组件环境中处理的。而对于自定义事件,会把 listeners 作为 vnodecomponentOptions 传入,放在子组件初始化阶段中处理, 在子组件的初始化的时候, 拿到了父组件传入的 listeners,然后在执行 initEvents 的过程中,会处理这个 listeners

所以铺垫了这么多,结论来了:父组件给子组件的注册事件中,把自定义事件传给子组件,在子组件实例化的时候进行初始化;而浏览器原生事件是在父组件中处理。

换句话说:实例初始化阶段调用的初始化事件函数initEvents实际上初始化的是父组件在模板中使用v-on或@注册的监听子组件内触发的事件。

3. initEvents函数分析

了解了以上过程之后,我们终于进入了正题,开始分析initEvents函数,该函数位于源码的src/instance/events.js中,如下:

export function initEvents (vm: Component) {vm._events = Object.create(null)// init parent attached eventsconst listeners = vm.$options._parentListenersif (listeners) {updateComponentListeners(vm, listeners)}
}

可以看到,initEvents函数逻辑非常简单,首先在vm上新增_events属性并将其赋值为空对象,用来存储事件。

vm._events = Object.create(null)

接着,获取父组件注册的事件赋给listeners,如果listeners不为空,则调用updateComponentListeners函数,将父组件向子组件注册的事件注册到子组件的实例中,如下:

const listeners = vm.$options._parentListeners
if (listeners) {updateComponentListeners(vm, listeners)
}

这个updateComponentListeners函数是什么呢?该函数定义如下:

export function updateComponentListeners (vm: Component,listeners: Object,oldListeners: ?Object
) {target = vmupdateListeners(listeners, oldListeners || {}, add, remove, vm)target = undefined
}function add (event, fn, once) {if (once) {target.$once(event, fn)} else {target.$on(event, fn)}
}function remove (event, fn) {target.$off(event, fn)
}

可以看到,updateComponentListeners函数其实也没有干什么,只是调用了updateListeners函数,并把listeners以及addremove这两个函数传入。我们继续跟进,看看updateListeners函数干了些什么,updateListeners函数位于源码的src/vdom/helpers/update-listeners.js中,如下:

export function updateListeners (on: Object,oldOn: Object,add: Function,remove: Function,vm: Component
) {let name, def, cur, old, eventfor (name in on) {def = cur = on[name]old = oldOn[name]event = normalizeEvent(name)if (isUndef(cur)) {process.env.NODE_ENV !== 'production' && warn(`Invalid handler for event "${event.name}": got ` + String(cur),vm)} else if (isUndef(old)) {if (isUndef(cur.fns)) {cur = on[name] = createFnInvoker(cur)}add(event.name, cur, event.once, event.capture, event.passive, event.params)} else if (cur !== old) {old.fns = curon[name] = old}}for (name in oldOn) {if (isUndef(on[name])) {event = normalizeEvent(name)remove(event.name, oldOn[name], event.capture)}}
}

可以看到,该函数的作用是对比listenersoldListeners的不同,并调用参数中提供的addremove进行相应的注册事件和卸载事件。其思想是:如果listeners对象中存在某个key(即事件名)而oldListeners中不存在,则说明这个事件是需要新增的;反之,如果oldListeners对象中存在某个key(即事件名)而listeners中不存在,则说明这个事件是需要从事件系统中卸载的;

该函数接收5个参数,分别是onoldOnaddremovevm,其中on对应listenersoldOn对应oldListeners

首先对on进行遍历, 获得每一个事件名,然后调用 normalizeEvent 函数(关于该函数下面会介绍)处理, 处理完事件名后, 判断事件名对应的值是否存在,如果不存在则抛出警告,如下:

for (name in on) {def = cur = on[name]old = oldOn[name]event = normalizeEvent(name)if (isUndef(cur)) {process.env.NODE_ENV !== 'production' && warn(`Invalid handler for event "${event.name}": got ` + String(cur),vm)}
}

如果存在,则继续判断该事件名在oldOn中是否存在,如果不存在,则调用add注册事件,如下:

if (isUndef(old)) {if (isUndef(cur.fns)) {cur = on[name] = createFnInvoker(cur)}add(event.name, cur, event.once, event.capture, event.passive, event.params)
}

这里定义了 createFnInvoker 方法并返回invoker函数:

export function createFnInvoker (fns) {function invoker () {const fns = invoker.fnsif (Array.isArray(fns)) {const cloned = fns.slice()for (let i = 0; i < cloned.length; i++) {cloned[i].apply(null, arguments)}} else {// return handler return value for single handlersreturn fns.apply(null, arguments)}}invoker.fns = fnsreturn invoker
}

由于一个事件可能会对应多个回调函数,所以这里做了数组的判断,多个回调函数就依次调用。注意最后的赋值逻辑, invoker.fns = fns,每一次执行 invoker 函数都是从 invoker.fns 里取执行的回调函数,回到 updateListeners,当我们第二次执行该函数的时候,判断如果 cur !== old,那么只需要更改 old.fns = cur 把之前绑定的 involer.fns 赋值为新的回调函数即可,并且 通过 on[name] = old 保留引用关系,这样就保证了事件回调只添加一次,之后仅仅去修改它的回调函数的引用。

if (cur !== old) {old.fns = curon[name] = old
}

最后遍历 oldOn, 获得每一个事件名,判断如果事件名在on中不存在,则表示该事件是需要从事件系统中卸载的事件,则调用 remove方法卸载该事件。

以上就是updateListeners函数的所有逻辑,那么上面还遗留了一个normalizeEvent 函数是干什么用的呢?还记得我们在解析事件的时候,当事件上有修饰符的时候,我们会根据不同的修饰符给事件名前面添加不同的符号以作标识,其实这个normalizeEvent 函数就是个反向操作,根据事件名前面的不同标识反向解析出该事件所带的何种修饰符,其代码如下:

const normalizeEvent = cached((name: string): {name: string,once: boolean,capture: boolean,passive: boolean,handler?: Function,params?: Array<any>
} => {const passive = name.charAt(0) === '&'name = passive ? name.slice(1) : nameconst once = name.charAt(0) === '~'name = once ? name.slice(1) : nameconst capture = name.charAt(0) === '!'name = capture ? name.slice(1) : namereturn {name,once,capture,passive}
})

可以看到,就是判断事件名的第一个字符是何种标识进而判断出事件带有何种修饰符,最终将真实事件名及所带的修饰符返回。

4. 总结

本篇文章介绍了生命周期初始化阶段所调用的第二个初始化函数——initEvents。该函数是用来初始化实例的事件系统的。

我们先从模板编译时对组件标签上的事件解析入手分析,我们知道了,父组件既可以给子组件上绑定自定义事件,也可以绑定浏览器原生事件。这两种事件有着不同的处理时机,浏览器原生事件是由父组件处理,而自定义事件是在子组件初始化的时候由父组件传给子组件,再由子组件注册到实例的事件系统中。

也就是说:初始化事件函数initEvents实际上初始化的是父组件在模板中使用v-on或@注册的监听子组件内触发的事件。

最后分析了initEvents函数的具体实现过程,该函数内部首先在实例上新增了_events属性并将其赋值为空对象,用来存储事件。接着通过调用updateComponentListeners函数,将父组件向子组件注册的事件注册到子组件实例中的_events对象里。

相关文章:

【Vue2.0源码学习】生命周期篇-初始化阶段(initEvents)

文章目录 1. 前言2. 解析事件3. initEvents函数分析4. 总结 1. 前言 本篇文章介绍生命周期初始化阶段所调用的第二个初始化函数——initEvents。从函数名字上来看&#xff0c;这个初始化函数是初始化实例的事件系统。我们知道&#xff0c;在Vue中&#xff0c;当我们在父组件中…...

SQL高级知识点

MySQL基础 1、安装 1)设置编码 2)设置密码 2、配置文件&#xff1a;my.ini、my.cnf 1)设置端口号 port3306 2)设置编码 default-character-setutf8character-set-serverutf8 3)存储引擎 default-storage-engineINNODB 4)最大连接数 max_connections100 注意&…...

【安全】原型链污染 - Code-Breaking 2018 Thejs

目录 准备工作 环境搭建 加载项目 复现 代码审计 payload 总结 准备工作 环境搭建 Nodejs BurpSuite 加载项目 项目链接 ① 下载好了cmd切进去 ② 安装这个项目 可以检查一下 ③运行并监听 可以看到已经在3000端口启动了 复现 代码审计 const fs require(fs) cons…...

【架构】探索计算机处理器的世界:ARM和x86架构解析及指令集

目录 导语ARM架构x86架构AMD公司对比与应用不同架构处理器的指令集结语 导语 计算机处理器是数字化时代的核心引擎&#xff0c;而在众多处理器架构中&#xff0c;ARM和x86是备受关注的三个。本文将带您深入探索这三个架构&#xff0c;介绍它们的特点、公司背景以及应用领域。让…...

SpringBoot权限认证

SpringBoot的安全 常用框架&#xff1a;Shrio,SpringSecurity 两个功能&#xff1a; Authentication 认证Authorization 授权 权限&#xff1a; 功能权限访问权限菜单权限 原来用拦截器、过滤器来做&#xff0c;代码较多。现在用框架。 SpringSecurity 只要引入就可以使…...

OpenGL-入门-BMP像素图glReadPixels

glReadPixels函数用于从帧缓冲区中读取像素数据。它可以用来获取屏幕上特定位置的像素颜色值或者获取一块区域内的像素数据。下面是该函数的基本语法&#xff1a; void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *da…...

同源策略以及SpringBoot的常见跨域配置

先说明一个坑。在跨域的情况下&#xff0c;浏览器针对复杂请求&#xff0c;会发起预检OPTIONS请求。如果服务端对OPTIONS进行拦截&#xff0c;并返回非200的http状态码。浏览器一律提示为cors error。 一、了解跨域 1.1 同源策略 浏览器的同源策略&#xff08;Same-Origin Po…...

基于jeecg-boot的flowable流程跳转功能实现

更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a; https://gitee.com/nbacheng/nbcio-boot 前端代码&#xff1a;https://gitee.com/nbacheng/nbcio-vue.git 在线演示&#xff08;包括H5&#xff09; &#xff1a; http://122.227.135.243:9888 今天我…...

react图片预加载

道阻且长&#xff0c;行而不辍&#xff0c;未来可期 图片预加载的原理&#xff1a;new一个image对象&#xff0c;用这个对象加载图片&#xff0c;等这个对象将这个图片请求完后&#xff0c;再将这个图片放入原本应该放置的位置 代码如下&#xff1a; import React, { useEffe…...

数据库管理

SQL语言分类&#xff1a; DDL&#xff1a;数据定义语言&#xff0c;用于创建数据库对象&#xff0c;如库、表、索引等 DML&#xff1a;数据操纵语言&#xff0c;用于对表中的数据进行管理 DQL&#xff1a;数据查询语言&#xff0c;用于从数据表中查找符合条件的数据记录 DCL&am…...

【2023年11月第四版教材】《第8章-整合管理》(第3部分)

《第8章-整合管理》&#xff08;第3部分&#xff09; 9 监控项目工作9.1 监控项目工作★★★9.2 数据分析9.4 决策9.5 工作绩效报告 10 实施整体变更控制10.1 实施整体变更控制★★★ &#xff08;18上36&#xff09;10.2 变更请求★★★10.3变更控制工具★★★10.4 数据分析10…...

初阶数据结构(三)链表

&#x1f493;博主csdn个人主页&#xff1a;小小unicorn&#x1f493; ⏩专栏分类&#xff1a;c &#x1f69a;代码仓库&#xff1a;小小unicorn的学习足迹&#x1f69a; &#x1f339;&#x1f339;&#x1f339;关注我带你学习编程知识 前面我们讲的线性表的顺序存储结构。它…...

Python小知识 - 八大排序算法

八大排序算法 排序算法是计算机科学中非常重要的一个研究领域。排序算法可以分为内部排序和外部排序&#xff0c;内部排序是数据记录在计算机内部&#xff0c;而外部排序是数据记录在计算机外部&#xff0c;这里我们主要讨论内部排序。 内部排序中的算法大致可以归纳为四类&…...

安卓动态申请权限

我们在使用一些官方app时&#xff0c;刚下载进去之后经常会弹出各种各样的权限获取请求&#xff0c;今天简单学习了下&#xff0c;希望不会误人子弟哈哈哈哈。 一、将需要用到的权限添加到Manifest清单里 <uses-permission android:name"android.permission.WRITE_EXT…...

关于亚马逊云科技云技能孵化营学习心得

1、活动介绍 本活动主要是面向想要全面了解亚马逊云科技 (Amazon Web Services) 云的个人&#xff0c;而不受特定技术角色的限制。内容包括亚马逊云科技云概念、亚马逊云科技服务、安全性、架构、定价和支持等等&#xff0c;此外还可以参加亚马逊的认证考试。 2、学习过程 该…...

计算机安全学习笔记(III):强制访问控制 - MAC

基本概念 强制访问控制是一种高级访问控制机制&#xff0c;旨在通过强制执行事先定义的安全策略&#xff0c;实现资源和信息的严格保护。与自主访问控制&#xff08;Discretionary Access Control&#xff0c;DAC&#xff09;不同&#xff0c;MAC 的控制权不由用户自身决定&am…...

java判断ip是否为指定网段

具体网络知识原理请看这个博文 /**** param address servletRequest.getRemoteAddr();* param host servletRequest.getRemoteHost();* return* Description 检验IP是否符合安全限定*/private boolean ipIsInNet(String address, String host){Set<String> iPset allow…...

如何通过人工智能和自动化提高供应链弹性?

全球供应链中的数字化转型已经引起了广泛关注&#xff0c;尽管在过去的十年中&#xff0c;这一话题被广泛讨论&#xff0c;但许多公司仍然对如何实现这一不明确的目标感到困惑。人们普遍认识到这种转变的重要性&#xff0c;而新冠疫情及其带来的巨大影响也为行业向数字化转型方…...

【Apollo学习笔记】——规划模块TASK之PATH_REUSE_DECIDER

文章目录 前言PATH_REUSE_DECIDER功能简介PATH_REUSE_DECIDER相关配置PATH_REUSE_DECIDER总体流程PATH_REUSE_DECIDER相关子函数IsCollisionFreeTrimHistoryPathIsIgnoredBlockingObstacle和GetBlockingObstacleS Else参考 前言 在Apollo星火计划学习笔记——Apollo路径规划算…...

框架分析(6)-Ruby on Rails

框架分析&#xff08;6&#xff09;-Ruby on Rails 专栏介绍Ruby on Rails核心概念以及组件讲解MVC架构模式约定优于配置强大的ORM支持自动化测试丰富的插件生态系统RESTful路由安全性总结 优缺点优点快速开发简单易学MVC架构强大的ORM支持大量的插件和Gem支持 缺点性能问题学习…...

LLMs NLP模型评估Model evaluation ROUGE and BLEU SCORE

在整个课程中&#xff0c;你看到过类似模型在这个任务上表现良好&#xff0c;或者这个微调模型在性能上相对于基础模型有显著提升等陈述。 这些陈述是什么意思&#xff1f;如何形式化你的微调模型在你起初的预训练模型上的性能改进&#xff1f;让我们探讨一些由大型语言模型开…...

BlazorServer中C#与JavaScript的相互调用

BlazorServer中C#与JavaScript的相互调用 前言&#xff1a; ​ 虽然BlazorServer中推荐使用C#在razor页面中的替代JavaScript来完成逻辑的编写&#xff0c;但当需要使用第三方的javascript文件/组件里的内容时&#xff0c;则难免要在C#中调用其方法或对象。反之当你的(用到第…...

深入理解 MD5 消息摘要算法和在密码存储中的应用及安全隐患

MD5 算法相信很多开发人员都听说过, 一个最常见的使用到它的地方就是密码的存储. 当然, 很多人会说, 这个算法已经不太安全了, 确实如果你想更安全的保存密码, 则应该考虑其它更安全的算法, 不过这不属于此次讨论的主题. 什么是 MD5 MD5 是一种算法, MD5 中的 MD 代表 Message…...

python网络爬虫指南二:多线程网络爬虫、动态内容爬取(待续)

文章目录 一、多线程网络爬虫1.1 线程的基础内容、GIL1.2 创建线程的两种方式1.3 threading.Thread类1.4 线程常用方法和锁机制1.5 生产者-消费者模式1.5.1 生产者-消费者模式简介1.5.2 Condition 类协调线程 1.6 线程中的安全队列1.6 多线程爬取王者荣耀壁纸1.6.1 网页分析1.6…...

华为AirEgine9700S AC配置示例

Vlan97为管理Vlan <AirEgine9700S>dis cu Software Version V200R021C00SPC100 #sysname AirEgine9700S #http timeout 60http secure-server ssl-policy default_policyhttp secure-server server-source -i allhttp server enable #set np rss hash-mode 5-tuple # md…...

VUE3基础

一、vue-router v4.x 介绍 | Vue Router 1、安装 yarn add vue-routernext next代表最新的版本 2、路由配置 在src目录下&#xff0c;新建router/index.ts&#xff0c;具体配置如下 import {RouteRecordRaw,createRouter,createWebHashHistory} from vue-router const r…...

Qt应用开发(基础篇)——日历 QCalendarWidget

一、前言 QCalendarWidget类继承于QWidget&#xff0c;是Qt设计用来让用户更直观的选择日期的窗口部件。 时间微调输入框 QCalendarWidget根据年份和月份初始化&#xff0c;程序员也通过提供公共函数去改变他们&#xff0c;默认日期为当前的系统时间&#xff0c;用户通过鼠标和…...

Python学习笔记:正则表达式、逻辑运算符、lamda、二叉树遍历规则、类的判断

1.正则表达式如何写&#xff1f; 序号实例说明1.匹配任何字符(除换行符以外)2\d等效于[0-9]&#xff0c;匹配数字3\D等效于[^0-9]&#xff0c;匹配非数字4\s等效于[\t\r\n\f]&#xff0c;匹配空格字符5\S等效于[^\t\r\n\f]&#xff0c;匹配非空格字符6\w等效于[A-Za-z0-9]&…...

【滑动窗口】leetcode1004:最大连续1的个数

一.题目描述 最大连续1的个数 这道题要我们找最大连续1的个数&#xff0c;看到“连续”二字&#xff0c;我们要想到滑动窗口的方法。滑动窗口的研究对象是一个连续的区间&#xff0c;这个区间需要满足某个条件。那么本题要找的是怎样的区间呢&#xff1f;是一个通过翻转0后得到…...

力扣:73. 矩阵置零(Python3)

题目&#xff1a; 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚…...

平台网站开发/52种新颖的促销方式

重要国策《文化产业振兴规划》于9月26日正式对外公布。巧合的是&#xff0c;就在前一天&#xff08;9月25日 &#xff09;&#xff0c;盛大游戏&#xff08;SDG&#xff09;成功在NASDAQ上市&#xff0c;并且创下了美股IPO规模之最&#xff08;10亿美元&#xff09;。1&#xf…...

哪个网站做木材批发的/电商seo是什么意思啊

0 说明 本文主要说明在裸机和linux启动中链接地址和运行地址的关系&#xff0c;但是这俩哥们的关系和位置无关码以及MMU有所牵扯&#xff0c;所以放到一起进行说明。 以下针对imx6ull处理器平台 1、imx6u的Memory Maps 首先确定一下物理内存的开始地址&#xff0c;不同处理器D…...

31省新增最新消息/湖南seo优化首选

【题目描述】 物流公司要把一批货物从码头A运到码头B。由于货物量比较大&#xff0c;需要n天才能运完。货物运输过程中一般要转停好几个码头。物流公司通常会设计一条固定的运输路线&#xff0c;以便对整个运输过程实施严格的管理和跟踪。由于各种因素的存在&#xff0c;有的时…...

网站运营维护合同/数据分析网站

Web服务器收到客户端的http请求&#xff0c;会针对每一次请求&#xff0c;分别创建一个用于代表请求的request对象、和代表响应的response对象。request和response对象即然代表请求和响应&#xff0c;那我们要获取客户机提交过来的数据&#xff0c;只需要找request对象就行了。…...

如何网站点击率/如何在百度搜索排名靠前

逆元主要用于除法取余的情况下&#xff1a; 设 inv[b] 是 b 的逆元, 那么 (a/b) %p (a*inv[b]) %p inv[b]是b的逆元的前提是gcd(b,p)1,此时逆元存在 &#xff08;在mod p情况下的逆元&#xff09;定义&#xff1a;x是a 的逆元则ax1&#xff08;modp&#xff09;其中x <p…...

越秀区建网站公司/直播网站排名

我不断听到队友说 “测试不是我的工作&#xff0c;我是<insert_role>”或“设计产品不是我的工作&#xff0c;我是<insert_role>” 如果不加以检查&#xff0c;我会对消息引起的行为感到非常厌倦。 一个团队不仅仅是其各个部分的总和&#xff0c;一个团队具有协…...