Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FDCon2019 第4届中国前端开发者千人峰会 - 《Omi - Cross-Frameworks Framework》 #335

Open
dntzhang opened this issue May 12, 2019 · 14 comments

Comments

@dntzhang
Copy link
Collaborator

dntzhang commented May 12, 2019

活动官网

大家好,大家有没有数左边的图片里有多少个 Omi?Omi 团队很在意这里,特意数了下,有三个。Omi 团队希望 Omi 以后在各大会议里能够印刷得更加大一些。今天给大家带来的主题是 《Omi - Cross-Frameworks Framework》,这也是 Omi 最新的 slogan。Omi 基于 Web Components 设计,和三大框架并不是你死我活的关系,可以很好的共存,无缝地集成,等听完这个分享大家就能 get 到 Omi 的精髓。子标题的灵感来自于 preact 作者以前打算分享的《Push react to the limit》,本来 TFC2017 邀请了他,后来个人原因他没有来,当然他后来去了google,可能这个标题政治不正确:)。

download-2

这是这次分享所包含的内容,不是目录。会涉及到上面4部分内容。

download-1

第一部分讲下 Omi & Web Components。

download

Omi 准确来说是 5-6年前开始弄的,但是真正切换到 shadow dom v1 还是2018年10月份左右,比 google 的 angular 更早使用 shadow dom v1。切换之后,得到了一段快速发展的时期,一致延续到现在。当时是什么初衷让我开始打造 Omi?

download-2

先来看看 react 的生命周期。当年看 React 的生命周期函数太长而且太多了,所以我要写个生命周期函数更短且更少的框架,比如 Omi 的(install, uninstall, installed),而且贴合 npm 的命令:)。

download-1

看下 Omi 中的生命周期,没有与对应的 shouldComponentUpdate对应的生命周期函数,没有 state 初始化、没有 constructer 函数,生命周期函数命名更加端、生命周期函数更加少。大道至简是真理,Omi 希望开发者不用手动优化 shouldComponentUpdate,怎么做到的,后面 PPT 会讲。

download

打造 Omi 的初衷,还有一点非常重要的是,scoped css!scoped css 可以让组件的 css 选择器更加简洁,比如可以直接写 tag selector,如果组件里只有一个 h1 的话。

h1 {
  color: red;
}

这个 h1 不会污染组件外部的 h1,也不会污染组件内部的组件的 h1。

download-5

来看下 ng。ng 支持四种模式

  • 模拟 scope
  • shadow dom v0(不推荐,在未来版本去掉)
  • 无 scope
  • shadow dom v1

download-4

这是第一种模式,ng 模拟 scope 生成的 html 结构,每个元素和子元素都会加上 scope attr。

download-3

这是第三种模式,html 结构什么 attr 都不加。

download-2

这是第四种模式,也就是 shadow dom 的模式,和 Omi 一样。

download-1

Omio 使用的也是 scope attr 模拟 shadow dom,这里在内部实现的时候有两点需要注意:

  • scoped attr 需要加到组件内部所有的子元素上,包括组件内部的组件本身
  • scoped attr 需要加到组件内部所有的子元素上,不包括组件内部的组件的子元素

有点绕,仔细体会下。即 scoped 到组件为止,包括组件不进入组件。

download

这是 Omi 和 React 生成的 dom 结构对比。Omi 使用 shadow dom 进行隔离。

download

来看这张图,很关键。这张图来自 twiter。twiter 有个问题是 web components 能不能替换掉整个框架?stencil 团队 show 出了这张图,给 Omi 团队带来了很大的灵感。Omi 不仅仅要自带状态管理、自带组件体系替换掉整个框架,也能输出单一的 custom elements 和任意框架集成。仔细看上图,stencil 在各个框架中使用还是有一些差异。

omi-inside-outside

Omi 做得更加彻底!所有框架使用 custom elements 方式一模一样!完全不用记忆差异,只靠肌肉记忆编程!Omi 的组件变更也是完全基于 setAttributeremoveAttribut,这种设计关键,因为其他框架不管 vdom 还是 real dom,最后都需要操作 dom 的 attribute,操作 attribute 就能进入到 Omi 的元素周期内部,这样就无缝勾住了,在任意框架都能使用 Omi 写的 custom elements。这里有一点需要注意:

  • 如果使用 Omi 写的 custom element 想要跨框架使用,必须声明 static propTypes

