vlambda博客
学习文章列表

现代化 JavaScript 框架 Mithril 的简单介绍及用法

本文转载于 SegmentFault 社区

作者:我可能神经病啊



公司的一个项目的迭代,在前期研究中,考虑开发及生产环境的一些情况和要求,大佬们选择了 Mithril 这个框架,前端方面由我负责。在这之前我是没听说过 Mithril的,就花了点时间去学习,发现关于这个框架的内容还是比较少的。这篇文章也是简单整理了一下一些基础的内容,算是学习笔记。



 

一、Mithril 介绍


1. 是什么


mithril 是一个小巧的、mvc 模式的、用于构建单页面应用的现代化 JavaScript 框架。

最新版本
(2.0.4) 支持 IE11 以上的浏览器,v1版本支持 IE9 以上。

2. 与其他框架的对比


首先,从框架的体积大小来说, Mithril 与其他框架相比都要小的多。这里贴一个 Mithril 官网的对比图:



对比现在流行的三大框架, Mithril 与 React 比较类似,两者都是由虚拟 DOM 通过 diff 算法渲染视图的,不过不一样的是 React 只是单纯的视图库,在实际项目中需要依赖其他第三方库,而 Mithril 虽然麻雀虽小,但是五脏俱全,它内置了例如路由功能和 XHR 工具,语法上,Mithril 也支持 JSX 的写法。

至于 Mithril 和 Angular 以及 Vue 之间,差不多可以类比 React 与这两者之间的区别。

3. 特点


•  轻量级  

压缩后体积小,无依赖
api 少,上手简单

•  快速

提供了一个模板引擎与一个虚拟的 DOM diff 实现,实现高性能渲染
自动重绘

•  mvc

层次化的 mvc 组件,耦合性低可维护性高
 


 

二、用法示例


1. 安装


CDN

 
   
   
 
<!-- Development: whichever you prefer -->
<script src="https://unpkg.com/mithril/mithril.js"></script>
<script src="https://cdn.jsdelivr.net/npm/mithril/mithril.js"></script>

<!-- Production: whichever you prefer -->
<script src="https://unpkg.com/mithril/mithril.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/mithril/mithril.min.js"></script>
npm

  
    
    
  
npm install mithril --save
 
   
   
 
如果项目是使用 ts 开发的,需要安装类型声明文件。

  
    
    
  
npm install @types/mithril --save-dev


2. 基本使用示例


  
    
    
  
const vnode = m('div.container', [
m("li", "hello"),
m("li", "mithril")
]);
m.render(document.body, vnode);
//
// 上面代码会生成如下的 HTML 结构
// <div class="container">
//   <span>hello</span>
//   <span>mithril</span>
// </div>

3. JSX


Mithril 也支持 JSX 语法:
 
  
    
    
  
const MyComponent = {
  view(vnode) {
    return (
      <div>Hello Mithril</div>
    )
  }
}
m.mount(document.body, <MyComponent />);
要注意的是,使用 JSX 必须通过 Babel 对代码进行转换。

在 Webpack 中使用 Babel

如果项目使用了 Webpack 的话,直接在 Webpac k的配置中添加 Babel 配置。
首先创建 .babelrc 文件

  
    
    
  
{
    "presets": ["@babel/preset-env"],
    "plugins": [
      ["@babel/plugin-transform-react-jsx", {
          "pragma": "m",
          "pragmaFrag": "'['"
      }]
    ]
  }
然后安装 Babel 相关依赖
 
   
   
 

  
    
    
  
npm install @babel/core babel-loader @babel/preset-env @babel/plugin-transform-react-jsx --save-dev
最后在 Webpack 的配置文件中添加 Babel 相关的配置信息
  
    
    
  
const path = require('path');

  module.exports = {
    entry: './src/index.js',
    output: {
      path: path.resolve(__dirname, './bin'),
      filename: 'app.js',
    },
    module: {
      rules: [{
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      }]
    }
  }

独立安装 Babel

一样先要创建 .babelrc 文件,文件具体内容和上面 Webpack 中的一样。

然后安装依赖

 
   
   
 
npm install @babel/core @babel/cli @babel/preset-env @babel/plugin-transform-react-jsx --save-dev
JSX 与 Hyperscript

JSX 是一种 JavaScript 的语法扩展,能让我们可以在 JS 中写 html。 在React 中,JSX 只是 React.createElement() 函数的语法糖。

同样的,在 Mithril 中, JSX 最终也会被转换成 Hyperscript 语法,文章中的例子一般都是用的 Hyperscript (m("span", "hello mithril"))。

上面 JSX 代码转换后:

现代化 JavaScript 框架 Mithril 的简单介绍及用法

对于 JSX 语法,熟悉 React 的就完全不是问题了,对于前端来说,使用 JSX 来写 DOM 会比较舒适,可以在 js 中使用 HTML 。但是 JSX 并不能直接运行,需要编译成 hyperscript 之后才能运行。而 hyperscript 就是标准的 javascript 语法,并不需要额外的编译过程。这两者之间各有各的好处,至于在实际开发使用哪种方式就看个人选择了。


  

三、核心概念


1. vnodes


