Skip to content

Vue2.0原理-指令 #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
some-code opened this issue Jun 14, 2018 · 0 comments
Open

Vue2.0原理-指令 #15

some-code opened this issue Jun 14, 2018 · 0 comments
Labels

Comments

@some-code
Copy link
Owner

some-code commented Jun 14, 2018

指令是 模板解析 的续章,本文会尝试从源码的角度来理解 指令 是如何被提取和应用的。

指令的提取

指令的提取过程是在parse阶段进行的,在 parseHTML 方法中,会解析字符串模板为如下的单个ast对象

  <div class="app">
    hello {{ a }}
    <span v-for='i in 5'>{{i}}</span>
  </div>
  
被解析为如下文本

{
  "type": 1,
  "tag": "div",
  "attrsList": [
    {
      "name": "class",
      "value": "app"
    }
  ],
  "attrsMap": {
    "class": "app"
  },
  "children": []
}

其中的 <span v-for='i in 5'>{{i}}</span> 被解析为如下文本

{
  "type": 1,
  "tag": "span",
  "attrsList": [
    {
      "name": "v-for",
      "value": "i in 5"
    }
  ],
  "attrsMap": {
    "v-for": "i in 5"
  },
  "parent": {
    "type": 1,
    "tag": "div",
    "attrsList": [],
    "attrsMap": {
      "class": "app"
    },
    "children": [
      {
        "type": 2,
        "expression": "\"\\n    hello \"+_s(a)+\"\\n    \"",
        "tokens": [
          "\n    hello ",
          {
            "@binding": "a"
          },
          "\n    "
        ],
        "text": "\n    hello {{ a }}\n    "
      }
    ],
    "plain": false,
    "staticClass": "\"app\""
  },
  "children": []
}

通过提取为格式化的对象,就可以对单个的节点进行解析了

指令的解析

首先对 v-for , v-if , v-once 三个指令进行解析

// processFor 调用 parseFor,
// 通过 /([^]*?)\s+(?:in|of)\s+([^]*)/ 正则表达式match后,
// 将 v-for='i in 5' 解析为 {for: "5", alias: "i"} 对象
// 再把 {for: "5", alias: "i"} 与 element 对象合并
processFor(element)

processIf(element) // 同上 

processOnce(element) // 同上 

// processElement 是一个比较大的方法,
// 里面对 key ref slot component attrs进行了提取和解析
// 其中的 processAttrs 方法,会提取attrs里面的 v-bind(:) v-on(@)
// 构造成props, attrsMap, events, directives等对象
// 然后与 element 对象合并
// 值得一提的是 v-model 会被当做props,然后再次进入directives中处理
processElement(element) 

指令的处理

通过上面的步骤,Vue 提取出了 props, events, attrsMap , for 等指令对象,那么 Vue 又是如何去处理这些指令的呢? codegen/index.js 中有这样一些代码:

  ...
  if (el.key) {
    data += `key:${el.key},`
  }
  // ref
  if (el.ref) {
    data += `ref:${el.ref},`
  }
  ...
  if (el.attrs) {
    data += `attrs:{${genProps(el.attrs)}},`
  }
  if (el.props) {
    data += `domProps:{${genProps(el.props)}},`
  }
  if (el.events) {
    data += `${genHandlers(el.events, false, state.warn)},`
  }
  ...

上面这些代码会对每个属性进行处理,然后拼接为字符串

 比如这段代码
 <div class="app">
   hello {{ a }}
   <span v-for='i in 5' @click='a'>{{i}}</span>
   <input type="text"  v-model='a'>
 </div>
 
 会被处理为
 _c('div',{staticClass:"app"},[_v("\n    hello "+_s(a)+"\n    "),_l((5),function(i){return _c('span',{on:{"click":a}},[_v(_s(i))])}),_v(" "),_c('input',{directives:[{name:"model",rawName:"v-model",value:(a),expression:"a"}],attrs:{"type":"text"},domProps:{"value":(a)},on:{"input":function($event){if($event.target.composing)return;a=$event.target.value}}})],2)
 
 这段代码在 to-function.js 中,会被 createFunction 处理为一个匿名函数

剩下的事情,就交给 vm.$createElement 去生成vNode了。

@some-code some-code added the vue label Jun 14, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant