jsの読み込み方と、定義が見えるかどうか。変数のスコープ
MACのChrome バージョン: 76.0.3809.132
head内 | body直下 | body内の子要素 | template内 | templateからコピーされたbody直下要素 | shadow-dom内 | |
---|---|---|---|---|---|---|
class定義 | 読めた | 読めた | 読めた | 読めなかった | 読めた | |
global変数定義 | 読めた | 読めた | 読めた | 読めなかった | 読めた | 読めた |
global変数定義(esm) | 読めなかった | 読めなかった | 読めなかった | |||
window変数定義(esm) | 読めた | 読めなかった | 読めた | |||
export(esm) | 読めなかった | 読めなかった | 読めなかった |
このことから
- shadow-domは特にjsの実行環境を隔離したりはしない
- esmを使ってもesmの中でwindow変数を弄られたら無理
- template(DocumentFragment)は、実際にFragmentじゃないDomに追加するまで、読めないしそもそも定義が走ってないっぽい
という事がわかる。
つまり、webcomponentを作るときに、副作用なく&自己管理なものを作るとなると、
- html/cssについては、shadow-domを使えば思考停止でOK
- jsは、exportが定義されている行儀のよいesmのみを利用する
- jsは、windowに書き込まない行儀のよいもののみ利用する
- jsは、idはclassを扱わず、直接elementを扱う行儀のよいもののみ利用する
を守る必要がある。現実にそんなお行儀の良いjsなわけはないので、現実的ではない。
「副作用なく&自己管理」を諦めて、
- 「webcomponentの依存ライブラリを、予めmainHTML側の責任で読み込む」という使い方を強いる
- jsは、idはclassを扱わず、直接elementを扱う行儀のよいもののみ利用する
- 「webcomponent側で、引数にshadowRootを取りjs関係をactivateする関数を提供し、mainHTML側の責任でそれをcallさせる」という使い方を強いる
が限界か。
定義箇所詳細
head内
head内
<head>
<script></script><!-- ← ここで定義 -->
</head>
<body>
<button onclick="console.dir(xxx)">check</button> <!-- ← ここでチェック -->
</body>
body直下
body直下
<body>
<script></script> <!-- ← ここで定義 -->
<button onclick="console.dir(xxx)">check</button> <!-- ← ここでチェック -->
</body>
body内の子要素
body内の子要素
<body>
<main>
<script></script> <!-- ← ここで定義 -->
</main>
<button onclick="console.dir(xxx)">check</button> <!-- ← ここでチェック -->
</body>
template内
template内
<head>
<template>
<script></script> <!-- ← ここで定義 -->
</template>
</head>
<body>
<button onclick="console.dir(xxx)">check</button> <!-- ← ここでチェック -->
</body>
templateからコピーされたbody直下要素
templateからコピーされたbody直下要素
<head>
<template>
<script></script> <!-- ← ここで定義 -->
</template>
</head>
<body>
<script>
const body = document.querySelector('body');
const template = document.querySelector('template');
body.appendChild(
document.importNode(template.content, true)
);
</script>
<button onclick="console.dir(xxx)">check</button> <!-- ← ここでチェック -->
</body>
shadow-dom内
<head>
<template>
<script></script> <!-- ← ここで定義 -->
</template>
</head>
<body>
<main></main>
<script>
const main = document.querySelector('main');
const template = document.querySelector('template');
main.attachShadow({mode: 'open'}).appendChild(document.importNode(template.content, true));
</script>
<button onclick="console.dir(xxx)">check</button> <!-- ← ここでチェック -->
</body>
定義方法詳細
class定義したinlinejs
<script src="./XxxClass.js"></script>
./XxxClass.js
class XxxClass {
}
global変数定義
<script src="./XxxGlobalVar.js"></script>
./XxxGlobalVar.js
xxx = "{{{xxx}}}";
global変数定義(esm)
<script type="module" src="./XxxGlobalVarEsm.js"></script>
./XxxGlobalVarEsm.js
xxx = "{{{xxx}}}";
window変数定義(esm)
<script type="module" src="./XxxWindowVarEsm.js"></script>
./XxxGlobalVarEsm.js
window.xxx = "{{{xxx}}}";
export(esm)
<script type="module">
import { xxx } from "./XxxExport.js";
</script>
XxxExport.js
export const xxx = '{{{xxx}}}';