• JS响应数据


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

      当然,最直接的方法就是操作节点,页面加载之后获取节点,再将数据填充到获取的节点之上,每当数据修改,就将该数据对应的节点重新填充数据,像修改数据这样的操作只能通过页面中的操作来实现,也就是事件绑定调用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>
  • 相关阅读:
    SparkR初体验2.0
    R语言数据集合并、数据增减、不等长合并
    采坑复盘:logging日志能用封装后的函数来打日志,发现filename一直显示封装logging函数的方法所在的文件名
    flask实战-个人博客-编写博客前台
    flask实战-个人博客-电子邮件支持
    linux虚拟机获取不到ip的解决方法 --
    flask实战-个人博客-视图函数
    flask实战-个人博客-表单
    flask实战-个人博客-模板 --
    flask实战-个人博客-数据库-生成虚拟数据 --
  • 原文地址:https://www.cnblogs.com/zzmiaow/p/8372637.html
Copyright © 2020-2023  润新知