Riot.js的版本是1.0.4。官网地址:https://muut.com/riotjs
整个Riot.js只有3个可以在外面调用的函数接口,分别是observable、render、route。
observable函数只有一个参数el,指向Model,该接口的作用是预处理Model,使得Model具有事件定义和发布功能,返回值是处理后的Model。我们来看源码:
riot.observable = function(el) { var callbacks = {}, slice = [].slice; el.on = function(events, fn) { if (typeof fn === "function") { events.replace(/\S+/g, function(name, pos) { (callbacks[name] = callbacks[name] || []).push(fn); fn.typed = pos > 0; }); } return el; }; el.off = function(events, fn) { if (events === "*") callbacks = {}; else if (fn) { var arr = callbacks[events]; for (var i = 0, cb; (cb = arr && arr[i]); ++i) { if (cb === fn) { arr.splice(i, 1); i--; } } } else { events.replace(/\S+/g, function(name) { callbacks[name] = []; }); } return el; }; // only single event supported el.one = function(name, fn) { if (fn) fn.one = true; return el.on(name, fn); }; el.trigger = function(name) { var args = slice.call(arguments, 1), fns = callbacks[name] || []; for (var i = 0, fn; (fn = fns[i]); ++i) { if (!fn.busy) { fn.busy = true; fn.apply(el, fn.typed ? [name].concat(args) : args); if (fn.one) { fns.splice(i, 1); i--; } fn.busy = false; } } return el; }; return el; };
render函数顾名思义就是用来呈现html模板的。直接上代码:
var FN = {}, // Precompiled templates (JavaScript functions) template_escape = {"\\": "\\\\", "\n": "\\n", "\r": "\\r", "'": "\\'"}, render_escape = {'&': '&', '"': '"', '<': '<', '>': '>'}; function default_escape_fn(str, key) { return str == null ? '' : (str+'').replace(/[&\"<>]/g, function(char) { return render_escape[char]; }); } riot.render = function(tmpl, data, escape_fn) { if (escape_fn === true) escape_fn = default_escape_fn; tmpl = tmpl || ''; return (FN[tmpl] = FN[tmpl] || new Function("_", "e", "return '" + tmpl.replace(/[\\\n\r']/g, function(char) { return template_escape[char]; }).replace(/{\s*([\w\.]+)\s*}/g, "' + (e?e(_.$1,'$1'):_.$1||(_.$1==null?'':_.$1)) + '") + "'") )(data, escape_fn); };
route函数的参数是一个回调函数,用来在”页面加载“时触发。
这段代码比较有意思。
pops = riot.observable({})
先通过observable函数定义一个”临时model“,用来可以订阅和发布事件(这里主要是页面加载事件)。
// listen if (typeof to === "function") return pops.on("pop", to);
通过route函数,传入回调函数to,定义pops上得”pop“事件,回调函数为to。
function pop(hash) { hash = hash.type ? location.hash : hash; if (hash !== currentHash) pops.trigger("pop", hash); currentHash = hash; } /* Always fire pop event upon page load (normalize behaviour across browsers) */ // standard browsers if (listen) { listen("popstate", pop, false); doc.addEventListener("DOMContentLoaded", pop, false); // IE } else { doc.attachEvent("onreadystatechange", function() { if (doc.readyState === "complete") pop(""); }); }
监听页面的加载事件,然后触发”pop“事件。
下面是整个route函数的相关代码:
/* Cross browser popstate */ (function () { // for browsers only if (typeof window === "undefined") return; var currentHash, pops = riot.observable({}), listen = window.addEventListener, doc = document; function pop(hash) { hash = hash.type ? location.hash : hash; if (hash !== currentHash) pops.trigger("pop", hash); currentHash = hash; } /* Always fire pop event upon page load (normalize behaviour across browsers) */ // standard browsers if (listen) { listen("popstate", pop, false); doc.addEventListener("DOMContentLoaded", pop, false); // IE } else { doc.attachEvent("onreadystatechange", function() { if (doc.readyState === "complete") pop(""); }); } /* Change the browser URL or listen to changes on the URL */ riot.route = function(to) { // listen if (typeof to === "function") return pops.on("pop", to); // fire if (history.pushState) history.pushState(0, 0, to); pop(to); }; })();