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

man standing beside another sitting man using computer

不要内部创业,内部创业只会让你不幸

作为公司内的员工,你可能会被激励员工的“内部创业”所打动。但你要知道的是,你可以做内部创新、可以在螺狮壳里造道场,切不可在其中投入你的感情和心力。

这里的矛盾在于,你使用公司的资源做内部创业,你所有的产出,其实是归属一公司所有的。你可以用公司内部创业的路子去做所有你不那么看重的事情。但如果你真的看重一件事,且愿意为之付出极大的心力、劳力,那么一定要跳出来自己做。

跳出来,还有生的可能,留在企业内部,一定会死。

原因很简单,随着业务的高速发展、团队的扩大,创始人提供的价值会越来越少,慢慢跟不上自己的团队的迭代。这个时候公司有几个选择:

  1. 换一个负责人,你出局,不给你任何补偿,但业务可以更好的活下去
  2. 不换负责人,让你继续做,但可能业务慢慢挂掉。
  3. 换负责人,并给你一些赔偿:别想了,你以为是自己创业的么?公司怎么可能给你补偿。

这种变化,如果不是你心爱的产品还好,但如果是你心心念念的产品,一次就够你万念俱灰了。

既然都是风险,干嘛不出来做呢?

从另外一个角度来看,因为你是「内部创业」,那么你就必须遵循企业的大规范。如果企业的战略有调整,作为企业战略当中的一份子,你的业务必须被砍掉的时候,你应当如何自处?自然是跟随企业的战略调整,不然还能怎么办?

最后的结论你会发现,还是自己出来干最省心。

fan of 100 U.S. dollar banknotes

为什么我选择放弃生财有术?

今天,我想和你聊聊我从 2020 年开始加入生财有术,到 2021 年续费,再到 2022 年决定停止续费的心路历程。

心路历程

怀疑期(加入生财有术前)

在 2020 年之前,我其实是对生财有术不感冒的。和大多数人一样,我的理解是「既然有赚钱的事情,你为什么不留着自己做,而要分享出来」。

这也是生财有术在很长一个周期上在面对的外界的指责。

认可期(加入生财有术之后)

加入生财有术源自于一个契机,我朋友圈的老朋友,工程师,技术总监 —— 朱鹏飞同学在朋友圈发了生财有术的推广,于是乎我想了想,基于对他的信任,加入了生财有术。

我的目的也非常明确:我自己是一个工程师,是一个创造者。我擅长的是创造一个东西,而不是把一个东西推广的更好。生财有术中的经验贴可以帮助我看到别人是怎么思考同一个问题的,可以为我补充更多的信息。

于是乎,我在生财有术开始疯狂的了解信息,还参与了深圳和北京的活动,还因为一次轻享,获得了一颗龙珠,加入了龙珠群。

d2b5ca33bd970f64a6301fa75ae2eb22 20

生财有术提供了我想要的信息,帮助我理解了不一样的世界,看到了不一样的选择。

在这个阶段,我也领悟出为什么有的人愿意分享自己的心得给别人,总的来说,其实是「我知道这个东西能赚钱,但是这个钱太少了,我懒得做,顺手发给你也行」

基于这个思路,我还启发了一个人做了保罗 · 格雷厄姆的中文站点

反思期(现在)

但到了 2022 年,我为什么开始选择离开?其实核心点在于我意识到,生财有术提供的价值对于我来说,开始逐渐发生了变化。以及价格的上涨对于我来说,意味着 ROI 不符合预期。

一方面,生财有术开始从提供信息,到提供更加丰富的活动和交互转变。虽然依然会有提供信息,但对于我来说,活动所带来的溢价对我没什么价值,我并不太需要持续参与一个活动。

另一方面,得益于知识星球的机制,即使我不再续费,我依然可以看到之前我买的内容。现有的 3000 多篇精华我都没看完,完全不着急续费。

d2b5ca33bd970f64a6301fa75ae2eb22 21

从这个角度来看,知识星球真的不错。

不适合我,那适合谁?

在写这篇文章的时候,我也在想,既然不适合我了,那适合谁?毕竟,生财有术是一个社群,不可能适合所有人。

先说说为什么不适合我:我自己是有一些明确的标签的「工程师」、「创作者」,本身有在做个人品牌影响力方面的事情。对于我来说,我需要的是补充信息,丰富自己的品牌。当现有的信息已经无法处理的情况下,那么我完全没必要马上获得更多的新信息来满足自己 FOMO 的情绪。我需要的不是去发展更多的横向业务,而是让自己在纵向业务上发展的尽可能好。所以短时间来看,生财有术并不能提供我所需要的东西。

再说说适合谁:

  • 拥有自己业务的老板:我在生财有术的龙珠群里,里面有不少专家达人,如果你加入龙珠群,完全可以在其中达成一些合作,促进更好的业务的发展。
  • 没有自己形态的普通人:除了老板,绝大多数人是普通人,普通人的坏处是没什么特点,但好处是也有塑造的机会,而 2022 年生财有术会有不少的训练营和大航海、小航海,正适合普通人先抓住一个机会上车。

总结

唠唠叨叨这么多,总体来说,就是一句话 —— 不匹配了。但不匹配我,不意味着不匹配你,如果说上面我描述的这些你觉得符合你的诉求,那生财有术依然是一个符合你预期的社群。

0d5b1c4c7f720f698946c7f6ab08f687
如果看了这么多,你觉得生财有术适合你,那就大胆扫码加入吧!

