简析vue文件编译——AST

news2024/11/17 12:52:28

简介

首先了解一个概念ASTabstract syntax tree)抽象语法树,按照大多数教程中的描述,这是一种源代码的抽象语法结构树,树上的每个节点都表示源代码中的一种结构,将源码中的各种嵌套括号等形式,隐含在树的结构中,不依赖于源语言的语法。这个概念不但名称AST很抽象, 描述得也很抽象,理解起来很难。个人习惯,将所有抽象的东西抽象的理解,(下文中都将简称为AST或语法树),粗暴的代入到各个场景中,抓住几个主要的过程描述:

  • 对于常见编译型语言(例如:Java)来说,编译步骤分为:词法分析->语法分析->语义检查->代码优化和字节码生成

  • 对于解释型语言(例如 JavaScript)来说,通过词法分析 -> 语法分析 -> 语法树,就可以开始解释执行了

抓住以上两点,语言分为编译型语言,解释型语言。在解释型语言中,前端常用的javascript就有话说了,AST原来在javascript引擎执行中起到了作用。

思考

基于以上先向下延申到JavaScript的执行过程,这就到了js引擎编译部分——V8引擎以及其工作的原理了,太深奥了,先到此为止(@~@),再往下就暴露自己了,毕竟看破不说破;只能再横向拓展,js引擎的执行过程以及编译解析深入不了,就看看其执行环境node、浏览器、智能编辑器以及编译器。在以上各个不同环境中,很多经验丰富的一线的开发者,很容易就想到兼容的问题,例如es6 语法在低版本的浏览器中,需要转换为es5, 这个兼容经常在开发框架中经常被提到。脑洞打开,将复杂的东西简单化。在现阶段经典的开发框架中Angular、React、Vue中抓住共同点,开发工具脚手架都有一个编译打包的配置。这里面是不是都有这个过程,毕竟各种不同的语法文件后缀.ts、.jsx、.vue,最终都要在同一个环境中js引擎中运行,肯定是有AST的编译过程。

梳理知识

梳理下知识点,就个人能力而言A/V/R三大框架都精通?咳咳~目前而言,还差亿丢丢(~@^@~)。只能以当前使用最熟练的vue来做栗子了。 扒一扒vue框架源码中compiler的插件——vue-loader、compiler、render等插件。这里就比较夹杂了vue2与vue3的兼容。以下简单说一下2.x与3.x版本的更新点,vue2.x是vue-template-compiler,在vue3在@vue/compiler-sfc。尽管两个版本的插件不同,代码实现的设计底层原理不同,但步骤都相差不大,模板文件的解析步骤如下:

先通过装载器加载模板文件,再通过编译插件将转为可以被渲染的代码,最后通过render函数进行页面渲染。按照理解进一步画了如下几个步骤,其中template中的标签、指令、修饰符等需要传入到编译器中,通过几个编译中的几个api 转换为可以渲染的代码,进入到render函数中进行页面渲染。

其中的compiler编译中分为了三个部分进行拆分为 dom 、script、style中分别进行AST转换,通过generate函数将AST转换为可执行代码。传入到render函数执行页面渲染的dom

示例源码

以vue-template-compiler插件为例,将字符串代码转换AST的函数工具

const vueCompiler = require('vue-template-compiler');
​
function templateCompile(code) {
    const {ast} = vueCompiler.compile(code);
    const dfs = (node, path) => {
        for (let index in node.children) {
            let child = node.children[index];
            if ('img' === child.tag) {
                child.attrsList.forEach(attr => {
                    if (attr.name === ':src' || attr.name === 'src') {
                        console.log('这是一个img', attr.value, ` path= ${ path }`);
                    }
                })
            }
            dfs(child, path.concat(child.tag))
        }
​
    }
    dfs(ast, []);
    return ast
}

函数执行测试如下:

const templateCode = `
  <div id="app">
    <div class="header_box" :style="\`height: \${headerHeight}px\`"></div>
    <transition :name="transitionName">
        <div :style="\`height: calc(100% - \${headerHeight + footerHeight}px)\`">
            <navigator title="编辑资料"  @goback="handleGoback" btn_name="完成" @reset="handleSave">
            <span slot="left" class="left-text">取消</span>
            </navigator>
            <div class="detail">
                <div class="img" @click="updateAvatar">
                    <img v-if="info.userIcon" :src="info.userIcon" alt="">
                    <i v-else></i>
                    <!-- <img v-else src="@/asset/images/bg_my_avatar@3x.png" alt=""> -->
                    <span>更换头像</span>
                </div>
                <div class="form-item">
                    <span>昵称</span>
                    <hm-input :clearable="true" type="text" placeholder="请输入昵称" :maxlength="32" v-model="nickName" />
                </div>
            </div>
        </div>
    </transition>
    <div class="footer_box" :style="\`height: \${footerHeight}px\`"></div>
  </div>
`;
const templateResult = templateCompile(templateCode);
console.log('------------------------------');
console.log(templateResult.ast);

执行结果如下:

babel工具类

使用@babel/parse、@babel/traverse进行转换js、css的示例

const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
​
function scriptCompile(code) {
    const ast = parser.parse(code, { sourceType: 'module' });
    const visitor = {
        ImportDeclaration(path) {
            const {specifiers, source} = path.node;
            console.log('这是一个import', specifiers[0].local.name, source.value);
        }        
    }
    traverse(ast, visitor);
​
    return ast;
}

测试用例如下:

const scriptCode = `
import navigator from '@/components/navigator/navigator';
import { setTimeout } from 'timers';
export default {
    name: 'app',
    data () {
        return {
            info: {},
            transitionName: '',
            nickName: '',
            headerHeight: 10,
            footerHeight: 10
        };
    },
    // 添加一个注释
    created () {
        this.headerHeight = this.GLOBAL.isMobile ? (this.GLOBAL.statusBarHeight / window.devicePixelRatio) : 10;
        this.footerHeight = this.GLOBAL.isMobile ? (this.GLOBAL.bottomSafeAreaHeight / window.devicePixelRatio) : 10;
    },
    mounted () {
        this.listenBackbutton();
        // 这是一个注释
        this.getInfo();
    },
    methods: {
        listenBackbutton () {
            hatom.setBridge(
                'onBackPressed',
                (res) => {
                    hatom.page.exit();
                }
            );
        },
        getInfo () {
            this.$http({
                method: 'post',
                url: this.$api.USER_INFO
            }).then((res) => {
                console.log(res.data);
                this.nickName = res.data.nickName;
                this.info.userIcon = res.data.userIcon;
                this.info = res.data;
            });
        },
        exitWebApp () {
            hatom.page.exit((res) => {
                this.$toast('exit成功');
            });
        },
        updateAvatar () { },
        handleGoback () {
            if (this.GLOBAL.isMobile) {
                hatom.page.exit();
            } else {
                window.location.hash = '';
                window.location.pathname = 'index';
            }
        },
        handleSave () {    }
    },
    watch: {
        '$route' (to, from) {
            if (to.meta.index < from.meta.index) {
                this.transitionName = 'right';
            }
            if (to.meta.index > from.meta.index) {
                this.transitionName = 'left';
            }
        }
    },
    components: {
        navigator
    }
};
`;
const scriptResult = scriptCompile(scriptCode);
console.log(scriptResult);
console.log('------------------------------');

测试结果如下:

以上是使用@babel/parse、@babel/traverse用例,查找babel官网上,其中babel全家桶中,@babel/core、@babel/generator、@babel/types等,

css的转换AST

其中css转换的工具类有cssom、postcss,以postcss为例,建立一个test.js脚本文件

const postcssNested = require('postcss-nested')
const autoprefixer = require('autoprefixer')
const postcss = require('postcss')
const fs = require('fs')
​
fs.readFile('src/app.css', (err, css) => {
  postcss([postcssNested, autoprefixer])
     .process(css, { from: 'src/app.css', to: 'dest/app.css' })
     .then(result => {
       console.log(result.root);
  })
  const ast = postcss.parse(css, {
​
  })
  console.log(ast)
})

执行bash node test.js,结果如下:

