petite-vue源码剖析-v-if和v-for的工作原理

在这里插入图片描述

《Petite-Vue源码剖析》
结合示例从在线渲染、响应式系统和沙箱模型分别对源码逐行解读,其中还对响应式系统中利用JS引擎的SMI优化依赖清理算法作详细分析。绝对是入门Vue3源码前绝佳的踏脚石喜欢的话记得转发、赞赏哦

深入v-if的工作原理

<div v-scope="App"></div>

<script type="module">
  import {
      createApp } from 'https://unpkg.com/petite-vue?module'

  createApp({
     
    App: {
     
      $template: `
      <span v-if="status === 'offline'"> OFFLINE </span>
      <span v-else-if="status === 'UNKOWN'"> UNKOWN </span>
      <span v-else> ONLINE </span>
      `,
    }
    status: 'online'
  }).mount('[v-scope]')
</script>

人肉单步调试:

  1. 调用createApp根据入参生成全局作用域rootScope,创建根上下文rootCtx
  2. 调用mount<div v-scope="App"></div>构建根块对象rootBlock,并将其作为模板执行解析处理;
  3. 解析时识别到v-scope属性,以全局作用域rootScope为基础运算得到局部作用域scope,并以根上下文rootCtx为蓝本一同构建新的上下文ctx,用于子节点的解析和渲染;
  4. 获取$template属性值并生成HTML元素;
  5. 深度优先遍历解析子节点(调用walkChildren);
  6. 解析<span v-if="status === 'offline'"> OFFLINE </span>

解析<span v-if="status === 'offline'"> OFFLINE </span>

书接上一回,我们继续人肉单步调试:

  1. 识别元素带上v-if属性,调用_if原指令对元素及兄弟元素进行解析;
  2. 将附带v-if和跟紧其后的附带v-else-ifv-else的元素转化为逻辑分支记录;
  3. 循环遍历分支,并为逻辑运算结果为true的分支创建块对象并销毁原有分支的块对象(首次渲染没有原分支的块对象),并提交渲染任务到异步队列。
// 文件 ./src/walk.ts

// 为便于理解,我对代码进行了精简
export const walk = (node: Node, ctx: Context): ChildNode | null | void {
   
  const type = node.nodeType
  if (type == 1) {
   
    // node为Element类型
    const el = node as Element

    let exp: string | null

    if ((exp = checkAttr(el, 'v-if'))) {
   
      return _if(el, exp, ctx) // 返回最近一个没有`v-else-if`或`v-else`的兄弟节点
    }
  }
}
// 文件 ./src/directives/if.ts

interface Branch {
   
  exp?: string | null // 该分支逻辑运算表达式
  el: Element // 该分支对应的模板元素,每次渲染时会以该元素为模板通过cloneNode复制一个实例插入到DOM树中
}

export const _if = (el: Element, exp: string, ctx: Context) => {
   
  const parent = el.parentElement!
  /* 锚点元素,由于v-if、v-else-if和v-else标识的元素可能在某个状态下都不位于DOM树上,
   * 因此通过锚点元素标记插入点的位置信息,当状态发生变化时则可以将目标元素插入正确的位置。
   */
  const anchor = new Comment('v-if')
  parent.insertBefore(anchor, el)

  // 逻辑分支,并将v-if标识的元素作为第一个分支
  const branches: Branch[] = [
    {
   
      exp, 
      el
    }
  ]

  /* 定位v-else-if和v-else元素,并推入逻辑分支中
   * 这里没有控制v-else-if和v-else的出现顺序,因此我们可以写成
   * <span v-if="status=0"></span><span v-else></span><span v-else-if="status === 1"></span>
   * 但效果为变成<span v-if="status=0"></span><span v-else></span>,最后的分支永远没有机会匹配。
   */
  let elseEl: Element | null
  let elseExp: string | null
  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值