dev.to 的前端:一次性头脑风暴
添加有关 JavaScript 前端的开发人员文档 #2507
@提及评论框中的自动完成功能 #354
部署 Storybook #338
主题
目前有一个问题需要改进前端文档(参见前端 · 开发文档),以便用户更快地上手前端。非常感谢@rhymes提出这个问题!
添加有关 JavaScript 前端的开发人员文档 #2507
您的功能请求是否与问题相关?请描述。
在关于迁移到 TypeScript 并提高前端 DEV 代码库整体质量的想法、DEV 注意事项:不要忘记清除缓存!和https://github.com/thepracticaldev/dev.to/issues/2499之后,我认为如果有某种关于前端部分(特别是 JavaScript 层)如何组合在一起的概述会很好并且很有用。
我知道有两组 JS 代码库,由 Rails 的 sprockets 管理的app/assets/javascripts中的“遗留”代码库和由 webpack 管理的app/javascript中的“遗留”代码库。
除此之外,我不确定一切是如何运作的(我承认除了偶尔修复错误或小功能外,我没有在前端花费太多时间)。
可能会回答以下几个问题docs/frontend/javascript.md
:
- 初始化如何工作?
- Preact 层是否完全忽略了原始 JS 层?
- 两个 JS 代码库是否相互交互/调用?
- 它们是如何附加到模板页面的?每个网页是否提供两组打包/压缩后的 JS 文件?
- 在 dev.to 上下文中,Service Worker 做了什么?它在整个页面上都注册了吗?有多个 Service Worker 吗?
- 边缘缓存如何适应这一切?
- 即时点击如何适应这一切?
以及其他任何被认为重要的内容。不必非常深入,只需一张藏宝图,了解各个部分的功能以及它们如何组合在一起即可。
描述您想要的解决方案
包含 JS 前端工作原理描述的文档文件
描述你考虑过的替代方案
老实说,我还没有考虑过其他选择,现状还可以,只是如果有一些关于代码库的入职文档,对贡献者来说会更容易一些,特别是如果目标是重构它、使其现代化,甚至适应 TypeScript 或其他解决方案。
我决定写这篇文章,是因为我想为这个文档问题贡献一份力量,并认为它对包括我自己在内的所有人都有益。我希望大家能在评论区提问,或者补充文章中遗漏的内容。
香草 JS
该文件夹中包含大量前端代码库app/assets/javascripts
。这部分代码库不使用ES 模块。它负责加载脚本,在 DOM 加载完成后运行代码,在全局范围内运行代码,并为 dev.to 提供大量客户端功能。
资源通过标准的rails / fastly方法加载,这些方法添加了<script />
标签来加载前端代码。大多数(如果不是全部)脚本都会被延迟加载(请参阅<script>defer
中的属性:Script 元素 - HTML)。
Preact、webpacker 和 webpack
该应用程序也包含更现代的 JavaScript 部分,但它并非单页应用程序(SPA)。它是一组分散在关键位置的组件,例如搜索、v2 编辑器、用户引导等。
Preact 组件使用webpacker gem和webpack进行管理。如果你对 webpacker 感兴趣,可以联系团队中的@maestromac 。
Webpack 入口点的脚本已添加到 Ruby ERB 模板中,但它们使用 Webpackerjavascript_pack_tag
在服务器端添加脚本。有一个 Webpack 配置文件,但它是 yaml 格式的。该配置中包含一些设置,用于确定代码的位置以及如何定义入口点。
dev.to/webpacker.yml at master · thepracticaldev/dev.to · GitHub
...
default: &default
source_path: app/javascript
source_entry_path: packs
...
查看上面的配置,这部分前端代码库可以在app/javascript
文件夹中找到,并在文件夹中找到 webpack 入口点app/javascript/packs
。
这代表了 webpack 的基础配置。如果某个环境需要额外的配置,webpacker 允许你通过 webpack 配置导出来增强配置。
dev.to/development.js at master · thepracticaldev/dev.to · GitHub
const environment = require('./environment');
const config = environment.toWebpackConfig();
// For more information, see https://webpack.js.org/configuration/devtool/#devtool
config.devtool = 'eval-source-map';
module.exports = config;
随着项目的不断推进,希望看到客户端的更多东西变得预先实现(这是我刚刚编造的,轰!)。
@提及评论框中的自动完成功能 #354
功能请求或任务
作为用户,我希望能够输入 @ 并弹出下拉菜单。在这种情况下,这是预期的行为。
这意味着该框必须变成一个可编辑内容的 div,并且代码应该附加到每个框上。要实现这一部分,可能得费点功夫。
这应该用 Preact 编写,如果您觉得包含其他库有意义,欢迎讨论。我们对每个依赖项都要求严格,因为我们希望在这方面提高效率。这并不意味着您绝对不能使用库,但我们只想讨论一下如果这样做会有什么影响。
下拉菜单将使用 Algolia 搜索,我们可能需要在User
模型上添加适当的新自定义索引来实现这一点。
完成的定义
当所有内容框都包含下拉菜单,并且其行为在 GitHub 和 Twitter 等网站上正常运作时,即可实现此目的。核心团队会协助您解决此问题的细微之处。
Preact 在前端代码库中如何工作的示例
- 搜索入口点脚本通过 webpacker 加载
javascript_pack_tag
,例如<%= javascript_pack_tag "Search", defer: true %>
。
dev.to/application.html.erb at master · thepracticaldev/dev.to · GitHub
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<% title = yield(:title) %>
<title><%= title || "#{ApplicationConfig['COMMUNITY_NAME']} Community" %></title>
<% if internal_navigation? %>
<style>
...
</style>
<% else %>
...
<style>
..
</style>
...
<%= javascript_pack_tag "Search", defer: true %>
...
- 搜索栏在页面初始加载时也会在服务器端渲染。这就是我目前所说的 Preact 的 ghetto 服务器端渲染 (SSR)。我知道@ben曾经想过添加 Preact SSR,但当时优先级并不高。也许现在随着越来越多的组件使用 Preact 创建,它的排名会更高。
dev.to/_top_bar.html.erb at master · thepracticaldev/dev.to · GitHub
...
<div id="nav-search-form-root">
<div class="nav-search-form">
<form acceptCharset="UTF-8" method="get">
<input class="nav-search-form__input" type="text" name="q" id="nav-search" placeholder="search" autoComplete="off" />
</form>
</div>
</div>
...
- 在客户端,一旦 DOM 内容加载完毕,Preact 就会接管。
dev.to/Search.jsx at master · thepracticaldev/dev.to · GitHub
import { h, render } from ‘preact’;
import { Search } from ‘../src/components/Search’;
document.addEventListener(‘DOMContentLoaded’, () => {
const root = document.getElementById(‘nav-search-form-root’);
render(<Search />, root, root.firstElementChild);
});
- 从那时起,与搜索框的所有交互都是客户端的。
即时点击
正如其宣传语所说,“ InstantClick是一个 JavaScript 库,可以显著提升您的网站速度,在大多数情况下实现即时导航。” 它的工作原理是,当用户将鼠标悬停在超链接上时,他们很可能是想点击它。InstantClick 会在用户悬停在超链接上时开始预加载页面,这样当他们真正点击时,页面加载就已经是即时的了。请注意,在移动设备上,预加载从“ touchstart ”开始。
除了预取页面之外,InstantClick 还允许您自定义 InstantClick 页面发生变化时发生的情况。
dev.to/githubRepos.jsx at master · thepracticaldev/dev.to · GitHub
...
window.InstantClick.on('change', () => {
loadElement();
});
...
您还可以通过该属性决定是否在 InstantClick 加载的页面中重新执行脚本data-no-instant
。我认为代码库中没有任何示例将脚本重新执行列入黑名单。您也可以将链接列入黑名单。以下是代码库中的一个示例。
dev.to/buildCommentHTML.js.erb at master · thepracticaldev/dev.to · GitHub
...
function actions(comment) {
if (comment.newly_created) {
return '<div class="actions" data-comment-id="'+comment.id+'" data-path="'+comment.url+'">\
<span class="current-user-actions" style="display: '+ (comment.newly_created ? 'inline-block' : 'none') +';">\
<a data-no-instant="" href="'+comment.url+'/delete_confirm" class="edit-butt" rel="nofollow">DELETE</a>\
<a href="'+comment.url+'/edit" class="edit-butt" rel="nofollow">EDIT</a>\
</span>\
<a href="#" class="toggle-reply-form" rel="nofollow">REPLY</a>\
</div>';
} else {
...
有关更多信息,请参阅InstantClick 文档中的事件和脚本重新评估。
代码检查/代码格式化
eslint 和 prettier
该项目使用带有 Prettier 插件的 eslint。这意味着所有与代码格式化相关的 eslint 规则都由 Prettier 处理。在大多数情况下,我们使用扩展配置提供的开箱即用规则,但也进行了一些调整。
另外,如上所述,有些对象存在于全局作用域中,例如Pusher
。我们需要告诉 eslint 它已定义,否则它会抱怨它未定义。这时 eslintglobals
部分就派上用场了。
...
globals: {
InstantClick: false,
filterXSS: false,
Pusher: false,
algoliasearch: false,
}
...
哈士奇,棉绒阶段
代码库自带预提交钩子,允许我们在提交代码之前执行一些操作,例如运行 eslint。如果列表中存在可以修复的问题,它们将自动修复并提交。如果存在无法解决的问题,则提交失败,需要手动处理更改。
故事书
dev.to 前端代码库使用Storybook。它用于开发/展示组件。它的自定义配置可以在master · thepracticaldev/dev.to · GitHub 的 dev.to/app/javascript/.storybook中找到。
写故事书故事
Storybook 文档非常好,但如果您正在寻找一些示例,请参阅master · thepracticaldev/dev.to · GitHub 上的dev.to/app/javascript/src/components/stories。
Storybook 的用途
目前尚未部署到 Netlify,但存在一个未解决的问题。
部署 Storybook #338
任务
Storybook 可以在本地运行,但我也编写了一个脚本来为其生成静态网站。在执行此操作之前,我一直在等待项目开源。运行npm run build-storybook
,它将为 dev.to 的 Storybook 生成一个静态网站。它目前构建到./storybook-static
文件夹。您可以将该文件夹部署到任何位置。我假设是 Netlify,因为 dev.to 文档就在那里。
此外,您还需要为故事书网站的名称添加 DNS 记录,例如 storybook.dev.to
我还采取了预防措施,将此文件夹添加到.gitignore
完成的定义
作为 CI 的一部分npm run build-storybook
,它应该运行,如果失败(很可能是因为有人忘记更新),构建也应该失败。当 PR 合并到主分支时,上述情况仍然会发生,但./storybook-static
静态文件夹也应该部署到像 Netlify 这样的服务上,该服务会解析为网站 URL,例如https://storybook.dev.to。
如果您需要讨论其中任何内容,请随时联系我@maestromac或@benhalpern。
这部分代码库可能需要一些改进。对于有兴趣贡献代码的前端开发者来说,这里可能有很多唾手可得的成果,因为我相信其中有几个组件是 Storybook 中没有的。
主题
我没有参与这个项目,但我知道它大量使用 CSS 变量来设置主题,并提供了回退机制。这真是实现现代主题的好方法。
因此,所有可主题化的内容始终会应用 CSS 变量及其当前值(除非由于浏览器不支持 CSS 变量而只能采用后备)。
主题切换的神奇之处在于用户配置。在这里,我们可以看到夜间主题或粉色主题的样式应用情况。
dev.to/_user_config.html.erb at master · thepracticaldev/dev.to · GitHub
<script>
try {
var bodyClass = localStorage.getItem('config_body_class');
document.body.className = bodyClass;
if (bodyClass.includes('night-theme')) {
document.getElementById('body-styles').innerHTML = '<style>\
:root {\
--theme-background: #0d1219;\
--theme-color: #fff;\
--theme-logo-background: #0a0a0a;\
...
--theme-social-icon-invert: invert(100)</style>'
} else if (bodyClass.includes('pink-theme')) {
document.getElementById('body-styles').innerHTML = '<style>\
:root {\
--theme-background: #FFF7F9;\
--theme-color: #333;\
--theme-logo-background: #fff7f9;\
...
--theme-social-icon-invert: invert(0)</style>'
}
} catch(e) {
console.log(e)
}
</script>
所以,如果你正在为项目中任何 CSS 相关的内容做贡献,请记得在工作中是否需要应用主题。别害羞,如果问题中没有明确提到,可以直接询问。@venarius在这方面做了很多工作,所以他可能是个不错的主题相关咨询对象。
未知数
服务人员
我完全没有在代码库中开发过任何与 Service Worker 相关的功能,所以如果有人能分享一下它的使用情况就太好了😺。我知道它支持离线页面,画图会很有趣。感谢@aspittel在离线页面上的出色工作!另外,我相信它也做了很多缓存工作,但同样,我不太了解这部分代码库的所有细节。
边缘缓存和前端
我没有做过任何关于边缘缓存的工作,但我知道 dev.to 使用的是Fastly。我猜想所有前端都会在全球的 CDN 上进行大量缓存。@ben我觉得你可以更详细地解释一下这部分。😺
希望这能为大家提供更多关于 dev.to 前端的说明。👋
其他资源:
- 前端 · 开发文档
- ESLint - 可插入的 JavaScript linter
- Prettier · 自定义代码格式化程序
- webpacker,在 Rails 中,使用 Webpack 管理类似应用程序的 JavaScript 模块,下载webpacker的源码_GitHub_帮酷
- webpack
- GitHub - typicode/husky: 🐶 轻松使用 Git hooks
- GitHub - okonet/lint-staged:🚫💩 — 在 git 暂存文件上运行 linters
- Storybook:面向前端开发人员的 UI 组件研讨会
- InstantClick——让你的网站即时访问的 JS 库