上一节获取到了DOM树的字符串,准备进入compile阶段:

    // Line-9326
function compileToFunctions(template,options,vm) {
// 获取配置参数
options = options || {}; // ... // Go!
var compiled = compile(template, options); // ...
}

  该函数接受两个参数,DOM树字符串、配置参数,如图:,进函数:

    // Line-9287
function compile(template, options) {
var finalOptions = Object.create(baseOptions);
var errors = [];
var tips = [];
finalOptions.warn = function(msg, tip$$1) {
(tip$$1 ? tips : errors).push(msg);
};
// 合并参数
if (options) {
if (options.modules) {
finalOptions.modules = (baseOptions.modules || []).concat(options.modules);
}
if (options.directives) {
finalOptions.directives = extend(
Object.create(baseOptions.directives),
options.directives
);
}
for (var key in options) {
if (key !== 'modules' && key !== 'directives') {
finalOptions[key] = options[key];
}
}
}
// 核心编译函数
var compiled = baseCompile(template, finalOptions);
errors.push.apply(errors, detectErrors(compiled.ast));
// 提示与报错属性添加
compiled.errors = errors;
compiled.tips = tips;
return compiled
}

  compile主要做了3件事:

  1、参数合并

  这里涉及到baseOptions与传进来的options,baseOptions是内置对象,与options合并后得到finalOptions作为参数传进第二步的函数。

    // Line-9529
var baseOptions = {
expectHTML: true,
modules: modules$1,
directives: directives$1,
isPreTag: isPreTag,
isUnaryTag: isUnaryTag,
mustUseProp: mustUseProp,
canBeLeftOpenTag: canBeLeftOpenTag,
isReservedTag: isReservedTag,
getTagNamespace: getTagNamespace,
staticKeys: genStaticKeys(modules$1)
};

  这个对象十分复杂,涉及的时候再做讲解。

  2、调用baseCompile函数

  该函数接受2个参数:字符串、参数对象。

    // Line-9261
function baseCompile(template, options) {
// 解析字符串为AST
var ast = parse(template.trim(), options);
// 优化
optimize(ast, options);
//
var code = generate(ast, options);
return {
ast: ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
}

  简单看一眼这个函数,3个调用都很简单暴力,后面一个一个讲解。

  3、添加提示、报错属性并返回compiled值

  将过程中出现的error与tips作为属性添加到compiled对象上,以便一次性输出。

  跑流程的话,主要还是看第二步,理一理DOM树字符串是如何被解析和编译的,来看parse这个函数吧! 

  这函数太长了,分两部分来: 

    // Line-7982
function parse(template, options) {
// 参数修正
warn$2 = options.warn || baseWarn;
// 这几个属性都是原型链上面的
platformGetTagNamespace = options.getTagNamespace || no;
platformMustUseProp = options.mustUseProp || no;
platformIsPreTag = options.isPreTag || no;
preTransforms = pluckModuleFunction(options.modules, 'preTransformNode');
transforms = pluckModuleFunction(options.modules, 'transformNode');
postTransforms = pluckModuleFunction(options.modules, 'postTransformNode');
// 这是自家的 值为undefined…
delimiters = options.delimiters; // 变量声明
var stack = [];
var preserveWhitespace = options.preserveWhitespace !== false;
var root;
var currentParent;
var inVPre = false;
var inPre = false;
var warned = false; // 功能函数
function warnOnce(msg) {
if (!warned) {
warned = true;
warn$2(msg);
}
} function endPre(element) {
if (element.pre) {
inVPre = false;
}
if (platformIsPreTag(element.tag)) {
inPre = false;
}
} parseHTML( /*...*/ );
return root
}

  首先是获取options参数的属性,其中大部分都是定义在原型上,即baseOptions,所以没什么讲的。唯一涉及的函数就是pluckModuleFunction,比较简单,看一下内容:

    // Line-5684
function pluckModuleFunction(modules, key) {
// 返回modules[key]组成的数组
return modules ?
modules.map(function(m) {
return m[key];
}).filter(function(_) {
return _;
}) : []
}

  简而言之,就是返回一个数组,内容是modules[key],这里返回空数组。

  第一部分没什么讲的,主要是声明一些变量,第二部分才是核心:

    // Line-7982
function parse(template, options) {
// ...part-1 parseHTML(template, {
warn: warn$2,
expectHTML: options.expectHTML,
isUnaryTag: options.isUnaryTag,
canBeLeftOpenTag: options.canBeLeftOpenTag,
shouldDecodeNewlines: options.shouldDecodeNewlines,
start: function start(tag, attrs, unary) {
/* code... */
}, end: function end() {
/* code... */
}, chars: function chars(text) {
/* code... */
}
});
return root
}

  这部分就是巨大的函数调用,第一个参数为DOM字符串,第二个参数是一个对象,包含多个属性与方法,属性内容如下:

    // Line-7578
