皮皮网
皮皮网

【python requests源码分析】【源码对冲】【wxg源码】babelplugin插件源码_babel 插件

来源:免费导航页源码 发表时间:2024-11-28 10:51:57

1.使用 unplugin-vue-components 按需引入组件(内附实现原理)
2.elementUI 按需引入的插插件babel-plugin-component
3.了解babel:polyfill、loader、插插件 preset-env及 core之间的插插件关系
4.一口气解决项目中JS 所有精度丢失问题!
5.import方式随意互转,插插件感受babel插件的插插件威力
6.想弄懂Babel?你必须得先弄清楚这几个包

babelplugin插件源码_babel 插件

使用 unplugin-vue-components 按需引入组件(内附实现原理)

       在开发Vue项目时,通常会利用组件库进行开发,插插件python requests源码分析组件库的插插件加载方式主要有两种:全局引入和按需引入。全局引入组件库虽方便,插插件但往往会导致产物体积过大,插插件对性能要求较高的插插件项目不太友好。

       为了解决这个问题,插插件出现了使用babel-plugin-import进行按需加载的插插件解决方案。它可以省去style的插插件引入,但还是插插件需要手动引入组件,且需要依赖babel插件。插插件而unplugin-vue-components的出现,使得开发者无需手动引入组件,能够像全局组件那样开发,但实际上是在进行按需引入,而且不限制打包工具,无需使用babel。

       以Antd Vue和vite为例,unplugin-vue-components能够自动引入Antd Vue的组件,无需手动import组件以及组件样式,使用起来就像全局组件一样,但这是按需自动引入,可以减少产物大小。直接使用即可,unplugin-vue-components为主流的UI组件库提供了内置支持,通过使用对应UI组件库的解析器(resolvers),就能自动引入对应的组件库组件及样式。

       解析器可以是一个函数或是一个对象。以对象为例,resolve是一个函数,当遇到特定组件时(如a-button),它会调用该函数,并返回一个对象。unplugin-vue-components会根据这个返回的对象修改编译后的代码,从而实现按需引入。

       unplugin-vue-components的实现原理非常简单,它通过正则匹配Vue的全局组件(编译后使用_resolveComponent包裹),然后引入组件并替换_resolveComponent,从而实现将全局使用转换为按需引入的方式。

       unplugin-vue-components也存在局限性,但总体上,源码对冲它能够非常方便地实现按需引入组件的功能,从而减小项目体积、加快项目加载速度,提升用户体验。它能够自动引入项目components目录下的组件,也支持自定义指定的自动按需引入,更多内容请查看unplugin-vue-components文档。

       使用unplugin-vue-components能够实现更加高效、便捷的组件引入方式,为项目开发提供便利,提升开发效率和项目性能。尝试使用unplugin-vue-components,让您的Vue项目开发更加轻松、高效。

elementUI 按需引入的babel-plugin-component

       为什么要使用babel-plugin-component来实现按需引入?

       babel-plugin-component是一个为element-ui项目单独开发的babel模块化构建插件。

       最初我以为这是一个通用的babel插件,直到在GitHub上看到介绍,才了解到它实际上是element-ui针对自身项目开发的。

       因此,只有使用这种静态路径转换方案,才能正确引入element-ui。因为它是专门针对element-ui的babel插件。

       如果你不使用此插件,只是按照以下方式进行引入

       项目也能正常运行,但build打包的体积将不会减少,和完全引入element-ui一样,体积相同。因此,即使使用import { Button, Select } from 'element-ui'这种引入方式,webpack的import依旧会引入整个element-ui包,因为webpack不知道element-ui包内部子级包的存放路径规则,所以必须完整引入。

       使用此插件的细节:

       vue的项目默认有一个presets预设规则,

       而babel的presets数组是有先后顺序的,所以我暂时也不知道这两个应该谁先谁后,但是我测试都是可以的。

       vue预设在前dist目录为1.7M,@babel/preset-env在前dist目录是1.8M,

       不使用babel-plugin-component插件dist目录是5.6M。

       细节2是,vuecli搭建的vue2项目,现在是使用babel7,而预设es的名字改成了@babel/preset-env,用于编译成ES+,wxg源码所以改成@babel/preset-env就好了。

