Skip to content

Instantly share code, notes, and snippets.

@ryo-utsunomiya
Last active November 18, 2019 06:27
Show Gist options
  • Save ryo-utsunomiya/b9e1f6ad64047f0f7e219d8037852604 to your computer and use it in GitHub Desktop.
Save ryo-utsunomiya/b9e1f6ad64047f0f7e219d8037852604 to your computer and use it in GitHub Desktop.
Object.definePropertyによるVue.jsのリアクティビティを紐解く ref: https://qiita.com/ryo511/items/b3e639be256d17e31422
const vm = new Vue({
el: '#app',
data: {
message: 'Hello, World',
}
});
// ここではvm.messageはリアクティブ
vm.message = 'Hello, Vue.js!';
function initData (vm: Component) {
let data = vm.$options.data
// (中略)
// observe data
observe(data, true /* asRootData */)
}
export function observe (value: any, asRootData: ?boolean): Observer | void {
// valueの型に応じた処理等があるが、諸々省略すると以下のようになる
return new Observer(value)
}
// リアクティビティに関係ある部分のみ抜粋
export class Observer {
constructor (value: any) {
this.walk(value)
}
walk (obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i], obj[keys[i]])
}
}
const data = { message: 'Hello, World' };
defineRaactive(data, 'message', 'Hello, World');
/**
* Define a reactive property on an Object.
*/
export function defineReactive (
obj: Object,
key: string,
val: any,
customSetter?: ?Function,
shallow?: boolean
) {
const dep = new Dep()
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
const getter = property && property.get
const setter = property && property.set
let childOb = !shallow && observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
if (Dep.target) {
dep.depend()
if (childOb) {
childOb.dep.depend()
}
if (Array.isArray(value)) {
dependArray(value)
}
}
return value
},
set: function reactiveSetter (newVal) {
const value = getter ? getter.call(obj) : val
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter()
}
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
childOb = !shallow && observe(newVal)
dep.notify()
}
})
}
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>defineProperty</title>
</head>
<body>
<h1 id="message"></h1>
<input type="text" id="input">
<script>
// 下準備
let message = 'Hello, World!';
const data = {};
const h1 = document.getElementById('message');
const input = document.getElementById('input');
h1.textContent = input.value = message;
// リアクティブプロパティの定義
Object.defineProperty(data, 'message', {
get() {
return message;
},
set(newVal) {
message = newVal;
h1.textContent = message;
}
});
// 入力値をdataオブジェクトに伝播
input.addEventListener('input', (ev) => {
data.message = ev.target.value;
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment