代码生成:从AST到render函数

0 阅读6分钟

在前几篇文章中,我们学习了代码编译--转成--生成的过程。今天,我们将聚焦于指令系统——这个 Vue 中强大的声明式功能。从内置指令(v-if、v-for、v-model)到自定义指令,我们将深入它们的编译原理和运行时实现。

前言:指令的本质

指令是 Vue 模板中带有 v- 前缀的特殊属性。它本质上是一种声明式的语法糖,让我们能够在模板中直接操作 DOM 元素。

<!-- 使用指令 -->
<input v-model="message" />
<div v-if="visible">条件渲染</div>
<div v-custom:arg.modifier="value">自定义指令</div>

指令的注册方式

全局注册

const app = createApp(App);

app.directive('focus', {
  mounted(el) {
    el.focus();
  }
});

app.directive('color', {
  mounted(el, binding) {
    el.style.color = binding.value;
  },
  updated(el, binding) {
    el.style.color = binding.value;
  }
});

局部注册

export default {
  directives: {
    focus: {
      mounted(el) {
        el.focus();
      }
    },
    color: {
      mounted(el, binding) {
        el.style.color = binding.value;
      },
      updated(el, binding) {
        el.style.color = binding.value;
      }
    }
  }
}

import { directive } from 'vue';

export default {
  setup() {
    const vFocus = {
      mounted(el) {
        el.focus();
      }
    };
    
    return { vFocus };
  }
}

组件注册原理

// 指令注册的内部实现
function createDirective(name, definition) {
  // 规范化指令定义
  if (typeof definition === 'function') {
    // 函数简写形式
    definition = {
      mounted: definition,
      updated: definition
    };
  }
  
  return {
    name,
    ...definition
  };
}

// 全局注册表
const globalDirectives = new Map();

app.directive = function(name, definition) {
  if (definition === undefined) {
    // 获取指令
    return globalDirectives.get(name);
  } else {
    // 注册指令
    globalDirectives.set(name, createDirective(name, definition));
    return this;
  }
};

指令生命周期钩子

完整的钩子函数

const myDirective = {
  // 在绑定元素的 attribute 前
  // 或事件监听器应用前调用
  created(el, binding, vnode) {
    console.log('created', binding);
  },
  
  // 在元素被插入到 DOM 前调用
  beforeMount(el, binding, vnode) {
    console.log('beforeMount', binding);
  },
  
  // 在绑定元素的父组件
  // 及他自己的所有子节点都挂载完成后调用
  mounted(el, binding, vnode) {
    console.log('mounted', binding);
    el.focus();
  },
  
  // 在包含组件的 VNode 更新前调用
  beforeUpdate(el, binding, vnode, prevVnode) {
    console.log('beforeUpdate', binding);
  },
  
  // 在包含组件的 VNode 及其子组件的 VNode 更新后调用
  updated(el, binding, vnode, prevVnode) {
    console.log('updated', binding);
  },
  
  // 在绑定元素的父组件卸载前调用
  beforeUnmount(el, binding, vnode) {
    console.log('beforeUnmount', binding);
  },
  
  // 在绑定元素的父组件卸载后调用
  unmounted(el, binding, vnode) {
    console.log('unmounted', binding);
  }
};

binding 对象的属性

const binding = {
  value: 'directive value',        // 指令绑定的值
  oldValue: 'old value',            // 更新前的值
  arg: 'argName',                   // 指令参数
  modifiers: {                       // 修饰符对象
    prevent: true,
    stop: true
  },
  instance: componentInstance,       // 组件实例
  dir: directiveDefinition,          // 指令定义对象
  // 在 Vue 3.4+ 中新增
  modifiersKeys: ['prevent', 'stop'] // 修饰符数组
};

组件钩子函数的调用时机

组件钩子函数的调用时机

编译阶段的指令处理

指令的 AST 表示

我们来看一个比较复杂的自定义指令的例子:

<div v-custom:arg.mod1.mod2="value"></div>

这个例子对应的 AST 节点如下:

const elementNode = {
  type: 'Element',
  tag: 'div',
  props: [
    // 普通属性
    { name: 'class', value: 'container' },
    // 指令
    {
      type: 'Directive',
      name: 'custom',
      arg: 'arg',
      modifiers: ['mod1', 'mod2'],
      value: 'value',
      exp: {
        type: 'Expression',
        content: 'value'
      }
    }
  ]
};

