Omi框架学习之旅 - 插件机制之omi-router及原理说明
先来看看官网的介绍吧:https://github.com/AlloyTeam/omi/tree/master/plugins/omi-router
其实我推荐直接看官网的介绍。我所写的,主要给个人做的笔记。也许看的get不到点吧。所以强烈看官网
文档:https://alloyteam.github.io/omi/website/docs-cn.html
github: https://github.com/AlloyTeam/omi
好了,该做笔记了。这次主要记录一下omi-router插件,关于路由,我想不用我多说,因为我也没怎么用其做个项目,实在惭愧。
我的个人理解是:
就是在同一个页面做多个页面的活。
代码量可能多了(基本要用模块化了),但是可以共享一些公用的js代码,或者其他html,css。
不同的组件可以根据hash值插入到指定dom中(omi是不同的组件然后实例化一个,然后omi.render一下就好了)。
hash值可以无刷新前进和后退,这样就做到了页面高速渲染不同的内容,无需等待空白新页面.
单页应用的好处可以直接看第一个链接。用过vue,react的我想都很清楚。只是我。。。
先看下demo吧
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>router2</title> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0"> <style type="text/css"> * { margin: 0; padding: 0; } </style> </head> <body> <div id="app"></div> <script type="text/javascript" src="./src/omi11.js"></script> <script> Omi.OmiFinger.init(); class Home extends Omi.Component { constructor(data) { super(data); } render() { return ` <div> Home </div> `; } }; Omi.tag('Home', Home); class About extends Omi.Component { constructor(data) { super(data); } render() { return ` <div> About </div> `; } }; Omi.tag('About', About); class UserList extends Omi.Component { constructor(data) { super(data); } render() { return ` <ul> <li> <a omi-router to="/user/omi/category/html" href="">omi</a> </li> <li> <a omi-router to="/user/dnt/category/css" href="">dnt</a> </li> <li> <a omi-router to="/user/sorrowx/category/js" href="">sorrowx</a> </li> </ul> `; } }; Omi.tag('UserList', UserList); class User extends Omi.Component { constructor(data) { super(data); } beforeRender() { this.data.name = this.$store.$route.params.sName; this.data.category = this.$store.$route.params.sType; var info = this.queryInfo(this.data.name); this.data.age = info.age; this.data.sex = info.sex; } queryInfo(name) { this.mockData = { omi: {age: 1, sex: '女'}, dnt: {age: 12, sex: '男'}, sorrowx: {age: 13, sex: '男'}, }; return this.mockData[name]; } back() { history.back(); } render() { return ` <div> <button omi-finger tap="back">back</button> <ul> <li>name: {{name}}</li> <li>age: {{age}}</li> <li>sex: {{sex}}</li> <li>category: {{category}}</li> </ul> </div> `; } }; Omi.tag('User', User); class App extends Omi.Component { constructor(data) { super(data); } install() { Omi.OmiRouter.init({ routes: [ { path: '/', component: Home }, { path: '/about', component: About }, { path: '/user-list', component: UserList }, { path: '/user/:sName/category/:sType', component: User } ], renderTo: "#view", defaultRoute: '/about', // 默认 root: this }); } style() { return ` ul{ border-bottom: 1px solid #ccc; padding-bottom:5px; } li{ display:inline-block; } ul li a { text-decoration: none; } #view li{ display:block; } #view ul{ border-width: 0px; } `; } render() { return ` <div> <ul> <li> <a omi-router to="/" href="">Home</a> </li> <li> <a omi-router to="/about" href="">About</a> </li> <li> <a omi-router to="/user-list" href="">用戶</a> </li> </ul> <div id="view"></div> </div> `; } }; var app = new App(); Omi.render(app, '#app'); </script> </body> </html>
还是直接运行一把,看下结果吧,
http://wx.indoorun.com/indoorun/wxtest/touch/router3.html#/user-list
在手机上点击,或者浏览器f12进入模拟器查看,因为使用了tap事件。
至于omi怎么实现的?
答:我就大概的说一下其 实现原理吧,快下班了, 源码会贴在下面的。
1. 入口: Omi.OmiRouter.init(option);
option的参数如下: {
routes:[{}, {}, {}...], // {path:'路由映射的hash', component: '组件,需要实例化的组件然后渲染到指定dom'}
renderTo: '#id', // 渲染到哪个dom去啊,对吧
defaultRoute: '默认路由hash', // 打开链接时不同的用户看到的结果一样撒
root: this // 把组件实例顺便传进去
}
2. 当我们点击链接时,我们需要触发了,入口如下
render() { return ` <div> <ul> <li> <a omi-router to="/" href="">Home</a> </li> <li> <a omi-router to="/about" href="">About</a> </li> <li> <a omi-router to="/user-list" href="">用戶</a> </li> </ul> <div id="view"></div> </div> `; }
每个a标签都加了omi-router属性,说明被omi-router插件的一下这个方法注册了事件,代码如下
Omi.extendPlugin('omi-router', function (dom, instance) { // 给Omi.plugins 添加 omi-router 插件 dom.setAttribute('href', 'javascript:void(0)'); dom.addEventListener('click', function () { hashMapping(dom.getAttribute('to')); }, false); });
里面的匿名函数,每当点击有omi-router属性的标签便会触发这个匿名函数。调用了这个关键的函数hashMapping(dom.getAttribute('to'));,代码如下
function hashMapping(to) { // to: 路径地址 routerOption.routes.every(function (route) { // route: 路由对象 var toArr = to.match(route.reg); // to匹配route.reg正则返回匹配到的结果数组 if (toArr) { var pathArr = route.path.match(route.reg); // route.path匹配route.reg正则返回匹配到的结果数组 params = getParams(toArr, pathArr); renderTo = route.renderTo || routerOption.renderTo; // 渲染到哪去(显然每个路由对象也可以有renderTo属性) store = route.store || routerOption.store; // 数据存储 Component = route.component; // route的组件 pushState(to); return false; // 只有匹配到结果后到达这里就跳出函数啦 }; return true; // 否则继续遍历 }); };
to: 路径地址指的是标签中的to属性的值,然后和配置选项中的routes数组,进行对比。主要获取store,Component,params的数值,然后pushState()方法会改变浏览器
中的url地址,当然也给window注册了一个hashchange事件,所以当url改变时,会触发该事件,如下
window.addEventListener('hashchange', function() { // hashchange事件, url改变就触发 hashMapping(window.location.hash.replace('#',''), renderTo); render(); }, false);
这里的render()方法比较重要哦,组件实例化,和渲染都在里面执行,如果已经有了对应的实例,还得从omi的instances删掉,在重新实例化一个,代码如下
function render(){ if(store){ store.$route = { }; store.$route.params = params; }else{ store = { methods:{ install: function() { this.$route = { }; this.$route.params = params; } } }; }; if(preRenderTo === renderTo&&preInstance){ deleteInstance(preInstance); }; var instance = new Component(); // 组件类 Omi.render(instance, renderTo, { store: store }); preInstance = instance; preRenderTo = renderTo; };
从omi的instances移除实例的代码如下:
function deleteInstance(instance) { // 删除存在的实例 for (var key in Omi.instances) { if(Omi.instances.hasOwnProperty(key)){ Omi.instances[key].id = instance.id; delete Omi.instances[key]; instance = null; break; }; }; };
主要代码基本讲完了。
ps:
路由demo实战源码:https://github.com/SorrowX/resume_demo
demo效果: https://sorrowx.github.io/resume_demo/