这个其实也不麻烦,因为如果你使用 typescript 写组件的话,本身就需要声明 props 的类型,改成大写就能用在 propTypes 上。Omi 内部会根据声明的类型,把使用组件时候传入的字符串转化为对应的类型。

download

来看个实际的例子,这个 m-button 可以在任意框架中使用。需要注意的是,可以传递 json 字符串作为 attr 给 m-button 内部使用。可以看到上面的 icon 的 paths 并不是标准的 json,但是没有关系,omi 内部会转成标准 json 再进行 JSON.parse 。而且你可以直接使用 setAttribute 设置 json。

const btn = document.querySelector('m-button')
btn.setAttribute('icon',  {
  path: 'xx',
  color: 'xxx'
})

这里需要注意一下,如果你再 react 中使用 Omi 的 element,而且需要传递 json 的话,可以包裹一下 Omi.o,省得手动转成字符串:

class TestOmiElement extends React.Component {
  render() {
    return (
    <div>
      <m-button icon={Omi.o({ path: 'xxx', color: 'xxxx' })}>omi button</m-button>
    </div>
    );
  }
}

download-9

这是 Omi 的初始化管线,前面说了 Omi 利用了 HTMLElement 钩子函数 connectedCallback 监听自定义元素插入到 dom 的行为,这个行为可能是 Omi 触发的,也可以是 react、vue、ng 或原生 js 触发的,触发之后就进入了 Omi 的内部管线。

  • 创建 shadow dom
  • 创建 style 并插入到 shadow dom
  • 执行 render 创建虚拟 dom 并保存一份方便下次 update 的时候 diff
  • 根据虚拟 dom 生成 真实 dom 并插入到 shadow dom

很清晰简明的管线。JSX 生成的虚拟 dom 会挂载在真实 dom 的 __omiattr_ 属性上用于 dfii。 有没有必有使用虚拟 dom?有必要!当然也可以修改 h 函数保存真实 dom 用于 diff,主要区别在于内存开销,速度差别不大。因为虚拟 dom 更轻量,属性更少,都是必要属性,所以用虚拟 dom。

download-8

看下 web components 最常用的三个生命周期函数。在 connectedCallback 会去执行 Omi 的 install,在 disconnectedCallback 会去执行 Omi 的 uninstall,最后一个 attributeChangedCallback 在 Omi 内部并没有使用,因为 Omi 重写了自定义元素的 setAttribute,从源头上已经可以监控到 attr change。

download-7

看下这段从 Omi 扒出来的源码,Omi 重写了 removeAttribute 和 setAttribute,这两个方法被调用会自动触发组件的更新,当然 Omi 也保留了原生的 removeAttribute 和 setAttribute,以 pure 开头,用于 Omi 内部使用,因为内部 diff 和 apply diff 的时候并不是需要每次都需要调用组件的 update。

download-6

看下这张图,很关键。用过 react 的同学都知道,react 性能优化的关键就是 shouldComponentUpdate,最被诟病的也是这个,很多开发者也吐槽这个钩子函数,shouldComponentUpdate 也可以配合不可变数据类型,直接进行引用地址比较,来决定组件是否需要更新。Omi 团队一直在思考,这个东西可不可以去掉,实时证明是可以的。因为 Omi 自定义元素完全基于字符串传递 props,不管是 boolean、string、number、json,都通过字符串传递,所以在进行组件更新之前,Omi 会进行一次浅比较,比较的结果决定是否更新,非常机智的做法。

download-5

在使用 web components 过程中,最被大家诟病的就是样式穿透问题。大家有些场景就是需要穿透组件怎么办?穿透不了就只能重写组件了,或者修改组件的源码。这个项目维护会带来巨大的问题。Omi 为了解决这个问题,支持属性 css,用于覆盖组件内部的 css 样式。看上面的代码,h1 是红色的。

download-4

在父组件中使用my-element,通过传递 css,把 h1 颜色改成 green 。而且还可以动态修改组件内部的样式,可以 onClick 内部的代码便知。

download-3

有的时候,我们不知道我们外部注入组件的选择器权重是否足够,我们可以通过加上 !important 保证一定覆盖掉组件内部的样式。

