Vue原理学习(五)持续更新。。。

template模板是怎么通过Compile编译的

我们回头再来看Vue原理学习(一)中的这张图:

image

对于这张图,我们这次来研究complited到render function这一部分。
另外,我们同样再来看下面一段Vue代码:

<template>
  <div>hello</div>
</template>

这段Vue代码,使用了template语法,而我们本次讨论的就是 template模板是怎么通过Compile编译的?

前面我们也已经简单的提到过,Vue通过正则解析(parse)、优化(optimize),最终生成(generate)AST,转成render function

而这里,我们在详细的介绍下。

准备一个示例

首先我们准备一个示例,围绕这个示例来进行探讨学习。

<div :class="c" class="demo" v-if="isShow">
    <span v-for="item in sz">{{item}}</span>
</div>

字符串形式:

const html = '<div :class="c" class="demo" v-if="isShow"><span v-for="item in sz">{{item}}</span></div>';

对于AST,我们可以先看下究竟长什么样子。

{
  // 标签属性map,记录标签上都有哪些属性
  'attrsMap': {
    ':class': c,
    'class': 'demo',
    'v-if': 'isShow'
  },
  // 解析得到的:class
  ‘classBinding’: 'c',
  // v-if 解析
  'if': 'isShow',
  // v-if 的条件
  'ifConditions': [
    {
      'exp': 'isShow'
    }
  ],
  // 标签属性class,非绑定class
  'staticClass': 'demo',
  // 标签tag
  'tag': 'div',
  // 子节点数据
  'children': [
    {
      'attrsMap': {
         'v-for': "item in sz"
       },
       // for 循环参数,alis:循环项的别名, 也就是 xx in data 中的 xx
      'alias': 'item',
      // for 循环对象
      'for': 'sz',
      // for 循环是否已被处理标记
      'forProcessed': true,
      'tag': 'span',
      'children': [
        {
          /* 表达式,_s是一个转字符串的函数 */
          'expression': '_s(item)',
          'text': '{{item}}'
        }
      ]
    }
  ]
}

你会发现,和Virtual DOM 有相似的感觉,但是不要混淆,其作用不同。另外,如果你仔细的阅读Vue源码,会发现,上述代码描述,在源码中都存在,包括下面介绍的正则代码。

定义正则

前面提到了,parse的过程,就是用到了正则表达式进行检索查询处理。

