如何使用Vanilla JavaScript编写Atom程序包

本文由Vildan Softic进行同行评审。 感谢所有SitePoint的同行评审人员使SitePoint内容达到最佳状态!

Atom是现代的,可入侵的核心编辑器。 很好,但是对于不熟练使用CoffeeScript的开发人员,很难遵循文档。 了解Atom的生态系统可能会造成混乱。 让我们研究一下如何用JavaScript编写Atom程序包的各个方面。

了解原子

Atom是一个基于Node.jsChromium的应用程序,使用GitHub的Electron框架编写。 从技术上讲,这意味着它是在桌面上运行的Web应用程序。 Atom的内部功能被分解为微型核心程序包 ; 它们的开发方式与社区中的任何其他软件包相同。 尽管它们都是用CoffeeScript编写的,但是可以用纯JavaScript编写它们,也可以通过Babel对其进行转换。

通过Babel激活对ES2015的全面支持

Babel是一个源到源编译器 ; 将ECMAScript 2015(以前称为ES6)代码转换为ECMAScript 5代码。 由于环境是Chromium,因此已经有很多受支持的ES2015功能可用。 但是,我建议不要使用Babel来编译您的代码,而不是总是查找实现了哪些代码。 在更高版本中(当Chromium更好地支持ES2015时),您可以再次停用Babel,并使代码库(几乎)保持不变。

要使用Babel激活转译,每个文件都需要一个'use babel'; 开头的语句,类似于ECMAScript 5中的严格模式 。这还使您能够通过省略语句来决定应转储哪些文件,而不应转储哪些文件。

package.json

这有助于将Atom软件包视为npm模块 。 您对API的访问权限与在Node.js上运行的任何工具相同。 因此,可以添加任何需要的npm依赖项。 还需要package.json ,其中包含项目的所有元数据。 基本文件应如下所示:

{
  "name": "your-package",
  "main": "./lib/main",
  "version": "0.1.0",
  "description": "A short description of your package",
  "keywords": [
    "awesome"
  ],
  "repository": "https://github.com/<your-name>/<package>",
  "license": "MIT",
  "engines": {
    "atom": ">=1.0.0 <2.0.0"
  },
  "dependencies": {
  }
}

重要的键是main (主键),它定义软件包的主入口点(默认为index.js / index.coffee ),以及enginesindex.coffee ),它们告诉Atom软件包在哪个版本上运行。 还有一组可用的可选键, 记录在“ wordcount”包文档package.json节)中

包源代码

您的所有程序包代码都位于顶级目录lib/ 。 我建议您也将入口点放在此文件夹中,因为它可以使结构保持整洁并使扫描项目更容易。

您的主文件必须是一个单例对象,该对象必须维护包的整个生命周期。 即使您的程序包仅包含一个视图,也将全部从此对象进行管理。 您的入口点需要一个activate()方法,但还应具有可选的 deactivate()serialize()

// lib/main.js
'use babel';

// This is your main singleton.
// The whole state of your package will be stored and managed here.
const YourPackage = {
  activate (state) {
    // Activates and restores the previous session of your package.
  },
  deactivate () {
    // When the user or Atom itself kills a window, this method is called.
  },
  serialize () {
    // To save the current package's state, this method should return
    // an object containing all required data.
  }
};

export default YourPackage;

激活你的包裹

activate()函数是唯一需要的方法。 在这里初始化所有模块,视图或帮助器。 它传递了一个对象,该对象包含程序包的先前序列化状态。 如果您不序列化包中的任何内容,它将是一个空对象。 这意味着,序列化的内容完全取决于您和您的包体系结构。

停用

deactivate()方法是可选的,但很重要。 当窗口关闭或用户在设置中将其禁用时,它将由Atom调用。 当您的软件包被用户停用时,并且您不处置添加的事件/命令时, 它们仍然可用 。 当Atom关闭窗口时,这不是问题。 它将拆除事件和命令。 但是,如果您的程序包正在监视文件或正在执行其他任何工作,则应在deactivate()释放它们。

