Open xinglie opened 3 years ago
2015~2018 magix3目标是项目统一,2018以后,magix5目标是性能及所有开发细节的完善
大到应用小到组件,均由数据描述驱动,由开发者同步或异步操作这份描述数据,再由magix5异步的更新界面。 这种方案尽可能的把浏览器资源优先用于处理重要的数据,而非界面更新。 比如上万条的列表,不用任何优化手段,全部渲染到界面上来,同时要进行全选,反选等选中操作。 首先用数据把这个列表表示出来,然后交与magix5渲染界面。如果进行全选、反选,也是操作数据进行相应的选中表示,再由magix5更新界面,如果要获取哪些选中行,也是通过数据进行查找获取。
magix5
即:一个功能纯靠数据进行实现,ui仅仅是把描述数据进行可视化。浏览器操作数据是快速的,这一步或同步,或异步进行操作。后续magix5则是通过异步、确保浏览器不卡的情况下更新界面,在magix5更新的过程中,用户依然可以继续操作这份数据,magix5最终会把界面渲染到与这份数据一致。
ui
弊端:因为更新是异步的,如果界面更新量大,用户在后续操作时,数据变化不能及时反馈到界面上。当数据量大时,依然需要其它手段进行优化,不能单靠magix5。
@
@:
升级原因是现在npm包支持如@ali/sub-module这样的形式,之前的@很容易把这样的字符串识别为路径,故升级规则
npm
@ali/sub-module
因为和magix有关,magix5重新实现了模板、view嵌套渲染等机制,为了抛弃历史编译工具的包袱,直接建立新的编译工具来打包编译。
magix
view
global@path/to/style
全局样式需要使用新的规则来书写,同时为了减轻编译工作量,全局样式不再由编译工具处理。可阅读magix-composer升级章节来获取进一步的信息
magix-composer
如果需要,可使用如
let styleString=`style@:./path/to/style`; Magix.applyStyle('@:moduleId'+'ext.style',styleString)
代替。其中style@:./path/to/style是编译器提供的替换器,更多其它替换器可查看magix-composer升级章节
style@:./path/to/style
替换器
css
:global(.selector){}
@global{.selector{}}
["ref@:./path.css:name"]{}
@:scoped.style:name
html
@:$(name)
@:$var(--name)
@:$keyframes(scale)
@:$font-face(scale)
js
css3
@:./path.css:--name
color:var("ref@:./path.css:--name")
@:./path.css:@font-face(name) @:./path.css:@keyframes(name)
counter("ref@:./path.css#counter-name")
.user--name
@keyframe anim--1
--
<div class="user--{{=userType}}">
cssGlobalVarPrefixes
--scoped-
@scoped.style:--scoped-var-name
--var-name
color:var("ref@:./path.css--var-name")
{{! html }}
<tag x-html="{{=html}}">
each
by asc
by desc
step
*
<mx-vframe src="./path/to/view" *user-id="{{=userId}}"/>
{{@ expr }}
{{# expr }}
{{@: expr }}
<mx-slot>
更多不支持场景及其它细节修改可参考:https://github.com/thx/magix-composer/issues/1
全新实现的渲染、事件、分片任务机制
如果不能直接使用esm,则模块标识符为magix5,如
esm
import Magix from 'magix5'
由原来的dom id与vframe一一映射改为节点与vframe一一映射
vframe.id
view.id
dom id
document.getElementById
dom
vframe
vframe.root
view.root
Magix.Vframe.get
Magix.Vframe.byNode
Magix.Vframe.byId
Magix.Vframe.root()
mountZone
unmountZone
mountView
unmountView
mountVframe
unmountVframe
mount
unmount
invoke
let result = await vframe.invoke('viewMethod')
unloadTest
created altered
updater
由原来的操作updater改为操作view
如
this.updater.get('userId') //升级为 this.get('userId')
this.updater.digest(); console.log(Magix.node(`dom_`+this.id)); //升级为 await this.digest(); console.log(Magix.node(`dom_`+this.id));
console.log(this.root);
用于在其它方法内确保异步更新的view完成更新。
export default Magix.View.extend({ render() { this.digest(); }, async 'someEvent<click>'() { await this.finale(); // your code } })
magix5在实现时,因为是全局统一绑定的事件,所以把所有事件都绑定在了捕获阶段。但之前所有版本都使用冒泡阶段,因此为了减少修改为捕获阶段带来的风险,目前仍采用冒泡方案,而对于需要捕获处理的,可参考以下代码 `div<scroll>&{capture:true}`(){
magix5在实现时,因为是全局统一绑定的事件,所以把所有事件都绑定在了捕获阶段。但之前所有版本都使用冒泡阶段,因此为了减少修改为捕获阶段带来的风险,目前仍采用冒泡方案,而对于需要捕获处理的,可参考以下代码
`div<scroll>&{capture:true}`(){
}
增加事件`指示器`:`&{capture:true}`,目前支持`capture`为`true`,把该事件升级为捕获阶段,及`&{passive:flase}`,允许在事件内部使用`preventDefault`。注意:浏览器并不是把所有事件的`passive`都默认为`true`,只有影响性能的如`mousewheel`等默认为`true`,如果遇到调用`preventDefault`调试工具有提示时,才需要写上`&{passive:flase}` 可合写为`&{capture:true,passive:false}` https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener #### 强制assign方法 magix5强制所有view都必须实现`assign`方法,同时只有在该方法返回`false`时阻止更新当前`view`,其它值则更新 该方法的第2个参数升级为当前节点的`innerHTML`字符串。 #### 删除mixins,统一merge替代 ```ts import Dragdrop from '../gallery/mx-dragdrop/index'; import Dialog from '../gallery/mx-dialog/index'; export default Magix.View.extend({ minins: [Dragdrop, Dialog], render() { this.digest(); } }); //升级为 import Dragdrop from '../gallery/mx-dragdrop/index'; import Dialog from '../gallery/mx-dialog/index'; export default Magix.View.extend({ render() { this.digest(); } }).merge(Dragdrop,Dialog)
Magix.State
未来考虑使用数据中介来处理跨view的复用数据,正式版本Magix.State将考虑删除,请不要再使用。
task lowTask taskFinale lowTaskFinale
import {task,lowTaskFinale} from 'magix' let collectRects = (nodes) => { //process nodes client rectangle }; let walk = nodes => { for (let e of nodes) { if (e.nodeType == 3) { task(collectRects, [e]); } else if (e.nodeType == 1) { if (e.childNodes.length) { task(walk, e.childNodes); } else if (e.tagName.toUpperCase() != 'TD') { task(collectRects, [e]); } } } }; walk(nodes); await lowTaskFinale();
大多数配置兼容magix-combine的
大纲性的编译深入了解可阅读:magix-composer模板编译阶段分析 magix-composer整体编译阶段分析
#
#开头的编译指令升级为magix-composer#,更清楚的表达这是为什么服务的
magix-composer#
https://github.com/thx/magix-composer/issues/5
方便在代码中以字符串的形式引用其它文件内容 let base64Image='base64@:./path/to/test.jpg'; 内置的替换器有 str以字符串存储文件内容,如str@:./path/to/test.css base64以base64存储文件内容,如base64@:./path/to/test.jpg style以字符串存储样式文件内容,Magix.applyStyle支持的样式及处理过程该style均支持 html以字符串存储html文件内容,该方法会压缩html
方便在代码中以字符串的形式引用其它文件内容
let base64Image='base64@:./path/to/test.jpg';
内置的替换器有
str
str@:./path/to/test.css
base64
base64@:./path/to/test.jpg
style
Magix.applyStyle
扩展替换器。
let composer = require('magix-composer'); composer.config({ fileReplacerPrefixes:['action1','action2'], fileReplacerProcessor(action,file){ console.log(file,action); if(action=='action1'){ return 'action1 test'; } return 'action2 test' } });
mx-
模板中所有使用mx-属性的地方会被编译工具自动升级为mx5-
mx5-
<div mx-click="test()"></div> <!--编译渲染后为--> <div mx5-click="test()"></div>
该过程是自动的,开发时仍然写mx-,需要知道有这个转换过程。
这里主要为同一个页面存在多个magix做准备,版本不同,mx-或mx5-或mx6-前缀不同,彼此间不会影响。
mx6-
mx-key
如果想进一步提升渲染性能,可对如each列表指定key,magix5会在数据移动时,直接移动节点替换逐个更新节点。新手请忽略该配置 {{each list as e}} <div mx-key="{{=e.id}}"> {{=e.id}} </div> {{/each}}
如果想进一步提升渲染性能,可对如each列表指定key,magix5会在数据移动时,直接移动节点替换逐个更新节点。新手请忽略该配置
key
{{each list as e}} <div mx-key="{{=e.id}}"> {{=e.id}} </div> {{/each}}
mx-updateby
magix-composer会自动分析出渲染mx-view所依赖的代码中的根数据,如
mx-view
<mx-vframe src="./path/to/view" *user-id="{{# user.id }}" *card="{{# list[0].cards[1]}}"/>
./path/to/view只与根数据user及list有关联,如果这2项数据未更新,则view不会触发更新。
./path/to/view
user
list
2
如果有以下代码更新界面
this.digest({ coupons:[] });
则./path/to/view会跳过更新。
可通过mx-updateby强制view与某些数据关联,如
<mx-vframe src="./path/to/view" *user-id="{{# user.id }}" *card="{{# list[0].cards[1]}}" mx-updateby="user,list,coupons"/>
新手请忽略该配置
全局样式书写规则为mx-前缀,如.mx-tag-input,只有类选择器,其它一律禁止使用。禁止标签、ID、多级嵌套等。
.mx-tag-input
全局样式的引入可通过入口页面link标签引入,或在入口代码中使用
link
let styleString=`style@:./path/to/style`; Magix.applyStyle('@:moduleId'+'ext.style',styleString);
引入。
可通过magix-composer的配置项对某些规则进行关闭检测,如
composer.config({ checker: {//代码检测 /** * 模板中class的代码检测 * @param {string} selector 模板中使用到的样式选择器 */ tmplClassCheck(selector) { return selector && !selector.startsWith('mx-') && !selector.startsWith('magix-'); } } });
由编译工具注入,能在模板中直接使用的变量使用$开头。
$
$viewId获取当前view对象的id,模板中的$viewId等同于代码中的this.id
$viewId
id
this.id
如模板
<!-- index.html --> <button id="{{= $viewId }}_btn">click here</button>
代码
import Magix from 'magix5'; export default Magix.View.extend({ async render() { await this.digest(); let btnNode = Magix.node(`${this.id}_btn`); console.log(btnNode) } })
试验中的片断传递 <mx-slot name="list" fn="$list"> list fn: {{if list}} {{each $list as item index}} {{=index+1}}:{{=item.title}} {{/each}} {{/if}} {{=outerVariable}} </mx-slot>
试验中的片断传递
<mx-slot name="list" fn="$list"> list fn: {{if list}} {{each $list as item index}} {{=index+1}}:{{=item.title}} {{/each}} {{/if}} {{=outerVariable}} </mx-slot>
<mx-vframe src="./x" *ref="{{# $slots.list }}"/>
带`fn`属性的`mx-slot`会被编译成函数,不带`fn`的则为普通变量 现阶段在当前`view`中声明并使用`mx-slot`没有任何问题,但通过参数的形式传递给其它`view`有可能在`view`渲染传递数据上有问题,请先在简单场景下使用。 使用`mx-slot` ```html <mx-slot use="list"/> <mx-slot use="list" params="a,b,c"/>
fn允许有默认值,如
fn
<mx-slot name="test" fn="num=20,arr=['array'],obj={},str='string'"> partial content {{=array}} </mx-slot>
内嵌在mx-vframe直接作为参数
mx-vframe
<mx-vframe src="./index"> <mx-slot name="header"> header content </mx-slot> </mx-vframe>
在./index的模板里使用
./index
<mx-slot use="header"> default content </mx-slot>
不要套娃,如
<mx-vframe src="./c"> <mx-slot name="a"> <div id="slot-test"> <mx-vframe src="./a"> <mx-slot name="test" fn="abc"> inner "a" group abc value:{{=abc}} </mx-slot> </mx-vframe> </div> </mx-slot> <mx-slot name="header"> some text here;{{=abc}}<button mx-click="change({data:'{{#abc}}'})"></button> </mx-slot> </mx-vframe>
mx-slot遵循先声明后使用,同时声明不能嵌套的规则,如
<!-- 不支持嵌套 --> <mx-slot name="outer"> <mx-slot name="inner"> aaa </mx-slot> </mx-slot> <!-- 不支持先使用后声明 --> <mx-slot use="a1"/> <mx-slot name="a1"> a1 content </mx-slot> <!-- 可以在slot内部引用其它slot --> <mx-slot name="a1"> a1 content </mx-slot> <mx-slot name="a2"> a2 content <mx-slot use="a1"/> a2 content </mx-slot>
mx-slot为试验用法,不用或减少使用
magix5核心思路
大到应用小到组件,均由数据描述驱动,由开发者同步或异步操作这份描述数据,再由
magix5
异步的更新界面。 这种方案尽可能的把浏览器资源优先用于处理重要的数据,而非界面更新。 比如上万条的列表,不用任何优化手段,全部渲染到界面上来,同时要进行全选,反选等选中操作。 首先用数据把这个列表表示出来,然后交与magix5
渲染界面。如果进行全选、反选,也是操作数据进行相应的选中表示,再由magix5
更新界面,如果要获取哪些选中行,也是通过数据进行查找获取。即:一个功能纯靠数据进行实现,
ui
仅仅是把描述数据进行可视化。浏览器操作数据是快速的,这一步或同步,或异步进行操作。后续magix5
则是通过异步、确保浏览器不卡的情况下更新界面,在magix5
更新的过程中,用户依然可以继续操作这份数据,magix5
最终会把界面渲染到与这份数据一致。弊端:因为更新是异步的,如果界面更新量大,用户在后续操作时,数据变化不能及时反馈到界面上。当数据量大时,依然需要其它手段进行优化,不能单靠
magix5
。全局通用升级
at规则升级,由原来的
@
升级为@:
。样式、模板及代码中所有使用@
的地方均需要改写为@:
magix-combine升级为magix-composer
样式升级
删除
global@path/to/style
如果需要,可使用如
代替。其中
style@:./path/to/style
是编译器提供的替换器
,更多其它替换器
可查看magix-composer
升级章节其它使用升级
css
中保持选择器不被变换::global(.selector){}
或@global{.selector{}}
css
中引用样式:["ref@:./path.css:name"]{}
@:scoped.style:name
html
中引用样式:@:$(name)
。只能引用当前文件有关联的样式,不能引用其它样式html
中引用变量:@:$var(--name)
。只能引用当前文件有关联的样式,不能引用其它样式html
中引用@规则:@:$keyframes(scale)
,@:$font-face(scale)
。只能引用当前文件有关联的样式,不能引用其它样式js
中引用css3
中的变量@:./path.css:--name
;css
中引用其它css
文件中的变量color:var("ref@:./path.css:--name")
;@:./path.css:@font-face(name) @:./path.css:@keyframes(name)
counter("ref@:./path.css#counter-name")
.user--name
@keyframe anim--1
,即只编译从后向前两个连接线--
前面的部分,后面的保留,方便在模板中实现如<div class="user--{{=userType}}">
带变量的场景cssGlobalVarPrefixes
这个数组,默认为['--mx-','--magix-']--scoped-
前缀,在js
中需要使用时@scoped.style:--scoped-var-name
--var-name
,引用其它文件中的color:var("ref@:./path.css--var-name")
模板升级
{{! html }}
输出html
的功能,使用<tag x-html="{{=html}}">
代替each
支持by asc
和by desc
进行排序,支持step
指定循环步长view
参数传递必须写*
号,如<mx-vframe src="./path/to/view" *user-id="{{=userId}}"/>
{{@ expr }}
升级为{{# expr }}
。这里主要考虑之前@
升级为@:
,为了保持统一和简单,这里不升级为{{@: expr }}
而是直接改为{{# expr }}
<mx-slot>
)更多不支持场景及其它细节修改可参考:https://github.com/thx/magix-composer/issues/1
代码升级
如果不能直接使用
esm
,则模块标识符为magix5
,如vframe升级
vframe.id
与view.id
可查出dom id
,并通过document.getElementById
获取dom
节点,现在vframe.id
与view.id
仅是内部全局唯一的一个标识,不再与dom
节点有关联。如果需要通过vframe
或view
对象反查对应的dom
节点,可使用vframe.root
或view.root
获取所在的dom
节点Magix.Vframe.get
方法升级为Magix.Vframe.byNode
通过dom
节点对象获取它上面的vframe
及Magix.Vframe.byId
通过前面提到的vframe.id
或view.id
查找vframe
Magix.Vframe.root()
获取根节点vframe
实例上的mountZone
、unmountZone
、mountView
及unmountView
方法,原则上减少手动渲染view
,全部走数据驱动界面的方案。vframe
实例上的mountVframe
和unmountVframe
为mount
和unmount
,同时第一个参数由字符串改为dom
对象vframe
实例上的invoke
修改为异步,使用时需要let result = await vframe.invoke('viewMethod')
vframe
实例上新增unloadTest
方法,允许view
阻止卸载,实现高级场景的功能,如页面切换时,内容未保存提示。created altered
事件,高频且用处较少,后续可能会插件支持删除
view
对象上的updater
对象。如
view
更新改为异步view新增root对象
view新增finale方法
支持全部事件
}
精简
Magix.State
未来考虑使用数据中介来处理跨
view
的复用数据,正式版本Magix.State
将考虑删除,请不要再使用。增加
task lowTask taskFinale lowTaskFinale
实现分片任务magix-composer升级
大纲性的编译深入了解可阅读:magix-composer模板编译阶段分析 magix-composer整体编译阶段分析
升级
#
开头的编译指令#
开头的编译指令升级为magix-composer#
,更清楚的表达这是为什么服务的https://github.com/thx/magix-composer/issues/5
替换器
扩展替换器。
mx-
属性升级模板中所有使用
mx-
属性的地方会被编译工具自动升级为mx5-
该过程是自动的,开发时仍然写
mx-
,需要知道有这个转换过程。mx-key
指定节点标识mx-updateby
更新view
magix-composer
会自动分析出渲染mx-view
所依赖的代码中的根数据,如./path/to/view
只与根数据user
及list
有关联,如果这2
项数据未更新,则view
不会触发更新。如果有以下代码更新界面
则
./path/to/view
会跳过更新。可通过
mx-updateby
强制view
与某些数据关联,如新手请忽略该配置
全局样式
全局样式书写规则为
mx-
前缀,如.mx-tag-input
,只有类选择器,其它一律禁止使用。禁止标签、ID、多级嵌套等。全局样式的引入可通过入口页面
link
标签引入,或在入口代码中使用引入。
可通过
magix-composer
的配置项对某些规则进行关闭检测,如$viewId $slots
$viewId
获取当前view
对象的id
,模板中的$viewId
等同于代码中的this.id
如模板
代码
<mx-vframe src="./x" *ref="{{# $slots.list }}"/>
fn
允许有默认值,如内嵌在
mx-vframe
直接作为参数在
./index
的模板里使用不要套娃,如
mx-slot遵循先声明后使用,同时声明不能嵌套的规则,如
mx-slot为试验用法,不用或减少使用