woman in black and white zip up jacket sitting on black office rolling chair

关注用户的长期价值,而不是短期价值

在进行互联网产品设计时,常常会说,「我们要关注用户价值」,但似乎落实到现实中,大家都默认选择了一个更短时间的价值 —— 用户走过我的摊位,我要产生多大的交易额?用户访问了我的网站,给我产生了多少的收入?

多年「羊毛出在狗身上」的免费互联网模式,让我们下意识以为,我们只有「羊毛出在狗身上」才能够赚钱,我们不得不从流量大潮中分流出一部分,经过过滤,再将其回归到流量的大潮里。

这个思维有一个最大的问题是:我们提供给用户的价值是短暂的,是会停止的。但实际上,一个用户可能是会持续的使用你的产品的,在这个过程中,你是完全有可能在一个更长的周期产生出更大的价值。

不过这里也有一些问题在于,为什么过去的产品不会太过于关注这个问题?一方面是品牌建设并非绝大多数产品会考虑到的,甚至很多产品直到产品火了,才开始关注到品牌建设。另一方面是更长期的模式需要损失短期的收益,对于企业和产品来说,能否扛到看到长期收益是个问题。

不过,对于独立开发者而言,完全可以猥琐发育,直到看到产品的长期价值。

group of men running in track field

关于「竞争」,给年轻人的一些建议

很抱歉,虽然我年纪稍长,对于很多人来说,都还是一个小朋友,但依然要用一个非常老气横秋的口气来和你讨论关于「竞争」这个话题。因为有太多的人来问我关于竞争的问题。我不得不选择将我的观点和建议整理成一篇文章,以减少我重复介绍自己观点的次数。

竞争中的软门槛与硬门槛

在竞争中,常常被大部分人忽略的是,竞争并非是静态的。实际上,由于竞争中存在的软门槛和硬门槛的不同,使得竞争往往是动态的。

竞争中最常见到的是硬门槛,也是我们一般而言,给到新人的建议 —— 你必须去做某些事情,不然你就会付出极大的代价。比如,最常见的是,我们给到每一个年轻人的建议是你应该读完本科。如果你是专科,那就努力专升本。或者想办法走成人高考,获得一个本科的文凭。

d2b5ca33bd970f64a6301fa75ae2eb22 16
应聘中的软门槛和硬门槛

硬门槛的意义在于它真的可能在关键问题上,阻止你进行下一步。比如说,对于航天科技的核心工程师来说,相关专业的文凭就是硬门槛,如果没有对应的文凭,相关的企业和单位是不敢录用你的,因为你完全可能为航天事业埋下后患。

除了硬门槛,被很多人忽视的就是软门槛,以及,很多人会把软门槛认知为硬门槛。和硬门槛不同,软门槛不会成为你能够完成某个职位工作的阻拦,但软门槛会成为别人筛选你时的重要标准。在你求职时,你有没有某个证书、你有没有某个认证,都只是你求职过程中的软门槛。不要将软门槛看作是硬门槛。硬门槛是用来筛掉不适合的 60% 的人的,而软门槛则是用来选出想要的 20 % 的人。剩下的 20% 的人,就是那些符合了硬门槛,但无法跨过软门槛的人。

竞争是全方位的,但也是局部的

对于绝大多数的人来说,面对的竞争往往都是全方位的。你并没有什么异于常人的特点,你和你的竞争者并没有什么不同。这个时候,你们并没有太大的差距。但倘若你拥有一个和其他人不同的优势, 这个优势就会成为提升你综合评分而获得选中的重要因素。

d2b5ca33bd970f64a6301fa75ae2eb22 17
大家都相同的时候,你的竞争优势会帮你获得最终的成功

绝大多数的时候,竞争即会看你的所有竞争条件,希望尽可能选择一个足够好的候选人。另一方面,如果你在某些点上特别的突出,就能够让你自己成为被选中的佼佼者。

如果你的某个技能超出统一领域的其他人一个数量级的水平,那么即使你的其他能力不够强,依然可以获得青睐。但作为普通人的我们,应当有这样的自知之明,是否是这样的人。

d2b5ca33bd970f64a6301fa75ae2eb22 19
数量级的差距可以让你获得青睐

高学历带来的不一定是竞争优势,也可能是竞争劣势

对于一般来说,我们都认为高学历带来的是更大的竞争优势。但是,高学历也会为你的选择来到沉没成本。这些沉没成本会让我们失去一些可能性和选择。

当然,失去的可能性未必是坏事,也可能是一件好事。但作为选择的人来说,确实会有可能担心庙小容不下大佛而不愿意选择,将你从当前的选择中踢出。换句话说,高学历意味着某些事情,你再也做不了了。

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

silver mercedes benz emblem on blue surface

OneInStack 下部署 WordPress Multisite 的 Nginx 转发规则

在 OneIn Stack 下如果需要配置一套 WordPress MU 的话,需要配置 Nginx 相应的转发规则。具体的规则如下:

 if (!-e $request_filename) {            
 	rewrite /wp-admin$ $scheme://$host$uri/ permanent;            
 	rewrite ^(/[^/]+)?(/wp-.*) $2 last;            
 	rewrite ^/[^/]+(/.*.php)$ $1 last;    
 }    
 location / {        
 	try_files $uri $uri/ /index.php?$args;    
 }
Code language: PHP (php)

将上述规则放置在 /usr/local/nginx/conf/rewrite/wordpressmu.conf ,并在创建 VHost 时选择对应的主机即可。