博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[译]在 Babel 中支持 TC39 标准的装饰器
阅读量:6094 次
发布时间:2019-06-20

本文共 3832 字,大约阅读时间需要 12 分钟。

  • 原文地址:
  • 原文作者:
  • 译文出自: ·
  • 译者:
  • 校对者: ,

欢迎拍砖,协助我们更好的建设中文社区。

Babel 7.1.0 最终支持了新的装饰器提案:你可以使用 插件来提前尝试此功能 ?。

相关历史

三年多以前, 了装饰器的概念。TypeScript 在 (2015)中发布了对装饰器的支持以及许多 ES6 的相关特性。 一些主流框架,如 Angular 和 MobX 等开始使用它们来增加开发者体验:这使得装饰器非常受欢迎,并给社区带来了一种已经稳定的错觉。

Babel 第一次实现装饰器是在 ,但由于该提案仍在不断变化,则在 Babel v6 中移除了它们。 创建了一个非官方的插件(),它延用了 Babel 5 中装饰器的行为;在 Babel 7 的 alpha 版本发布期间该库被移至 Babel 官方的仓库中。当时该插件仍使用旧的装饰器语法,因为新提案尚未明确。

自那时起,、 以及 就一起成为了该提案的共同作者,该提案几乎已被完全重写。当然并非一切事情都已确定,因为至今尚未出现符合规范的实现方式。

Babel 7.0.0 为 @babel/plugin-proposal-decorators 插件引入了新的标识:legacy 选项,其唯一有效值为 true。这种突破性变更是必要的,它为提案从第一阶段到当前阶段平稳过渡作铺垫。

在 Babel 7.1.0 中,我们引入了对这个新提案的支持,并且当 @babel/plugin-proposal-decorators 插件被使用时,默认启用。而在 Babel 7.0.0 中如果我们不设置 legacy: true 选项,默认情况下就不能使用该语义(相当于 legacy: false)。

新提案同时支持使用装饰器实现私有字段(private fields)和私有方法(private methods)。我们尚未在 Babel 中实现此功能(在每个 class 中使用装饰器或私有元素),但我们会很快去出现它。

新提案有何变化?

尽管新提案看起来与旧提案非常相似,但还是有几个重要的差异使得它们互不兼容。

语法

旧提案允许任何有效的左表达式(字面量,函数,类表达式,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() {},};复制代码

由于与当前对象字面量语义的某些不兼容性,它们已从提案中被移除。如果你的代码中使用了它们,请继续关注,因为它们可能会在后续提案中被重新引入。()

装饰器函数相关参数

新提案提出的第三个重要变化与传递给装饰器函数参数相关。

在提案的第一个版本中,类元素装饰器接收的参数分别为目标类(或对象),key 以及属性描述符 - 与传递给 Object.defineProperty 的形式类似。类装饰器将目标构造函数(constructor)作为唯一参数。

新的装饰器提案更加强大:元素装饰器会接收一个对象,该对象除更改属性描述符外,还允许更改 key 值,可以赋值(staticprototype 或者 own),以及元素的类型(fieldmethod)。它们还可以创建其他属性并在装饰类上定义运行函数(完成器)。

类装饰器接收一个包含类描述符的对象,使得类在创建之前修改它们成为可能。

升级

鉴于这些不兼容性问题,新提案中不可能使用现有的装饰器:这将使得迁移变得缓慢,因为现有库(MobX,Angular等)无法在不引入这些突破性变化的情况下进行升级。 为解决此问题,我们发布了实用工具包,它将装饰器包装在你的代码当中。运行后, 你可以安心的更改你的 Babel 配置以便使用新提案 ?。

使用如下命令来升级你的文件:

npx wrap-legacy-decorators src/file-with-decorators.js --decorators-before-export --write复制代码

如果你的代码仅在 Node 中运行,或者你会使用 webpack 或 rollup 构建你的代码,则需要使用外部依赖项(external dependency),避免在每个文件中注入包装函数:

npm install --save decorators-compatnpx wrap-legacy-decorators src/file-with-decorators.js --decorators-before-export --external-helpers --write复制代码

欲了解更多信息,请参阅.

开放问题

