神的尾巴

全栈工程师、独立开发者

0%

源码解析之AlloyTeam-omix

JSX 的解析

简单看了下生成的 bundler.js:

1
2
3
4
5
6
7
8
9
10
11
12
value: function render() {
return _index2["default"].x(
"div",
null,
_index2["default"].x(_hello2["default"], { name: this.name }),
_index2["default"].x(
"h3",
{ onclick: this.handleClick.bind(this) },
"Scoped css and event test! click me2!"
)
);
}

解析 jsx 的过程,是通过 babel-plugin-transform-omi-jsx(基于 babel-helper-builder-react-jsx)和 babel-plugin-transform-omi-display-name。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// babel-plugin-transform-omi-jsx
...
visitor.Program = function (path, state) {
var file = state.file;
//var id = state.opts.pragma || "Omi.x";
var id = "Omi.x"
for (var _iterator = file.ast.comments, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : (0, _getIterator3.default)(_iterator);;) {
var _ref2;
...

// babel-plugin-transform-omi-display-name
...
var isCreateClassCallExpression = t.buildMatchMemberExpression("Omi.x");
var isCreateClassAddon = function isCreateClassAddon(callee) {
return callee.name === "createOmiClass";
};
...

jsx 最终被解析成如下:

1
2
3
4
5
6
7
8
9
Omi.x(
tagName: 'div'|'component-a'|ComponentA,
{
name: this.name
...
}, // 属性
Omi.x(...), // 子组件,一个或多个
...
)

其中 tagName 有三种可能:

  1. 普通的 html 标签
  2. 通过 Omi.tag(‘component-a’, ComponentA),注册的 tagName
  3. Component 实例

Omi.x 执行生成虚拟 dom,结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
// VNode
{
tagName: 'div', // tag
children: [
...
], // 子VNode实例
props: {
name: 'godtail'
...
}, // 属性列表
key: 'key', // 对应props.key
count: n, // 节点数量,包含子VNode
}

另外需要注意的一点是,如果 props 设置了 children,会 push 到 VNode 的 children 里去。

组件生命周期

初次渲染:install 流程

4-2

render 过程

1. render 的一些配置

_omi_increment: 指定是否增量更新,如果设置为 true,每次更新的时候会增加。
store: 设置所有组件共享的$store。
ssr:

render 流程,使用前面生成的虚拟 DOM,进行标准化后,生成实际的 dom 节点。

2. 标准化虚拟 DOM

// TODO

3. ref 引用处理

如果设置了属性 ref,在 Component 实例,会存储一个键为属性名,值为 DOM 节点的 refs 对象

4. 执行插件

执行插件的方法

插件机制

Omi 的插件,存储在 Omi.plugins。在 reader 的时候,会获取到设置 plugin 属性的 DOM 节点,并执行对应的插件方法。

注册插件方法如下:

1
2
3
Omi.extendPlugin('omi-plugin', function (dom, instance) {
// ...
}

scope css

比较关键的两个属性

__st_[x]: 对应的是组件样式的作用域
__s_[x] : 对应的是组件实例的 id

同一个组件,实例化多次,组件作用域 id 一致,但是组件 id 不一致

其中,核心的正则表达式:new RegExp('([^\r\n,{}:]+)(:[^\r\n,{}]+)?(,(?=[^{}]*{)|\s*{)', 'g')

4-1

源代码中有相关注释

对应的分组匹配:(.classname)(:pesudo)( {)

解析规则结果如下:

1
2
3
4
5
.test {
}
// scoped后
.test[__st_1],[__st_1] .test {
}

确保当前元素和当前元素的子节点能够匹配样式。

生成后在 head 添加 style 标签,存放样式数据。

一些疑问

每个元素要不要都设置__st_[x]__s_[x],感觉只有组件的根节点需要设置。
style 的 id 要和__st_[x]匹配好点,目前使用的是__s_[x]

觉得对你有帮助的话,请我喝杯咖啡吧~.