MVVM原理的理解和实现

“终于懂了“系列:Jetpack AAC完整解析(四)MVVM - Android架构探索 Jetpack AAC 系列文章: “终于懂了“系列:Jetpack AAC完整解析(一)Lifecycle 完全掌握! “终于懂了“系列:Jetpack AAC完整解析(二)LiveData 完全掌握! “终于懂了“系列:Jetpack AAC完整解析(三)ViewModel 完全掌握! “终于懂了“系列:Jetpack AAC完整解析(四)MVVM - Android架构探索! … 欢迎关注我的 公 众 号,微信搜索 胡飞洋 ,文章更新可第一时间收到。 前面三篇介绍了Jetpack 架构组件中. 阅读详情

MVVM框架概念

1.概念

http://cn.vuejs.org/images/mvvm.png?_=5619070

经典MVVM模型图,由 View、Model、ViewModel 三部分组成。

View:视图模版,负责将Model转化为UI,并展示;

Model:数据模型,根据业务逻辑操作数据;

ViewModel:通过*双向绑定*连接了View和Model;

2.原理

在MVVM的架构中 Vue.js 的原理是对数据(Model)进行劫持(Object.defineProperty( )),在数据发生变化时,数据会触发劫持时绑定的setter方法,对视图(View)进行更新。

3.对比

jQuery 的原理,如果数据发生变化,需要先获取对应的DOM元素,然后才能更新UI。数据以及业务逻辑和页面形成了强耦合。

MVVM 的原理则是监听数据,数据变化后只刷新对应的UI,只需要关心操作数据,不需要操作DOM。MVVM中核心就是数据双向绑定。

Vue数据双向绑定原理

在这里插入图片描述

1.原理

Vue数据双向绑定通过数据劫持以及发布者-订阅者模式的方式实现。

2.相关代码

Object.defineProperty()

通过defineProperty()劫持属性的getter/setter,结合发布者-订阅者的方式,发送消息给订阅者,触发对应的回调函数。通过指令(v-xxx)去对DOM进行封装。当数据发生变化,指令修改对应的DOM,数据驱动DOM的变化。反向,Vue也会监听操作,修改视图时,Vue监听到变化后,改变数据。数据的双向变化形成。

双向绑定实现

1.创建Observer

本质上是一个数据属性监听器,核心方法就是Object.defineProperty()。注意:是对所有属性进行监听,那么就意味着如果数据是一个复杂对象,那么就要进行递归遍历深层属性。

创建Observer:

// 为了实现数组的响应式,需要重写数组的原型方法
const originPrototype = Array.prototype; // 空数组
const newArrPrototype = Object.create(originPrototype)
const methods = [
    'push',
    'pop',
    'shift',
    'unshift',
    'splice',
    'sort',
    'reverse'
]
methods.forEach(method => {
    // 重新准备数组原型方法,放在newArrPrototype上
    newArrPrototype[method] = function(){
        // 获取原来的对应的原型方法
        originPrototype[method].apply(this, arguments);
    }
})

// 模拟defineReactive
let defineReactive = (obj, key, value) => {
    // 递归调用,判断value是否也是对象,用于把深层对象设置成响应式属性
    observer(value);

    // obj 传入对象; key监听的属性; value初始值;
    // 拦截传入的参数,进行判断,是否发生数据变化
    // defindReactive对数组的拦截无效,需要修改原型
    Object.defineProperty(obj, key, {
        get() {
            console.log('get: ' + key);
            return value;
        },
        set(newValue) {
            if (newValue !== value) {
                // 为了防止传进来的也是一个对象,set的时候也要调用observer
                observer(newValue);
                console.log('set: ' + key + ', value: ' + newValue);
                value = newValue;
            }
        }
    })
}