指令的编译转换

/**
 * 指令转换插件
 */
const transformDirective = (node, context) => {
  if (node.type !== 'Element') return;
  
  if (!node.props) node.props = [];
  
  // 收集指令
  const directives = [];
  
  for (let i = node.props.length - 1; i >= 0; i--) {
    const prop = node.props[i];
    
    if (prop.type === 'Directive') {
      directives.push(prop);
      node.props.splice(i, 1); // 从props中移除
    }
  }
  
  if (directives.length === 0) return;
  
  // 为节点添加指令信息
  node.directives = directives.map(dir => ({
    name: dir.name,
    arg: dir.arg,
    modifiers: dir.modifiers,
    value: dir.value,
    exp: dir.exp
  }));
};

/**
 * 内置指令的转换
 */
const transformBuiltInDirectives = (node, context) => {
  if (!node.directives) return;
  
  for (const dir of node.directives) {
    switch (dir.name) {
      case 'if':
        transformVIf(node, dir, context);
        break;
      case 'for':
        transformVFor(node, dir, context);
        break;
      case 'model':
        transformVModel(node, dir, context);
        break;
      case 'show':
        transformVShow(node, dir, context);
        break;
      case 'on':
        transformVOn(node, dir, context);
        break;
      case 'bind':
        transformVBind(node, dir, context);
        break;
      // 自定义指令会保留,运行时处理
    }
  }
};

指令的代码生成

/**
 * 生成指令的运行时代码
 */
const genDirective = (dir, context) => {
  const { name, arg, modifiers, value } = dir;
  
  // 处理参数
  const argStr = arg ? `'${arg}'` : 'null';
  
  // 处理修饰符
  const modifiersObj = {};
  if (modifiers) {
    for (const mod of modifiers) {
      modifiersObj[mod] = true;
    }
  }
  
  // 生成指令对象
  return {
    name: `'${name}'`,
    value: `() => ${value}`,
    arg: argStr,
    modifiers: JSON.stringify(modifiersObj)
  };
};

/**
 * 生成节点上的所有指令
 */
const genDirectives = (node, context) => {
  if (!node.directives || node.directives.length === 0) return '';
  
  const dirs = node.directives.map(dir => genDirective(dir, context));
  
  return `directives: [${dirs.map(d => `{${Object.entries(d).map(([k, v]) => `${k}: ${v}`).join(', ')}}`).join(', ')}]`;
};

运行时的指令调用

指令调度器

/**
 * 运行时指令管理器
 */
class DirectiveManager {
  constructor() {
    this.directives = new Map(); // 全局指令
    this.instances = new WeakMap(); // 元素上的指令实例
  }
  
  /**
   * 注册指令
   */
  register(name, definition) {
    this.directives.set(name, definition);
  }
  
  /**
   * 获取指令定义
   */
  get(name) {
    return this.directives.get(name);
  }
  
  /**
   * 在元素上应用指令
   */
  applyDirectives(el, vnode) {
    const { directives } = vnode;
    if (!directives) return;
    
    const instances = [];
    
    for (const dir of directives) {
      const definition = this.get(dir.name);
      if (!definition) {
        console.warn(`指令 ${dir.name} 未注册`);
        continue;
      }
      
      // 创建指令实例
      const instance = {
        dir: definition,
        binding: this.createBinding(dir, vnode),
        vnode
      };
      
      instances.push(instance);
      
      // 调用 created 钩子
      if (definition.created) {
        definition.created(el, instance.binding, vnode);
      }
    }
    
    this.instances.set(el, instances);
  }
  
  /**
   * 创建 binding 对象
   */
  createBinding(dir, vnode) {
    return {
      value: dir.value ? dir.value() : undefined,
      oldValue: undefined,
      arg: dir.arg,
      modifiers: dir.modifiers || {},
      instance: vnode.component,
      dir: this.get(dir.name)
    };
  }
  
