百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

你想听的——Vue源码分析之Compile

bigegpt 2024-08-29 11:27 1 浏览

示例

首先,我们知道在Vue的模块化中它是由响应式(比如reactive和ref)Compile(编译)等组成的,在之前的文章中我们已有介绍过vue的响应式原理,那么今天我们就来借助这个例子来搞懂Vue源码中是如何实现Compiler的吧~

模板字符串及其结构

我们可以把模板字符串想象成构建网页的“蓝图”。在 Vue3 中,它是程序员告诉程序如何渲染页面的地方。就比如,这些模板字符串看起来可能有点像这样:

<div id="#app">
    <div @click="()=>console.log('xx')" :id="name">{{name}}</div>
    <h1 :name="title">玩转Vue3</h1>
    <p>编译原理</p>
</div>

这个蓝图告诉 Vue3 诸如:“在 #app 这个容器中,有一个 <div>,点击它时触发一个函数,显示 name 的内容;然后是一个 <h1> 标签,显示标题 title;最后是一个 <p> 标签,显示文字‘编译原理’。”

简而言之,模板字符串就像是一个指南,告诉 Vue3 应该在页面上放置什么元素以及它们应该如何工作。

实现一个编译器

词法分析器(tokenizer)的设计与实现

词法分析器就像是一个语言翻译机器,它能够理解并分解我们写的模板字符串,把它们翻译成计算机可以理解的“语言”。

function tokenizer(input) {
    let tokens = []
    let type = '' // 标签 属性 ....
    let val = ''
    // 逐一字符分词 
    for (let i = 0; i < input.length; i++) {
        let ch = input[i] // 每个字符 
        if (ch === '<') {
            push()
            if (input[i + 1] === '/') {
                type = 'tagend'
            } else {
                type= 'tagstart'
            }
        }

        if (ch === '>') {
            if (input[i-1] == '=') {
                // 箭头函数
            } else{
                push()
                type="text"
                continue
            }
        } else if (/[\s]/.test(ch)) {
            push() 
            type='props' 
            continue // div 拿完了 不需要再加ch 
        }

        val += ch 

    }
    
    return tokens;

    function push() {
        if (val) {
            // <div 
            if (type === 'tagstart') val = val.slice(1) // <div  div
            if (type === 'tagend') val = val.slice(2) // </div  div 
            tokens.push({
                type,
                val
            })
            val = ''
        }
    }    
}

首先,它会把模板字符串中的每一个字符都看一遍,然后根据不同的符号或者空格,把这些字符分成一段一段的小块,这些小块就是我们的“Token(词)”。比如,碰到了 < 符号,就会把它认为是一个标签的开始;碰到了 > 符号,就认为是标签的结束;空格则表示着一个属性的结束。

简而言之:词法分析器做的事情就像是把一篇文章拆成了一句一句的话,这样计算机就能逐个去理解了。

解析器(parse)的设计与实现

解析器就像是一个小小的工程师,它接收词法分析器给的“词”,然后把这些词组织成一棵“树”,这棵树叫做 AST(Abstract Syntax Tree)抽象语法树,简单来说就是计算机理解模板字符串的一种方式。

function parse(template) {
    // 分词
    const tokens = tokenizer(template);
    // console.log(tokens);
    let cur = 0
    let ast = {
        type: 'root',
        props: [],
        children: []
    }
    while(cur < tokens.length) {
        ast.children.push(walk())
    }
    return ast
    function walk() {
        let token = tokens[cur]
        if (token.type == 'tagstart') {
            let node = {
                type: 'element',
                tag: token.val,
                props: [],
                children: []
            }
            token = tokens[++cur]
            while (token.type !== 'tagend') {
                if (token.type == 'props') {
                    node.props.push(walk())
                } else {
                    node.children.push(walk())
                }
                token = tokens[cur]
            }
            cur++
            return node
        }

        if (token.type === 'tagend') {
            cur++
        } 
        if (token.type === 'text') {
            cur++ 
            return token
        }
        if (token.type === 'props') {
            cur++
            const [key,val] = token.val.replace('=', '~').split('~')
            return {
                key,
                val
            }
        }
    }
}

解析器的工作就像是在组装一棵树,它先看第一个词,如果是一个标签的开始,那么它就会创建一个树枝,表示这个标签;然后它再看下一个词,如果是属性,那么就把这个属性加到刚才那个标签上;如果是文本内容,就把这个文本放到标签里面。

通过walk函数,就这样不断走进每根树枝内心深处,一层一层地处理,就像搭积木一样,最后就组装出了一棵树,这棵树就是我们模板字符串的“蓝图”。

编译器(compiler)的实现

编译器就像是一个翻译员,它负责把我们写的模板字符串翻译成计算机能够理解的指令。

function compiler(template) {
    const ast = parse(template)
    console.log(ast);
}

首先,编译器会把模板字符串交给词法分析器,让它把字符串分成一段段的小块,就像把一篇文章拆成了一句一句的话。

然后,编译器把这些小块交给解析器,让解析器把这些小块组织成一棵树,这棵树就是我们的“蓝图”,告诉计算机我们想要在页面上放置什么元素,以及它们应该如何工作。

