当前位置: 首页 > news >正文

【翻译】babel对TC39装饰器草案的实现

原文地址:https://babeljs.io/blog/2018/...
原文作者:Nicolò Ribaudo

Babel 7.1.0最终支持新的装饰器提案,可以通过@babel/plugin-proposal-decorators插件使用。

历史
装饰器这个概念三年多前被Yehuda Katz首次提出。TypeScript在版本1.5(2015年)中发布了对装饰器的支持以及许多ES6特性。很多主流框架,像Angular和MobX,为了提高开发体验也开始使用装饰器。这些使装饰器变得很流行,并且给了社区一种很稳定的错觉。

Babel在版本5里面首次实现了装饰器,但在版本6的时候移除了,因为提案在不断的变化。Logan Smyth创建了一个非官方的插件(babel-plugin-transform-decorators-legacy)来代替Babel5里面的装饰器,在第一个Babel7 alpha版本发布的时候的时候,它被移到了Babel官方的存储库。这个插件还是使用老版本的插件语法,因为还不清楚新的提案会变成什么样。

从那个时候开始,Daniel Ehrenberg和Brain Terlson和Yehuda Katz一起成为了提案的作者,提案几乎完全被重写了。并非所有的事情都已经确定,而且目前也没有合规实施的方案。

Babel7.0.0为@babel/plugin-proposal-decorators插件介绍了一个新的标志:配置项legacy的唯一有效值为true。为了从提案的第一阶段平滑过渡到当前版本,需要有这种重大的改变。

在Babel7.1.0,我们引入了对这个新提案的支持,并且在使用@babel/plugin-proposal-decorators插件的时候会默认启用。如果我们不在Babel7.0.0里引入配置项legacy为true的话,在默认情况下就不可能使用正确的语义(也就意味着配置项legacy的值为false)

新的提案还支持私有字段和方法上的装饰器。我们还没有在Babel中实现这个功能(对于每个类而言,你可以使用装饰器或者私有元素),但很快就会实现的。

新的提案的改变点
尽管新的提案看上去跟旧的很相似,但还是有一些重要的不同点。

  • 语法

    旧的提案允许任何有效的左侧表达式(文字、函数和类表达式,new表达式和函数调用,简单和计算属性访问)作为装饰器的主体:

    class MyClass {
         @getDecorators().methods[name]
         foo() {}
    
         @decorator
         [bar]() {}
     }

    这个语法有一个问题:[...]这个符号在装饰器里进行属性访问以及定义计算属性名字的时候也会被用到。为了消除这个歧义,新的提案值允许用点符号来进行属性访问(foo.bar),也可以在最后加上一个括号(foo.bar())。如果你需要更多复杂的表达式,你可以用括号括起来:

    class MyClass{ 
         @decorator
         @dec(arg1, arg2)
         @namespace.decorator
         @(complex ? dec1 : dec2)
         method() {}
     }
  • 对象装饰器
    旧版本的提案允许出现除了类和类元素装饰器之外的对象成员装饰器:

    const myObj = {
      @dec1 foo: 3,
      @dec2 bar() {},
    };

    由于跟当前对象的一些表达语法的不兼容性,在提案中被移除了。如果你在你的代码中使用了对象成员装饰器,继续关注因为它们可能会在后续提案中被引入。

  • 函数装饰器的参数
    新提案引入的第三个重要变化是关于传递给装饰器函数的参数。

    在第一版提案中,类元素装饰器接受一个目标类(对象),一个变量,和一个属性描述符-类似于传递给Object.defineProperty的参数。类装饰器将目标构造函数作为唯一的参数。

    新的提案的装饰器更强大一些:元素装饰器接受一个对象,该对象除了更改属性操作符之外,还允许更改变量值,位置(staticprototype或者own)以及元素的种类(fieldmethod)。他们还可以创建其他的属性并定义运行在类装饰器里的函数。
    类装饰器接受一个包含每个类元素的描述符的对象,从而保证可以在创建类之前修改它们。

  • 升级
    由于这些不兼容性,不能在现有的装饰器上使用新的提案:这会让升级特别慢,因为现有的库(MobX,Angular等)不能再没有引入重大改变的情况下进行升级。为了解决这个问题,我们已经发布了一个实用程序包,它将装饰器包装在你的代码里。运行这个之后,你可以安全地修改你的Babel配置以使用新的提案。
    你可以使用下面这行代码去升级文件:

    npx wrap-legacy-decorators src/file-with-decorators.js --decorators-before-export --write 

    如果你的代码只在Node中运行,或者你用Webpack或Rollup打包你的代码,你可以使用外部依赖来避免在每个文件中都注入包装函数:

    npm install --save decorators-compat npx wrap-legacy-decorators src/file-with-decorators.js --decorators-before-export --external-helpers --write

