源码分析:Vue编译(compile)流程编译入口解析

  • 时间:
  • 浏览:0
  • 来源:卉睬糜网络科技

现在,我将着重为大家解答有关源码分析:Vue编译(compile)流程编译入口解析的问题,希望我的回答能够给大家带来一些启发。关于源码分析:Vue编译(compile)流程编译入口解析的话题,我们开始讨论吧。

文章目录列表:

源码分析:Vue编译(compile)流程编译入口解析

Vue编译(Compile)入口

Vue中的数据渲染到页面的整个流程,我们之前介绍过,不清楚可以点击这里,其中的编译流程,是将我们的template模板编译生成render函数。那为什么需要编译呢,因为template模板中有很多指令(比如@click,v-for等),原生js是识别不了的。接下来我们来分析一下Vue编译的过程:

在我们之前分析的mount过程中有这样一段逻辑,compileToFuncti 就是执行编译的函数,将我们定义的template作为参数传入,并生成render函数,下面我们来看下compileToFuncti :

if?(template)?{??......??c t?{?render,?staticRenderFns?}?=?compileToFuncti (template,?{????shouldDecodeNewlines,????shouldDecodeNewline orHref,????delimiters:?opti .delimiters,????comments:?opti .comments??},?this)??opti .render?=?render??opti .staticRenderFns?=?staticRenderFns??......}compileToFuncti

compileToFuncti 定义在src/platform/web/compiler/index.js中:

import?{?baseOpti ?}?from?'./opti 'import?{?createCompiler?}?from?'compiler/index'c t?{?compile,?compileToFuncti ?}?=?createCompiler(baseOpti )export?{?compile,?compileToFuncti ?}

可以看到,compileToFuncti 是通过调用createCompiler生成的,参数baseOpti 是web 的一些配置项,继续看createCompiler函数,定义在src/compiler/index.js中:

import?{?parse?}?from?'./parser/index'import?{?optimize?}?from?'./optimizer'import?{?generate?}?from?'./codegen/index'import?{?createCompilerCreator?}?from?'./create-compiler'//?`createCompilerCreator`?allows?creating?compilers?that?use?alternative//?parser/optimizer/codegen,?e.g?the?SSR?optimizing?compiler.//?Here?we?just?export?a?default?compiler?using?the?default?parts.export?c t?createCompiler?=?createCompilerCreator(function?baseCompile?(??template:?string,??opti :?CompilerOpti ):?CompiledResult?{??//?解析成ast树??c t?ast?=?parse(template.trim(),?opti )??//?优化ast树??if?(opti .optimize?!==?false)?{????optimize(ast,?opti )??}??//?生成代码??c t?code?=?generate(ast,?opti )??return?{????ast,????render:?code.render,????staticRenderFns:?code.staticRenderFns??}})

可以看到这里的createCompiler又是通过createCompilerCreator这个函数去生成的,createCompilerCreator这个函数的参数是一个函数baseCompile,这里面的逻辑是编译过程的核心,包含ast树的生成、ast树的优化、代码生成,这三个流程我们之后再去详细的分析。我们继续看createCompilerCreator这个函数,定义在src/compiler/create-compiler.js:

