どのように1つのコンポーネントの論理を多重化するかについて
25388 ワード
前言
本稿では,ReactとVueの2つの主流のビジュアルライブラリの論理的組合せと多重化モードの歴史を簡単に検討した:最初のMixinsからHOC,さらにRender Props,最後に最新のHooksである.
*注意:この文書では、JSスクリプトファイルはすべてグローバルに導入されています.したがって、ESModulesインポートの書き方ではなく、
全文は22560字で、読み終えるのに約45分かかります.
Mixins
オブジェクト向けmixin
mixinsは従来のオブジェクト向けプログラミングで非常に流行している論理多重モードであり、その本質は属性/方法のコピーであり、例えば以下の例である.
Vueのmixin
Vueではmixinには、data、computed、mountedなどのライフサイクルフック関数など、すべてのコンポーネントインスタンスが入力できるオプションが含まれます.同じ名前の競合マージポリシーは、値をオブジェクトとするオプションがコンポーネントデータを優先し、同じ名前のライフサイクルフック関数が呼び出され、mixinのライフサイクルフック関数がコンポーネントの前に呼び出されます.
mixinの同名衝突合併戦略からも分かるように、コンポーネントにmixinを追加するには、コンポーネントに特殊な処理が必要であり、多くのmixinsを追加するとパフォーマンス損失が発生することは避けられない.
Reactのmixin
ReactではmixinはcreateClassメソッドに従って16バージョンで削除されましたが、15のバージョンを探してみることもできます.
Mixinsの欠陥最初にMixinsは暗黙的な依存関係を導入し、特に複数のmixinが導入され、さらにはネストされたmixinが導入された場合、コンポーネント内の属性/メソッドのソースは非常に不明確である. 次にMixinsは名前空間の衝突を引き起こす可能性があり、導入されたmixinはすべて同じ名前空間にあり、前のmixinが導入した属性/方法は後のmixinの同名属性/方法で上書きされ、これはサードパーティ製パッケージを参照した項目に特に友好的ではない ネストMixinsは互いに依存して結合し、雪だるま式の複雑さをもたらし、コードメンテナンス に不利である.
はい、以上はmixinのすべての内容についてです.もしあなたが少し疲れたら、先に休んでみてください.後にはたくさんの内容があります.
HOC
高次関数
まず、高次関数を理解し、ウィキペディアの概念を見てみましょう.
数学とコンピュータ科学では、高次関数は少なくとも以下の条件を満たす関数である:1つ以上の関数を入力として受け入れ、1つの関数を出力する
多くの関数式プログラミング言語で見つけられるmap関数は高次関数の一例である.パラメータとして関数fを受け入れ、リストを受け入れ、各要素にfを適用する関数を返します.関数式プログラミングでは,別の関数を返す高次関数をCurry化関数と呼ぶ.
例を挙げます(私がタイプチェックをしていないことを無視してください):
ReactのHOC
上記の概念によると、高次コンポーネントはコンポーネント関数を受け入れ、コンポーネント関数のCurry化関数を出力するものであり、HOCの最も古典的な例は、コンポーネントにロード状態を包むことである.例えば、
一部のロードが遅いリソースでは、コンポーネントは最初は標準的なLoading効果を示しますが、一定時間(例えば2秒)後には、「リソースが大きく、積極的にロードしています.少々お待ちください」という友好的なヒントになり、リソースのロードが完了してから具体的な内容を示します.
大体こう書いてあります.
VueのHOC
VueでHOCを実装する考え方も同じですが、Vueの入出力コンポーネントは関数やクラスではなく、template/renderオプションを含むJavaScriptオブジェクトです.
このため、VueにおけるHOCの入力は、
ネストされたHOC
ここではReactの書き方を例に挙げます.
したがって,HOCにとってpropsにもネーミング競合の問題があることは明らかである.同じように複数のHOCを導入してHOCをネストした場合でも,コンポーネント内のpropの属性/メソッドのソースは非常に不明確である.
HOCの優位性と欠陥
まず欠陥を言います.まずMixinsと同様にHOCのpropsも暗黙的な依存関係を導入し,複数のHOCを導入し,さらにはネストされたHOCを導入した場合,コンポーネント内のpropの属性/方法源は非常に不明確である .次HOCのpropsは、名前空間の競合を引き起こす可能性があり、propの同名のプロパティ/メソッドは、その後に実行されるHOCによって上書きされます. HOCは、論理をカプセル化するために追加のコンポーネントインスタンスネストを必要とし、無駄なパフォーマンスオーバーヘッド をもたらす.
さらにメリット: HOCは副作用のない純粋な関数であり,ネストされたHOCは相互に依存する相互結合しない である.出力コンポーネントは入力コンポーネントと共有されていない状態であり、自身の
HOCがMixinsがもたらす問題をあまり解決していないことを知りたいかもしれませんが、なぜMixinsを使い続けないのですか?
クラス/関数構文に基づいて定義されたコンポーネントは、mixinsのプロパティ/メソッドをコンポーネントにコピーするにはインスタンス化が必要であり、開発者はコンストラクション関数で自分でコピーできますが、クラスライブラリではこのようなmixinsオプションを提供するのは難しいためです.
さて、以上がHOCについての本文のすべてです.HOCを使った注意事項/compose関数などの知識点は紹介されておらず、不慣れな読者はReactの公式文書を読むことができます.
Render Props
ReactのRender Props
実際には、上記のネストされたHOCのセクションでRender Propsの使い方を見たことがあります.その本質は、レンダリング関数をサブコンポーネントに渡すことです.
Vueのslot
VueにおけるRender Propsに対応する概念は、スロット(slots)またはRenderless Componentsと大まかに呼ばれる.
Vueでは、
紙面に限る、Vue 2.6版以前の書き方:
本稿では,ReactとVueの2つの主流のビジュアルライブラリの論理的組合せと多重化モードの歴史を簡単に検討した:最初のMixinsからHOC,さらにRender Props,最後に最新のHooksである.
*注意:この文書では、JSスクリプトファイルはすべてグローバルに導入されています.したがって、ESModulesインポートの書き方ではなく、
const { createElement: h } = React;
のようなオブジェクトの書き方を解くことができます.また、コメントの内容を読むことに注意してください!全文は22560字で、読み終えるのに約45分かかります.
Mixins
オブジェクト向けmixin
mixinsは従来のオブジェクト向けプログラミングで非常に流行している論理多重モードであり、その本質は属性/方法のコピーであり、例えば以下の例である.
const eventMixin = {
on(type, handler) {
this.eventMap[type] = handler;
},
emit(type) {
const evt = this.eventMap[type];
if (typeof evt === 'function') {
evt();
}
},
};
class Event {
constructor() {
this.eventMap = {};
}
}
// mixin Event
Object.assign(Event.prototype, eventMixin);
const evt = new Event();
evt.on('click', () => { console.warn('a'); });
// 1 click
setTimeout(() => {
evt.emit('click');
}, 1000);
Vueのmixin
Vueではmixinには、data、computed、mountedなどのライフサイクルフック関数など、すべてのコンポーネントインスタンスが入力できるオプションが含まれます.同じ名前の競合マージポリシーは、値をオブジェクトとするオプションがコンポーネントデータを優先し、同じ名前のライフサイクルフック関数が呼び出され、mixinのライフサイクルフック関数がコンポーネントの前に呼び出されます.
const mixin = {
data() {
return { message: 'a' };
},
computed: {
msg() { return `msg-${this.message}`; }
},
mounted() {
// ?
console.warn(this.message, this.msg);
},
};
new Vue({
// el ? el , mounted , , mounted created
el: '#app',
mixins: [mixin],
data() {
return { message: 'b' };
},
computed: {
msg() { return `msg_${this.message}`; }
},
mounted() {
// data message merge, b; msg , msg_b
console.warn(this.message, this.msg);
},
});
mixinの同名衝突合併戦略からも分かるように、コンポーネントにmixinを追加するには、コンポーネントに特殊な処理が必要であり、多くのmixinsを追加するとパフォーマンス損失が発生することは避けられない.
Reactのmixin
ReactではmixinはcreateClassメソッドに従って16バージョンで削除されましたが、15のバージョンを探してみることもできます.
// ,React ,
const mixin = {
// getInitialState() {
// return { message: 'a' };
// },
componentWillMount() {
console.warn(this.state.message);
this.setData();
},
// setData() {
// this.setState({ message: 'c' });
// },
};
const { createElement: h } = React;
const App = React.createClass({
mixins: [mixin],
getInitialState() {
return { message: 'b' };
},
componentWillMount() {
// Vue React : , mixin 。
console.warn(this.state.message);
this.setData();
},
setData() {
this.setState({ message: 'd' });
},
render() { return null; },
});
ReactDOM.render(h(App), document.getElementById('app'));
Mixinsの欠陥
はい、以上はmixinのすべての内容についてです.もしあなたが少し疲れたら、先に休んでみてください.後にはたくさんの内容があります.
HOC
高次関数
まず、高次関数を理解し、ウィキペディアの概念を見てみましょう.
数学とコンピュータ科学では、高次関数は少なくとも以下の条件を満たす関数である:1つ以上の関数を入力として受け入れ、1つの関数を出力する
多くの関数式プログラミング言語で見つけられるmap関数は高次関数の一例である.パラメータとして関数fを受け入れ、リストを受け入れ、各要素にfを適用する関数を返します.関数式プログラミングでは,別の関数を返す高次関数をCurry化関数と呼ぶ.
例を挙げます(私がタイプチェックをしていないことを無視してください):
function sum(...args) {
return args.reduce((a, c) => a + c);
}
const withAbs = fn => (...args) => fn.apply(null, args.map(Math.abs));
// , ,
// function withAbs(fn) {
// return (...args) => {
// return fn.apply(null, args.map(Math.abs));
// };
// }
const sumAbs = withAbs(sum);
console.warn(sumAbs(1, 2, 3));
console.warn(sumAbs(1, -2));
ReactのHOC
上記の概念によると、高次コンポーネントはコンポーネント関数を受け入れ、コンポーネント関数のCurry化関数を出力するものであり、HOCの最も古典的な例は、コンポーネントにロード状態を包むことである.例えば、
一部のロードが遅いリソースでは、コンポーネントは最初は標準的なLoading効果を示しますが、一定時間(例えば2秒)後には、「リソースが大きく、積極的にロードしています.少々お待ちください」という友好的なヒントになり、リソースのロードが完了してから具体的な内容を示します.
const { createElement: h, Component: C } = React;
// HOC
function Display({ loading, delayed, data }) {
if (delayed) {
return h('div', null, ' , , ');
}
if (loading) {
return h('div', null, ' ');
}
return h('div', null, data);
}
// , Curry
const A = withDelay()(Display);
const B = withDelay()(Display);
class App extends C {
constructor(props) {
super(props);
this.state = {
aLoading: true,
bLoading: true,
aData: null,
bData: null,
};
this.handleFetchA = this.handleFetchA.bind(this);
this.handleFetchB = this.handleFetchB.bind(this);
}
componentDidMount() {
this.handleFetchA();
this.handleFetchB();
}
handleFetchA() {
this.setState({ aLoading: true });
// 1 ,
setTimeout(() => {
this.setState({ aLoading: false, aData: 'a' });
}, 1000);
}
handleFetchB() {
this.setState({ bLoading: true });
// 7 , 5
setTimeout(() => {
this.setState({ bLoading: false, bData: 'b' });
}, 7000);
}
render() {
const {
aLoading, bLoading, aData, bData,
} = this.state;
return h('article', null, [
h(A, { loading: aLoading, data: aData }),
h(B, { loading: bLoading, data: bData }),
// ,
h('button', { onClick: this.handleFetchB, disabled: bLoading }, 'click me'),
]);
}
}
// 5
function withDelay(delay = 5000) {
// ?
}
ReactDOM.render(h(App), document.getElementById('app'));
大体こう書いてあります.
function withDelay(delay = 5000) {
return (ComponentIn) => {
class ComponentOut extends C {
constructor(props) {
super(props);
this.state = {
timeoutId: null,
delayed: false,
};
this.setDelayTimeout = this.setDelayTimeout.bind(this);
}
componentDidMount() {
this.setDelayTimeout();
}
componentDidUpdate(prevProps) {
// / , ,
if (this.props.loading !== prevProps.loading) {
clearTimeout(this.state.timeoutId);
this.setDelayTimeout();
}
}
componentWillUnmount() {
clearTimeout(this.state.timeoutId);
}
setDelayTimeout() {
// / delayed
if (this.state.delayed) {
this.setState({ delayed: false });
}
//
if (this.props.loading) {
const timeoutId = setTimeout(() => {
this.setState({ delayed: true });
}, delay);
this.setState({ timeoutId });
}
}
render() {
const { delayed } = this.state;
// props
return h(ComponentIn, { ...this.props, delayed });
}
}
return ComponentOut;
};
}
VueのHOC
VueでHOCを実装する考え方も同じですが、Vueの入出力コンポーネントは関数やクラスではなく、template/renderオプションを含むJavaScriptオブジェクトです.
const A = {
template: 'a',
};
const B = {
render(h) {
return h('div', null, 'b');
},
};
new Vue({
el: '#app',
render(h) {
// , template/render JavaScript
return h('article', null, [h(A), h(B)]);
},
// ,
// components: { A, B },
// template: `
//
//
//
//
// `,
});
このため、VueにおけるHOCの入力は、
const Display = {
// ,
props: ['loading', 'data', 'delayed'],
render(h) {
if (this.delayed) {
return h('div', null, ' , ');
}
if (this.loading) {
return h('div', null, ' ');
}
return h('div', null, this.data);
},
};
//
const A = withDelay()(Display);
const B = withDelay()(Display);
new Vue({
el: '#app',
data() {
return {
aLoading: true,
bLoading: true,
aData: null,
bData: null,
};
},
mounted() {
this.handleFetchA();
this.handleFetchB();
},
methods: {
handleFetchA() {
this.aLoading = true;
// 1 ,
setTimeout(() => {
this.aLoading = false;
this.aData = 'a';
}, 1000);
},
handleFetchB() {
this.bLoading = true;
// 7 , 5
setTimeout(() => {
this.bLoading = false;
this.bData = 'b';
}, 7000);
},
},
render(h) {
return h('article', null, [
h(A, { props: { loading: this.aLoading, data: this.aData } }),
h(B, { props: { loading: this.bLoading, data: this.bData } }),
// ,
h('button', {
attrs: {
disabled: this.bLoading,
},
on: {
click: this.handleFetchB,
},
}, 'click me'),
]);
},
});
withDelay
関数も簡単に書けます.function withDelay(delay = 5000) {
return (ComponentIn) => {
return {
// ComponentIn ComponentOut props `props: ComponentIn.props`
props: ['loading', 'data'],
data() {
return {
delayed: false,
timeoutId: null,
};
},
watch: {
// watch componentDidUpdate
loading(val, oldVal) {
// / , ,
if (oldVal !== undefined) {
clearTimeout(this.timeoutId);
this.setDelayTimeout();
}
},
},
mounted() {
this.setDelayTimeout();
},
beforeDestroy() {
clearTimeout(this.timeoutId);
},
methods: {
setDelayTimeout() {
// / delayed
if (this.delayed) {
this.delayed = false;
}
//
if (this.loading) {
this.timeoutId = setTimeout(() => {
this.delayed = true;
}, delay);
}
},
},
render(h) {
const { delayed } = this;
// props
return h(ComponentIn, {
props: { ...this.$props, delayed },
});
},
};
};
}
ネストされたHOC
ここではReactの書き方を例に挙げます.
const { createElement: h, Component: C } = React;
const withA = (ComponentIn) => {
class ComponentOut extends C {
renderA() {
return h('p', { key: 'a' }, 'a');
}
render() {
const { renderA } = this;
return h(ComponentIn, { ...this.props, renderA });
}
}
return ComponentOut;
};
const withB = (ComponentIn) => {
class ComponentOut extends C {
renderB() {
return h('p', { key: 'b' }, 'b');
}
// HOC
renderA() {
return h('p', { key: 'c' }, 'c');
}
render() {
const { renderB, renderA } = this;
return h(ComponentIn, { ...this.props, renderB, renderA });
}
}
return ComponentOut;
};
class App extends C {
render() {
const { renderA, renderB } = this.props;
return h('article', null, [
typeof renderA === 'function' && renderA(),
'app',
typeof renderB === 'function' && renderB(),
]);
}
}
// renderA ? withA(withB(App)) ?
const container = withB(withA(App));
ReactDOM.render(h(container), document.getElementById('app'));
したがって,HOCにとってpropsにもネーミング競合の問題があることは明らかである.同じように複数のHOCを導入してHOCをネストした場合でも,コンポーネント内のpropの属性/メソッドのソースは非常に不明確である.
HOCの優位性と欠陥
まず欠陥を言います.
さらにメリット:
setState
を用いて出力コンポーネントの状態を直接修正することもできず、状態修正ソースが単一であることを保証している.HOCがMixinsがもたらす問題をあまり解決していないことを知りたいかもしれませんが、なぜMixinsを使い続けないのですか?
クラス/関数構文に基づいて定義されたコンポーネントは、mixinsのプロパティ/メソッドをコンポーネントにコピーするにはインスタンス化が必要であり、開発者はコンストラクション関数で自分でコピーできますが、クラスライブラリではこのようなmixinsオプションを提供するのは難しいためです.
さて、以上がHOCについての本文のすべてです.HOCを使った注意事項/compose関数などの知識点は紹介されておらず、不慣れな読者はReactの公式文書を読むことができます.
Render Props
ReactのRender Props
実際には、上記のネストされたHOCのセクションでRender Propsの使い方を見たことがあります.その本質は、レンダリング関数をサブコンポーネントに渡すことです.
const { createElement: h, Component: C } = React;
class Child extends C {
render() {
const { render } = this.props;
return h('article', null, [
h('header', null, 'header'),
typeof render === 'function' && render(),
h('footer', null, 'footer'),
]);
}
}
class App extends C {
constructor(props) {
super(props);
this.state = { loading: false };
}
componentDidMount() {
this.setState({ loading: true });
setTimeout(() => {
this.setState({ loading: false });
}, 1000);
}
renderA() { return h('p', null, 'a'); }
renderB() { return h('p', null, 'b'); }
render() {
const render = this.state.loading ? this.renderA : this.renderB;
// render,
return h(Child, { render });
}
}
ReactDOM.render(h(App), document.getElementById('app'));
Vueのslot
VueにおけるRender Propsに対応する概念は、スロット(slots)またはRenderless Componentsと大まかに呼ばれる.
const child = {
template: `
header
`,
// , :
// render(h) {
// return h('article', null, [
// h('header', null, 'header'),
// // slot, default Vnode
// this.$slots.default,
// h('footer', null, 'footer'),
// ]);
// },
};
new Vue({
el: '#app',
components: { child },
data() {
return { loading: false };
},
mounted() {
this.loading = true;
setTimeout(() => {
this.loading = false;
}, 1000);
},
template: `
a
b
`,
});
Vueでは、
$slots
を介してライブラリが自動的に渡されるレンダー関数を明示的に渡す必要はありません.紙面に限る、Vue 2.6版以前の書き方:
slot
とslot-scope
ここでは紹介しません.読者はVueの公式ドキュメントを読むことができます.ここではv-slot
の書き方を紹介します.const child = {
data() {
return {
obj: { name: 'obj' },
};
},
// slot , `v-slot:[name]="slotProps"` , slotProps ,
template: `
header
`,
};
new Vue({
el: '#app',
components: { child },
data() {
return { loading: false };
},
mounted() {
this.loading = true;
setTimeout(() => {
this.loading = false;
}, 1000);
},
// #content v-slot:content
template: `
a
b
{{ obj.name }}
`,
});
slot
とは異なり、v-slot
は , 。
Render Props
,Render Props prop , , / prop , HOC 。 prop , HOC prop 。
, Render Props , Hooks。
Hooks
React Hooks
Hooks React 16.8 , useState
にしか できません.
const { createElement: h, useState } = React;
function App() {
// super(props), this.onClick = this.onClick.bind(this)
const [count, setCount] = useState(0);
function onClick() {
// this.state.count, this.setState
setCount(count + 1);
}
return h('article', null, [
h('p', null, count),
h('button', { onClick }, 'click me'),
]);
}
ReactDOM.render(h(App), document.getElementById('app'));
にはライフサイクル フックがないため、React Hooksは の のフックuseEffect
を し、フックのcallbackはレンダリングが した に び されます.const { createElement: h, useState, useEffect } = React;
function App() {
const [message, setMessage] = useState('a');
const [count, setCount] = useState(0);
useEffect(() => {
// `useEffect` , callback, use effect
console.warn('use effect', count);
setTimeout(() => {
setMessage('b');
}, 1000);
// useEffect , effect
return () => {
console.warn('clean up', count);
};
});
useEffect(() => {
// React effect props state , , componentDidMount
}, []);
// state
// const [fake] = useState(0);
// useEffect(() => {}, [fake]);
useEffect(() => {
return () => {
// componentWillUnmount
};
}, []);
console.warn('render', count);
return h('article', null, [
h('p', null, count),
h('button', { onClick }, 'click me'),
h('p', null, message),
]);
}
ReactDOM.render(h(App), document.getElementById('app'));
この2つの も なフックに えて、React HooksはuseCallback
の を げる くの フック を しています.const { createElement: h, useCallback } = React;
function useBinding(initialValue) {
const [value, setValue] = useState(initialValue);
// useCallback
const onChange = useCallback((evt) => {
setValue(evt.target.value);
}, [value]);
return [value, onChange];
}
function App() {
const [value, onChange] = useBinding('text');
return h('article', null, [
h('p', null, value),
h('input', { value, onChange }),
]);
}
はい、Hooksの な い を りました.では、 のHOCの をHooksでどのように き えるのでしょうか.
のロードが いリソースでは、コンポーネントは は なLoading を しますが、 ( えば2 ) には、「リソースが きく、 にロードしています. お ちください」という なヒントになり、リソースのロードが してから な を します.
のwithDelay
をよく ると、コンポーネントレベルではdelayedというpropを コンポーネントに しただけであることがわかります.
Hooksにとっても であり、ビューコンポーネントDisplay
とルートコンポーネントApp
が わらないことを することができ、withDelay
というHOCをカスタムHook useDelay
に するだけで、このHookはdelayed
しか さない.function useDelay({ loading, delay = 5000 }) {
// Hook, delayed
}
function HookDisplay(props) {
const delayed = useDelay({ loading: props.loading });
// Display React HOC
return h(Display, { ...props, delayed });
}
// props , ( HOC )
const A = HookDisplay;
const B = HookDisplay;
// ?
function useDelay({ loading, delay = 5000 }) {
const [delayed, setDelayed] = useState(false);
useEffect(() => {
// / delayed
if (delayed) {
setDelayed(false);
}
//
const timeoutId = loading ? setTimeout(() => {
setDelayed(true);
}, delay) : null;
return () => {
clearTimeout(timeoutId);
};
}, [loading]);
return delayed;
}
VueのComponentAPI
VueにおけるHooksはComposition APIの と ばれているが、 のAPIはまだ していないため、 の が される がある.
に、まず のフックを てみましょう.Vueは、ref
とreactive
( のRFCではvalue
とstate
と ばれていました):
{{ count }}
// vueCompositionApi.default install ( Vue )
const { ref, default: VueCompositionApi } = vueCompositionApi;
Vue.use(VueCompositionApi);
new Vue({
el: '#app',
setup() {
// count , value , 。 ref (value wrapper)
// , `count` `count.value`
const count = ref(0);
function increment() {
// `.value`, Vue `value` `ref` ( value value )
count.value += 1;
}
return { count, increment };
},
});
Vueのref
フックとReactのuseRef
フックにはいくつかの いがあり、useRef
は に のフックではない(あるいは の はビューに しない).const { createElement: h, useRef } = React;
function App() {
const count = useRef(0);
function onClick() {
// ref , current
console.warn(count.current);
count.current += 1;
}
return h('article', null, [
h('p', null, count.current),
h('button', { onClick }, 'click me'),
]);
}
ReactDOM.render(h(App), document.getElementById('app'));
{{ state.count }}
{{ state.double }}
const { default: VueCompositionApi, reactive, computed } = vueCompositionApi;
Vue.use(VueCompositionApi);
new Vue({
el: '#app',
setup() {
const state = reactive({
count: 0,
double: computed(() => state.count * 2),
});
// Vue.observable
// ,vueCompositionApi.computed , state.double
// const state = Vue.observable({
// count: 0,
// double: computed(() => state.count * 2),
// });
function increment() {
state.count += 1;
}
return { state, increment };
},
});
React Hooksは、コンポーネントがレンダリングされるたびに び され、 の コンポーネントノードに を にマウントし、 のレンダリング に び し に り します. 、Vueのsetup() コンポーネントインスタンスは、 に1 のみ び され、ステータスはsetup()の パケットに によって されます.
そのため、Vueは の のフックを していません. としてライフサイクル のフックを しています.on を えた は、onMountedを に げます.
- {{ item }}
const { default: VueCompositionApi, reactive, onMounted } = vueCompositionApi;
Vue.use(VueCompositionApi);
new Vue({
el: '#app',
setup() {
const list = reactive([1, 2, 3]);
onMounted(() => {
setTimeout(() => {
list.push(4);
}, 1000);
});
return { list };
},
});
では、 のHOCの をCompositionAPIに するには、Display
コンポーネントオブジェクトおよびルートVueインスタンスオプションを する はほとんどありません.function useDelay(props, delay = 5000) {
// Hook, delayed
}
const HookDisplay = {
props: ['loading', 'data'],
setup(props) {
const delayed = useDelay(props);
return { delayed };
},
render(h) {
// Display Vue HOC
return h(Display, {
props: {
...this.$props, delayed: this.delayed,
},
});
},
};
const A = HookDisplay;
const B = HookDisplay;
const {
default: VueCompositionApi, ref, watch, onMounted, onUnmounted,
} = vueCompositionApi;
Vue.use(VueCompositionApi);
function useDelay(props, delay = 5000) {
const delayed = ref(false);
let timeoutId = null;
// props loading
// loading , ( getter/setter)
watch(() => props.loading, (val, oldVal) => {
if (oldVal !== undefined) {
clearTimeout(timeoutId);
setDelayTimeout();
}
});
onMounted(() => {
setDelayTimeout();
});
onUnmounted(() => {
clearTimeout(timeoutId);
});
function setDelayTimeout() {
if (delayed.value) {
delayed.value = false;
}
if (props.loading) {
timeoutId = setTimeout(() => {
delayed.value = true;
}, delay);
}
}
return delayed;
}
Hooksのメリット
HooksとRender Propsの え は し ていることがわかりますが、Render Propsはコンポーネントを し、Hooksはいくつかの を します( でコンポーネントに える があります).Hooksのような のカプセル のおかげで、レンダリング はコンポーネントを して する がなくなり、Render Propsが をカプセル するために のコンポーネントインスタンスネストを とする が された.
が, せと モードに する のすべてである. に れや りがあるのは けられないが, の と を んでいる.