总结

以上就是本人对vue文件解析的简单理解,从AST概念简单描述,进一步联想到在vue编译的使用场景,通过vue的编译原理,了解到AST转换工具类;目前探究到的就这么多。其中关于AST的进一步使用场景,仍在探究,有兴趣的,大家可以在线上astexplorer 训练场进行尝试。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/956781.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android安卓实战项目(13)---记账APP详细记录每天的收入和支出并且分类统计【生活助手类APP】强烈推荐自己也在用!!!(源码在文末)

Android安卓实战项目&#xff08;13&#xff09;—记账APP详细记录每天的收入和支出并且分类统计【生活助手类APP】强烈推荐自己也在用&#xff01;&#xff01;&#xff01;&#xff08;源码在文末&#x1f415;&#x1f415;&#x1f415;&#xff09; 一.项目运行介绍 B站…

antd实现年日输入框联动

效果: 1、默认显示年&#xff0c;日期区间默认显示今年2023——2024 年份显示前5年后5年 2、如果选择了月份&#xff0c;日期区间显示从1月份到12月份 部分代码: (react 使用class类组件)

Lee滤波python实现(还包括frost等滤波)

Lee滤波按定义实现&#xff1a; from scipy.ndimage.filters import uniform_filter from scipy.ndimage.measurements import variancedef lee_filter(img, size):img_mean uniform_filter(img, (size, size))img_sqr_mean uniform_filter(img**2, (size, size))img_varian…

最小生成树 -prim算法

一般无向图建图稠密图-prim算法稀疏图-kruskal算法 prim : 加点法 1.先随机选一个点&#xff0c;加入集合 &#xff0c;之后寻找最短的距离的点加入集合&#xff0c;行程最小生成树。 2.注意最小生成树是不能有回路的&#xff0c; 所以可以把回路设置成最大值&#xff0c;即假装…

idea使用maven时的java.lang.IllegalArgumentException: Malformed \uxxxx encoding问题解决

idea使用maven时的java.lang.IllegalArgumentException: Malformed \uxxxx encoding问题解决 欢迎使用Markdown编辑器1、使用maven clean install -X会提示报错日志2、在Poperties.java文件的这一行打上断点3、maven debug进行调试4、运行到断点位置后&#xff0c;查看报错char…

贝锐蒲公英异地组网方案,如何阻断网络安全威胁?

随着混合云和移动办公的普及&#xff0c;企业网络面临着越来越复杂的安全威胁环境。 大型企业有足够的能力和预算&#xff0c;构建覆盖全部个性化需求的定制化网络安全方案。 但对于广大中小企业来说&#xff0c;由于实际业务发展情况&#xff0c;他们难以在部署周期、预算成本…

Redis数据结构应用场景及原理分析

目录 一、Redis介绍 二、应用场景 2.1 String应用场景 2.2 Hash应用场景 2.3 List应用场景 2.4 Set应用场景 2.5 Zset应用场景 一、Redis介绍 单线程多路复用底层数据结构&#xff1a;全局哈希表&#xff08;key-value&#xff09; 二、应用场景 2.1 String应用…

安装centos7修改网关时出现ifconfig命令找不到的解决方法

系列文章专栏 学习以来遇到的bug/问题专栏 文章目录 系列文章专栏 一 问题描述 二 解决方法 2.1 原因分析 前言 本文主要介绍安装centos7修改网关时出现ifconfig命令找不到的解决方法 一 问题描述 安装centos7修改网关时出现ifconfig命令找不到的情况 二 解决方法 2…

Revit SDK:SolidSolidCut 实体几何裁剪

