本文来自网易云社区

让我们来加点互动

前面学生信息的身高的单位都是默认m,如果新增一个需求,要求学生的身高的单位可以在mcm之间切换呢?

首先需要一个变量来保存度量单位,因此这里必须用一个新的Model:

const tk = {
    'first-name': 'Jessica',
    'last-name': 'Bre',
    'height': 180,
    'weight': 70,
}
const measurement = 'cm'

为了让tk更方便的被其他模块重用,这里选择增加一个measurement数据源,而不是直接修改tk

在视图部分要增加一个radio单选表单,用来切换身高单位。

const createList = function(kvPairs){
  const createListItem = function (label, content) {
    const li = document.createElement('li')
    const labelSpan = document.createElement('span')
    labelSpan.textContent = label
    const contentSpan = document.createElement('span')
    contentSpan.textContent = content
    li.appendChild(labelSpan)
    li.appendChild(contentSpan)
    return li
  }
  const root = document.createElement('ul')
  kvPairs.forEach(function (x) {
    root.appendChild(createListItem(x.key, x.value))
  })
  return root
}
const createToggle = function (options) {
  const createRadio = function (name, opt){
    const radio = document.createElement('input')
    radio.name = name
    radio.value = opt.value
    radio.type = 'radio'
    radio.textContent = opt.value
    radio.addEventListener('click', opt.onclick)
    radio.checked = opt.checked
    return radio
  }
  const root = document.createElement('form')
  options.opts.forEach(function (x) {
    root.appendChild(createRadio(options.name, x))
    root.appendChild(document.createTextNode(x.value))
  })
  return root
}
const createToggleableList = function(vm){
  const listView = createList(vm.kvPairs)
  const toggle = createToggle(vm.options)
  const root = document.createElement('div')
  root.appendChild(toggle)
  root.appendChild(listView)
  return root
}

接下来是ViewModel部分,createToggleableList函数需要与之前的createList函数不同的参数。因此,对View-Model结构重构是有必要的:

const createVm = function (model) {
  const calcHeight = function (measurement, cms) {
    if (measurement === 'm'){
      return cms / 100 + 'm'
    }else{
      return cms + 'cm'
    }
  }
  const options = {
    name: 'measurement',
    opts: [
      {
        value: 'cm',
        checked: model.measurement === 'cm',
        onclick: () => model.measurement = 'cm'
      },
      {
        value: 'm',
        checked: model.measurement === 'm',
        onclick: () => model.measurement = 'm'
      }
    ]
  }
  const kvPairs = [
    {
      key: 'Name: ',
      value: model.student['first-name'] + ' ' + model.student['last-name']
    },
    {
      key: 'Height: ',
      value: calcHeight(model.measurement, model.student['height'])
    },
    {
      key: 'Weight: ',
      value: model.student['weight'] + 'kg'
    },
    {
      key: 'BMI: ',
      value:  model.student['weight'] / (model.student['height'] * model.student['height'] / 10000)
    }]
  return {kvPairs, options}
}

这里为createToggle添加了ops,并且将ops封装成了一个对象。根据度量单位,使用不同的方式去计算身高。当任何一个radio被点击,数据的度量单位将会改变。

看上去很完美,但是当你点击radio标签的时候,视图不会有任何改变。因为这里还没有为视图做更新算法。有关MVVM如何处理视图更新,那是一个比较大的课题,需要另辟一个博文来讲,由于本文写的是一个精简的MVVM框架,这里就不再赘述,并用最简单的方式实现视图更新:

const smvvm = function (root, {model, view, vm}) {
  let m = {...model}
  let m_old = {}
  setInterval( function (){
    if(!_.isEqual(m, m_old)){
      const rendered = view(vm(m))
      root.innerHTML = ''
      root.appendChild(rendered)
      m_old = {...m}
    }
  },1000)
}
smvvm(document.body, {
      model: {student:tk, measurement}, 
      view: createToggleableList, 
      vm: createVm 
})

上述代码引用了一个外部库lodashisEqual方法来比较数据模型是否有更新。此段代码应用了轮询,每秒都会检测数据是否发生变化,有变化了再更新视图。这是最笨的方法,并且在DOM结构比较复杂时,性能也会受到很大的影响。还是同样的话,本文的主题是一个精简的MVVM框架,因此略去了很多细节性的东西,只把主要的东西提炼出来,以达到更好的理解MVVM模式的目的。

MVVM框架的诞生

以上便是一个简短精简的MVVM风格的学生信息的示例。至此,一个精简的MVVM框架其实已经出来了:

/**
* @param {Node} root
* @param {Object} model
* @param {Function} view
* @param {Function} vm
*/
const smvvm = function (root, {model, view, vm}) {
  let m = {...model}
  let m_old = {}
  setInterval( function (){
    if(!_.isEqual(m, m_old)){
      const rendered = view(vm(m))
      root.innerHTML = ''
      root.appendChild(rendered)
      m_old = {...m}
    }
  },1000)
}

什么?你确定不是在开玩笑?一个只有十行的框架?

请记住: 框架是对如何组织代码和整个项目如何通用运作的抽象。

这并不意味着你应该有一堆代码或混乱的类,尽管企业可用的API列表经常都很可怕的长。但是如果你研读一个框架仓库的核心文件夹,你可能发现它会出乎意料的小(相比于整个项目来说)。其核心代码包含主要工作进程,而其他部分只是帮助开发人员以更加舒适的方式构建应用程序的附件。有兴趣的同学可以去看看cycle.js,这个框架只有124行(包含注释和空格)。

总结

此时用一张图来作为总结再好不过了!

本文来自网易云社区,经作者顾静授权发布。

了解网易云 :
网易云官网:https://www.163yun.com
网易云社区:https://sq.163yun.com/blog
网易云新用户大礼包:https://www.163yun.com/gift