let observer = function(obj){
    // 数组相关的方法是需要单独处理的
    if(typeof obj === 'object' && obj != null){
        // 增加对数组的判断,数组需要特殊处理
        if(Array.isArray(obj)){
            // 重新指定对象原型
            obj.__proto__ = newArrPrototype;
            // 遍历数组元素,创建响应式
            for (let i = 0; i < obj.length; i++) {
                // 如果是多维数组,递归调用
                observer(obj[i])
            }
        }else{
            // 获取obj的所有key
            let allKey = Object.keys(obj);
            // 遍历所有key,让每个属性都变成响应式属性
            allKey.forEach(key => {
                defineReactive(obj, key, obj[key]);
            })
        }
    }
}

2.创建Dep并植入Oberver

在原理图中可以看出,对属性监听完成后,需要一个消息订阅器(Dep),Dep的作用就是收集所有的watcher。便于数据发生变化时,通知(notify)相关的watcher,去更新视图(View)。

那么创建Dep后,就需要改造上边创建的Observer:

// 植入Dep,管理订阅者(watcher)
// 创建劫持监听
class Observer {
    constructor(value) {
        this.value = value;
        // 判断value的类型:对象or数组
        if (typeof value === 'object' && value != null && !Array.isArray(value)) {
            this.walk(value)
        } else {
            // 判断value是数组类型
            // 为了实现数组的响应式,需要重写数组的原型方法
            const originPrototype = Array.prototype; // 空数组
            const newArrPrototype = Object.create(originPrototype)
            const methods = [
                'push',
                'pop',
                'shift',
                'unshift',
                'splice',
                'sort',
                'reverse'
            ]
            methods.forEach(method => {
                // 重新准备数组原型方法,放在newArrPrototype上
                newArrPrototype[method] = function(){
                    // 获取原来的对应的原型方法
                    originPrototype[method].apply(this, arguments);
                }
            })
           	// 重新指定对象原型
            obj.__proto__ = newArrPrototype;
            // 遍历数组元素,创建响应式
            for (let i = 0; i < obj.length; i++) {
                // 如果是多维数组,递归调用
                observer(obj[i])
            }
        }
    }
    // 对象
    walk(obj) {
        Object.keys(obj).forEach(key => {
            defineReactive(obj, key, obj[key]);
        })
    }
    // 数组
}
let observer = function (obj) {
    if (typeof obj === 'object' && obj != null) {
        // 创建Observer实例
        new Observer(obj);
    }
}

// 模拟defineReactive
let defineReactive = (obj, key, value) => {
    // 递归调用,判断value是否也是对象,用于把深层对象设置成响应式属性
    observer(value);
		
  	// ------------------------------------------------------
  	// 创建Dep,关联指定的key
    let dep = new Dep();
		// ------------------------------------------------------
  
    // obj 传入对象; key监听的属性; value初始值;
    // 拦截传入的参数,进行判断,是否发生数据变化
    // defindReactive对数组的拦截无效,需要修改原型
    Object.defineProperty(obj, key, {
        get() {
            console.log('get: ' + key);
          	// ------------------------------------------------------
          	// !!!!!!!!!!注意:在触发getter的时候,开始收集依赖。
          	// 也就是说,所有的依赖收集过程都发生在getter
          	if(收集依赖的条件){
              dep.addDep(订阅者)
            }
          	// ------------------------------------------------------
            return value;
        },
        set(newValue) {
            if (newValue !== value) {
                // 为了防止传进来的也是一个对象,set的时候也要调用observer
                observer(newValue);
                console.log('set: ' + key + ', value: ' + newValue);
                value = newValue;
            }
        }
    })
}


创建Dep:

// 依赖收集
// 管理某个key对应的所有的watcher
class Dep {
    constructor(){
        this.deps = [];
    }
    addDep(dep){
        this.deps.push(dep)
    }
    notify(){
        this.deps.forEach(dep => {
            console.log(dep);
            dep.update();
        })
    }
}

总结:Dep收集watcher放在了getter中,是让Watcher在初始化的时候进行触发。

3.创建Wacther

创建watcher的时候,即初始化watcher的时候,主动触发getter,将自己添加到Dep中:

class Watcher{
    constructor(vm, key, fn){
        this.vm = vm;
        this.key = key;
        this.updateFn = fn;

        // 当前watcher赋值给target
        Dep.target = this;
        // 因为依赖收集发生在getter阶段,所以主动触发getter,将对应key的watcher收集到dep中
        this.vm[this.key];
        // 当前key的依赖收集完成后,立即释放target
        Dep.target = null;

    }
    update(){
        this.updateFn.call(this.vm, this.vm[this.key]);
    }
}

Watcher创建完成后,那么需要对第二步中的Observer继续改造:

// 植入Dep,管理订阅者(watcher)
// 创建劫持监听
class Observer {
    constructor(value) {
        this.value = value;
        // 判断value的类型:对象or数组
        if (typeof value === 'object' && value != null && !Array.isArray(value)) {
            this.walk(value)
        } else {
            // 判断value是数组类型
            // 为了实现数组的响应式,需要重写数组的原型方法
            const originPrototype = Array.prototype; // 空数组
            const newArrPrototype = Object.create(originPrototype)
            const methods = [
                'push',
                'pop',
                'shift',
                'unshift',
                'splice',
                'sort',
                'reverse'
            ]
            methods.forEach(method => {
                // 重新准备数组原型方法,放在newArrPrototype上
                newArrPrototype[method] = function(){
                    // 获取原来的对应的原型方法
                    originPrototype[method].apply(this, arguments);
                }
            })
           	// 重新指定对象原型
            obj.__proto__ = newArrPrototype;
            // 遍历数组元素,创建响应式
            for (let i = 0; i < obj.length; i++) {
                // 如果是多维数组,递归调用
                observer(obj[i])
            }
        }
    }
    // 对象
    walk(obj) {
        Object.keys(obj).forEach(key => {
            defineReactive(obj, key, obj[key]);
        })
    }
    // 数组
}
let observer = function (obj) {
    if (typeof obj === 'object' && obj != null) {
        // 创建Observer实例
        new Observer(obj);
    }
}

// 模拟defineReactive
let defineReactive = (obj, key, value) => {
    // 递归调用,判断value是否也是对象,用于把深层对象设置成响应式属性
    observer(value);
		
  	// ------------------------------------------------------
  	// 创建Dep,关联指定的key
    let dep = new Dep();
		// ------------------------------------------------------
  
    // obj 传入对象; key监听的属性; value初始值;
    // 拦截传入的参数,进行判断,是否发生数据变化
    // defindReactive对数组的拦截无效,需要修改原型
    Object.defineProperty(obj, key, {
        get() {
            console.log('get: ' + key);
          	// ------------------------------------------------------
          	// !!!!!!!!!!注意:在触发getter的时候,开始收集依赖。
          	// 也就是说,所有的依赖收集过程都发生在getter
          	Dep.target && dep.addDep(Dep.target);
          	// ------------------------------------------------------
            return value;
        },
        set(newValue) {
            if (newValue !== value) {
                // 为了防止传进来的也是一个对象,set的时候也要调用observer
                observer(newValue);
                console.log('set: ' + key + ', value: ' + newValue);
                value = newValue;
              	
          			// ------------------------------------------------------
              	// 在setter触发的时候,通知对应的watcher去更新view
              	dep.notify()
          			// ------------------------------------------------------
            }
        }
    })
}

至此为止,只需要将Observer和Watcher关联起来,就可以实现数据的双向绑定。

4.创建Compile

Compile的作用就是编译模版,包括模版上的指令;

在Compile的构造中需要两个参数,一个是el(new Vue的时候挂载到跟元素),一个是实例。

开始编译时(compile()),开始遍历节点进行判断,判断是什么节点以及是否存在子节点,如果存在递归遍历。

编译过程中,对于指定类型的节点开始进行初始化,此时,开始建立关联关系!