开放问题
并非所有的事情都已经确定:装饰器是一个非常大的功能,而且要以最好的方式定义它们是非常复杂的。

  • 导出类的装饰器应该放在哪里
    这个问题在装饰器的提案里反复出现:装饰器应该在export这个关键字的前面还是后面?

    export @decorator class MyClass {}
    
    // or
    
    @decorator
    export class MyClass {}

    根本问题是export关键字是否是类声明的一部分,还是只是一个“包装器”。如果是前一种情况,它应该放在装饰器的后面,因为装饰器出现在声明的开头;在第二种情况下,它应该在装饰器前面,因为装饰器是类装饰器的一部分。

  • 如何让装饰器和私有元素安全地互动?
    装饰器引起了重要的安全问题:如果可以装饰私有元素,那么私有名称(也可以称为私有属性的变量名)可能会被泄漏。有不同的安全级别需要考虑:
    1) 装饰器不应该意外泄漏私有名称。恶意代码不应该以任何方式从其他装饰器中“窃取”私有名称。
    2) 只有直接应用于私有元素的装饰器才能被视为可信任:类装饰器应该无法读写私有元素?
    3) 硬私有(类字段提案的目标之一)意味着私有元素应该只能有类的内部访问:任何装饰器是否可以访问私有名称?装饰器只能装饰公共元素么?
    这些问题需要进一步讨论才能解决,这也是Babel的用武之地。

    Babel的作用
    随着What's Happening With the Pipeline(|>) Proposal?这篇文章里的趋势,随着Babel7的发布,我们开始利用我们在JS生态系统中的位置,通过让开发人员测试和反馈有关提案的不同版本的体验来帮助提案的提出者们。
    由于这个原因,在@babel/plugin-proposal-decorators更新的同时,我们也引入了一个新的属性:decoratorsBeforeExport,允许用户同时使用export @decorator class C {}@decorator export default class
    我们也将引入一个属性来自定义私有属性装饰器的隐私约束。在TC39人员做出决定之前,这些属性是必需的,这样我们可以让默认行为成为最终提案知道的内容。
    如果你直接使用我们的解析器(@babel/parse,以前的babylon),你已经可以在版本7.0.0里使用decoratorsBeforeExport属性:

    const ast = babylon.parse(code, {
      plugins: [
        ["decorators", { decoratorsBeforeExport: true }]
      ]
    })

    用法
    Babel用法:
    shell版本:

    npm install @babel/plugin-proposal-decorators --save-dev

    JSON版本:

    {
      "plugins": ["@babel/plugin-proposal-decorators", {"decoratorsBeforeExport": true }]
    }

    查看@babel/plugin-proposal-decorators文档了解更多属性。

    你的作用
    作为JavaScript的开发人员,你可以帮助概述该语言的未来。你可以测试装饰器的新语法,并向提案的作者们提出反馈意见。我们需要知道你在现实生活的项目中是怎么使用它们的。你也可以通过阅读问题中的讨论和proposal's repository中的笔记中发现为什么要这样设计。
    如果你想要立即尝试装饰器,你可以在我们的repl里使用不同的预设属性值。

相关文章:

  • Fabric架构演变之路
  • CSS实用技巧干货
  • Objective-C 中关联引用的概念
  • 鱼骨图 - 如何绘制?
  • Centos安装gerrit
  • 模型微调
  • 专属程序员的西游记,不是程序员读不懂哦?
  • 第十八天-企业应用架构模式-基本模式
  • 人脸识别最新开发经验demo
  • 百度地图api文档实现任意两点之间的最短路线规划
  • 链表
  • Spark一些必须知道的概念
  • Linux系列(5)linux基础命令
  • 08r2活动目录迁移升级2012r2--(DHCP迁移)
  • 唯一分解定理
  • ----------
  • 9月CHINA-PUB-OPENDAY技术沙龙——IPHONE
  • ECMAScript6(0):ES6简明参考手册
  • ES6核心特性
  • JavaScript新鲜事·第5期
  • js操作时间(持续更新)
  • ReactNativeweexDeviceOne对比
  • Sublime Text 2/3 绑定Eclipse快捷键
  • vue 个人积累(使用工具,组件)
  • 工作踩坑系列——https访问遇到“已阻止载入混合活动内容”
  • 海量大数据大屏分析展示一步到位:DataWorks数据服务+MaxCompute Lightning对接DataV最佳实践...
  • 前端性能优化——回流与重绘
  • 软件开发学习的5大技巧,你知道吗?
  • 我这样减少了26.5M Java内存!
  • 消息队列系列二(IOT中消息队列的应用)
  • 学习Vue.js的五个小例子
  • 远离DoS攻击 Windows Server 2016发布DNS政策
  • AI算硅基生命吗,为什么?
  • AI又要和人类“对打”,Deepmind宣布《星战Ⅱ》即将开始 ...
  • ​LeetCode解法汇总2583. 二叉树中的第 K 大层和
  • # 20155222 2016-2017-2 《Java程序设计》第5周学习总结
  • #{}和${}的区别是什么 -- java面试
  • #Z0458. 树的中心2
  • #我与Java虚拟机的故事#连载01:人在JVM,身不由己
  • (02)vite环境变量配置
  • (1) caustics\
  • (20050108)又读《平凡的世界》
  • (Matlab)基于蝙蝠算法实现电力系统经济调度
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • (排序详解之 堆排序)
  • (三)uboot源码分析
  • (算法二)滑动窗口
  • .bat批处理(五):遍历指定目录下资源文件并更新
  • .net CHARTING图表控件下载地址
  • .NET Core MongoDB数据仓储和工作单元模式封装
  • .NET/C# 使用 SpanT 为字符串处理提升性能
  • .net获取当前url各种属性(文件名、参数、域名 等)的方法
  • @WebServiceClient注解,wsdlLocation 可配置
  • [ 云计算 | AWS 实践 ] 基于 Amazon S3 协议搭建个人云存储服务
  • [Android 13]Input系列--获取触摸窗口