【WebComponents】CustomElements(v1)とbabelでトランスパイルしたコードを組み合わせる

CustomElements(v1)では下のコードのようにES6Classを使ってCustomElementsを作れるようになった。

class CustomElement extends HTMLElement {
	constructor() {
		super();
	}
}

しかし、ES5ClassでCustomElements(v1)は作れない。そのため、babelでES6ClassをES5Classに変換してしまうと、エラーが発生する。これは、native-shimというpolyfillを読み込ませることによって解決される。

(逆に、CustomElements(v1)に対応していないブラウザはCustomElementsのpolyfillを読み込ませればエラーは発生しない。)

しかし、このnative-shimはES6で書かれているため、IE11などの古いブラウザでは読み込み時にSyntaxErrorが発生してしまう。なんてこったい。babelを使う時というのは、新しいESの構文を使って書いたコードをIE11などの古いブラウザにも対応させる状況だというのに・・・

ということで、このnative-shimを読み込めるブラウザのみ読み込むという方法を取る。具体的にどうやるのかというと、native-shimのコードを全て文字列にして、try~catche句の中でeval()を使って実行させる。SyntaxErrorをcatchするパターンである。

native-shimを文字列にするにはminifyにかけて1行にしてやって、クォートで囲むのがいいだろう。(テンプレートストリングを使いたくなるが、古いブラウザはテンプレートストリングには対応していない。)

try {
	eval("(()=>{'use strict';if(!window.customElements)return;const NativeHTMLElement=window.HTMLElement;const nativeDefine=window.customElements.define;const nativeGet=window.customElements.get;const tagnameByConstructor=new Map();const constructorByTagname=new Map();let browserConstruction=!1;let userConstruction=!1;window.HTMLElement=function(){if(!browserConstruction){const tagname=tagnameByConstructor.get(this.constructor);const fakeClass=nativeGet.call(window.customElements,tagname);userConstruction=!0;const instance=new(fakeClass)();return instance}browserConstruction=!1};window.HTMLElement.prototype=NativeHTMLElement.prototype;window.customElements.define=(tagname,elementClass)=>{const elementProto=elementClass.prototype;const StandInElement=class extends NativeHTMLElement{constructor(){super();Object.setPrototypeOf(this,elementProto);if(!userConstruction){browserConstruction=!0;elementClass.call(this)}userConstruction=!1}};const standInProto=StandInElement.prototype;StandInElement.observedAttributes=elementClass.observedAttributes;standInProto.connectedCallback=elementProto.connectedCallback;standInProto.disconnectedCallback=elementProto.disconnectedCallback;standInProto.attributeChangedCallback=elementProto.attributeChangedCallback;standInProto.adoptedCallback=elementProto.adoptedCallback;tagnameByConstructor.set(elementClass,tagname);constructorByTagname.set(tagname,elementClass);nativeDefine.call(window.customElements,tagname,StandInElement)};window.customElements.get=(tagname)=>constructorByTagname.get(tagname)})()");
}catch(e) {}

これで、CustomElements(v1)に対応しているブラウザはnative-shimを読み込むので、ES5Classにトランスパイルされたコードが動くようになり、かつnative-shimを読み込めないIE11のようなブラウザも通常のCustomElementsのpolyfillを読み込ませることによってCustomElementsが使えるようになる。

なぜ、CustomElements(v1)がES5Classに対応していないかというのはたぶんCustomElements(v1)を実装しているブラウザはすでにES6Classも実装しているからだろう。

補足: こういうbabelのプラグインもあるらしい。試してないので何とも言えないが。 babel-plugin-transform-custom-element-classes

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です