对于前端 MVC 来说, DOM API 是否是过度封装了?

题叶
  • 17.3k

没从 C++/Java 学起, 对 MVC 和面向对象的理解还是后来慢慢积累起来的
相对我觉得脚本语言各种范式各种灵活, 只是缺少面向对象组织代码的整齐呆板
而且从前使用 DOM API 时候就觉得 DOM 就像一种数据类型,
JS 操作数据同时, 也在更改界面. 通过直接访问界面, 也能读取数据, 很强大对吧

但我接触 Backbone 一段时间, 困惑也觉得越来越多
而从前灵活解决前端问题的思路, 随着 MVC 提出的分离有了各种阻碍, 或者不方便直接 JS DOM 操作吧
MVC 在 Backbone 里是 Model 和 View-Controller 两者分离, 另加上 Collection.
每次 Model(Collection) 发生改变, View 重新渲染一次, 然后搞定
或者 DOM 上的事件从 Controller 传到 Model 里进行一次改变, View 跟着改变好
这样其实 jQuery 丰富的 DOM 操作就显得无所用处

如果在 Backbone 的应用里加上大量的 DOM 操作, View 刷新时又变得尴尬
为了性能, JS 在进行 DOM 操作时会讲操作全在 HTML 字符串里存储好, 一次写入
可在 Backbone 里, 如果小的 item 也是 Model, 那么 render 也是分开了自己做的
我理解, 这样两者就不能够很好地兼容, 至少有一个方面要打折扣了

另一个功能是实时保存和更新, 纯的 DOM 操作, JS 监听 DOM, 在背后保存数据很简单
可在 MVC 里 Model 被监听, 每次更新操作就会导致界面被重新绘制, 也显得很怪

回复
阅读 7k
3 个回答
willerce
  • 2k

最早的时候,业务很简单,我们写样的代码。

  $.ajax({
    url: 'http://ooxx.cn/getUser'
    success: function(user){
      //进行DOM操作
      $('#name').val(user.name);
      $('#email').val(user.email);
    }
  })

后来,user 的字段越来越多。

  $.ajax({
    url: 'http://ooxx.cn/getUser'
    success: function(user){
      //进行DOM操作
      $('#name').val(user.name);
      $('#email').val(user.email);
      $('#address').val(user.address);
      $('#phone').val(user.phone);
      $('#blog').val(user.blog);
      //可能还有更多的字段,并且DOM操作不一定这么简单只是值的修改
      //可能还要负责整个视图是否出现:比如 isVIP 为 false 时,出现付费引导等。
    }
  })

于是我们考虑将对象与模版进行绑定,进一步抽离

  $.ajax({
    url: 'http://ooxx.cn/getUser'
    success: function(user){
      rener(user);
    }
  })
  
  var render = function(obj){
      //进行DOM操作
      $('#name').val(user.name);
      $('#email').val(user.email);
      $('#address').val(user.address);
      $('#phone').val(user.phone);
      $('#blog').val(user.blog);
  }
  

只是简单的代码封装,整个结构就清晰很多。但紧接着,我们开始使用 websocket 来做界面的即时更新,于是代码再次修改。

这是一个用户列表

  
  socket = new WebSocket("ws://192.168.1.71:7070");
  socket.onmessage = function(data) {
      var obj = JSON.parse(data)
      if(obj.action === "update"){
          render(userList);
      }
  }
  
  var render = function(obj){
      //进行DOM操作
      $('#name').val(user.name);
      $('#email').val(user.email);
      $('#address').val(user.address);
      $('#phone').val(user.phone);
      $('#blog').val(user.blog);
  }
  

很棒,接着我们想像到这个 user 将会有非常多的 function,如果到处是 $("") 这样的方式进行DOM操作,如果有一天,HTML 上的 id/class 要稍变一下,那改起来就痛苦了。
把用户这个做成 Model,加上 render(), showPhone(), sendEmail() 这样各种的方法。将大大增加代码的组织能力。

-------------------------

其实上面写了这么多,只是想说明,需求在不断增加的时候,项目不断变大的时候,原有的代码组织无法满足需要,需要站在各高的层次,将项目中常用的方法进行抽离封装。于是,便有了框架。

backbone 做得事很专注,充分发挥MVC的组织分层能力,继续把DOM交给 jQuery 去做。

而更新的前端MVC框架,如:angularjs, emberjs 等,将 model 与 template(HTML) 进行绑定,直接帮助你渲染,让我们不用再关心渲染、DOM处理,而是把注意力集中在业务及交互处理上。

既然是框架,解决的必然是复杂的、复杂的事情。如果只是简单的几个DOM处理,用框架肯定是太重太浪费了。

Jin
  • 406

Backbone 基本没有关于Dom 渲染的功能. 所以从jQuery角度讲, 数据ajax过来后直接操作Dom 感觉很直观,没必要在重新定义一个model.

但是如果同样的数据在同一个页面多处使用呢? 同一个数据在多个页面多出使用呢. 作为SPA,所以都在一个页面上, 这样把 ajax过来的数据抽象成一个model, 就比较方便.

但Backbone的view 没有加入bind的功能, 所以感觉上还需要在view上监听model变化再render刷新整个view. 建议使用 一个插件 modelbinder. 这样数据变化后就不需要刷新整个view而是只更新某个属性对应的小dom, 一般就是span标签或某个input标签,这样大大简化程序复杂度,代码方便,而且性能更好.

backbone,由于没有C, 整个都是view里面, 而view的设计有没有集成模板和dom渲染,显得有些不太好用.

所以其他的框架 例如angular js, knockout 都提出ViewModel的概念 就是数据绑定的概念,只要model变化了数据, view会自动变化,非常方便. 而backbone并没有提供这些功能,而只是一个代码组织的框架. angular 里面基本不在需要jQuery操作DOM, 就是数据和View是一体的. 数据和view是绑定的, 没有在单独操作dom的概念.

宣传栏