前言 这个例子介绍了 Revit 中的一个实体几何裁剪。 内容 这个例子介绍如何使用 SolidSolidCutUtils 的接口来做几何裁剪以及取消几何裁剪。内容相对来说非常简单。 namespace Autodesk.Revit.DB {public static class SolidSolidCutUtils{public static void AddCutBetwee…

vue3组合式api bus总线式通信

vue2中可以创建一个 vue 实例&#xff0c; 做为 总结来完成组件间的通信 但是在vue3中&#xff0c; 这种方法是不能使用的。 因为vue3中main.js中&#xff0c; 使用的createApp() 没有机会再写 new Vue了 但是我们可以使用 mitt 的插件来解决这个问题 vue3 bus组件的用法 安装…

Mysql表关联简单介绍(inner join、left join、right join、full join不支持、笛卡尔积)

文章目录 0. 交集、并集、差集含义说明1. 简单演示上图七种情况0. A、B表数据准备1. left outer join 简称 left join 左表所有数据&#xff0c;右表关联数据&#xff0c;没有的以null填充2. right outer join 简称 right join&#xff0c;右表所有数据&#xff0c;左表关联数据…

【SpringCloud】SpringCloud整合openFeign

文章目录 前言1. 问题分析2. 了解Feign3. 项目整合Feign3.1 引入依赖3.2 添加注解3.3 编写Feign客户端3.4 测试3.5 总结 4. 自定义配置4.1 配置文件方式4.2 Java代码方式 5. Feign使用优化5.1 引入依赖5.2 配置连接池 6. Feign最佳实践6.1 继承方式6.2 抽取方式 前言 微服务远…

报错处理:Too many open files

报错处理 Too many open files 报错环境 Linux 排错思路 当打开的文件句柄超过系统允许的最大值时&#xff0c;会出现该错误。这可能是由于系统参数限制或者应用程序打开了过多的文件导致的。 解决方法 可以通过修改系统参数来增加最大允许打开文件句柄数。 临时性修改&#xf…

ssm农业视频实时发布管理系统源码

ssm农业视频实时发布管理系统源码108 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm package com.controller;import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; impo…

ui网页设计实训心得

ui网页设计实训心得篇一 通过这次实训对这门课程的学习&#xff0c;做好网页&#xff0c;并不是一件容易的事&#xff0c;它包括网页的选题、 内容采集整理、 图片的处理、 页面的排版设置、 背景及其整套网页的色调等很多东西。 所以我得出一下总结&#xff1a; 一、 准备资…

将本地jar打包到本地maven仓库或maven私服仓库中

将本地jar包打包到本地的maven仓库中的命令&#xff1a; mvn install:install-file -DgroupIdtebie.applib.api -DartifactIdapiclient -Dversion1.0-SNAPSHOT -Dfile本地jar路径 -Dpackagingjar说明&#xff1a; DgroupId pom中的<groupId></groupId> Dartifact…

【爬虫】实验项目二:模拟登录和数据持久化

目录 一、实验目的 二、实验预习提示 三、实验内容 实验要求 基本要求&#xff1a; 改进要求A&#xff1a; 改进要求B&#xff1a; 四、实验过程 基本要求&#xff1a; 源码如下&#xff1a; 改进要求A: 源码如下&#xff1a; 改进要求B&#xff1a; 源码如下&…

deepspeed多机多卡并行训练指南

文章目录 前言离线配置训练环境共享文件系统多台服务器之间配置互相免密登录pdsh多卡训练可能会碰到的问题注意总结 前言 我的配置&#xff1a; 7机14卡&#xff0c;每台服务器两张A800 问&#xff1a;为啥每台机只挂两张卡&#xff1f; 答&#xff1a;给我的就这样的&#…

线性代数的学习和整理16:什么是各种空间(类型),向量空间,距离(类型)?

目录 1 空间相关的群&#xff0c;环&#xff0c;域&#xff0c;集合&#xff0c;空间的预备知识 1.1&#xff1a;群&#xff0c;环&#xff0c;域&#xff0c;集合&#xff0c;空间的定义&#xff08;表示不懂&#xff0c;只是做个标记&#xff09; 2 空间 2.1 各种空间概念…

如何修复 Cloudflare 错误 1015“您受到速率限制”

目录 错误 1015 您受到费率限制 如何修复错误 1015 您的速率受到限制 Cloudflare 降低用户活动速度 禁用网站的速率限制 擦除当前的速率限制设置 增加网站可以处理的请求数量 修改时间限制 增加带宽支持 网站优化 社区支持 为网站访问者提供无错误的体验 应该如何学…