怎样在实战项目中进行mvvm-simple双向绑定

秋敏姑娘_8817

秋敏姑娘_8817

2018-06-07

1539人浏览

原创

这次给大家带来怎样在实战项目中进行mvvm-simple双向绑定,在实战项目中进行mvvm-simple双向绑定的注意事项有哪些,下面就是实战案例,一起来看一下。

mvvm模式解放DOM枷锁

mvvm原理分析

JavaScript在浏览器中操作HTML经历了几个不同阶段

第一阶段 直接用浏览器提供的原生API操作DOM元素

var dom = document.getElementById('id');
dom.innerHTML = 'hello mvvm';

第二阶段 jQuery的出现解决了原生API的复杂性和浏览器间的兼容性等问题,提供了更加简易方便的API

$('#id').text('hello mvvm')

第三阶段 MVC模式使前端可以和后端配合,修改服务端渲染后的页面内容

而随着产品对于用户体验的重视,交互体验越来越重要,仅用jQuery远远不够。 MVVM模型解决了频繁操作的痛点,Model-View-ViewModel模式将数据与视图的同步交由ViewModel完成

jQuery修改节点内容:

<p>name: <span>vist</span>!</p>
<p>age: <span>25</span>.</p>
var name = 'bestvist';
var age = 26;
$('#name').text(name);
$('#age').text(age);

MVVM模式下只需要关注数据结构:

var me = {
  name: 'vist',
  age: 25
}

修改相应属性就好

me.name = 'bestvist';
me.age = 26;

mvvm实现

mvvm实现数据绑定的几种方式:

  1. 发布-订阅模式

  2. 脏值检查

  3. 数据劫持

比较流行的vue采用的就是数据劫持和发布-订阅模式,通过劫持es5提供的Object.defineProperty()中各个属性的get,set方法, 数据更新时触发消息给订阅者,实现数据绑定功能。

Object.defineProperty(obj, prop, descriptor)方法直接在一个对象上定义一个新属性,或者修改一个已经存在的属性,并返回这个对象。 该方法接受3个参数:

  • obj 定义属性的对象。

  • prop 被定义或修改的属性名。

  • descriptor 被定义或修改的属性的描述符。

一般情况通过直接给对象属性赋值来创建属性或者修改对应属性,而使用Object.defineProperty可以修改对象属性的一些额外默认配置。 如:

const obj = {name: 'Tom'};
Object.defineProperty(obj, 'name', {
 get: function(val) {
   return 'Jerry'; 
 }
})
console.log(obj.name);
//输出: Jerry

Object.defineProperty详细解释,请戳这里

mvvm实现的主要流程:

  • 数据代理,访问实例上的属性时直接返回对应data里的属性

  • 数据监听,对实例上的属性监听,如果数据改变通知订阅者更新

  • 指令解析,对每个元素节点进行解析,替换数据并绑定更新函数

  • 链接数据监听和指令解析,保证每个数据的更新,指令解析都可以获取并更新视图

实例化类:

new MVVM({
  el: '#app',
  data() {
    return {
      message: 'hello mvvm'
    }
  }
})

数据代理:

class MVVM {
  constructor(options) {
    this.$options = options || {};
    let data = this._data = this.$options.data();
    // 数据代理 vm.xxx => vm._data.xxx
    Object.keys(data).forEach(key => {
      this._proxyData(key);
    });
    // observe(data, this);
    // this.$compile = new Compile(options.el || document.body, this);
  }
  _proxyData(key) {
    Object.defineProperty(this, key, {
      configurable: true,
      enumerable: true,
      get: () => {
        return this._data[key];
      },
      set: newVal => {
        this._data[key] = newVal;
      }
    });
  }
}

数据监听,劫持实例属性更新

class Observer {
  constructor(data) {
    this.data = data;
    Object.keys(this.data).forEach(key => {
      this.defineReactive(key, this.data[key]);
    })
  }
  // 定义反应
  defineReactive(key, val) {
    let dep = new Dep();
    Object.defineProperty(this.data, key, {
      enumerable: true,
      configurable: false,
      get: () => {
        return val;
      },
      set: newVal => {
        if (val === newVal) {
          return;
        }
        val = newVal;
        // 赋值对象再进行劫持
        observe(val);
        ... // 数据修改通知
      }
    })
  }
}
function observe(val) {
  if (!val || typeof val !== 'object') {
    return;
  }
  return new Observer(val);
}

指令解析部分代码

