简析vue文件编译——AST

这篇具有很好参考价值的文章主要介绍了简析vue文件编译——AST。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

简介

首先了解一个概念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。尽管两个版本的插件不同,代码实现的设计底层原理不同,但步骤都相差不大,模板文件的解析步骤如下:

简析vue文件编译——AST,vue.js,前端,javascript

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

简析vue文件编译——AST,vue.js,前端,javascript

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

简析vue文件编译——AST,vue.js,前端,javascript

示例源码

以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);

执行结果如下:

简析vue文件编译——AST,vue.js,前端,javascript

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('------------------------------');

测试结果如下:

简析vue文件编译——AST,vue.js,前端,javascript

简析vue文件编译——AST,vue.js,前端,javascript

以上是使用@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.js,前端,javascript

简析vue文件编译——AST,vue.js,前端,javascript

总结

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

到了这里,关于简析vue文件编译——AST的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 前端(vue)js在线预览PDF、Word、Excel、ppt等office文件

    可选参数 pdf=true,word文档尝试以pdf方式显示,默认false watermark=水印文本,显示文本水印;“img:”+图片url表示图片水印,如:img:https://view.xdocin.com/demo/wm.png saveable=true,是否允许保存源文件,默认false printable=false,是否允许打印,默认true ©able=false,是否允许选择复制内容,

    2024年02月13日
    浏览(77)
  • vue前端开发中,通过vue.config.js配置和nginx配置,实现多个入口文件的实现方法

    由于vue为单页面项目,通过控制组件局部渲染,main.js是整个项目唯一的入口,整个项目都在一个index.html外壳中。 若项目过大,会造成单页面负载过重;同时,多页面利于模块独立部署。 如果项目中不同的页面需要不同的main.js和App.vue这样就需要配置多个入口了。 要单独将页

    2024年01月22日
    浏览(90)
  • 带你揭开神秘的Javascript AST面纱之Babel AST 四件套的使用方法

    作者:京东零售 周明亮 这里我们初步提到了一些基础概念和应用: 分析器 抽象语法树 AST AST 在 JS 中的用途 AST 的应用实践 有了初步的认识,还有常规的代码改造应用实践,现在我们来详细说说使用 AST, 如何进行代码改造? 其实在解析 AST 这个工具上,有很多可以使用,上

    2023年04月12日
    浏览(28)
  • 深入学习 JavaScript 转译器 Babel ,AST还原混淆代码

    JavaScript 是一种广泛应用于 Web 开发的脚本语言,它最初是由网景公司的 Brendan Eich 开发的。JavaScript 具有易学易用、灵活、动态等特点,它能够帮助开发者在 Web 应用中实现丰富的交互和动态效果。 然而,由于 JavaScript 的语法和特性不断更新,旧版的浏览器和环境可能无法完

    2024年02月07日
    浏览(46)
  • JavaScript - 判断当前时间是否在指定区间内,例如:9:00~12:00(检查当前时间是否处于规定的两个时间段范围内),适用于 vue.js / uniapp / 微信小程序等前端项目

    例如,您想知道当前时间是否处于 9:00 ~ 12:00 时间区间内,然后根据这个判断进而实现业务逻辑。 如下示例所示, 本文提供一个函数,您只需要传入 2 个时间区间,便可得出当前时间是否在该时间区间范围内: 您可以一键复制,直接粘贴到您的项目中。 您只需要传入开始时

    2024年02月16日
    浏览(63)
  • 【庖丁解牛】vue-element-admin前端CRUD通用操作组件详解,对,核心就是crud.js文件

    vue-element-admin 框架之所以能够快速定制应用,得益于其通配的CRUD操作,属性配置多样化且个性化,能够满足绝大部分开发需求,也方便了代码生成。 可以深入学习重点源文件是: src/components/Crud/crud.js ,一共 863 行代码,以及下图中其它四个vue组件,形成了对通用CRUD操作的高

    2024年01月18日
    浏览(59)
  • jQuery.js - 前端必备的Javascript库

    作者: WangMin 格言: 努力做好自己喜欢的每一件事 jQuery.js 是什么? jQuery是一个快速简洁、免费开源易用的JavaScript框架, 倡导写更少的代码,做更多的事情 。它封装JavaScript常用的功能代码,提供了一种简便的JavaScript设计模式,以及我们开发中常用到的操作DOM的API,优化HTML文

    2024年02月05日
    浏览(77)
  • web前端框架JS学习之JavaScript类型转换

    vascript有多种数据类型,如字符串、数字、布尔等,可以通过typeof语句来查看变量的数据类型。数据类型转换就是数据类型之间相互转换,比如把数字转成字符串、把布尔值转成字符串、把字符串转成数字等,这在工作也是经常碰到的。 本期我们就给大家说说web前端框架JS学

    2024年02月10日
    浏览(64)
  • 【前端灵魂脚本语言JavaScript⑤】——JS中数组的使用

    🐚 作者: 阿伟 💂 个人主页: Flyme awei 🐋 希望大家多多支持😘一起进步呀! 💬 文章对你有帮助👉关注✨点赞👍收藏📂 第一种: var 数组名 = new Array(); 创建一个空数组 第二种: var arr2 = new Array(10); 创建一个定长为10的数组 第三种 var arr3 = new Array(a,b,c); 创建时直接指定元素值

    2023年04月08日
    浏览(65)
  • 〖大前端 - 基础入门三大核心之JS篇㉓〗- JavaScript 的「数组」

    当前子专栏 基础入门三大核心篇 是免费开放阶段 。 推荐他人订阅,可获取扣除平台费用后的35%收益,文末名片加V! 说明:该文属于 大前端全栈架构白宝书专栏, 目前阶段免费开放 , 购买任意白宝书体系化专栏可加入 TFS-CLUB 私域社区。 福利:除了通过订阅\\\"白宝书系列专

    2024年02月04日
    浏览(70)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包