◆ イベントだと処理結果を子に渡す方法が setter やメソッド呼び出しになる
  ◆ 親アクセスの前後で処理が分かれる
  ◆ 親から情報もらってデータ設定するのはほぼ親からもらったデータに対して
  ◆ イベントで更新依頼を出して親が更新して その結果の新しい状態を再描画だけすれば困らない
  ◆ 突き詰めると状態から DOM を作れればいいのでコンポーネントじゃなくて良くなる
◆ イベントは bubbles が有効なら最上位コンポーネントやその外まで伝わる
  ◆ 関数渡しの方法で深いコンポーネントまで渡していくのは手間が多い
  ◆ 途中の使わないコンポーネントで保持するのもなんか違う気がする
◆ 関数を渡す方法だと好きなタイミングで呼び出して結果を取得できる
  ◆ lit-element だと楽に設定できるけどただの WebComponents だとそうでもない

lit-element を使っていて 子コンポーネントから親コンポーネントへアクセスするときにどうするか迷いました
用途はコンポーネント内での一部の処理を そのコンポーネントを使う親側で指定するときです

イベントを使う方法

イベントを使う方法では 子コンポーネントは this.dispatchEvent(event) でイベントを起こします
親側でそのイベントを listen しておき イベント時になにかの処理を行います
子の状態が変わるのなら 子に渡していたデータを親側で修正し 再度渡したり 子の update メソッドを呼び出したりなどです

関数渡しを使う方法

関数渡しを使う方法では 親コンポーネントが子コンポーネントに関数を渡します
渡す関数は基本は親コンポーネントのメソッドで 呼び出すことで親コンポーネント側で定義した処理を行えます

普通の WebComponents を使っていると

lit-element は使わず WebComponents を直接使っているときは わざわざメソッドを渡すのなんて面倒です
特に innerHTMLでまとめて ShadowDOM 内の HTML を設定した場合はセレクタでコンポーネントを取得して設定となるので手間も多くあえてやる気は起きません
DOM らしくイベントでやればいいじゃん と思って特にメソッド渡しなんてやろうと思ったこともないくらいです

lit-element の場合

lit-element を使うと lit-html 記法が使えます
そのおかげで innerHTML を直接設定するのとほぼ同じ記法で特定の Element に対してリスナ設定やプロパティ設定が簡単に行なえます
リスナつけるのもメソッドを渡しも 親コンポーネント側を作るコストはほぼ変わりません
となると どっちを使うか迷います

比較

関数渡しの場合は その場で関数を呼び出すだけなので 考え方はシンプルです
返り値として値も受け取れます
それに対し イベントの場合は 一方的に親に通知して終わりの場合は良いのですが 結果をもらって続きから処理をする場合には少し面倒です
一旦処理が切れるので await なしのころの非同期処理的な感じがあります

しかし 作ってると返り値が欲しい場合というのは値を設定するためであって その設定先は親からもらったデータです
子コンポーネントで状態を更新しようとするから親から値をもらって続きの処理が必要になって不便になります
データが親からもらうものならその更新処理もすべて親でやって 子コンポーネントは今の状態に応じて表示するだけにすれば親にアクセスした返り値を使ってなにかの処理を行うなんてほぼ無いと思います

ただその方針を極めていくと DOM は状態を表示するだけで更新などデータ処理はルートコンポーネントの外側の全く別のところでするということになります
elm などでの作り方がそうなのですが そうなるとコンポーネントと言わなくなると思うんですよね
内部でデータを操作して完結するのではなく親からもらったデータから DOM を作るだけなので ただの関数でよくなります
WebComponents の機能は CSS のスコープ分けくらいでしかなくていいとも言えます

イベントの場合は bubbles を有効にするとその Element だけでなく window まで親 Element に伝わっていくので親コンポーネントに対してだけではなく親の親やルートにまで伝えたいものがある場合はこっちを使うほうが楽かもしれません
関数渡しだと 深い位置にあると関数を子コンポーネントに渡し続けるのでコード量が増えます
明示的に渡してプロパティに持っているのでわかりやすいと言えばわかりやすいのかもしれませんが 実際に使うのは最下部のコンポーネントなのに途中のものでも関数を保持しておかないといけないのはなんか抵抗あります

結局どっちつかずで 1 つのコンポーネントで混ざってたりもするのですが 関数渡しは lit-element ならでは感が強いので 他でも使うイベントのほうがいいかな と少しイベントよりです
でも 親の関数呼び出しで結果を取得できて 子コンポーネントで処理するほうがコンポーネントらしい気もします

子コンポーネントのボタンを押すと親コンポーネントのプロパティが更新されて画面の文字も変わるシンプルなものです
子コンポーネントが親からもらったデータを表示とかは入ってません

イベントの場合

import { LitElement, html } from "https://unpkg.com/lit-element?module"

class MainComponent extends LitElement {
static get properties() {
return { name: { type: String } }
}

render() {
return html`
<h1>${this.name}</h1>
<sub-component @name-changed=${this.onNameChanged}></sub-component>
`
}

onNameChanged(eve) {
this.name = eve.detail.name
}
}

class SubComponent extends LitElement {
render() {
return html`<button @click=${this.onClick}>ChangeName</button>`
}

onClick() {
const newname = Math.random()
.toString(36)
.substr(2, 8)
this.dispatchEvent(new CustomEvent("name-changed", { detail: { name: newname } }))
}
}

customElements.define("main-component", MainComponent)
customElements.define("sub-component", SubComponent)
document.body.append(document.createElement("main-component"))

関数渡しの場合

import { LitElement, html } from "https://unpkg.com/lit-element?module"

class MainComponent extends LitElement {
static get properties() {
return { name: { type: String } }
}

render() {
return html`
<h1>${this.name}</h1>
<sub-component .changeName=${this.changeName}></sub-component>
`
}

changeName = (name) => {
this.name = name
}
}

class SubComponent extends LitElement {
static get properties() {
return { changeName: { type: Function } }
}

render() {
return html`<button @click=${this.onClick}>ChangeName</button>`
}

onClick() {
const newname = Math.random()
.toString(36)
.substr(2, 8)
this.changeName(newname)
}
}

customElements.define("main-component", MainComponent)
customElements.define("sub-component", SubComponent)
document.body.append(document.createElement("main-component"))