<!--view--> <html> ... <div> <span id="name"></span> <div id="data"></div> </div> ... </html> <script> // 生成model数据模型 function getDataApi() { // 模拟接口返回 return { name: 'mvc', data: 'mvc 数据信息' } } // controller控制逻辑 function pageController() { const result = getDataApi(); document.getElementById('name').innerText = `姓名: ${result.name}`; document.getElementById('data').innerText = result.data; } </script>复制代码
什么是MVVM?
<!--view页面--> <html> ... <div> <span vm-bind-key="name"></span> <div vm-bind-key="data"></div> </div> ... </html> <script> // 生成model数据模型 function getDataApi() { // 模拟接口返回 return { name: 'mvc', data: 'mvc 数据信息' } } // ViewModel控制逻辑 function pageViewModel() { const result = getDataApi(); return result; } </script>复制代码
// html <body> <div> <span>{{name}}</span> <span>{{data}}</span> </div> <body> //js <script src="vue.js"></script> <script> // 生成model数据模型 function getDataApi() { // 模拟接口返回 return { name: 'mvc', data: 'mvc 数据信息' } } new Vue({ el: 'body', data() { return { name:'', data: '', } }, mounted() { const result = getDataApi(); this.name = result.name; this.data = result.data; } }) </script>复制代码
// html <body> <span id="name"></span> <body> <script> var data = { name: '' }; // Data Bindings Object.defineProperty(data, 'name', { get : function(){}, set : function(newValue){ // 页面响应处理 document.getElementById('name').innerText = newValue data.name = value }, enumerable : true, configurable : true }); // 页面DOM listener document.getElementById('name').onchange = function(e) { data.name = e.target.value; } </script>复制代码
这里采用Vue3.0最新的实现方式,用Proxy和Reflect来替代Object.definePropertypry的方式。至于Vue3.0为何不再采用2.0中Object.defineProperty的原因,我会在后续详写,先来介绍一下ES6里的Proxy与Reflect。
const myObj = { _id: '我是myObj的ID', name: 'mvvm', age: 25 } const myProxy = new Proxy(myObj, { get(target, propKey) { if (propKey === 'age') { console.log('年龄很私密,禁止访问'); return '*'; } return target[propKey]; }, set(target, propKey, value, receiver) { if (propKey === '_id') { console.log('id无权修改'); return; } target[propKey] = value + (receiver.time || ''); }, // setPrototypeOf(target, proto) {}, // apply(target, object, args) {}, // construct(target, args) {}, // defineProperty(target, propKey, propDesc) {}, // deleteProperty(target, propKey) {}, // has(target, propKey) {}, // ownKeys(target) {}, // isExtensible(target) {}, // preventExtensions(target) {}, // getOwnPropertyDescriptor(target, propKey) {}, // getPrototypeOf(target) {}, }); myProxy._id = 34; console.log(`age is: ${myProxy.age}`); myProxy.name = 'my name is Proxy'; console.log(myProxy); const newObj = { time: ` [${new Date()}]`, }; // 原对象原型链赋值 Object.setPrototypeOf(myProxy, newObj); myProxy.name = 'my name is newObj'; console.log(myProxy.name); /** * id无权修改 * 年龄很私密,禁止访问 * age is: * * { _id: '我是myObj的ID', name: 'my name is Proxy', age: 25 } * my name is newObj [Thu Mar 19 2020 18:33:22 GMT+0800 (GMT+08:00)] */复制代码
const myObj = { _id: '我是myObj的ID', name: 'mvvm', age: 25 } const myProxy = new Proxy(myObj, { get(target, propKey) { if (propKey === 'age') { console.log('年龄很私密,禁止访问'); return '*'; } return target[propKey]; }, set(target, propKey, value, receiver) { if (propKey === '_id') { console.log('id无权修改'); return; } target[propKey] = value + (receiver.time || ''); }, setPrototypeOf(target, proto) { if (proto.status === 'enable') { Reflect.setPrototypeOf(target, proto); return true; } return false; }, }); const newObj = { time: ` [${new Date()}]`, status: 'sable' }; // 原对象原型链赋值 const result1 = Reflect.setPrototypeOf(myProxy, { time: ` [${new Date()}]`, status: 'disable' }); myProxy.name = 'first set name' console.log(result1) //false console.log(myProxy.name); //first set name // 原对象原型链赋值 const result2 = Reflect.setPrototypeOf(myProxy, { time: ` [${new Date()}]`, status: 'enable' }); myProxy.name = 'second set name' console.log(result1) //true console.log(myProxy.name); //second set name [Thu Mar 19 2020 19:43:59 GMT+0800 (GMT+08:00)] /*当执行到这里时直接报错了*/ // 原对象原型链赋值 Object.setPrototypeOf(myProxy, { time: ` [${new Date()}]`, status: 'disable' }); myProxy.name = 'third set name' console.log(myProxy.name); /** * 报错 */复制代码
解释一下上面的这段代码,通过Reflec.setPrototypeOf方法修改原对象原型时,必须经过Proxy里hander的挂载的setPrototypeOf挂载函数,在挂载函数里进行条件proto.status是否是enable筛选后,再决定是否真正修改原对象myObj的原型,最后返回true或者false来告知外部原型是否修改成功。
这里还有一个关键点,就是在代码执行到原有的Object.setPrototypeOf方法时,程序则直接抛错,这其实也是Reflect出现的一个原因,即使现在ES5里的Object有同样的功能,但是Reflect实现的更友好,更适合开发者开发应用程序。
<!DOCTYPE html> <html> <div> name: <input id="name" /> age: <input id="age" /> </div> </html> <script> // 与页面绑定 const data = { name: '', age: 0 } // 暴露到外部,便于查看效果 window.data = data; window.myProxy = new Proxy(data, { set(target, propKey, value) { // 改变数据Model时修改页面 if (propKey === 'name') { document.getElementById('name').value = value; } else if (propKey === 'age') { document.getElementById('age').value = value; } Reflect.set(...arguments); }, }); // 页面变化改变Model内数据 document.getElementById('name').onchange = function(e) { Reflect.set(data, 'name', e.target.value); } document.getElementById('age').onchange = function(e) { Reflect.set(data, 'age', e.target.value); } </script>复制代码