Omi框架学习之旅 - Hello World 及原理说明
学什么东西都从hello world开始, 我也不知道为啥。
恩,先上demo代码, 然后提出问题, 之后解答问题, 最后源码说明。
hello world - demo:
class Hello extends Omi.Component { // 1. Hello类先继承Component类
constructor(str, data) { // 2. Hello类构造函数的参数和个数随便传,但是必须要有个数据对象data(其实可以是对象或者传undefined,因为其还有第二个参数,不传undefined的话,第二个参数就变成了第一个参数)
super(data); // 3. es6和es5的继承还是有区别的,es6需要先得到父类的this实例,然后子类对其加工。Component父类其实可以接受2个参数的,这里就先接受data数据对象作为第一个参数
this.str = str;
console.log(this);
} style() { // 4. style方法其实是Hello类重写了Component类的style方法(其实父类的style方法也只是空函数)
return `
h1 {
color: red;
}
`;
} clickHandle(t, evt) { // 5. Hello类的原型方法和Component类没关系哈
console.log(t, evt);
} render() { // 6. render方法也是Hello类重写了Component类的render方法
return `
<h1 onclick="clickHandle(this, event)">aaa{{name}}</h1>
`;
}
}; var hello = new Hello('str', {name: 'hello world'}); // 7. 创建Hello类的实例
Omi.render(hello, '#app'); // 8.让omi渲染(生成html和局部css和把内置事件和对应的实例clickHandle函数关联起来)
先看看omi中文文档的说明:
组件生成的HTML最终会插入到#app中。上面的例子展示了Omi的部分特性:
- data传递: new Hello(data,..)的data可以直接提供给render方法里的模板
- 局部CSS: h1只对render里的h1生效,不会污染外面的h1
- 声明式事件绑定: onclick调用的就是组件内的handleClick,this可以拿到当前的DOM元素,还可以拿到当前的event
- 文档地址:https://alloyteam.github.io/omi/website/docs-cn.html#
接下来说说这个demo的疑问和疑问的说明:
疑问1:
自己写的类一定要继承Omi.Component类吗?
答:废话,想用omi,你还不继承别人的属性和方法,怎么玩下去(当然牛逼的人可以自己写哈)。
注: Component类是核心, 源码在component.js中,然后
Omi.Component = Component; 把Component类挂载到Omi的Component对象上去了。
疑问2:
super()方法中可以传什么数据类型的参数,和参数的个数?
答:Component构造函数可以接收2个参数。
理论上可以有这几种传参的方式:
传1个参数 super()/super(undefined), super({}), super('str'), super(true), super([]), super(null)
传2个参数 super(data, 真值)
先说传1个参数会导致啥吧:
传 true, 'str', [], null, 会使变量isReRendering则为true, {}, undefined/或者不传参数, 变量isReRendering则为false,然而isReRendering变量是if的条件会开了2个分支,
处理不同的逻辑。
传2个参数会怎样啊:
第二个参数可以任意类型的值:因为会和对象{server: false, ignoreStoreData: false}合并
感受下omi的源码:
constructor(data, option) { // data: 数据data, option: 一般为对象
const componentOption = Object.assign({ // 和并option
server: false, // 老版中的server属性
ignoreStoreData: false // 忽略存储数据
}, option);
this._omi_ignoreStoreData = componentOption.ignoreStoreData; // 是否忽略存储数据属性_omi_ignoreStoreData
//re render the server-side rendering html on the client-side
const type = Object.prototype.toString.call(data); // data的数据类型
const isReRendering = type !== '[object Object]' && type !== '[object Undefined]'; // 是否重新渲染
if (isReRendering) {
this.renderTo = typeof data === "string" ? document.querySelector(data) : data
this._hidden = this.renderTo.querySelector('.omi_scoped__hidden_data')
this.id = this._hidden.dataset.omiId
this.data = JSON.parse(this._hidden.value)
} else {
this.data = data || {}; // 给实例添加 data属性
this._omi_server_rendering = componentOption.server; // _omi_server_rendering属性
this.id = this._omi_server_rendering ? (1000000 + Omi.getInstanceId()) : Omi.getInstanceId(); // id属性
};
this.refs = {};
this.children = []; // 每个实例的孩子
this.childrenData = []; // 每个实例孩子的数据
this.HTML = null; // 实例中render返回的字符串
this._addedItems = [];
Omi.instances[this.id] = this; // Omi._instanceId 对应 Component类的每个实例(或者子类)
this.dataFirst = true; this._omi_scoped_attr = Omi.STYLESCOPEDPREFIX + this.id; // 实例的样式局部属性 omi_scoped_ + id
//this.BODY_ELEMENT = document.createElement('body')
this._preCSS = null; // 预设样式
this._omiGroupDataCounter = {};
if (this._omi_server_rendering || isReRendering) {
this.install()
this._render(true)
this._childrenInstalled(this)
this.installed()
}
}
疑问3:
style()方法是干啥的?
答: style方法返回一个css字符串,用来生成局部css的。style方法中可以写各种语句哈。只要返回css字符串即可。
疑问4:
render()方法是干啥的?
答:render方法返回一个html字符串,就是html组合好的标签啦。render方法中可以写各种语句哈。只要返回字符串即可。
疑问5:
那omi是怎么帮我们生成局部css和html插入到指定的dom容器中呢。html内置的事件又是怎样对应起Hello类中clickHandle方法?
答:这一切就得从Omi.render(hello, '#app'); 这个语句开始。
Omi.render = function(component , renderTo , incrementOrOption){ // 实例, 渲染到的dom, xx
component.renderTo = typeof renderTo === "string" ? document.querySelector(renderTo) : renderTo; // 实例的renderTo属性
if (typeof incrementOrOption === 'boolean') {
component._omi_increment = incrementOrOption; // 实例的_omi_increment 属性(老版)
} else if (incrementOrOption) { // 新增
component._omi_increment = incrementOrOption.increment;
component.$store = incrementOrOption.store;
if (component.$store) {
component.$store.instances.push(component);
};
component._omi_autoStoreToData = incrementOrOption.autoStoreToData;
};
component.install(); // Component类的install方法(被实例继承了)
component._render(true); // Component类的_render方法(被实例继承了)
component._childrenInstalled(component);
component.installed();
return component;
}
render方法就omi上的一个静态方法,接收3个参数(实例, 渲染到的dom, xx), xx表示我也不清楚这个变量干啥的,以后肯定会知道的。
方法中的这个语句对于这个demo很重要component._render(true);接下来终点看看这个方法.
这个方法里面对于这个demo最重要的语句就是
this._generateHTMLCSS(); // 生成 html 和 css, 怎么生成html和css呢,看如下代码
_generateHTMLCSS() { // 生成css 和 html
this.CSS = (this.style()|| '').replace(/<\/?style>/g,''); // 处理下style()方法中的字符串样式
if (this.CSS) {
this.CSS = style.scoper(this.CSS, "[" + this._omi_scoped_attr + "]"); // 给css标签搞成 (标签名[omi_scoped_0], [omi_scoped_0] 标签名) 变成属性选择器
if (this.CSS !== this._preCSS && !this._omi_server_rendering) { // 现在的CSS不等于_preCSS 且 _omi_server_rendering 为假值
style.addStyle(this.CSS, this.id); // 在head中添加局部css
this._preCSS = this.CSS; // 本次的css存一下
};
};
let tpl = this.render(); // 用户提供的html字符串
this.HTML = this._scopedAttr(Omi.template(tpl ? tpl : "", this.data), this._omi_scoped_attr).trim(); // 给每个html元素添加omi_scoped_0 = '', 有模板数据的也计算好了. eg: <h1 omi_scoped_0 onclick="clickHandle(this, event)">aaahello world</h1>
if (this._omi_server_rendering) {
this.HTML = '\r\n<style id="'+Omi.STYLEPREFIX+this.id+'">\r\n' + this.CSS + '\r\n</style>\r\n'+this.HTML
this.HTML += '\r\n<input type="hidden" data-omi-id="' + this.id + '" class="' + Omi.STYLESCOPEDPREFIX + '_hidden_data" value=\'' + JSON.stringify(this.data) + '\' />\r\n'
}
}
局部css生成好了,并且添加到了head中了,html也生成好了,并且把data数据也生成好了,这里使用的是Mustache模板。
那html中的声明式事件绑定怎么和clickHandle方法对应的呢,代码如下:
// event.js 文件 正则不好,感兴趣的可以研究一把
function scopedEvent(tpl,id) { // 模板字符串, id
return tpl.replace(/<[\s\S]*?>/g, function (item) {
return item.replace(/on(abort|blur|cancel|canplay|canplaythrough|change|click|close|contextmenu|cuechange|dblclick|drag|dragend|dragenter|dragleave|dragover|dragstart|drop|durationchange|emptied|ended|error|focus|input|invalid|keydown|keypress|keyup|load|loadeddata|loadedmetadata|loadstart|mousedown|mouseenter|mouseleave|mousemove|mouseout|mouseover|mouseup|mousewheel|pause|play|playing|progress|ratechange|reset|resize|scroll|seeked|seeking|select|show|stalled|submit|suspend|timeupdate|toggle|volumechange|waiting|autocomplete|autocompleteerror|beforecopy|beforecut|beforepaste|copy|cut|paste|search|selectstart|wheel|webkitfullscreenchange|webkitfullscreenerror|touchstart|touchmove|touchend|touchcancel|pointerdown|pointerup|pointercancel|pointermove|pointerover|pointerout|pointerenter|pointerleave|Abort|Blur|Cancel|CanPlay|CanPlayThrough|Change|Click|Close|ContextMenu|CueChange|DblClick|Drag|DragEnd|DragEnter|DragLeave|DragOver|DragStart|Drop|DurationChange|Emptied|Ended|Error|Focus|Input|Invalid|KeyDown|KeyPress|KeyUp|Load|LoadedData|LoadedMetadata|LoadStart|MouseDown|MouseEnter|MouseLeave|MouseMove|MouseOut|MouseOver|MouseUp|MouseWheel|Pause|Play|Playing|Progress|RateChange|Reset|Resize|Scroll|Seeked|Seeking|Select|Show|Stalled|Submit|Suspend|TimeUpdate|Toggle|VolumeChange|Waiting|AutoComplete|AutoCompleteError|BeforeCopy|BeforeCut|BeforePaste|Copy|Cut|Paste|Search|SelectStart|Wheel|WebkitFullScreenChange|WebkitFullScreenError|TouchStart|TouchMove|TouchEnd|TouchCancel|PointerDown|PointerUp|PointerCancel|PointerMove|PointerOver|PointerOut|PointerEnter|PointerLeave)=(('([\s\S]*?)')|("([\s\S]*?)"))/g, function (eventStr, b, c) {
if (c.indexOf('Omi.instances[') === 1) { // 声明式事件函数如果包含Omi.instances[ 的话
return eventStr; // 那就直接返回 "onclick="Omi.instances[0].clickHandle(this, event)""
} else if (c.lastIndexOf(')') === c.length - 2) { // 找到c指定的)最后出现的位置 和 去掉2个"" 相等的话
return eventStr.replace(/=(['|"])/, '=$1Omi.instances[' + id + '].'); // eg: "onclick="Omi.instances[0].clickHandle(this, event)""
} else {
let str = eventStr.replace(/=(['|"])/, '=$1Omi.instances[' + id + '].');
return str.substr(0, str.length - 1) + "(event)" + str.substr(str.length - 1, 1);
};
});
});
}
内置事件也对应好了,那就把html插到指定的dom容器中去吧。
this.renderTo.innerHTML = this.HTML;
ps:
好了, hello world的这个demo就这么说完了, 这个简单的demo只是抽取了对应的源码,
每个demo都将只抽取对应源码, 等到所有demo写完,omi源码基本就明白是怎么一回事了。
如果写的有问题,欢迎指出,也可以进群(看开篇扯蛋的帖子)和原作者交流。哈哈
Omi框架学习之旅 - Hello World 及原理说明的更多相关文章
- Omi框架学习之旅 - 生命周期 及原理说明
生命周期 name avatars company constructor 构造函数 new的时候 install 初始化安装,这可以拿到用户传进的data进行处理 实例化 installed 安装完 ...
- Omi框架学习之旅 - 之开篇扯蛋
说实话, 我也不知道Omi是干啥的, 只因此框架是alloyTeam出的, dntzhang写的, 也有其他腾讯大神参与了, 还有一些其他贡献者, 以上我也不太清楚, 当我胡说八嘎. 因其写法有人说好 ...
- Omi框架学习之旅 - 插件机制之omi-touch 及原理说明
这个插件也能做好多好多的事,比如上拉下拉加载数据,轮播,等一切和运动有关的特效. 具体看我的allowTouch这篇博客,掌握了其用法,在来看它是怎么和omi结合的.就会很简单. 当然使用起来也比较方 ...
- Omi框架学习之旅 - 插件机制之omi-router及原理说明
先来看看官网的介绍吧:https://github.com/AlloyTeam/omi/tree/master/plugins/omi-router 其实我推荐直接看官网的介绍.我所写的,主要给个人做 ...
- Omi框架学习之旅 - 插件机制之omi-transform及原理说明
给omi-transform插件做个笔记,使用起来也很爽. transform.js这个库,一直想写一篇帖子的,可是,数学不好,三维矩阵和二位矩阵理解的不好,所以迟迟没写了, 这也是一个神库,反正我很 ...
- Omi框架学习之旅 - 插件机制之omi-finger 及原理说明
以前那篇我写的alloyfinger源码解读那篇帖子,就说过这是一个很好用的手势库,hammer能做的,他都能做到, 而且源码只有350来行代码,很容易看懂. 那么怎么把这么好的库作为omi库的一个插 ...
- Omi框架学习之旅 - 获取DOM节点 及原理说明
虽然绝大部分情况下,开发者不需要去查找获取DOM,但是还是有需要获取DOM的场景,所以Omi提供了方便获取DOM节点的方式. 这是官网的话,但是我一直都需要获取dom,对dom操作,所以omi提供的获 ...
- Omi框架学习之旅 - 通过omi-id来实现组件通讯 及原理说明
这个demo是通过omi-id来获取子类的实例,然后更改data属性,之后updata一下就好了. 老规矩:先上demo代码, 然后提出问题, 之后解答问题, 最后源码说明. class Hello ...
- Omi框架学习之旅 - 通过对象实例来实现组件通讯 及原理说明
组件通讯不是讲完了吗(上帝模式还没讲哈),怎么又多了种方式啊. 你484傻,多一种选择不好吗? 其实这个不属于组件通讯啦,只是当父组件实例安装和渲染完毕后,可以执行installed这个方法(默认是空 ...
随机推荐
- vue1.0学习总结
前言 使用vue已经有三.四个月了,但是只是学着使用了一些基本方法.因为现在的前端框架越来越多(Angular,React...),但是我相信万变不离其宗,很多用法框架之间还是想通的,所以借总结的vu ...
- .bat文件设置IP、DNS
这几天遇到个烦心事,每次开机之后都要去手动去设置一下IP地址,一大串的数字还是有点麻烦,于是就想写个批处理文件设置IP 注意:在DOS下设置IP时需要管理员权限运行 1.查看机子设置IP需要用到的名字 ...
- 深入子元素的width与父元素的width关系
深入理解父元素与子元素的width关系 对于这一部分内容,如果理解准确,可以更容易控制布局,节省不必要的代码,这里将简单研究. 第一部分:父子元素都是内联元素 代码演示如下: <!DOCTYPE ...
- Spring Boot 基础教程系列学习文档
Spring Boot基础教程1-Spring Tool Suite工具的安装 Spring Boot基础教程2-RESTfull API简单项目的快速搭建 Spring Boot基础教程3-配置文件 ...
- php抽奖概率算法(刮刮卡,大转盘)
两种方法:①概率随着抽的奖项的变少而时刻变化 经典的概率算法函数:如下 <?php /* * 经典的概率算法, * $proArr是一个预先设置的数组, * 假设数组为:array(20, ...
- Swift 实现俄罗斯方块详细思路解析(附完整项目)
一:写在开发前 俄罗斯方块,是一款我们小时候都玩过的小游戏,我自己也是看着书上的思路,学着用 Swift 来写这个小游戏,在写这个游戏的过程中,除了一些位置的计算,数据模型和理解 Swift 语言之外 ...
- javase基础回顾(一)ArrayList深入解析 解读ArrayList源代码(JDK1.8.0_92)
我们在学习这一块内容时需要注意的一个问题是 集合中存放的依然是对象的引用而不是对象本身. List接口扩展了Collection并声明存储一系列元素的类集的特性.使用一个基于零的下标,元素可以通过它们 ...
- 基于DDD的.NET开发框架ABP实例,多租户 (Sass)应用程序,采用.NET MVC, Angularjs, EntityFramework-介绍
介绍 基于ABPZERO的多租户 (Sass)应用程序,采用ASP.NET MVC, Angularjs-介绍 ASP.NET Boilerplate作为应用程序框架. ASP.NET MVC和ASP ...
- BZOJ 3926: [Zjoi20150]诸神眷顾的幻想乡(后缀自动机)
被这道题坑了= =只与一个空地相连的空地不超过20个只与一个空地相连的空地不超过20个 因为很重要所以说两遍 就是说儿子节点最多只有20个 把这20个节点作为根遍历一遍所得到的tire所得到的所有不同 ...
- 令人费解的java泛型
对于我们java中的泛型,可能很多人知道怎么使用并且使用的还不错,但是我认为想要恰到好处的使用泛型,还是需要深入的了解一下它的各种概念和内部原理.本文将尽可能的囊括java泛型中的重要的概念 ...