// 自闭合标签
var isUnaryTag = makeMap(
'area,base,br,col,embed,frame,hr,img,input,isindex,keygen,' +
'link,meta,param,source,track,wbr'
);
// 可以省略闭合标签
var canBeLeftOpenTag = makeMap(
'colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr,source'
); // 当前浏览器是否会对换行转义
var shouldDecodeNewlines = inBrowser ? shouldDecode('\n', ' ') : false; function shouldDecode(content, encoded) {
var div = document.createElement('div');
div.innerHTML = "<div a=\"" + content + "\">";
return div.innerHTML.indexOf(encoded) > 0
}

  第一个expectHTML在baseOptions中是默认true的,其余几个是标签名数组与换行转义判断的集合。

  剩余三个方法包括start、end、chars,单独讲不好讲,在parseHTML方法边跑边说。

  暂时结束,parseHTML方法非常长,单独讲。

.6-Vue源码之AST(2)的更多相关文章

  1. 大白话Vue源码系列(03):生成AST

    阅读目录 AST 节点定义 标签的正则匹配 解析用到的工具方法 解析开始标签 解析结束标签 解析文本 解析整块 HTML 模板 未提及的细节 本篇探讨 Vue 根据 html 模板片段构建出 AST ...

  2. [Vue源码]一起来学Vue模板编译原理(一)-Template生成AST

    本文我们一起通过学习Vue模板编译原理(一)-Template生成AST来分析Vue源码.预计接下来会围绕Vue源码来整理一些文章,如下. 一起来学Vue双向绑定原理-数据劫持和发布订阅 一起来学Vu ...

  3. [Vue源码]一起来学Vue模板编译原理(二)-AST生成Render字符串

    本文我们一起通过学习Vue模板编译原理(二)-AST生成Render字符串来分析Vue源码.预计接下来会围绕Vue源码来整理一些文章,如下. 一起来学Vue双向绑定原理-数据劫持和发布订阅 一起来学V ...

  4. Vue源码后记-其余内置指令(3)

    其实吧,写这些后记我才真正了解到vue源码的精髓,之前的跑源码跟闹着玩一样. go! 之前将AST转换成了render函数,跳出来后,由于仍是字符串,所以调用了makeFunction将其转换成了真正 ...

  5. 大白话Vue源码系列(02):编译器初探

    阅读目录 编译器代码藏在哪 Vue.prototype.$mount 构建 AST 的一般过程 Vue 构建的 AST 题接上文,上回书说到,Vue 的编译器模块相对独立且简单,那咱们就从这块入手,先 ...

  6. 大白话Vue源码系列(03):生成render函数

    阅读目录 优化 AST 生成 render 函数 小结 本来以为 Vue 的编译器模块比较好欺负,结果发现并没有那么简单.每一种语法指令都要考虑到,处理起来相当复杂.上篇已经生成了 AST,本篇依然对 ...

  7. 大白话Vue源码系列(04):生成render函数

    阅读目录 优化 AST 生成 render 函数 小结 本来以为 Vue 的编译器模块比较好欺负,结果发现并没有那么简单.每一种语法指令都要考虑到,处理起来相当复杂.上篇已经生成了 AST,本篇依然对 ...

  8. 大白话Vue源码系列(05):运行时鸟瞰图

    阅读目录 Vue 实例的生命周期 实例创建 响应的数据绑定 挂载到 DOM 节点 结论 研究 runtime 一边 Vue 一边源码 初看 Vue 是 Vue 源码是源码 再看 Vue 不是 Vue ...

  9. 入口文件开始,分析Vue源码实现

    Why? 网上现有的Vue源码解析文章一搜一大批,但是为什么我还要去做这样的事情呢?因为觉得纸上得来终觉浅,绝知此事要躬行. 然后平时的项目也主要是Vue,在使用Vue的过程中,也对其一些约定产生了一 ...

  10. 入口开始,解读Vue源码(一)-- 造物创世

    Why? 网上现有的Vue源码解析文章一搜一大批,但是为什么我还要去做这样的事情呢?因为觉得纸上得来终觉浅,绝知此事要躬行. 然后平时的项目也主要是Vue,在使用Vue的过程中,也对其一些约定产生了一 ...

