前端开发指南
背景
Gitea 在其前端中使用Fomantic-UI(基于jQuery)和 Vue3。
HTML 页面由Go HTML Template渲染。
源文件可以在以下目录中找到:
- CSS 样式:
web_src/css/
- JavaScript 文件:
web_src/js/
- Vue 组件:
web_src/js/components/
- Go HTML 模板:
templates/
通用准则
我们推荐使用Google HTML/CSS Style Guide和Google JavaScript Style Guide。
Gitea 特定准则
- 每个功能(Fomantic-UI/jQuery 模块)应放在单独的文件/目录中。
- HTML 的 id 和 class 应使用 kebab-case,最好包含2-3个与功能相关的关键词。
- 在 JavaScript 中使用的 HTML 的 id 和 class 应在整个项目中是唯一的,并且应包含2-3个与功能相关的关键词。建议在仅在 JavaScript 中使用的 class 中使用
js-
前缀。 - 不应覆盖框架提供的 class 的 CSS 样式。始终使用具有2-3个与功能相关的关键词的新 class 名称来覆盖框架样式。Gitea 中的帮助 CSS 类在
helpers.less
中。 - 后端可以通过使用
ctx.PageData["myModuleData"] = map[]{}
将复杂数据传递给前端,但不要将整个模型暴露给前端,以避免泄露敏感数据。 - 简单页面和与 SEO 相关的页面使用 Go HTML 模板渲染生成静态的 Fomantic-UI HTML 输出。复杂页面可以使用 Vue3。
- 明确变量类型,优先使用
elem.disabled = true
而不是elem.setAttribute('disabled', 'anything')
,优先使用$el.prop('checked', var === 'yes')
而不是$el.prop('checked', var)
。 - 使用语义化元素,优先使用
<button class="ui button">
而不是<div class="ui button">
。 - 避免在 CSS 中使用不必要的
!important
,如果无法避免,添加注释解释为什么需要它。 - 避免在一个事件监听器中混合不同的事件,优先为每个事件使用独立的事件监听器。
- 推荐使用自定义事件 名称前缀
ce-
。 - 建议使用 Tailwind CSS,它可以通过
tw-
前缀获得,例如tw-relative
. Gitea 自身的助手类 CSS 使用gt-
前缀(gt-ellipsis
),Gitea 自身的私有框架级 CSS 类使用g-
前缀(g-modal-confirm
)。 - 尽量避免内联脚本和样式,建议将JS代码放入JS文件中并使用CSS类。如果内联脚本和样式不可避免,请解释无法避免的原因。
可访问性 / ARIA
在历史上,Gitea大量使用了可访问性不友好的框架 Fomantic UI。
Gitea 使用一些补丁使 Fomantic UI 更具可访问性(参见 aria.md
),
但仍然存在许多问题需要大量的工作和时间来修复。
框架使用
不建议混合使用不同的框架,这会使代码难以维护。 一个 JavaScript 模块应遵循一个主要框架,并遵循该框架的最佳实践。
推荐的实现方式:
- Vue + Vanilla JS
- Fomantic-UI(jQuery)
- htmx (部分页面重新加载其他静态组件)
- Vanilla JS
不推荐的实现方式:
- Vue + Fomantic-UI(jQuery)
- jQuery + Vanilla JS
- htmx + 任何其他需要大量 JavaScript 代码或不必要的功能,如 htmx 脚本 (
hx-on
)
为了保持界面一致,Vue 组件可以使用 Fomantic-UI 的 CSS 类。 尽管不建议混合使用不同的框架, 我们使用 htmx 进行简单的交互。您可以在此 PR 中查看一个简单交互的示例,其中应使用 htmx。如果您需要更高级的反应性,请不要使用 htmx,请使用其他框架(Vue/Vanilla JS)。 但如果混合使用是必要的,并且代码设计良好且易于维护,也可以工作。
async
函数
只有当函数内部存在await
调用或返回Promise
时,才将函数标记为async
。
不建议使用async
事件监听器,这可能会导致问题。
原因是await
后的代码在事件分发之外执行。
参考:https://github.com/github/eslint-plugin-github/blob/main/docs/rules/async-preventdefault.md
如果一个事件监听器必须是async
,应在任何await
之前使用e.preventDefault()
,
建议将其放在函数的开头。
如果我们想在非异步上下文中调用async
函数,
建议使用const _promise = asyncFoo()
来告诉读者
这是有意为之的,我们想调用异步函数并忽略Promise。
一些 lint 规则和 IDE 也会在未处理返回的 Promise 时发出警告。
获取数据
要获取数据,请使用modules/fetch.js
中的包装函数GET
、POST
等。他们
接受内容的data
选项,将自动设置 CSRF 令牌并返回
Response。
HTML 属性和 dataset
禁止使用dataset
,它的驼峰命名行为使得搜索属性变得困难。
然而,仍然存在一些特殊情况,因此当前的准则是:
-
对于旧代码:
- 应将
$.data()
重构为$.attr()
。 - 在极少数情况下,可以使用
$.data()
将一些非字符串数据绑定到元素上,但强烈不推荐使用。
- 应将
-
对于新代码:
- 不应使用
node.dataset
,而应使用node.getAttribute
。 - 不要将任何用户数据绑定到 DOM 节点上,使用合适的设计模式描述节点和数据之间的关系。
- 不应使用
显示/隐藏元素
- 推荐在Vue组件中使用
v-if
和v-show
来显示/隐藏元素。 - Go 模板代码应使用
.tw-hidden
和showElem()/hideElem()/toggleElem()
来显示/隐藏元素,请参阅.tw-hidden
的注释以获取更多详细信息。