本文来自网易云社区

让我们来加点互动

前面学生信息的身高的单位都是默认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. UVa 12265 - Selling Land

    链接: https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem& ...

  2. Windows 使用iCloud日历

    作者:Lumos Night链接:https://www.zhihu.com/question/34287617/answer/97299386来源:知乎著作权归作者所有.商业转载请联系作者获得授权, ...

  3. 关于JWT.NET

    1.JWT的概念: JWT全称是Json Web Token,是一种用于双方之间传递安全信息的简洁的.URL安全的表述性声明规范.JWT作为一个开放的标准( RFC 7519 ),定义了一种简洁的,自 ...

  4. Segmentation fault(Core Dump)

    Segmentation fault 这个提示还是比较常见的,这个提示就是段错误,这是翻译还是十分恰当的. Core Dump 有的时候给我们呈现的翻译很有趣是”吐核“,但是实际上比较贴切的翻译是核心 ...

  5. 【转载】iPhone屏幕尺寸、分辨率及适配

    iPhone屏幕尺寸.分辨率及适配 转载http://m.blog.csdn.net/article/details?id=42174937 1.iPhone尺寸规格 iPhone 整机宽度Width ...

  6. 常见的springmvc、SpringBoot的注解

    springMvc的常用注解 : @Controller :用于标记在一个类上,使用它标记的类就是一个springmcv Controller对象,分发处理器将会扫描使用了该注解 的类的方法,并检测该 ...

  7. BZOJ 3771: Triple(生成函数 FFT)

    Time Limit: 20 Sec  Memory Limit: 64 MBSubmit: 911  Solved: 528[Submit][Status][Discuss] Description ...

  8. 配置SpringBoot方便的切换jar和war

    配置SpringBoot方便的切换jar和war 网上关于如何切换,其实说的很明确,本文主要通过profile进行快速切换已实现在不同场合下,用不同的打包方式. jar到war修改步骤 pom文件修改 ...

  9. Throwable类

    1.Throwable是所有异常的基类(父类),两个子类Error和Exception ①Error:java运行时系统的内部错误或资源耗尽错误,应用程序不应该抛出这种类型的对象,一旦发生这种异常除了 ...

  10. ueditor 富文本编辑器 Uncaught TypeError: Cannot set property 'innerHTML' of undefined问题

    ueditor.addListener("ready", function () { ueditor.setContent(‘内容'); });