更多网易研发、产品、运营经验分享请访问网易云社区

一个只有十行的精简MVVM框架(下篇)的更多相关文章

  1. 一个只有十行的精简MVVM框架(上篇)

    本文来自网易云社区. 前言 MVVM模式相信做前端的人都不陌生,去网上搜MVVM,会出现一大堆关于MVVM模式的博文,但是这些博文大多都只是用图片和文字来进行抽象的概念讲解,对于刚接触MVVM模式的新 ...

  2. 一个只有十行的精简MVVM框架

    本文来自网易云社区. 前言 MVVM模式相信做前端的人都不陌生,去网上搜MVVM,会出现一大堆关于MVVM模式的博文,但是这些博文大多都只是用图片和文字来进行抽象的概念讲解,对于刚接触MVVM模式的新 ...

  3. ViewModel从未如此清爽 - 轻量级WPF MVVM框架Stylet

    Stylet是我最近发现的一个WPF MVVM框架, 在博客园上搜了一下, 相关的文章基本没有, 所以写了这个入门的文章推荐给大家. Stylet是受Caliburn Micro项目的启发, 所以借鉴 ...

  4. 实现一个类 Vue 的 MVVM 框架

    Vue 一个 MVVM 框架.一个响应式的组件系统,通过把页面抽象成一个个组件来增加复用性.降低复杂性 主要特色就是数据操纵视图变化,一旦数据变化自动更新所有关联组件~ 所以它的一大特性就是一个数据响 ...

  5. 依赖注入[5]: 创建一个简易版的DI框架[下篇]

    为了让读者朋友们能够对.NET Core DI框架的实现原理具有一个深刻而认识,我们采用与之类似的设计构架了一个名为Cat的DI框架.在<依赖注入[4]: 创建一个简易版的DI框架[上篇]> ...

  6. 迷你MVVM框架 avalonjs 学习教程18、一步步做一个todoMVC

    大凡出名的MVC,MVVM框架都有todo例子,我们也搞一下看看avalon是否这么便宜. 我们先从react的todo例子中扒一下HTML与CSS用用. <!doctype html> ...

  7. 如何实现一个简单的MVVM框架

    接触过web开发的同学想必都接触过MVVM,业界著名的MVVM框架就有AngelaJS.今天闲来无事,决定自己实现一个简单的MVVM框架玩一玩.所谓简单,就是仅仅实现一个骨架,仅表其意,不摹其形. 分 ...

  8. 剖析手写Vue,你也可以手写一个MVVM框架

    剖析手写Vue,你也可以手写一个MVVM框架# 邮箱:563995050@qq.com github: https://github.com/xiaoqiuxiong 作者:肖秋雄(eddy) 温馨提 ...

  9. .NET CORE学习笔记系列(2)——依赖注入[5]: 创建一个简易版的DI框架[下篇]

    为了让读者朋友们能够对.NET Core DI框架的实现原理具有一个深刻而认识,我们采用与之类似的设计构架了一个名为Cat的DI框架.在上篇中我们介绍了Cat的基本编程模式,接下来我们就来聊聊Cat的 ...

随机推荐

  1. [USACO07OPEN]吃饭Dining

    嘟嘟嘟 这应该是网络流入门题之一了,跟教辅的组成这道题很像. 把每一只牛看成书,然后对牛拆点,因为每一只牛只要一份,食物和饮料分别看成练习册和答案. #include<cstdio> #i ...

  2. 日期时间JS插件

    首先要引入基本文件:bootstrap的基本js和css文件,因为用到了jquery所以要引入jquery文件 <script src="jquery-1.11.2.min.js&qu ...

  3. 【原创】多字节版本下MFC控件处理字符集的BUG

    工程项目属性: 字符集:多字节 stdafx.h文件中添加: #pragma comment(linker,"/manifestdependency:\"type='win32' ...

  4. 全新释放 | RealSight APM, 让客户的极致数字体验成为可能

    根据专业评测机构 downdetector.com 统计,2018年,Facebook 系统全年宕机 200 次,Youtube 宕机  140 次,Google 宕机 100 次.每次宕机损失至少 ...

  5. C++自定义函数

    函数可以看成是用户为了解决某特定问题而定义的操作.除了在C++语法里自带的函数,有时候我们需要处理某个问题.但C++里并没有这个函数,这时我们就可以自己来定义一个函数实现需要的功能. 比如我们定义个求 ...

  6. mysql5.7.22tar包安装

    mysql5.7.22tar包安装 #卸载系统自带的Mariadb [root@ ~]# rpm -qa|grep mariadb mariadb-libs-5.5.44-2.el7.centos.x ...

  7. 课时53.video标签第二种格式(掌握)

    由于视频数据非常非常的重要,所以五大浏览器厂商都不愿意支持别人都视频格式,所以导致了没有一种视频格式是所有浏览器都支持的,这个时候W3C为了解决这个问题,所以推出了第二种video标签的格式 如何查看 ...

  8. python3爬虫编码问题

    使用爬虫爬取网页经常遇到各种编码问题,因此产生乱码今天折腾了一天,全部总结一遍环境:win10,pycharm,python3.41.首先先来网页编码是utf-8的:以百度首页为例:使用request ...

  9. 【转载】JavaScript导出Excel

    [转载]JavaScript导出Excel 原文地址 如果没有用到前端插件,也没有用到后台poi导出的话,用js导出也是一种方式.亲测可用. /** * 导出excel */ var idTmr; f ...

  10. Linux中文件函数(二)

    一.link.linkat.unlink.unlinkat.remove函数 创建一个指向现有文件的链接的方法是使用link函数或linkat函数.函数的原型为: #include <unist ...