页面中展示的信息都是存储在服务器中的数据,离开数据的页面就像是一块画板的作用,如何通过数据来描述一个页面,又怎么映射数据变化和页面渲染的关系。

  当然,最直接的方法就是操作节点,页面加载之后获取节点,再将数据填充到获取的节点之上,每当数据修改,就将该数据对应的节点重新填充数据,像修改数据这样的操作只能通过页面中的操作来实现,也就是事件绑定调用ajax,成功时修改节点。这样的方式存在很大的缺陷,如多个类似的区块,几百个节点都是与页面数据相关的,再比如管理应用中数据的管理,这种情况实际应用基本不可行,对于开发维护都是一件很恐怖的操作。采用节点操作的方法来填充数据,并没有将页面数据化。

  既然要通过数据来管理页面,那么就需要将页面的数据结构抽象化,并将数据同步至结构,那么管理的这个区块就应该有自己的结构、数据和操作,这样就很容易联想到使用类的实例,也就是对象来管理页面。我们在constructor构造器中定义数据、页面结构、页面中的挂载点,页面结构使用字符串模板的形式,将数据加载到结构中,并提供render( ) 方法将页面渲染到挂载点。除此之外,页面中的行为定义到hander( )方法中,并且把事件绑定放在hander( )之中。由于所有的页面都包含render( )和hander( )操作,所以定义init( )方法,依次执行render和hander,就可已初步完成页面的加载。当数据发生修改,修改完成后调用init( ) 重新生成新的节点并绑定事件。这样的做法将页面的各功能模块独立出来,并采用集中管理的模式,减少了很多重复的操作。并且数据被抽象成对象的模式,方法被击中在handle( )中,便于维护和管理。由于每次修改数据都要重新渲染,增加浏览器的压力,并且这样的管理模式仍然不够直观,数据的修改和re-render( ) 还是通过每个事件绑定来完成的,那么怎样才能让数据的变化更加智能化呢?

  既然是以对象来描述数据,那么想要知道每次数据的修改是什么时候发生的,那么可以使用set( )方法定义对象的属性,每次数据发生修改,那么就调用处理该数据与视图之间联系的方法,达到修改数据节点的re-render( ),并且数据的获取采用get( ) 可以很轻松的管理数据,实现视图与数据的响应。

  首先定义数据,通过对象管理需要定义一个管理的类:

class Data {
constructor(props) {
this.data = props.data;
this.template = props.template;
this.el = props.el;
}

  具体应用的相关数据通过实例化参数传递实现:

const data = new Data({
data: {
title: "现在数字为:",
count: 1
},
template: `
<h1 data-on="title"></h1>
<h1 data-on="count"></h1>
<input type="number" i-model="count" />
 `,
el: "#app"
})

  其中data是页面中需要的数据,包括标题和数字两个属性,template用于描述页面的结构,标签采用data-自定义属性绑定对应的数据,而表单元素通过i-model来绑定,这样就首先建立数据与视图的关系,el为该模块的挂载点,这就要求html结构中首先有id为app的元素。

  首次数据的加载:

  loadData() {
const _this = this;
return {
// 相关数据的观察列表,记录那些数据需要响应的响应的办法
watchers: {},
// 视图和数据的描述,数据将会加载到对应的节点,并将视图和数据的管理操作封装到观察列表中
subsciribe(key, value) {
// 文本标签的加载
document
.querySelector(_this.el)
.querySelectorAll(`[data-on=${key}]`)
.forEach(item => {
function doLoad(value) {
item.innerHTML = value;
}
doLoad(value);
if (this.watchers[key]) {
this.watchers[key].push(doLoad)
} else {
this.watchers[key] = [doLoad];
}
})
// 表单元素的加载和监察
document
.querySelector(_this.el)
.querySelectorAll(`[i-model=${key}]`)
.forEach(item => {
function doLoad(value) {
item.value = value;
}
doLoad(value);
item.oninput = function () {
_this.data[key] = this.value
};
if (this.watchers[key]) {
this.watchers[key].push(doLoad)
} else {
this.watchers[key] = [doLoad];
}
})
},
// 对于数据修改后的具体操作,将会锁定到观察列表中的内容
emit(key, value) {
this.watchers[key].forEach(doLoad => doLoad(value))
}
}
}

  由于文本节点和表单节点数据的加载方式不同,所以需要分开处理数据。将数据加载到节点,并且将加载的方式加入到watchers对象中,暴露emit( ) 方法实际操作数据和视图,那么什么时候应当重新加载视图,那么肯定是在属性的set( ) 方法中。电泳loadData( )方法将会获得一个完成的描述对象,那么在对其设置定义为响应的方式即可,实例获得的对象交给this.ob(如下)。

setData() {
const _this = this;
for (let key in this.data) {
let value = this.data[key]
Object.defineProperty(this.data, key, {
get() {
return value;
},
set(_value) {
if (value !== _value) {
value = _value;
_this.ob.emit(key, _value);
}
}
})
this.ob.subsciribe(key, value);
}
}

  这样数据和视图的关联性就完整地表现出来,只需要依次执行页面加载、对象描述和属性定义设置即可,再在constructor( )中调用init( ),页面的相响应数据就能够实现了。

  init() {
document
.querySelector(this.el)
.innerHTML = this.template;
this.ob = this.loadData()
this.setData();
}

  这里的loadData( )调用获得数据处理的描述对象并需交给this.ob,保证生成的观察者对象的唯一性。

完整代码:

<!DOCTYPE html>

<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head> <body>
<div id="app"></div>
<script type="text/javascript">
class Data {
constructor(props) {
this.data = props.data;
this.template = props.template;
this.el = props.el;
this.init();
}
init() {
document
.querySelector(this.el)
.innerHTML = this.template;
this.ob = this.loadData()
this.setData();
}
loadData() {
const _this = this;
return {
watchers: {},
subsciribe(key, value) {
document
.querySelector(_this.el)
.querySelectorAll(`[data-on=${key}]`)
.forEach(item => {
function doLoad(value) {
item.innerHTML = value;
}
doLoad(value);
if (this.watchers[key]) {
this.watchers[key].push(doLoad)
} else {
this.watchers[key] = [doLoad];
}
})
document
.querySelector(_this.el)
.querySelectorAll(`[i-model=${key}]`)
.forEach(item => {
function doLoad(value) {
item.value = value;
}
doLoad(value);
item.oninput = function () {
_this.data[key] = this.value
};
if (this.watchers[key]) {
this.watchers[key].push(doLoad)
} else {
this.watchers[key] = [doLoad];
}
})
},
emit(key, value) {
this.watchers[key].forEach(doLoad => doLoad(value))
}
}
}
setData() {
const _this = this;
for (let key in this.data) {
let value = this.data[key]
Object.defineProperty(this.data, key, {
get() {
return value;
},
set(_value) {
if (value !== _value) {
value = _value;
_this.ob.emit(key, _value);
}
}
})
this.ob.subsciribe(key, value);
}
}
}
const data = new Data({
data: {
title: "this is a title",
count: 1
},
template: `
<h1 data-on="title"></h1>
<h1 data-on="title"></h1>
<h1 data-on="count"></h1>
<input type="number" i-model="count" />
  `,
el: "#app"
})
</script>
</body>
</html>

JS响应数据的更多相关文章

  1. jQuery-1.9.1源码分析系列(十六)ajax——响应数据处理和api整理

    ajax在得到请求响应后主要会做两个处理:获取响应数据和使用类型转化器转化数据 a.获取响应数据 获取响应数据是调用ajaxHandleResponses函数来处理. ajaxHandleRespon ...

  2. MetricGraphics.js – 时间序列数据的可视化

    MetricsGraphics.js 是建立在D3的基础上,被用于可视化和布局的时间序列数据进行了优化.它提供以产生一个原则性的,一致的和响应式的方式的图形常见类型的简单方法.该库目前支持折线图,散点 ...

  3. Flask04 后台获取请求数据、视图函数返回类型、前台接受响应数据

    1 后台获取请求数据 1.1 提出问题 前台发送请求的方式有哪些 后台如何获取这些请求的参数 1.2 前台发送请求的方式 GET.POST.AJAX 点睛:如果不指定请求方式,浏览器默认使用GET请求 ...

  4. vue.js响应式原理解析与实现

    vue.js响应式原理解析与实现 从很久之前就已经接触过了angularjs了,当时就已经了解到,angularjs是通过脏检查来实现数据监测以及页面更新渲染.之后,再接触了vue.js,当时也一度很 ...

  5. server的响应数据

    前言 如果使用了MVC框架(比方,struts2). server的响应数据.分3种情况 1.响应数据是结果页面 2.响应数据是json格式的数据 3.响应数据是json格式的数据,然后再又一次发出一 ...

  6. 深入解析vue.js响应式原理与实现

    vue.js响应式原理解析与实现.angularjs是通过脏检查来实现数据监测以及页面更新渲染.之后,再接触了vue.js,当时也一度很好奇vue.js是如何监测数据更新并且重新渲染页面.vue.js ...

  7. 使用Typescript重构axios(六)——实现基础功能:获取响应数据

    0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...

  8. 使用Typescript重构axios(十三)——让响应数据支持泛型

    0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...

  9. 使用Typescript重构axios(十六)——请求和响应数据配置化

    0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...

随机推荐

  1. java容易混淆的15个知识点

    java知识点不多,但是有一些经常会被我们忽略 1.java是强类型的语言,数组也是对象,一旦确定数组的类型,里面就只能存放一个类型的数据. 2.新建的对象都被存放到堆上,如果没有引用,会很快垃圾回收 ...

  2. python 去掉 pyc

    python 去掉 .pyc 在开发的机器上(Ubuntu),python自动生成的pyc文件太影响心情,把下面的语句添加到 /etc/profile中: # do not produce .pyc ...

  3. 手把手带你做一个超炫酷loading成功动画view Android自定义view

    写在前面: 本篇可能是手把手自定义view系列最后一篇了,实际上我也是一周前才开始真正接触自定义view,通过这一周的练习,基本上已经熟练自定义view,能够应对一般的view需要,那么就以本篇来结尾 ...

  4. R-- Dplyr包

    Dplyr 包应用 1. 筛选 filter() 按照给定的逻辑判断选择出合适的数据子集 fliter(data,year==2015,month==1) 支持对同一对象的任意条件组合 fliter( ...

  5. UE4 Hello World 创建第一个UE4工程

    首先先熟悉几个UE4常用的类 AGameMode(控制整个项目的逻辑) The GameMode defines the game being played. It governs thegame r ...

  6. 【Visual C++】游戏编程学习笔记之五:单一背景滚动

    本系列文章由@二货梦想家张程 所写,转载请注明出处. 本文章链接:http://blog.csdn.net/terence1212/article/details/44224963 作者:ZeeCod ...

  7. Android实现RecyclerView侧滑删除和长按拖拽-ItemTouchHelper

    RecyclerView这个被誉为ListView和GirdView的替代品,它的用法在之前的一篇博文中就已经讲过了,今天我们就来实现RecyclerView的侧滑删除和长按拖拽功能,实现这两个功能我 ...

  8. OpenCV 实现分水岭算法

    种子点的标记没有太搞懂,这个算法的速度还是很快的 // watershed_test20140801.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h&q ...

  9. OAF更改动态头行

    选择头信息,动态刷新行信息.本文将详细介绍该种需求的做法. 本例沿用<OAF-头行结构>的am与vo,所以在进行本例之前,请先完成<OAF-头行结构> 一.创建页面 在test ...

  10. Unity Editor 编写unity插件类

    在unity写了一个编辑类,基于iTweenpath插件,为了更方便的操作iTweenpath,顺便练习UnityEditor的操作,写了一个CreateiTweenPath,放在Editor文件夹中 ...