インタビュー中に、応答性 に関連するコンテンツについてよく尋ねられました 。vue3.0の更新後、インタビュアーは新しい武器を手に入れました。
インタビュアー: Vue3.0がレスポンシブシステムを書き換えるのはなぜですか?
インタビュアーが何を求めているのか、どのように答えればよいのか、何が起こっているのかわかりません。
経験豊富な友人の中には、プロキシの利点を説明することから始めて、次のように簡単に話すことがあります。オブジェクトのプロパティを乗っ取るのではなく、オブジェクトを直接プロキシする。
そのような答えはほとんど資格がありません。
だから私は何に答えるべきですか?
インタビュアーの背後にあるロジック
心配しないで、最初に私たちのアイデアを整理しましょう。孫子の戦争の芸術は、「自分と敵を知ってください、あなたは百の戦いで終わることは決してありません」と言います;インタビューは戦争のようなものです、あなたは来て私は行きます、それで私たちは必要ですそれについて考えるために、それについて考えてください、なぜインタビュアーはそのような質問をするのでしょうか?インタビュアーはこの質問からどのような答えを求めていますか?この質問についてどのような技術的ポイントを調査できますか?この問題について明確に考えてから、自分自身に戻ってください。これらすべての技術的なポイントをマスターしましたか?
率直に言って、面接は試験のようなものです。質問に答える前に、質問を読んで確認する必要があります。
なぜ多くの人が「インタビューはロケットと作業ネジを作る」と思うのですか?共感がないため、面接の質問の背後にある論理は明確ではありません。
論理を考えた後は、技術的なポイントを抽出し、アイデアを整理し、それに対応する答えを出す必要があります。もちろん、 これらの技術的な能力が必要であることが前提です。
それでは次に、このインタビューの質問を分解して、知識のポイントを抽出しようと思います。
あなたにとって、これらの知識のポイントを見てください。あなたはどれくらい持っていますか?
Vue3.0がレスポンシブシステムを書き換えるのはなぜですか?
なぜ書き直すのですか?以前にそれがうまく行われていれば、書き直すことは無意味です。以前はどのような問題があり、現在どのように解決されていますか?重要なポイントです。
Vue 2.xの応答性をどれだけ習得したかわかりません。技術的負債を負っていますか?それは問題ではありません、私はあなたが借金を返済するのを手伝います、最初にVue2.xの応答性を整理します。
実際、このインタビューの質問に基づいて、その背後にある多くの技術的なポイントがあります。上記は現在のトピックに直接関連しています。実際のインタビューでは、これらの技術的なポイントが詳細なコミュニケーションに使用される可能性が非常に高いです。利益を上げたとしても、今すぐこれらの問題を解決してください。
Vue2.xレスポンシブ
実際、この点は公式のVueドキュメントですでに説明されており、非常に詳細です。公式ドキュメント: https:// cn.vuejs.org/v2/guide/r eactivity.html
通常のJavaScript オブジェクトをデータオプションとしてVueインスタンスに渡すと 、Vueはこのオブジェクトのすべてのプロパティをトラバースし、 Object.defineProperty を使用してこれらすべてのプロパティをgetter / setterに変換します。Object.definePropertyは、ES5ではシムできない機能です。そのため、VueはIE8以下のブラウザーをサポートしていません。
これらのゲッター/セッターはユーザーには表示されませんが、内部的にはVueが依存関係を追跡し、プロパティがアクセスまたは変更されたときに変更を通知することができます。ここで、コンソールでデータオブジェクトを印刷するときに、ブラウザが異なるとゲッター/セッターがフォーマットされることに注意してください。したがって、データ をチェックするためのより使いやすいユーザーインターフェイスを取得するには、vue-devtoolsをインストールすることをお勧めし ます。
各コンポーネントインスタンスはウォッチャーインスタンスに対応し、コンポーネントのレンダリングプロセス中に「タッチされた」データプロパティを依存関係として記録します。後で、依存関係のセッターがトリガーされると、ウォッチャーに通知されるため、関連するコンポーネントが再レンダリングされます。
プロセス全体を整理するために、職員から提供されたアイコンを使用します。
まず、コードの一部を見てみましょう
レスポンシブ原則
データ内のobjは通常の JavaScript オブジェクトです。[クリック]ボタンをクリックすると、取得したランダムな番号がthis.messageに割り当てられ、this.messageはデータ内のobjオブジェクトのメッセージプロパティを指します。メッセージデータが変更されると、ページ上のH1タグの内容はそれに応じて変更されます。このプロセスは応答性が高いので、Vueはどのように実装しますか?
まず、VueはObject.defineProperty()を内部的に使用して、データの各メンバーをゲッター/セッター形式に変換します。ゲッターは依存関係の収集に使用され、セッターは更新の配布に使用されます。テンプレートのコンテンツは最終的にレンダリングにコンパイルされます。 関数。render関数では、_v(_s(message))メッセージにアクセスし、依存関係の収集を実行するためにゲッターがトリガーされることがわかります。コードのクリックイベントで、イベントハンドラーがトリガーされて実行されるとの場合、メッセージは変更され、セッターがトリガーされて更新が配布されます。
プロセスは明確ですが、何かが足りないといつも感じています。どうすればもっと透明性を高めることができますか?
コードを使用して、実装プロセス全体をシミュレートします。
definePropertyシミュレーションコード
definePropertyの基本的な使用法については、マニュアルを直接読んでください:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
コードを見てみましょう:
<div id="app">
hello
</div>
<script>
// 模拟 Vue 中的 data 选项
let data = {
msg: 'hello'
}
// 模拟 Vue 的实例
let vm = {}
// 数据劫持:当访问或者设置 vm 中的成员的时候,做一些干预操作
Object.defineProperty(vm, 'msg', {
// 可枚举(可遍历)
enumerable: true,
// 可配置(可以使用 delete 删除,可以通过 defineProperty 重新定义)
configurable: true,
// 当获取值的时候执行
get () {
console.log('get: ', data.msg)
return data.msg
},
// 当设置值的时候执行
set (newValue) {
console.log('set: ', newValue)
if (newValue === data.msg) {
return
}
data.msg = newValue
// 数据更改,更新 DOM 的值
document.querySelector('#app').textContent = data.msg
}
})
// 测试
vm.msg = 'Hello World'
console.log(vm.msg)
</script>
あなたはそれを正しく読み、コメント、合計36行のコードを読みます。これは、Vue2.xの応答性の実装のプロセス全体です。
複数のデータの応答性を引き続き実現
<body>
<div id="app">
hello
</div>
<script>
// 模拟 Vue 中的 data 选项
let data = {
msg: 'hello',
count: 10
}
// 模拟 Vue 的实例
let vm = {}
proxyData(data)
function proxyData(data) {
// 遍历 data 对象的所有属性
Object.keys(data).forEach(key => {
// 把 data 中的属性,转换成 vm 的 setter/setter
Object.defineProperty(vm, key, {
enumerable: true,
configurable: true,
get () {
console.log('get: ', key, data[key])
return data[key]
},
set (newValue) {
console.log('set: ', key, newValue)
if (newValue === data[key]) {
return
}
data[key] = newValue
// 数据更改,更新 DOM 的值
document.querySelector('#app').textContent = data[key]
}
})
})
}
// 测试
vm.msg = 'Hello World'
console.log(vm.msg)
</script>
</body>
上記のコードは応答性の原理をシミュレートしているだけですが、Vueの実装では、それは間違いなくそれほど単純ではありません。次に、ソースコードを見てみましょう...
Vue2ソースコードの解釈
まず、レスポンシブコードの処理場所を見つけます。
キーポジション | 効果 | ソースの場所 |
---|---|---|
関数ビュー(){} | Vueコンストラクター | コア/インスタンス/インデックス。js:8 |
Vue.prototype._init | コンポーネントインスタンスオブジェクトを初期化します | コア/インスタンス/初期化。js:16 |
initState | コンポーネントの状態に関連するメンバーを初期化します | core / instance / state.js:48 |
initData | ユーザーから渡されたデータを初期化します | core / instance / state.js:112 |
観察する | データを観察する | core / observer / index.js:110 |
キーポジション | 効果 | ソースの場所 |
---|---|---|
観察する | データを観察する | core / observer / index.js:110 |
クラスオブザーバー | オブザーバーロジック | core / observer / index.js:37 |
歩く | オブジェクトメンバーを個別にトラバースします | core / observer / index.js:64 |
反応性を定義する | コンポーネントインスタンスの応答データを定義する | core / observer / index.js:135 |
Object.defineProperty | データへのアクセスと変更を傍受する | core / observer / index.js:157 |
Vue 2.xのレスポンシブコードを読んだ後、戻って元の質問について考えてみましょう 。Vue3.0がレスポンシブシステムを書き換えるのはなぜですか?
なぜ書き直すのですか?書き換えがかなり前に行われていれば、書き換えても意味がありません。以前の問題は何ですか?つまり、definePropertyの問題は何ですか?
Object.definePropertyの問題
実際、definePropertyの問題は、Vue 2.xのマニュアルですでに言及されています。「ねえ、多くの人はドキュメントを読んでいないだけです。」
https:// cn.vuejs.org/v2/guide/r eactivity.html#%E5%AF%B9%E4%BA%8E%E6%95%B0%E7%BB%84
以下では、Vue2とVue3を使用して小さな関数を実装しています。コードはまったく同じです。関数はもちろん同じですが、Vue2にはバグがあり、Vue3で実行しても問題はありません。
ビュー2:
<template>
<div class="about">
<h1>This is an about page</h1>
<p v-for="(v, k) in users">
{
{ v.names }}
</p>
<button @click="changes">更新</button>
</div>
</template>
<script>
export default {
data() {
return {
users: [
{ id: 1, names: "路飞-v2" },
{ id: 2, names: "鸣人-v2" },
],
};
},
methods: {
changes() {
// this.users[0] = {id:'0',names:'liuneng'}
// this.users[1].names = 'lnsdsdfg'
this.users[1] = { id: "1", names: "刘能-v2" };
},
},
};
</script>
<style lang="stylus" scoped></style>
Vue3:
<template>
<div class="about">
<h1>This is an about page</h1>
<p v-for="(v, k) in users">
{
{ v.names }}
</p>
<button @click="changes">更新</button>
</div>
</template>
<script>
export default {
data() {
return {
users: [
{ id: 1, names: "路飞-v3" },
{ id: 2, names: "鸣人-v3" },
],
};
},
methods: {
changes() {
// this.users[0] = {id:'0',names:'liuneng'}
// this.users[1].names = 'lnsdsdfg'
this.users[1] = { id: "1", names: "刘能-v3" };
},
},
};
</script>
コアポイントは、definePropertyが配列の添え字を適切に監視できないことです。Vue2の実装コードでは、これを改善するためのより良い解決策はありません。Youdaは単に実装をあきらめました。この問題については、Githubが応答し、誰もが見ることができる写真。
したがって、Vueがまだ解決していない問題は、明らかにVue3で解決されます。問題は、Vue3がどのように解決するかということです。以前のVue3のコードでは、従来のオプションApiを使用してデータの応答性を実現しました。Vue3では、新しいコンポジションApiもレスポンシブシステムを実装しています。まず、コンポジションApiの基本的な使用法を体験してみましょう。
構成APIのレスポンシブシステム
レスポンシブ
<template>
<!-- 不需要.value -->
<button @click="addNu"> Composition API: {
{nu}}</button>
</template>
<script>
// 引入 ref
import {ref} from "vue"
export default {
setup() {
// 定义 ref 响应式数据
const nu = ref(1);
// 定义函数
function addNu(){
nu.value++;
}
// 将数据和方法返回,即可在模板中直接使用
return {
nu,
addNu
};
},
};
</script>
反応性
<template>
<!-- 不需要.value -->
<button @click="addNu"> Composition API: {
{nu}}</button>
<!-- reactive 响应式数据 -->
<h2>{
{revData.name}}</h2>
<h3 @click="ageAdd">年龄:{
{revData.age}}</h3>
<p v-for="(v,k) in revData.skill"> {
{v}} </p>
</template>
<script>
// 引入 ref
import {reactive, ref} from "vue"
export default {
setup() {
// 定义 ref 响应式数据
const nu = ref(1);
// reactive 响应式数据
const revData = reactive({
name: '路飞',
age: 22,
skill:['橡胶机关枪','吃鸡腿'],
})
function ageAdd(){
revData.age++
}
// 定义函数
function addNu(){
nu.value++;
}
// 将数据和方法返回,即可在模板中直接使用
return {
nu,
addNu,
revData,
ageAdd
};
},
};
</script>
Vue3のレスポンシブスタイルはどのように実装されていますか?重要なのはプロキシ機能です。
プロキシ実装の原則
Proxyを使用して実装されたリアクティブコードは、definePropertyを使用したコードよりもはるかに単純です。これは、Proxyがトラバーサル後にデータ行を監視する必要なしにオブジェクト全体を自然に監視でき、配列の添え字の問題も解決するためです。
シミュレーションコードの一部を見てみましょう。
<div id="app">
hello
</div>
<script>
// 模拟 Vue 中的 data 选项
let data = {
msg: 'hello',
count: 0
}
// 模拟 Vue 实例
const vm = new Proxy(data, {
// 执行代理行为的函数
// 当访问 vm 的成员会执行
get (target, key) {
console.log('get, key: ', key, target[key])
return target[key]
},
// 当设置 vm 的成员会执行
set (target, key, newValue) {
console.log('set, key: ', key, newValue)
if (target[key] === newValue) {
return
}
target[key] = newValue
document.querySelector('#app').textContent = target[key]
}
})
// 测试
vm.msg = 'Hello World'
console.log(vm.msg)
</script>