随机推荐

  1. 《Head First Java》读书笔记(1) - Java语言基础

    <Head First Java>(点击查看详情) 1.写在前面的话 这本书的知识点说实话感觉有点散乱,但是贵在其将文字转换成了生动和更容易接受的图片,大量的比喻让人感受到了知识点的有趣之 ...

  2. 一张图告诉你移动Web前端所有技术(工程化、预编译、自动化)

    你要的移动web前端都在这里! 大前端方向:移动Web前端.Native客户端.Node.js. 大前端框架:React.Vue.js.Koa 跨终端技术:HTML5.CSS 3.JavaScript ...

  3. 王者荣耀_KEY

    WZRY 为了排位赛的Cjj神,最近耗尽气力来打WZRY. Cjj神最近有N局预约的排位赛,其中第i局需要耗时Li的时间.因为浓浓的Gay情,Cjj神不能改变这些排位赛的的顺序.作为一个很有(mei) ...

  4. 我的第一个python web开发框架(1)——前言

    由于之前经验不是很丰富,写的C#系统太过复杂,所以一直想重写,但学的越多越觉得自己懂的越少,越觉的底气不足.所以一直不敢动手,在内心深处对自己讲,要静下心来认真学习,继续沉淀沉淀.这两年多以来找各种机 ...

  5. 深入理解String的关键点和方法

    String是Java开发中最最常见的,本篇博客针对String的原理和常用的方法,以及String的在开发中常见问题做一个整体性的概括整理.因为之前对String的特性做过一些分析,所以不在详细描述 ...

  6. 什么是PWM信号

    PWM信号脉宽调制PWM是开关型稳压电源中的术语.这是按稳压的控制方式分类的,除了PWM型,还有PFM型和PWM.PFM混合型.脉宽宽度调制式(PWM)开关型稳压电路是在控制电路输出频率不变的情况下, ...

  7. AngularJS 路由精分

    AngularJS 路由机制是由ngRoute模块提供,它允许我们将视图分解成布局和模板视图,根据url变化动态的将模板视图加载到布局中,从而实现单页面应用的页面跳转功能. AngularJS 路由允 ...

  8. vue脚手架使用swiper /引入js文件/引入css文件

    1.安装vue-cli 参考地址:https://github.com/vuejs/vue-cli 如果不使用严格语法需要在后三项打no:(加了挺头疼的,老是报错,但是对自己的代码规范性也是有很大的帮 ...

  9. 【转】 Python调用(运行)外部程序

    在Python中可以方便地使用os模块运行其他的脚本或者程序,这样就可以在脚本中直接使用其他脚本,或者程序提供的功能,而不必再次编写实现该功能的代码.为了更好地控制运行的进程,可以使用win32pro ...

  10. Python单元测试框架

    目录 概况 系统要求 使用PyUnit构建自己的测试 安装 测试用例介绍 创建一个简单测试用例 复用设置代码:创建固件 包含多个测试方法的测试用例类 将测试用例聚合成测试套件 嵌套测试用例 测试代码的 ...