分类: 技术

各种开发心得,包括语言、软件工程、开发工具等

  • 搬家记:从 Vercel 到 Cloudflare(Nuxt 项目x2)

    搬家记:从 Vercel 到 Cloudflare(Nuxt 项目x2)

    月底的时候,收到 Vercel 的邮件,提示我账号内有一些额度用量已经消耗 50%。我没在意,心想反正月底,马上不就归零了么?第二天起来越想越不对,我的 Vercel 是付费的 Pro 账号,计费周期是上月 22 日到本月 22 日,这才月底,才一周多,怎么会就用掉 50%?

    于是我回想起之前收到 Vercel 的邮件,说以后我每个月的费用会上升 30+美金,不由得背后一凛。赶紧上 Vercel 后台查看,发现 Vercel 开始用新的计费单位:Function Invocations。且 4 月 25 日起,新开通 Pro 的用户会使用这个方式计费;而 5 月 25 日(即上月底),我们这些老 Pro 用户也会采用这个方式计价。

    很明显,新计价单位比老的方式严苛很多,以至于我之前一半都用不掉的套餐费用,现在连半个月都撑不住。公平一点来说,我的项目都是 Nuxt,要走 Serverless Function,所以消耗可能比 Next.js 走 Edge Function 要高。但是,无论如何,这个价位都让我们动起了搬家的念头。目标毫无疑问,就是 Cloudflare。

    具体额度可能随时有变化,我就不在这里贴数字了,简单给大家划个标准:我在 Vercel 每月 $20 的额度,大约对齐 Cloudflare 的免费版;Cloudflare 每月 $5 的套餐额度,足够我 $50 Vercel + $5 Upstash,还有一大堆我没用起来的服务。

    单纯算经济帐,肯定是 Cloudflare 划算得多。但是 Vercel 行走江湖,靠的就是一手优秀的 DX,尤其在我搬家折腾完之后,更是这么觉得。整体过还比较顺利,但是坑也真没躲过去。接下来分享一些踩坑经历,希望对大家有帮助。

    第一步,创建项目。在 Cloudflare Pages 导入 Git 仓库即可,操作跟 Vercel 基本一致,无需详述。接下来,我们就有了部署在 xxx.pages.dev 子域名的网站,建议测试充分之后再切换域名。

    第二步,解决各种 [unenv] 问题。这个问题是由于 Cloudflare Worker 运行时精简(删掉)了一些 Node.js API 导致的。可能发生在各个环节,从部署到执行都有可能。我遇到的问题主要是:

    1. ioredis。因为我使用 Upstash Redis,所以直接换 @upstash/redis/cloudflare 然后使用 http endpoint 即可解决。两者的 API 有些许区别,不过功能性差异不大,替换起来不麻烦。
    2. Nitro Stroage。其实跟上面是一个问题,只不过表现不太一样。如果通过 nuxt.config.js 配置 Storage 使用 Redis,那么部署那一关都过不去……
    3. crypto.createHash。这个暂时没找到好办法,主要是 TiDB Serverless 最初使用 Digest 认证,所以我才必须使用。现在他们支持 Basic Auth 认证,所以我直接去掉对这个 API 的依赖就好了。

    第三步,安排 Cron job。这一步目前我也还没做好。Cloudflare Pages 不支持 Cron job,只能在 Workers 里安排 Cron Trigger。所以比较合理的做法是,建立一个统一的 cron worker,定时运行,粒度控制在比较合理的范围内,然后通过这个 worker 去调用分散在各个项目中的 cron job。

    于是,由于我们暂时无法离开 Vercel 的 Cron job,所以我们就还不能关掉 Vercel 上的项目。那么我们就必须开一个分支出来进行开发。好在 Cloudflare 可以支持指定特定分支作为 Production 的做法,所以这方面也没有问题。

    哦,对了,Pages 不支持在 wrangler.toml 里定义环境变量,以及我们在 Pages 里修改环境变量也不会触发重新部署,这点和 Worker 不一样,需要注意。

    最后,就是改变域名指向。我们必须在 Workers & Pages > 刚才的项目 > Custom Domains 配置自定义域名,仅在在 Websites > CDN 里配置是不够的,请大家注意。另外,由于 Nuxt 是 SSR 框架,所以我们必须关闭位于 Speed > Optimization > Content Optimization 的 “Auto Minify”,否则,可能会遇到重复的页面内容。

    还有一点,与 Vercel 不同,Cloudflare Pages 只包含 Edge 网络,并没有前面的 CDN 层。所以在 Cloudflare Pages 里增加缓存必须通过 Cloudflare CDN 来进行,同时 CDN 必须开启 Proxied,否则,原本在 Vercel 有效的缓存头就只能在浏览器里工作了。

    以及,Cloudflare Pages 没有提供页面重定向等功能,所以原本放在 Vercel.json 里的一些东西可能就要我们自己实现了。

    总结

    按惯例来总结一下。Cloudflare 的套餐性价比非常高,同样用量,价格可能 Vercel 的 1/10,还能覆盖其它服务。所以值得一试。但是问题也不少,比如运行时被进一步缩减、不支持 Cron job、没有 CDN 层、没有更高层级的 Headers 控制,等等,所以在使用时,体验可能还是略逊于 Vercel。尤其是迁移搬家,更要注意。

    好了,如果你对全栈开发,网站基础设施感兴趣,有问题有疑问,欢迎留言讨论。

  • 移动网页高度自适应最佳实践

    移动网页高度自适应最佳实践

    移动 Web 开发就要在“螺蛳壳里做道场”。移动设备限于屏幕尺寸,不得已左支右绌,既要多呈现内容,又要保证功能不要缺失。普通内容类网页还好,自然往下滚就行了;开发 Web App 的时候,当我们因为某种原因,需要限制滚动区域的时候,就很难处理。

    这篇文章会分享我的一些经验,希望能节省大家摸索的时间。

    无用单位:vh, svh, dvh, lvh

    很早以前,我们就有了 vwvh 单位,分别指代视窗宽高的 1%。需要注意的是,移动浏览器的视口包含被各种组件占据的部分,所以 vw 就比较好用,因为没有干扰;但是高度上,被通知栏、地址栏等占用的“临时”空间就会成为我们的麻烦。

    为了妥善利用屏幕空间,在我们上下滚屏的时候,大多数手机浏览器都会把地址栏、工具栏、或者通知栏隐藏起来,这就导致浏览器的可视面积其实会不断变化。原本就没用的 vh 便更没用了……于是后面新增了 svhdvhlvh 三种 长度单位,但其实帮助不大,因为当我们需要限制容器高度的时候,通常来说就不能让页面自由滚动。

    因为这几个长度单位过于没用,所以我就不详细介绍了。感兴趣的同学可以看下 TailwindCSS 里的演示:https://tailwindcss.com/docs/height#viewport-height

    虚拟键盘则让这个问题雪上加霜,因为虚拟键盘的显示和隐藏都不会影响这几个长度单位,所以当我们需要手动控制容器高度、位置的时候,就会很难做。

    最佳实践:常规页面,交给浏览器

    首先我们要信任浏览器,能够留给浏览器处理的,尽量交给浏览器原生处理。

    比如,常规页面,长一点,留给浏览器自然滚动。文本框输入的时候,浏览器会自动聚焦和滚动,通常情况下没什么问题,基本体验有保证。

    最佳实践:输入框文字不小于 16px

    如果文本框 font-size 小于 16px,iOS Safari 下,当文本框获得焦点,Safari 会自动放大整个页面;而失去焦点的时候,页面并不会自动缩小到 100%,所以就很蛋痛。

    解决方案有几个:

    1. 取消缩放。会使得可用性评价恶化,不推荐。
    2. blur 时自动恢复 100%。增加特性就是增加 bug 的可能,我觉得能不用就不用。
    3. 保持字体大小。应该大部分时候都更简单有效。

    最佳实践:使用 dvh 并解决兼容问题

    虽然但是,当我们需要固定高度的时候,表示视窗净高度的 dvh 仍然是我们最佳选择。

    不过,在我写文章的现在,dvh 的兼容性不是很好,所以必须做好兼容性配置。我建议用 JS 结合 CSS 变量来做。在 <head> 里插入这段 JS:

    // 首先,判断是否支持 dvh 单位
    if (!CSS.supports('height', '100dvh')) {
      // 如果不支持,就定义 --app-height 为视口高度,即 window.innerHeight
      document.body.style.setProperty('--app-height', window.innerHeight + 'px');
      // 当屏幕缩放时,改变内容高度。因为 resize 事件触发很频繁,所以使用节流减少性能损耗
      let timeout;
      function onResize() {
        clearTimeout(timeout);
        timeout = setTimeout(() => {
          clearTimeout(timeout);
          document.body.style.setProperty('--app-height', window.innerHeight + 'px');
        }, 500);
      }
      window.addEventListener('resize', onResize);
    }
    

    然后定义 CSS 样式:

    :root {
      --app-height: 100dvh;
    }
    
    .h-dvh-app {
      height: var(--app-height);
    }
    

    如果使用 TailwindCSS,那么在配置文件里增加配置即可:

    export default {
      theme: {
        extend: {
          spacing: {
            'dvh-app': 'var(--app-height)',
          },
        },
      },
    }        
    

    最佳实践:使用 CSS 变量解决虚拟键盘

    只是限制高度为 100dvh,当虚拟键盘弹出之后,因为视口缩小,很可能会出现问题。此时 window.resize 事件也不会触发,所以我们应该侦听文本框的 focus 事件,动态改变容器高度;并在文本框 blur 之后,恢复高度。

    此时,我们可以借助 CSS 变量的“默认值”功能,即 var(--custom-value, --default-value) 来处理。当我们需要暂时的高度以应对虚拟键盘时,设置 --custom-value;之后,移除 —custom-value,恢复到预定义的 --app-height

    首先,修改 css,定义 --input-height: initial,这个值会被认为是空值而忽略。

    :root {
      --input-height: initial; 
      --app-height: 100dvh;
    }
    
    .h-dvh-app {
      height: var(--input-height, var(--app-height));
    }
    

    然后侦听输入框的 focusblur 事件:

    async function onTextareaFocus(): Promise<void> {
      // 桌面端忽略这个需求
      if (window.innerWidth > 640) return;
    
      // 给虚拟键盘弹出一些时间
      await sleep(300);
      const { innerHeight } = window;
      document.body.style.setProperty('--input-height', `${innerHeight}px`);
      // 需要的话,可以在这里插入一个滚动
    }
    async function onTextareaBlur(event: FocusEvent): Promise<void> {
      // 同样,也给虚拟键盘收起留一些时间
      await sleep(250);
      document.body.style.removeProperty('--input-height');
    }
    

    总结

    至此,遵守以上最佳实践之后,基本上我们可以妥善处理移动网页里的浏览器高度。当然,并不完美,比如,iOS Safari 在输入 position: sticky 里的文本框时,会凭空多出一大块空白,很烦,但是没办法解决。可以绕开,但是我觉得绕开的方案更难用。

    希望这篇文章对大家有用。如果你对移动网页开发有什么问题,欢迎留言讨论。

  • 感谢 Zeabur 成为本站第一位赞助商

    感谢 Zeabur 成为本站第一位赞助商

    2023 年年初,我在刷推的时候受 @xiqingongzi 启发,决定为本站进行招商。想法很简单:钱不钱的不重要(因为肯定挣不到钱……),关键是想看看有没有老板愿意支持我。换句话说,主要在于认可。

    果不其然,2023 年过去了,没人来。没关系,2024 继续努力。终于在 部署网站该选 Vercel 还是 Cloudflare Pages 一文之后,有老板主动联系我做投放了!!

    这位老板来自 Zeabur,他们的产品类似 Vercel + Fly.io,既支持使用 Edge Function 的基于 SSR 框架的产品部署,比如 Next.js,Nuxt 应用;也支持基于 Docker 的容器部署,比如 MySQL、WordPress。可以说是一站式满足独立开发者、新产品验证阶段的需求,还提供大量的免费额度,非常值得一试。他们还提供中文文档,对各位英语苦手的国内开发者来说,相信也会有很大帮助。

    推荐各位开发者尝试这款产品,也请大家在注册的时候,务必使用我的推广链接:

    后面我还会基于他们的产品做一些试用和介绍,敬请期待。

    感谢大家的支持,每一位读者都是支持我继续写下去的动力。2024 年已经过去 1/3 了,希望后面 2/3 我能带给大家更多有价值的内容。

  • 如何理解开发团队的报价,怎么选择合适的开发团队

    如何理解开发团队的报价,怎么选择合适的开发团队

    我很早就开始在 在行 上提供 咨询服务,这么些年也帮助了一些有意参与互联网创业,或者往小了说,“想做网站 / App”的小老板们。其中有一些共性问题,我觉得应该慢慢总结积累下来,便于分享复用。

    以下是第一篇:如何理解开发团队的报价,怎么选择合适的开发团队。

    为什么开发团队的报价差别那么大?

    有位想给自家工厂做电商网站的朋友问我:

    我想做一个网站,把自家产品放上去。 如果有客户看到了,想要哪个产品,就直接下载合同,打印出来签好字寄给我,我就给他们发货。 我去找了一些开发团队,有些报价 1w,有些报价 1k。我现在不知道该找谁,怕钱少的不靠谱,也怕钱多的坑我。但其实我的心理价位有 3w,还算充足吧 我该怎么判断这些团队呢?

    相信很多不熟悉互联网研发的朋友都会有类似的问题。我先说下结论:

    1. 这些报价或高或低,其实都合理,因为需求理解和产出物不一样。
    2. 正确的做法是明确需求,严审产出,确保自己下一步工作能顺利开展。
    3. 重点是对成本收益做到心里有数,不要追求低价,也不要觉得高价一定就能买到好东西。

    接下来,我来详细解释一下。

    网站的真正构成

    首先,网站并不只是前后端的代码和运行代码的服务器。除了这些东西,网站上的内容往往更加重要。因为网站上线后,这些内容决定了用户会不会来、留不留得住、能否会产生购买、电询等转化。

    好的内容包括:

    1. 有吸引力、让用户看了就想下单的图像、视频
    2. 准确的描述,详细覆盖各种场景的文字说明
    3. 排版合理,准确使用 HTML 标签,形成良好的 SEO

    而网站开发团队的报价里,极大概率不包括内容维护。原因也很简单,开发团队的专业在于开发网站,他们并不掌握你们要推广的产品,更不清楚围绕这些产品打造的内容,而且也不可能长期跟进网站内容的维护。

    业余内容团队的问题

    所以,基本上,你需要一个内容团队来维护网站的内容。

    他们也许是公司原本的文员,也可能是你新招募的小弟小妹,但是大概率,他们都不具备内容网站的工作经验。他们可能只是上学期间用过 Word,平时用手机拍过一些游客照;没有写过博客、没有用过公众号后台、也不知道什么是 SEO。

    他们能做的,就是把公司准备好的材料复制粘贴到网站后台,然后点击发布按钮。

    但是这对吸引用户、对 SEO、对促进消费都没有帮助。

    为什么建站报价忽高忽低?

    对有经验的开发者来说,做网站其实不难。拿一般的企业级网站为例:

    1. 内容发布系统,有开源的 WordPress
    2. 服务器托管,有免费的 fly.io,或者买各种云服务的虚拟主机,¥100/年够用了
    3. 数据库也有免费的,比如 TiDB,足够用很久
    4. 要让用户快速看到图片、视频、音频等比较大的资源,你需要 CDN,免费的七牛、CF,或者各大云也有免费额度
    5. 快速建站可以套模版,有免费的有付费的,均价 $50 以内,能重复用
    6. 如果要电商,WooCommerce,基础功能免费
    7. 没有在线成交需求,比如电询类,连 WooCommerce 都不需要,留个表单就行了

    结论就是,如果我的朋友找我帮他做网站,我可以不收钱,花上半天时间帮他用各种免费资源搭建起一整套网站产品,足够验证他的想法。回头请我吃顿饭就行。

    如果我的朋友手头很宽裕,预算很充足,希望高举高打,一次搞定,三年内不需要烦恼技术问题。那也简单,现在的开发基建相当丰富,不管是框架还是服务器。我再去多找几个靠谱的产品经理、设计师、前后端、运维,搞个小团队,从零开始,把一切可能的问题都帮他考虑到,提前布局。产品方面,尽可能做到傻瓜化自动化,文档写清楚,能录视频全录成视频。只要有预算,一切不是问题。

    这样下来,有个百十万的预算,也能花光。

    总结一下,做一个网站,从零元到上百万,都有可能。但是最后拿到的东西,是不一样的。

    从传统行业踏足互联网,正确的做法

    以我的经验,很多网站没能坚持到最后的原因,不是遭遇不靠谱的技术团队,而是低估了内容团队的价值和操作难度,最终倒在“没有用户”这个难关下。

    想提升内容质量,一方面要好好建设内容团队;另一方面,就是在研发阶段,要好好下功夫做足产品工作。

    建设可靠的内容团队

    组建和管理内容团队不是我的本职工作,不过我的第一份工作在某垂直媒体门户,也算耳濡目染五六年,简单聊几点吧。

    1. 要有合适的工具。所谓工欲善其事必先利其器,好的工具能极大提高工作效率,降低错误率。这点后面会展开说。
    2. 建立标准。无论是照片、视频、文字,都要建立明确的、准确的、可执行的标准,并且坚决执行下去,让每个人的产出都能维持在标准线以上。
    3. 制定流程。要用流程和纪律保证工具和标准的执行,保证最终产出的质量。

    打造优质的发布系统

    另一个方向则是打造优质可靠的发布系统。这个发布系统需要和内容团队的标准、流程相结合,保证内容团队标准、流程的执行。

    不同网站需求不同,难以一概而论,我简单列举一些,请大家参考。

    1. 产品参数。关键参数必须填写清楚,可以提供导入功能和验证功能,避免出错。
    2. 产品图片。商品的图片必须清晰,包含必要的构图,包含指定的细节。
    3. 所有编辑修改过程做到可追溯,可回滚,避免出错,也方便日后究责。
    4. 提供必要的 SEO 选项和 SEO 检查。
    5. 编写高质量的实用教程,录制高质量的教学视频,方便内部培训。

    不担心花钱,也要清楚花的每一分钱去到哪里

    正如前面所说,建站方案很多。有不花钱的免费方案,但是需要学习开源产品,培训工作人员;也可以根据最终产品展示方案完全定制一比一的所见即所得编辑工具。

    对于各位老板来说,重要的事情不是省钱;而是知道自己花的每一分钱会带来什么价值,会给将来的内容维护带来怎样的好处,会给用户体验带来什么提升。

    所以我们需要明确产品的界面、交互、使用流程。需要制定产品规格书,要求开发者保质保量交付。这个过程不可以图省事,也不要怕露怯,当甩手掌柜。毕竟,前期准备越充分,后面工作就越好开展,项目成功的概率就越高。

    总结

    总之,使用开源软件和免费服务,可以减少支付的费用,甚至不花钱就拥有一个网站。但其实,各位老板还是会以“培训成本”、”学习成本“、”运营成本“、甚至”试错成本“的方式,把这笔钱或多或少的支付出去。

    所以,合适的做法是,搞清楚自己需要什么,要求开发承包商提供相应的服务和物料。好好验收,确认服务稳定可靠。这样才能收获真正有用的网站,继续下一阶段工作的开展。


    如果您对上述内容有疑问,有意见,欢迎留言讨论。

  • 继续蝉联思否 Top Writer 2023

    继续蝉联思否 Top Writer 2023

    2023 年,我仍然坚持每日回答一个问题,并时不时参加一些征文活动,到年底,成功再次蝉联思否问答组 Top Writer。目前声望 21k,粉丝 7700。这次纪念品挺丰富,有一枚金色奖章,一块牌子(我装了半天没装起来,后来才发现要把上面的塑料膜撕掉……),鼠标垫 T 恤、贴纸若干,竟然还有一双袜子,不过没试,不知道穿不穿的上。

    感谢思否对我的认可,希望过去一年我真的帮助到一些同学,也希望今年我能帮助到更多的同学。

    2024 年,刚刚过去两个月,风物长宜放眼量,一切都还有机会,大家一起加油吧,希望明年的这个时候,我们都能取得让自己满意的成长。


    权威发布 | SegmentFault 思否 2023 年度 Top Writer

  • 【电鸭分享】如何找到一份兼职远程工作?

    【电鸭分享】如何找到一份兼职远程工作?

    自我介绍

    大家好,我是 Meathill,中年光头程序员,兴趣使然的分享者。我喜欢编程,希望把程序员作为终身职业坚持下去。我也喜欢学习新技术,分享各种技术和职业相关的话题。目前,我跟一位老板合作,尝试用 AI 做一些改善人们生活的产品。

    我跟电鸭缘分不浅。初识是在一早一晚,后面来到电鸭。去年,在前厂资金链断裂,被裁员之后,我在电鸭上找到两份工作机会,一份就是我目前的全职远程;另一份因为时间有限,只帮他们做一些顾问工作,提供咨询和建议。

    所以我经常以电鸭自来水自居,在各种帖子里挺电鸭。我看到有些同学想找兼职、远程的工作,但是一时不明就里,所以写一篇文章分享一下。

    先说总则

    1. 坚持做好事,做对的事
    2. 广结善缘
    3. 保持耐心,不要急于求成

    努力做好你现在手头的工作

    如果你现在还没找到很好的远程,或兼职机会,那么我建议你,先努力做好手头工作。

    我认为,现在的工作,是你最容易得到别人认可、积累技术经验、学习项目管理知识的机会。基本上,我能合作到现在的设计师、开发者、甚至老板,都是在某段职业经历中结识的。长期合作让我们有充足时间了解彼此,磨合协作方式,建立长期联系。这样积累的人际关系远比日后网上随机组队来的靠谱。

    所以,不管你正在经历什么样的工作状态,我都建议你:

    • 努力做好手头工作
    • 建立好的合作关系
    • 寻求长期合作伙伴

    做好自我介绍

    常在电鸭闲逛的同学会看到很多招募帖,帖子下面总会有或多或少的自荐跟贴。不过坦率地说,大部分自荐跟帖都是浪费时间,除非你写了一个机器人自动跟帖,否则单凭这样的跟帖,能找到靠谱的工作机会,才怪。

    电鸭虽然需要花费电量才能“买”到联系方式,但其实电量获取并不难,多分享些知识、经验,攒几百电量轻轻松松。

    用电量获取联系方式之后,认真写一封求职信,讲明自己的优势,自己能拿出多少工作时间,自己对项目的期待等,发给招募方,大部分时间都能收获认真的回复。即使有多人同时竞争这个机会,愿意先付出的人常常也能脱颖而出。

    扩展自己的技术栈

    与大公司兵强马壮粮草充足不同,会在电鸭这样的第三方平台招募开发者的老板,通常来说预算有限,无法支持完整团队。不过,另一方面,他们要做的全新产品通常也没有多少用户,基本上随便用什么技术栈都能应对。

    所以,如果我们能够独立完成项目开发,机会就多的多。

    目前来说,最合适的技术栈当然是基于 Node.js 的 Nuxt/Next,配合 Vercel、Cloudflare 等云平台,前后端同构、Serverless Function 高速响应及自动伸缩、免费额度完全可以应对 MVP 需求等。可以做到快速低成本启动,对老板和开发者来说都是好事。建议大家有空多看看。

    私货时间,推荐我去年做的全栈开发教学视频:Nuxt3+Vercel+Serverless 数据库全栈开发

    扩展自己的团队

    技术栈再全面,也很难一个人做完全部工作,很多时候,团队带来的帮助绝不是简单的 1+1,而是 N*N。所以我建议大家可以的话,努力组建自己的团队。独行快,众行远。

    不过我不觉得在论坛上随机组队是个好办法,我觉得还是从工作合作中找比较靠谱的人一起组队比较好。

    团队人可以不用太多,每项工作都有人能胜任即可,比如我日常合作的就只有几位 PM+设计师、前端、全栈工程师而已。如果他们没时间,很多东西我宁愿不做。

    只做利润率高、值得付出的项目

    接下来就涉及到“项目选择”这个话题了。 我认为,真正值得做的项目并不多,能够合作的老板也很少,能够开心协作的团队更难找,所以我建议大家对远程兼职这件事不要太执着,很多时候,做不了接不到,是老天在帮你。

    比如论坛上很多几百块,小几千但是要求很高的项目, 我就觉得完全没必要做。

    不过话说回来,我也不觉得嫌工资低就可以去别人帖子里阴阳怪气。只要大家都坦诚相待,认真做事,及时结算,那就是好的合作关系。

    与合适的人建立长期合作关系

    正如前面所说,好团队、好老板、好项目很难找到,所以我建议大家一旦遇到了,就好好珍惜,有时候,亏点时间、亏点钱、累一点,也得维护好。

    我的建议是,如果你还年轻,比如处在职业生涯的前 10 年,有长期做远程、独立开发、接单外包这方面的想法,那么我建议你找好队友,找好老板,找好项目,好好建立长期合作的关系。不要太在意一时一地的得失。

    总结

    正如我在一个帖子里的回复那样:

    我觉得,只要你是个靠谱、出色、且技术栈相对全面的程序员,兼职私单的机会几乎是做不完的。

    我的个人经验告诉我,按照前面的行事要点,坚持一段比较长的时间,就能达到:只要市场上有人在找人做一些兼职私单,那你就有很大的机会。

    希望我的分享对大家有帮助,如果你有意见、疑问、其它想分享的内容,欢迎跟帖讨论。祝大家 2024 都能有各种各样丰富多彩的职业机会。

    我还有其它一些 关于“程序员副业”的分享,欢迎大家观看,并求一键三连。

  • CSS 小教程:在网格型选择工具上添加渐变背景

    CSS 小教程:在网格型选择工具上添加渐变背景

    新年开始了,要努力,本博客开始 2024 年招商,欢迎各位想推广产品的老板投广告,目前定价 4800/年,亦可增加评测文章、教学文章配合,详情可与我联系。具体详情更新在 本站广告 2024 年招商


    好几个月之前,我遇到这样一个需求:

    这个东西叫作 Emotional Scale,是一大堆描述情绪的形容词,存在从积极到消极的渐进关系,老板希望我们用颜色给予它们比较明显的区分。于是我就想到这样一种表现方式:

    1. 用网格化来布局每个形容词
    2. 给它们添加从上至下的渐变背景
    3. 再让 ChatGPT 给每个形容词选一个 emoji

    最后一项不用多说,直接丢给 ChatGPT 就行。这里重点说下前面两个。

    网格化布局用 display:grid + grid-template-columns: repeat(N, minmax(0, 1fr)) 即可,其中 N 可以根据宽度调整成需要的列数。

    接下来处理渐变。这里的难点在于,每个小格子是独立的,要维持统一的渐变比较困难。我想到两个方式:

    1. 画一个大的渐变背景,然后用 clip-path
    2. 通过计算的方式给每个小格子加渐变背景

    后面我决定选择第二种方式。我总共有 38 个格子,一行 4 个的话,就是 10 行;有 6 个颜色,刚好每两行是一个过渡。于是一行就应该是 0 -> 50%,如何渐变一半呢?我尝试了一下,发现可以用 -100% 和 200% 来做。

    于是我就配合 Vue 开发了这个组件:

    <script lang="ts" setup>
    function getBgColor(index: number): string {
    const colors = ['#1B4397', '#648EF7', '#5DC264', '#FFAE43', '#F15146', '#b91c1c']; // 总共 6 个颜色
    const start = index / 8 >> 0; // 8个=2行,就是两行完成两个颜色的过渡
    const end = start + 1;
    const line2 = index % 8 >= 4;

      // 借助 CSS 变量完成过渡效果
    return `--tw-gradient-stops: ${colors[start]} ${line2 ? '-100%' : ''}, ${colors[end]} ${line2 ? '' : '200%'}`;
    }
    </script>

    <template lang="pug">
      .grid.grid-cols-4.gap-2
    label.flex.flex-col.justify-center.items-center.bg-base-100.rounded-3xl.w-full.aspect-square.cursor-pointer.bg-gradient-to-b(
    v-for="([label, face], index) in todayDoingsScale"
    class="hover:bg-base-200"
    :style="getBgColor(index)"
    :key="index"
    @click="doSelect"
    )
    input.hidden(
    type="radio"
    name="feel"
    :value="label"
    v-model="score"
    )
    </template>

    最终完成的效果就如同上面截图所示。

    CSS 作为前端开发的必备技能,搞起来别有一番乐趣。新特性不断被添加进来,就有越来越多的功能可以实现,有越来越多的功能可以更容易的实现。相信我们下一步的开发环境和产品体验会更好。

    如果各位读者对上面的 CSS 实现有什么问题,或者有其它关于 CSS 的问题,欢迎留言提问。

  • SSR,云平台,ChatGPT——我的 2023 技术关键词

    SSR,云平台,ChatGPT——我的 2023 技术关键词

    前言

    2023 年,因为换工作,启动新项目等原因,我对我的技术栈进行了比较大的更新,主要集中在这三个方向:

    1. SSR(Server Side Rendering,服务器端渲染)。之前我开发的项目基本上都是 SPA(Single Page Application),比如 Vue,但之后我会越来越多开始用 Nuxt。由于基础设施的发展,以后 SSR 会更方便更好用。
    2. 云平台。以前我大概买了 3、4 台云服务器用来做各种尝试,在上面各种折腾。去年使用 Vercel、Supabase、CloudFlare 平台之后,我已经不打算再在服务器上浪费时间了,云平台实在太好用了。未来我会努力把所有服务都迁移到云平台上,新增产品都直接云原生。
    3. ChatGPT。相信不只是我,很多人都会把 ChatGPT 作为去年技术的首选关键词。如今我不仅在上面完成产品开发,日常也会使用它替代大部分的搜索;甚至我家孩子写作业也会使用它来帮忙。我认为,未来 ChatGPT 就像是搜索引擎一样,决定了一个人的起点和成长速度。

    接下来逐个分享。

    服务器端渲染,SSR

    起初我不是很看重 SSR,我总觉得,我当年也写过 PHP,有什么“服务器端渲染”我没见过?实际用过之后,我承认:真香……

    首先,使用 SSR 可以提升用户体验,且有利于 SEO,这点相信大家都知道。如果对其原理不太清楚的话,欢迎观看我的视频:从浏览器渲染机制理解 Web 性能——“在浏览器地址栏输入 URL,按下回车后会发生什么?”

    其次,如今的 SSR 与当年 PHP 模版套页面的实现有很大区别:

    1. 语言同构化:开发难度大大降低,没有心智负担。
    2. 数据传递与状态管理:虽然数据不能完全通用,但是框架尽量会帮我们处理好,让我们在服务器端和客户端都能自由使用。
    3. 渲染由边缘计算负责:这一点有点依赖云平台,不过考虑到浏览器的渲染机制,SSR 并不会拖慢渲染速度,用户体验只会更好。
    4. 页面切换不需要重新加载。对于旧的编程语言来说,因为前后端环境割裂,所以页面切换的时候都是重新加载完整页面;但是新框架下,则只需要加载数据即可,此处跟 SPA 的体验无二。

    第三,如今的 SSR 框架都很好的整合了服务器,包括中间件等功能,还有各种官方第三方模块支持,能大大降低我们开发服务器软件的成本。所以已经是我启动新项目的不二之选。

    云平台

    以前我长期维护好几台服务器,一方面可以部署自己做的产品 demo,另一方面也可以部署一些开源项目方便日常使用。因为各种云都有面向新用户的优惠活动,所以成本不高,我觉得值得一搞。

    自己的服务器当然比较比较自由,坏处就是免不了产生运维成本,即使使用 docker 也一样。部署新代码至少要去跑一遍拉取脚本,对吧?我的一位老板朋友甚至请我帮忙写了一套服务器脚本,用来做 CI/CD。

    初期这么搞没问题,但后来就越来越觉得功能不够,性价比也太低,开始寻求替代方案。之前我参加 Hackathon 的时候了解到 Vercel 云平台。它与 GitHub Pages 不同,支持 SSR、支持云函数,配合一些云数据库,比如 Upstash,可以快速搭建起来一套可用的服务。去年年初,我的那位老板朋友想做一套打分系统,放在他的静态网站里,于是,我就尝试用 Nuxt.js + Upstash 开发了一套,并且部署在 Vercel 上,效果非常好,免运维,多环境,推到 GitHub 自动部署,实在太好用。

    我把这个过程制作成了系列课程:Nuxt3+Vercel+Serverless 数据库全栈开发。大家感兴趣不妨看一看。

    后面一发不可收拾,过去一年我不再采购新的单体服务器,旧的服务器用完也不再续费。新产品都部署在 Vercel 等云平台上面,帮我节省了大量的时间。

    Vercel 去年年中的时候开通了存储功能,实际上就是打包了几家云数据库服务来卖,我也很快获准开通。从此,云平台使用就更加顺利了。临近年底,我尝试 CloudFlare Pages,效果也非常好。他们家的优势是自带统计分析功能,远比 Vercel 大方,一站式解决更省心。

    云数据库方面,我使用 Upstash 的 Redis,KV 数据库足以满足大部分产品需求。数据库用 Supabase 和 TiDB 比较多。前者支持 PG Vector,方便我们进行 LLM Embedding & Search;后者则提供 5GB 免费额度,比较好用。云存储有 CF 的 R2,空间和流量也相当充足。如果不是 PHP 太老没人支持,我都想把博客这台机器退掉了。

    ChatGPT,以及其它

    ChatGPT 更是值得大书特书的一个技术关键词。不过考虑到大家去年一整年应该已经被类似的内容淹没了,所以我这里就少写一些,只说说我的情况。

    我目前订阅了 ChatGPT+,方法是借用国外亲戚的手机号注册,并且用他的手机号注册 PayPal,通过 Google Play 订阅。订阅的原因是 ChatGPT 4 + DALL-E 都可以随便用,比 API 便宜得多。

    在编程领域,GPT-4 比 GPT-3.5 好太多了,知识库更新到去年 4 月份之后,除了 next.js 14 的内容外,我日常的编程问题大多可以用 GPT-4 解决,比如:

    • 写正则
    • 写 SQL
    • 查函数、查第三方库
    • 纠正函数错误

    帮我节省了大量的 Google 时间,单凭这点,每月 $19.99 的订阅费用就很值得。

    除此之外,我还在继续使用 GitHub Copilot。Copilot 也很好用,除了生成工具函数、编写测试外,我发现翻译语言和框架方面也有很大的作用。去年我就完全靠它开发了一个 flutter 应用,方法就是把 TS+Vue 写好的代码丢给它让它翻译。

    所以,无论是学习新东西,保障日常开发,还是扩展新领域,AI 对我都帮助巨大。

    总结

    总而言之,如果再有同学问,前端想学后端,应用学什么语言框架以及是否需要搭自己的服务器?我都会建议他们:不要学 Express、Koa;习惯用 Vue 就学 Nuxt,习惯用 React 就学 Next.js;不需要搭建服务器,就用云存储就能解决绝大多数问题。

    我还建议大家,尽快想办法开通 ChatGPT,再不济国产大模型也要用起来,未来是 AI 的时代,学会用 AI,效率会大幅度提升。半年的初入门新人,善用 AI 可以赶上 3 年的老程序员;而老程序员学会用 AI 之后,可以快速把自己的能力扩展到其它领域。

    以上,就是我去年关键的技术栈总结,希望对大家有所帮助。如果大家有什么意见建议,想说的想聊的,欢迎留言。

  • 值得关注的 CSS 新特性

    值得关注的 CSS 新特性

    CSS 作为前端的重要组成部分,虽然受瞩目的程度逊于 JS,但是也在不停地进步。CSS 可以让我们的开发环境更好,用户体验更佳,所以大家也需要保持关注。这里记录一些值得关注的新特性(评判标准由我主观决定),有些已经实装,可以取代旧样式,提供更好的效果;有些还没有普及,但是可以逐步应用到我们的产品当中,渐进式增强。

    overflow-wrap

    这个属性我还是通过 TailwindCSS 学到的。它可以用来处理文本换行,拥有 3 个可选值:

    1. overflow-wrap: normal 默认值,按照标准模式换行
    2. overflow-wrap: anywhere 可以在任意处换行
    3. overflow-wrap: break-word 尽量利用宽度,只在超宽时打破单词换行

    我们知道,中文一个字就是一个字,随时可以换行;而英文则把一个单词算作一个字,单词内部不能随意断开。但是有时候,容器宽度不够,文字就会撑破容器。以前我们只有 word-break: break-all,但是 word-break: break-all 会在单词的任意位置断开,甚至在不必要的时候断开;而 overflow-wrap: break-word 会尝试把单词放到独立一行,如果还是不行,那就再断开。所以它的效果会更好。推荐大家使用。

    Scoped CSS

    CSS 的优先级非常重要,平时常常用到,面试的时候也会常常问到。CSS 优先级分成若干层级:

    1. #id
    2. .class
    3. element
    4. [attr="value"]
    5. :pseudo-class

    这样的优先级规划在大部分场景下,都能正常工作,但是当我们需要使用公共仓库,或者开发公共仓库的时候,就会遇到困难。

    比如 Awesome Comment 项目,它是一个嵌入页面的评论框,用户可以把它嵌入到自己的网站当中,给自己的网站添加评论功能。我们使用 TailwindCSS + DaisyUI 作为样式库,直接使用一切都好,但是当我们要把它嵌入别的网页时,就可能跟目标页已有的样式产生各种冲突。于是,在 Scoped CSS 尚未普及的今天(主要是 Safari 不支持),我们就必须给所有样式加上 ac- 前缀,非常影响开发效率。还有一位同学,他想给现在使用 bootstrap 的项目里引入 TailwindCSS,也面临严重问题。

    如果有了 Scoped CSS,我们只需要这样:

    @scope (.awesome-comment) {
      .a { }
    }

    就可以非常轻松地让我们的样式只在 .awesome-coment 节点内生效,且盖过其它样式。可以大大提升我们集成别人的样式,或别人使用我们样式时的效率。相信以后这个功能会大大改善第三方框架的开发。

    更多更详细的用法,建议大家深入阅读:@scope – CSS: Cascading Style Sheets | MDN (mozilla.org)

    <textarea> 根据内容自动缩放

    这项目功能目前还在讨论之中:[css-ui] ? Allow <textarea> to be sized by contents. · Issue #7542 · w3c/csswg-drafts (github.com)。大概意思是,<textarea> 用途非常广泛,让它能够根据内容长度自动调整大小会非常有价值。不过,具体选用什么方案,目前还没有定论。

    我搜了一下,目前无论 form-sizing 还是 field-sizing 都还没有进入规范,也没有间接实现方案(比如 PostCSS),所以大家先保持关注吧。

    View Transition 视图切换动画

    View Transition 让我们的产品可以在页面切换时,复用一些页面元素,展现出漂亮的动画,让用户更加理解页面逻辑。如下面这则视频所示,当用户点击列表中的链接时,大标题、作者等信息会以动画形式,移动到页头位置;当用户回到列表页的时候,它们又会移回列表里的相应位置。

    使用 View Transition 的方法比较复杂,我暂时还没有 Demo,所以就不详细说明了,大家感兴趣的话,可以看下面两个链接:

    1. Getting started with View Transitions on multi-page apps | daverupert.com
    2. https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API

    light-dark() 函数

    light-dark() 函数可以接受两个参数,分别作为 light 模式和 dark 模式下生效的属性。如以下代码所示:

    body {
      background-color: light-dark(white, black);
    }

    不过我觉得这个函数的作用不会很大,我还是喜欢集中定义和管理这些变量。

    color-mix() 函数

    相对来说,我觉得 color-mix() 函数的作用就大多了,它可以基于一个颜色,生成新的值。比如,我们先约定一批颜色,分别作为 primarysecondarysuccesserror 等,然后使用 color-mix 函数,生成一些衍生颜色,以便适用在 disabledactivehighlight 等场景下。

    使用方式也比较简单,它接受 3 个参数:混合模式,颜色1,颜色2。比如下面的代码,就是两种颜色各取一半,以 srgb 模式混合,得到新颜色。其中百分比也可以调整,比较好理解。

    /* 减淡,即混合白色 */
    color: color-mix(in srgb, var(--primary) 50%, white);
    /* 加深,即混合黑色 */
    background-color: color-mix(in srgb, var(--primary) 50%, black);

    有兴趣的同学可以深入了解:color-mix() – CSS: Cascading Style Sheets | MDN (mozilla.org)

    原生嵌套 CSS

    嵌套 CSS 相信大家在使用预处理工具的时候都会用到:

    .a {
      .b {
        .c { }
      }
    }

    因为确实有用,所以如今原生 CSS 也开始支持嵌套,可惜有些浏览器还没有支持。我建议大家先配合 TailwindCSS 使用,等到将来都支持了,再去掉 TailwindCSS 即可。

    export default {
      plugins: {
        'tailwindcss/nesting': {},
        tailwindcss: {}, // 不需要 TW 的话可以不要这行
        autoprefixer: {},
      },
    }

    详细规范请继续阅读:https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_nesting/Using_CSS_nesting

    CSS 容器查询

    我猜大部分同学都用过 @media query,以适配不同设备和分辨率。但我们现在已经是组件化开发的时代,很多时候,我们需要让自己的组件能够适配不同的使用场景,于是仅仅根据分辨率调整布局就不够了。万一被用在桌面显示器上,但是本身只占据一小块空间,那就无法保证用户体验。

    这个时候我们可以使用 CSS 容器查询(Container query),它的用法很简单,首先,假设我们的组件渲染出来的 HTML 是这样的:

    <div class="post">
      <div class="card">
        <h2>Card title</h2>
        <p>Card content</p>
      </div>
    </div>

    接着,我们要把目标容器标记为“容器控制上下文”(containment context)。未特意标记的容器不会产生容器查询,我猜这样可以减轻浏览器布局的计算负担。最后,使用 @container 查询,让容器在某个尺寸时,内部某些样式生效,即可:

    .post {
      container-type: size;
    }
    
    /* 默认样式 */
    .card h2 {
      font-size: 1em;
    }
    
    /* 仅在容器 >= 640px 时才生效的样式
    @container (min-width: 640px) {
      .card h2 {
        font-size: 2rem;
      }
    }

    容器查询还有一些进阶用法,大家可以深入阅读:CSS container queries – CSS: Cascading Style Sheets | MDN (mozilla.org)

    总结

    好的,先写这么多。感谢所有标准贡献者、开源软件开发者、社区参与者,我们 Web 开发者能收获现在的成就,跟所有贡献者的辛勤工作分不开。

    希望这些新特性能尽早进入实装,成为我们开发时的利器。

    如果你有什么想补充的,或者有问题想讨论一下,欢迎留下评论。

  • 鸣谢各位老板舰长赞助商

    鸣谢各位老板舰长赞助商

    想来想去,决定开个页面用来供奉各位支持过我的老板。

    赞助我,让我贡献更多内容吧!

    • 有了赞助,我会有更高的热情输出各种内容
    • 可以在我的各种内容里获得致谢
    • 可以定制感兴趣的内容
    (更多…)