vue源码阅读之parser

parser目录下包含如下几个文件,核心文件在于path.js。

  • directive.js:

    parseDirective
    
  • expression.js:

      parseExpression
    
  • path.js:

     getPath, 
     setPath, 
     parsePath
    
  • template.js:

    parseTemplate,
    cloneNode, 
    parseTemplate
    
  • text.js:
    parseText,
    tokensToExp,
    compileRegex

parser工作流程图

函数解读:

  • parseDirective: 判断表达式中是否存在过滤器,如果存在提取出来
  • parseExpression: 负责将表达式添加getter 和 setter方法
  • makeGetterFn:将简单字符串表达式添加scope作用域并转化为function返回
  • compileGetter:将复杂字符串表达式进行正则替换并在变量后添加scope作用域
    例: status == "todo" 返回 scope.status == "todo"
  • compileSetter:将parse函数生成数组遍历并将最后一个属性作为最后的key

[path.js] 通过parse函数对 a.b.c 进行逐个字节扫描,最终获得['a','b','c',raw:'a.b.c']数组

// actions
var APPEND = 0  // 拼接字符在keystr后
var PUSH = 1  // 将keystr添加到keys数组中
var INC_SUB_PATH_DEPTH = 2  // 拼接当前字符在keystr后,并且深度+1
var PUSH_SUB_PATH = 3   // 如果深度大于0将状态置回IN_SUB_PATH,相反将keystr添加到keys数组

// states
var BEFORE_PATH = 0  // 初始状态
var IN_PATH = 1   //  遇到空格 和 ]
var BEFORE_IDENT = 2  // 遇到' . ' 或 空格时
var IN_IDENT = 3  //  遇到字符串数字时
var IN_SUB_PATH = 4  //  遇到 ' 或 " 或 [ 时
var IN_SINGLE_QUOTE = 5  //  遇到 '
var IN_DOUBLE_QUOTE = 6  //  遇到 "
var AFTER_PATH = 7  //  结束状态
var ERROR = 8  //  报错

var pathStateMachine = []   // 状态 + 动作  管理仓库

pathStateMachine[BEFORE_PATH] = {
  'ws': [BEFORE_PATH],
  'ident': [IN_IDENT, APPEND],
  '[': [IN_SUB_PATH],
  'eof': [AFTER_PATH]
}

pathStateMachine[IN_PATH] = {
  'ws': [IN_PATH],
  '.': [BEFORE_IDENT],
  '[': [IN_SUB_PATH],
  'eof': [AFTER_PATH]
}

pathStateMachine[BEFORE_IDENT] = {
  'ws': [BEFORE_IDENT],
  'ident': [IN_IDENT, APPEND]
}

pathStateMachine[IN_IDENT] = {
  'ident': [IN_IDENT, APPEND],
  '0': [IN_IDENT, APPEND],
  'number': [IN_IDENT, APPEND],
  'ws': [IN_PATH, PUSH],
  '.': [BEFORE_IDENT, PUSH],
  '[': [IN_SUB_PATH, PUSH],
  'eof': [AFTER_PATH, PUSH]
}

pathStateMachine[IN_SUB_PATH] = {
  "'": [IN_SINGLE_QUOTE, APPEND],
  '"': [IN_DOUBLE_QUOTE, APPEND],
  '[': [IN_SUB_PATH, INC_SUB_PATH_DEPTH],
  ']': [IN_PATH, PUSH_SUB_PATH],
  'eof': ERROR,
  'else': [IN_SUB_PATH, APPEND]
}

pathStateMachine[IN_SINGLE_QUOTE] = {
  "'": [IN_SUB_PATH, APPEND],
  'eof': ERROR,
  'else': [IN_SINGLE_QUOTE, APPEND]
}

pathStateMachine[IN_DOUBLE_QUOTE] = {
  '"': [IN_SUB_PATH, APPEND],
  'eof': ERROR,
  'else': [IN_DOUBLE_QUOTE, APPEND]
}

