乐于分享
好东西不私藏

「软件设计思想杂谈」“切图仔”也能懂编译原理?框架源码也许没那么难.聊聊 Vue 的编译(上)

「软件设计思想杂谈」“切图仔”也能懂编译原理?框架源码也许没那么难.聊聊 Vue 的编译(上)

Hi!这里是JustHappy,作为一个标准的“切图仔”,如何轻松的拿捏Vue或者React的框架源码呢?一行一行的看?一处一处的打断点?太磨人了,我们换种方式,直击重点!今天我们从编译原理,也就是编译器的角度聊聊吧!

编译原理真的那么高大上吗?

仔细回想,其实从大学第一节C语言课开始我们就接触了“编译原理”,众所周知,C语言是一门“高级语言”,是计算机所不能直接读懂的语言,对于一个.c的文件我们需要对其进行如下操作:预处理 → 编译 → 汇编 → 链接 ...... 好的,你成功的睡着了...

所谓“编译原理”并不是什么新兴概念,几乎从软件工程诞生的最开始时候就有了

它只是:把一种东西 变成另一种东西

其实很常见例如:

C代码 ----> 机器码
TypeScript ----> JavaScript
Vue模板 ----> Render函数

本质都是同一个事情。

哈哈还不清楚没事,至少我们已经知道这不是啥“高不可攀”的东西,而且我们这次只讲 “编译”,是不是轻松很多呢?

所以话不多说,我们开始吧!

简单的来说编译就只有几个步骤,为了缩短篇幅,我们直接就着Vue讲吧🚀🚀

.vue.js,需要几个步骤

众所周知,我们的浏览器是只能处理JavaScript、Css和Html的,而我们在编写Vue的时候是写在一个.vue文件中的,就像下面这样一个简单的组件:

<template>    <div>Hello {{ name }}</div></template><script setup>    const name = 'JustHappy'</script>

问题来了。浏览器认识这个东西吗?显然不认识。

浏览器只认识:

  • • HTML
  • • CSS
  • • JavaScript

所以在浏览器运行之前。

一定有人把它翻译成浏览器认识的代码,这个翻译的人,就是:Vue Compiler

如何读懂.vue 文件呢? Webpack loader给你解决

我们都知道.vue文件是Vue 自己设计出来的文件格式。

比较官方的概念是Single File Component

顾名思义:“一个组件 一个文件”

把原本分散的:

HTMLCSSJavaScript

全部塞进同一个文件里。对于开发者来说确实舒服了:

组件逻辑在这里模板在这里样式也在这里

维护起来非常方便。

但是浏览器读不懂啊?于是就需要 Webpack loader

其实Webpack loader就干一件事:让 webpack 可以识别任意后缀的文件

其大致原理就是将下面这个.vue 文件 拆解 为 “HTML 、CSS 、JavaScript“

(注意:是以 JS module 的形式拆解)

<template>    <div>Hello {{ name }}</div></template><style scoped>    .red { color: red; } </style><script setup>    const name = 'JustHappy'</script>

以上例子在经过 vue-loader 后大致会变成下面这样

import { render } from './App.vue?type=template'import script from './App.vue?type=script'import './App.vue?type=style'script.render = renderexport default script

具体 Loader 的原理可以参照https://www.webpackjs.com/loaders/

如何编译?为什么要这么编译?

经过上面我们把 .vue 文件拆开来之后会发现,其实对于 JS、CSS 部分的处理都比较简单。

JS 本来就是 JavaScript。

CSS 本来就是 CSS。

最多做一些语法糖转换或者兼容性处理。

而对于 template 中的内容,我们就不能直接把它当成 HTML 了。

哈哈,HTML 可不能写:

<div v-if="isLogin">  用户中心</div>// 或者<li v-for="item in list">  {{ item }}</li>

这些都属于 Vue 自己扩展出来的语法。

这种为了某个特定领域而设计的语言,我们一般称为:

DSL(Domain Specific Language)领域专用语言

而 Vue Template 就是一种专门用于描述界面的 DSL。

既然是一门语言,那么编译器就必须先「看懂」它。

但是计算机其实不擅长直接处理字符串

例如对于:

<div class="app">  <span>{{ msg }}</span></div>

编译器首先会把它转换成一种更容易处理的数据结构。

这个过程叫:parse 解析。

那么完成这个过程的代码就可以叫做: parser 解析器。

解析后的结果大致会变成:

{  type: 'Root',  children: [    {      type: 'Element',      tag: 'div',      props: [        {          name: 'class',          value: 'app'        }      ],      children: [        {          type: 'Element',          tag: 'span',          children: [            {              type: 'Interpolation',              content: 'msg'            }          ]        }      ]    }  ]}

这个结构在软件工程中称为:

AST(Abstract Syntax Tree)抽象语法树

名字听起来很唬人,其实本质上就是:把字符串 变成对象

这样编译器后续就不需要对着一大坨字符串做分析了,而是可以像操作普通 JavaScript 对象一样去遍历、查找和修改节点。

接下来 Vue Compiler 会在 AST 的基础上继续处理,我们下篇再聊...

我们为什么要学习源码?

难道说是为了卷吗?我想不是的,在学习Vue源码之前,我也是认为这是“卷”,而且学的迷迷糊糊的。不过大体上过了一遍,也就是脑袋里有个印象,Vue源码大概由几部分组成?每个部分的作用是什么?用的什么样的方式去实现的?虽然不能说是精通,但是也是极大的提升了我对编码的看法(😂但是还是会写出屎),总之我的体感上是学习源码能让我更好的管理我编写的代码