- Vue.js
Vue $attrsとuseAttrsの使い方|inheritAttrs早見表
$attrs とは、親がコンポーネントに渡した属性やイベントリスナーのうち、props にも emits にも宣言されていないもの(フォールスルー属性)をまとめたオブジェクトです。 template では $attrs、script setup では useAttrs() で取り出せます。何もしなければルート要素に自動で付き、inheritAttrs: false を指定すると自動では付かなくなるので、v-bind="$attrs" で付けたい要素を自分で選べます。
この記事では、inheritAttrs の有無、ルート要素が1つか複数か、class・style・イベントリスナーのそれぞれについて「どの要素に付くか」を、実際に出力した HTML つきの一覧表にまとめました。複数ルートのときに出る警告文の再現、defineOptions と v-bind で作る入力ラッパーの最小コード、useAttrs の値が更新に追従する書き方と追従しない書き方、TypeScript での型付けまで扱います。
確認環境:Ubuntu 24.04 / Node.js 22.22.2 / Vue 3.5.43 / Vitest 5.0.3 / @vue/test-utils 2.5.1 / jsdom 30.1.1 / vue-tsc 3.3.12(TypeScript 6.0.3)
inheritAttrsと$attrsの早見表
親から id="x1" class="big" style="margin: 4px" data-id="7" @click="…" を渡したときに、それぞれがどこに付いたかの一覧です(出力 HTML は次の章に載せています)。
| 子コンポーネントの形 | class | style | id・data 属性 | click リスナー | 警告 |
|---|---|---|---|---|---|
| 単一ルート(指定なし) | ルートに追加(結合) | ルートに追加(結合) | ルート | ルート | なし |
単一ルート + inheritAttrs: false |
付かない | 付かない | 付かない | 付かない | なし |
inheritAttrs: false + 内側の要素に v-bind="$attrs" |
内側の要素 | 内側の要素 | 内側の要素 | 内側の要素 | なし |
| 複数ルート(指定なし) | 付かない | 付かない | 付かない | 付かない | 2件出る |
複数ルート + 1つに v-bind="$attrs" |
指定した要素 | 指定した要素 | 指定した要素 | 指定した要素 | なし |
| ルートが別のコンポーネント | 孫のルートまで届く | 孫のルートまで届く | 孫のルート | 孫のルート | なし |
| ルートが v-if / v-else | 表示中の要素 | 表示中の要素 | 表示中の要素 | 表示中の要素 | なし |
| ルートがテキストだけ | 付かない | 付かない | 付かない | 付かない | 2件出る |
取り出し方の対応は次のとおりです。
| 場所 | 書き方 | 用途 |
|---|---|---|
| template | $attrs |
v-bind="$attrs" で要素にまとめて付ける |
| script setup | const attrs = useAttrs() |
一部だけ取り出す、加工する |
| script setup での設定 | defineOptions({ inheritAttrs: false }) |
自動の継承を止める |
| setup 関数 | setup(props, { attrs }) |
第2引数から受け取る |
| Options API | this.$attrs / inheritAttrs: false |
コンポーネントのオプションに書く |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Vue $attrsとuseAttrsの使い方|inheritAttrs早見表」
どの要素に付くかを出力HTMLで確かめる
ここでは、子コンポーネントを Vue Test Utils でマウントし、次の属性を渡して出力 HTML を確認しました。
mount(Component, {
attrs: {
id: 'x1',
class: 'big',
style: 'margin: 4px',
'data-id': '7',
onClick: () => clicks++,
},
})
click リスナーについては、どの要素に登録されたかを見分けるため、各要素に「バブリングしない click イベント」を直接送り、ハンドラーが呼ばれた回数を数えています。
単一ルート:ルート要素に自動で付く
<!-- src/SingleRoot.vue -->
<template>
<div class="box" style="color: red">
<span>中身</span>
</div>
</template>
<div class="box big" style="color: red; margin: 4px;" id="x1" data-id="7"><span>中身</span></div>
div を直接クリック → ハンドラー 1 回
span を直接クリック → ハンドラー 0 回
class は「box big」、style は「color: red; margin: 4px;」と、子がもともと持っていた値に親の値が結合されます。 id や data 属性、click リスナーもルートの div に付きました。
inheritAttrs: false:どこにも付かなくなる
<!-- src/NoInherit.vue -->
<script setup lang="ts">
defineOptions({ inheritAttrs: false })
</script>
<template>
<div class="box" style="color: red">
<span>中身</span>
</div>
</template>
<div class="box" style="color: red;"><span>中身</span></div>
div を直接クリック → ハンドラー 0 回
span を直接クリック → ハンドラー 0 回
class と style も含めて、自動では付かなくなりました。 属性は消えたわけではなく、$attrs の中に入ったままです。
inheritAttrs: false + v-bind="$attrs":付ける要素を選ぶ
<!-- src/NoInheritBind.vue -->
<script setup lang="ts">
defineOptions({ inheritAttrs: false })
</script>
<template>
<div class="box" style="color: red">
<span v-bind="$attrs">中身</span>
</div>
</template>
<div class="box" style="color: red;"><span id="x1" class="big" style="margin: 4px;" data-id="7">中身</span></div>
div を直接クリック → ハンドラー 0 回
span を直接クリック → ハンドラー 1 回
属性もリスナーも、v-bind を書いた span に移りました。引数なしの v-bind="オブジェクト" は、オブジェクトの全プロパティを属性として展開する書き方で、onClick のようなリスナーもまとめて登録されます。
複数ルート:警告が出て、どこにも付かない
<!-- src/MultiRoot.vue -->
<template>
<header>見出し</header>
<main>本文</main>
</template>
[Vue warn]: Extraneous non-props attributes (id, class, style, data-id) were passed to component but could not be automatically inherited because component renders fragment or text or teleport root nodes.
at <MultiRoot id="x1" class="big" style="margin: 4px" ... >
at <VTUROOT>
[Vue warn]: Extraneous non-emits event listeners (click) were passed to component but could not be automatically inherited because component renders fragment or text root nodes. If the listener is intended to be a component custom event listener only, declare it using the "emits" option.
at <MultiRoot id="x1" class="big" style="margin: 4px" ... >
at <VTUROOT>
<header>見出し</header><main>本文</main>
header を直接クリック → ハンドラー 0 回
main を直接クリック → ハンドラー 0 回
警告は属性用(Extraneous non-props attributes)とリスナー用(Extraneous non-emits event listeners)の2件に分かれて出ます。かっこの中に、行き先が決まらなかった属性名・イベント名が並びます。Vue はどのルートに付けるべきかを決められないため、どこにも付けずに知らせてくれます。
複数ルート + v-bind="$attrs":指定した要素に付く
<!-- src/MultiRootBind.vue -->
<template>
<header>見出し</header>
<main v-bind="$attrs">本文</main>
</template>
<header>見出し</header><main id="x1" class="big" style="margin: 4px;" data-id="7">本文</main>
header を直接クリック → ハンドラー 0 回
main を直接クリック → ハンドラー 1 回
どれか1つに v-bind="$attrs" を書けば、警告は出なくなり、その要素に付きます。 複数ルートの場合は inheritAttrs の指定がなくても、この1行で解決しました。
ルートが別のコンポーネント:孫まで届く
<!-- src/Nested.vue -->
<script setup lang="ts">
import SingleRoot from './SingleRoot.vue'
</script>
<template>
<SingleRoot class="mid" />
</template>
<div class="box mid big" style="color: red; margin: 4px;" id="x1" data-id="7"><span>中身</span></div>
div を直接クリック → ハンドラー 1 回
class は「box(孫自身)+ mid(子が渡した分)+ big(親が渡した分)」の順に結合されました。ルートがコンポーネントの場合、フォールスルー属性はそのコンポーネントへそのまま引き継がれます。
ルートがv-if / v-else:表示中の要素に付く
<!-- src/IfRoot.vue -->
<script setup lang="ts">
defineProps<{ loading: boolean }>()
</script>
<template>
<p v-if="loading">読み込み中</p>
<ul v-else><li>完了</li></ul>
</template>
class="big" id="x1" を渡し、loading を true から false に切り替えた前後の HTML です。
<p class="big" id="x1">読み込み中</p>
<ul class="big" id="x1"><li>完了</li></ul>
template に要素が2つ書かれていても、同時に描画されるルートが1つなら単一ルートとして扱われ、警告は出ませんでした。
ルートがテキストだけ:複数ルートと同じ警告
template の中身が「テキストだけ」という文字列1つだけのコンポーネントでも、複数ルートのときと同じ2件の警告が出ました。警告文の「fragment or text or teleport root nodes」のうち、text にあたるケースです。
propsとemitsに宣言したものは$attrsに入らない
attrsのキーを出力して確かめる
<!-- src/KeysView.vue -->
<script setup lang="ts">
import { useAttrs } from 'vue'
defineOptions({ inheritAttrs: false })
defineProps<{ title: string }>()
defineEmits<{ close: [] }>()
const attrs = useAttrs()
</script>
<template>
<pre>{{ Object.keys(attrs).join(', ') }}</pre>
</template>
親から title、id、class、style、data-id、@click、@close と、v-model に相当する modelValue・onUpdate:modelValue を渡した結果です。
attrs のキー: id, class, style, data-id, onClick, onUpdate:modelValue, modelValue
| 渡したもの | attrs に入ったか | 理由 |
|---|---|---|
| title | 入らない | defineProps で宣言済み |
| @close | 入らない | defineEmits で宣言済み |
| id / class / style / data-id | 入る | 宣言していない属性 |
| @click | onClick として入る | 宣言していないリスナー |
| v-model | modelValue と onUpdate:modelValue として入る | defineModel などで宣言していない |
イベントリスナーは「on + イベント名」のキー(onClick など)で入ります。 そのため v-bind="$attrs" を書くだけで、リスナーも一緒に登録されます。
宣言したpropsがある場合の出力
title を props に、close を emits に宣言した単一ルートのコンポーネントに、title="注文一覧" id="x1" @close @click を渡した結果です。
<div class="box" id="x1">注文一覧</div>
title は属性として出力されず、props として使われています。id だけがルートに付きました。
入力ラッパーを作る最小コード
defineOptionsとv-bind="$attrs"で作るBaseInput
ラベル付きの入力欄のように、ルートは外枠の label、属性を付けたいのは内側の input という部品が、$attrs のいちばんよくある使いどころです。
<!-- src/BaseInput.vue -->
<script setup lang="ts">
defineOptions({ inheritAttrs: false })
defineProps<{ label: string }>()
const model = defineModel<string>({ required: true })
</script>
<template>
<label class="field">
<span class="field-label">{{ label }}</span>
<input v-model="model" v-bind="$attrs" />
</label>
</template>
親からは、普通の input と同じ感覚で属性を書けます。
<BaseInput
v-model="name"
label="氏名"
id="name"
class="wide"
placeholder="山田 太郎"
maxlength="20"
required
@focus="onFocus"
/>
出力された HTML:
<label class="field"><span class="field-label">氏名</span><input id="name" class="wide" placeholder="山田 太郎" maxlength="20" required=""></label>
id・class・placeholder・maxlength・required がすべて input に付きました。値を入力すると親の name が更新され、input に focus イベントを送ると親の onFocus も呼ばれています(name = 佐藤 / logs = ["focus","focus"]。2件目の focus は次の BaseInputSplit の分です)。
classとstyleだけ外枠に付ける:useAttrsで振り分ける
「幅や余白の class は外枠に、それ以外は input に」と振り分けたいときは、useAttrs で取り出して分けます。
<!-- src/BaseInputSplit.vue -->
<script setup lang="ts">
import { computed, useAttrs } from 'vue'
defineOptions({ inheritAttrs: false })
defineProps<{ label: string }>()
const model = defineModel<string>({ required: true })
const attrs = useAttrs()
// class と style は外枠へ、それ以外は input へ
const rest = computed(() => {
const { class: _class, style: _style, ...others } = attrs
return others
})
</script>
<template>
<label class="field" :class="attrs.class" :style="attrs.style">
<span class="field-label">{{ label }}</span>
<input v-model="model" v-bind="rest" />
</label>
</template>
id="name2" class="wide" style="margin: 8px" placeholder="山田 太郎" を渡したときの出力:
<label class="field wide" style="margin: 8px;"><span class="field-label">氏名</span><input id="name2" placeholder="山田 太郎"></label>
class と style は label に、id と placeholder は input に分かれました。
付け方の判断表
| やりたいこと | 書き方 |
|---|---|
| ルートにそのまま付けたい | 何も書かない(既定の動き) |
| 内側の1要素に全部付けたい | inheritAttrs: false + その要素に v-bind="$attrs" |
| class・style と、それ以外を別の要素に付けたい | inheritAttrs: false + useAttrs で振り分け |
| 複数ルートで、どれか1つに付けたい | その要素に v-bind="$attrs" |
| 特定の属性だけ使いたい | $attrs.id や attrs.id で個別に参照 |
| 属性ではなく値として受け取りたい | defineProps に宣言する($attrs から外れる) |
useAttrsの値は更新に追従するか
公式の説明と、試した結果
公式ドキュメントには、attrs オブジェクトは常に最新のフォールスルー属性を反映しているが、パフォーマンス上の理由でリアクティブではない、という注意書きがあります。Vue 3.5.43 で、書き方ごとに表示が更新されるかを試しました。
<!-- src/AttrsStale.vue -->
<script setup lang="ts">
import { computed, useAttrs, watchEffect, ref } from 'vue'
defineOptions({ inheritAttrs: false })
const attrs = useAttrs()
// (1) setup で一度だけ取り出した値
const { status } = attrs
const label = `状態:${attrs.status}`
// (2) 読むたびに attrs を参照する
const live = computed(() => `状態:${attrs.status}`)
const runs = ref(0)
watchEffect(() => {
void attrs.status
runs.value++
})
</script>
<template>
<p class="a">分割代入: {{ status }}</p>
<p class="b">setupで作った文字列: {{ label }}</p>
<p class="c">computed: {{ live }}</p>
<p class="d">$attrs: {{ $attrs.status }}</p>
<p class="e">watchEffect の実行回数: {{ runs }}</p>
</template>
親から status と data-n の2つを渡し、まず status を「下書き」から「公開」に、次に data-n を 1 から 2 に変えたときの表示です。
初期 : 分割代入: 下書き / setupで作った文字列: 状態:下書き / computed: 状態:下書き / $attrs: 下書き / watchEffect の実行回数: 1
status変更: 分割代入: 下書き / setupで作った文字列: 状態:下書き / computed: 状態:公開 / $attrs: 公開 / watchEffect の実行回数: 2
data-n変更: 分割代入: 下書き / setupで作った文字列: 状態:下書き / computed: 状態:公開 / $attrs: 公開 / watchEffect の実行回数: 3
| 書き方 | status 変更後の表示 | 追従したか |
|---|---|---|
const { status } = attrs(setup で分割代入) |
下書き | 追従しない |
| setup の中で作った文字列 | 状態:下書き | 追従しない |
computed(() => attrs.status) |
状態:公開 | 追従した |
template の $attrs.status |
公開 | 追従した |
watchEffect(() => attrs.status) |
再実行された | 追従した(下の補足あり) |
この結果から言えることは2つです。
- setup の中で一度だけ取り出した値は、そのまま変わりません。 分割代入や、setup 直下で組み立てた文字列がこれにあたります。
- computed・template・watchEffect のように読むたびに attrs を参照する書き方は、今回の版では更新に追従しました。 ただし watchEffect は、読んでいない data-n だけを変えたときにも再実行されています(実行回数が 2 から 3 に)。props のようにキー単位で細かく追跡されるわけではない、という点が公式の注意書きと合っています。
watchとonUpdatedで受け取る例
watch にゲッター関数で渡す書き方と、onUpdated フックの中で読む書き方も試しました。
<!-- src/AttrsReactive.vue -->
<script setup lang="ts">
import { computed, useAttrs, watch, watchEffect, onUpdated, ref } from 'vue'
defineOptions({ inheritAttrs: false })
const attrs = useAttrs()
const log = ref<string[]>([])
const viaComputed = computed(() => `computed:${attrs.status}`)
watch(() => attrs.status, (v) => log.value.push(`watch:${v}`))
watchEffect(() => log.value.push(`watchEffect:${attrs.status}`))
onUpdated(() => console.log('onUpdated: attrs.status =', attrs.status))
defineExpose({ log, viaComputed })
</script>
<template>
<p>template:{{ $attrs.status }}</p>
</template>
親が渡す status を「下書き」から「公開」に変えた前後で、template の表示、computed の値、log の中身を出力した結果です。
初期: template:下書き | computed:下書き | ["watchEffect:下書き"]
onUpdated: attrs.status = 公開
変更後: template:公開 | computed:公開 | ["watchEffect:下書き","watch:公開","watchEffect:公開"]
watch(() => attrs.status, …) は、status を変えたときにコールバックが呼ばれました(log の watch:公開)。onUpdated フックの中で attrs.status を読むと、更新後の値(公開)が取れています。
使い分けの目安
| 目的 | 向いている書き方 |
|---|---|
| 属性を要素に付けるだけ | template の v-bind="$attrs" |
| 振り分けや加工をしたい | computed(() => …attrs…)(BaseInputSplit の rest) |
| 値の変化に応じて処理を動かしたい | その値を defineProps に宣言して watch する |
| 更新後の属性を確認して何かしたい | onUpdated の中で attrs を読む |
値の変化をきっかけに処理を動かしたいものは、props として宣言するのが確実です。 props ならキー単位で追跡され、型も付けられます。
TypeScriptでの型付け
useAttrsの戻り値の型
vue-tsc に型を表示させた結果です。
| 式 | 型 |
|---|---|
useAttrs() |
Attrs |
attrs.class |
ClassValue | undefined |
attrs.style |
StyleValue | undefined |
attrs.id など、それ以外のキー |
unknown |
class と style はそのまま :class と :style に渡せます(BaseInputSplit はキャストなしで vue-tsc が通りました)。それ以外は unknown なので、script の中で使うときは typeof attrs.id === 'string' のように絞り込んでから使います。
親が渡す属性を型チェックする
BaseInput のように v-bind="$attrs" で流すだけの部品は、親が渡す属性の型まではチェックされません。input の属性として型チェックもしたい場合は、defineProps の型に、input 用の属性の型を「型チェック専用」として足す書き方ができます。
<!-- src/TypedInput.vue -->
<script setup lang="ts">
import type { InputHTMLAttributes } from 'vue'
defineOptions({ inheritAttrs: false })
// @vue-ignore を付けた型は型チェックだけに使われ、props としては登録されない
defineProps<{ label: string } & /* @vue-ignore */ InputHTMLAttributes>()
const model = defineModel<string>({ required: true })
</script>
<template>
<label class="field">
<span class="field-label">{{ label }}</span>
<input v-model="model" v-bind="$attrs" />
</label>
</template>
placeholder="山田 太郎" と maxlength: 20 を渡してマウントした出力:
<label class="field"><span class="field-label">氏名</span><input placeholder="山田 太郎" maxlength="20"></label>
label は props として使われ、placeholder と maxlength は $attrs 経由で input に付いています。この状態で、親が maxlength にオブジェクトを渡すと vue-tsc が指摘します。
<TypedInput v-model="name" label="氏名" :maxlength="{ a: 1 }" />
<BaseInput v-model="name" label="氏名" :maxlength="{ a: 1 }" />
src/TypedParent.vue(10,42): error TS2322: Type '{ a: number; }' is not assignable to type 'Numberish | undefined'.
指摘されたのは TypedInput の行だけで、型を足していない BaseInput の行は通りました。
なお、同じことを interface Props extends /* @vue-ignore */ InputHTMLAttributes { label: string } と interface の継承で書いたところ、今回の環境では label が props として登録されず、「Property "label" was accessed during render but is not defined on instance.」という警告が出ました。交差型(&)で書く形が、今回動作を確認できた書き方です。
script setup以外での書き方
Options API:inheritAttrsオプションとthis.$attrs
<!-- src/OptionsStyle.vue -->
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
inheritAttrs: false,
mounted() {
console.log('this.$attrs のキー:', Object.keys(this.$attrs).join(', '))
},
})
</script>
<template>
<div class="outer">
<button v-bind="$attrs">送信</button>
</div>
</template>
id="send" class="primary" disabled @click を渡した結果です。
this.$attrs のキー: id, class, disabled, onClick
<div class="outer"><button id="send" class="primary" disabled="">送信</button></div>
setup関数:第2引数のattrs
<!-- src/SetupCtx.vue -->
<script lang="ts">
import { defineComponent, h } from 'vue'
export default defineComponent({
inheritAttrs: false,
setup(_props, { attrs }) {
return () => h('div', { class: 'outer' }, [h('button', { ...attrs }, '送信')])
},
})
</script>
<div class="outer"><button id="send" class="primary" disabled="">送信</button></div>
描画関数の中で { ...attrs } と展開すると、template の v-bind="$attrs" と同じ結果になりました。
警告が出たときのチェックリスト
| 警告・症状 | 確認すること | 対応 |
|---|---|---|
| Extraneous non-props attributes (…) | template のルートが複数、またはテキストだけになっていないか | 付けたい要素に v-bind="$attrs"、または1つの要素で包む |
| Extraneous non-emits event listeners (…) | 親が付けた @イベントの行き先があるか | 子が発火するイベントなら defineEmits に宣言、DOM イベントなら v-bind="$attrs" |
| class が外枠に付いてしまう | ルートが外枠の要素になっていないか | inheritAttrs: false + 内側に v-bind="$attrs" |
inheritAttrs: false にしたら属性が消えた |
v-bind="$attrs" を書いたか |
付けたい要素に書く |
| 属性を変えても表示が変わらない | setup で分割代入していないか | computed か template で読む、または props にする |
| 属性が HTML に出てほしくない | props に宣言したか | defineProps に追加する |
よくある質問
Q. class だけは inheritAttrs: false でも継承されますか?
A. Vue 3.5.43 で試した結果、inheritAttrs: false を指定すると class と style もルートには付きませんでした。class と style は $attrs の中に入っているので、付けたい要素に :class="$attrs.class" や v-bind="$attrs" で渡します。
Q. $attrs と props の違いは何ですか? A. 親が渡したもののうち、defineProps・defineEmits で宣言したものは props とイベントとして扱われ、宣言していない残りが $attrs に入ります。子の中で値として使うものは props、要素にそのまま流すものは $attrs、と分けると整理しやすくなります。
Q. defineOptions はどのような場面で使いますか?
A. script setup の中で inheritAttrs のようなコンポーネントのオプションを指定するときに使います。公式ドキュメントでも、script setup で defineOptions({ inheritAttrs: false }) と直接書ける方法として紹介されています。
Q. イベントリスナーも $attrs で渡せますか?
A. 渡せます。@click は onClick、@focus は onFocus というキーで $attrs に入るので、v-bind="$attrs" を書いた要素にリスナーごと登録されます。BaseInput の例では、親の @focus が内側の input に届きました。
まとめ
- $attrs は、props と emits に宣言していない属性・リスナーの集まり。template は
$attrs、script setup はuseAttrs()で取り出す - 単一ルートなら自動でルートに付き、class と style は結合される
inheritAttrs: falseにすると class と style も含めて自動では付かなくなり、v-bind="$attrs"を書いた要素に付く- 複数ルートやテキストだけのルートでは警告が2件出る。どれか1つの要素に
v-bind="$attrs"を書くと解消する - 入力ラッパーは
defineOptions({ inheritAttrs: false })+ 内側の input にv-bind="$attrs"が最小の形 - useAttrs の値は、setup で分割代入すると更新に追従しない。computed や template で読むか、追跡したい値は props にする
- 型は class と style だけ付いており、ほかは unknown。親側の型チェックは交差型と @vue-ignore で足せる
参考・出典
- Vue.js 公式ドキュメント「フォールスルー属性」 https://ja.vuejs.org/guide/components/attrs
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Vue $attrsとuseAttrsの使い方|inheritAttrs早見表」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。