download-2

关于样式,还有一点需要注意,font-fact 不能放在 shadow dom 中,不然不生效。当然这里浏览器有差异,火狐可以放在 shadow dom 中,google chrome 不行。所以我们一般都放在外面用来兼容所有浏览器。

download-1

使用 font-face 的 font-family 的 class 定义必须放在 shadow dom 中,这个是有点割裂,但是还是很好理解,因为 shadow dom 本身就是隔离的。

download

除了赋能 web 其他框架,Omi 也提供了替换整个框架的解决方案,且看 store 体系。
store 体系是用于组件树共享数据和逻辑,如果熟悉 react context api 的一定了解,但是 Omi store 不完全一样,store 上 data 的变更会产生一条 path,path 会去和组件上定义的 path 匹配,匹配上了就会进行更新,所有就达到了 局部 diff,局部更新 的目的。

download-5

看这是一个静态声明 path 的例子,当然也可以使用 initUse 声明动态 path。看上图,计算属性也可以放在声明里。可以这样理解,store 是一个数据源头,关于数据最后要怎么预处理建议不要放在 store 里,而是放在组件的 use 声明里,比如你要对字符串反转,或者对数字平方什么的,不建议放在 store 里。

download-4

在 render 里可以使用 this.use 去访问 store 的数据,当然也可以通过 this.store.data.xx 的方式,后者书写起来稍微麻烦一些。

download-3

这是命中的规则,第一列是由 proxy 的数据变更产生的 path,后者是组件定义的 path,只要命中一条就会更新组件。store 整个体系设计得非常简单直接方便,没有复杂的概念。

download-2

Omi 对 typescript 支持越来越好,后面 Omi 生态新增的所有组件、工具后者类库都会使用 typescript 来书写。上图解释了怎么让组件使用者能够在 typescript 或者智能提示。注意 Omi.props & ... 的目的是把 HTML 标准的属性也集成到智能提示上,本身 Omi 写的自定义元素就是标准的 HTMLElement。使用 JSX 写 Omi 元素的时候,在 ts 的检查会更加宽松,比如 tabindex="1",在 react ts 中必须写成 tabIndex={1},Omi 顺从 HTML 标签,一段 HTML 直接粘贴到 render 函数中就可以用,当然自闭合的标签必须手动闭合下,比如 img。

download-1

在使用原生的 web components 的 customElements.define 的使用,体验很不好。重复定义直接报错,而且从报错信息上也看不出是什么元素重复定义了。

download

Omi 基于 customElement.define 封装了 define 方法,有了前置的检查逻辑,不报错,只改告警,而且重复的名称也会突出打印出来。

download-8

download-7

download-6

download-5

download-4

download-3

download-1

download-2

download

PPT以及演讲内容以图文的形式还原
待更新...
待更新...

@dntzhang dntzhang pinned this issue May 12, 2019
@Mider0325
Copy link

期待

@ZainChen
Copy link
Contributor

期待+1

@yisar
Copy link
Contributor

yisar commented May 14, 2019

窝巢,大赞!

@mtonhuang
Copy link

@FAKER-A
Copy link
Contributor

FAKER-A commented May 14, 2019

🛋️

@ddzy
Copy link

ddzy commented May 14, 2019

不错

@liulinboyi
Copy link
Contributor

期待+1

@haua
Copy link

haua commented Jun 2, 2019

期待

@qiulang
Copy link

qiulang commented Sep 25, 2019

我大致看了下文档,但一时还是没搞清楚在vue之上再引入omi的好处在哪,能出些文档提供这方面的描述吗?

@dntzhang
Copy link
Collaborator Author

@qiulang 没啥好处吧?好处主要是跨框架,以及跨平台(omi-kbone),还有兼容性好omio(IE8)。

@jackyleungcn
Copy link

Omi能做 前端组件微服务 吗? @dntzhang

@dntzhang
Copy link
Collaborator Author

额。啥是微服务? @jackyleungcn

@yisar
Copy link
Contributor

yisar commented Sep 25, 2019

可以做微服务……

@avennn
Copy link

avennn commented Jan 5, 2022

请问目前omi主要的用户有哪些?有没有比较大型的应用?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet