手机版

深入分析Vue源代码实例挂载和编译过程的实现思路

时间:2021-08-24 来源:互联网 编辑:宝哥软件园 浏览:

在开始本文之前,我们应该了解基于源代码的vue的两个版本,一个是仅运行时版本,另一个是runtime Plus编译器版本。两个版本的主要区别在于后者的源代码包含编译器。

什么是编译器,百度百科上面解释的是

简单来说,编译器就是把“一种语言(通常是高级语言)”翻译成“另一种语言(通常是低级语言)”的程序。现代编译器的主要工作流程是:源代码)预处理器)编译器)目标代码)链接器)可执行文件。

一般来说,编译器是将源代码提供给目标代码的工具。此外,vue的内置编译器实现了转换和编译功能。vue文件转换成可执行的javascript脚本。

3.1.1 Runtime + Compiler

完整的vue版本包括一个编译器,所以我们可以使用模板来编译模板。编译器会自动将模板编译成渲染函数。

//需要编译器版本new vue({ template : ' div { { hi } }/div ' })

3.1.2 Runtime Only

。对于没有编译器的纯运行时版本,需要传递编译后的render函数,如下所示。

//不需要编译器newvue ({render (h) {return h ('div),这个。嗨)})。显然,编译过程有一定的性能损失,而且由于添加了编译过程的代码,vue代码也比较大,所以我们可以借助webpack的vue-loader工具进行编译,将编译阶段和vue的构建分开,这样不仅优化了

3.2 挂载的基本思路

vue的安装过程比较复杂,所以我们通过流程图明确了基本的实现思路。

如果用一句话概括挂载的过程,可以描述为挂载组件,从渲染函数生成虚拟DOM,在更新视图时将虚拟DOM渲染成真实DOM。

详细过程如下:首先确定挂载的DOM元素,保证元素不能是html、body等节点。确定选项中是否有渲染属性(如果在运行时没有编译,则需要在初始化选项时传递渲染函数)。当存在渲染属性时,我们默认使用仅运行时版本,从而跳过模板编译阶段并调用真正的挂载函数$mount。另一方面,当我们传递一个模板时(即我们将使用运行时编译器版本,而不使用外部编译器),Vue源代码将首先进入编译阶段。这个阶段的核心是两个步骤,一个是将模板解析成抽象语法树,也就是我们经常听到的AST,另一个是根据给定的AST生成目标平台所需的代码,浏览器端就是前面提到的render函数。模板编译完成后,也将进入$mount阶段。真正的挂载过程是执行mountComponent方法,这个函数的核心是实例化一个渲染观察器,指定观察器的内容,在另一章讨论。只要我们知道渲染观察器的功能,一个是初始化时执行回调函数,另一个是当vm实例中监控的数据发生变化时执行回调函数。而这个回调函数就是updateComponent。该方法将通过虚拟机生成虚拟DOM。_render,最后通过vm将虚拟DOM转换为真实DOM。_更新。

接下来,我们从代码的角度来理解mount的实现思路,下面只提取mount骨架代码的描述。

//方法vue . prototype . $ mount=function(El,补水){El=浏览器中的El?查询(el) :未定义;//调用mountComponent方法挂载返回mountcomponent (this,El,hycing)};//在原型上缓存了$mount方法var mount=Vue.prototype. $ mount//重定义$mount,为有编译器和没有编译器的版本提供不同的包。最后,调用缓存原型上的$mount方法vue . prototype . $ mount=function(El,补水){//获取mount元素El=El query(El);//如果(El===document . body | | El===document . document element){ warn('不要将vue挂载到html或body-mount到普通元素,');返回this } var options=this。$ options//编译if(!选项。render){//用内部编译器编译模板}//调用缓存的$mount方法returnmount.call (this,El,补水)}//mountcomponent方法thin function mount component(VM,El,补水){//定义updateComponent方法,在watch回调时调用。update component=function(){//render function渲染成虚拟DOM,虚拟DOM渲染成真实DOM VM。_更新(虚拟机。_ render(),补水);};//实例化渲染watcher new watcher (VM,update component,noop,{})}

3.3 编译过程 - 模板编译成 render 函数

通过文章前半部分的学习,我们对Vue的挂载过程有了初步的了解。接下来,我们将从编译模板的过程开始。在阅读源代码时,发现模板的编译过程相当复杂,用很短的篇幅解释整个编译过程是不切实际的,所以本节剩下的内容就只简单介绍一下实现思路。

3.3.1 template的三种写法

模板有三种写法,即:

//1.熟悉的字符串模板var vm=new Vue({ el: '#app ',Template: 'div模板字符串/div'})//2。innerHTML模板div id=' app' div test1/div脚本类型=' x-template ' id=' test ' ptest/p/script/div var VM=new Vue({ El : ' # app ',Template:' # test'})//3。innerHTML模板div id=' app ' div test1/div span id=' test ' div class=' test2 ' test 2/div/span/Divar VM=new vue({ El : ' # app ',template 3360 document . queryselector(# test ')})对应于代码的三个不同分支。

