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

惠州市企业网站seo点击软件南宁市平台公司

惠州市企业网站seo点击软件,南宁市平台公司,外贸建设网站制作,05网英语书目录 绑定事件与解绑事件优化事件的绑定和解绑方式处理不同事件类型的绑定处理同一事件类型多个事件处理函数事件冒泡与更新时机问题 绑定事件与解绑事件 既然要处理事件#xff0c;那么首先面临的问题是如何在 vnode 中描述这个事件#xff0c;在 vnode.props 中#xff0… 目录 绑定事件与解绑事件优化事件的绑定和解绑方式处理不同事件类型的绑定处理同一事件类型多个事件处理函数事件冒泡与更新时机问题 绑定事件与解绑事件 既然要处理事件那么首先面临的问题是如何在 vnode 中描述这个事件在 vnode.props 中凡是以 on 开头的字符串都被视作事件例如 const vnode {type: h1,props: {onClick: () {alert(world)}},children: hello }当有了正确的描述之后我们需要做的就是在处理 vnode.props 的时候增加一个分支当检测到以 on 开头的属性之后就作为事件处理如下 export function patchProp(el, key, prevValue, nextValue) {if (key class) {patchClass(el, nextValue)} else if (key style) {patchStyle(el, prevValue, nextValue)} else if (isOn(key)) {patchEvent(el, key, prevValue, nextValue)}// 处理 DOM Propertieselse if (shouldSetAsProp(el, key)) {patchDOMProp(el, key, nextValue)}// 处理 HTML Attributeselse {patchAttr(el, key, nextValue)} }// isOn 方法实现非常简单如下 function isOn(value) {return value.startsWith(on) }忘记这块处理逻辑的可以翻阅之前的文档patchEvent 具体实现如下 export function patchEvent(el, key, prevValue, nextValue) {// 获取事件名称// - onClick - clickconst evnetName key.slice(2).toLowerCase()// 根据绑定的事件进行处理if (nextValue) {// 绑定事件el.addEventListener(evnetName, nextValue)} }那我们来看一下这个是否可行呢如图 解决初次绑定那么如果是更新事件应该如何处理呢我们很容易想到的方式就是直接将上一次绑定的事件移除如下 export function patchEvent(el, key, prevValue, nextValue) {const evnetName key.slice(2).toLowerCase()// 移除prevValue el.removeEventListener(evnetName, prevValue)if (nextValue) {el.addEventListener(evnetName, nextValue)} }这样做代码确实能够按照预期的进行工作但是不够好我们可以使用一种性能更优的方式来解决。 优化事件的绑定和解绑方式 在绑定事件时我们可以绑定一个伪造的事件处理函数 invoker然后把真正的事件处理函数设置为 invoker.value 属性的值这样当更新事件的时候我们将不再需要调用 removeEventListener 函数来移除上一次的事件只需要更新 invoker.value 的值即可如下 export function patchEvent(el, key, prevValue, nextValue) {const evnetName key.slice(2).toLowerCase()// 获取上一次的伪造的事件处理函数 invokerlet invoker el._veiif (nextValue) {// 如果存在新的vnode且且上一次的invoker不存在则创建新的invokerif (!invoker) {// vei 是 vue event invoker 的缩写invoker el._vei e {// invoker.value 的值就是真正要执行的事件回调函数invoker.value(e)}// 把真正的事件处理函数赋值给 invoker.valueinvoker.value nextValue// 绑定 invoker 为事件处理函数el.addEventListener(evnetName, invoker)}// 如果新的vnode存在且invoker也存在则表示这是一次更新操作else {// 此时我们的更新操作要做的就只是把 invoker.value 更新成新的回调函数即可// - 而不再需要重新绑定事件处理函数invoker.value nextValue}} else if (invoker) {// 如果新的 vnode 不存在且 invoker 存在则表示这个 el 之前绑定过事件函数需要解绑el.removeEventListener(evnetName, invoker)} }此时性能就得到了优化但是 invoker 的作用还不至于此还可以解决事件冒泡和事件更新之间的相互影响的问题不过此时我们先暂时不探讨这个问题。 处理不同事件类型的绑定 我们目前面临的是一个其他的问题我们现在是把事件处理函数缓存到 el._vei 属性中问题是在同一时刻只能缓存一个事件处理函数这意味这如果绑定了多种事件就会出现事件覆盖的问题比如同事存在 click 和 mousedown 事件如下 const vnode {type: h1,props: {onClick: () {console.log(我是 click 事件)},onMouseDown: () {console.log(我是 mousedown 事件)}},children: hello }我们现在绑定了两个事件来看一下打印的结果如图 就仅仅输出了一个并且是后者覆盖前者所以为了处理不同类型的事件时我们需要修改一下缓存事件的结构如下 export function patchEvent(el, key, prevValue, nextValue) {const evnetName key.slice(2).toLowerCase()// 将 el._vei 改为对象let invokers el._vei || (el._vei {})// 根据事件名称获取对应的invokerlet invoker invokers[key]if (nextValue) {if (!invoker) {// 进行缓存的时候通过 key 作为区分避免覆盖invoker el._vei[key] e {invoker.value(e)}invoker.value nextValueel.addEventListener(evnetName, invoker)} else {invoker.value nextValue}} else if (invoker) {el.removeEventListener(evnetName, invoker)} }现在我们来看一下执行的结果如图 处理同一事件类型多个事件处理函数 当完成了不同类型的事件绑定之后我们还面临一个问题就是同一个事件类型的事件绑定多个事件处理函数时只会执行一次vnode 如下 const vnode {type: h1,props: {onClick: [() {console.log(我是 click1事件)},() {console.log(我是 click2 事件)}],},children: hello }按照预期是两个都会触发但是实际情况并不是这样所以为了达到这个目标我们还需要调整一下代码如下 export function patchEvent(el, key, prevValue, nextValue) {const evnetName key.slice(2).toLowerCase()let invokers el._vei || (el._vei {})let invoker invokers[key]if (nextValue) {if (!invoker) {invoker el._vei[key] e {// 如果 invoker.value 是一个数组则依次执行if (isArray(invoker.value)) {invoker.value.forEach(fn fn(e))}// 不是数组则表示只有一个事件处理函数直接执行else {invoker.value(e)}}// nextValue 可能会直接是一个函数也可能会是一个包含多个函数的数组invoker.value nextValueel.addEventListener(evnetName, invoker)} else {invoker.value nextValue}} else if (invoker) {el.removeEventListener(evnetName, invoker)} }执行结果如图 事件冒泡与更新时机问题 我们来看一下下面这个案例如下 const bol ref(false)effect(() {console.log(effect)const vnode {type: div,props: bol.value? {onClick() {console.log(父元素 clicked)}}: {},children: [{type: p,props: {onClick() {bol.value trueconsole.log(子元素 p clicked, bol.value)}},children: click me}]}render(vnode, document.querySelector(#app)) })根据这段代码我们可以思考一个问题首次渲染完成之后点击 p 标签的点击事件是否会触发 div 的点击事件 根据代码的逻辑分析一开始 bol 的值为 false则不会给 div 绑定点击事件那么点击 p 元素时就算因为事件冒泡的存在但是因为没有给 bol 绑定点击事件所以就不会触发 div 的点击事件。 但是实际上却不是在运行这段代码时触发了 p 元素的点击事件也会触发 div 的点击事件这是因为在 p 元素的点击事件中奖 bol 的值改为 true我们又处于 effect 函数中bol 是一个响应式数据所以 bol 发生改变后就会立即执行 effect 函数自然也会重新执行 render 函数渲染也就是说在 p 元素的 click 点击事件还没处理完成的时候就会再次渲染了再次渲染就会给 div 绑定点击事件而当 p 元素点击事件执行完成之后进行冒泡此时 div 已经绑定了点击事件所以就会触发 div 的点击事件。 根据这个流程我们可以使用一个图来表达执行顺序如图 那如何解决呢我们其实在这个图中可以发现事件触发的时间是在时间处理函数被绑定之前的这就意味着事件触发时目标元素上还没有绑定相关事件处理函数我们可以根据这个特点来解决问题即屏蔽所有绑定时间晚于事件触发时间的事件处理函数的执行如下 export function patchEvent(el, key, prevValue, nextValue) {const evnetName key.slice(2).toLowerCase()let invokers el._vei || (el._vei {})let invoker invokers[key]if (nextValue) {if (!invoker) {invoker el._vei[key] e {// e.timeStamp 是事件触发的时间// 如果 e.timeStamp 小于于绑定事件的时间则不执行if (e.timeStamp invoker.attrched) returnif (isArray(invoker.value)) {invoker.value.forEach(fn fn(e))} else {invoker.value(e)}}invoker.value nextValue// 添加时间绑定的时间invoker.attrched performance.now()el.addEventListener(evnetName, invoker)} else {invoker.value nextValue}} else if (invoker) {el.removeEventListener(evnetName, invoker)} }这样就可以解决了为什么可以解决呢我们就来分析一下这新增的两行代码performance.now() 是一个高精时间具体的可以查阅文档DOMHighResTimeStamp这时间源是创建浏览器上下文的时间在这里可以简单理解为一个只会增加的时间页面刷新就会重置重新计算所以首次渲染之后点击 p 元素触发点击事件将 bol 的值改为 true此时会执行 effect 然后绑定事件并记录下绑定的时间重点来了此时的 e.timeStamp 触发的时间是 p 事件触发的时间而非这个冒泡到 div 的事件触发时间所以这个 timeStamp 一定比 div 的事件绑定时间要早而比这个早就会被 if (e.timeStamp invoker.attrched) return 这句代码拦截而后续触发的时候invoker 已经存在所以不会重新设置 invoker.attrched 的值所以后续触发时e.timeStamp 的时间就是大于 invoker.attrched 所记录的时间。这样就解决了我们的问题。 如果不够直观我们可以看一下初次渲染点击的时间记录如图 再来看看再次点击的执行结果如图
http://www.hyszgw.com/news/107877.html

