分类目录归档:技术

code 1076536 640

那些影响你前端开发体验的问题(1)

最近在研究和体验一些 GitHub 上的前端项目,遇到了一些让人体验不佳的点, 这里梳理一些我遇到的,一方面是留存,记录那些让我体验不好的事情。另一方面警示自己在开发前端项目的时候应该注意一下开发体验,确保不会让别人在开发的时候也遇到这些问题。

1. 项目应该有文档

这是不少 GitHub 上面项目的普遍问题。现有的文档要么是以 Framework 生成的 Readme 为主,要么直接没有文档。

但没有文档对于后来者的开发其实非常不友好,比如几个常见问题:

  1. 如何启动一个 Server & 如何设置后台 API 的 Prefix?
  2. 项目的组织结构是什么样的?
  3. 涉及到的第三方服务应该如何配置?

上面这些文档是帮助后来者快速使用你的项目所必需的(换句话说,如果你要做开源项目,想要打造开源社区,这也是必要的)。

2. 项目控制算法复杂度

我在开一些前端项目的时候,明显会感到浏览器的响应变慢了,我以为是我的 Mac Mini 2012 性能不行。但我打开自己的博客,依然如丝般顺滑,我就明白了。问题不在于我的电脑性能跟不上,而是这个前端页面加入了大量的有用或无用的代码,使得 CPU 需要大量的计算。

虽然进入 SPA 的时代以后,我们开始将算力的使用从服务端往客户端转移,但也建议各位工程师控制自己项目的复杂度,不要一开网页,风扇就呼呼的转。浏览体验不好,还凸显自己的技术不行。

3. 使用 debuuger 调试,而不只是 console.log

在编译型语言当中,使用 Debugger 来进行调试是一项基本技能,因为需要在运行时去看不同的变量的值,以此来完成调试。

d2b5ca33bd970f64a6301fa75ae2eb22
爆炸的 Console

但在前端领域,因为 console.log 的过于好用,大家开始习惯于使用 console.log 来打印变量,但对于大型项目来说,大量的 console.log 也会让你的项目输出许多无意义的内容,让整个项目的调试和开发受到阻碍。

总结

最近看项目,总结了三条自己觉得影响体验的点,后续随着看的项目的深入,会逐渐总结出更多影响体验的点,引以为鉴。

grayscale photo of person using MacBook

WordPress Contact Form 7 的正确用法

Contact Form 7在实际使用过程中,有些人为了简化表单的设计和使用,会选择直接在多个地方使用同一个表单,并通过页面选择器来实现不同的样式(常见于基于 Elementor 构建的页面)。

但这样会造成长期的可维护性问题:

  1. 每个页面单独写样式选择器来控制样式,且在样式命名方面没有习惯的话, 会导致页面的样式混乱,修改成本较高。
  2. 不同的页面的样式采用了相同的类名,在进行样式修改时,交接性极差。

如果想要解决这个问题,有两个方式:

  1. 规范页面样式类规范:不同风格和样式的按钮采用不同的类名,如(.blueBtn.yellowBtn);
  2. 多处使用的表单,采用复制的方式,而不是使用同一个表单:Contact Form 7 提供了 Duplicate 功能,可以非常方便的复制一个表单,并根据表单的位置来命名。降低配置表单的成本。
d2b5ca33bd970f64a6301fa75ae2eb22 14
Duplicate 一个表单
black laptop computer

从一封钓鱼邮件聊起:针对普通人的钓鱼邮件设计

收到了一封钓鱼邮件,刚好最近没有什么内容要写,就聊聊这一封钓鱼邮件。

我收到的这一封钓鱼邮件是这样的

d2b5ca33bd970f64a6301fa75ae2eb22 9

接下来看看里面的钓鱼邮件设计的三个巧妙之处:

1. 针对独立域名的钓鱼邮件

我的对外的邮箱目前使用的是 [email protected] 的邮件,而由于 Linux.com 邮箱设计,实际上并不会有一个邮箱给你使用,而是你可以选择一个邮箱地址,系统会将发送到这个地址的邮件自动转发给你,我将邮件转发到了我自己的 Google 邮箱当中。

所以实际上我收到的邮件有两种:以 gmail 地址收到的邮件和以 linux.com 地址收到的邮件。

可以看到,上面的这个邮件当中我的收信地址是 linux.com 的地址,而不是我的 gmail 地址。

这正是这封邮件设计的巧妙之处:为特定人群发送特定内容的钓鱼邮件。试问自己:如果你的 QQ 邮箱收到了上述的邮件,你会把他当成是企业给你发送的安全邮件么?显然不会,因为你知道, QQ 不会给你发送这样的邮件。