class Compile {
  constructor(el, vm) {
    this.$vm = vm;
    this.$el = this.isElementNode(el) ? el : document.querySelector(el);
    if (this.$el) {
      this.$fragment = this.node2Fragment(this.$el);
      this.init();
      this.$el.appendChild(this.$fragment);
    }
  }
  init() {
    this.compileElement(this.$fragment);
  }
  node2Fragment(el) {
    let fragment = document.createDocumentFragment(), child;
    // 原生节点拷贝到fragment
    while (child = el.firstChild) {
      // appendChild将元素从dom上移到fragment
      fragment.appendChild(child);
    }
    return fragment;
  }
  compileElement(el) {
    let childNodes = el.childNodes;
    [].slice.call(childNodes).forEach(node => {
      let text = node.textContent;
      let reg = /\{\{(.*)\}\}/;
      if (this.isElementNode(node)) {
        this.compile(node);
      } else if (this.isTextNode(node) && reg.test(text)) {
        this.compileText(node, RegExp.$1);
      }
      if (node.childNodes && node.childNodes.length) {
        this.compileElement(node);
      }
    })
  }
}

其中

while (child = el.firstChild) {
  // appendChild将元素从dom上移到fragment
  fragment.appendChild(child);
}

通过appendChild改变原dom结构特点,逐步把dom元素节点移到fragment中。

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

GenStore
GenStore

一款AI开发辅助工具,主要用于AI对话生成在线商店,一个平台满足所有电商需求,适合需要提升相关任务效率的用户。

下载

怎样对json对象排序并删除相同id数据

怎样操作JS读取xml内容并输出到div内

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
Uvicorn 报错与问题解决教程
Uvicorn 报错与问题解决教程

本专题汇总了Uvicorn服务器常见问题与报错解决的教程合集,涵盖启动失败、端口占用、模块导入错误、事件循环冲突及热重载失效等典型故障的排查思路。同时整理了多进程与Worker配置、SSL/HTTPS证书报错、跨域与代理配置等实战解决方案,助你快速定位并解决Uvicorn运行中的各类问题。

2026.11.02

0

14

Uvicorn使用教程
Uvicorn使用教程

本专题汇总了Uvicorn使用教程与启动运行指南,涵盖其作为Python ASGI服务器的定位与特点,以及命令行启动(uvicorn main:app --reload)、参数配置(host/port/workers)、程序内嵌运行与Gunicorn结合部署等核心用法。同时整理了热重载调试、日志配置与生产环境部署优化等技巧,助你快速掌握Uvicorn的运行与配置。

2026.11.02

0

17

Uvicorn安装配置教程
Uvicorn安装配置教程

本专题汇总了Uvicorn的安装配置教程与部署指南,涵盖通过pip安装Uvicorn、启动FastAPI/Starlette应用、开发模式热重载(--reload)及多进程工作模式配置等核心操作。同时整理了结合Gunicorn的生产部署方案、HTTPS与反向代理配置及性能调优技巧,助你快速完成Uvicorn服务的安装与上线部署。

2026.11.02

0

17

Vite 项目如何打包和部署
Vite 项目如何打包和部署

Vite项目打包部署专题汇集生产构建、产物预览、输出目录、资源路径、Nginx与GitHub Pages部署及常见白屏和路由404问题的已审核文章,帮助读者完成上线流程。

2026.10.28

100

16

Vite 如何搭配 Vue.js
Vite 如何搭配 Vue.js

Vite搭配Vue.js专题汇集创建Vue 3项目、配置开发环境、路径别名、Vue插件、代理、多环境构建与调试的已审核文章,帮助读者完成从初始化到日常开发的配置。

2026.10.28

80

15

Vite 项目目录怎么看
Vite 项目目录怎么看

Vite项目目录专题汇集入口文件、src源码、public静态资源、路径别名、项目根目录和构建输出目录相关的已审核文章,帮助读者理解各文件夹的用途与引用方式。

2026.10.28

240

10

Gradle插件开发入门教程
Gradle插件开发入门教程

本专题介绍Gradle插件开发的基本流程,涵盖插件类编写、自定义任务注册、扩展参数设计和插件应用方法,帮助你理解插件如何组织构建逻辑,减少多个项目之间的重复配置。

2026.10.22

140

11

Gradle Wrapper配置教程
Gradle Wrapper配置教程

本专题介绍Gradle Wrapper的基本作用,涵盖文件组成、生成方法、版本配置和任务调用,帮助你理解项目如何使用指定的Gradle版本,减少不同开发环境带来的构建差异。

2026.10.22

140

10

Golangci-lint检查器使用教程
Golangci-lint检查器使用教程

本专题整理Golangci-lint常用Linters的用途与配置方法,覆盖错误处理、无效代码、安全、复杂度、命名和依赖检查,帮助项目合理选规则。

2026.10.22

100

15

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Vue.js 2.0 从入门到实战
Vue.js 2.0 从入门到实战

共192课时 | 25.6万人学习

vue 3.0全新实战课程-第一季
vue 3.0全新实战课程-第一季

共51课时 | 25万人学习

麦子学院Vue.js视频教程
麦子学院Vue.js视频教程

共30课时 | 9.7万人学习