var模板=options.templateIf (template) {//匹配字符串模板和选择器If的模板(template的类型===' string '){//匹配选择器的模板,选择器if前缀为' #' (template。charat(0)==' # '){//获取匹配元素/*伊斯坦布尔忽略if */if(!Template){ warn(“‘Template元素未找到或为empty :’(options . Template)),这);} }//匹配dom元素} else if (template.nodeType) {//获取匹配元素的innerhtml template=template . innerhtml;} else {//其他类型判断为非法传入{warn('无效模板选项: '模板,此);}返回this}} else if (el) {//如果没有传入模板,则默认使用el元素的根节点作为基本模板。template=GetOuthHTML(El);} X-Template模板一般用于非常大的模板或非常小的应用程序的演示,不建议在其他情况下使用它,因为它会将模板与组件的其他定义分开。

3.3.2 流程图解

vue源代码相当费解,其中涉及到更多的函数处理逻辑。在实现过程中,巧妙地运用部分函数的技巧,提取配置项处理和编译的核心逻辑。为了理解这个设计思路,我画了一个逻辑图来帮助理解。

3.3.3 逻辑解析

,即使有流程图,编译逻辑还是晦涩难懂。然后结合代码分析各个环节的执行过程。

var ref=compileToFunctions(模板,{ outputsourcerange : ' development '!=='production ',shouldedenewlines : shouldedenewlines,shouldedenewlineforhref : shouldedenewlineforhref,delimiters: options .分隔符,comments: options.comments},this);//将compileToFunction方法作为静态方法公开给Vue。compile=compiletofunctions这是编译的入口,也是Vue暴露的编译方法。CompileToFunctions需要传递三个参数:模板模板、编译配置选项和Vue实例。让我们大致了解一下配置中的几个默认选项

1.分隔符此选项可以更改纯文本插入的分隔符。没有传递值时,vue的默认分隔符为{{}},用户可以修改。2.注释当设置为true时,模板中的HTML注释将被保留和呈现。默认行为是丢弃它们。

然后逐步寻找compileToFunctions的根

var create compiler=create compiler(function base compile(template,options){//将模板解析为抽象语法树var ast=parse (template。trim(),选项);//如果配置中有代码优化选项,Ast语法树将在(options.optimize!==false) { optimize(ast,options);} var code=generate(ast,options);返回{ ast:render: code.render,static renderfns 3360 code . static renderfns } });createCompilerCreator被定位为编译器的创建者。他传递了一个基本的编译器,baseCompile作为参数。baseCompile是真正执行编译功能的地方。他传递了一个模板和基本配置选项作为参数。实现了两个功能

1.将模板解析成抽象语法树,简称为AST,以及代码中对应的解析部分。2.可选:优化AST语法树并执行优化方法。3.根据不同平台将AST语法树生成所需代码和相应的生成函数

详见createCompilerCreator的实现。

函数create编译器(base compile) {return函数create编译器(base options){//内部定义编译方法函数compile (template,Options){//将删除空格后的模板和合并选项后的配置作为参数传递给base compile方法,其中finalOptions是base Options和用户选项的合并。var compiled=basecompile(模板。trim(),最终选项);{ detectErrors(compiled.ast,warn);} compiled.errors=errorscompiled.tips=tips函数return compiled } return { compile : compile,compile to functions : create compile to functions fn(compile)} } create compile creator只有一个函数。分部函数用于缓存baseCompile的基本编译方法,返回一个编译器函数,内部定义实际执行编译的编译方法,最后返回Compile和compile functions作为两个对象属性,这也是compile functions的来源。内部编译的功能是将基础配置baseOptions与用户自定义的配置选项结合起来(baseOptions是与外部平台相关的配置),最后在结合配置后返回baseCompile的编译方式。

CompileToFunctions来自createCompileToFunctionFn函数的返回值,该返回值在编译后的方法编译中作为参数传递。

函数createCompileToFunctionFn(编译){ var cache=object . create(null);return functions CompileTofunctions(模板,选项,vm) { options=extend({},options);//cache的作用:避免重复编译同一个模板造成的性能浪费if(cache[key]){ return cache[key]}//执行编译方法varcompiled=compile (template,options);//将代码转换为函数var RES={ };var fngenerors=[];//将编译后的函数体字符串作为参数传递给createFunction,返回最终的render function RES . render=create function(compiled . render,fngenericerrors);//RES . static rederns=已编译。staticrederfns.map(函数(代码){return create function(代码,fngenericerrors)});Return (cache [key]=res)}}最后我们找到了compileToFunctions的真实执行过程:var compiled=compile(模板,选项);编译后的函数体字符串通过creatFunction转换为render函数并返回。

function createFunction(代码,错误){ try { return new Function(代码)} catch(err){ errors . push({ err : err,代码: code });函数体字符串类似于“with(this){return _m(0)}”,最终的渲染函数是function(){ with(this){ return _ m(0)} }

至此,Vue中编译过程的思路已经梳理清楚,编译逻辑之所以循环,主要是因为Vue在不同的平台上有不同的编译过程,每个编译过程的baseOptions选项会有所不同。同时,在同一个平台下,不期望每次编译都传入相同的baseOptions参数,所以在createCompilerCreator初始化编译器时,会传入参数,并使用分部函数缓存配置。同时剥离了与编译相关的合并配置,这是Vue在编译方面非常巧妙的设计。

摘要

以上是边肖介绍的Vue源代码实例的挂载和编译过程。希望对大家有帮助。如果你有任何问题,请给我留言,边肖会及时回复你。非常感谢您对我们网站的支持!如果你觉得这篇文章对你有帮助,请转载,请注明出处,谢谢!

版权声明:深入分析Vue源代码实例挂载和编译过程的实现思路是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。