但如果你的邮箱刚好是一个自定义域名,且刚好你所在的企业的 IT 并没有拦截到这封邮件,那么这封邮件对于那些安全意识不高的人来说,马上就会中招。

2. 使用了一个内网的地址来降低警惕度

对于绝大多数人来说,可能对于内网地址和外网地址没感知。可能直接就点击进去了。但对于一些对于计算机网络略有耳闻的人来说,可能会熟记的一个地址是 192.168.0.1,这个地址被不少的路由器作为默认的地址和网关地址来使用,从而成为不少人的心中的安全地址。

邮箱中的 192.168.22.23 这个地址就会让一些人放下警惕,然后点击进去查看内容 —— 然后成功的掉进陷阱中。实际上邮件只是用内网地址来作为一个表面展示的文字,真正的链接地址是 http://szfdxled.com/function/uploadfile/20220412/20220412000529_78464.html#[email protected]

这提醒了我们,如果邮件当中有链接,最好复制出来,而不是直接点进去(说不定别人替换了呢?)

d2b5ca33bd970f64a6301fa75ae2eb22 10

3. 自动识别的邮箱域名

我点击链接进去以后注意到,他在顶部加入了对应的邮箱域展示和 Icon 的展示。设计的挺巧妙。

如果点击进去展示的是无关的域信息,可能你也不会点击进去查看。但如果展示的是你自己的邮件域,会进一步放松警惕(特别是你以为你点击进去的其实是 192.168.xxx.xxx 时)。