/* 
   开始状态下当遇到对应字符会将状态变化成对应状态并执行对应动作 
   我们以 a["b"].c 为线索进入parse函数并进行逐个字节扫描
   index=0时: 
   char='a'  mode=BEFORE_PATH  pathStateMachine[BEFORE_PATH]['ident']=[IN_IDENT, APPEND]   newchar="a",keys=[]
   index=1时 :
   char='['  mode=IN_IDENT pathStateMachine[IN_IDENT]['[']=[IN_SUB_PATH, PUSH]  newchar=undefined,keys=['a']
   index=2时 :
   char='"'  mode=IN_SUB_PATH pathStateMachine[IN_SUB_PATH]['"']=[IN_DOUBLE_QUOTE,APPEND]  newchar='"',keys=['a']
   index=3时 :
   char='b' mode=IN_DOUBLE_QUOTE pathStateMachine[IN_DOUBLE_QUOTE]['ident']= pathStateMachine[IN_DOUBLE_QUOTE]['else'] = [IN_DOUBLE_QUOTE,APPEND]  newchar='"b',keys=['a']
   index=4时 :
   char='"'  mode=IN_DOUBLE_QUOTE pathStateMachine[IN_DOUBLE_QUOTE]['"']=[IN_SUB_PATH,APPEND]  newchar='"b"',keys=['a']
   index=5时 :
   char="]" mode=IN_SUB_PATH pathStateMachine[IN_SUB_PATH][']']=[IN_PATH,PUSH_SUB_PATH]  newchar=undefined,keys=['a','b']
   index=6时 :
   char="."  mode=IN_PATH pathStateMachine[IN_PATH]['.']=[BEFORE_IDENT]  newchar=undefined,keys=['a','b']
   index=7时 :
   char="c"  mode=BEFORE_IDENT pathStateMachine[BEFORE_IDENT]['indent']=[IN_IDENT,APPEND] newchar='c',keys=['a','b']
   index=8时 :
   char=undefined mode=IN_IDENT pathStateMachine[IN_IDENT]['eof']=[AFTER_PATH,PUSH] newchar=undefined,keys=['a','b','c']

    最终循环完成当状态变为AFTER_PATH时,keys = ['a','b','c',raw:'a["b"].c']
*/

function parse (path) {
    var keys = []    
    var index = -1
    var mode = BEFORE_PATH
    var subPathDepth = 0
    var c, newChar, key, type, transition, action, typeMap
    var actions = []
    actions[PUSH] = function () {
        if (key !== undefined) {
            keys.push(key)
            key = undefined
        }
    }
    actions[APPEND] = function () {
        if (key === undefined) {
            key = newChar
        } else {
            key += newChar
        }
    }
    actions[INC_SUB_PATH_DEPTH] = function () {
         actions[APPEND]()
         subPathDepth++
    }
    actions[PUSH_SUB_PATH] = function () {
        if (subPathDepth > 0) {
            subPathDepth--
            mode = IN_SUB_PATH
            actions[APPEND]()
        } else {
            subPathDepth = 0
            key = formatSubPath(key)
            if (key === false) {
                return false
            } else {
                actions[PUSH]()
            }
        }
    }
    function maybeUnescapeQuote () {
          var nextChar = path[index + 1]
          if ((mode === IN_SINGLE_QUOTE && nextChar === "'") ||
          (mode === IN_DOUBLE_QUOTE && nextChar === '"')) {
              index++
              newChar = '\\' + nextChar
              actions[APPEND]()
              return true
          }
     }
     while (mode != null) {
          index++
          c = path[index]
          if (c === '\\' && maybeUnescapeQuote()) {
              continue
          }
          type = getPathCharType(c)
          typeMap = pathStateMachine[mode]
          transition = typeMap[type] || typeMap['else'] || ERROR
          if (transition === ERROR) {
              return // parse error
          }
          mode = transition[0]
          action = actions[transition[1]]
          if (action) {
              newChar = transition[2]
              newChar = newChar === undefined
              ? c
              : newChar
              if (action() === false) {
                    return
              }
          }
          if (mode === AFTER_PATH) {
               keys.raw = path
               return keys
          }
      }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容