Vue에서 컴파일 작업 방법을 사용하는 방법
这篇文章主要介绍了Vue 中的compile操作方法,非常不错,具有参考借鉴价值,需要的朋友参考下吧
在 Vue 里,模板编译也是非常重要的一部分,里面也非常复杂,这次探究不会深入探究每一个细节,而是走一个全景概要,来吧,大家和我一起去一探究竟。
初体验
我们看了 Vue 的初始化函数就会知道,在最后一步,它进行了 vm.$mount(el)
的操作,而这个 $mount 在两个地方定义过,分别是在 entry-runtime-with-compiler.js
(简称:eMount) 和 runtime/index.js(简称:rMount) 这两个文件里,那么这两个有什么区别呢?
// entry-runtime-with-compiler.js const mount = Vue.prototype.$mount // 这个 $mount 其实就是 rMount Vue.prototype.$mount = function ( el?: string | Element, hydrating?: boolean ): Component { const options = this.$options if (!options.render) { ... if(template) { const { render, staticRenderFns } = compileToFunctions(template, { shouldDecodeNewlines, shouldDecodeNewlinesForHref, delimiters: options.delimiters, comments: options.comments }, this) options.render = render options.staticRenderFns = staticRenderFns } ... } return mount.call(this, el, hydrating) }
其实 eMount 最后还是去调用的 rMount,只不过在 eMount 做了一定的操作,如果你提供了 render 函数,那么它会直接去调用 rMount,如果没有,它就会去找你有没有提供 template,如果你没有提供 template,它就会用 el 去查询 dom 生成 template,最后通过编译返回了一个 render 函数,再去调用 eMount。
从上面可以看出,最重要的一部分就是 compileToFunctions 这个函数,它最后返回了 render 函数,关于这个函数,它有点复杂,我画了一张图来看一看它的关系,可能会有误差,希望大侠们可以指出。
编译三步走
看一下这个编译的整体过程,我们其实可以发现,最核心的部分就是在这里传进去的 baseCompile 做的工作:
parse: 第一步,我们需要将 template 转换成抽象语法树(AST)。
optimizer: 第二步,我们对这个抽象语法树进行静态节点的标记,这样就可以优化渲染过程。
generateCode: 第三步,根据 AST 生成一个 render 函数字符串。
好了,我们接下来就一个一个慢慢看。
解析器
在解析器中有一个非常重要的概念 AST,大家可以去自行了解一下。
在 Vue 中,ASTNode 分几种不同类型,关于 ASTNode 的定义在 flow/compile.js 里面,请看下图:
我们用一个简单的例子来说明一下:
<p id="demo"> <h1>Latest Vue.js Commits</h1> <p>{{1 + 1}}</p> </p>
我们想一想这段代码会生成什么样的 AST 呢?
我们这个例子最后生成的大概就是这么一棵树,那么 Vue 是如何去做这样一些解析的呢?我们继续看。
在 parse 函数中,我们先是定义了非常多的全局属性以及函数,然后调用了 parseHTML 这么一个函数,这也是 parse 最核心的函数,这个函数会不断的解析模板,填充 root,最后把 root(AST) 返回回去。
parseHTML
在这个函数中,最重要的是 while 循环中的代码,而在解析过程中发挥重要作用的有这么几个正则表达式。
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/ const ncname = '[a-zA-Z_][\\w\\-\\.]*' const qnameCapture = `((?:${ncname}\\:)?${ncname})` const startTagOpen = new RegExp(`^<${qnameCapture}`) const startTagClose = /^\s*(\/?)>/ const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`) const doctype = /^<!DOCTYPE [^>]+>/i const comment = /^<!\--/ const conditionalComment = /^<!\[/
Vue 通过上面几个正则表达式去匹配开始结束标签、标签名、属性等等。
关于 while 的详细注解我放在我仓库里了,有兴趣的可以去看看。
在 while 里,其实就是不断的去用 html.indexOf('<')
去匹配,然后根据返回的索引的不同去做不同的解析处理:
__等于 0:__这就代表这是注释、条件注释、doctype、开始标签、结束标签中的某一种
__大于等于 0:__这就说明是文本、表达式
__小于 0:__表示 html 标签解析完了,可能会剩下一些文本、表达式
parse 函数就是不断的重复这个工作,然后将 template 转换成 AST,在解析过程中,其实对于标签与标签之间的空格,Vue 也做了优化处理,有些元素之间的空格是没用的。
compile 其实要说要说非常多的篇幅,但是这里只能简单的理一下思路,具体代码还需要各位下去深扣。
优化器
从代码中的注释我们可以看出,优化器的目的就是去找出 AST 中纯静态的子树:
把纯静态子树提升为常量,每次重新渲染的时候就不需要创建新的节点了
在 patch 的时候就可以跳过它们
optimize 的代码量没有 parse 那么多,我们来看看:
export function optimize (root: ?ASTElement, options: CompilerOptions) { // 判断 root 是否存在 if (!root) return // 判断是否是静态的属性 // 'type,tag,attrsList,attrsMap,plain,parent,children,attrs' isStaticKey = genStaticKeysCached(options.staticKeys || '') // 判断是否是平台保留的标签,html 或者 svg 的 isPlatformReservedTag = options.isReservedTag || no // 第一遍遍历: 给所有静态节点打上是否是静态节点的标记 markStatic(root) // 第二遍遍历:标记所有静态根节点 markStaticRoots(root, false) }
下面两段代码我都剪切了一部分,因为有点多,这里就不贴太多代码了,详情请参考我的仓库。
第一遍遍历
function markStatic (node: ASTNode) { node.static = isStatic(node) if (node.type === 1) { ... } }
其实 markStatic 就是一个递归的过程,不断地去检查 AST 上的节点,然后打上标记。
刚刚我们说过,AST 节点分三种,在 isStatic 这个函数中我们对不同类型的节点做了判断:
function isStatic (node: ASTNode): boolean { if (node.type === 2) { // expression return false } if (node.type === 3) { // text return true } return !!(node.pre || ( !node.hasBindings && // no dynamic bindings !node.if && !node.for && // not v-if or v-for or v-else !isBuiltInTag(node.tag) && // not a built-in isPlatformReservedTag(node.tag) && // not a component !isDirectChildOfTemplateFor(node) && Object.keys(node).every(isStaticKey) )) }
可以看到 Vue 对下面几种情况做了处理:
当这个节点的 type 为 2,也就是表达式节点的时候,很明显它不是一个静态节点,所以返回 false
当 type 为 3 的时候,也就是文本节点,那它就是一个静态节点,返回 true
如果你在元素节点中使用了 v-pre 或者使用了 <pre class="brush:php;toolbar:false"></code> 标签,就会在这个节点上加上 pre 为 true,那么这就是个静态节点</p><p>如果它是静态节点,那么需要它不能有动态的绑定、不能有 v-if、v-for、v-else 这些指令,不能是 slot 或者 component 标签、不是我们自定义的标签、没有父节点或者元素的父节点不能是带 v-for 的 template、 这个节点的属性都在 type,tag,attrsList,attrsMap,plain,parent,children,attrs 里面,满足这些条件,就认为它是静态的节点。</p><p>接下来,就开始对 AST 进行递归操作,标记静态的节点,至于里面做了哪些操作,可以到上面那个仓库里去看,这里就不展开了。</p><p><strong>第二遍遍历</strong></p><p>第二遍遍历的过程是标记静态根节点,那么我们对静态根节点的定义是什么,首先根节点的意思就是他不能是叶子节点,起码要有子节点,并且它是静态的。在这里 Vue 做了一个说明,如果一个静态节点它只拥有一个子节点并且这个子节点是文本节点,那么就不做静态处理,它的成本大于收益,不如直接渲染。<br/></p><p>同样的,我们在函数中不断的递归进行标记,最后在所有静态根节点上加上 staticRoot 的标记,关于这段代码也可以去上面的仓库看一看。</p><p><span style="color: #ff0000"><strong>代码生成器</strong></span></p><p>在这个函数中,我们将 AST 转换成为 render 函数字符串,代码量还是挺多的,我们可以来看一看。</p><p class="jb51code"></p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>export function generate (
ast: ASTElement | void,
options: CompilerOptions
): CodegenResult {
// 这就是编译的一些参数
const state = new CodegenState(options)
// 生成 render 字符串
const code = ast ? genElement(ast, state) : &#39;_c("p")&#39;
return {
render: `with(this){return $[code]}`,
staticRenderFns: state.staticRenderFns
}
}</pre><div class="contentsignin">로그인 후 복사</div></div><p></p><p>可以看到在最后代码生成阶段,最重要的函数就是 genElement 这个函数,针对不同的指令、属性,我们会选择不同的代码生成函数。最后我们按照 AST 生成拼接成一个字符串,如下所示:</p><p class="jb51code"></p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>with(this){return _c(&#39;p&#39;,{attrs:{"id":"demo"}},[(1>0)?_c(&#39;h1&#39;,[_v("Latest Vue.js Commits")]):_e(),...}</pre><div class="contentsignin">로그인 후 복사</div></div><p></p><p>在 render 这个函数字符串中,我们会看到一些函数,那么这些函数是在什么地方定义的呢?我们可以在 core/instance/index.js 这个文件中找到这些函数:</p><p class="jb51code"></p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>// v-once
target._o = markOnce
// 转换
target._n = toNumber
target._s = toString
// v-for
target._l = renderList
// slot
target._t = renderSlot
// 是否相等
target._q = looseEqual
// 检测数组里是否有相等的值
target._i = looseIndexOf
// 渲染静态树
target._m = renderStatic
// 过滤器处理
target._f = resolveFilter
// 检查关键字
target._k = checkKeyCodes
// v-bind
target._b = bindObjectProps
// 创建文本节点
target._v = createTextVNode
// 创建空节点
target._e = createEmptyVNode
// 处理 scopeslot
target._u = resolveScopedSlots
// 处理事件绑定
target._g = bindObjectListeners
// 创建 VNode 节点
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)</pre><div class="contentsignin">로그인 후 복사</div></div><p></p>
<p>在编译结束后,我们根据不同的指令、属性等等去选择需要调用哪一个处理函数,最后拼接成一个函数字符串。</p>
<p>我们可以很清楚的看到,最后生成了一个 render 渲染字符串,那么我们要如何去使用它呢?其实在后面进行渲染的时候,我们进行了 <code>new Function(render)
的操作,然后我们就能够正常的使用 render 函数了。
总结
大流程走完之后,我相信大家会对编译过程有一个比较清晰的认识,然后再去挖细节相信也会容易的多了,读源码,其实并不是一个为了读而读的过程,我们可以在源码中学到很多我们可能在日常开发中没有了解到的知识。
至于最后代码生成器中的那一大段代码,我还没有把它注释好,后面应该会将源码注释放到仓库里,不过我也相信大家也能够顺利的去读懂源码。
还有一点要提的是在 render 函数中,Vue 使用了 with 函数,我们平时肯定没见过,因为官方不推荐我们去使用 with,我抱着这样的想法去找了找原因,最后我在知乎上找到了尤大大的回答,这是链接,大家可以去了解下。
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
浅谈angular4.0中路由传递参数、获取参数最nice的写法
위 내용은 Vue에서 컴파일 작업 방법을 사용하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











Tomato Novel은 매우 인기 있는 소설 읽기 소프트웨어입니다. 우리는 종종 Tomato Novel에서 읽을 새로운 소설과 만화를 가지고 있습니다. 많은 친구들도 용돈을 벌고 소설의 내용을 편집하고 싶어합니다. 글로 쓰고 싶은데, 그 안에 소설을 어떻게 쓰는지 친구들도 모르니까, 소설 쓰는 방법에 대한 소개를 함께 살펴보는 시간을 가져보겠습니다. 토마토 소설을 사용하여 소설을 쓰는 방법에 대한 튜토리얼을 공유하세요. 1. 먼저 휴대폰에서 토마토 무료 소설 앱을 열고 개인 센터 - 작가 센터를 클릭하세요. 2. 토마토 작가 도우미 페이지로 이동하여 새로 만들기를 클릭하세요. 소설의 끝 부분에 예약하십시오.

컬러풀한 마더보드는 중국 국내 시장에서 높은 인기와 시장 점유율을 누리고 있지만 일부 컬러풀한 마더보드 사용자는 아직도 설정을 위해 BIOS에 진입하는 방법을 모르시나요? 이러한 상황에 대응하여 편집자는 다채로운 마더보드 BIOS에 들어갈 수 있는 두 가지 방법을 특별히 가져왔습니다. 방법 1: U 디스크 시작 단축키를 사용하여 U 디스크 설치 시스템에 직접 들어갑니다. 한 번의 클릭으로 U 디스크를 시작하는 Colour 마더보드의 단축키는 ESC 또는 F11입니다. 먼저 Black Shark 설치 마스터를 사용하여 Black을 만듭니다. Shark U 디스크 부팅 디스크를 켠 후 컴퓨터를 켜면 시작 화면이 나타나면 키보드의 ESC 또는 F11 키를 계속 눌러 시작 항목을 순차적으로 선택할 수 있는 창으로 커서를 "USB. "가 표시된 후

불행하게도 사람들은 어떤 이유로든 실수로 특정 연락처를 삭제하는 경우가 많습니다. WeChat은 널리 사용되는 소셜 소프트웨어입니다. 사용자가 이 문제를 해결할 수 있도록 이 문서에서는 삭제된 연락처를 간단한 방법으로 검색하는 방법을 소개합니다. 1. WeChat 연락처 삭제 메커니즘을 이해하면 삭제된 연락처를 검색할 수 있습니다. WeChat의 연락처 삭제 메커니즘은 연락처를 주소록에서 제거하지만 완전히 삭제하지는 않습니다. 2. WeChat에 내장된 "연락처 복구" 기능을 사용하세요. WeChat은 "연락처 복구"를 제공하여 시간과 에너지를 절약합니다. 사용자는 이 기능을 통해 이전에 삭제한 연락처를 빠르게 검색할 수 있습니다. 3. WeChat 설정 페이지에 들어가서 오른쪽 하단을 클릭하고 WeChat 애플리케이션 "나"를 열고 오른쪽 상단에 있는 설정 아이콘을 클릭하여 설정 페이지로 들어갑니다.

모바일 게임은 기술의 발전과 함께 사람들의 삶에 없어서는 안될 부분이 되었습니다. 귀여운 드래곤 알 이미지와 흥미로운 부화 과정으로 많은 플레이어들의 관심을 끌었으며, 특히 주목을 받은 게임 중 하나가 드래곤 알 모바일 버전이다. 플레이어가 게임에서 자신만의 드래곤을 더 잘 육성하고 성장시킬 수 있도록 이 글에서는 모바일 버전에서 드래곤 알을 부화시키는 방법을 소개합니다. 1. 적절한 유형의 드래곤 알을 선택하십시오. 플레이어는 게임에서 제공되는 다양한 유형의 드래곤 알 속성과 능력을 기반으로 자신이 좋아하고 적합한 드래곤 알 유형을 신중하게 선택해야 합니다. 2. 부화기의 레벨을 업그레이드하세요. 플레이어는 작업을 완료하고 소품을 수집하여 부화기의 레벨을 향상시켜야 합니다. 부화기의 레벨에 따라 부화 속도와 부화 성공률이 결정됩니다. 3. 플레이어가 게임에 참여하는데 필요한 자원을 수집하세요.

현대 사회에서 휴대폰은 우리 삶에 없어서는 안 될 필수품이 되었습니다. 일상적인 의사소통, 업무, 생활을 위한 중요한 도구로 WeChat이 자주 사용됩니다. 그러나 서로 다른 거래를 처리할 때 두 개의 WeChat 계정을 분리해야 할 수도 있습니다. 이를 위해서는 휴대폰이 동시에 두 개의 WeChat 계정에 로그인하는 기능을 지원해야 합니다. 국내 유명 브랜드인 화웨이 휴대폰은 많은 사람들이 사용하고 있습니다. 그렇다면 화웨이 휴대폰에서 위챗 계정을 2개 개설하는 방법은 무엇일까요? 이 방법의 비밀을 공개해보겠습니다. 우선, Huawei 휴대폰에서 두 개의 WeChat 계정을 동시에 사용해야 합니다.

휴대폰이 사람들의 일상 생활에서 중요한 도구가 되면서 글꼴 크기 설정은 중요한 개인화 요구 사항이 되었습니다. 다양한 사용자의 요구를 충족하기 위해 이 기사에서는 간단한 조작을 통해 휴대폰 사용 경험을 개선하고 휴대폰의 글꼴 크기를 조정하는 방법을 소개합니다. 휴대폰의 글꼴 크기를 조정해야 하는 이유 - 글꼴 크기를 조정하면 텍스트가 더 명확하고 읽기 쉬워집니다. - 다양한 연령대의 사용자의 읽기 요구에 적합 - 시력이 좋지 않은 사용자가 글꼴 크기를 사용하는 것이 편리합니다. 휴대폰 시스템의 설정 기능 - 시스템 설정 인터페이스에 들어가는 방법 - 찾기에서 설정 인터페이스의 "디스플레이" 옵션을 입력합니다. - "글꼴 크기" 옵션을 찾아 타사를 통해 글꼴 크기를 조정합니다. 애플리케이션 - 글꼴 크기 조정을 지원하는 애플리케이션 다운로드 및 설치 - 애플리케이션을 열고 관련 설정 인터페이스로 진입 - 개인에 따라

LinuxDeploy 작업 단계 및 주의 사항 LinuxDeploy는 사용자가 Android 장치에 다양한 Linux 배포판을 신속하게 배포하여 모바일 장치에서 완전한 Linux 시스템을 경험할 수 있도록 도와주는 강력한 도구입니다. 이 기사에서는 LinuxDeploy의 작동 단계와 주의 사항을 자세히 소개하고 독자가 이 도구를 더 잘 사용할 수 있도록 구체적인 코드 예제를 제공합니다. 작업 단계: Linux 설치배포: 먼저 설치

스마트폰이 대중화되면서 스크린샷 기능은 일상적인 휴대폰 사용에 필수적인 기술 중 하나로 자리 잡았습니다. Huawei의 주력 휴대폰 중 하나인 Huawei Mate60Pro의 스크린샷 기능은 자연스럽게 사용자로부터 많은 관심을 끌었습니다. 오늘은 모두가 더욱 편리하게 스크린샷을 찍을 수 있도록 Huawei Mate60Pro 휴대폰의 스크린샷 작업 단계를 공유하겠습니다. 우선, Huawei Mate60Pro 휴대폰은 다양한 스크린샷 방법을 제공하며, 개인 습관에 따라 자신에게 맞는 방법을 선택할 수 있습니다. 다음은 일반적으로 사용되는 몇 가지 차단에 대한 자세한 소개입니다.
