Mithril.js入门 – 构建单页面应用的秘密武器 – 又一个框架?



Mithril.js入门 – 构建单页面应用的秘密武器 – 又一个框架?

0 2


mithril-getting-started

Mithril 入门

On Github seqs / mithril-getting-started

Mithril.js入门

构建单页面应用的秘密武器

Created by Seqs

又一个框架?

有什么不同?

文件大小

框架 版本 大小 图表 Reactive.js v0.7.2 162KB AngularJS v1.3.15 123KB React v0.13.2 119KB Vue.js v0.11.8 59KB Mithril.js v0.2.0 19KB Riot.js v2.0.7 9KB

为什么文件大小很重要

  • 下载快(对移动端友好)
  • 解析编译快(同样对移动端友好)
  • 代码少、理解简单
  • 代码少、问题少
  • 更多的空间来写自己的代码 :)

API 接口

框架 数量 图表 AngularJS 200+ Ember.js 100+ jQuery 200+ React.js ~50 Mithril.js 20

为什么 API 接口很重要?

  • 学习少
  • 思考少
  • 记忆少
  • 集成更自由
  • 更少技术壁垒

性能

框架 时间 图表 Mithril.js 73ms Vue.js 117ms AngularJS 265ms Backbone 266ms Reactive.js 300ms React 448ms Ember 734ms

http://matt-esch.github.io/mercury-perf/

为什么性能很重要?

  • 种种原因
  • 移动端友好
  • UX

拥抱函数式编程

  • 容易测试
  • 容易扩展
  • 无副作用,Bug少

与React对比

  • @pelonpelon: When I learned React, I learned abstractions that solved problems. When I learn Mithril, I learn more about javascript.
  • @pelonpelon: Mithril will ever have an ecosystem like React. Javascript is its ecosystem.

特性

与 React 类似的虚拟DOM

小巧的路由层

模块化

Ajax 助手方法

组件内聚,易于组合

原生组件和自定义组件融合渲染

属性驱动全局更新,不用关注细节更新

- 仅此而已 -

没有万能类

没有继承树

没有模板语言

没有模块加载器

没有你不需要的杂七杂八的东西

什么时候不该使用?

初学者需要强结构化的框架

有很多第三方代码需要集成

核心 API

m

构建虚拟DOM的便捷方法

m("br") => <br>

m("div", "Hello") => <div>Hello</div>

m("div", {class: "container"}, "Hello")
=> <div class="container">Hello</div>

m("div.container", "Hello")
=> <div class="container">Hello</div>

m.component

加载组件

var MyComponent = {
  controller: function(args) {
    return {greeting: args.message}
  },
  view: function(ctrl) {
    return m("h2", ctrl.greeting)
  }
}

var App = {
  view: function() {
    return m(".app", [
      m("h1", "My App"),

      // nested component
      m.component(MyComponent, {message: "Hello"})
    ])
  }
}

m.mount(document.body, App)

m.mount

将组件渲染到DOM

var Hello = {
  view: function(ctrl, props) {
    return m("b", props.title);
  }
};

m.mount(document.getElementById('root'), Hello);

m.prop

getter/setter API

var myCtrl = function() {
  this.name = m.prop('admaster');

  console.log(this.name); // -> function(){..}

  console.log(this.name()); // -> "admaster"

  // change value
  this.name('openmaster');

  console.log(this.name()); // -> "openmaster"
};

m.withAttr

配合onclick, oninput, onchange等事件使用

var user = {
  model: function(name) {
    this.name = m.prop(name);
  },
  controller: function() {
    return {user: new user.model("John Doe")};
  },
  view: function(controller) {
    m.render("body", [
      m("input", {onchange: m.withAttr("value", controller.user.name), value: controller.user.name()})
    ]);
  }
};

路由 API

m.route(rootElement, defaultRoute, routes)

定义路由

m.route(document.body, "/", {
  "/": Home,
  "/login": Login,
  "/dashboard": Dashboard,
});

变长参数路由

m.route(document.body, "/files/pictures/pic1.jpg", {
  "/files/:file...": gallery
});

m.route.param("file") === "pictures/pic1.jpg"
m.route(document.body, "/blog/2014/01/20/articles", {
  "/blog/:date.../articles": articleList
});

m.route.param("date") === "2014/01/20"

m.route(path)

重定向

m.route("/dashboard");

m.route()

返回当前激活路由

m.route.param

获取路由参数

// a sample component
var Dashboard = {
    controller: function() {
        return {id: m.route.param("userID")}
    },
    view: function(controller) {
        return m("div", controller.id);
    }
}

// setup routes to start w/ the `#` symbol
m.route.mode = "hash";

// define a route
m.route(document.body, "/dashboard/johndoe", {
  "/dashboard/:userID": Dashboard
});

带参数路由

m.route("/grid?sortby=date&dir=desc")

var sortBy = m.route.param("sortby") // "date"
var dir = m.route.param("dir") // "desc"

