ファイル名の意図
- TodoApp: Exampleファイル。
- TodoApp.vue: 通常のvueファイル
- TodoApp.vue.esm.js: vueファイルをjsに変換したもの。esmodule形式。vueコンポーネントがexportされている。
- TodoApp.vue.shadowmode.esm.js: vueファイルをjsに変換したもの。esmodule形式。vueコンポーネントがexportされている。styleがheadではなくtemplateにinjectされる番(shadowdomのため)。
- TodoApp.esm.js: vueコンポーネントをwebcomponentに変換したもの。esmodule形式。
- todo-app.js: webcomponentをcustomeElement.defineした、そのままタグとして利用できるもの。
TodoApp.vue -> TodoApp.vue.esm.js
rollup + rollup-plugin-vue
rollup.config.js
import VuePlugin from 'rollup-plugin-vue'
export default {
input: 'TodoApp.vue'
output: {
file: 'TodoApp.vue.esm.js',
format: 'esm'
},
plugins: [
VuePlugin()
]
}
TodoApp.vue -> TodoApp.vue.shadowmode.esm.js
webpackでvue-loaderを使えばshadowmode: trueというoptionを使うだけで良いのだが、webpackではesmは作れない。
rollupで頑張る。
今回の記事のキモ。
via: https://github.com/vuejs/rollup-plugin-vue/issues/284#issuecomment-499009722
rollup.config.js
import VuePlugin from 'rollup-plugin-vue'
export default {
input: 'TodoApp.vue'
output: {
file: 'TodoApp.vue.shadowmode.esm.js',
format: 'esm'
},
plugins: [
VuePlugin({
normalizer: '~./normalize-component.js'
})
]
}
normalize-component.js
'use strict';
function createInjectorShadow(shadow) {
return (inject, { source, map, media }) => {
const style = document.createElement('style');
style.appendChild(document.createTextNode(source));
shadow.appendChild(style);
};
}
function normalizeComponent(
template,
style,
script,
scopeId,
isFunctionalTemplate,
) {
var options = typeof script === 'function' ? script.options : script; // render functions
if (template && template.render) {
options.render = template.render;
options.staticRenderFns = template.staticRenderFns;
options._compiled = true; // functional template
if (isFunctionalTemplate) {
options.functional = true;
}
} // scopedId
if (scopeId) {
options._scopeId = scopeId;
}
var hook;
if (style) {
hook = function() {
style.call(
this,
createInjectorShadow(this.$root.$options.shadowRoot),
);
};
}
if (hook) {
if (options.functional) {
// register for functional component in vue file
var originalRender = options.render;
options.render = function renderWithStyleInjection(h, context) {
hook.call(context);
return originalRender(h, context);
};
} else {
// inject component registration as beforeCreate hook
var existing = options.beforeCreate;
options.beforeCreate = existing
? [].concat(existing, hook)
: [hook];
}
}
return script;
}
export default normalizeComponent;
TodoApp.vue.shadowmode.esm.js -> TodoApp.esm.js
これは単にファイルを作るだけで、rollupは使わない。
TodoApp.esm.js
import Vue from 'vue/dist/vue.esm.js'
import wrap from '@vue/web-component-wrapper'
import TodoAppVueComponent from './TodoApp.vue.js'
const TodoApp = wrap(Vue, TodoAppVueComponent)
export default TodoApp
TodoApp.esm.js -> todo-app.js
これは単にファイルを作るだけで、rollupは使わない。
todo-app.js
import TodoApp from './TodoApp.js'
window.customElements.define('todo-app', TodoApp);
使用方法
ここで作ったすべてのファイルは、通常のフロントエンドnodeパッケージと同様に、そのままwebpackなどに読み込ませて使えるはず。