相关文章:

  • 建立网站和新媒体信息发布制度西安网站建设哪家好一些
  • 在手机上怎么制作网站东莞外贸网站建设哪家好
  • 石家庄工信部网站备案重构网站
  • 做美食分享网站源码山东电商网站建设
  • 网站设计提成多少钱深圳微信网站建设
  • 自己的网站如何让百度收录深圳百度快速排名提升
  • seo网站优化方案品牌网址是什么
  • 网站备案主体注销桔子seo
  • 网站建设的功能和定位网站构成要素
  • 免费建自己的网站赚钱做亚马逊网站一般发什么快递公司
  • 大连网站建设讯息数字广东网络建设有限公司总经理
  • 海东市城市规划建设局网站宁波三盛网络网站建设
  • 口碑最好的旅游网站镇江建设银行网站
  • 正规网站有哪些中企动力z邮箱
  • 企业建设网站怎么做账卖鞋子网站建设策划书
  • 广东网站设计专业团队自己建设一个网站
  • 想做一个部门的网站怎么做邢台专业做网站公司
  • 百度收录不了网站什么招聘网最好找工作
  • 用ps怎么做网站导航条怎么做吉林省示范校建设专题网站
  • 织梦儿童早教教育培训网站模板深圳家园网社区论坛
  • 猎头做单的网站百度网站下载安装
  • 免费刷粉网站推广豫建设标去哪个网站
  • 网站优化搜索排名优秀网页设计案例分析图文
  • 新淘客wordpress南昌seo锐创
  • 自己建设网站需要哪些许昌抖音推广公司
  • 天河网站建设公司排名南京站建设
  • c2c网站 多钱公司网站开发比选
  • 公众号首图制作网站设置wordpress静态主页
  • 外贸网站建设行情工业设计的概念是什么
  • 电脑做网站怎么解析域名大气企业网站织梦模板