// 创建编译器
// 获取dom节点,遍历节点
// 判断节点类型:dom节点/文本节点
class Compiler {
    constructor(el, vm) {
        this.$el = document.querySelector(el);
        this.$vm = vm;

        if (this.$el) {
            this.compile(this.$el);
        }
    }
    compile(el) {
        // 获取子节点
        const childNodes = el.childNodes;
        // 遍历孩子节点,用于判断是dom节点还是文本节点
        Array.from(childNodes).forEach(node => {
            if (this.isElement(node)) {
                // console.log('元素节点');
                this.compileElement(node);
            } else if (this.isInter(node)) {
                // console.log('文本节点');
                this.compileText(node);
            }
            // 判断是否有子节点,递归调用
            if (node.childNodes && node.childNodes.length > 0) {
                this.compile(node);
            }
        })
    }
    isElement(node) {
        return node.nodeType === 1;
    }
    isInter(node) {
        // 判断插值文本{{}}
        return node.nodeType === 3 && /\{\{(.*)\}\}/.test(node.textContent);
    }
    compileText(node) {
        // 从实例中获取插值
        // 创建更新器后,会被抽取到公共方法中
        // node.textContent = this.$vm[RegExp.$1];
        // 创建更新器
        this.update(node, RegExp.$1, 'text');
    }
    compileElement(node) {
        // 遍历元素节点的属性
        const attrs = node.attributes;
        Array.from(attrs).forEach(attr => {
            // 获取指令名称
            const attrName = attr.name;
            // 判断是一般写法还是简写
            if (attrName.indexOf('-') > 0) {
                if (attrName.indexOf('on') > 0) {
                    const exp = attrName.substring(2, 4);
                    this[exp] && this[exp](attr, node);
                } else {
                    // 常规指令写法
                    const exp = attrName.substring(2);
                    this[exp] && this[exp](attr, node);
                }
            }
        })
    }
    update(node, exp, dir) {
        // 初始化操作
        const fn = this[dir+'Updater'];
        fn && fn(node, this.$vm[exp])

        // 更新操作
        // 在更新的时候创建一个watcher
      	// ----------------------------------------------------
      	// 创建了Observer和Watcher的关系
        new Watcher(this.$vm, exp, function(value){
            fn && fn(node, value);
            //textUpdater
        })
    }
    textUpdater(node, value){
        node.textContent = value;
    }
    text(attribute, node) {
        this.update(node, attribute.value, 'text');
    }
    // 更新函数!!!
    htmlUpdater(node, value){
        node.innerHTML = value;
    }
    html(attribute, node) {
        this.update(node, attribute.value, 'html');
    }
    on(attribute, node) {
        node.addEventListener('click', this.$vm[attribute.value])
    }

}

5.何时调用Compile

在new Vue的时候调用了Compile:

const app = new kVue({
  el: '#app',
  data: {
    counter: 0,
    str: '<span style="color: red">我是html代码</span>'
  },
  methods: {
    aa() {
      alert(1)
    }
  },
})

创建kVue类:

// 创建kVue的构造函数
class kVue {
    constructor(options) {
        // 保存options
        this.$options = options;
        this.$data = options.data;
        this.$methods = options.methods;
        // 实例化的时候只有data中的数据需要实现响应化
        // 处理响应化的时候需要判断传入的参数是对象还是数组
        observer(this.$data);

        // 代理this中的$data
        proxy(this, '$data');
        proxy(this, '$methods')

        // 编译
        new Compile(this.$options.el, this)
    }
}

总结

以上就实现了一个简单的数据的双向绑定,对于各种指令以及事件,可以自行扩展。

代码奉上