活动订阅

程序包通常订阅多个事件,例如添加自定义命令,侦听更改或修改文件。 可以将它们捆绑到CompositeDisposable()的实例中,这样就可以一次将它们全部处理掉。

// lib/main.js
import { CompositeDisposable } from 'atom';

const YourPackage = {
  subscriptions: null,

  activate (state) {
    // Assign a new instance of CompositeDisposable...
    this.subscriptions = new CompositeDisposable();

    // ...and adding commands.
    this.subscriptions.add(
      atom.commands.add('atom-workspace', {
        'your-package:toggle': this.togglePackage
      })
    );
  },

  // When your package get's deactivated, all added
  // subscriptions will be disposed of at once.
  deactivate () {
    this.subscriptions.dispose();
  },

  togglePackage () {
    // Code to toggle the package state.
  }
};

序列化所有东西!

序列化是Atom软件包的强大功能,但又是可选功能。 当窗口从上一个会话关闭,刷新或还原时,会发生序列化/反序列化。 由您决定应由哪些组件和多少组件来序列化其数据。 重要的是它返回JSON。 如果您有一个视图,并且希望能够对其进行刷新,则需要使其与序列化和反序列化兼容。

这个非常基本的组件带有一个对象,该对象将用作组件的内部数据。 然后,您的组件可能会对数据进行一些处理,并可以通过serialize()方法将其状态序列serialize()

// lib/fancy-component.js
class FancyComponent {
  constructor (configData) {
    this.data = configData;
  }

  // This method will be called when the class
  // is restored by Atom.
  static deserialize (config) {
    return new FancyComponent(config);
  }

  // The returned object will be used to restore
  // or save your data by Atom.
  // The "deserializer" key must be the name of your class.
  serialize () {
    return {
      deserializer: 'FancyComponent',
      data: this.data
    };
  }

  doSomethingWithData () {}
}

// Add class to Atom's deserialization system
atom.deserializers.add(FancyComponent);

export default FancyComponent;

为了使所有这些有用,必须在您的包主单例中调用此组件并将其序列化。

// lib/main.js
import FancyComponent from './fancy-component';
import SomeView from './some-view';

const YourPackage = {
  fancyComponent: null,
  someView: null,

  activate (state) {
    // If the component has been saved at a previous session of Atom,
    // it will be restored from the deserialization system. It calls your
    // your components static 'deserialize()' method.
    if (state.fancy) {
      this.fancyComponent = atom.deserializers.deserialize(state.fancy);
    }
    else {
      this.fancyComponent = new FancyComponent({ otherData: 'will be used instead' });
    }

    // More activation logic.
  },

  // As well as your component, your package has a serialize method
  // to save the current state.
  serialize () {
    return {
      fancy: this.fancyComponent.serialize(),
      view: this.someView.serialize()
    };
  }
};

您要序列化的所有对象都需要serialize()方法。 它必须返回一个“可序列化对象”和一个具有已注册反序列化器名称的deserializer序列化deserializer密钥。 根据Atom的说法, “它通常是类本身的名称” 。 除此之外,类还需要静态deserialize()方法。 此方法将对象从先前的状态转换为真正的对象。

为了使所有这些成为可能,您必须使用atom.deserializers.add()将类添加到反序列化系统中。

窗格和视图

窗格是Atom中的单个窗口。 它包含所有打开的选项卡,称为“项目”。 这些窗格存储在atom.workspace对象中。 使用atom.workspace.getActivePane()您需要当前的活动窗格。 窗格对象不包含任何DOM元素,但包含Atom内部组件的所有实例(例如TextEditorGutterContainerNotificationManager )。 了解这些窗格对于为程序包创建自定义视图至关重要。

您要添加的视图或任何其他自定义UI元素必须使用JavaScript创建。 Atom完全由Web Components构建,但是您不必这样做。 自定义模态的一个非常基本的示例如下。