const ncname = '[a-zA-Z_][\\w\\-\\.]*';
const singleAttrIdentifier = /([^\s"'<>/=]+)/
const singleAttrAssign = /(?:=)/
const singleAttrValues = [
  /"([^"]*)"+/.source,
  /'([^']*)'+/.source,
  /([^\s"'=<>`]+)/.source
]
const attribute = new RegExp(
  '^\\s*' + singleAttrIdentifier.source +
  '(?:\\s*(' + singleAttrAssign.source + ')' +
  '\\s*(?:' + singleAttrValues.join('|') + '))?'
)

const qnameCapture = '((?:' + ncname + '\\:)?' + ncname + ')'
const startTagOpen = new RegExp('^<' + qnameCapture)
const startTagClose = /^\s*(\/?)>/

const endTag = new RegExp('^<\\/' + qnameCapture + '[^>]*>')

const defaultTagRE = /\{\{((?:.|\n)+?)\}\}/g

const forAliasRE = /(.*?)\s+(?:in|of)\s+(.*)/

advance

这里我们介绍下,由于解析template采用的是循环进行字符串匹配的方式,也就是我们每次匹配解析完一段代码后,都要将已经匹配的代码去掉,头部指针重新指向接下来需要匹配的部分,直到没有可匹配项。

advance方法在Vue源码中存在定义,大概在8861行。

// 参数n就是每次匹配的位置,或者说每次匹配到的那段代码的长度
function advance(n) {
  index += n;
  html = html.substring(n);
}

当我们匹配完<div :class="c" class="demo" v-if="isShow">这段代代码后,指针将移动43个字符(因为长度等于43),此时,调用advancef方法:
advance(43)

parseHTML

parseHTML方法在Vue源码中也存在定义,大概在8734行。这里我们对其做了简化。

function parseHTML(html) {
  while(html) {
    let textEnd = html.indexOf('<');
    if (textEnd === 0) {
      if (html.match(endTag)) {
        //...process end tag
        // endTag: 前面定义的正则
        // 匹配到了结束标签 
        continue;
      }
      if (html.match(startTagOpen)) {
         //...process start tag
        // startTagOpen: 前面定义的正则
        // 匹配到了开始标签 
        continue;
      }
    } else {
      continue;
    }
  }
}

这里我们用到了前面定义的两个正则:endTagstartTagOpen。这里,我们把源码中的parseHTML方法做了简化,方便理解。总的来看,就是利用了while来循环解析template,用正则匹配结束和开始标签以及文本,分别来做不同的处理,直到整个template解析完成。

parseStartTag

到这里,我们大概了解了parseHTML的原理。但是前面我们也说了,parseHTML内部没有做任何处理,只是做了判断,直接continue了。

所以,这里的parseStartTag的作用就是用来解析开始标签的。比如<div :class="c" class="demo" v-if="isShow">

该方法大概存在与Vue源码中的8866行, 此处我们也直接拿过来用。

function parseStartTag() {
  const start = html.match(startTagOpen); // 匹配开始位置
  if (start) {
    // 成功匹配到了
    const match = {
      tagName: start[1], // 得到标签名
      attrs: [], // 用来存放标签内属性
      start: index // index: 全局变量,当前正则遍历的template字符位置
    };
    advance(start[0].length);
    let end, attr; // 变量初始化
    while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))){
      // 如果结束位置不等于标签的结束位置
      // 并且属性也不匹配
      // 说明该部分还没遍历完
      advance(attr[0].length);
      match.attrs.push({
        name: attr[1],
        value: attr[3]
      });
    }
    if (end) {
      match.unarySlash = end[1];
      advance(end[0].length);
      match.end = index;
      return match;
    }
  }
}

首先,我们利用了startTagOpen正则得到了标签的头部,拿到了tagName(标签名 start[1]),定义了attrs空数组,用来存放标签内属性。

const start = html.match(startTagOpen); 
const match = {
  tagName: start[1], // 得到标签名
  attrs: [], // 用来存放标签内属性
  start: index // index: 全局变量,当前正则遍历的template字符位置
};

接下来,我们使用了while,用startTagCloseattribute两个正则来匹配解析标签的结束和标签内的属性,一直循环到startTagClose,解析完内部所有属性为止。

stack

我们现在思考这样一个问题:标签是存在嵌套关系的,而我们解析是根据字符逐个解析的,这样就会导致嵌套关系的混乱,无法保存刚刚解析好的标签头。

为了解决这一问题,我们维护一个stack栈来保存已经解析好的标签头。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,656评论 18 139
  • 上期推荐非常及时,本来当晚写好了就发出来的,唯恐泄露天机,决定第二天8点多发出来,后来忙其他事务,居然忘记。第一期...
    观澜看彩阅读 656评论 4 2
  • 那天到办公室签外出条,和儿子小学一年级的数学老师、我的同事张老师攀谈几句,聊起儿子高中就读的学校,聊起她即将初中毕...
    爱如你我阅读 633评论 0 7
  • 想象你的样子,眼睛忽闪忽闪的,透亮的脸蛋,倔强的抿着嘴唇。天真如孩童,温婉如似水女子,倔强时如同决绝的小鹿,蹦跳着...
    LOVE玲媛阅读 67评论 0 0
  • 如果詹姆斯能设法率领这支并没有争冠实力的骑士夺下自己职业生涯第四座总冠军奖杯,那么这肯定是前所未有的成就。 但如果...
    zoneball阅读 295评论 0 0