magix
magix copied to clipboard
Magix is designed to construct large scaled,complex interactions app.
## 什么是区块 其实html标签就是区块,比如 div span a等标签。前端工程师就是在重复使用、组装这些区块(html标签)形成最终的需求页面。这是最原子的区块,我们要达到想要的页面效果,就得不停的重复嵌套这些区块。 但是呢,通常我们所说的`区块`是这些html标签的组合。比如当我们开发一个页面时,通常都是由多个逻辑关联不强的区块组成的,如常见的页头、页尾、内容区。这些区块通常是由多个html标签组合而成的。 我们可以借签html标签的这种嵌套及DOM Tree思路的,开发一套用于组合嵌套这种大区块的框架。我们把一个复杂的页面拆分成n个区块来开发,再由框架来便利的组合它们。这就是接下来要介绍的magix做的事情 ## 定义区块 在这里我们把区块定义为由一段`html`标签组成的代码片断,区块最小是一个标签,如``。区块可以像`html`标签一样嵌套,即一个区块可以由多个子区块组成 ## 项目中的区块 当我们在开发一个项目时,如前面提到的页头、页尾、内容区等,这些在我们项目里就是一个个区块,当然像内容区我们仍然可以再拆分子区块。像页头、页尾在项目里可以理解为原子区块,没必要再拆分了。这些区块应该很容易被复用。 ## 准备工作 magix适配了不同的加载器与dom操作类库,有amd、cmd及kissy,可[点击这里](https://github.com/thx/magix/tree/master/dist)查看,当然您也可以自己定制,定制方法[点击这里](https://github.com/thx/magix/issues/10) 接下来我们使用cmd版本,即seajs+jq来讲解magix的区块化管理 ## 安装 新建一个magix-test目录,在magix-test目录里放上package.json文件如下: ``` js { "name": "magix-test", "version": "1.0.0", "description":...
> 通过继承Magix.View来实现自己的view ### 通过Magix.View.extend方法来实现 ```js let Magix = require('magix'); module.exports = Magix.View.extend({ tmpl:'@demo.html', render(){ console.log('render ui') } }); ``` ### view的生命周期 #### 显式的init、render方法 >每个view默认都有一个init(初始化时调用,只会调用一次)和render(需要更新界面时被调用,可能会调用多次) ```js let Magix = require('magix'); module.exports...
> 通过继承Magix.View来实现自己的View ### 通过Magix.View.extend方法来实现 ```js import Magix from 'magix'; export default Magix.View.extend({ tmpl:'@demo.html', render(){ console.log('render ui') } }); ``` ### view的关键方法和事件 #### 显式的ctor、init、render方法 >每个view默认都有一个ctor、init(初始化时调用,只会调用一次)和render(需要更新界面时被调用,可能会调用多次) ```js import Magix from 'magix'; export...
```js //#exclude = define define('scroll', ['magix', '$'], (require, exports, module) => { let Magix = require('magix'); let $ = require('$'); let { Router, Vframe } = Magix; let checkVframes =...
目前 magix本身已经支持了虚拟dom diff。 但是做的是先`模板编译`,再`运行时字符串转换`到vdom,再去`diff`。 这带来一些问题: * 模板编译这块因为是渲染之后变成,需要处理很多html实体转换的问题,参数透传也很麻烦 * 运行时转换vom性能应该比较差,当然期待后续的具体测试。 另外,项目人员使用magix时经常会纠结语法的问题。 基于这些考虑,我们是否可以直接用jsx去写magix解决这些问题。
非冒泡事件的处理
在magix项目中,所有事件都是绑定在body上的。利用事件的冒泡,在body上监听到相应的事件后,查找包含mx-eventType的节点与对应的处理事件的view。 > 对于$win与$doc是2个特殊的节点,这2个是直接绑定,其它事件均是代理在body上 对于一些不冒泡的事件,如节点的scroll事件,img的load、error事件等无法通过类似mx-error进行方便的监听
先说普通事件代理,以`jQuery`为例 ``` $(document.body).on('click','p',function(e){ }); ``` 这样其实是为`document.body`上绑定一个`click`事件,当鼠标在页面上点击时,看点击的元素是否是`p`元素,如果是则触发,不是则向从当前节点向上查找到符合的`p`元素,一直查到`body`。这里其实是有性能问题的(jQuery已经弃用的live),稍后我们再讨论并给出解决方案。 事件代理其实就是把事件处理函数绑定在父级,通过事件冒泡的特点,识别触发事件的元素是否是自已期望的元素。对,重点是这个事件冒泡 `Magix`中的事件代理的过程是这样的:把相应的事件绑定在父节点后,当事件发生在父节点里,父节点的事件处理函数被调用后,从触发事件的节点(`target`)开始,判断节点是否有`mx-eventType`属性,如果有则停下来调用相应`view`的事件处理函数,如果没有则向上查找,直到父节点 再来看`Magix1.0`之前的事件代理方案 当前`view`使用的事件都统一绑定到`vframe`根节点上,而`vframe`又可以嵌套,比如嵌套后的结构如下:  我们的事件是绑定在`vframe`上的,当`vframe2`与`vframe1`绑定相同类型的事件时,比如`click`,而`vframe2`的`view`与`vframe1`的`view`具有相同的事件处理函数时,比如`selectAll`,那么问题就来了:鼠标点击在`vframe2`的SelectAll checkbox上时,`vframe2`先处理,而同时事件冒泡,冒泡到`vframe1`上时,`vframe1`的`selectAll`方法同样会被触发。 这里因为`vframe1`节点与`vframe2`节点都绑定了`click`事件,而由于事件冒泡的原因,同时事件类型及方法名又一致,当点击在`vframe2`内部时,`vframe2`处理完事件向上冒泡到`vframe1`,导致`vframe1`又处理了一遍,那或许你会说为什么不取消事件冒泡呢? 因为在我们的项目中,我们有可能引入其它组件,比如弹出日历等,需要鼠标点击在页面其它位置时日历需要隐藏消失。所以我们不能直接就把事件冒泡取消,否则日历也无法隐藏(像日历这种通常是绑定在`document`,需要相关事件冒泡到`document`) 问题出来了总得有个解决办法 第1个解决方案: 明确事件的处理`vframe`,类似上图,当鼠标点击在`vframe2`时,当`vframe2`处理完事件后,给打一个标记,标明当前事件是被处理过的,当`vframe1`收到后,先判断是否是处理过的,如果是处理过则不再理会 说到这里,大家可以想象一下,如果嵌套的`vframe`比较多时,鼠标点击在最内部的`vframe`时,由于事件冒泡,冒泡到每一层的`vframe`时,这一层的`vframe`都需要对事件做一次判断,而这些判断其实是不必要的,因为事件已经被内部`vframe`处理了,为什么还在再次判断?这地方多少会有些性能损耗 第2个解决方案: 把代理节点提高到`body`上,如果事件代理在`body`上而不是`vframe`上,把注册在`vframe`上相同的监听统一合并到`body`上,这样`vframe`节点不需要注册任何事件,对于嵌套的`vframe`,也是统一在`body`上处理事件,不需要每一层`vframe`的判断了。 该方案所有事件在绑定时,不管什么事件类型都绑定的是同一个事件处理函数,在这个事件处理函数内部再决定调用哪一个view的处理函数。内部针对magix的特点,对事件这块再特殊优化,从而提升性能,原理接下来讨论 回到开头我们的那个问题(jQuery live):事件是绑定在`body`上的,每当事件发生时,我们都要从发生事件的节点向上查找带有`mx-eventType`标识的节点,直到`body`。那我们来看一种情况:  假设html结构是这样的: ``` html...
> 在我们做一个复杂的长列表页面时,我们通常要优先加载主功能,或者滚动到可视区域时才加载相应的view,这时候就可以使用该插件。 源码: ```js let Magix = require('magix'); let $ = require('$'); let { Vframe, Event: MEvent, mix } = Magix; let WaitObserver = mix({}, MEvent); let Base = Vframe.prototype;...
> 该插件基本准确反映项目中加载及渲染的进度 **kissy版本只需要把seajs.use改成kissy.use即可** #### 使用方式一 通过script标签的方式直接引入到主页面上,如 ```html ``` #### 使用方式二 通过magix提供的扩展机制加载,如 ```js let Magix = require('magix'); Magix.boot({ exts:['progbressbar'] }); ``` #### 以下是插件源码,保存成progressbar.js ```js seajs.use('magix', Magix => { let ProgressBar =...
在magix项目中我们一直使用hash做为url的记录。 当然我们也支持history.pushState来改变url [magix中的url解析与处理](https://github.com/thx/magix/issues/12) 最终我们的url处理成path与params 两部分,而在magix项目中,通常path与view一一对应。 比如我们做一个展示用户相册的功能,我们可能这样设计url ```bash /photo?userId=xxx&page=1&page_size=20 ``` 因为我们的模块photo是固定的,变化的只是用户id,所以我们把模块photo设计在path中,变化的userId放在参数中。 看过别的前端或后端web框架的,通常它们都有url重写功能,比如我们把这个url重写成 ```bash /xxx/photo?page=1&page_size=20 ``` 把用户id放在path中,对于个人用户会更友好一些 在magix当中该如何做呢? > 考虑到向前兼容,旧的url我们要仍然支持 参考 UrlRewrite的设计 [http://blog.csdn.net/dyllove98/article/details/9281453](http://blog.csdn.net/dyllove98/article/details/9281453) 在magix的配置项中增加rewrites配置,示例如下 ```js Magix.config({ rewrites:[{ from:/$\/(.*)/photo$/, to:'photo?user=$1' }], routes:{ '/photo':'app/views/default'...