◆ render 後に childNodes を移動すると 移動先が更新される
  ◆ 同じ要素とテンプレートの必要あり
  ◆ 移動後の render で指定した要素の childNodes は空のまま

やりたいこと

lit-html で hyperHTML の wire のようにサブツリーの render 処理を分けたいです

前に書いたように hyperHTML の wire は返り値を捨てても実行のタイミングで更新されているので wire 部分だけの更新を行えます
最初に作った返り値をどこかに append したらその後は 「wire()`html`」 のテンプレートリテラルを実行するだけで差分が更新されます
これのおかげで サブツリーだけを更新したり サブツリー内は更新しなかったり コンポーネントのようにツリーの一部を分けて扱えます

lit-html でやろうとした場合 「html``」 ではテンプレートを返すだけでまだ実行はされず更新されていません
hyperHTML の bind と wire の違いはなく html タグ関数はテンプレートを返すだけ 更新するなら render 関数を使う必要があります
render 関数ではどこに render するかとしてホストとなる DOM 要素を指定する必要があります
そうなるとサブツリーとして扱いたい場合は 何らかの DOM 要素を作ってそれの上下で分けることになります
上側では作った要素をツリーの末端として設置し 下側では作った要素をホストとして render を行います
これだと 埋め込む部分は作った 1 つの要素の Node だけになります
せっかく lit-html が複数の最上位ノードに対応してるので複数にしたいですし 手動で作る要素なしで 完全に lit-html に任せたいです

render 後に子要素を変更すると

良い方法がないかいろいろ試していると こういうことができました

const tpl = x => html`<div>(${x})</div>`

const fragment = document.createDocumentFragment()
render(tpl(100), fragment)

document.body.append(new Text("A"), fragment, new Text("B"))

render(tpl(200), fragment)

console.log(fragment.childNodes)
// NodeList []
console.log(document.body.innerHTML)
// A<!----><div>(200)</div><!---->B

render したあとに そこで作った子要素を他の場所に移動します
その後再度 render すると移動先の要素が更新されます
render の場合は指定したホストの要素の中がテンプレートから作った要素だけになることを保証してるのかと思ったのですが そういう確認はしないようです
一度 render 済みで同じテンプレートとホストのセットが来たら単純に埋め込み部分の値更新だけしてくれるようです

分けてみる

これのおかげで 好きな場所へ html タグ関数で作ったルートノードを持ってこれます
またツリーの一部だけを更新することもできます

<!doctype html>
<script type="module">
import { html, render } from "https://unpkg.com/lit-html"

let value1 = "A"
let value2 = "B"

let fragment = null
let nodes = null
const renderSub = () => {
if(!fragment) {
fragment = document.createDocumentFragment()
render(tpl2(), fragment)
nodes = [...fragment.childNodes]
}else{
render(tpl2(), fragment)
}
return nodes
}

const tpl1 = () => html`
<div>${value1}</div>
<div>${renderSub()}</div>
<button @click=${update1}>update1</button>
<button @click=${update2}>update2</button>
`
const tpl2 = () => html`<span style="color:red">${value2}</span>`

const renderBody = () => render(tpl1(), document.body)

const update1 = () => {
value1 += "1"
value2 += "1"
renderBody()
}
const update2 = () => {
value1 += "2"
value2 += "2"
renderSub()
}

renderBody()
</script>

黒文字の A は tpl1 で
赤文字の B は tpl2 でレンダリングされます

update1 ボタンでは tpl1 を更新します
renderSub を常に呼び出してるので tpl2 も常に更新されます

update2 ボタンでは tpl2 だけ更新します
value1 と value2 には同じ文字を追加しますが画面上は赤文字の方だけが追加されます

renderSub では毎回 render を実行して tpl2 で更新しますが 初回の DOM 構築後にトップノードの一覧を nodes に保持して それ以降はその nodes を返します
ここで普通にテンプレートを返すと親ツリー (tpl1) の render で一緒に render される普通のツリーの一部になって レンダー処理を分ける部分ツリーにできません
親ツリー的には tpl2 が作るルートノードを設定するだけで終わらせて tpl2 の中は tpl2 の render で処理します