Web 原生开发解决方案
从 2012 年开始,H5 成为 html 最后一个稳定版本,不再兼容之前的 API,从此诞生的每一个新 API,一旦结束试验期将永远存在,2015 年 ES6 的出现又淘汰掉一堆 JavaScript 框架,再结合 CSS3,前端原生 API 已经可以替代任何脚手架和打包工具,本文介绍使用原生代码替代前端流行框架的解决方案:分析框架提供的每一个特性以及相对应的原生替代品。
如何推广
前期由我负责给页面搭架子、寻找开源库、指导开发,其他小伙伴填内容、维护后续更新、和设计组讨论。
Web Component 单文件组件
Web Component API 是为了取代 iframe 组件和 Vue 组件等而推出的浏览器原生接口,虽然不能 100% 取代 Vue、React 等框架,因为组件框架还是具有 SSR、SEO 等微弱优势,但多数应用场景下是能够完美替代的,这一点上 Vue 的官网也有说明:https://v3.vuejs.org/guide/web-components.html#web-components-vs-vue-components 。因此我们制定出了一套用原生接口实现的单文件组件格式。单文件组件的概念参考 Vue 官网教程:https://cn.vuejs.org/v2/guide/single-file-components.html ,指以一个 html 组件为最小分割单元实现高内聚低耦合:组件内提高内聚性,组件间减少耦合度。基于 WebComponent 的单文件组件主要有 2 种可接受的实现方式:js 文件和 html 文件:2 种方式各有优劣。
以.js 文件为组件
文件中通过字符串模板定义 html 和 css,然后在自定义元素的构造函数中引入它们。但为了让内嵌于 js 字符串中的 html 和 css 支持自动格式化、语法高亮、语义提示,需要引入微软官方的 vscode 插件:https://github.com/microsoft/typescript-lit-html-plugin 。目前主推的是这种方式。
const html = `
<style>
...
</style>
`;
customElements.define(
"custom-element",
class extends HTMLElement {
constructor() {}
connectedCallback() {}
disConnectedCallback() {}
}
);
以.html 文件为组件
结构和 Vue 相似,原先每个.vue 文件替换成 .html 文件,文件从根部分为 style、script、template 三个元素,前端通过简单的转换器注册组件,参考 std.js,好处是支持语法高亮、格式化、代码提示,缺点是注册时需要转换一下,不如.js 组件来的直接、无法初始化动态 html(需要引入自定义可执行标签,参考 std.js)。
<style>
:host {
}
</style>
<template>
<!-- html -->
</template>
<script type="module">
export default class extends HTMLElement {}
</script>
可执行的动态 html 元素
在以 html 文件为组件的情况下,经常需要用到模板引擎来提升效率,模板引擎指在静态的 html 中插入一些可执行的代码,用以动态生成 html 片段,这个代码可以是任何编程语言的表达式,市面上有数不胜数的模板引擎: https://expressjs.com/en/resources/template-engines.html ,我们使用自定义元素 z-z 来作为模板引擎。
<div>今天的日期是:<z-z>new Date().toLocaleDateString()</z-z></div>
<!-- 等同于 -->
<div>今天的日期是:12/19/2021</div>
数据绑定
数据到样式的单向绑定,通过按需更新 CSS 全局变量实现,如若需要修改元素的文本值则必须通过选择器来查找元素,请尽快熟悉这套操作,过渡阶段可以酌情使用静态引入 100KB 的 Vue 来实现 MVVM:https://cdn.jsdelivr.net/npm/vue@2。
统一 UI 风格
经过实测,Ant Design 这样基于 React 框架的 UI 库无法按需打包出单独的组件,因此需要针对特定组件进行 CSS 模拟,例如,本仓库中通过调整样式,将第三方表格库渲染成 Ant Design 的风格,后面我会逐一实现其他使用到的 Ant 组件。
兼容性
使用原生开发的应用在兼容上不如使用框架,因为无论 Vue、React、Angular 都偏向使用古老的语法和接口从而保证向下兼容旧版浏览器,但代价是代码量的翻倍,使用原生开发,并尽可能采用最新的语法和接口能够大大提升性能,代价是我们的用户必须安装最新的主流浏览器包括 Chrome、Edge、Firefox,但这也是无可厚非的,因为我们项目所依赖的虚幻引擎也要求使用 Chromium89 以上的 WebRTC 接口,因此安装最新浏览器(2 年以内的版本)是没有争议的必选项,无须考虑兼容性。
SSR 构建时
从前端生产线中剔除【打包构建(SSR)】这一过程是一种大胆的、极具挑战性、里程碑式的创新。首先,去掉脚手架和多余的依赖意味着文件体积大大降低,理论上不需要再压缩代码了,最多使用一下代码丑化工具(UglifyJS)去除空白字符和注释。然后,不到万不得已别使用 SSR 服务端渲染,引入 SSR 给整个生产线增加了一层“构建时”,增加了调试成本。前端开发的最优解是让生产环境和开发环境合二为一,让整个工程目录直接作为最终的可发布的代码,以字体图标的生产模式为例:与其新建文件夹来存放所有 svg 图标,同时新建一个打包脚本用来合成字体文件,不如只存一个字体文件,然后随时通过字体设计软件来编辑它————后者在发布的时候无须删除任何多余文件,更加方便。
HTTP2.0
前端打包工具能将多个 js 文件合并,在 http1.1 下能减少连接数,但在 http2.0 中则无需这个步骤,因为 http2.0 的多路复用能够并发地请求文件,因此后端开启 http2.0 静态文件服务是非常好的选择。
单页面应用
单页面应用并不是前端脚手架的独创,使用原生代码也能轻松实现,但是单页面既有优点也有缺点,对于小型应用单页面足够,对于我们中等规模的前端应用,适当分为 2~3 个独立页面即可。
虚拟 DOM
99% 的页面交互都不需要引入虚拟 DOM (既有优点也有缺点),只有当巨量 DOM 元素存在的时候,比如大型分页表格,这时才需要考虑虚拟 DOM,而常见的表格框架例如 ag-grid、tabulator、grid.js 都内置了虚拟 DOM,我们只要学会使用框架就好了。
仓库目录结构
待定
常用组件
下面列举常用的组件,对于那些很简单的组件,本仓库都提供了样例代码,稍微复杂点的组件(表格、图表)则使用我推荐的轻量框架。
表格:https://gridjs.io/
图表:https://www.chartjs.org/
按钮:原生 button 外加一些 CSS
开关:通过原生的 checkbox 实现
标签页、单选菜单:通过原生的单选按钮 radio 实现
多选折叠菜单:封装 details 元素
轮播图:通过 CSS 动画实现
弹窗:封装 dialog 实现
输入框:封装 input 实现
字体图标
首先并不一定需要引入外部的图标,因为用户的操作系统和浏览器中已经内置了几十万个 Unicode 图标,几乎可以涵盖所有场景,因此首选方式是从 Unicode 库中搜索可用的字符然后直接拿来用,有 2 种搜索方式可选:字符含义、字符形状:可以通过搜索引擎、输入法提示、emoji 表等各种途径来找到需要的字符,例如在百度上搜索“齿轮字符”就能找到“⚙”,因为每一个 Unicode 字符都有其独一无二的含义解释;也可以通过字符形状来进行图像识别,比如通过 https://shapecatcher.com/ 网站能够很方便地手画形状然后通过形状来搜索字符。当然,想要设计自己的图标也可以,页面上所有图标都以字体的形式存储,字体文件通过免费软件例如 FontForge、BirdFont 来打包合成,不用单独存储 svg 图片,图标可以上各大图标素材网站白嫖,也可以自己设计(但需要一些图形学知识)。
中文标识符
在业务密集型的代码片段中,用中文或其他非 ASCII 字符来命名变量、函数、属性、字段、参数、接口、键值是很好的设计模式,中文名能够提升可读性和可写性,还能减少注释,虽然看上去违反常规的编程习惯,但作为以“简易”为宗旨的解释型语言,JavaScript 从 2015 年开始支持 Unicode 标识符并鼓励这种写法:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Grammar_and_types 。