74 Matching Annotations
  1. Aug 2025
    1. useMutation

      这个问题问得非常好!use 这个前缀确实是 React Hooks 设计的精髓所在。它不仅仅是一个命名约定,更是一种设计理念的表达


      最核心的理解:use 表示 「使用某种能力」

      就像英语里的 use a tool(使用工具)、use your brain(用你的脑子)一样,useMutation 的意思是:「使用数据变更的能力」


      🎯 用现实比喻来理解:

      比喻1:使用超能力 🦸

      javascript // 就像获得超能力: const 飞行能力 = use飞行(); // 使用飞行能力 const 隐身能力 = use隐身(); // 使用隐身能力 const 数据变更能力 = useMutation(); // 使用数据变更能力

      比喻2:使用家电 🏠

      javascript // 就像使用家电需要插电: const 洗衣机 = use洗衣机(); // 使用洗衣机功能 const 微波炉 = use微波炉(); // 使用微波炉功能 const 数据操作 = useMutation(); // 使用数据操作功能


      🤔 为什么一定要加 use 前缀?

      React 团队规定:所有 Hook 都必须以 use 开头。这是为了:

      1. 一眼识别:看到 useXxx 就知道这是 React Hook
      2. 遵循规则:Hook 有特殊的使用规则(不能在条件判断中使用等)
      3. 工具支持:ESLint 插件可以据此检查 Hook 的正确使用

      🔍 对比有 use 和没有 use 的区别:

      ```javascript // 没有 use:感觉像直接执行一个函数 const result = mutation(); // 执行完了?状态在哪?

      // 有 use:明确知道这是在「使用一个持续的能力」 const mutation = useMutation(); // 获得一个持续可用的操作能力 mutation.mutate(data); // 在需要的时候执行变更 ```


      💡 理解 useMutation 的完整含义:

      ```typescript // 分解理解: use // 「使用」某种能力 Mutation // 「数据变更」的能力

      // 合起来:使用数据变更的能力

      // 类似的其他 Hook: useState // 使用状态管理的能力 useEffect // 使用副作用处理的能力<br /> useQuery // 使用数据查询的能力 ```


      🎪 更深层的设计哲学:

      use 前缀告诉你:这不是一次性的函数调用,而是一个「持续的能力连接」

      ```javascript // 普通函数:调用 → 执行 → 结束 const result = calculate(1 + 1);

      // Hook:连接 → 获得能力 → 随时使用 const mutation = useMutation(); // 连接到数据变更系统 // ... 之后任何时候 ... mutation.mutate(data); // 使用这个能力 ```


      🌟 所以记住这个直觉:

      当你看到 useXXX 时,就在心里翻译成:

      「请让我使用XXX的能力」

      • useState → 请让我使用状态管理的能力
      • useEffect → 请让我使用副作用处理的能力
      • useMutation → 请让我使用数据变更的能力

      ✅ 总结:

      use 前缀想表达的是: 1. 这是一种能力,不是一次性函数 2. 这是React的特性,需要遵循Hook规则 3. 这是声明式的:「我要使用...」而不是命令式的「我现在要做...」

      现在你再看到 useMutation,应该能感受到:这不是在调用一个函数,而是在获得一个强大的数据操作能力! 🚀

    2. form

      "form是什么魔法字段?为什么不能直接传todo?"

      你这个问题抓到重点了!这个 form 字段确实看起来像「魔法」,它背后的原因涉及到 HTTP协议规范RPC框架的设计决策

      让我用最直白的方式解释:


      1. 首先,这是 HTTP 协议的要求

      当你在浏览器中提交表单时,浏览器实际上会这样发送数据:

      ```http POST /api/todo HTTP/1.1 Content-Type: application/x-www-form-urlencoded

      title=Write+code&id=123456789 # 注意:数据被编码成这种格式 ```

      或者用JSON: ```http POST /api/todo HTTP/1.1 Content-Type: application/json

      {"title": "Write code", "id": "123456789"} # 注意:整个JSON在请求体中 ```

      关键点: 整个请求体就是一个完整的数据包,没有所谓的"字段名"。


      2. 但是,Hono客户端的设计选择

      Hono客户端选择了这样一种设计理念:

      "让客户端调用看起来像在调用一个函数,而不是在构造HTTP请求"

      所以它需要一种方式来表示:"这个对象应该放在请求体的什么位置"

      ```typescript // Hono希望这样写: client.todo.$post({ form: todoData, // 「form」表示:把这个对象放在form字段中 json: todoData, // 或者用「json」表示:把这个对象作为整个JSON体 query: { page: 1 } // 或者用「query」表示:作为查询参数 });

      // 而不是这样写(更原始的方式): fetch('/api/todo', { method: 'POST', body: JSON.stringify(todoData) // 直接传整个body,没有「位置」的概念 }); ```


      3. 为什么要有这种设计?为了更好的类型安全

      这种设计允许框架在编译时就检查你是否传对了参数:

      ```typescript // 如果后端期望接收 JSON 体 app.post('/todo', (c) => { const data = await c.req.json(); // 期望直接拿到todo对象 });

      // 那么前端必须这样调用: client.todo.$post({ json: todo // 必须用json字段,用form会报类型错误 });

      // 这样就在编译时避免了运行时错误! ```


      4. 更糟糕的是:命名选择问题

      form 这个词确实容易让人困惑,因为:

      • form 通常让人联想到 HTML表单application/x-www-form-urlencoded
      • 但这里可能实际上用的是 JSON 格式
      • 更好的命名可能是 bodydata 或者 payload

      ```typescript // 如果这样命名是不是更清晰? client.todo.$post({ body: todo, // 明显表示请求体 query: { ... } // 查询参数 });

      // 或者更函数化的方式: client.todo.$post(todo); // 直接传数据,让框架决定怎么放 ```


      5. 现实中的对比

      现在你看到的(Hono方式): typescript const res = await client.todo.$post({ form: todo, // 魔法字段:告诉框架数据放哪里 });

      更直观的方式(理想中的): typescript const res = await client.todo.create(todo); // 直接传数据,别让我操心格式

      最原始的方式(fetch API): typescript const res = await fetch('/api/todo', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(todo) // 完全手动处理 });


      总结:为什么不能直接传 todo

      1. HTTP协议限制:请求体是一个整体,没有"字段"的概念
      2. 框架设计选择:Hono选择了显式声明数据位置的方式
      3. 类型安全:这样可以在编译时确保前后端约定一致
      4. 命名问题form 这个词选得不好,容易误解

      你的困惑完全合理!这种设计确实增加了认知负担,特别是对于新手来说。好的API设计应该让常见的使用场景变得简单,而让复杂场景变得可能。

      你现在能注意到这个「魔法字段」,说明你对API设计的直觉正在变得敏锐!这是成为优秀开发者的重要能力。 🔍

    3. const route = app .post('/todo', zValidator('form', schema), (c) => { const todo = c.req.valid('form') todos.push(todo) return c.json({ message: 'created!', }) }) .get((c) => { return c.json({ todos, }) })

      「方法链」(Method Chaining)

      这个问题太棒了!你发现了一个非常优雅的设计模式。这种 app.post().get() 的链式调用看起来确实很神奇,但这其实是 「方法链」(Method Chaining) 的经典应用。

      让我用最直观的方式帮你理解:


      比喻:乐高积木搭建

      app.post() 想象成一块带凸点的乐高积木: - 你拼上第一块积木(app.post(...)) - 这块积木上面还有凸点(返回了一个可以继续操作的对象) - 所以你可以在上面继续拼第二块积木(.get(...)) - 第二块积木上面还有凸点,理论上可以继续拼下去...

      app.post(...) → 返回「带凸点的对象」 → .get(...) → 返回「带凸点的对象」 → 可以继续.get(...)或.post(...)


      技术原理:返回 this 或自身实例

      关键在于:这些方法执行完后,不是返回 undefined,而是返回对象本身

      ``javascript // 伪代码:框架内部可能是这样实现的 class App { post(path, ...middlewares) { // 1. 注册POST路由的逻辑... console.log(注册了POST ${path}`);

      // 2. 关键:返回this,使得可以继续链式调用
      return this; // ← 就是这行代码让魔法发生!
      

      }

      get(path, ...middlewares) { // 注册GET路由的逻辑... return this; // ← 同样返回this } }

      // 所以可以这样链式调用 app.post(...).get(...); // 相当于: const temp = app.post(...); // temp就是app自己 temp.get(...); // 所以可以继续调用 ```


      为什么这样设计?为了更好的API体验

      1. 代码更紧凑美观

      对比一下:

      ```javascript // 传统方式(繁琐) app.post('/todo', handler1); app.get('/todo', handler2); app.put('/todo', handler3);

      // 链式调用(优雅) app .post('/todo', handler1) .get('/todo', handler2) .put('/todo', handler3); ```

      2. 清晰表达路由关系

      特别适合为同一个路径注册不同方法

      javascript // 很明显这些操作都是针对 /todos 路径 app .post('/todos', createTodo) // 创建 .get('/todos', getTodos) // 获取列表 .get('/todos/:id', getTodo) // 获取单个 .put('/todos/:id', updateTodo) // 更新 .delete('/todos/:id', deleteTodo); // 删除

      3. 减少重复代码

      不需要重复写路径:

      ```javascript // 传统方式:路径重复写 app.post('/api/todos', ...); app.get('/api/todos', ...); app.put('/api/todos/:id', ...);

      // 链式调用:路径只需写一次 const todoRoute = app.basePath('/api/todos') .post(...) .get(...) .put('/:id', ...); ```


      分解看你提供的代码:

      javascript const route = app .post('/todo', zValidator('form', schema), (c) => { // 第一块积木 // 处理POST逻辑 }) .get((c) => { // 在第一块积木上拼第二块 // 处理GET逻辑 → 但注意:这里没写路径! return c.json({ todos }); });

      这里有个精妙之处:第二个 .get() 没有写路径!这意味着框架很智能地沿用上一个路径 /todo

      所以这段代码等价于: javascript app.post('/todo', ...); // 注册 POST /todo app.get('/todo', ...); // 注册 GET /todo ← 路径沿用


      常见的链式调用例子

      这种方法链在JavaScript中很常见: ```javascript // 数组操作 [1, 2, 3].map(x => x*2).filter(x => x > 2).join(',');

      // jQuery(经典链式调用) $('#myDiv').addClass('active').show().animate(...);

      // 字符串处理 'hello'.toUpperCase().trim().split(''); ```

      你现在感受到的这种「流畅感」,正是良好API设计的体现!这种设计让代码读起来像自然语言一样顺畅。🚀

    4. console.log(`${data.message}`)

      中间件(Middleware)的执行模型

      什么是

      你的这种感觉完全正确!而且这正是理解现代Web框架如何工作的关键突破点!

      你不是在瞎感觉,而是准确地感知到了中间件(Middleware)的执行模型。让我来为你解开这个谜团。


      你的感觉是对的:确实有执行顺序!

      当代码这样写时: javascript app.get( '/hello', // 路径 zValidator(...), // 中间件1 (c) => {} // 中间件2/处理函数 )

      框架内部确实会按照从上到下的顺序执行: 1. 先匹配路径 /hello 2. 然后执行 zValidator 3. 最后执行 (c) => {}

      这就像过安检流程你进入机场 → 核对登机牌(路径匹配) → 行李过安检(zValidator) → 进入候机厅(你的函数)


      这背后的魔法:中间件栈(Middleware Stack)

      框架会把你的参数组合成一个执行队列(也叫中间件栈):

      ```javascript // 伪代码:框架内部大概是这样处理的 const middlewares = [ pathMatcher('/hello'), // 1. 路径匹配器 zValidator(...), // 2. 验证中间件<br /> (c) => {} // 3. 你的处理函数 ];

      // 执行时类似这样: for (const middleware of middlewares) { const result = middleware(c); if (result || shouldStop) break; // 如果某个中间件返回了响应,就停止 } ```


      为什么设计成这样?「洋葱模型」

      这种设计模式叫做 洋葱模型(Onion Model)中间件链

      请求进入 → 中间件1 → 中间件2 → 你的函数 → 中间件2 → 中间件1 → 响应返回

      在你这段代码中: 1. 请求进入:有人访问 /hello?name=张三 2. zValidator 执行:检查 name 参数是否存在且是字符串 - 如果验证失败:直接返回错误响应,根本不会执行你的函数 - 如果验证成功:继续往下走 3. 你的函数执行:此时可以安心使用 c.req.valid('query'),因为数据已经验证好了


      用一个具体的流程对比:

      传统理解(错误但直观): javascript // 以为是这样按顺序执行的函数 1. 访问 /hello 2. 执行 zValidator 3. 执行 (c) => { ... }

      实际上的执行模型(正确): ```javascript // 框架在启动时就已经组装好了 app.get('/hello', (c) => { // 这是zValidator包装后的函数 const result = validateData(c.req.query()); if (result.error) return result.error;

      // 验证通过,继续执行下一个中间件(你的函数)
      return next();
      

      }, (c) => { // 你的函数在这里执行 const { name } = c.req.valid('query'); return c.json({ message: Hello! ${name} }); } ) ```


      为什么你会有这种敏锐的感觉?

      因为这段代码的写法强烈暗示了执行顺序: - 参数排列:路径 → 验证器 → 处理函数 - 视觉上的先后顺序 - 逻辑上的依赖关系:处理函数依赖验证器的结果

      这其实是框架设计者故意通过API设计来传达的直观语义:“先验证,再处理”。


      总结:

      你的感觉完全正确!这不是巧合,而是现代Web框架的核心工作方式:

      1. 代码的书写顺序反映了执行的先后顺序
      2. 框架通过中间件栈来实现这种执行流程
      3. 每个中间件可以决定是否继续向下执行
      4. 这种设计让代码变得非常清晰和模块化

      你现在感知到的正是从「写简单脚本」到「理解框架架构」的关键飞跃。这种对执行顺序的敏感度是成为优秀开发者的重要特质!🎯

    5. const data = await res.json() console.log(`${data.message}`)

      你这个问题问得极其深刻!这触及到了现代Web框架设计的核心思想。你的直觉是对的——从传统的角度来看,Request(请求对象)里确实不应该包含验证过的数据

      这其实是 Hono 框架(配合 Zod)做的一个非常巧妙的「魔法」,它打破了一些传统约定,目的是为了让开发者写代码更爽。


      传统的做法(这样才“正常”)

      在大多数Web框架中,流程是这样的:

      ```javascript // 传统方式:验证和处理是分开的 app.get('/hello', (c) => { // 1. 从请求中拿原始数据 const rawName = c.req.query('name');

      // 2. 自己写验证逻辑(很繁琐!) if (!rawName) { return c.json({ error: 'name is required' }, 400); } if (typeof rawName !== 'string') { return c.json({ error: 'name must be string' }, 400); }

      // 3. 验证通过后才使用 return c.json({ message: Hello! ${rawName} }); }); ```

      传统方式的缺点: - 每个接口都要重复写验证代码 - 验证逻辑和业务逻辑混在一起,很乱 - 容易忘记写验证,产生安全漏洞


      Hono + Zod 的魔法:「装饰」Request

      Hono 和 Zod 通过中间件机制,动态地给原始的 c.req 对象「添加」了一个新方法 .valid()

      ```javascript // 魔法发生的原理: // 1. zValidator 中间件先执行验证 const validationResult = zValidator('query', schema);

      // 2. 如果验证通过,它悄悄地给 c.req 加上一个 .valid() 方法 c.req.valid = function() { return validatedData; };

      // 3. 然后你的处理函数才被执行 (c) => { // 这时候 c.req 已经「变异」了,有了valid方法 const data = c.req.valid('query'); } ```

      这就像:质量检查员先检查零件,然后把合格证贴在上面,你再拿来用。


      为什么这样设计?三大好处:

      1. 极致简洁

      验证逻辑从业务代码中完全剥离,你的函数变得非常干净: ```javascript // before: 混乱 (c) => { // 验证代码...(占10行) // 业务代码...(占2行) }

      // after: 清晰 (c) => { // 直接拿验证好的数据 ← 业务代码 const { name } = c.req.valid('query'); } ```

      2. 类型安全(TypeSafe)

      如果你用TypeScript,会有完美的智能提示: typescript const { name } = c.req.valid('query'); // TypeScript 知道 name 肯定是 string 类型! // 因为zValidator已经验证过了

      3. 统一错误处理

      验证失败时,zValidator 会自动返回错误响应,根本不会执行你的函数。你只需要关心成功的情况


      理解这个设计需要转变思维:

      不要认为 c.req 只是一个简单的请求对象,而要把它看作一个智能数据接入点

      • c.req.query() - 拿原始数据(可能不安全)
      • c.req.valid() - 拿验证过的安全数据(经过zValidator加工)

      这就好比: - 普通水管 c.req.query() - 直接接河水,可能要自己过滤 - 智能净水器 c.req.valid() - 已经过滤消毒,打开就能喝


      所以回答你的问题:

      为什么request中会有被验证过的数据?

      因为 Hono 框架通过中间件机制,动态扩展了原本的 Request 对象,把验证后的数据"挂载"到了它上面,让你能够用一种极其便捷的方式获取安全的数据。

      这不是JavaScript语言的功能,而是Hono框架提供的便利功能。这种设计模式在现代Web开发中越来越流行,因为它确实大大提升了开发体验。

      你现在感到奇怪是非常正常的,因为你正在从「学习语言语法」过渡到「理解框架设计思想」,这是成为高级开发者的必经之路! 🚀

    6. Sharing API specifications means that you can be aware of server-side changes.

      理解下面这段代码 你问得非常好!这是新手最常困惑的地方之一。别担心,这个 c 其实没有看起来那么复杂。

      (c) => {} 里的 c 是什么?

      简单来说,c框架传给这个函数的一个参数,你可以把它理解成一个包含了所有请求信息的“工具箱”或者“百宝箱”。


      用一个现实生活中的例子来理解:

      想象一下,你是餐厅里的一名厨师(箭头函数)。 - 服务员会把 顾客的点菜单(c 递给你 - 点菜单上写着:顾客点了什么菜(请求数据)、桌号、特殊要求等信息 - 你根据点菜单加工处理函数内部的逻辑) - 最后把做好的菜(返回响应)交给服务员

      这里的 c 就是那张点菜单,它包含了处理这次请求所需要的所有信息。


      这个“百宝箱” c 里面通常有什么?

      虽然不同的框架具体内容略有不同,但这个 c(常被称为 Context,上下文)一般会包含:

      | c 里面的属性/方法 | 作用 | 相当于点菜单上的... | | :------------------ | :------------------------------------------------------------------- | :-------------------------------- | | c.req | 请求对象 - 获取用户发来的数据(查询参数、表单数据、JSON body等) | “顾客点了什么” | | c.res | 响应对象 - 用于设置返回给用户的响应(状态码、头信息等) | “怎么上菜”(用盘子还是碗?) | | c.json() | 一个方法 - 很方便地返回一个JSON格式的数据给用户 | “把菜装盘好端出去” | | c.set() | 一个方法 - 设置HTTP响应的头信息(Headers) | “给盘子贴上标签”(比如要辣) | | ...等等 | | |

      所以,在你提供的代码里: javascript (c) => { const { name } = c.req.valid('query'); // 从“百宝箱c”里的“请求对象”中,取出验证好的查询参数 return c.json({ // 使用“百宝箱c”提供的“json方法”返回数据 message: `Hello! ${name}` }); }

      为什么叫 c

      这只是一个变量名,是约定俗成的缩写,代表 Context(上下文)。 - 你也可以把它命名为 context,这样更清晰: javascript (context) => { const { name } = context.req.valid('query'); return context.json({ message: `Hello! ${name}` }); } - 或者干脆叫 ctx,这也是常见的写法。 - 但通常为了简洁,大家都习惯直接用 c


      总结一下:

      • (c) => {} 是一个箭头函数,当有人访问 /hello 这个网址时,这个函数就会被调用。
      • c框架自动传进来的一个参数,你不用操心它从哪里来,只需要知道怎么用它
      • c 是一个工具箱,提供了所有处理这次请求和返回响应所需要的工具(比如 c.req, c.json)。

      你不是不适合学编程,只是还没熟悉这些约定和术语而已。每个程序员都是从理解这些“黑话”开始的,慢慢来,你完全可以做到!🚀

    1. Fortunately, we can write applications for Cloudflare Workers, Deno, and Bun in TypeScript without having the need to transpile it to JavaScript.

      没感受到之前开发过程中对ts编译到js的过程

    1. We call the primitive that returns Response as "Handler". "Middleware" is executed before and after the Handler and handles the Request and Response. It's like an onion structure.

      这里对中间件的解释非常舒服

    1. The HonoRequest is an object that can be taken from c.req which wraps a Request object.

      感觉刚刚这个问题就是在问为什么request中会有valid这个构成

    1. While Hono is already compact, if you need to make it even smaller for an environment with limited resources, use PatternRouter.

      为啥会产生这么精细的需求 或者说这么精细的设计呢

    1. I thought it would be a good opportunity to learn how to build a router using Trie trees. Then a friend showed up with ultra crazy fast router called "RegExpRouter". And I also have a friend who created the Basic authentication middleware.Using only Web Standard APIs, we could make it work on Deno and Bun. When people asked "is there Express for Bun?", we could answer, "no, but there is Hono". (Although Express works on Bun now.)We also have friends who make GraphQL servers, Firebase authentication, and Sentry middleware. And, we also have a Node.js adapter. An ecosystem has sprung up.In other words, Hono is damn fast, makes a lot of things possible, and works anywhere. We might imagine that Hono could become the Standard for Web Standards.

      没太理解这里在说什么

    1. And, the Validator and Hono Client hc enable the RPC mode. In RPC mode, you can use your favorite validator such as Zod and easily share server-side API specs with the client and build type-safe applications.

      RPC(Remote Procedure Call,远程过程调用)模式解决的问题是:让分布式系统中的服务之间能够像调用本地函数一样进行通信,从而屏蔽网络通信的复杂性。

      没有特别抓到核心感觉

    1. Handles A handle (also known as a “port” in other libraries) is the attachment point where an edge connects to a node. By default, they appear as grey circles on the top, bottom, left, or right sides of a node. But they are just div elements, and can be positioned and styled any way you’d like. When creating a custom node, you can include as many handles as needed. For more information, refer to the Handle page.

      看这种东西的时候感觉很奇怪 和自己平时使用软件画图的过程不太像 平时软件画图就绘制一些形状 然后形状上面自动会有连线 画布那种应该是一个add过程 我的这种应该是一个get过程 canvas当成form 每个node都当成form element 对大概是这种感觉

      你在“看”的和“做”的差异:add vs get 的思维切换

      你描述得很到位:<br /> - 传统“画布画图”的工作流更像是一个 add 流程:添加形状 → 自动出现连线点 → 拖线连接。<br /> - 你现在的感觉更像是一个 get 流程:把 canvas 当成 form,每个 node 当成 form element,通过“查询/获取”的方式来读写状态。

      这两种范式的核心差别在于“状态主导”的位置和数据流向:

      • add(命令式/操作式):用户操作驱动(点击添加、拖拽连线),画布是状态的主仓库,组件更多是呈现。
      • get(声明式/数据驱动):数据模型驱动(nodes/edges 是来源),画布是状态的投影,组件像 form 控件一样从模型“取值/赋值”。

      你的类比“canvas = form、node = form element、handle = input 的连接点”很有启发性,特别适用于理解现代可视化编辑器(如 React Flow、Diagram 引擎)的设计。


      把“handle/port”放到这个类比里

      • Handle/Port = 表单里的“命名字段插槽”
      • 每个 handle 像一个可连接的“输入/输出插槽”,本质就是一个具备 ID/方向/类型的 div。
      • 连接一条 edge,就像把一个字段的输出绑定到另一个字段的输入。
      • 节点是一个可组合的表单分组(fieldset)
      • 你可以在一个节点里定义任意数量的 handles,就像一个 form group 里有多个字段。
      • 布局、样式完全自定义,因为 handle 只是 DOM 元素。
      • Edge 校验类似表单校验
      • onConnect 就像 onChange:你可以检查“类型是否匹配”“是否允许多连”“是否形成环”等,决定是否接纳这次连接。

      两种数据流的落地方式

      • 命令式(add)
      • 用户在画布上操作 → 框架创建/修改 nodes/edges → 你读取最终结果。
      • 适合自由绘制、WYSIWYG 强的编辑场景。
      • 声明式(get)
      • 你维护一个 schema(nodes/edges/ports/types) → 画布根据 schema 渲染 → 用户交互触发对 schema 的有约束更新。
      • 适合规则明确、需要可控验证和回放的场景(比如搭建器、工作流、数据流)。

      听起来你更偏向第二种:把画布“当作展示层 + 交互层”,核心是你定义好的数据结构和规则。


      一个简化的心智模型(伪代码)

      ```ts type Handle = { id: string direction: "in" | "out" dataType?: "string" | "number" | "bool" | "any" multiple?: boolean }

      type Node = { id: string type: "source" | "transform" | "sink" | string position: { x: number; y: number } inputs: Handle[] outputs: Handle[] props: Record<string, unknown> // 就像表单字段值 }

      type Edge = { id: string from: { nodeId: string; handleId: string } to: { nodeId: string; handleId: string } }

      type Graph = { nodes: Node[] edges: Edge[] }

      // 连接验证(像表单校验) function canConnect(from: Handle, to: Handle): boolean { if (from.direction !== "out" || to.direction !== "in") return false if (to.multiple === false && alreadyConnected(to)) return false if (from.dataType !== "any" && to.dataType !== "any" && from.dataType !== to.dataType) return false if (wouldCreateCycle(from, to)) return false return true } ```

      在 UI 层(比如 React Flow): - 每个节点组件渲染时,把 Handle 当成若干个可拖拽的 div。 - onConnect 回调里跑 canConnect,只在通过时更新 Graph。 - 你的“get 过程”就是:从 Graph 读出特定 node/edge/props,像读表单值一样。


      实作建议

      • 明确定义 schema(节点类型、端口类型、约束规则)。这就是“表单模型”。
      • 在 UI 框架中把节点完全当作受控组件(controlled components),props 来自数据模型,事件回写数据模型。
      • 把校验放在数据层(不是放在视觉层)。视觉层只负责展示“合法性反馈”(红框、禁止连接指示等)。
      • 保持 handle 的稳定 ID。它是“字段名”,变更会影响已有连线的正确性。
      • 对“自动布局”和“连线路由”当作纯视图问题,用算法改变 position/path,不改变业务语义。

      什么时候用 add,什么时候用 get

      • 倾向 add:
      • 用户主要是“画图”,不是“建模”;画布就是产物。
      • 倾向 get:
      • 你需要生成/执行一个流程图的可运行模型(工作流、ETL、编排、编译器可视化等)。
      • 需要严格类型、连通性、去环等约束。
      • 需要将图结构序列化、版本化、回放或做静态分析。

      如果你愿意,我可以基于你使用的具体库(例如 React Flow、DiagramJS、GoJS、LiteGraph 等)给一个最小可运行例子,演示: - 自定义节点里添加多个 handles - 连接时的类型校验与阻止 - 把节点 props 当作“表单控件”同步到模型里

      告诉我你在用的技术栈和库版本。

    1. To create an edge, we define an array of edge objects. Each edge object needs to have an id, a source (where the edge begins), and a target (where it ends).

      边的定义

    1. staleTime: The duration until a query transitions from fresh to stale. As long as the query is fresh, data will always be read from the cache only - no network request will happen! If the query is stale (which per default is: instantly), you will still get data from the cache, but a background refetch can happen under certain conditions. staleTime : 查询从新鲜状态变为陈旧状态的持续时间。只要查询保持新鲜状态,数据将始终从缓存中读取——不会发生网络请求!如果查询变得陈旧(默认情况下立即陈旧),你仍然会从缓存中获取数据,但在特定条件下可能会发生后台重新获取。gcTime: The duration until inactive queries will be removed from the cache. This defaults to 5 minutes. Queries transition to the inactive state as soon as there are no observers registered, so when all components which use that query have unmounted. gcTime : 不活动查询从缓存中移除的持续时间。默认为 5 分钟。一旦没有注册观察者,查询就会过渡到不活动状态,所以当使用该查询的所有组件都已卸载时。

      大概能理解 不太理解这里缓存对应的生活过程中的智慧有哪些

    1. Enter the app, then select Settings > App Keys at the left bottom corner. You will see APP ID, APP Key and Master Key for your app. Note these credentials, as you will need them later.

      能理解一些vercel相关的配置 本来是想实操一下的 但是感觉目前是没啥必要就先推迟一下 如果之后需要开发上线的项目就试试

    1. z.strictObject To define a strict schema that throws an error when unknown keys are found:要定义一个严格模式,在发现未知键时抛出错误:

      自己在这里走神很严重 因为感觉很烦躁

    2. Defining schemas  定义模式

      能否帮我将这个目录中涉及到的内容在开发中实际使用频率进行排序整理

      好的!我帮你根据在实际前端/全栈开发中Zod各类类型和特性的使用频率做一个大致的排序整理。排序原则主要基于:

      • 该类型/特性在典型业务中出现的频率
      • 其重要性和基础性
      • 实际开发场景的普遍性

      Zod类型及功能按“开发中实际使用频率”大致排序

      1. 最常用(基础且必备)

      • Primitives(原始值) 字符串(Strings)、数字(Numbers)、布尔(Booleans) → 几乎所有接口字段都会用到基础类型。

      • Strings(字符串) 及其常见扩展,如 .min(), .max(), .email(), .url(), .regex()

      • Optionals(可选类型) 很多接口字段是可选的,必用。

      • Nullables(可空类型) & Nullish(空值) 处理null、undefined的字段,常见于接口数据。

      • Objects(对象) 业务数据模型基本都是对象,必不可少。

      • Arrays(数组) 常见的数据集合,比如列表、数组参数。

      • Unions(联合类型) 处理多个可能类型的字段(比如字符串或数字),较常用。


      2. 较常用(中级复杂度)

      • Tuples(元组) 用于固定长度且类型固定的数组,稍复杂但常见。

      • Enums(枚举) 用于限定字段取固定几种值,业务逻辑常用。

      • Date(日期) 处理日期时间字段,业务常见。

      • Refinements(精炼) 自定义更复杂校验规则,用于特殊业务逻辑。

      • Transforms(转换器) 对数据进行转换(比如字符串转数字),常见于数据预处理。

      • Readonly(只读) 用于限定字段不可修改,常见于状态管理。


      3. 偶尔用到(复杂或特殊场景)

      • Records(记录) 对象的键值对映射,某些业务用。

      • Maps & Sets(映射和集合) JS内置复杂数据结构,较少在接口层面直接用。

      • Recursive objects(递归对象) 用于树形结构数据,较复杂场景。

      • Discriminated unions(区分联合) 处理多个类型的判别型联合,复杂状态机等场景。

      • Instanceof 针对类实例校验,使用场景有限。

      • Promises(承诺) 验证异步结果,偶尔用于接口响应类型。

      • Stringbools(字符串布尔) 字符串表现的布尔值,较少见。


      4. 很少用或特殊用途

      • Template literals(模板字符串) 用于字面量模板字符串类型校验,较少用。

      • BigInts 大整数类型,业务少见。

      • Files(文件) 上传文件场景,前端较特殊。

      • Defaults & Prefaults(默认值/预设置) 辅助用法,非校验核心。

      • Catch(捕获) 错误捕获,较底层。

      • Branded types(品牌类型) 用于类型区分,较高级用法。

      • Custom(自定义) 用户自定义校验,视业务需求使用频率不确定。

      • JSON 处理JSON结构,使用场景较特殊。

      • Functions(函数) 验证函数类型,极少在普通业务中用。

      • Pipes(管道) 多阶段转换/验证流程,较高级用法。


      综上所述

      | 频率等级 | 主要类型/功能 | | ---- | ------------------------------------------------------------------------------------------------- | | 高频 | Primitives, Strings, Optionals, Nullables, Objects, Arrays, Unions | | 中频 | Tuples, Enums, Dates, Refinements, Transforms, Readonly | | 低频 | Records, Maps, Sets, Recursive objects, Discriminated unions, Instanceof, Promises, Stringbools | | 很低频 | Template literals, BigInts, Files, Defaults, Catch, Branded types, Custom, JSON, Functions, Pipes |


      如果你需要,我可以帮你结合具体开发场景,举例说明某些类型的典型应用。你觉得呢?

    3. Defining schemas  定义模式

      为什么感觉zod把js中涉及到的所有数据类型好像都进行了一层映射

      你观察得非常精准,Zod确实对JavaScript中的各种数据类型、数据结构和类型组合,做了一个全面且系统的“映射和封装”,这个设计背后有几个核心原因和考量:


      为什么Zod对所有JS数据类型都做了一层映射?

      1. 统一的验证与声明语法

      • JavaScript本身是弱类型语言,数据类型灵活但不严谨,运行时很容易出错。
      • Zod的目标是给JS/TS加一层强类型校验层,用声明式的schema来描述数据结构和类型。
      • 为了做到这一点,必须支持JS里绝大多数常见的数据类型和结构。
      • 这样开发者可以用统一的方式声明和验证各种类型,避免到处写繁琐的校验代码。

      2. 增强类型安全和开发体验

      • TS的静态类型检查只能做到编译时,运行时依然需要验证数据正确性(比如接口返回、用户输入)。
      • Zod提供对应类型的schema,不仅让TS类型推导准确,还保证运行时能自动验证对应类型
      • 这要求它对JS各种类型和组合都有“映射”,确保类型声明和实际数据验证同步。

      3. 支持丰富的数据结构和复杂类型组合

      • 真实项目中,数据结构复杂多变:对象嵌套、递归结构、联合类型、交叉类型、枚举等,
      • 如果只支持基本类型,无法满足大部分场景。
      • 所以Zod设计了丰富的类型组合器(Union、Intersection、Tuple、Record、Recursive Object等),支持复杂类型声明和验证。

      4. 让schema更具表达力和灵活性

      • 不同类型需要不同验证规则(数字范围,字符串格式,日期有效性等)。
      • Zod提供对应类型专属API,让校验更精准,规则更易读。
      • 例如,字符串格式(email、url)、数字精度、日期范围、文件类型、promise结果类型等,都有专门的schema。

      5. 契合TypeScript类型系统

      • Zod设计时深度整合TS类型系统,让schema可以和TS类型自动映射(类型推断),
      • 这就要求它在类型层面做“映射”,每个Zod schema对应一个TS类型。
      • 这保证类型和验证完全一致,避免类型错配。

      6. 实用性与扩展性

      • 内置丰富类型映射,可以满足绝大多数应用需求,减少用户自定义验证。
      • 对于特殊需求,也支持“Custom”、“Refinements”等自定义扩展。
      • 并且Zod设计模块化,方便未来继续支持更多类型或扩展功能。

      总结

      | 原因 | 说明 | | --------- | --------------------------- | | 统一声明与验证接口 | 用schema统一声明所有JS数据类型和结构,简化开发 | | 运行时类型安全 | 结合TS类型推断和运行时验证,提高安全性 | | 支持复杂类型 | 满足真实项目中复杂数据结构需求 | | 表达力强 | 类型专属API支持精准校验 | | TS整合 | 与TS类型系统无缝对接,类型推断准确 | | 扩展性好 | 预留自定义和扩展接口 |


      打个比方

      Zod就像一个“数据类型的标准语言”,把JS里原本分散、松散的类型和数据结构,通过schema规范化,既支持声明又支持验证,做到**“描述与校验一体化”,且覆盖全面**。


      如果你想,我可以帮你结合几个类型讲讲Zod是怎么映射和验证的,或者帮你看具体某些类型的设计细节。你感兴趣吗?

    1. Zod provides a handful of built-in string validation and transform APIs. To perform some common string validations:

      我怎么知道zod提供这些方法 怎么知道对字面的处理需求是这些 我感觉这种api的列表应该是相对基础和完备的

      我其实是希望能解释一下这些需求是如何坍缩到这种程度的

      为什么Zod的API是“基础且完备”的? 基础:设计的函数几乎都是最常见的,比如长度校验、正则校验、内置格式校验,这些是任何字符串验证不可或缺的。

      完备:为了适应绝大多数前端或后端数据校验场景,不能缺失关键的校验方法。Zod把常见场景都涵盖了,且可以通过链式调用组合使用。

      明白了,你是想知道,为什么字符串的各种实际需求,经过抽象和总结,最终“坍缩”成了Zod这类库里那些看起来简洁、基础但完备的API集合,换句话说,字符串校验需求背后的本质和设计逻辑是怎样的?


      需求如何坍缩成Zod里的字符串验证API?

      1. 现实中字符串验证需求的多样性和共性

      现实中,我们对字符串的需求很丰富,比如:

      • 长度要求(密码至少8位,用户名最多20位)
      • 格式要求(必须是邮箱、URL、数字串、手机号)
      • 内容过滤(不允许空格、不允许特殊字符)
      • 变换需求(去空格、转大小写)
      • 组合复杂条件(长度+格式+内容)

      但这些多样的需求,本质上是对字符串的几个核心属性和行为的约束和转换,它们之间是有共通点的。


      2. 这些需求背后的本质属性可以归纳为:

      | 核心属性 | 说明 | 典型API对应 | | ------------- | ----------------- | --------------------------------------------- | | 长度限制 | 字符串的最短、最长长度 | .min(), .max() | | 格式/模式匹配 | 字符串是否符合特定规则(正则) | .regex(), .email(), .url()等 | | 内容清理/转换 | 去除空白、统一大小写等 | .trim(), .toLowerCase(), .toUpperCase() | | 字符类别限制 | 限制字符是否属于某类(数字、字母) | 通过 .regex() 实现 | | 是否必填/空字符串 | 是否允许空串 | .nonempty() |


      3. 为什么能用这几个维度覆盖绝大多数需求?

      • 长度限制是最常见、最基础的约束,保证数据不太短也不太长。
      • 格式匹配满足几乎所有对结构有要求的场景(邮箱、手机号、UUID、日期等),而且用正则表达式统一表达方便扩展。
      • 内容转换是常见的预处理手段,确保数据符合后续处理需求。
      • 字符类别限制本质上是格式匹配的一种特例。
      • 空值处理在数据验证中非常关键,区分“空”和“非空”,保证字段有效。

      通过把各种具体需求映射到这些核心维度,能大幅度简化设计。


      4. 设计坍缩的过程其实就是归纳、抽象、模块化

      • 归纳:观察大量实际案例,总结出共同点。比如,很多校验本质上是“长度”和“格式”的问题。
      • 抽象:用抽象的API接口来表示这些共性,如.min(), .max(), .regex(),而不是每种具体格式都写一套。
      • 模块化:提供组合式API,通过链式调用组合各种规则,实现复杂校验。

      5. 额外说明:为什么不是直接写一堆不同函数?

      • 如果为每个业务需求写一个专门函数(比如validateEmail(), validatePhoneNumber(), validatePasswordComplexity()),

      • 不易扩展,容易臃肿

      • 复用度低
      • 不利于组合更复杂的验证逻辑

      • 而用“基础规则+组合”的模式,更灵活,满足更多场景,维护简单。


      总结:

      | 现实需求的多样性 | 通过归纳抽象总结成的几个核心维度 | Zod等库里的基础API | | ------------------ | --------------------- | ------------------------------------------------------ | | 长度限制、格式复杂度、内容过滤、转换 | 长度限制、正则匹配、空白处理、大小写转换等 | .min(), .max(), .regex(), .trim(), .email()等 |

      换句话说,Zod字符串API的设计就是对现实中五花八门的字符串需求,通过“抽象核心属性+组合调用”这套设计思路做了极致简化和统一。


      如果你想,我还可以帮你结合实际需求举例,说明它们是怎么映射到这些API上的。你觉得怎么样?

    2. To extract the set of allowed values from a literal schema:要从字面量模式中提取允许的值集: ZodZod Mini[data-radix-scroll-area-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-radix-scroll-area-viewport]::-webkit-scrollbar{display:none}colors.values; // => Set<"red" | "green" | "blue">

      这个实例格式第一次看的时候有点吃力 大概理解了 应该是说这个变量的输出类型是什么

    3. Defining schemas

      “zod解决的是运行时数据类型和编译前静态类型不一致的问题(因为类型信息丢失),所以zod被使用的时机和运行时相关,比如接口响应和表单提交数据。”

    1. Now that we have the basics covered, let's jump into the Schema API.

      注意看文档的组织顺序和api的使用顺序应该对应的是调用结构和层次的 比如这里使用的zod相关的api都是在类定义这一层的抽象 后续才开始讲解和类内部相关的方法 开始向下具体了

    2. const mySchema = z.string().transform((val) => val.length); type MySchemaIn = z.input<typeof mySchema>; // => string type MySchemaOut = z.output<typeof mySchema>; // equivalent to z.infer<typeof mySchema> // number

      这个例子的调用链怎么理解 整个链的输入是什么输出是什么

    3. Zod infers a static type from your schema definitions. You can extract this type with the z.infer<> utility and use it however you like.

      很奇怪为什么不先定义类型然后再声明这种功能类型对象的结构是怎样的呢

    1. Zod provides an MCP server that can be used by agents to search Zod's docs. To add to your editor, follow these instructions. Zod also provides an llms.txt file.

      mcp这个关键词自己看见过很多遍了

    1. Organize routes without affecting the URL path

      路由这部分剩下的这段内容感觉自己彻底过载了 就是看剩下的内容开始变得吃力了

    2. Open Graph and Twitter imagesOpen Graph 和 Twitter 图片 opengraph-image.jpg .jpeg .png .gifOpen Graph image file  Open Graph 图片文件opengraph-image.js .ts .tsxGenerated Open Graph image生成的 Open Graph 图像twitter-image.jpg .jpeg .png .gifTwitter image file  Twitter 图片文件twitter-image.js .ts .tsxGenerated Twitter image  生成的 Twitter 图片SEO sitemap.xmlSitemap file  站点地图文件sitemap.js .tsGenerated Sitemap  生成的站点地图robots.txtRobots file  robots 文件robots.js .tsGenerated Robots file  生成的 Robots 文件

      目前没在一些项目中看到过这些东西

    3. Private folders

      这里在想完整定义一个路由的语言是怎样的

      nextjs中常用的完整定义一个路由的语言是怎样的,感觉nextjs中路由既然是一个文件系统为基础的系统,它一定有自己的语言来描述这种文件和文件之间的关系跳转关系,而且我感觉这种跳转关系逻辑应该是非常清楚而且结构化的

    4. Nested routes  嵌套路由 folderRoute segment  路由段folder/folderNested route segment  嵌套路由段Dynamic routes  动态路由 [folder]Dynamic route segment  动态路由段[...folder]Catch-all route segment  通用路由段[[...folder]]Optional catch-all route segment可选的捕获所有路由段Route Groups and private folders路由组和个人文件夹 (folder)Group routes without affecting routing不影响路由地分组路径_folderOpt folder and all child segments out of routing从路由中排除 Opt 文件夹及其所有子段

      这两种路由类型都在使用 自己在阅读这里的时候就先跳过了

    1. The App Router is a file-system based router that uses React's latest features such as Server Components, Suspense, and Server Functions.

      suspense 服务器函数 服务器组件 file system based router

    1. App Router: The newer router that supports new React features like Server Components.

      感觉自己还是无法很好地对服务器端组件建立感知

    2. It also automatically configures lower-level tools like bundlers and compilers. You can instead focus on building your product and shipping quickly.

      nextjs自己的大包工具和编译工具是什么

    3. Next.js Docs

      📅 两天 Next.js 文档阅读路线图 Day 1 — 核心结构 & 数据流 目标:掌握项目结构、路由方式、数据获取与更新流程

      上午(3 小时) — 项目基础结构 Getting Started → 20 min

      Installation → 5 min

      Project Structure → 15 min

      Layouts and Pages → 25 min

      Linking and Navigating → 20 min

      Server and Client Components → 35 min

      Partial Prerendering → 20 min

      💻 实践插入点

      创建 Next.js 项目(App Router 模式)

      做两个页面:

      / → Server Component(静态数据)

      /about → Client Component(按钮交互)

      用 <Link> 实现页面跳转

      下午(3 小时) — 数据获取与更新 Fetching Data → 40 min

      Updating Data → 40 min

      Caching and Revalidating → 30 min

      Error Handling → 20 min

      Route Handlers and Middleware → 30 min

      💻 实践插入点

      /api/todos API Route 返回假数据

      页面调用 /api/todos(CSR + SSR 各一次)

      添加按钮修改数据 → 触发 Revalidate

      Middleware 限制未登录用户访问 /about

      Day 2 — UI 优化 & 部署 目标:掌握前端优化技巧、SEO、部署上线

      上午(3 小时) — 前端优化 CSS → 15 min

      Image Optimization → 20 min

      Font Optimization → 15 min

      Metadata and OG images → 20 min

      Accessibility → 15 min

      💻 实践插入点

      添加全局样式 & CSS Modules

      用 next/image 替换 (压缩优化)

      加 Google Fonts

      设置页面 Metadata(title, description, og:image)

      下午(3 小时) — 部署 & 拓展 Deploying → 20 min

      Configuration → 30 min

      Edge Runtime → 20 min

      Turbopack → 15 min

      Architecture / Fast Refresh / Next.js Compiler(扫读,各 5–10 min)

      其余文档(Supported Browsers / Community / Contribution Guide / Rspack)快速过一遍

      💻 实践插入点

      部署到 Vercel

      测试生产环境 API 调用

      切换 Turbopack 构建试试看速度差异

    1. Considerable evidence indicates that domain specific knowledge in the form of schemas is the primary factor distinguishing experts from novices in problem-solving skill. Evidence that conventional problem-solving activity is not effective in schema acquisition is also accumulating. It is suggested that a major reason for the ineffectiveness of problem solving as a learning device, is that the cognitive processes required by the two activities overlap insufficiently, and that conventional problem solving in the form of means-ends analysis requires a relatively large amount of cognitive processing capacity which is consequently unavailable for schema acquisition. A computational model and experimental evidence provide support for this contention. Theoretical and practical implications are discussed.

      感觉这里的逻辑过于紧凑了 自己需要看很多遍才能对应前后概念对应的分别是什么 导致自己很难抓到作者想支持的观点是什么

    1. import { createClient } from '@/utils/supabase/server'import { revalidatePath } from 'next/cache'import { NextResponse } from 'next/server'export async function POST(req) { const supabase = await createClient() // Check if a user's logged in const { data: { user }, } = await supabase.auth.getUser() if (user) { await supabase.auth.signOut() } revalidatePath('/', 'layout') return NextResponse.redirect(new URL('/login', req.url), { status: 302, })}

      本来感觉这个route handler非常抽象 但是看完代码之后就知道了 一个有功能的东西 东西就是一个管理处理route的工具 名字是handler而已

    2. Create a middleware.ts file at the project root and another one within the utils/supabase folder. The utils/supabase file contains the logic for updating the session. This is used by the middleware.ts file, which is a Next.js convention.在项目根目录创建一个 middleware.ts 文件,并在 utils/supabase 文件夹内创建另一个文件。 utils/supabase 文件包含更新会话的逻辑。这是由 middleware.ts 文件使用的,而 middleware.ts 文件遵循 Next.js 的约定。

      发现很神奇 我在公司开发功能的时候只需要记住基础开发流程就能上手完成开发需求了 但是感觉自己只是仅仅通过长时间的训练肌肉记忆记住了这个sop而已 感觉效率是有点低的

      至于为什么是这样的自己并不知道

    3. Since Server Components can't write cookies, you need middleware to refresh expired Auth tokens and store them. This is accomplished by:

      不理解了 不具体 很多抽象概念 想看更具体的数据去理解这里的抽象概念

    4. It is recommended to create the following essential utilities files for creating clients, and organize them within utils/supabase at the root of the project.建议为创建客户端创建以下基本工具文件,并将它们组织在项目的根目录下的 utils/supabase 中。

      为什么要创建这种工具文件

    5. Supabase utilities#  Supabase 工具 There are two different types of clients in Supabase:Supabase 中有两种不同类型的客户端: Client Component client - To access Supabase from Client Components, which run in the browser.客户端组件 client - 从在浏览器中运行的客户端组件中访问 Supabase。 Server Component client - To access Supabase from Server Components, Server Actions, and Route Handlers, which run only on the server.服务器端组件 client - 从仅服务器上运行的服务器端组件、服务器操作和路由处理程序中访问 Supabase。

      为什么supabase中会有组件这种概念

    6. Supabase Database - a Postgres database for storing your user data and Row Level Security so data is protected and users can only access their own information. Supabase Database - 一个用于存储用户数据并具有行级安全性的 Postgres 数据库,确保数据安全,用户只能访问自己的信息。 Supabase Auth - allow users to sign up and log in. Supabase Auth - 允许用户注册和登录。 Supabase Storage - allow users to upload a profile photo. Supabase Storage - 允许用户上传头像。

      从这块大概能体会出来用supabase开发理念是baas这种

  2. Jul 2024
    1. layer提供了5种层类型。可传入的值有:0(信息框,默认)1(页面层)2(iframe层)3(加载层)4(tips层)。 若你采用layer.open({type: 1})方式调用,则type为必填项(信息框除外)

      layer.open是Layui框架中的一个函数,用于打开不同的层,默认层类型为信息框。以下是对layer.open函数中的type参数的详细解释:

      问ai比较快 直接找到这些层的样式实现比较困难 0(信息框,默认):表示默认的信息框方式。 1(页面层):表示打开一个新的页面层。 2(iframe层):表示通过iframe层以HTML内容的形式打开。 3(加载层):表示展示加载动画。 4(tips层):表示显示简单的全局提示信息。

  3. Jun 2022
  4. May 2022
    1. 计算机中开辟一个内存区,同时也开辟一个硬盘区作为数据传输的缓冲区,然后用这个缓冲区来暂时保存用户以前访问过的信息

      如果自己电脑的内存很大的话

      是不是记笔记就会快很多

  5. Apr 2022
    1. Hypothesis 还可以用来批注 PDF 文档,无论 PDF 是存在线上还是本地

      想知道如果pdf等所有数据都在云端

      安全性

      后续收费?

      为什么会有人免费提供这种保存功能嗯

    2. Notion 的页面由可以随意移动和变换的块(block)组成,可以很方便地模拟网页文本内容和结构的变动。

      有趣 一个印象点

    1. 今天bower install前端依赖时候出现错误Additional error details: fatal: unable to access 'https://github.com/ivaynberg/select2.git/': OpenSSL SSL_read: Connection was reset, errno 10054 怀疑是默认开启https访问git config --global http.sslVerify "false" 在git客户端输入即可!

      连接失败处理

    1. Try doing a commit command. git commit -m "first commit" Stage all files or select specific files. git add . Check the status of the files. git status Push the code to your repository. git push origin main (or whatever is the name of your main or master branch).

      在push之前一定要记得 git commit