  /**
   * 更新指令
   */
  updateDirectives(oldVNode, newVNode) {
    const el = newVNode.el;
    const oldInstances = this.instances.get(el) || [];
    const newDirectives = newVNode.directives || [];
    
    // 创建新实例的映射
    const newInstances = [];
    const newDirMap = new Map();
    
    for (const dir of newDirectives) {
      newDirMap.set(dir.name, dir);
    }
    
    // 更新现有指令
    for (const oldInstance of oldInstances) {
      const newDir = newDirMap.get(oldInstance.dir.name);
      
      if (newDir) {
        // 指令仍然存在,更新 binding
        const oldBinding = oldInstance.binding;
        const newBinding = this.createBinding(newDir, newVNode);
        newBinding.oldValue = oldBinding.value;
        
        // 调用 beforeUpdate
        if (oldInstance.dir.beforeUpdate) {
          oldInstance.dir.beforeUpdate(el, newBinding, newVNode, oldInstance.vnode);
        }
        
        // 更新实例
        oldInstance.binding = newBinding;
        oldInstance.vnode = newVNode;
        newInstances.push(oldInstance);
        
        newDirMap.delete(oldInstance.dir.name);
      } else {
        // 指令被移除,调用 beforeUnmount
        if (oldInstance.dir.beforeUnmount) {
          oldInstance.dir.beforeUnmount(el, oldInstance.binding, oldInstance.vnode);
        }
      }
    }
    
    // 添加新指令
    for (const [name, dir] of newDirMap) {
      const definition = this.get(name);
      if (!definition) continue;
      
      const instance = {
        dir: definition,
        binding: this.createBinding(dir, newVNode),
        vnode: newVNode
      };
      
      // 调用 created
      if (definition.created) {
        definition.created(el, instance.binding, newVNode);
      }
      
      newInstances.push(instance);
    }
    
    this.instances.set(el, newInstances);
  }
  
  /**
   * 触发指令钩子
   */
  invokeHook(el, hookName, ...args) {
    const instances = this.instances.get(el);
    if (!instances) return;
    
    for (const instance of instances) {
      const hook = instance.dir[hookName];
      if (hook) {
        hook(el, instance.binding, ...args);
      }
    }
  }
}

// 创建全局指令管理器
const directiveManager = new DirectiveManager();

与渲染器的集成

/**
 * 在渲染器中集成指令
 */
class Renderer {
  patch(oldVNode, newVNode, container) {
    // ... 其他patch逻辑
    
    if (oldVNode && newVNode && oldVNode.el === newVNode.el) {
      // 更新指令
      directiveManager.updateDirectives(oldVNode, newVNode);
    }
  }
  
  mountElement(vnode, container, anchor) {
    const el = document.createElement(vnode.type);
    vnode.el = el;
    
    // 在挂载前调用指令钩子
    directiveManager.applyDirectives(el, vnode);
    
    // ... 其他挂载逻辑
    
    // 挂载后调用 mounted
    directiveManager.invokeHook(el, 'mounted');
  }
  
  unmount(vnode) {
    const el = vnode.el;
    
    // 调用 beforeUnmount
    directiveManager.invokeHook(el, 'beforeUnmount', vnode);
    
    // ... 卸载逻辑
    
    // 调用 unmounted
    directiveManager.invokeHook(el, 'unmounted');
  }
}

内置指令的编译实现

常见内置指令

内置指令编译处理运行时示例
v-if转为条件表达式条件渲染<div v-if="show">
v-for转为renderList循环渲染<li v-for="item in list">
v-model拆分为value+事件双向绑定<input v-model="text">
v-show转为style控制切换display<div v-show="visible">
v-on转为事件绑定事件监听<button @click="fn">
v-bind转为属性绑定属性更新<div :class="cls">
自定义指令保留指令信息调用钩子<div v-custom>

v-if 的编译

function transformVIf(node, dir, context) {
  // 将元素转换为条件节点
  node.type = 'Conditional';
  node.condition = dir.value;
  node.consequent = node;
  
  // 查找相邻的 v-else-if 和 v-else
  let current = node;
  while (current.next) {
    const nextNode = current.next;
    const elseDir = nextNode.directives?.find(d => d.name === 'else-if' || d.name === 'else');
    
    if (elseDir) {
      if (elseDir.name === 'else-if') {
        // 转换为条件分支
        current.alternate = {
          type: 'Conditional',
          condition: elseDir.value,
          consequent: nextNode
        };
        current = current.alternate;
      } else {
        // v-else
        current.alternate = nextNode;
      }
      
      // 移除指令标记
      nextNode.directives = nextNode.directives?.filter(d => d.name !== 'else-if' && d.name !== 'else');
    } else {
      break;
    }
  }
}

/**
 * 生成 v-if 代码
 */