// lib/custom-view-element.js
export default class YourPackageView {
  constructor (state) {
    this.data = state;
    this.element = document.createElement('div');
    this.message = document.createElement('span');
    this.textNode = document.createTextNode(this.data.content);

    this.element.classList.add('your-package');
    this.message.classList.add('your-package-message');

    this.message.appendChild(this.textNode);
    this.element.appendChild(this.message);
  }

  serialize () {
    return {
      data: this.data
    };
  }

  destroy () {
    this.element.remove();
  }

  getElement () {
    return this.element;
  }

  doSomethingWithData () {}
}
// lib/main.js
import YourPackageView from './custom-view-element';
import { CompositeDisposable } from 'atom';

const YourPackage = {
  subscriptions: null,
  packageView: null,
  modal: null,

  activate (state) {
    this.subscriptions = new CompositeDisposable();
    // We don't use the serialization system here because we assume
    // that our view won't work with any data.
    this.packageView = new YourPackageView(state.viewState);
    // Here we add the custom view to the modal panel of Atom.
    this.modal = atom.workspace.addModalPanel({
      item: this.packageView.getElement(),
      visible: false
    });

    this.subscriptions.add(
      atom.commands.add('atom-workspace', {
        'your-package:toggle-modal': this.toggleModal()
      })
    );
  },

  // We destroy both the custom view and Atom's modal.
  deactivate () {
    this.subscriptions.dispose();
    this.packageView.destroy();
    this.modal.destroy();
  },

  serialize () {
    return {
      viewState: this.packageView.serialize()
    };
  },

  toggleView () {
    if (this.modal.isVisible()) {
      this.modal.hide();
    }
    else {
      this.modal.show();
    }
  },

  doSomethingElseWithView () {}
};

export default YourPackage;

atom.workspace.addModalPanel()方法将模式元素添加到Atom的工作空间。 如果要将自定义视图添加到窗格(例如,用于设置页面),则需要做更多的工作。

使程序包可配置

包配置应在JSON Schema中进行描述。 要添加设置,您的包对象需要一个带有数据的config键。 或者,您可以将配置移至config-schema.json文件并import 。 这样可以使您的配置分离,并组织架构。

// lib/config-schema.json
{
  "activateHyperMode": {
    "description": "Turns the package into hyper mode.",
    "type": "boolean",
    "default": false
  },
  "setRange": {
    "type": "integer",
    "default": 42,
    "minium": 1,
    "maximum": 9000
  }
}
// lib/main.js
import packageConfig from './config-schema.json';

const YourPackage = {
  config: packageConfig,
  subscriptions: null,

  activate (state) {
    // ...
  }
};

这将在软件包的设置页面上自动创建配置。 可以在Atom API文档的配置页面上找到所有受支持类型的列表。 您的设置对象以及所有其他软件包配置都存储在atom.config对象中。

获取和设置

您可以使用get()set()方法获取并设置配置的任何键。 也可以获取Atom的常规设置或其他软件包的设置。 如果要与其他软件包交互,则需要提供和使用服务

atom.config.get('yourPackage'); // Returns the entire configuration object
atom.config.get('yourPackage.activateHyperMode'); // Returns false
atom.config.get('core.fileEncoding'); // Returns 'utf8'
atom.config.get('differentPackage');

atom.config.set('yourPackage.activateHyperMode', true);
atom.config.set('yourPackage.myNewValue', 'value');

倾听变化