最后,编译器会把这棵树转换成一系列的指令,这些指令就是告诉计算机如何去操作页面元素的步骤,比如说,“在这个位置放一个按钮”,“点击这个按钮时,执行这个函数”。

这样,编译器就完成了它的任务,把我们写的模板字符串翻译成了计算机能够理解的指令,让页面可以正常工作啦!

最后测试一下

现在,我们就来测试一下,通过我们自己手写的编译器是否可以完成字符串模板的编译效果呢~

运行结果:


结语

从模板字符串到打造一个编译器,从vue源码的学习中我终于体会到了高质量代码的魅力:学习到了闭包的使用案例,并且每个模块内部的功能也分工的很明确。从把模板字符串分成一段段小块,接着交给封装好的解析函数再一步步组成一棵屹立不倒的大树。

站在这棵大树下,我感觉我向往的楚门还很遥远,但是我依然充满激情,依然不断探索着....

相关推荐

得物可观测平台架构升级:基于GreptimeDB的全新监控体系实践

一、摘要在前端可观测分析场景中,需要实时观测并处理多地、多环境的运行情况,以保障Web应用和移动端的可用性与性能。传统方案往往依赖代理Agent→消息队列→流计算引擎→OLAP存储...

warm-flow新春版:网关直连和流程图重构

本期主要解决了网关直连和流程图重构,可以自此之后可支持各种复杂的网关混合、多网关直连使用。-新增Ruoyi-Vue-Plus优秀开源集成案例更新日志[feat]导入、导出和保存等新增json格式支持...

扣子空间体验报告

在数字化时代,智能工具的应用正不断拓展到我们工作和生活的各个角落。从任务规划到项目执行,再到任务管理,作者深入探讨了这款工具在不同场景下的表现和潜力。通过具体的应用实例,文章展示了扣子空间如何帮助用户...

spider-flow:开源的可视化方式定义爬虫方案

spider-flow简介spider-flow是一个爬虫平台,以可视化推拽方式定义爬取流程,无需代码即可实现一个爬虫服务。spider-flow特性支持css选择器、正则提取支持JSON/XML格式...

solon-flow 你好世界!

solon-flow是一个基础级的流处理引擎(可用于业务规则、决策处理、计算编排、流程审批等......)。提供有“开放式”驱动定制支持,像jdbc有mysql或pgsql等驱动,可...

新一代开源爬虫平台:SpiderFlow

SpiderFlow:新一代爬虫平台,以图形化方式定义爬虫流程,不写代码即可完成爬虫。-精选真开源,释放新价值。概览Spider-Flow是一个开源的、面向所有用户的Web端爬虫构建平台,它使用Ja...

通过 SQL 训练机器学习模型的引擎

关注薪资待遇的同学应该知道,机器学习相关的岗位工资普遍偏高啊。同时随着各种通用机器学习框架的出现,机器学习的门槛也在逐渐降低,训练一个简单的机器学习模型变得不那么难。但是不得不承认对于一些数据相关的工...

鼠须管输入法rime for Mac

鼠须管输入法forMac是一款十分新颖的跨平台输入法软件,全名是中州韵输入法引擎,鼠须管输入法mac版不仅仅是一个输入法,而是一个输入法算法框架。Rime的基础架构十分精良,一套算法支持了拼音、...

Go语言 1.20 版本正式发布:新版详细介绍

Go1.20简介最新的Go版本1.20在Go1.19发布六个月后发布。它的大部分更改都在工具链、运行时和库的实现中。一如既往,该版本保持了Go1的兼容性承诺。我们期望几乎所...

iOS 10平台SpriteKit新特性之Tile Maps(上)

简介苹果公司在WWDC2016大会上向人们展示了一大批新的好东西。其中之一就是SpriteKitTileEditor。这款工具易于上手,而且看起来速度特别快。在本教程中,你将了解关于TileE...

程序员简历例句—范例Java、Python、C++模板

个人简介通用简介:有良好的代码风格,通过添加注释提高代码可读性,注重代码质量,研读过XXX,XXX等多个开源项目源码从而学习增强代码的健壮性与扩展性。具备良好的代码编程习惯及文档编写能力,参与多个高...

Telerik UI for iOS Q3 2015正式发布

近日,TelerikUIforiOS正式发布了Q32015。新版本新增对XCode7、Swift2.0和iOS9的支持,同时还新增了对数轴、不连续的日期时间轴等;改进TKDataPoin...

ios使用ijkplayer+nginx进行视频直播

上两节,我们讲到使用nginx和ngixn的rtmp模块搭建直播的服务器,接着我们讲解了在Android使用ijkplayer来作为我们的视频直播播放器,整个过程中,需要注意的就是ijlplayer编...

IOS技术分享|iOS快速生成开发文档(一)

前言对于开发人员而言,文档的作用不言而喻。文档不仅可以提高软件开发效率,还能便于以后的软件开发、使用和维护。本文主要讲述Objective-C快速生成开发文档工具appledoc。简介apple...

macOS下配置VS Code C++开发环境

本文介绍在苹果macOS操作系统下,配置VisualStudioCode的C/C++开发环境的过程,本环境使用Clang/LLVM编译器和调试器。一、前置条件本文默认前置条件是,您的开发设备已...