Open zeroone001 opened 5 years ago
<body>
hello,world
<input type="text" id="model">
<p id="word"></p>
</body>
<script>
const model = document.getElementById("model")
const word = document.getElementById("word")
var obj= {};
const newObj = new Proxy(obj, {
get: function(target, key, receiver) {
console.log(`getting ${key}!`);
return Reflect.get(target, key, receiver);
},
set: function(target, key, value, receiver) {
console.log('setting',target, key, value, receiver);
if (key === "text") {
model.value = value;
word.innerHTML = value;
}
return Reflect.set(target, key, value, receiver);
}
});
model.addEventListener("keyup",function(e){
newObj.text = e.target.value
})
</script>
<div id="app">
<input type="text" id="input">
<div>
TODO:
<span id="text"></span>
</div>
<div id="btn">Add To Todo List</div>
<ul id="list"></ul>
</div>
const input = document.getElementById('input')
const text = document.getElementById('text')
const list = document.getElementById('list')
const btn = document.getElementById('btn')
let render
const inputObj = new Proxy({}, {
get (target, key, receiver) {
return Reflect.get(target, key, receiver)
},
set (target, key, value, receiver) {
if (key === 'text') {
input.value = value
text.innerHTML = value
}
return Reflect.set(target, key, value, receiver)
}
})
class Render {
constructor (arr) {
this.arr = arr
}
init () {
const fragment = document.createDocumentFragment()
for (let i = 0; i < this.arr.length; i++) {
const li = document.createElement('li')
li.textContent = this.arr[i]
fragment.appendChild(li)
}
list.appendChild(fragment)
}
addList (val) {
const li = document.createElement('li')
li.textContent = val
list.appendChild(li)
}
}
const todoList = new Proxy([], {
get (target, key, receiver) {
return Reflect.get(target, key, receiver)
},
set (target, key, value, receiver) {
if (key !== 'length') {
render.addList(value)
}
return Reflect.set(target, key, value, receiver)
}
})
window.onload = () => {
render = new Render([])
render.init()
}
input.addEventListener('keyup', e => {
inputObj.text = e.target.value
})
btn.addEventListener('click', () => {
todoList.push(inputObj.text)
inputObj.text = ''
})
<b id="count"></b>
<button onclick="increase()">+</button>
<button onclick="decrease()">-</button>
const data = { count: 0 };
const proxy = new Proxy(data, {
get(target, property) {
return target[property];
},
set(target, property, value) {
target[property] = value;
render(value);
}
});
render(proxy.count);
function render(value) {
document.getElementById('count').innerHTML = value;
}
function increase() {
proxy.count += 1;
}
function decrease() {
proxy.count -= 1;
}
let person = {
name:'jesse',
age:25
}
let proxy = new Proxy(person,{
get(target,prop){
console.log('get')
return target[prop]
},
set(obj,prop,value){
if(value>=30){
throw new Error('invalid')
}
obj[prop] = value
}
})
console.log(proxy.name) //get jesse
proxy.age = 30 //Uncaught Error: invalid
利用Proxy实现一个简化版的MVVM
参照vue的响应式设计模式,将数据劫持部分的Obejct.defineProperty
替换为Proxy
即可,其他部分,如compile(编译器没有实现,用写好的html模拟已完成编译),watcher,dep,事件监听等基本保持不变,简单实现代码如下:
<!-- html部分 -->
<div id="foo"></div>
<input type="text" name="" id="bar"/>
// js部分
class Watcher{
constructor(cb){
this.cb = cb;
}
update(){
this.cb()
}
}
class Dep{
constructor(){
this.subs = [];
}
publish(){
this.subs.forEach((item)=>{
item.update && item.update();
})
}
}
class MVVM{
constructor(data){
let that = this;
this.dep = new Dep();
this.data = new Proxy(data,{
get(obj, key, prox){
that.dep.target && that.dep.subs.push(that.dep.target);
return obj[key]
},
set(obj, key, value, prox){
obj[key] = value;
that.dep.publish();
return true;
}
})
this.compile();
}
compile(){
let divWatcher = new Watcher(()=>{
this.compileUtils().div();
})
this.dep.target = divWatcher;
this.compileUtils().div();
this.dep.target = null;
let inputWatcher = new Watcher(()=>{
this.compileUtils().input();
})
this.dep.target = inputWatcher;
this.compileUtils().input();
this.compileUtils().addListener();
this.dep.target = null;
}
compileUtils(){
let that = this;
return {
div(){
document.getElementById('foo').innerHTML = that.data.foo;
},
input(){
document.getElementById('bar').value = that.data.bar;
},
addListener(){
document.getElementById('bar').addEventListener('input', function(){
that.data.bar = this.value;
})
}
}
}
}
let mvvm = new MVVM({foo: 'foo233', bar: 'bar233'})
通过mvvm.data.foo
或者mvvm.data.bar
可以操作数据,可以观察到view做出了改变;在输入框改变输入值,也可以通过mvvm.data
观察到数据被触发改变
<b id="count"></b> <button onclick="increase()">+</button> <button onclick="decrease()">-</button>
const data = { count: 0 }; const proxy = new Proxy(data, { get(target, property) { return target[property]; }, set(target, property, value) { target[property] = value; render(value); } }); render(proxy.count); function render(value) { document.getElementById('count').innerHTML = value; } function increase() { proxy.count += 1; } function decrease() { proxy.count -= 1; }
set 方法必须返回 true 或者 false 你这样写是有问题的 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/set
<body>
His Name:<span id="username"></span>
</body>
<script>
//定义需要监控的_data对象
var _data = { name: "zhangsan", age: 18 };
//监控_data
var data = new Proxy(_data, {
set(obj, key, value) {
obj[key] = value;
//当有值的时候刷新显示区域
render();
},
get(obj, key) {
return obj[key];
}
})
function render() {
document.getElementById("username").innerHTML = data.name;
}
render();
</script>
可打开f12修改 data.name
查看改变情况
监听dom的value变化 去更新 obj obj的数据发生变化 去更新 dom
普通简易版本:
名字:<input type="text" id="name"><br/>
你的名字: <p id="pName"></p>
<script type="text/javascript">
var obj = {
name: ''
}
Object.defineProperty(obj, 'name', {
set: function(value) {
document.getElementById('name').value = value
document.getElementById('pName').innerHTML = value
}
})
document.getElementById('name').addEventListener('input', function(e){
obj.name = e.target.value
})
</script>
proxy版本 好像没啥特殊的:
名字:<input type="text" id="name"><br/>
你的名字: <p id="pName"></p>
<script type="text/javascript">
// Proxy
var obj = {
name: ''
}
var proxyObj = new Proxy(obj, {
get: function(target, key, receiver) {
return Reflect.get(target, key, receiver)
},
set: function(target, key, value, receiver) {
if (key === 'name') {
document.getElementById('name').value = value
document.getElementById('pName').innerHTML = value
}
return Reflect.set(target, key, value, receiver)
}
})
document.getElementById('name').addEventListener('input', function(e){
proxyObj.name = e.target.value
})
</script>
<body>
<div id="root">
<input type="text" v-model="title">
<input type="text" v-model="title">
<div v-bind="title"> </div>
</div>
<script>
'user strict'
function View() {
// 设置代理拦截
let proxy = new Proxy({}, {
get(obj, property) { },
set(obj, property, value) {
document.querySelectorAll(`[v-model='${property}'],[v-bind='${property}']`)
.forEach(el => el.innerHTML = el.value = value)
}
})
// 初始化 绑定元素
this.run = function () {
const elems = document.querySelectorAll("[v-model]");
elems.forEach(el => {
el.addEventListener('keyup', event => {
proxy[event.target.getAttribute('v-model')] = event.target.value;
})
})
}
}
let view = new View();
view.run();
</script>
</body>
<input id="inp" type="text" oninput="handleChange()">
<div id="app"></div>
<script>
let inp = document.getElementById('inp')
let app = document.getElementById('app')
let obj = {
defaultValue: 'hello world'
}
let proxy = new Proxy(obj, {
get: function(obj, key) {
console.log('get')
return obj[key]
},
set(obj, key, value) {
obj.defaultValue = value
notify()
}
})
app.innerHTML = proxy.defaultValue
inp.value = proxy.defaultValue
function notify() {
app.innerHTML = proxy.defaultValue
}
function handleChange() {
proxy.defaultValue = inp.value
}
</script>
<body> hello,world <input type="text" id="model"> <p id="word"></p> </body> <script> const model = document.getElementById("model") const word = document.getElementById("word") var obj= {}; const newObj = new Proxy(obj, { get: function(target, key, receiver) { console.log(`getting ${key}!`); return Reflect.get(target, key, receiver); }, set: function(target, key, value, receiver) { console.log('setting',target, key, value, receiver); if (key === "text") { model.value = value; word.innerHTML = value; } return Reflect.set(target, key, value, receiver); } }); model.addEventListener("keyup",function(e){ newObj.text = e.target.value }) </script>
model.value = value; 这一行代码是不是不需要写
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<span></span>
<br>
<input type="text" oninput="input(event)">
<script>
let spanEl = document.querySelector('span')
let inputEl = document.querySelector('input')
let data = {
val: null
}
let proxy = new Proxy(data, {
get(target, p, receiver) {
return Reflect.get(target, p, receiver)
},
set(target, p, val, receiver) {
spanEl.innerText = val
inputEl.value = val
return Reflect.set(target, p, val, receiver)
}
})
function input(e) {
proxy.val = e.target.value
}
</script>
</body>
</html>
<input type="text" id="input">
<p id="text">hello world</p>
<script>
let input = document.getElementById('input');
let text = document.getElementById('text');
let obj = {};
let proxy = new Proxy(obj, {
get(target, property){
return Reflect.get(...arguments);
},
set(target, property, value){
target[property] = value;
text.innerText = target[property];
return Reflect.set(...arguments);
}
})
proxy.val = text.innerText;
input.value = proxy.val;
input.addEventListener('input', function(){
proxy.val = this.value;
})
</script>
let activeEffect = null;
let wm = new WeakMap();
function watchEffect(fn) {
activeEffect = fn;
fn();
activeEffect = null;
}
function reactive(obj) {
return new Proxy(obj, {
get(target, key, receiver) {
track(target, key);
return Reflect.get(target, key, receiver)
},
set(target, key, val, receiver) {
Reflect.set(target, key, val, receiver);
trigger(target, key);
}
})
}
function track(target, key) {
if (activeEffect) {
let map = wm.get(target);
if (!map) {
map = new Map();
wm.set(target, map);
}
let bucket = map.get(key);
if (!bucket) {
bucket = [];
map.set(key, bucket);
}
bucket.push(activeEffect);
}
}
function trigger(target, key) {
let map = wm.get(target);
if (map) {
let bucket = map.get(key);
if (bucket) {
bucket.forEach(item => item());
}
}
}
const t1 = reactive({a: 1, b: 2});
watchEffect(() => {
console.log(t1.a);
})
setTimeout(() => {
t1.a = 222;
}, 3000);
这里,欢迎star https://github.com/GuoYuFu123/test-project/blob/master/proxy/proxyvue.html