了解babel:polyfill、loader、 preset-env及 core之间的关系

       在使用webpack配置babel解析es6语法的过程中,我们通常仅按照文档说明进行配置,而并未深入了解babel工具链的运作机制。以下是对相关工具链的回顾:

       我们经常接触到的有babel、babel-loader、@babel/core、@babel/preset-env、@babel/polyfill以及@babel/plugin-transform-runtime,它们各自的作用是什么?

       1、babel:根据babel官网的定义,babel是一个工具链,主要用于将ECMAScript +代码转换为向后兼容版本的JavaScript代码。它不仅包含语法转换等功能,还可以通过@babel/polyfill实现目标环境中缺少的功能。

       需要注意的是,babel是一个可以安装的包,并且在webpack 1.x配置中使用它作为loader的简写。然而,这种方式在webpack 2.x以后不再支持,并会出现错误提示。此时,我们需要删除babel包,安装babel-loader,并指定loader: 'babel-loader'。

       2、@babel/core:@babel/core是babel的核心库,包含了所有的核心Api,这些Api供babel-loader调用。

       3、@babel/preset-env:这是一个预设的插件集合,包含了一组相关的插件,用于指导Babel如何进行代码转换。该插件包含所有es6转化为es5的翻译规则。

       4、@babel/polyfill:@babel/preset-env提供了语法转换的规则,但无法弥补浏览器缺失的一些新功能。此时,我们需要polyfill来作为JavaScript的垫片,弥补低版本浏览器缺失的新功能。

       需要注意的是,polyfill的源码安装_体积很大,如果我们不做特殊说明,它会将目标浏览器中缺失的所有es6的新功能都做垫片处理。为了解决这个问题,我们通常在presets的选项里配置"useBuiltIns": "usage",这样只对使用的新功能做垫片,同时也不需要单独引入import '@babel/polyfill'。

       5、babel-loader:当使用webpack打包js时,webpack并不知道如何调用这些规则去编译js。此时,就需要babel-loader作为中间桥梁,通过调用babel/core中的api来告诉webpack如何处理js。

       6、@babel/plugin-transform-runtime:polyfill的垫片是在全局变量上挂载目标浏览器缺失的功能,因此在开发类库、第三方模块或组件库时,不能使用babel-polyfill,否则可能会造成全局污染。此时,应使用transform-runtime,它的转换是非侵入性的,不会污染原有的方法。

一口气解决项目中JS 所有精度丢失问题!

       在 JavaScript 中,精度丢失问题是一个常见的挑战。当你试图计算像 0.1 + 0.2 的结果时,实际输出的可能并非你期待的 0.3。这是因为 JavaScript 的浮点数存储机制导致的。要解决这个问题,可以借助 decimal.js 这个库,它提供了高精度的数学运算。

       decimal.js 的使用相当简单,只需引入库并进行相应的操作,如 `new Decimal(0.1).add(0.2)`,就能得到正确的结果。然而,频繁手动引入和使用 decimal.js 可能会变得繁琐,尤其是对于大型项目。

       为了解决这个问题,我决定创建一个 babel 插件。通过利用 babel 插件,我们可以自动将项目中的浮点数运算转换为 decimal.js 的形式。例如,表达式 `0.1 + 0.2` 将被转换为 `new Decimal(0.1).add(0.2)`,ims源码从而消除精度丢失。

       开发插件需要准备 Rollup 打包环境,定义一个 babel 插件文件,并配置 Rollup。在插件代码中,我们利用抽象语法树(AST)进行操作,找出浮点数运算节点并替换为 decimal.js 的相应方法。关键点在于遍历 AST,并在适当位置插入 decimal.js 的导入和调用。

       实现后,通过 `npm run build` 打包并发布到 NPM,然后在项目中安装并配置 babel-plugin-sx-accuracy。例如,只需在 .babelrc 或 babel.config.js 中添加该插件,代码如 `console.log(0.1 + 0.2)` 就会自动转换为 decimal.js 的精确运算,输出结果不会丢失精度。这样,你就可以在项目中轻松避免精度问题,而无需频繁手动处理。

import方式随意互转,感受babel插件的威力

       当我们import一个模块的时候,可以这样默认引入:

importpathfrom'path';path.join('a','b');functionfunc(){ constsep='aaa';console.log(path.sep);}

       也可以这样解构引入:

import{ join,sepas_sep}from'path';join('a','b');functionfunc(){ constsep='aaa';console.log(_sep);}

       第一种默认引入叫defaultimport,第二种解构引入叫namedimport。

       不知道大家习惯用哪一种。

       如果有个需求,让你把所有的defaultimport转成namedimport,你会怎么做呢?

       可能你会说这个不就是找到所有用到引入变量的地方,修改成直接调用方法,然后那些方法名以解构的方式写在import语句里么。

       但如果说要改的项目有多个这种文件呢?(触发treeshking就需要这么改)

       这时候就可以考虑Babel插件了,它很适合做这种有规律且数量庞大的代码的自动修改。

       让我们通过这个例子感受下babel插件的威力吧。

       因为代码比较多,大家可能没耐心看,要不我们先看效果吧:

测试效果

       输入代码是这样:

importpathfrom'path';path.join('a','b');functionfunc(){ constsep='aaa';console.log(path.sep);}

       我们引入该babel插件,读取输入代码并做转换:

const{ transformFileSync}=require('@babel/core');constimportTransformPlugin=require('./plugin/importTransform');constpath=require('path');const{ code}=transformFileSync(path.join(__dirname,'./sourceCode.js'),{ plugins:[[importTransformPlugin]]});console.log(code);

       打印如下:

       我们完成了defaultimport到namedimport的自动转换。

       可能有的同学担心重名问题,我们测试一下:

       可以看到,插件已经处理了重名问题。

思路分析

       import语句中间的部分叫做specifier,我们可以通过astexplorer.net来可视化的查看它的AST。

       比如这样一条import语句:

importReact,{ useStateastest,useEffect}from'react';

       它对应的AST是这样的:

       也就是说默认import是ImportDefaultSpecifier,而解构import是ImportSpecifier

       ImportSpecifier语句有local和imported属性,分别代表引入的名字和重命名后的名字:

       那我们的目的明确了,就是把ImportDefaultSpecifier转成ImportSpecifier,并且使用到的属性方法来设置imported属性,需要重命名的还要设置下local属性。

       怎么知道使用到哪些属性方法呢?也就是如何分析变量的引用呢?

       babel提供了scope的api,用于作用域分析,可以拿到作用域中的声明,和所有引用这个声明的地方。

       比如这里就可以用scope.getBinding方法拿到该变量的声明:

constbinding=scope.getBinding('path');

       然后用binding.references就可以拿到所有引用这个声明的地方,也就是path.join和path.sep。

       之后就可以把这两处引用改为直接的方法调用,然后修改下import语句为解构就可以了。

       我们总结一下步骤:

       找到import语句中的ImportDefaultSpecifier

       拿到ImportDefaultSpecifier在作用域的声明(binding)

       找到所有引用该声明的地方(reference)

       修改各处引用为直接调用函数的形式,收集函数名

       如果作用域中有重名的变量,则生成一个唯一的函数名

       根据收集的函数名来修改ImportDefaultSpecifier为ImportSpecifier

       原理大概过了一遍,我们来写下代码

代码实现

       babel插件是函数返回对象的形式,返回的对象中主要是通过visitor属性来指定对什么AST做什么处理。

       我们搭一个babel插件的骨架:

const{ declare}=require('@babel/helper-plugin-utils');constimportTransformPlugin=declare((api,options,dirname)=>{ api.assertVersion(7);return{ visitor:{ ImportDeclaration(path){ }}}});module.exports=importTransformPlugin;

       这里我们要处理的是import语句ImportDeclaration。

       @babel/helper-plugin-utils包的declare方法的作用是给api扩充一个assertVersion方法。而assertVersion的作用是如果这个插件工作在了babel6上就会报错说这个插件只能用在babel7,可以避免报的错看不懂。

       path是用于操作AST的一些api,而且也保留了node之间的关联,比如parent、sibling等。

       接下来进入正题:

       我们要先取出specifiers的部分,然后找出ImportDefaultSpecifier:

ImportDeclaration(path){ //找到import语句中的defaultimportconstimportDefaultSpecifiers=path.node.specifiers.filter(item=>api.types.isImportDefaultSpecifier(item));//对每个defaultimport做转换importDefaultSpecifiers.forEach(defaultSpecifier=>{ });}

       然后对每一个defaultimport都要根据在作用域中的声明找到所有引用的地方:

//import变量的名字constimportId=defaultSpecifier.local.name;//该变量的声明constbinding=path.scope.getBinding(importId);binding.referencePaths.forEach(referencePath=>{ });

       然后对每个引用到该import的地方都做修改,改为直接调用函数,并且把函数名收集起来。这里要注意的是,如果作用域中有同名变量还要生成一个新的唯一id。

//该变量的声明constbinding=path.scope.getBinding(importId);constreferedIds=[];consttransformedIds=[];//收集所有引用该声明的地方的方法名binding.referencePaths.forEach(referencePath=>{ constcurrentPath=referencePath.parentPath;constmethodName=currentPath.node.property.name;//之前方法名referedIds.push(currentPath.node.property);if(!currentPath.scope.getBinding(methodName)){ //如果作用域没有重名变量constmethodNameNode=currentPath.node.property;currentPath.replaceWith(methodNameNode);transformedIds.push(methodNameNode);//转换后的方法名}else{ //如果作用域有重名变量constnewMethodName=referencePath.scope.generateUidIdentifier(methodName);currentPath.replaceWith(newMethodName);transformedIds.push(newMethodName);//转换后的方法名}});

       这部分逻辑比较多,着重讲一下。

       我们对每个引用了该变量的地方都要记录下引用了哪个方法,比如path.join、path.sep就引用了join和sep方法。

       然后就要把path.join替换成join,把path.sep替换成sep。

       如果作用域中有了join或者sep的声明,需要生成一个新的id,并且记录下新的id是什么。

       收集了所有的方法名,就可以修改import语句了:

import{ join,sepas_sep}from'path';join('a','b');functionfunc(){ constsep='aaa';console.log(_sep);}0

       没有babel插件基础可能看的有点晕,没关系,知道他是做啥的就行。我们接下来试下效果。

思考和代码

       我们做了defaultimport到namedimport的自动转换,其实反过来也一样,不也是分析scope的binding和reference,然后去修改AST么?感兴趣的同学可以试下反过来转换怎么写。

       插件全部代码如下:

import{ join,sepas_sep}from'path';join('a','b');functionfunc(){ constsep='aaa';console.log(_sep);}1总结

       我们要做defaultimport转namedimport,也就是ImportDefaultSpecifier转ImportSpecifier,要通过scope的api分析binding和reference,找到所有引用的地方,替换成直接调用函数的形式,然后再去修改import语句的AST就可以了。

       babel插件特别适合做这种有规律且转换量比较大的需求,在一些场景下是有很大的威力的。

想弄懂Babel?你必须得先弄清楚这几个包

       Babel 是一个工具链,用于将采用 ECMAScript + 语法编写的代码转换为兼容旧版本的 JavaScript 语法,以在当前和旧版本的浏览器或其他环境中运行。

       Babel 主要通过 plugins、presets 这两个配置来实现转换功能。plugins 负责具体语法转换,而 presets 是一个语法插件集合包,简化了新特性的配置过程。

       @babel/core 是 Babel 实现编译的核心,是使用 Babel 的必要组件。版本如 Babel 6、Babel 7 实际指的就是 @babel/core 的版本。

       @babel/cli 是 Babel 自带的 CLI 命令行工具,允许在终端中编译文件,便于调试并提供打印信息功能。安装时,最好将其安装到项目本地目录,避免全局安装。

       @babel/preset-env 是一个智能预设,支持使用最新 JavaScript 特性,无需微观管理目标环境所需语法转换及浏览器补丁的导入。这简化了编码过程,使代码包更小。

       @babel/preset-env 的 preset 是语法插件集合,env 指运行目标环境,控制补丁导入和语法编译,确保 ES6+ 特性在目标环境中正常运行。

       @babel/preset-env 提供以下主要功能:不包括 stage-3 以下的语法提案,因为这些在 TC 过程中未被浏览器实现。用户需手动配置相应的 plugin。

       @babel/polyfill 是依赖 core-js@2.x.x 实现的 polyfill 包,用于提供旧版本浏览器缺失的 API。它与 2 版本的 core-js 绑定,从 3 版本开始才有 stable 文件夹。

       @babel/runtime 是一个包含 Babel 模块化运行时助手的库,辅助函数实现 ES6+ 语法糖的内联插入,节省代码大小。

       @babel/plugin-transform-runtime 插件用于重用 Babel 注入的帮助程序代码,避免重复代码生成,减少最终输出包体积。

       Babel 的使用结合 preset-env、polyfill、runtime 插件,能高效地编译 ES6+ 代码,适应多种目标环境,同时减小代码包大小,优化开发流程。

Babel教程7:Babel配置文件

       在深入学习Babel配置文件之前,我们先了解其作用。无论是通过命令行工具babel-cli,还是构建工具如webpack进行编译,都需要配置文件来指定编译规则。Babel的配置文件是默认在当前目录寻找的文件,如.babelrc、.babelrc.js、babel.config.js和package.json。它们配置项相同,作用一致,选择其一即可。对于.babelrc文件,配置如下。babel.config.js和.babelrc.js通过module.exports输出配置项。在package.json中增加babel属性配置。观察这些配置文件,会发现配置项主要是plugins和presets。

       配置文件的主要内容是配置plugins和presets数组,我们称其为插件数组和预设数组。除了plugins和presets,还有minified、ignore等配置项,但这些在日常使用中很少用到,因此建议专注于plugins和presets的学习。推荐使用后缀名js的配置文件,因为它可以使用JavaScript逻辑,更灵活。例如:

       在配置文件中使用插件和预设时,要明确它们的作用。插件和预设分别位于plugins和presets数组中,是npm包。Babel提供了大量插件和预设,处理不同版本的ECMAScript标准。例如,处理ES、ES等。所有插件和预设在使用前需要先安装到node_modules中。

       配置文件中的插件和预设数量众多,编写时可能会显得臃肿。为了解决这一问题,Babel提供了预设,预设是一组插件的集合,简化了配置。例如,babel-preset-es包含处理ES所需的所有插件。这样只需配置预设,而无需列出所有插件,减少了配置工作量。预设也可以是插件和其它预设的组合。

       配置文件中的插件和预设可以使用短名称,如babel-plugin-前缀的插件,可以省略前缀。同样,预设名称前缀为babel-preset-或@xxx/babel-preset-xxx的可以省略部分前缀。但是,推荐使用全称以避免可能的混淆。

       在配置文件的plugins插件数组和presets预设数组中,遵循一定的顺序规则。如果两个插件或预设需要处理同一段代码,那么将按照插件和预设的顺序执行。

       每个插件或预设数组成员项默认为字符串形式,表示名称。若需设置参数,则将其转换为数组形式,包含插件或预设名称和参数对象。

       综上所述,Babel配置文件的使用涵盖了插件、预设的配置以及参数设置等内容。在实际开发中,根据项目需求选择合适的插件和预设。后续教程将详细介绍如何在开发中选择适合的Babel插件和预设。

解剖Babel —— 向前端架构师迈出一小步

       解剖Babel,前端架构师的探索之旅

       Babel,作为前端工程基石,其功能远超API polyfill的简单定义。它是一个JavaScript编译器,负责接收并处理代码,转化为兼容目标环境的代码。这个过程涉及到Babel的核心组件,如preset、plugin和runtime等。

       尽管preset和plugin概念初学者可能感到困惑,但它们是Babel实现编译和扩展功能的关键。preset是插件的集合,允许根据特定目标环境动态调整编译行为。而plugin则提供了插件化接口,开发者可以通过它们定制编译过程。

       深入Babel的底层,核心模块如@babel/parser解析JavaScript源代码,生成抽象语法树(AST),再由@babel/traverse、@babel/types和@babel/generator等处理,最终输出修改后的代码。核心库@babel/core负责整合这些功能。

       上层功能中,Babel通过polyfill和语法转换支持高级特性向低版本浏览器和环境兼容。@babel/polyfill和@babel/preset-env是实现这些功能的重要工具,前者是core-js和regenerator-runtime的组合,而后者则允许按需加载特性,优化打包体积。

       学习Babel对前端架构师来说至关重要,它涉及的底层模块如@babel/plugin-*提供了API接入点,而preset-*如@babel/preset-env则是日常开发中的实用工具。掌握这些,对构建高效、兼容的前端工程至关重要。

       参考资料:

       [1] Babel仓库: github.com/babel/babel/

       [2] AST explorer: astexplorer.net/

       [3] core-js仓库: github.com/zloirock/core-js/

       [4] Browserslist: github.com/browserslist/

       [5] Babel v7.4.0: babeljs.io/docs/en/babel/

       [6] babel-plugin-syntax-decorators: github.com/babel/babel/

       [7] Babel playground: babeljs.io/repl/

相关栏目:时尚