function genVIf(node) {
  if (node.type !== 'Conditional') return;
  
  let code = `ctx.${node.condition} ? `;
  code += genNode(node.consequent);
  code += ' : ';
  
  if (node.alternate) {
    if (node.alternate.type === 'Conditional') {
      code += genVIf(node.alternate);
    } else {
      code += genNode(node.alternate);
    }
  } else {
    code += 'null';
  }
  
  return code;
}

v-show 的编译

function transformVShow(node, dir, context) {
  // v-show 只是添加 style 控制
  if (!node.props) node.props = [];
  
  const styleProp = node.props.find(p => p.name === 'style');
  
  if (styleProp) {
    // 合并现有 style
    styleProp.value = `[${styleProp.value}, ctx.${dir.value} ? null : { display: 'none' }]`;
  } else {
    // 添加 style 属性
    node.props.push({
      name: 'style',
      value: `ctx.${dir.value} ? null : { display: 'none' }`
    });
  }
  
  // 移除 v-show 指令
  node.directives = node.directives?.filter(d => d.name !== 'show');
}

/**
 * 生成 v-show 代码(在 props 中体现)
 */
function genVShow(node) {
  // v-show 已经在 props 中处理,这里不需要额外生成
  return genNode(node);
}

v-model 的编译

function transformVModel(node, dir, context) {
  const value = dir.value;
  const modifiers = dir.modifiers || [];
  
  // 根据元素类型生成不同的事件和属性
  let propName = 'modelValue';
  let eventName = 'onUpdate:modelValue';
  
  if (node.tag === 'input') {
    if (modifiers.includes('number')) {
      // v-model.number
      return genNumberModel(value);
    } else if (modifiers.includes('trim')) {
      // v-model.trim
      return genTrimModel(value);
    }
  } else if (node.tag === 'select') {
    propName = 'modelValue';
    eventName = 'onUpdate:modelValue';
  } else if (node.tag === 'textarea') {
    propName = 'modelValue';
    eventName = 'onUpdate:modelValue';
  }
  
  // 添加 props
  if (!node.props) node.props = [];
  
  // 添加 value 绑定
  node.props.push({
    name: propName,
    value: `ctx.${value}`
  });
  
  // 添加事件绑定
  node.props.push({
    name: eventName,
    value: genUpdateHandler(value, modifiers)
  });
}

/**
 * 生成更新处理器
 */
function genUpdateHandler(value, modifiers) {
  let handler = `$event => ctx.${value} = $event`;
  
  if (modifiers.includes('number')) {
    handler = `$event => ctx.${value} = parseFloat($event)`;
  } else if (modifiers.includes('trim')) {
    handler = `$event => ctx.${value} = $event.trim()`;
  }
  
  if (modifiers.includes('lazy')) {
    handler = handler.replace('$event', '$event.target.value');
  }
  
  return handler;
}

/**
 * 生成数字输入模型
 */
function genNumberModel(value) {
  return {
    type: 'Directive',
    name: 'bind',
    arg: 'value',
    value: `ctx.${value}`
  }, {
    type: 'Directive',
    name: 'on',
    arg: 'input',
    value: `$event => ctx.${value} = $event.target.value ? parseFloat($event.target.value) : ''`
  };
}

/**
 * 生成修剪模型
 */
function genTrimModel(value) {
  return {
    type: 'Directive',
    name: 'bind',
    arg: 'value',
    value: `ctx.${value}`
  }, {
    type: 'Directive',
    name: 'on',
    arg: 'blur',
    value: `$event => ctx.${value} = $event.target.value.trim()`
  };
}

v-for 的编译

function transformVFor(node, dir, context) {
  // 解析 v-for 表达式 "item in list"
  const match = dir.value.match(/(.*?) in (.*)/);
  if (!match) return;
  
  const [, alias, source] = match;
  
  // 转换为 For 节点
  node.type = 'For';
  node.source = source.trim();
  node.alias = alias.trim();
  node.children = node.children || [];
  
  // 添加 key 处理
  const keyProp = node.props?.find(p => p.name === 'key' || p.name === ':key');
  if (!keyProp) {
    // 自动添加 key 建议
    console.warn('v-for 应该提供 key 属性');
  }
  
  // 移除 v-for 指令
  node.directives = node.directives?.filter(d => d.name !== 'for');
}

/**
 * 生成 v-for 代码
 */
function genVFor(node) {
  if (node.type !== 'For') return;
  
  const { source, alias, children } = node;
  
  return `renderList(ctx.${source}, (${alias}, index) => {
    return ${genNode(children[0])}
  })`;
}

