我的技术和生活

  • 泰国曼谷八日数字游民小记

    泰国曼谷八日数字游民小记

    本文跟技术无关,偶尔写写日常。

    可能是失业的原因,去年一整年我对出游都兴趣寥寥。经过一年努力,跟新老板合作不错,顾问的公司也有所成长,感觉经济压力小了一些,于是又想出去耍。

    年初的时候去了日本,陪老婆猛逛六天街,带孩子泡温泉玩雪,感觉不错。于是又生出来泰国玩泼水节的想法。我们上次玩泼水节是 2015 年,在清迈,感觉非常棒,非常好玩。这次刚好,Vincent 也在曼谷,就想着来玩一下,顺便跟他见个面增进了解。

    后面就着手安排。孩子因为学习成绩不太稳定,不敢请假,好在父母愿意来广州帮我们照料。于是我和老婆订机票订酒店。先选了 2019 年 OpenResty 年会的酒店故地重游,Centara 水门。这家酒店地理位置不错,位于机场快轨和 Central World 商圈中间,交通很方便,适合陪老婆逛街。然后又定了两晚的 Anantara 湖畔酒店。泰国酒店很便宜,Anantara 带双人 Spa 才 1700,比日本便宜多了。

    后面执行计划,在曼谷住了八天七夜,明天一早飞机回广州,共计九天八夜。这里小记一些感受。

    1. 吃饭真贵。不知为何,曼谷吃饭非常贵。一般的商场内部,粉面饭要人民币40~50元,加点料就要 70~80 元。点菜的话,我和老婆普遍人均要 120 左右。街头小吃太过粗放,不太敢吃。稍微有个门脸就得 20、30 块,而且小小的一份,根本吃不饱,都得加料。
    2. 反倒是一些大餐能体现出性价比,比如建兴酒家,海鲜点几道菜,300~400,感觉比国内性价比高一些,味道也很特别,值得一吃。还有就是 Plu,很难走的一家饭店,但是味道非常好。
    3. 天气热,这个就不用说了,此刻太阳刚好直射扫过泰国,一年最热的时间,也不下雨,就一天天的暴晒。不过因为我要工作,所以并不出门,所以感觉还好。
    4. 交通不好。曼谷的公共交通不好,去哪儿几乎都要走很远。配上炎热的天气十分老火。路面很堵,动不动就 30、40 分钟。所以选个好酒店就很重要。对逛街而言,我觉得 Centara 真的不错。
    5. 商场很漂亮,有很强的设计感,实体经济也很发达,大家都喜欢逛商场。但是缺少本地品牌,购物欲望并不强烈。
    6. 泼水节真好玩。满大街都是兴高采烈的玩水的人,很难不被他们带动。拿着水枪穿着花衣服就表示你接受被人泼,时不时从远处飚来一道水柱,很刺激。男女老少没有界限,非常好玩。
    7. 妹子真多。这些年我在国内走男闯北,有一个奇怪的感觉,就是周围都是男人,比例很失调。但是这边妹子超级多,各国都有。穿着更是各种热辣清凉,非常养眼。坐在商场一楼的星巴克一边写代码一边打望,感觉非常好。推荐其他远程工作者都来试试。
    8. 星巴克很多,每家商场都有,很适合用来办公。插座很好用,比国内强的多。不过的微信支付太坑爹,多收了我好多钱。

    整体来说,考虑到高额的居住和生活成本,我给这次泰国九天八夜的数字游民之旅打 7 分,满分 10 分。一段时间内我可能不会再来了,明年泼水节可以考虑。

    如果大家对远程、数字游民、泰国旅游有什么想法,或者意见建议,欢迎留言讨论。

  • 【教程】浏览器扩展中实现一键登录 Google(1)

    【教程】浏览器扩展中实现一键登录 Google(1)

    本文分享我最近开发 AutomateGPT 扩展时集成 Google SSO 的经验。除了 Google 外,我还用到 Supabase 提供的用户管理与登录功能。

    本来想一篇博客搞定,没想到写着写着就超长了,那就拆成两篇吧,哈哈。

    内容简介

    开发浏览器扩展的时候,我们有时候需要建立用户体系,以便更好的服务用户。此时我们有多个选择:

    1. 让用户使用用户名密码登录,提供完整的注册、登录、验证邮箱、忘记密码等
    2. 让用户使用 SSO 登录

    很明显,第二种更好,因为我们不需要建立用户注册、校验等一系列功能,只要用第三方提供的用户身份标记就好。这样一来可以减少我们的开发成本,二来可以利用现有的互联网基建,对用户来说也更省事。

    作为面向 ChatGPT 用户的辅助应用,AutomateGPT 选择用户体系时,我很自然的选择了 Google。

    吐槽

    Google 是个让人又爱又恨的公司,一方面他们提供大量免费的互联网基建,给我们开发带来巨大帮助;另一方面,由于摊子铺的太大,各种年久失修或者考虑不周,导致我们经常要自己踩坑自己摸索才能最终完成想要的作品。

    这次也是。Google 提供的浏览器扩展开发文档包含一些错误和遗漏;Google Cloud 又是完全的黑盒,导致我折腾了将近两天才把这项功能做好。

    行吧,闲言少叙,下面开始正文。

    在 Chrome Web Store 里占个坑

    因为 Google SSO 要跟 Extension ID 绑定在一起,所以我们要先去 Chrome Web Store 里占个坑,保证以后扩展无论安装在哪里,都是统一的 ID。

    创建浏览器扩展项目

    我先假定大家都有一些扩展开发经验,可以自行创建浏览器扩展。如果你需要从零学起,我刚好做过一期相关视频,可以方便你快速上手:

    肉山小教程-浏览器扩展开发-快速入门_哔哩哔哩_bilibili

    创建完毕,在本地调试没问题之后,就打包,上传 Chrome Web Store。此时我们不需要添加太多功能,只要能加载,能看到效果就行,并不是最终发布,所以不用太担心。

    CWS 里创建应用

    假定各位读者已经拥有 Google 账号,那么就请进入开发者信息中心(Developer Dashboard)。

    接着点击“上传新内容“,上传刚才准备好的压缩包,稍等片刻,CWS 会帮我们创建一个应用,此时就能看到应用 ID 了,大约如图所示:

    具体的 ID 不重要,点击图中红框的 View public key,将 key 复制下来,粘贴到插件的 manifest.json 里,记得要把换行删掉:

    {
    "key": "刚才复制的 key,不能有换行符"
    }

    但是 manifest.json 加了 key 之后,为生产环境构建发版时可能会出问题(因为本地没有私钥,无法信任公钥)。此时,如果你用了我以前推荐的 CRXJS Vite Plugin,就很简单,判断一下当前环境即可:

    export default defineManifest(async function (env) {
    return {
    ...process.env.NODE_ENV === 'development' && {
    key: '刚才复制的 key,不能有换行符',
    },
    };
    });

    所以,推荐使用 Vite + CRXJS Vite Plugin 构建浏览器扩展开发环境。如果你还不会使用这个插件,建议看我另一个视频:

    2024 浏览器扩展开发必备:CRXJS Vite Plugin_哔哩哔哩_bilibili

    为了验证效果,可以在浏览器里删除之前导入的测试扩展,然后重新加载改好 key 的版本。如果之后看到的 ID 与你在 CWS 开发者中心看到的一致,那就说明操作成功。

    在 Google Cloud 里完成配置

    接下来,我们需要在 Google Cloud 里完成配置。如果你已经有项目,那么沿用之前的项目也没问题。如果没有的话,那就新建一个。目前 Google Cloud 还是免费的,新用户更是有 $300 的试用额度,可以放心体验。

    同样,我假定各位读者已经拥有 Google 账号,那么打开 Google Cloud Console,并按照向导指引创建即可。

    创建完成,点击左上角的菜单按钮,从里面选择“API和服务”,然后,选择“OAuth 同意屏幕”,跟随引导创建 OAuth 同意屏幕。注意,因为新应用默认采用白名单,为了之后普通用户能够正常注册登录,最好把应用设置为“已发布”。

    然后,进入“凭据”页面,添加登录凭证。点上面的“创建凭据“,然后选”OAuth 客户端 ID”。注意,虽然 Google 给我们准备了”Chrome 扩展程序“这个选项,但是不能选,要选择”Web 应用“。

    然后在”已获授权的重定向 URI”里填入 https://{刚才获得的 Extension ID}.chromiumapp.org,创建凭证。然后你会得到一个形如 xxxx-ooooo.apps.googleusercontent.com 的客户端 ID。

    在 Supabase 里注册应用

    用户登录免不了用到一些通用功能,比如用户管理、用户信息存储等等。开发这些功能免不了需要很多工作量,我觉得自己做并不合算。所以我建议大家选用一些第三方服务。

    我目前比较喜欢使用 Supabase,大概有几点优势:

    1. 免费 500MB 数据库和若干存储、带宽
    2. 很好的整合了用户授权体系,支持常见平台的 SSO
    3. 各种常见框架都有 SDK,包括我最常用的 Nuxt
    4. 行级权限管理,方便作为 Serverless 数据库使用
    5. PostgreSQL,插件生态丰富,接入 pgvector 就能作为矢量数据库

    简而言之,下限有保证,上限有空间,非常推荐。使用 Supabase 集成 Google SSO 非常简单。另外,因为 Supabase 的界面比较友好,不像 Google Cloud 那么反人类,我就不截图了,相信大家很容易找到。

    1. 注册账号
    2. 创建项目
    3. Authentication > Providers
    4. Enable Google,然后将上一步获得的客户端 ID 填入 “Authorized Client IDs (for Android, One Tap, and Chrome extensions) ”里面即可

    小结

    本文主要介绍了开发环境的配置。下篇博客会讲解如何写代码,并解释整个流程的原理,方便大家适配其它 SSO provider。

    如果大家对浏览器扩展开发有什么问题和想法,欢迎留言提问、讨论。


    AutomateGPT 是我们最近开发的 ChatGPT 增强扩展,方便大家更好的使用 ChatGPT,充分利用包月的价值。它能够帮你重复执行多个 prompt;也可以分解大文件,拆成块依次处理(开发中)。可以用在大文本翻译、批量生成内容、网站分析等领域。欢迎大家使用,反馈问题和意见,谢谢。

  • 部署网站该选 Vercel 还是 Cloudflare Pages

    部署网站该选 Vercel 还是 Cloudflare Pages

    全栈开发者部署网站,首选无外乎 Vercel,Cloudflare Pages 二选一。那么,这两者间有何区别呢?结合我过去一年多的体验,简单分享一下。

    我目前两边都在付费使用,且都经历了一些团队协作。我觉得本次分享应该言之有物,不过如果跟事实有出入,欢迎各位指正。

    相似之处

    两者整体体验非常接近,都很好用。

    • GitHub 导入仓库
    • 新 commit 自动部署
    • 分支自动部署,预览新版本
    • 支持多种构建脚本
    • 免费域名(二级域名)
    • 免费永久 SSL 证书
    • CDN
    • 可观的免费额度

    Vercel 的优势

    1. 域名放在哪里都可以

    Cloudfare 最大的问题就是你非得把域名根 DNS 转过去,很蛋疼。虽然我对其他域名提供商也没什么忠诚可言,但是我就懒得换……

    经网友提醒,我又查看了一下,Cloudflare Pages 不需要停放根域名到 CF,所以这点二者一致。

    Vercel 这方面比较大度,你域名放在哪里都行,只要 CNAME 过来就能用。

    2. 集成数据服务非常方便

    Vercel 集成了很多数据服务,包括 config、KV、Storage、DB,使用起来非常方便,只需要在 GUI 上点一下,然后在本地把环境变量配置一下即可。使用的时候只需要安装指定依赖,非常好理解,和常规工作流程完全一致。

    CF 也有类似的服务,但是使用起来就麻烦很多。他们家只针对自家 Worker 做了比较简单的集成,想在自己熟悉的框架里使用会比较麻烦。

    3. 团队管理更好用

    虽然 Vercel 团队需要花钱($20/人月,相当贵),但是团队管理很好用,该有的功能都有,权限配置好,用起来也很顺畅。

    CF 则不然,我到现在都没找到控制团队权限的做法,导致我无法方便的将统计报表分享给朋友。

    4. 更好的 Serverless 支持

    Next.js 本来就是 Vercel 主力开发的,所以自然,Next.js 在 Vercel 上可以放心使用,服务器环境绝对不会成为问题。默认情况下,Vercel 会使用 Edge 引擎提供更好的性能和更低廉的价格;实在不行,也可以切换到 Serverless 模式获得更好的兼容性。

    CF 只支持 node.js 的子集(相当于 Vercel Edge Function),所以如果我们的业务代码中使用的函数库包含了一些不被支持的功能,就无法使用 CF 了。

    Cloudflare Pages 的优势

    1. 提供很好用的分析工具,包括 Web Vitals

    我其实到现在都无法难理解,这东西在 Vercel 那边居然要付费使用,还得通过代码嵌入页面。

    CF Pages 这边用起来很简单。直接打开开关,它会负责把统计代码插入页面,然后我们就能看到包含访问量、来源、技术等的数据统计。更重要的是,我们还能看到 Web Vitals 数据,而 Web Vitals 数据,能直接影响 SEO,所以这个统计就非常有帮助了。

    2. 免费额度更高

    这点 Vercel 也很值得吐槽,它们家额度设计相当诡异。比如,类似(1)的访问量统计,一个月额度 25k,啥意思呢?你的网站访问量超过每天 800,这个月可能就要交钱了……我完全无法理解他们的设计思路。

    还有 KV 也是,明明是集成的 Upstash 服务,Upstash 每天免费 10K,它们家免费 5K……其它带宽、容量等就不细说了,都不敢放开用。

    CF 具体多少我没仔细看,不过印象里要多得多得多,可能是最大方的云服务商了,基本属于 $20/月 放开用。

    3. 全球最近接入

    这点说实话我不太敢确定,没有具体测过。按照官方文档的说法,Vercel 企业版是全球的,但是免费和团队版都要手动选择边缘计算服务器的部署位置。

    CF 按照后台的说法,会自动选择离用户最近的地方执行函数功能,我们就当他是全球最近接入吧。

    4. 目前 *.pages.dev 没有被墙

    这意味着即使你没有自己的域名,可以用 Cloudflare Pages 提供服务。相对来说,*.vercel.app 已经被墙的很彻底。

    不过,如果真的是持久运营的产品,还是早点切换到自己的域名吧。

    排除的其它选项

    • VPS:太麻烦了,我已经不想再自己折腾服务器了。
    • GitHub Pages:不支持边缘计算,纯静态限制太多。
    • Supabase:Deno 限制太多。

    总结

    我个人的感受:Vercel 强在开发体验(DX);CF Pages 强在量大管饱。具体怎么选择,还请大家酌情考虑。

    如果你对出海开发、全栈开发、云服务提供商有什么想法、问题和分享,欢迎留言讨论。祝大家清明安康。

  • 【视频】【身为乙方要学会培训甲方】如何理解外包开发者报价

    【视频】【身为乙方要学会培训甲方】如何理解外包开发者报价

    我把上一篇文章:如何理解开发团队的报价,怎么选择合适的开发团队 做成了视频,准备做一个系列“身为乙方要学会培训甲方”。其实上一次 私单经验分享——接洽不懂技术的甲方,用项目管理提升说服力 也能算在这个系列里。很多时候甲方的小老板未必想清楚,也未必能想清楚,这个时候,就需要我们乙方来培训甲方,才能实现双方共赢的结果。

    本次分享的内容是:如何理解外包开发者报价

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

    1. 这些报价或高或低,其实都合理,因为需求理解和产出物不一样。
    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. 编写高质量的实用教程,录制高质量的教学视频,方便内部培训。

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

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

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

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

    总结

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

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


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

  • 【视频】技术栈大升级:Vue3 到 Nuxt3(1)基础知识篇

    【视频】技术栈大升级:Vue3 到 Nuxt3(1)基础知识篇

    2023 年,我个人最大的变化,是从 Vue3 SPA 应用向 Nuxt3 SSR 应用过渡,在预期可能存在 SSR 需求的项目中,都尽量使用 SSR。包括 React 应用,也尽量使用 Next.js,而不是 React SPA。

    这个过程中,面临到很多问题,很多思路需要转换,很多以前没关注的点需要关注。本系列视频试图快速教会大家这些要点,帮助大家顺利从 SPA 切换到 SSR。

    本期视频主要介绍 SSR 所需的知识和概念,为下一阶段正式重构项目做准备。

    1. 什么是 SSR?为什么要用 SSR?
    2. SSR 的一般构成
    3. Nuxt3 的 SSR 组件
    4. Nuxt3 的渲染规则与缓存处理
    5. 如何鉴别用户身份

    视频中的课件:从 SPA 到 SSR,从 Vue3 到 Nuxt3

    有任何问题、意见、建议,欢迎留言弹幕私信与我交流。如果你觉得视频对你有所帮助,还请留下宝贵的一键三连,并完播分享,谢谢。

  • Next.js 14 SEO 最佳实践(2)Next.js 14 SEO 要点

    Next.js 14 SEO 最佳实践(2)Next.js 14 SEO 要点

    继续分享 Next.js 14 SEO 最佳实践。上次主要介绍 SEO 基础知识,以及一般原则,这次重点放在 Next.js 14 上。我先武断暴言一句:Vercel 在 Next.js 14 上的整了个烂活儿,给开发者留下了很大的坑要填。

    页面组件避免 'use client'

    Next.js 14(也可能更早,待查)引入了 serverclient 组件的区分。前者没有状态,渲染一次后就不会变化;后者则可以与用户互动,响应用户操作,并将变化体现在视图之中。

    SEO 有一些必备信息,包括 title, description,需要通过 export const metadatagetMetadata 返回。它们都不支持 client 组件,所以,我们应该避免让页面组件(page.tsx)成为 client

    按照官方文档的说法,client 组件也会在服务器端进行渲染,然后发给客户端,所以比较合适的做法是:

    1. 组件化颗粒度可以细一些,以便将不需要状态的 server 组件和响应用户操作的 client 组件进行隔离。
    2. 必要的数据,尤其是希望搜索引擎抓取并索引的数据,在页面组件里完成读取,然后传递给 client 组件,作为初始值,完成第一次 SSR。
    3. getMetadata 时,可以使用 React.cache 函数将结果缓存起来,重复使用,降低网络请求成本
    4. 限制 client 的功能比较多,比如 createContext,所以工具函数文件不妨也分离得细一些,避免产生影响。

    确保返回页面 metadata

    SEO 需要一些信息来理解页面,并在搜索时呈现给用户,这些信息基本上都要通过 metadata 提交给搜索引擎,所以我们应该保证在 page.tsx 里都有返回合适的 metadata

    一般来说,metadata 需要包含以下数据,后面的代码简单演示了动态获取并返回信息的方式:

    1. 页面 title
    2. 页面内容 description
    3. OpenGraph 信息
    4. Canonical link
    5. 如果有多语言,还应该有 alternate link
    export async function generateMetadata(
    { params, searchParams }: PracticeDetailPageProps,
    parent: any
    ): Promise<Metadata> {
    // 读取路由参数
    const courseId = params.courseId;

    // 这里的加载函数可以提前 cache,以避免重复请求 API
    const courseDetail =
    await webApi.courseApi.fetchCourseDetail<CourseDetailType>(courseId);

    const metadata: Metadata = {
    title: courseDetail.title,
    description: courseDetail.description,
    alternates: {
    canonical: `https://www.hackquest.io/practices/${courseId}`
    },
    openGraph: {
    title: courseDetail.title,
    description: courseDetail.description,
    image: courseDetail.thumbnail,
    },
    };

    return metadata;
    }

    增加组件颗粒度,创造条件使用 server 组件

    因为静态内容更有利于 SEO,而 server 组件是生成静态内容的最好方式,所以我们应该尽量多使用 server 组件。

    问题在于 client 组件具有传染性,当某个组件内部使用状态的时候,它就必须是 client 组件,所以我们应该尝试让组件颗粒化的程度更细,仅在需要交互的组件里使用状态。

    搜索引擎的爬虫可以沿着超链接爬取一个又一个页面,而它们并不知道某个 <div> 上绑定了跳转事件,所以应该使用 <Link> 帮助搜索引擎索引整个网站。

    很多时候,为满足埋点等需求,我们会使用 onClick 事件,没关系,因为事件会先于链接跳转触发,所以我们只需要正常使用埋点功能即可。

    全站使用统一的通用导航

    使用全站通用的顶部导航和底部导航是提升 SEO 和网页收录的好办法。

    通用导航里面可以包含大量站内链接,方便搜索引擎爬虫遍历整个网站,所以我们应该在通用导航里大量使用 <Link> ,让爬虫可以顺藤摸瓜,找到我们网站里的全部内容。当然,从用户角度的来说,通用导航可能并不是每次打开页面的主要目的,所以,通过 CSS 控制通用导航的显示状态,让爬虫可以按图索骥,但是不影响普通用户阅读网页,是我们应该做的。

    所以,我们应该让整个网站使用统一的导航内容,只调整显示状态,而不是每个页面显示不同的导航。

    分离工具函数和依赖,避免污染

    影响 client 组件的因素很多,比如各种 hooks,createContext,等。如果我们在 a 函数里使用了这些功能,然后在 bimport a,会导致 b 也必须是 use client。这就要求我们尽量将引用的工具函数进行分离,比如单独写一个 utils.client.ts 用来存放客户端才需要使用的工具函数。

    使用 [[...slug]] 处理 URL

    如同前面所说,我们应该给所有资源配置好合适的 URL。比如我们有一些博客内容,需要建立博客页面,那么,我们就应该给翻页功能分配好独立的 URL,如 /p/${page},而不是把翻页状态保存在页面状态里。同样,如果我们支持按标签、分类筛选,就可以提供 /tag/${tag-slug} 这样的 URL。

    此时,为了能更好的复用代码、复用组件,我们可以用可选通配符组件 [[...slug]] 来制作页面。

    const Blog: React.FC<BlogProps> = async function ({
    searchParams = {},
    params: { slug = [] }
    }) {
    const limit = 12;
    const minPage = Number(slug[1]) < 1 ? 1 : Number(slug[1]);
    const page = slug[0] === 'p' ? minPage : 1;

    // 处理加载分页后的博客数据
    ....
    }

    制作 robots.txtsitemap.xml

    制作 robots.txt 比较简单,很多时候手写一个放到 /public 目录里即可,所以我不再赘述。

    制作 sitemap.xml 就复杂很多,因为网站可能会有很多页面,其中更有不少需要动态生成,所以我们一般需要创建 sitemap.ts 才能完成。部署的时候,构建脚本会自动调用这个文件,生成新的 sitemap.xml

    详情请参考官方文档:Generating a sitemap using code (.js, .ts)

    小结

    如果你对 Next.js 或者 SEO 有什么问题和想法,欢迎留言讨论。

    本站目前还在招商:本站广告 2024 年招商,欢迎感兴趣的老板私信洽谈。

    系列文章

    系列长文好更不好看,所以我决定以后都在 notion 上做汇总页,方便日后整体阅读。本文实际已经基本写完了,感兴趣的同学可以先去围观:https://meathill.notion.site/Next-js-14-SEO-90d31eee22ff4621af6620524b4b2773?pvs=4

  • 继续蝉联思否 Top Writer 2023

    继续蝉联思否 Top Writer 2023

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

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

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


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

  • Next.js 14 SEO 最佳实践(1)了解 SEO,常规要点

    Next.js 14 SEO 最佳实践(1)了解 SEO,常规要点

    近日搞 SEO 比较多,本来想写篇“Nuxt3 SEO 最佳实践”,但是列完大纲,发现内容不太够,基本上只需要照搬各种 SEO 手册,跟 Nuxt3 关系不大,不符合我对内容的要求。然后我自信满满地想帮朋友搞 Next.js 14 SEO,发现,好难,Next.js 14 的坑真多……于是就有了这系列文章。

    什么是 SEO

    SEO 即搜索引擎优化(Search Engine Optimization),是指通过优化网站以提高网站在搜索引擎中的自然排名,从而增加网站流量,提高品牌曝光度的一系列操作。SEO 主要关注两个方面:站内优化与站外优化。站内优化包括确保网站的结构、内容质量、关键词布局、网页元标签、URL结构等对搜索引擎友好。而站外优化则更多关注于外部链接建设,即增加其他网站对你的网站的链接,这有助于提高网站的权威性和排名。

    作为技术人员我们一般会负责站内优化,本文也主要讨论站内优化。

    为什么要 SEO?

    1. SEO 目前仍然是最便宜的流量来源。
    2. SEO 跟用户体验并不冲突,事实上,搜索引擎一直在想办法把用户体验和搜索排名结合起来。
    3. 我们甚至可以以 SEO 之名,为优化网站争取开发时间

    现代 SEO 的注意事项

    作为一名前端/全栈开发,有一些东西归属于我们的同事,比如创建内容、扩展外链,我们可以不过分深究。但是也有一些注意事项,尽早注意,可以节省大量返工的时间。

    了解必要的知识

    本文主要是我的经验之谈,不是很全面,更多的知识请阅读后面扩展阅读里的各个链接。

    尽量用服务器端渲染

    1. 服务器渲染对 SEO 更加友好。
    2. 对于大部分搜索引擎爬虫来说,如果能采用静态分析,性能会比分析 JS 渲染的页面高很多。
    3. 服务器端渲染对用户体验也有提升。

    尽量用超链接而不是手动跳转

    1. 浏览器爬虫可以通过超链接 <a> 找到更多页面。
    2. onclick 无法做到这一点。
    3. 我们仍然可以使用 onclick 进行数据埋点统计,因为事件会早于链接跳转触发。

    给每个资源一个 URL

    1. 为了能够在用户间共享,以及让搜索引擎可以索引,我们需要给每个资源一个 URL。
    2. 比如一个页面,如果它包含翻页,我们应该给所有页面安排 URL,比如 /p/2/p/3 等等。而不是把状态保存在浏览器内存里,因为这样外界没法获取直接指向它的资源。
    3. 如果是音频、视频等需要长时间播放的资源,则可以利用锚点: #{时间} 来指向某个具体的时间点。长网页上的标题也应使用类似的锚点技术。
    4. 总之,尽量不要在内存里保存状态,都搞成 URL,做成超链接

    确保页面上有合适的 SEO 内容

    1. <title>
    2. <meta name="description" content="{description}" />
    3. <link ref="canonical" href="{canonical link}" />
    4. <link hreflang="{语言}" href="{其它语言链接}" />
    5. 使用 og 库添加 OpenGraph 信息
    6. 大量可以被搜索引擎阅读和理解的文本,少用图片
    7. 图片、音频、视频等,要配备 alt 等文本信息
    8. 使用 aria-* 帮助搜索引擎链接页面
    9. 使用超链接时,使用 rel 来告诉搜索引擎链接目标和本页面的关系

    尽量把内容全部展示出来

    1. 搜索引擎希望能一次抓取所有内容,这样它才能正确地索引全部内容,以及爬取剩下的内容。
    2. 有时候我们基于页面容量,会倾向于把一些内容隐藏起来,等待用户满足特定条件才展开。比如 tab、carousel 等。此时,我们最好使用 CSS 控制显示效果,而不是只输出部分内容。
    3. 当然,如果涉及到付费墙、私密信息等,可以不显示所有内容。

    设计良好的 URL 结构

    1. 良好的 URL 可以帮助搜索引擎理解我们的内容结构
    2. 也拿博客举例:
      1. / 首页
      2. /p/{page} 分页
      3. /tag/{tag-slug} 分类页
      4. /cate 分类页面
      5. /cate/sub-cate/ 子分类页面
      6. /cate/sub-cate/page-slug-一般来说是内容 具体文章页

    其它

    1. 生成正确的 robots.txtsitemap.xml 并提交给搜索引擎
    2. 避免 404 和 500
    3. 利用缓存改进访问速度
    4. 保证使用 heading,但不要滥用 heading 标题,确保 h1~h6 的合理顺序

    小结

    如果你对 Next.js 或者 SEO 有什么问题和想法,欢迎留言讨论。

    本站目前还在招商:本站广告 2024 年招商,欢迎感兴趣的老板私信洽谈。

    系列长文好更不好看,所以我决定以后都在 notion 上做汇总页,方便日后整体阅读。本文实际已经基本写完了,感兴趣的同学可以先去围观:https://meathill.notion.site/Next-js-14-SEO-90d31eee22ff4621af6620524b4b2773?pvs=4

  • “我的代码被同事覆盖了!”——帮群里的同学解决 Git 问题

    “我的代码被同事覆盖了!”——帮群里的同学解决 Git 问题

    本站 2024 年招商持续进行中,欢迎各位老板前来投放:

    https://blog.meathill.com/ad/advertise-on-this-site.html

    刚才,群里有位同学提问:

    我把我的分支合到 B 分支上了,然后另外一个同事将他的代码合到 B 分支将我的代码覆盖了,然后 B 分支又有很多提交记录,不能回退了,我的分支再合到 B 分支不显示冲突和修改了

    大佬们,这种情况怎么解决呀

    可能大家都有过被同事误操作丢失代码的经验吧,群里群情激愤,纷纷谴责起这位同事。也有不少同学七嘴八舌的开始支招,有说找责任人的,有说一个一个还原的,有说 rebase 的。大家很热心,可惜都没说到点子上。

    要用好 Git,绝对不能靠背套路,更不能瞎试,成了也不知道咋成的,败了也不知道为啥不行。要理解 Git 的设计和实现,遇到问题先分析,再寻找最佳方案,然后小心谨慎的实施。

    我们先分析一下问题的原因:

    1. 最大的可能是,同事合并代码时,遇到冲突,他在解决冲突时,选择保留自己的代码,丢弃了同学的代码(最恶心的情况)。
    2. 我们不考虑归责。同学现在要提交自己的代码,但是因为他的代码版本比较“旧”,所以 git 拒绝合并。也无法提交新的 PR。
    3. 如果前面推测没错,那么同事就不是用的 push -f
    4. 所以同学的代码应该有一部分在库里,只是冲突的部分可能没了。

    如此一来,除了一个文件一个文件比对(效率太低),那么就是把所有修改全部找出来,然后做成新的提交。因为部分代码已经在库里,这部分代码大概率会直接被 git 接受;只有少部分之前冲突的代码需要解决之后才能提交,总量应该不多。所以,这该会是最快的方法。

    想清楚该怎么做之后,就该制定方案了:

    1. 先备份代码。
    2. 回退到启动开发时,切出来的分支。假设 commit 是 b1:git reset b1 --mixed。这一步会将 b1 之后的变更,作为未提交的部分,集中起来,排除版本信息。
    3. 保留这部分的代码到暂存区:git stash
    4. 切到开发分支的最新版本:git checkout B
    5. 弹出暂存的代码 git stash pop
    6. 可能会失败,因为存在冲突。那就解决冲突。
    7. 解决冲突后,重新提交 PR,然后合并分支。

    总结一下。Git 原理很简单:

    1. 按行对比。
    2. 每次提交都会产生新版本。
    3. 版本是单向的,Git 负责维护版本历史。
    4. 两个无法分辨先后的版本修改了同一行,就有冲突。Git 无法解决冲突,会交给人处理。
    5. 人处理后,产生新版本。
    6. 分支是指针,指向某个版本,并随提交自动前进。

    在此基础上,可能衍生出各种问题,比如开头的那一幕。但是不管问题如何,都离不开根本原理。所以我们面对 Git 问题,不要背套路,要认真分析前因后果,然后选择解决方案,再根据方案制定流程。

    希望这次分享对大家有帮助。如果你对 Git 操作还有什么不清楚不明白的,欢迎提问。如果我上文有错误,也欢迎指出。希望 2024,大家共同进步,再没有会乱搞别人代码的同事。