不需要“编译”的前端富应用开发模式

Tags
webfrontendgem
Created
Apr 19, 2018 4:50 AM

组件可以使用 ShadowDOM 封装。App 只使用 LightDOM,slot 用 property 替代,part 使用 class 代替,这样可以使用 TailwindCSS,并且能 SSR(或者 Prerender)。

Building UI components in DevTools with Custom Elements (goo.gle/building-ui-devtools)

目标:

  1. 原生 js,简单 // https://ionicframework.com/blog/the-end-of-framework-churn/
  2. 功能全,自定性能高
  3. 速度快,

https://music.xianqiao.wang/ MV* 模式

概念的核心是 Web Component,但 Web Component + Shadow DOM 是不适用于 APP,因为:

  • 不能使用 svg 符号 // https://github.com/WICG/webcomponents/issues/772#issuecomment-438834060
  • URL Hash 没用了 // 找不到具有该 id 的内置元素, :target 伪类也没用了,但 ::target-text 能用
  • document.activeElement 没用了 // 好像可以模拟
  • document.addEventListener 小心用,有些事件默认不冒出 ShadowDOM // Event.target 不准确
  • 不方便集成 React/Vue 组件 // slot、gem-frame
  • 样式不能穿透(自定义样式);选择不到或者不能继承的属性不方便共享;
  • // ShadowDOM 中使用 <link> 可以解决

    // ::slotted // 注意一个空格就能占用插槽

    // 单层穿透 ::part,多层穿透 ::theme // 类似原生元素的伪类选择

    // Constructable Stylesheet Only actually parse the stylesheet when the first instance is connected.

  • 不能重复定义元素(作为外部库时影响大)
  • // 命名范围 // 名称可能会很长

slot 的问题:

  • 总是挂载
  • slot 未分配时可以占位,但分配时不能添加额外元素

https://forum.palemoon.org/viewtopic.php?f=1&t=24004 // 但是保存并不是那么重要

https://dev.to/richharris/why-i-don-t-use-web-components-2cia

https://dev.to/shihn/why-i-use-web-components-my-use-cases-1nip

模块化使用 ES6 Modules

  • HTTP2 Server Push 提前加载深层依赖 // 安装(skypack/jspm/npm 内容)依赖到本地
  • 无法单独为一个模块使用强制缓存
  • 如何使用依赖的打包/压缩版本?

JS 类型标记 // 使用 jsdoc/ts

ICON 使用自定义 ShadowDOM 元素(不能使用 SVG <use> // 片段标识不能越边界,讨论

全局布局 CSS 阻塞渲染

组件使用 Web Component(包含组件样式)

// 每个 ShadowDOM 都有 <style> 将影响性能 https://bugs.chromium.org/p/chromium/issues/detail?id=824684

// 可以用 link 元素,现在有 blocking 属性

// 构造函数 XXXElement,名称 应用名-系统名-组件名

// ES 字段是 [[Define]] 语义,不要使用 js 字段覆盖已有定义

// MutationObserver 元素时检测不到 ShadowDOM 内的更改

ShadowDOM 组件一定存在一层嵌套,解决:display: contents; // 不影响语义

组件/异步加载:Custom Element when defined import(<depend>)

跨平台

  • 组件支持多平台 // 媒体查询使用 <style media>, StyleSheet.media
  • 不同入口

App 数据管理使用订阅模式,数据更新时通知订阅该数据的组件 // 不是订阅单个值而是数据模块

  • 网络请求或者用户操作才会修改数据,本身是需要花费长时间
  • 只是修改当前页面,内容不会太多

跨组件通信通过全局数据 // 只有组件状态才使用组件数据

UI 跟数据绝对分离 // 复杂项目方便维护

Router,Modal 使用 History API // 只有一次 onload

  • 返回时通过 popstate event 响应
  • 重写 pushState,为导航添加响应 // 在 push 前响应会造成和 pop 行为偏差

单元测试,UI 测试使用 puppeteer 进行 // 类似 wpt wct

组件订阅数据,数据更新时通知组件更新 // 使用 Set 保证多次同步修改组件只更新一次

服务端渲染使用声明式 ShadowDOM // 水合 DSD ?

// 构造样式不能直接使用 getInnerHTML 读取到,但有重复样式代码的问题

如何更新引用数据的 DOM/Node?

  • 根据占位符使用查询建立数据和 DOM/Node 的绑定 -> diff(数据) -> 修改 DOM/Node
  • vdom/ast 建立数据和 DOM/Node 的绑定 -> diff(数据) -> 修改 DOM/Node
  • Parser 建立数据和 DOM/Node 的绑定

window.__ENV__DNS__HOST__ 格式区别环境 // Server 读取配置进行字符替换

键盘访问,语义化标签,Accessibility 支持

SEO 支持 扁平化 ShadowDOM,结构化页面可以使用 josn-ld

Tips:

  • 防止和原生属性冲突,如 `title`, `id`
  • 少使用组件/元素
  • 自定义链接组件需要包装 a 标签 // a 有些特性用户代理支持
  • 不要用 index 命名,不然在 devtools 和编辑器中全是同名文件
  • 严格进行错误处理,在元素上发出 error 事件

Note:

  • closest/matches 都是在 shadowRoot 内工作

Node Server:

  • live load // webpack
  • hot load(所有组件保存所有状态刷新在恢复) // 自定义元素不能重新定义,upgrade 没用,需要新元素替代(替换 -> attr/props 赋值 -> update)
  • 发布 // release
SuperMade with Super