好家伙,
在正式内容之前,我们来思考一个问题,
当我们使用vue开发页面时,
它会经历四步:
解析模板:Vue会解析<template>
中的内容,识别出其中的指令、插值表达式({{}}
),以及其他元素和属性。
生成AST:解析模板后,Vue会生成一个对应的AST(Abstract Syntax Tree,抽象语法树),用于表示模板的结构、指令、属性等信息。
生成渲染函数:根据生成的AST,Vue会生成渲染函数。渲染函数是一个函数,接收一些数据作为参数,并返回一个虚拟DOM(Virtual DOM)。
渲染到真实DOM:Vue执行渲染函数,将虚拟DOM转换为真实的DOM,并将其插入到页面中的指定位置。在这个过程中,Vue会根据数据的变化重新执行渲染函数,更新页面上的内容。
所以,步骤如下:模板解析 =》AST =》生成渲染函数 =》渲染到真实DOM
抽象语法树(abstract syntax code,AST)是源代码的抽象语法结构的树状表示,树上的每个节点都表示源代码中的一种结构,之所以说是抽象的,抽象表示把js代码进行了结构化的转化,转化为一种数据结构。
这种数据结构其实就是一个大的json对象,json我们都熟悉,他就像一颗枝繁叶茂的树。有树根,有树干,有树枝,有树叶,无论多小多大,都是一棵完整的树。
简单理解,就是把我们写的代码按照一定的规则转换成一种树形结构。
举个简单的例子:
假设代码如下:
随后我们将其转换为ast语法树(简单版本):
{ tag:'div' //节点类型 attrs:[{id:"app"}] //属性 children:[{tag:null,text:Hello},{xxx}] //子节点 }
当然,实际情况复杂得多,但总体结构不变
{ "type": "Program", "start": 0, "end": 32, "body": [ { "type": "ExpressionStatement", "expression": { "type": "JSXElement", "openingElement": { "type": "JSXOpeningElement", "name": { "type": "JSXIdentifier", "name": "div" }, "attributes": [ { "type": "JSXAttribute", "name": { "type": "JSXIdentifier", "name": "id" }, "value": { "type": "Literal", "value": "app" } } ], "selfClosing": false }, "closingElement": { "type": "JSXClosingElement", "name": { "type": "JSXIdentifier", "name": "div" } }, "children": [ { "type": "JSXText", "value": "Hello" }, { "type": "JSXExpressionContainer", "expression": { "type": "Identifier", "name": "msg" } } ], "selfClosing": false } } ], "sourceType": "module" }
来看这个例子
这无非就是一个简单的 开始标签: 文本: Hello{{msg}} 结束标签: 似乎只要用正则表达式来匹配就可以了,(事实上也确实是这么实现的) //从源码处偷过来的正则表达式
const attribute =
/^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
//属性 例如: {id=app}
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`; //标签名称
const qnameCapture = `((?:${ncname}\\:)?${ncname})` //
2.1.试验实例
我们来举一个实例看看:
代码已开源https://github.com/Fattiger4399/analytic-vue.git
**(关键的部分已使用绿色荧光标出,没有耐心看完整代码的话,只看有绿色荧光标记的部分就好)
**
项目目录如下:
首先来到index.html我们人为的制造一些假数据
注意:此处的vue是我们自己写的实验品,并非大尤的Vue
index.html
**