export?function?createCompilerCreator?(baseCompile:?Function):?Function?{??return?function?createCompiler?(baseOpti :?CompilerOpti )?{????//?这个函数的作用是将一些配置项合并一下,再执行传入的核心编译函数baseCompile????function?compile?(??????template:?string,??????opti ?:?CompilerOpti ????):?CompiledResult?{??????//?web 的基础配置,利用原型继承??????c t?finalOpti ?=?Object.create(baseOpti )??????c t?errors?=?[]??????c t?tips?=?[]??????finalOpti .warn?=?(msg,?tip)?=>?{????????(tip???tips?:?errors).push(msg)??????}??????//?配置合并??????if?(opti )?{????????//?merge?custom?modules????????if?(opti .modules)?{??????????finalOpti .modules?=????????????(baseOpti .modules?||?[]).concat(opti .modules)????????}????????//?merge?custom?directives????????if?(opti .directives)?{??????????finalOpti .directives?=?extend(????????????Object.create(baseOpti .directives?||?null),????????????opti .directives??????????)????????}????????//?copy?other?opti ????????for?(c t?key?in?opti )?{??????????if?(key?!==?'modules'?&&?key?!==?'directives')?{????????????finalOpti [key]?=?opti [key]??????????}????????}??????}??????//?核心编译函数??????c t?compiled?=?baseCompile(template,?finalOpti )??????if?(process.env.NODE_ENV?!==?'production')?{????????errors.push. ly(errors,?detectErrors(compiled.ast))??????}??????compiled.errors?=?errors??????compiled.tips?=?tips??????return?compiled????}????return?{??????compile,??????compileToFuncti :?createCompileToFunctionFn(compile)????}??}}

在这里,我们看到,compileToFuncti 最终是调用了createCompileToFunctionFn这个函数,我们继续看createCompileToFunctionFn,定义在src/compiler/to-function.js:

export?function?createCompileToFunctionFn?(compile:?Function):?Function?{??//?用来缓存??c t?cache?=?Object.create(null)??return?function?compileToFuncti ?(????template:?string,????opti ?:?CompilerOpti ,????vm?:?Component??):?CompiledFunctionResult?{????//?复制配置项????opti ?=?extend({},?opti )????c t?warn?=?opti .warn?||?baseWarn????delete?opti .warn????/*?istanbul?ignore?if?*/????//?判断是否支持new?Function语法????if?(process.env.NODE_ENV?!==?'production')?{??????//?detect?possible?CSP?restriction??????try?{????????new?Function('return?1')??????}?catch?(e)?{????????if?(e.toString().match(/unsafe-eval|CSP/))?{??????????warn(????????????'It?seems?you?are?using?the?standalone?build?of?Vue.js?in?an?'?+????????????'environment?with?Content?Security?Policy?that?prohibits?unsafe-eval.?'?+????????????'The?template?compiler?cannot?work?in?this?environment.?C ider?'?+????????????'relaxing?the?policy?to?allow?unsafe-eval?or?pre-compiling?your?'?+????????????'templates?into?render?functi .'??????????)????????}??????}????}????//?做缓存,编译是很耗时的工作,做缓存有助于提高性能,用空间换时间????//?check?cache????c t?key?=?opti .delimiters????????String(opti .delimiters)?+?template??????:?template????if?(cache[key])?{??????return?cache[key]????}????//?compile?这儿执行传入的compile函数????c t?compiled?=?compile(template,?opti )????//?check?compilation?errors/tips????//?检查编译的错误等????if?(process.env.NODE_ENV?!==?'production')?{??????if?(compiled.errors?&&?compiled.errors.length)?{????????warn(??????????`Error?compiling?template:\n\n${template}\n\n`?+??????????compiled.errors.map(e?=>?`-?${e}`).join('\n')?+?'\n',??????????vm????????)??????}??????if?(compiled.tips?&&?compiled.tips.length)?{????????compiled.tips.forEach(msg?=>?tip(msg,?vm))??????}????}????//?turn?code?into?functi ????//?这儿将编译生成的字符串代码转换成render函数,通过createFunction函数????c t?res?=?{}????c t?fnGenErrors?=?[]????res.render?=?createFunction(compiled.render,?fnGenErrors)????res.staticRenderFns?=?compiled.staticRenderFns.map(code?=>?{??????return?createFunction(code,?fnGenErrors)????})????//?check?function?generation?errors.????//?this?should?only?h en?if?there?is?a?bug?in?the?compiler?itself.????//?mostly?for?codegen?development?use????/*?istanbul?ignore?if?*/????if?(process.env.NODE_ENV?!==?'production')?{??????if?((!compiled.errors?||?!compiled.errors.length)?&&?fnGenErrors.length)?{????????warn(??????????`Failed?to?generate?render?function:\n\n`?+??????????fnGenErrors.map(({?err,?code?})?=>?`${err.toString()}?in\n\n${code}\n`).join('\n'),??????????vm????????)??????}????}????return?(cache[key]?=?res)??}}function?createFunction?(code,?errors)?{??try?{????return?new?Function(code)??}?catch?(err)?{????errors.push({?err,?code?})????return?noop??}}

可以看到我们绕了很多圈,最终找到了compileToFuncti 这个函数,现在我们来捋一下这个执行流程。

1.进入compileToFuncti 函数:执行compileToFuncti 这个函数,这个函数首先会验证当前环境是否支持newFunction语法,再利用闭包做了一个编译器缓存,然后执行传入的compile函数。

2.进入compile函数compile函数将 的配置拿到做了一个赋值,再结合当前传入的opti 参数,做了一些合并配置的操作,然后会执行传入的baseCompile函数,

3.进入baseCompile函数baseCompile函数是编译的核心函数,这个地方会根据传入的template生成ast树、优化ast树、生成字符串代码,完成编译后,

4.完成baseCompile函数的执行,进入compile函数对编译返回结果compiled对象挂载了errors、tips两个属性,

5.完成compile函数,进入compileToFuncti 函数进入compileToFuncti 这个函数,检查编译过程有没有错误,再将生成的字符串代码利用newFunction语法,转换成匿名函数。

最终的这个匿名函数就是最终生成的render函数。关于模板编译的结果可以参考这个网址/post/7094837500497100807

好了,今天关于“源码分析:Vue编译(compile)流程编译入口解析”的话题就到这里了。希望大家能够通过我的讲解对“源码分析:Vue编译(compile)流程编译入口解析”有更全面、深入的了解,并且能够在今后的生活中更好地运用所学知识。