数据 API

m.request

发起 ajax 请求

var users = m.request({method: "GET", url: "/user"});
var users = m.prop([]); // default value

m.request({method: "GET", url: "/user"}).then(users)

m.deferred

异步流程处理

// standalone usage
var greetAsync = function() {
  var deferred = m.deferred();
  setTimeout(function() {
    deferred.resolve("hello");
  }, 1000);
  return deferred.promise;
};

greetAsync()
  .then(function(value) {return value + " world"})
  .then(function(value) {console.log(value)}); //logs "hello world" after 1 second

m.sync

异步队列处理

var greetAsync = function(delay) {
  var deferred = m.deferred();
  setTimeout(function() {
    deferred.resolve("hello");
  }, delay);
  return deferred.promise;
};

m.sync([
  greetAsync(1000),
  greetAsync(1500)
]).then(function(args) {
  console.log(args); // ["hello", "hello"]
});

渲染 API

m.render

将虚拟DOM渲染到指定HTML element上

var links = [
  {title: "item 1", url: "/item1"}
];

m.render(document.body, [
  m("ul.nav", [
    m("li", links.map(function(link) {
      return m("a", {href: link.url, config: m.route}, link.title)
    })
  ])
]);

m.redraw

重绘

var doStuff = function() {
    var self = this;
    self.text = m.prop();

    setTimeout(function() {
        self.text("hello");

        m.redraw();
    }, 1000);
};

m.startComputation / m.endComputation

绘制计算

var doStuff = function() {
var self = this;
self.text = m.prop();

  m.startComputation(); //call `startComputation` before the asynchronous `setTimeout`

  setTimeout(function() {
    self.text("hello");

    m.endComputation(); //call `endComputation` at the end of the callback
  }, 1000);
};

m.trust

Mithril 默认 HTML 安全过滤的,该方法输出原生 HTML

// assume this content comes from the server
var content = "<h1>Error: invalid user</h1>";

m.render("body", [
  m("div", m.trust(content))
]);

输出

<body>
  <div>
    <h1>Error: invalid user</h1>
  </div>
</body>

视图选择

Vanilla Javascript

m("ul.things", [
  m("li", "iPhone"),
  m("li", "Macbook"),
])

Coffescript

m "ul.things",
  m "li", "iPhone"
  m "li", "Macbook"

React's JSX (via MSX)

<ul class="things">
  <li>iPhone</li>
  <li>Macbook</li>
</ul>

JSX

类似 xml 的语法,用来描述组件树

<div class="x">
  <a href="#">#</a>
  <Hello>1</Hello>
</div>

编译为

{tag: "div", attrs: {class:"x"}, children: [
  {tag: "a", attrs: {href:"#"}, children: ["#"]},
  m.component(Hello, {}, ["1"])
]}

JSX 嵌入变量

可以通过 {变量名} 来将变量的值作为属性值

var x = "http://www.alipay.com";
var y = <a href={x}>Alipay</a>;

PROPS

通过 props 我们可以获取传递给该组件的属性值

var Hello = {
  view: function(ctrl, props) {
    return <b>{props.title}</b>
  }
};

事件

可以通过设置原生 dom 组件的 onEventType 属性来监听 dom 事件,例如 onclick, onmousedown,在加强组件内聚性的同时,避免了传统 html 的全局变量污染

var LikeButton = {
  view: function(ctrl) {
    var text = ctrl.state.liked() ? 'like' : 'haven\'t liked';
    return (
      <p>
        You {text} this.
        <a href="javascript:;"
          onclick={ctrl.handleClick}>Click</a> to toggle.
      </p>
    );
  }
};

config

整合第三方基于DOM的类库,例如jquery.datepicker

var MyView = {
  view: function(ctrl) {
    return (
      <div>
        <input type="text"
          config={function(element, init, context) {
            if (init) { return false; }
            $(element).datepicker();
          }}/>
      </div>
    );
  }
};

组件组合

我们可以像使用原生 DOM 组件一样使用自定义的组件

var A = {
  view: function() {
    return <a href="#">a</a>;
  }
};

var B = {
  view: function() {
    return <i><A /></i>;
  }
};

组件组合 CHILDREN

自定义组件中我们可以通过 children 访问自定义组件的子节点

var NotesList = {
  view: function(ctrl, props, children) {
    return (
      <ol>
      {children.map(function(child) {
        return (<li>{child}</li>);
      })}
      </ol>
    );
  }
};

var ComponentChildren = {
  view: function(ctrl) {
    return (
      <NotesList>
        <span>hello</span>
        <span>world</span>
      </NotesList>
    );
  }
};

module.exports = ComponentChildren;

示例

http://seqs.github.io/mithril-msx-webpack/public/

谢谢!

http://seqs.github.io/mithril-getting-started

Mithril.js入门 构建单页面应用的秘密武器 Created by Seqs