自定义指令的编译处理

自定义指令的保留

/**
 * 处理自定义指令
 */
function transformCustomDirective(node, context) {
  if (!node.directives) return;
  
  // 保留自定义指令,运行时处理
  node.customDirectives = node.directives.filter(dir => {
    return !['if', 'for', 'model', 'show', 'on', 'bind'].includes(dir.name);
  });
  
  // 移除已处理的指令
  node.directives = node.directives.filter(dir => {
    return ['if', 'for', 'model', 'show', 'on', 'bind'].includes(dir.name);
  });
}

/**
 * 生成自定义指令代码
 */
function genCustomDirectives(node, context) {
  if (!node.customDirectives?.length) return '';
  
  const dirs = node.customDirectives.map(dir => {
    const { name, arg, modifiers, value } = dir;
    
    return {
      name: `'${name}'`,
      value: `() => ${value}`,
      arg: arg ? `'${arg}'` : 'null',
      modifiers: JSON.stringify(modifiers || {})
    };
  });
  
  return `directives: [${dirs.map(d => 
    `{${Object.entries(d).map(([k, v]) => `${k}: ${v}`).join(', ')}}`
  ).join(', ')}]`;
}

指令的参数和修饰符

/**
 * 解析指令参数和修饰符
 */
function parseDirective(name) {
  // 例如:v-on:click.prevent.stop
  const parts = name.split(':');
  const dirName = parts[0];
  
  let arg = parts[1] || '';
  let modifiers = [];
  
  // 解析修饰符
  if (arg.includes('.')) {
    const argParts = arg.split('.');
    arg = argParts[0];
    modifiers = argParts.slice(1);
  }
  
  return {
    name: dirName,
    arg,
    modifiers
  };
}

/**
 * 生成修饰符处理代码
 */
function genModifiers(modifiers) {
  const obj = {};
  for (const mod of modifiers) {
    obj[mod] = true;
  }
  return JSON.stringify(obj);
}

事件修饰符的实现

常用事件修饰符

通用事件修饰符

修饰符作用典型使用场景
.stop阻止事件冒泡。防止点击一个内部的按钮意外触发了外层容器的点击事件。
.prevent阻止事件的默认行为。自定义表单提交逻辑,或自定义链接行为。
.capture使用事件捕获模式。当你希望父元素能比子元素更早地捕获到事件时使用。
.self只有当 event.target 是当前元素自身时,才触发事件处理函数。严格区分是点击了元素本身还是其内部子元素的场景。
.once事件将只会触发一次。一次性操作,如首次点击的引导、支付按钮等,防止重复提交。
.passive告诉浏览器你不想阻止事件的默认行为,从而提升性能。尤其适用于移动端的滚动事件(touchmove),能让滚动更流畅。提升滚动性能,通常用于改善移动端设备的滚屏体验。

注:修饰符可以串联使用,比如 @click.stop.prevent 会同时阻止冒泡和默认行为。但需要注意顺序,因为相关代码会按顺序生成。

按键修饰符

按键修饰符专门用于监听键盘事件,方便监听按下了哪个键。Vue 为最常用的按键提供了别名,我们可以直接使用:

  • .enter (回车键)
  • .tab (制表键)
  • .delete (捕获“删除”和“退格”键)
  • .esc (退出键)
  • .space (空格键)
  • .up / .down / .left / .right (方向键)

鼠标按键修饰符

指定由特定鼠标按键触发的事件:

  • .left (鼠标左键)
  • .right (鼠标右键)
  • .middle (鼠标滚轮键)

运行时的事件处理

/**
 * 运行时事件绑定处理
 */
class EventManager {
  constructor() {
    this.eventHandlers = new WeakMap();
  }
  
  /**
   * 绑定事件
   */
  addEventListener(el, eventName, handler, options) {
    // 解析事件选项
    let useCapture = false;
    let isPassive = false;
    
    if (eventName.includes('!')) {
      useCapture = true;
      eventName = eventName.replace('!', '');
    }
    
    if (eventName.includes('~')) {
      isPassive = true;
      eventName = eventName.replace('~', '');
    }
    
    const eventOptions = {
      capture: useCapture,
      passive: isPassive
    };
    
    // 存储事件处理器
    if (!this.eventHandlers.has(el)) {
      this.eventHandlers.set(el, new Map());
    }
    
    const handlers = this.eventHandlers.get(el);
    handlers.set(eventName, { handler, options: eventOptions });
    
    // 绑定事件
    el.addEventListener(eventName, handler, eventOptions);
  }
  