要侦听更改,您可以观察配置的更改,或者让侦听器(称为onDidChange()侦听关键路径。 它们都返回一个Disposable ,您可以通过.dispose()取消订阅。

同样,将它们添加到CompositeDisposable的实例CompositeDisposable您可以一次处理多个事件:

this.subscriptions = new CompositeDisposable();

this.subscriptions.add(
  atom.config.observe('core.themes', value => {
    // do something
  })
);

this.subscriptions.add(
  atom.config.onDidChange('yourPackage', ({oldValue, newValue}) => {
    // do something
  })
);

或者,将它们单独处理:

const observeConfig = atom.config.observe('core.themes', doSomethingCool);
const onChangeConfig = atom.config.onDidChange('yourPackage', doSomethingOnChange);

// later in your code
observeConfig.dispose();
onChangeConfig.dispose();

使用菜单和按键图进行微调

菜单和键盘映射使软件包的功能在Atom环境中可供用户访问。 它们链接到界面的特定命令。 如果您的软件包可以切换,打开视图,执行一些自定义操作或执行其他任何操作,则用户应该可以使用它。

添加菜单

菜单定义可以作为JSON文件存储在menus/顶级目录中,也可以存储在package.jsonmenus键中。 以下示例将命令添加到“ Packages菜单栏和编辑器的上下文菜单。 在编辑器中右键单击时,将显示上下文菜单。

// menus/your-package.json
"menu": [
  {
    "label": "Packages",
    "submenu": [
      {
        "label": "Your Package",
        "submenu": [
          {
            "label": "Toggle",
            "command": "your-package:toggle"
          }, {
            "label": "Settings",
            "command": "your-package:show-settings"
          }
        ]
      }
    ]
  }
],
"context-menu": {
  "atom-text-editor": [
    {
      "label": "Toggle Your Package",
      "command": "your-package:toggle"
    }
  ]
}

按键图

使用键盘映射,您可以定义软件包命令的快捷方式。 它们绑定到特定范围,其中范围是CSS选择器,例如atom-text-editoratom-text-editor:not([mini])atom-workspace 。 当与选择器匹配的元素成为焦点,并且使用了击键模式时,将发出您的自定义操作。

// keymaps/your-package.json
{
  "atom-text-editor": {
    "alt-shift-e": "your-package:toggle",
    "cmd-ctrl-alt-shift-enter-backspace": "your-package:do-something-crazy"
  },
  "atom-text-editor[mini]": {
    "tab-escape": "your-package:transform"
  }
}

请记住,这些命令必须已在您的入口点中注册( atom.commands.add() )。

使用Chrome开发者工具进行调试

在Atom中进行调试与在网络上进行调试没有太大区别。 您可以在“ View > Developer > Toggle Developer Tools人员工具”下激活Chrome开发View > Developer > Toggle Developer Tools以查看引发的错误,代码日志或了解Atom的标记。

Atom编辑器中的Chrome开发者工具

茉莉花的单元测试

Atom使用Jasmine框架进行测试。 测试位于spec/顶级目录中,并且其中的文件必须-spec (例如fancy-component-spec.js )。 测试不是运行或发布程序包所必需的,但是它们是备份代码质量并确保添加新功能时不会损坏的一种好方法。

要运行测试,您可以使用window:run-package-specs命令或转到View > Developer > Run Package Specs

如果要在Travis CI上运行软件包规格,Atom博客上有简短的帖子介绍如何进行设置。

包装流程

那是很多投入。 Atom的实际流程或执行顺序大致如下(注意:测试不是程序包流程的一部分)。

  1. Atom启动并读取您的package.json
    • 菜单,键盘映射,样式表和所有其他配置都已应用
    • 如果定义了activationCommands ,则将运行它们
  2. 执行主入口点(即activate()
    • 您的打包魔术(例如,响应用户输入,创建视图,修改文件)开始生效
  3. 您停用软件包或关闭Atom
    • Atom序列化软件包状态

结论

希望我的文章能帮助您对Atom包开发有一个基本的了解。 仍然有更多的功能和许多主题,不幸的是,仅一篇文章无法涵盖这些主题。 查看《 Atom飞行手册》 ,看看还有什么可能。

您将开发什么软件包?

From: https://www.sitepoint.com/write-atom-packages-using-vanilla-javascript/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值