虚拟 DOM 树是描述 DOM 树的javaScript数据结构,它由嵌套的虚拟 DOM 节点 (也称为 vnode ) 组成。Mithril 的虚拟 DOM 引擎使用 vnode 树来生成 DOM 树。

虚拟 DOM 节点 (vnode) 是一个 JavaScript 对象,具有以下属性:

现代化 JavaScript 框架 Mithril 的简单介绍及用法
vnode 的 tag 属性决定了它的类型。有 5 种 vnode 类型:

现代化 JavaScript 框架 Mithril 的简单介绍及用法

2. 组件

 
组件是对视图的一部分进行封装,以方便组织代码和重用。任何具有 view 方法的 JavaScript 对象都是 Mithril 组件。

 
   
   
 
const component = { 
  view() { 
    return m('div''mithril component')
  }
}
生命周期

组件和虚拟 DOM 节点都有生命周期方法,包括 oninit 、 oncreate 、 onupdate 、 onbeforeremove 、 onremove 和 onbeforeupdate。

状态
  
vnode 有状态属性,组件的状态有3种方式可以进行访问:初始化时、通过 vnode.state 、通过组件方法中的 this 关键字。
 
组件传值
 
把一个 attrs 对象传入到 m() 函数的第二个参数,即可把参数传入到组件实例中,然后在组件的视图和生命周期方法中可以通过 vnode.attrs 来访问数据:

 
   
   
 
const component = {
view(vnode) {
return m("div""Hello, " + vnode.attrs.name);
}
};
m(component, { name: 'Component' });

3. 生命周期

 
所有生命周期方法都使用 vnode 作为第一个参数,并把 this 关键字绑定到了 vnode.state。

现代化 JavaScript 框架 Mithril 的简单介绍及用法


4. 自动重绘


Mithril 的自动重绘系统会在数据层的数据改变后更新 DOM 。

需要注意的是调用 m.mount() 或 m.route() 才会开启自动重绘,通过 m.render() 渲染的 vnode 不会进行自动重绘。

下面 3 种方法可以触发自动重绘:

  • DOM 事件之后触发
  • 调用请求 (m.request()) 方法之后
  •  改变路由时 (m.route())

如果不想进行重绘 可以用 e.redraw = false 禁用自动重绘:

 
   
   
 
const componemt = {
  text: 'initial state',
  view() {
    return m('div', [
      m('h1'`${this.text}`),
      m('button', {onclick: e => {
        this.text = 'state change';
e.redraw = false;    // 禁用重绘
      }}, 'click')
    ])
  }
}
m.mount(document.body, componemt);
 
一般在 setTimeout 、 setInterval 、 requestAnimationFrame 、 Promise 和第三方库的事件处理方法后并不会触发自动重绘,不过可以通过 m.redraw() 方法来手动重绘:

  
    
    
  
const componemt = {
  text: 'initial state',
  oninit() {
    setTimeout(() => {
      this.text = 'state change';
      m.redraw();    // 手动进行重绘
    }, 1000);
  },
  view() {
    return m('h2'this.text);
  }
}
m.mount(document.body, componemt);

5. keys


Key 是一种允许对 DOM 元素进行重新排序的机制,把列表中的指定数据项映射到各自对应的 DOM 元素。通常,key 属性应该数组中的唯一标识字段,即该字段的值不应产生重复。

有 key 意味着,如果数据数组被打乱,且视图被重新渲染,DOM 元素将按照和以前一致的排序方式进行排序,以便保持正确的焦点和 DOM 状态。

 
   
   
 
const userInputs1 = {
  users: [
    { id: 1, name: 'John' },
    { id: 2, name: 'Mary' },
  ],
  view() {
    return m('div'this.users.map(user => m('input',
      {
        value: user.name,
        onclick: () => {
          setTimeout(() => {
            this.users[0] = this.users.pop();
            m.redraw();
          }, 3000);
        }
      }
    )));
  }
}
const userInputs2 = {
  users: [
    { id: 1, name: 'Andi' },
    { id: 2, name: 'Sam' },
  ],
  view() {
    return m('div'this.users.map(user => m('input',
      {
        key: user.id,
        value: user.name,
        onclick: () => {
          setTimeout(() => {
            this.users[0] = this.users.pop();
            m.redraw();
          }, 3000);
        }
      }
    )));
  }
}

m.mount(document.querySelector('.div-1'), userInputs1);
m.mount(document.querySelector('.div-2'), userInputs2);
 
   
   
 


从上面的示例可以发现,没有 key 的 input 元素在重绘后没有保持获取焦点的状态,而有 key 的 input 元素并不受影响。


  
把 Mithril 基本的内容都简单梳理了一遍,总的来说,这个框架上手简单功能也比较全面。还是挺好用的。目前也是才接触不久,后期有时间考虑再来深入的探究一下。

参考

mithril 官网:
https://mithril.js.org/

mithril 中文介绍(官方最新的版本是 2.0.4,中文文档的版本是 1.1.7*):
http://www.mithriljs.net/

Mithril.js 入门介绍:
https://segmentfault.com/a/1190000003790908



点击左下角阅读原文,欢迎到  SegmentFault 思否社区  和文章作者展开更多互动和交流。

- END -