  /**
   * 更新事件
   */
  updateEventListener(el, eventName, newHandler) {
    const handlers = this.eventHandlers.get(el);
    if (!handlers) return;
    
    const old = handlers.get(eventName);
    if (old) {
      el.removeEventListener(eventName, old.handler, old.options);
    }
    
    if (newHandler) {
      this.addEventListener(el, eventName, newHandler.handler, newHandler.options);
    }
  }
}

手写实现:完整指令系统

/**
 * 完整指令编译器
 */
class DirectiveCompiler {
  constructor() {
    this.builtInDirectives = new Set(['if', 'for', 'model', 'show', 'on', 'bind']);
  }
  
  /**
   * 编译模板中的指令
   */
  compile(template) {
    // 1. 解析AST
    const ast = this.parse(template);
    
    // 2. 转换AST
    this.transform(ast);
    
    // 3. 生成代码
    const code = this.generate(ast);
    
    return code;
  }
  
  /**
   * 解析模板
   */
  parse(template) {
    // 简化的解析逻辑
    const ast = {
      type: 'Root',
      children: []
    };
    
    // 解析元素和指令
    const elementRegex = /<(\w+)([^>]*)>/g;
    const directiveRegex = /v-(\w+)(?::(\w+))?(?:\.(\w+))?="([^"]*)"/g;
    
    // ... 解析逻辑
    
    return ast;
  }
  
  /**
   * 转换AST
   */
  transform(node) {
    if (node.type === 'Element') {
      // 提取指令
      const directives = [];
      
      if (node.attributes) {
        for (const attr of node.attributes) {
          const match = attr.name.match(/^v-(\w+)(?::(\w+))?(?:\.([\w.]+))?$/);
          if (match) {
            const [_, name, arg, modifiersStr] = match;
            const modifiers = modifiersStr ? modifiersStr.split('.') : [];
            
            directives.push({
              name,
              arg,
              modifiers,
              value: attr.value,
              exp: {
                type: 'Expression',
                content: attr.value
              }
            });
            
            // 移除原始属性
            node.attributes = node.attributes.filter(a => a !== attr);
          }
        }
      }
      
      if (directives.length > 0) {
        node.directives = directives;
        
        // 处理内置指令
        for (const dir of directives) {
          if (this.builtInDirectives.has(dir.name)) {
            this.processBuiltInDirective(node, dir);
          }
        }
        
        // 保留自定义指令
        node.customDirectives = directives.filter(
          dir => !this.builtInDirectives.has(dir.name)
        );
      }
      
      // 递归处理子节点
      if (node.children) {
        for (const child of node.children) {
          this.transform(child);
        }
      }
    }
  }
  
  /**
   * 处理内置指令
   */
  processBuiltInDirective(node, dir) {
    switch (dir.name) {
      case 'if':
        this.processVIf(node, dir);
        break;
      case 'for':
        this.processVFor(node, dir);
        break;
      case 'model':
        this.processVModel(node, dir);
        break;
      case 'show':
        this.processVShow(node, dir);
        break;
      case 'on':
        this.processVOn(node, dir);
        break;
      case 'bind':
        this.processVBind(node, dir);
        break;
    }
  }
  
  /**
   * 处理 v-if
   */
  processVIf(node, dir) {
    node.type = 'Conditional';
    node.condition = dir.value;
    node.consequent = { ...node };
    delete node.consequent.directives;
    delete node.consequent.customDirectives;
  }
  
  /**
   * 处理 v-for
   */
  processVFor(node, dir) {
    const match = dir.value.match(/(.*?) in (.*)/);
    if (match) {
      node.type = 'For';
      node.alias = match[1].trim();
      node.source = match[2].trim();
      node.iterator = node;
      delete node.iterator.directives;
      delete node.iterator.customDirectives;
    }
  }
  
  /**
   * 处理 v-model
   */
  processVModel(node, dir) {
    if (!node.props) node.props = [];
    
    node.props.push({
      name: 'modelValue',
      value: `ctx.${dir.value}`
    });
    
    node.props.push({
      name: 'onUpdate:modelValue',
      value: this.genUpdateHandler(dir)
    });
  }
  
  /**
   * 处理 v-show
   */
  processVShow(node, dir) {
    if (!node.props) node.props = [];
    
    const styleProp = node.props.find(p => p.name === 'style');
    if (styleProp) {
      styleProp.value = `[${styleProp.value}, ctx.${dir.value} ? null : { display: 'none' }]`;
    } else {
      node.props.push({
        name: 'style',
        value: `ctx.${dir.value} ? null : { display: 'none' }`
      });
    }
  }
  
  /**
   * 处理 v-on
   */
  processVOn(node, dir) {
    if (!node.props) node.props = [];
    
    const eventName = dir.arg;
    let handler = `ctx.${dir.value}`;
    
    // 应用修饰符
    if (dir.modifiers) {
      handler = this.applyModifiers(handler, dir.modifiers);
    }
    
    node.props.push({
      name: `on${eventName.charAt(0).toUpperCase() + eventName.slice(1)}`,
      value: handler
    });
  }
  
  /**
   * 处理 v-bind
   */
  processVBind(node, dir) {
    if (!node.props) node.props = [];
    
    node.props.push({
      name: dir.arg,
      value: `ctx.${dir.value}`
    });
  }
  
  /**
   * 应用修饰符
   */
  applyModifiers(handler, modifiers) {
    for (const mod of modifiers) {
      switch (mod) {
        case 'stop':
          handler = `$event => { $event.stopPropagation(); ${handler}($event) }`;
          break;
        case 'prevent':
          handler = `$event => { $event.preventDefault(); ${handler}($event) }`;
          break;
        case 'once':
          handler = `once(${handler})`;
          break;
      }
    }
    return handler;
  }
  
  /**
   * 生成更新处理器
   */
  genUpdateHandler(dir) {
    let handler = `$event => ctx.${dir.value} = $event`;
    
    if (dir.modifiers) {
      if (dir.modifiers.includes('number')) {
        handler = `$event => ctx.${dir.value} = parseFloat($event)`;
      }
      if (dir.modifiers.includes('trim')) {
        handler = `$event => ctx.${dir.value} = $event.trim()`;
      }
      if (dir.modifiers.includes('lazy')) {
        handler = handler.replace('$event', '$event.target.value');
      }
    }
    
    return handler;
  }
  
  /**
   * 生成代码
   */
  generate(node) {
    if (!node) return 'null';
    
    switch (node.type) {
      case 'Root':
        return this.generateRoot(node);
      case 'Element':
        return this.generateElement(node);
      case 'Conditional':
        return this.generateConditional(node);
      case 'For':
        return this.generateFor(node);
      default:
        return 'null';
    }
  }
  
  /**
   * 生成元素代码
   */
  generateElement(node) {
    const parts = ['createVNode'];
    
    // 标签
    parts.push(`'${node.tag}'`);
    
    // 属性
    if (node.props) {
      const propsObj = {};
      for (const prop of node.props) {
        propsObj[prop.name] = prop.value;
      }
      parts.push(JSON.stringify(propsObj));
    } else {
      parts.push('null');
    }
    
    // 子节点
    if (node.children) {
      const children = node.children.map(child => this.generate(child));
      if (children.length === 1) {
        parts.push(children[0]);
      } else {
        parts.push(`[${children.join(', ')}]`);
      }
    } else {
      parts.push('null');
    }
    
    // 自定义指令
    if (node.customDirectives?.length) {
      const dirs = node.customDirectives.map(dir => ({
        name: `'${dir.name}'`,
        value: `() => ${dir.value}`,
        arg: dir.arg ? `'${dir.arg}'` : 'null',
        modifiers: JSON.stringify(dir.modifiers || {})
      }));
      
      parts.push(JSON.stringify({
        directives: dirs
      }));
    }
    
    return `createVNode(${parts.join(', ')})`;
  }
  
  /**
   * 生成条件节点
   */
  generateConditional(node) {
    return `${node.condition} ? ${this.generate(node.consequent)} : null`;
  }
  
  /**
   * 生成循环节点
   */
  generateFor(node) {
    return `renderList(ctx.${node.source}, (${node.alias}, index) => ${this.generate(node.iterator)})`;
  }
}

结语

理解指令系统,不仅帮助我们更好地使用内置指令,也能创建强大的自定义指令,提升开发效率。指令系统是 Vue 声明式编程的重要体现,它将 DOM 操作封装成声明式的语法,让开发者可以专注于业务逻辑。

对于文章中错误的地方或有任何疑问,欢迎在评论区留言讨论!