Skip to content

Latest commit

 

History

History
161 lines (142 loc) · 5.84 KB

directives概述.md

File metadata and controls

161 lines (142 loc) · 5.84 KB

本篇文章,我们来聊一聊,Vue中的指令。Vue内置的指令有很多,包括v-textv-htmlv-showv-ifv-elsev-else-ifv-forv-onv-bindv-modelv-prev-cloakv-once。每个指令的用途,大家可以自行查看文档

因为指令都是添加在模板的标签上的,所以第一步都是要经过模板编译的洗礼。

	if (!inVPre) {
    processPre(element)
    if (element.pre) {
      inVPre = true
    }
  }
  if (platformIsPreTag(element.tag)) {
    inPre = true
  }
  if (inVPre) {
    processRawAttrs(element)
  } else {
    processFor(element)
    processIf(element)
    processOnce(element)
    processKey(element)

    // determine whether this is a plain element after
    // removing structural attributes
    element.plain = !element.key && !attrs.length

    processRef(element)
    processSlot(element)
    processComponent(element)
    for (let i = 0; i < transforms.length; i++) {
      transforms[i](element, options)
    }
    processAttrs(element)
  }

以上代码摘自src/compiler/parser/index.js文件,这是标签处理的流程。本篇文章不会涉及每个指令解析的细节,只是概述一下整体上的处理流程。

processPre是用于解析v-preprocessFor解析v-forprocessIf解析v-ifv-else-ifv-elseprocessOnce解析v-onceprocessKey解析keyprocessRef解析refprocessSlot解析slotprocessComponent解析component,剩下的指令和属性统一都交给了processAttrs处理,比如v-bindv-on、上面提到的其它指令以及普通属性等。

function processAttrs (el) {
  const list = el.attrsList
  let i, l, name, rawName, value, modifiers, isProp
  for (i = 0, l = list.length; i < l; i++) {
    name = rawName = list[i].name
    value = list[i].value
    // 指令
    if (dirRE.test(name)) {
      // mark element as dynamic
      el.hasBindings = true
      // modifiers
      modifiers = parseModifiers(name)
      if (modifiers) {
        name = name.replace(modifierRE, '')
      }
      if (bindRE.test(name)) { // v-bind
        name = name.replace(bindRE, '')
        ...
      } else if (onRE.test(name)) { // v-on
        ...
      } else { // normal directives
        ...
        addDirective(el, name, rawName, value, arg, modifiers)
        ...
      }
    } else {
      ...
    }
  }
}

processAttrs中会遍历所有的属性,整体上分为两部分,一种是指令,一种是普通属性。指令又分v-bindv-on和其它指令的处理。

v-bindv-on之后单独讲,我们来看通用指令的处理流程,在注释“normal directives”的代码块中,有一个主要的处理函数addDirective,它只是把指令相关信息存到了el.directives数组中。

export function addDirective (
  el: ASTElement,
  name: string,
  rawName: string,
  value: string,
  arg: ?string,
  modifiers: ?ASTModifiers
) {
  (el.directives || (el.directives = [])).push({ name, rawName, value, arg, modifiers })
}

以上是生成ast的处理,普通指令在生成render函数字符串时,会添加到data数据上,且在data的第一位。

function genData (el: ASTElement): string {
  let data = '{'
  // directives first.
  // directives may mutate the el's other properties before they are generated.
  const dirs = genDirectives(el)
  if (dirs) data += dirs + ','
  ...
  return data
}
function genDirectives (el: ASTElement): string | void {
  const dirs = el.directives
  if (!dirs) return
  let res = 'directives:['
  let hasRuntime = false
  let i, l, dir, needRuntime
  for (i = 0, l = dirs.length; i < l; i++) {
    dir = dirs[i]
    needRuntime = true
    const gen: DirectiveFunction = platformDirectives[dir.name] || baseDirectives[dir.name]
    if (gen) {
      // compile-time directive that manipulates AST.
      // returns true if it also needs a runtime counterpart.
      needRuntime = !!gen(el, dir, warn)
    }
    if (needRuntime) {
      hasRuntime = true
      res += `{name:"${dir.name}",rawName:"${dir.rawName}"${
        dir.value ? `,value:(${dir.value}),expression:${JSON.stringify(dir.value)}` : ''
      }${
        dir.arg ? `,arg:"${dir.arg}"` : ''
      }${
        dir.modifiers ? `,modifiers:${JSON.stringify(dir.modifiers)}` : ''
      }},`
    }
  }
  if (hasRuntime) {
    return res.slice(0, -1) + ']'
  }
}

platformDirectives是平台相关的一些指令处理,web端包括v-htmlv-modelv-textbaseDirectives包括v-bindv-cloak的处理。

如果我们自定义的指令,返回的genfalse,所以needRuntime返回的永远都是true,然后拼接好数据,添加到res上。对于上面这五个指令,只有v-model返回的是true,其它的指令不会添加到directive数组中在patch过程中处理。

对于添加到directives上的数据,像classstyle等处理方式一致,是在patch中通过添加到cbs中的钩子函数处理的。具体过程见src/core/vdom/modules/directives.js文件中。

export default {
  create: updateDirectives,
  update: updateDirectives,
  destroy: function unbindDirectives (vnode: VNodeWithData) {
    updateDirectives(vnode, emptyNode)
  }
}

这三个钩子函数,最终调用的都是updateDirectives方法,updateDirectives接受旧新两个vnode对象作为参数。

function updateDirectives (oldVnode: VNodeWithData, vnode: VNodeWithData) {
  if (oldVnode.data.directives || vnode.data.directives) {
    _update(oldVnode, vnode)
  }
}

如果新或旧vnodedata中有directives属性,则调用_update方法来进行处理。_update方法的主要操作,其实就是调用指令的各种钩子函数。具体讲解,见自定义指令