并非所有内容都已确定:装饰器是一个非常强大的功能,想要将装饰器定义为最好的表现形式,是相当复杂的。

导出类(exported class)装饰器如何放置?

该问题在装饰器提案中反复被问到:装饰器应该放置在关键字 export 前还是关键字 export 后?

export @decorator class MyClass {}// 或者@decoratorexport class MyClass {}复制代码

根本问题是 export 关键字是否是类声明的一部分,或者它是否是一个"包装器"。第一种情况下,它应该在装饰器之后,因为装饰器出现在声明的起始位置;第二种情况下,它应该在装饰器之前,因为装饰器是类声明的一部分。

如何让装饰器与私有元素安全地交互?

,

装饰器引发了一个重要的安全隐患:如果装饰私有元素,那么私有名称(可以视为私有元素的 "key")可能会被泄露。有不同的安全级别需要考虑:

  1. 装饰器有意外泄露私有名称的风险。恶意代码不应该以任何方式从其他装饰器中"窃取"私有名称。
  2. 只有直接应用于私有元素的装饰器才被视为可信任:类装饰器是不是不应该读写私有元素?
  3. 高度隐私 (class fields 提案的目标之一) 意味着私有元素只能从类内部访问:是否需要让任何装饰器都可以访问私有名称?是否应该只装饰公共元素?

这些问题需要在解决之前进一步讨论,这正是 Babel 所存在的意义。

Babel 的作用

遵循 文章中的走向,随着 Babel 7 的发布,我们开始利用我们在 JS 生态系统中的地位,通过让开发人员能够测试提案的不同变体,根据他们给出的反馈来帮助提案的作者完善提案。

出于这样的角度,随着 @babel/plugin-proposal-decorators 的更新,我们引入了新的选项:decoratorsBeforeExport,它允许用户尝试使用 export @decorator class C {}@decorator export default class

我们还将采用一个选项来定制装饰器私有元素的隐私约束。使用该选项是必要的,直到 TC39 人员对它们做出选择,由此就可以让默认行为指定为最终提案中的内容。

如果你直接使用 (,之前的 babylon),你可以在 7.0.0 版本中使用 decoratorsBeforeExport 选项:

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

用法

用于 Babel 本身:

npm install @babel/plugin-proposal-decorators --save-dev复制代码
{  "plugins": ["@babel/plugin-proposal-decorators", { "decoratorsBeforeExport": true }]}复制代码

查阅 文档以获取更多相关选项。

你的作用

作为 JavaScript 开发者,你可以帮助规划改语言的未来。你可以为装饰器考虑各种语义环境同时进行测试,并向提案的作者提出反馈。我们需要知道你在真实项目环境中是如何使用它们的!你还可以通过阅读中的 issues 讨论及会议记录来找出为什么最终做出这样的设计决策。

如果想立即尝试装饰器,可以使用我们的 配置不同的 preset 选项进行试用!

关注我们

扫码关注我们的公众号,我们会定期推送一下社区相关的文章和动态。

转载于:https://juejin.im/post/5c35c87cf265da616c65cc00

你可能感兴趣的文章
Eclipse编译PostgreSQL 9.2.2
查看>>
Class字节码文件结构详解
查看>>
[又拍云]云计算中又一架重要的马车
查看>>
Ruby on Rails 学习笔记(一)
查看>>
Linux下安装pymysql步骤
查看>>
PHP+MySQL数据库教程
查看>>
jQuery UI Datepicker 添加时分秒
查看>>
linux查看和终止进程
查看>>
Beyond Compare比较表格小技巧
查看>>
以太坊·代币开发详解
查看>>
LibreOffice 中文版安装指南
查看>>
c++中调用c代码的3 种方法
查看>>
ISO C 标准定义的头文件
查看>>
nginx配置
查看>>
joomla1.5前台组件开发过程分享(附中文开发教程两本)
查看>>
2017/5 JavaScript基础11 --- 正则与模式匹配
查看>>
aix mount: 0506-351 错误处理
查看>>
SpringBoot集成Redis来实现缓存技术方案
查看>>
漏洞防御与修复工作
查看>>
解决GitHub下载速度太慢问题
查看>>