对于意识不太强,或者平时不常使用网页版的人来说,可能真的就直接输入账号密码登录了。我还试了试,如果把 URL 后缀的邮箱域修改了,还会自动替换邮箱域和对应的 icon(应该是抓的 favicon,但不知道为啥抓到的是这个。

d2b5ca33bd970f64a6301fa75ae2eb22 11

不过,也有一些设计的比较蠢的地方

1. 用了 Reply To 的字段暴露了自己的信息

在看这封邮件的时候,我注意到他设定了 Reply To 字段。Reply To 当中暴露了自己的 QQ 邮箱。

d2b5ca33bd970f64a6301fa75ae2eb22 12

然后我搜了一下,发现能搜到这个人,看起来似乎还像是正常使用的 QQ 号。。。如果是一个经验丰富的 Cracker ,可能会选择使用一个更加安全的沟通方式。

d2b5ca33bd970f64a6301fa75ae2eb22 13

总结

这封钓鱼邮件中给我不少的启发,里面的一些设计也很有意思。但对于有充足安全意识的人来说,这些问题确实都可以规避掉,从而不受诈骗的影响。此外,Cracker 的产品设计值得我们学习,一些好的设计,确实可以帮助用户更省事。

turned-on monitor

Re:Build 产品重构计划 #5:效率至上

Hi,,你好,

距离上次发 Newsletter ,已经过去了大半个月时间。在过去的时间里,我着力在为自己打造一些效率工具,来优化我自己的工作流。

仔细想想,其实在过去的很长时间里,我也都是在做这件事:

  • Logoly 是为了更快的设计出一个简洁大方的 Logo。
  • WordPress 中文语法检查插件是为了不用再复制文章内容到微信公众号/石墨进行语法检查。

而在过去的大半个月时间里,我为自己打造了两款工具:「Copy To Wechat」和「Filter Featured Image」。

Copy To WeChat

一直以来,我的博客更新频次都高于微信公众号,主要的原因是我更喜欢 WordPress 后台的编辑体验和这种内容尽在掌握中的体验。但 WordPress 的文章发布到微信公众号因为无法直接同步,十分的麻烦,也导致我很少更新微信公众号。

为了降低自己发微信公众号的阻力,我开发了一个新的插件 —— Copy To Wechat。

Copy To Wechat 提供了一个更加简单进行微信公众号排版的方式 —— 在文章最后加上一句 ?wx 就可以自动应用提前设置好的微信公众号样式,并在界面上生成几个复制按钮。这样在发布微信公众号时,可以更加快捷的复制格式化好的内容到微信公众号,缩短发布一篇文章的时间。

d2b5ca33bd970f64a6301fa75ae2eb22 2

就我自己而言,发布一篇公众号文章从过去的 5 分钟,缩减为如今的 1 分钟。我发微信公众号的阻力也变得更小。

现在,这个插件已经上架到了 WordPress 官方市场,如果你也希望使用这个插件,欢迎前往 WordPress 官方市场查看这个插件。此外,我还使用芦笋准备了一个简单的教程,帮助你更快的上手使用这个插件。

Filter Featured Image

随着图床问题的解决,我现在在 WordPress 后台写文章时,都会加上特色图像。但我的博客写了接近五年,历史文章很多没有特色图像,如果想要获得一个比较好的体验,就不得不一篇篇翻过去,查看是否有特色图像,并设置特色图像。整个流程颇为繁琐。

为此,我开发了 Filter Featured Image: 一个支持筛选特色图像的插件,安装后,会在文章列表多一个筛选工具,可以用来筛选是否拥有特色图像的文章。

d2b5ca33bd970f64a6301fa75ae2eb22 6

再配合 Featured Image Admin Thumb 插件,现在我只需要筛选出所有已发布文章中没有特色图像的图片,就可以实现在文章列表中设定特色图像,大大加快了我设置特色图像的进度。

d2b5ca33bd970f64a6301fa75ae2eb22 3

按照目前的情况来看,我有望在下周把我没有设定特色图像的 487 篇文章都设置上合适的特色图像、标签。如果你想试试看,可以参考我使用芦笋制作的 2 分钟的教学视频

总结

写两个插件看起来耗费了不少的时间,但也给了我更多的可能 —— 过去我需要五分钟才能做到的事情,现在只需要一分钟,可以做更多的事情;过去我没做好的事情,现在可以更简单的完成。每踩一个坑,都是我一篇新的博客文章。

虽然看起来似乎 ROI 不高,不过想想也不亏不是么?

9a1f326b911de6c1629837f3b57551e5 1

在项目中使用 Dead Simple LESS CSS Watch Compiler 来自动生成 css 文件

最近在写一个 WordPress 主题来帮助我完成从 WordPress 到微信公众号的实现。在这个过程中,我需要借助于一些 CSS 的超集,来帮助我完成样式的编写。考虑到 SCSS 的 C++ 依赖问题,我选择了 Less 来完成。但如果直接使用 lessc 的话,主要面临的问题在于无法检测文件更新,这样对于需要实时查看效果的我来说,是比较麻烦的。所以我选择使用 Dead Simple LESS CSS Watch Compiler 来完成自动监控文件变化并刷新的功能。

教程

安装

执行 npm 命令来安装 Dead Simple LESS CSS Watch Compiler

yarn global add less less-watch-compiler 

安装完成后,你就可以执行命令来监听文件的变化。

配置

这里为了方便,我在 WordPress 插件目录中初始化了 npm, 因此,可以非常方便的借助于 npm script 来完成命令的配置。

通过配置了单独的 Build 命令,实现了执行 npm run build 就会自动监听 less 文件夹下的文件,并转换成对应的 css 文件,放置在 css 目录中。

{
  "private": true,
  "scripts": {
    "build": "less-watch-compiler ./less ./css"
  },
  "devDependencies": {
    "less": "^4.1.2",
    "less-watch-compiler": "^1.16.3"
  }
}
Code language: JSON / JSON with Comments (json)

其他

如果你需要对 less 运行有更多配置的诉求,还可以创建一个 less-watch-compiler.config.json 来配置具体的执行目录。不过我对于这部分没有要求,就直接整个目录来进行配置了。

{
    "watchFolder": "<input_folder>",   
    "outputFolder": "<output_folder>",
    "mainFile": "<main-file>",   
    "includeHidden": false,
    "sourceMap": false,
    "plugins": "plugin1,plugin2",
    "lessArgs": "option1=1,option2=2",
    "runOnce": false,
    "enableJs": true
}

总结

SCSS 因为 node-scss 的编译问题被各种吐槽,虽然换成了 dart-scss ,但历史的阴影还在。选择了 less 后,通过一些配置,可以让我自己的开发变得更加简单。何乐而不为?

f8ef57b3b0e3c827d9f168f4a491ddc8

配置 Vercel 优化大文件缓存性能

Excalidraw 是一个非常好用的手绘风格的绘图工具,我给自己部署了一个版本来降低自己写作配图的难度。如果你感兴趣,可以访问 draw.ixiqin.com 体验我自己维护的版本。

白宦成

在配置 Vercel 的中文字体以后出现了一个很大的问题:中文字体文件巨大。因为和英文只有 26 个字母不同,中文想要让常见文字都要放在其中,则需要一个巨大的字库来支持不同文字的展示。也因此使得中文字体远大于英文字体。即使不同的文件类型会有优化,但近 100 倍的文件大小差距还是会让整个应用加载速度极慢。

d2b5ca33bd970f64a6301fa75ae2eb22 14

不过,好在字体是一个相对稳定的文件, 不会经常变化,因此我们可以借助浏览器和 HTTP 协议中设计的缓存能力来优化体验。通过对较大的字体文件进行持久缓存,来确保非第一次加载后的使用体验。

Cache-Control

 Cache-Control 通用消息头字段,被用于在http请求和响应中,通过指定指令来实现缓存机制。缓存指令是单向的,这意味着在请求中设置的指令,不一定被包含在响应中。

想要实现对于文件的缓存控制,少不了使用 Cache Control 头来进行控制。以我为例,我为这些字体配置的 Cache Control 的值为 public, max-age=31536000, immutable,这意味着:

  1. 我的字体文件可以被人任意对象缓存(public):这样 Vercel 的 CDN、用户的浏览器都会帮助我将字体文件进行缓存,提升访问的体验。
  2. 我的字体文件要缓存 365 天(max-age=31536000):我的字体文件可以缓存一年(实际上更长都无所谓,因为字体不会经常变化)
  3. 不主动发起校验请求(immutable):如果浏览器支持,则在读到 immutable ,不会再向服务器发起校验请求头(比如 If-None-MatchIf-Modified-Since)。关于这个属性,可以参考 https://datatracker.ietf.org/doc/html/rfc8246

经过上述的配置,在标准的浏览器中,当他们处理到我的字体文件时,就会将字体文件进行缓存,留存至本地,并在用户显式清理缓存之前,始终使用缓存中的字体文件来进行加载,从减少需要从服务器端加载的文件的数量,提升网站的访问体验。

在 Vercel 中配置

在 Vercel 中如果需要控制不同的文件的 Header,则需要修改项目根目录下的 vercel.json 文件,在其中配置 headers ,匹配要处理的文件,并设置特定的 Header。

{
  "public": true,
  "headers": [
    {
      "source": "/(.*).(ttf|otf|woff2)",
      "headers": [
        {
          "key": "Cache-Control",
          "value": "public, max-age=31536000, immutable"
        }
      ]
    }
  ]
}
Code language: JSON / JSON with Comments (json)

具体代码可以参考:https://github.com/bestony/excalidraw/commit/50e48fd054ccb5fe6e8fe302d135e8f643ed20eb

9a1f326b911de6c1629837f3b57551e5

为 Excalidraw 添加中文手写字体

Excalidraw 是一个非常好用的手绘风格的绘图工具,我给自己部署了一个版本来降低自己写作配图的难度。如果你感兴趣,可以访问 draw.ixiqin.com 体验我自己维护的版本。

白宦成

我在使用 Excalidraw 时,最大的的问题就是没有中文的手写字体,这也是促使我自建的一个重要的原因。

d2b5ca33bd970f64a6301fa75ae2eb22 15
效果

中文手写字体我选择的是 小赖字体 ,小赖字体可以免费商用,对于我这样的用途来说,更加的安全。

原理

实现的原理并不复杂,在页面中引入对应的字体文件,并为对应的字体设置对应的 Font Family 即可。

添加字体

在 Excalidraw 中添加字体,需要在 public 目录中加入你的字体文件,并在 public/fonts.css 中添加对应的字体引入,这样后续你的应用中就可以使用对应的字体来进行绘图。

需要注意的事,这里的 Font-Family 的值不要瞎填,后续会用到。

@font-face {  
	font-family: "XiaolaiSC";  
	src: url("XiaolaiSC-Regular.ttf");  
	font-display: swap;
}
Code language: CSS (css)

此外,为了让浏览器可以提前加载字体,还可以在 public/index.html 中添加如下代码来实现预加载。

<link rel="preload" href="XiaolaiSC-Regular.ttf" as="font" type="font/ttf" crossorigin="anonymous" />
Code language: HTML, XML (xml)

添加常量

在 Excalidraw 中,组件使用的字体被定义在 src/constants.ts 中的 FONT_FAMILY 常量中,你需要在其中添加相应的 Font。这里就会用到你刚刚填写的 Font Family

export const FONT_FAMILY = {
    Virgil: 1,
    Helvetica: 2,
    Cascadia: 3,
    XiaolaiSC: 4, // 这一行是新增的
};
Code language: JavaScript (javascript)

在控制台上添加对应的调用

当你完成了这些基本的配置后,最后就简单了,只需要在 src/actions/actionProperties.tsx 中的 actionChangeFontFamily 中添加对应的 value 即可实现新字体的引入。

{
    value: FONT_FAMILY.XiaolaiSC,
    text: "小赖字体",
    icon: <FontFamilyLaiIcon theme={appState.theme} />,
},
Code language: JavaScript (javascript)

参考代码:https://github.com/bestony/excalidraw/commit/f308dc32a958e4cb4fb4658cd9a5c9a19ad6d683

9a1f326b911de6c1629837f3b57551e5

移除 Excalidraw 右上角的 Plus 连接

Excalidraw 是一个非常好用的手绘风格的绘图工具,我给自己部署了一个版本来降低自己写作配图的难度。如果你感兴趣,可以访问 draw.ixiqin.com 体验我自己维护的版本。

白宦成

Excalidraw 在右上角加入了他们的 Plus 版本服务的连接。不过我是自己使用,所以就不需要这样的导流连接,因此,可以通过修改 src/excalidraw-app/index.tsx 文件,将其中的代码进行替换,即可实现移除 Plus Link

原版

const PlusLinkJSX = (
  <p style={{ direction: "ltr", unicodeBidi: "embed" }}>
    Introducing Excalidraw+
    <br />
    <a
      href="https://plus.excalidraw.com/plus?utm_source=excalidraw&utm_medium=banner&utm_campaign=launch"
      target="_blank"
      rel="noreferrer"
    >
      Try out now!
    </a>
  </p>
);
Code language: JavaScript (javascript)

移除后

const PlusLinkJSX = <></>;
Code language: JavaScript (javascript)
9a1f326b911de6c1629837f3b57551e5

自定义 Excalidraw 的字体大小

Excalidraw 是一个非常好用的手绘风格的绘图工具,我给自己部署了一个版本来降低自己写作配图的难度。如果你感兴趣,可以访问 draw.ixiqin.com 体验我自己维护的版本。

白宦成

Excalidraw 默认的字体整体比较小,对于我来说,习惯将图画的大一些,这样在不同的设备上,都可以方便的看清楚图片的内容。

修改 S、M、L、XL 对应的字体大小

Excalidraw 将字体大小的变化定义在 src/actions/actionProperties.tsx 当中,因此,你需要修改不同按钮具体的字体,则需要修改这里的配置。

d2b5ca33bd970f64a6301fa75ae2eb22 12
字体大小修改工具

将下方代码中的 options 中 value 修改为你需要的值,即可实现不同的字体大小。

export const actionChangeFontSize = register({
  name: "changeFontSize",
  perform: (elements, appState, value) => {
    return changeFontSize(elements, appState, () => value, value);
  },
  PanelComponent: ({ elements, appState, updateData }) => (
    <fieldset>
      <legend>{t("labels.fontSize")}</legend>
      <ButtonIconSelect
        group="font-size"
        options={[
          {
            value: 20,
            text: t("labels.small"),
            icon: <FontSizeSmallIcon theme={appState.theme} />,
            testId: "fontSize-small",
          },
          {
            value: 28,
            text: t("labels.medium"),
            icon: <FontSizeMediumIcon theme={appState.theme} />,
            testId: "fontSize-medium",
          },
          {
            value: 36,
            text: t("labels.large"),
            icon: <FontSizeLargeIcon theme={appState.theme} />,
            testId: "fontSize-large",
          },
          {
            value: 50,
            text: t("labels.veryLarge"),
            icon: <FontSizeExtraLargeIcon theme={appState.theme} />,
            testId: "fontSize-veryLarge",
          },
        ]}
        value={getFormValue(
          elements,
          appState,
          (element) => {
            if (isTextElement(element)) {
              return element.fontSize;
            }
            const boundTextElement = getBoundTextElement(element);
            if (boundTextElement) {
              return boundTextElement.fontSize;
            }
            return null;
          },
          appState.currentItemFontSize || DEFAULT_FONT_SIZE,
        )}
        onChange={(value) => updateData(value)}
      />
    </fieldset>
  ),
});
Code language: PHP (php)

修改默认字体大小

默认字体的大小被定义在 src/constants.ts,你可以修改其中的 DEFAULT_FONT_SIZE 来实现控制默认情况下的字体大小。

export const DEFAULT_FONT_SIZE = 28;
Code language: JavaScript (javascript)

参考代码:https://github.com/bestony/excalidraw/commit/78d2d103b40c48bdccbecd7deb85f1fd0d6b4d2f

https://github.com/bestony/excalidraw/commit/14bca18aa6395280cfda15202beaf56dc966a401

f8ef57b3b0e3c827d9f168f4a491ddc8

解决 Vercel 在国内访问被墙的问题

Vercel 因为被大量使用,自然而然被墙掉了,不过好在 Vercel 官方提供了单独的 IP 和 CNAME 地址给大家,对于国内的用户来说,配置一下单独的解析,依然可以享受 Vercel 提供的服务。

A记录地址:76.223.126.88

CNAME 记录地址:cname-china.vercel-dns.com

来源:https://www.vercel-status.com/incidents/r758bhbklgfd