【Java集合 6】arraycopy方法的作用 一、前言 对数组的复制,有四种方法: for clone System.arraycopy arrays.copyof 下面分析一下最常用的System.arraycopy() 二、源码拜读 三、深拷贝与浅拷贝 1、简单类型(深拷贝) package com.guor.test.javaSE.collection; import java.util.Arrays; public class ArrayTest { public static void main(String[ 阅读详情

相关推荐

C语言之三维数组

前言: 之前学习C语言的时候仅仅是掌握了二维数组,但是并没有对三维数组进行研究,今天的代码提前搞完了,摸摸鱼,对去年研究的三维数组的相关知识发布一下,期待能够帮助到有缘人!实际上,当你阅读此篇文章时,我假设你已经对C语言的二维数组非常熟悉了,并且非常熟悉数组与指针之间的关系,如果没有达到此要求,那直接翻篇,不要看,等待基础掌握扎实后再来学习此篇文章! 一,看图说话,一眼看三维数组 二,首先看下三维数组的遍历 #include "stdio.h" void main(){ //三维数组 .

十三阿哥的博客 5万+

System.arraycopy的使用方法详解

System.arraycopy的使用方法详解 System.arraycopy这个方法之前用得很少,前段时间在一个项目需要对很多字节的处理,使用这个方法是非常有用的。 这个方法的作用大家应该都是知道的吧:就是把一个数组中某一段字节数据放到另一个数组中。至于从第一个数组中取出几个数据,放到第二个数组中的什么位置都是可以通知这个方法的参数控制的。 一.System.arraycopy使用的基本定义 public static void arraycopy(Object src, int srcPos,

奇点 2万+

【前端知识点】前端设计模式 MVVM和MVC,vue的数据双向绑定原理

Vue 的双向数据绑定是通过数据劫持实现的。在 Vue 的 MVVM 模式中,View 层负责渲染数据和交互事件,Model 层负责存储数据和与后端交互,ViewModel 层则是负责监听 Model 的数据变化,通过双向数据绑定将数据变化自动同步到 View 上,同时也通过事件监听,将 View 上的用户操作同步到 Model 上。总之,Vue的MVVM架构中,Model层负责数据的存储和处理,ViewModel层负责逻辑的处理和控制数据的流向,View层负责页面元素的展示和与用户的交互。

代码的魅力,根本走不出来 1138

System.arraycopy详解

对数组的复制,有四种方法:forclone下面分析一下最常用的System.arraycopy()

yangruidage21的博客 4754

Vue源码解析:MVVM框架的实现细节

MVVM(Model-View-ViewModel)模式是一种设计模式,用于将用户界面(View)与数据模型(Model)分离,通过ViewModel(视图模型)作为中介来实现两者的双向数据绑定。MVVM旨在简化视图和业务逻辑之间的代码耦合,提高代码的可维护性和可测试性。

weixin_42576804的博客 898

基于OpenSSL的CA建立及证书签发(签发多域名/IP)

自签SSL证书(多域名/IP) 本文基于以下环境: 内核信息:Linux zabbix 3.10.0-957.el7.x86_64 #1 SMP Thu Nov 8 23:39:32 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux 系统版本:CentOS Linux release 7.6.1810 (Core) OpenSSL版本:OpenSSL 1.0.2k-fips 26 Jan 2017 【前言】 在基于OpenSSL的CA建立及证书签发一文中(后面统.

hobby云说 4647

System.arraycopy()

方法是 Java 提供的一个高效的数组复制工具,它可以快速地将一个数组的指定区域复制到另一个数组的指定位置。通过本文的介绍,你应该已经掌握了方法的基本语法、使用方法、性能优势以及一些注意事项。在实际应用中,可以灵活运用方法来提高数组复制的效率。

liu174的博客 3180

【SerDes】美信GMSL2-CSI调试心得

美信GMSL2加解串器芯片家族广泛应用于车载和工业视觉系统。文章介绍了MAX96717/MAX96717F加串器和MAX96724/MAX96724R解串器的关键特性,包括支持MIPI CSI-2接口、6Gbps传输速率等功能。提供了基于高通平台的demo系统配置和调试步骤,重点阐述了Link Lock、PCLK检测和Video Lock等关键调试环节,并详细说明了像素模式,隧道模式的工作原理及寄存器配置示例。文中还包含通过I2C读写寄存器的具体操作指令,取流freeze可能原因和解决方法。

qq_38089448的博客 2489

手写一套完整的基于Vue的MVVM原理

作为前端面试官我面试必须问一下面试者:描述一下你对MVVM的理解? 接下来,我将从零实现一套完整的基于Vue的MVVM,提供给来年“金三银四”跳槽高峰期的小伙伴们阅读也详细梳理一下自己对MVVM的理解。 MVVM是什么 在了解MVVM之前,我们来对MVC说明一下。MVC架构起初以及现在一直存在于后端。MVC分别代表后台的三层,M代表模型层、V代表视图层、C代表控制器层,这三层架构完全可以满足于绝...

qq_40588413的博客 558

cocos2dx中DrawNode画线锯齿和非凸多边形填充

记录一下在cocos2dx中使用DrawNode所遇到的2个问题 1.画线锯齿问题 ios版本下,在AppController.mm中设置CCEAGLView的multiSampling和numberOfSamples。如下图 CCEAGLView *eaglView = [CCEAGLView viewWithFrame: [window bounds]

hello-world 5414

小米万兆路由器里的Docker安装可道云(Kodexplorer)私有网盘

小米2022年12月份发布了万兆路由器,里面可以使用Docker。 今天尝试在小米的万兆路由器里安装[可道云](http://kodcloud.com/)(Kodexplorer)私有网盘。

engchina的专栏 1万+

用vue实现一个相册网页_基于 Vue 实现一个简历 MVVM

授权转载,作者:子弈,来自掘金社区。如果你对于MVVM的形成不是特别清晰,则可以先阅读以下部分。了解MV*的演变历史以及特性(可跳过)了解观察者模式(可跳过)本文可以帮助你了解什么?了解Vue的运行机制参考Vue的运行机制,将观察者模式改用中介者模式实现一个简易的MVVMMVVM的实现演示MVVM的流程设计中介者模式的实现数据劫持的实现数据双向绑定的实现简易视图指令的编译过程的实现Vie...

weixin_42659196的博客 1560

Win10系统下AppScan 10.0.0安装破解全流程(附百度网盘资源)

本文详细介绍了在Windows 10系统上安装和配置AppScan 10.0.0进行Web漏洞扫描的完整流程。内容涵盖环境准备、分步安装、核心文件部署、许可证配置及首次扫描实践,旨在帮助开发人员和安全爱好者快速搭建专业的Web应用安全扫描环境,并提升扫描效率。

u8v9w0x1y的博客 366

mvvm原理解读

mvvm原理解读 个人对mvvm原理的一点理解,文章包括了mvvm中涉及的基本知识,还有mvvm实现步骤和代码注释,希望能对大家有所帮助,有问题的还请多多指教 源码中用到的知识点 一,声明一个类 es5 构造函数 funtion Watcher(){•••} es6 class class Compile{} 二,原型链 继承 三,Object.defineProperty(obj, descriptor) 作用: 定义对象某属性的描述对象,并返回 该对象 属性的Descriptor: descrip

wwerdd的博客 485

SAP COPA实战:手把手教你用CMOD增强搞定自定义特性派生(以客户字段为例)

本文详细介绍了在SAP COPA模块中通过CMOD增强实现自定义特性派生的全流程,以客户字段为例,展示了从特性设计、SPRO配置到特性派生逻辑实现的完整步骤。重点讲解了如何利用CMOD增强技术解决业务需求,提升获利能力段分析的灵活性和准确性。

weixin_30275415的博客 429

ESP32-C3入门教程 环境篇②——Windows开发环境准备

文章目录一、注意事项二、下载ESP-IDF工具安装器三、安装内容四、详细安装步骤报错处理 一、注意事项 请注意 ESP-IDF 和 ESP-IDF 工具的安装路径不能超过 90 个字符,安装路径过长可能会导致构建失败。 Python 或 ESP-IDF 的安装路径中一定不能包含空格或括号。 除非操作系统配置为支持 Unicode UTF-8,否则 Python 或 ESP-IDF 的安装路径中也不能包括特殊字符(非 ASCII 码字符、中文字符) 二、下载ESP-IDF工具安装器 ESP-IDF Win

5243

Android中MVC、MVP和MVVM及实例

三种软件架构的框架(设计的典范)各自有什么特点?有什么区别? 一、MVC 1.组成及作用 Model:模型(提供并处理数据) View:视图(渲染界面,数据可视化) Controller:控制器(MV的连接器,负责用户交互,控制程序流程和业务逻辑的处理) 2.工作原理 当用户触发事件的时候,view层发送指令到controller层,接着controller去通知model层更新数据,model层更新完数据以后直接显示在view层上 #mermaid-svg-Lh6ahpnMcP8l7v3w .labe

czssltt的博客 448

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Android之练习MVVM+DataBinding框架模式

最近简单学习了MVVM框架,记录一下。 结果演示: 分析其功能在不同框架下的构成: 无框架 ...

augfun的博客 527

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

MVVM框架VUE实现原理

点击打开链接

github_38524608的博客 1443

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

c# mvvm模式获取当前窗口_基于 Vue 实现一个简历 MVVM

授权转载,作者:子弈,来自掘金社区。如果你对于MVVM的形成不是特别清晰,则可以先阅读以下部分。了解MV*的演变历史以及特性(可跳过)了解观察者模式(可跳过)本文可以帮助你了解什么?了解Vue的运行机制参考Vue的运行机制,将观察者模式改用中介者模式实现一个简易的MVVMMVVM的实现演示MVVM的流程设计中介者模式的实现数据劫持的实现数据双向绑定的实现简易视图指令的编译过程的实现Vie...

weixin_39689700的博客 553

MVVM开发模式简单实例MVVM Demo

本文主要是翻译Rachel Lim的一篇有关MVVM模式介绍的博文A Simple MVVMExample 并具体给出了一个简单的Demo(原文是以WPF开发的,对于我自己添加或修改的一部分会用红色标注) 现在开始: 在我看来,如果你使用的是WPF或Sliverlight来开发程序就应该使用MVVM设计模式。它是你的代码清晰明了并易于维护。 可问题是网上有很多有关MVVM...

anxuexie4674的博客 252

java mvc mvp mvvm_谈谈对MVC、MVP和MVVM的理解

刚出来工作的时候维护过一个老系统,该系统是用微软的ASP(Active Server Pages 动态服务器页面)写的。每一个页面都是一个ASP文件,每一个一个ASP文件中又同时包含了HTML、CSS、Java Script、VB Script代码,把这么多代码糅合在一起,可以想象一下如果一个这样的页面包含复杂的业务逻辑,每当要对系统进行维护或增加新功能的时候那个酸爽~像上面这种把界面、业务逻辑和...

weixin_39966225的博客 180

MVVM 原理重难点

说实在的我们大家在开发中确实用的不多,多数是修改内部特性,不过就是定义对象上的属性和值么?干嘛搞的这么费劲(纯属个人想法)But在实现框架or库的时候却发挥了大用场了,这个就不多说了,只不过轻舟一片而已,还没到写库的实力知其然要知其所以然,来看看如何使用let song = '发如雪';obj.singer = '周杰伦';// 1. value: '七里香',configurable: true, // 2. 可以配置对象,删除属性。......

油墨香^-^的博客 353

android mvvm框架搭建_选择恐惧症的福音!教你认清MVC,MVP和MVVM

相信大家对MVC,MVP和MVVM都不陌生,作为三个最耳熟能详的Android框架,它们的应用可以是非常广泛的,但是对于一些新手来说,可能对于区分它们三个都有困难,更别说在实际的项目中应用了,有些时候想用MVP的,代码写着写着就变成了MVC,久而久之就对它们三个的选择产生了恐惧感,如果你也是这样的人群,那么这篇文章可能会对你有很大的帮助,希望大家看完都会有收获吧!文章重点:(1)了解并区分MVC,...

weixin_39968861的博客 83

2、vue的底层原理(MVVM)

MVVM:model,view, viewmodel(也就是将我们的每个html页面拆分为了三个部分)Model:当前页面渲染时所需要依赖的数据源View: 当前页面所渲染的DOM结构(页面的结构)ViewMoel:是Vue 的实例(MVVM的核心) 其中我们的模型更加形象:view《-》viewModel《-》model

分享JAVA/ Python/ Ts学习路上的一些笔记,不足之处多多见谅。 312
上一篇: Vue-Router路由使用详解(二)
下一篇: unable to access android sdk add-on list 根本问题解决方案
小于___
博客等级 码龄12年 14粉丝 42原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小于___

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值