AI 9块9抢程序员饭碗?我专门研究了三个月,结论可能和你想的不一样
最近圈子炸了两次。第一次是Claude Code源码泄露事件。有人把Anthropic的核心代码扔到了GitHub上,虽然官方火速处理,但技术圈已经炸开了锅——大家突然发现,原来AI编程已经进化到了这个程度。第二次更刺激:豆包9块9。对,你没看错,9块9就能用上一个完整的AI编程工具。这价格,连一杯奶茶都买不到,但它已经开始抢程序员的饭碗了。然后就出现了两种声音:1.AI要把程序员干掉了2.AI最终不靠谱,锅还是要人来背的我专门花了三个月研究这件事,结论是——AI确实在抢饭碗,但抢的是另一群程序员的。什么意思?你会发现,现在最慌的不是那些真正写核心逻辑、做架构设计的人,而是那些每天写CRUD、做简单前端页面的"代码搬运工"。AI工具对这部分工作的替代效率,高得可怕。但反过来,那些真正能解决问题、能设计系统、能理解业务的人,AI不是来抢你饭碗的,它是来给你装上火箭助推器的。说白了,AI淘汰的不是程序员,而是低效的编程方式。你还在一个字母一个字母敲代码,别人已经在用AI生成框架、自动写测试、一键优化性能了。差距不是一点半点。(((顺便吆喝一句,技术大厂,前后端-测试机会,全国一线及双一线城市均有坑位坑位,待遇和稳定性还不错,感兴趣可以试试~所以问题来了:你和AI之间,是竞争关系,还是协作关系?这个问题的答案,决定了你是在2026年被淘汰,还是在2026年起飞。#嘉立创PCB#
Claude Code 初学者必看指南
01|什么是 Claude Code一句话:👉 Claude Code = 终端里的 AI 执行助手你可以用一句人话,让它帮你:写代码改文件整理资料分析数据自动完成任务📌 和普通 AI 最大区别:ChatGPT:告诉你怎么做 Claude Code:直接帮你做02|快速安装核心只有一句话:👉 先装 Node → 再装 Claude Code安装 Node.jsnodejs.org选择 LTS 版本验证:node -v npm -v安装 Claude Code
npm install -g [@anthropic](https://x.com/@anthropic) -ai/claude-code
验证
claude --version
不废话,技术大厂捞人:前端/后端/测试,全国多地可选,待遇和稳定度都在线。→试试试试不吃亏 03|连接国内大模型一、编辑或新增 settings.json 文件MacOS 在.claude文件夹下创建settings.json, ~/.claude/settings.jsonWindows 为用户目录/.claude/settings.json
#新增或修改里面的env字段# 注意替换里面的 `your_deepseek_key` 为您上一步获取到的 API Key
{
"env": {
"ANTHROPIC_AUTH_TOKEN": "your_deepseek_key",
"ANTHROPIC_BASE_URL":"https://api.deepseek.com/anthropic",
"API_TIMEOUT_MS": "3000000",
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": 1,
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "glm-4.5-air",
"ANTHROPIC_DEFAULT_SONNET_MODEL": "glm-4.7",
"ANTHROPIC_DEFAULT_OPUS_MODEL": "glm-4.7"
}
}
二、再编辑或新增 .claude.json 文件用去跳过MacOS & Linux 为 ~/.claude.jsonWindows 为用户目录/.claude.json
#再编辑或新增`.claude.json`文件# 新增 `hasCompletedOnboarding` 参数
{
"hasCompletedOnboarding": true
}
三、进入ClaudeCode确认首先确认系统默认的模型名称然后输入/status命令查看baseUrl和模型名称,输入命令
/status
——转载自:想不到一个好的ID
Vue3 后台分页写腻了?我用 1 个 Hook 删掉 90% 重复代码(附源码)
还在为每个列表页写重复的分页代码而烦恼吗? 还在复制粘贴 currentPage、pageSize、loading 等状态吗? 一个 Hook 帮你解决所有分页痛点,减少90%重复代码背景与痛点在后台管理系统开发中,分页列表查询非常常见,我们通常需要处理:当前页、页大小、总数等分页状态加载中、错误处理等请求状态搜索、刷新、翻页等分页操作数据缓存和重复请求处理这些重复逻辑分散在各个组件中,维护起来很麻烦。为了解决这个烦恼,我专门封装了分页数据管理 Hook。现在只需要几行代码,就能轻松实现分页查询,省时又高效,减少了大量重复劳动使用前提 - 接口格式约定查询接口返回的数据格式:
{
list: [ // 当前页数据数组
{ id: 1, name: 'user1' },
{ id: 2, name: 'user2' }
],
total: 100 // 数据总条数
}
先看效果:分页查询只需几行代码!
import usePageFetch from '@/hooks/usePageFetch' // 引入分页查询 Hook,封装了分页逻辑和状态管理
import { getUserList } from '@/api/user' // 引入请求用户列表的 API 方法
// 使用 usePageFetch Hook 实现分页数据管理
const {
currentPage, // 当前页码
pageSize, // 每页条数
total, // 数据总数
data, // 当前页数据列表
isFetching, // 加载状态,用于控制 loading 效果
search, // 搜索方法
onSizeChange, // 页大小改变事件处理方法
onCurrentChange // 页码改变事件处理方法
} = usePageFetch(
getUserList, // 查询API
{ initFetch: false } // 是否自动请求一次(组件挂载时自动拉取第一页数据)
)
这样子每次分页查询只需要引入hook,然后传入查询接口就好了,减少了大量重复劳动【顺便提一嘴】技术大厂,前端-后端-测试,全国均有机=会,感兴趣可以试试。待遇和稳定性都还不错~解决方案我设计了两个相互配合的 Hook:useFetch:基础请求封装,处理请求状态和缓存usePageFetch:分页逻辑封装,专门处理分页相关的状态和操作
usePageFetch (分页业务层)
├── 管理 page / pageSize / total 状态
├── 处理搜索、刷新、翻页逻辑
├── 统一错误处理和用户提示
└── 调用 useFetch (请求基础层)
├── 管理 loading / data / error 状态
├── 可选缓存机制(避免重复请求)
└── 成功回调适配不同接口格式
核心实现useFetch - 基础请求封装
// hooks/useFetch.js
import { ref } from 'vue'
const Cache = new Map()
/**
* 基础请求 Hook
* @param {Function} fn - 请求函数
* @param {Object} options - 配置选项
* @param {*} options.initValue - 初始值
* @param {string|Function} options.cache - 缓存配置
* @param {Function} options.onSuccess - 成功回调
*/
function useFetch(fn, options = {}) {
const isFetching = ref(false)
const data = ref()
const error = ref()
// 设置初始值
if (options.initValue !== undefined) {
data.value = options.initValue
}
function fetch(...args) {
isFetching.value = true
let promise
if (options.cache) {
const cacheKey = typeof options.cache === 'function'
? options.cache(...args)
: options.cache || `${fn.name}_${args.join('_')}`
promise = Cache.get(cacheKey) || fn(...args)
Cache.set(cacheKey, promise)
} else {
promise = fn(...args)
}
// 成功回调处理
if (options.onSuccess) {
promise = promise.then(options.onSuccess)
}
return promise
.then(res => {
data.value = res
isFetching.value = false
error.value = undefined
return res
})
.catch(err => {
isFetching.value = false
error.value = err
return Promise.reject(err)
})
}
return {
fetch,
isFetching,
data,
error
}
}
export default useFetch
usePageFetch - 分页逻辑封装
// hooks/usePageFetch.js
import { ref, onMounted, toRaw, watch } from 'vue'
import useFetch from './useFetch' // 即上面的hook ---> useFetch
import { ElMessage } from 'element-plus'
/**
* 分页数据管理 Hook
* @param {Function} fn - 请求函数
* @param {Object} options - 配置选项
* @param {Object} options.params - 默认参数
* @param {boolean} options.initFetch - 是否自动初始化请求
* @param {Ref} options.formRef - 表单引用
*/
function usePageFetch(fn, options = {}) {
// 分页状态
const page = ref(1)
const pageSize = ref(10)
const total = ref(0)
const data = ref([])
const params = ref()
const pendingCount = ref(0)
// 初始化参数
params.value = options.params
// 使用基础请求 Hook
const { isFetching, fetch: fetchFn, error, data: originalData } = useFetch(fn)
// 核心请求方法
const fetch = async (searchParams, pageNo, size) => {
try {
// 更新分页状态
page.value = pageNo
pageSize.value = size
params.value = searchParams
// 发起请求
await fetchFn({
page: pageNo,
pageSize: size,
// 使用 toRaw 避免响应式对象问题
...(searchParams ? toRaw(searchParams) : {})
})
// 处理响应数据
data.value = originalData.value?.list || []
total.value = originalData.value?.total || 0
pendingCount.value = originalData.value?.pendingCounts || 0
} catch (e) {
console.error('usePageFetch error:', e)
ElMessage.error(e?.msg || e?.message || '请求出错')
// 清空数据,提供更好的用户体验
data.value = []
total.value = 0
}
}
// 搜索 - 重置到第一页
const search = async (searchParams) => {
await fetch(searchParams, 1, pageSize.value)
}
// 刷新当前页
const refresh = async () => {
await fetch(params.value, page.value, pageSize.value)
}
// 改变页大小
const onSizeChange = async (size) => {
await fetch(params.value, 1, size) // 重置到第一页
}
// 切换页码
const onCurrentChange = async (pageNo) => {
await fetch(params.value, pageNo, pageSize.value)
}
// 组件挂载时自动请求
onMounted(() => {
if (options.initFetch !== false) {
search(params.value)
}
})
// 监听表单引用变化(可选功能)
watch(
() => options.formRef,
(formRef) => {
if (formRef) {
console.log('Form ref updated:', formRef)
}
}
)
return {
// 分页状态
currentPage: page,
pageSize,
total,
pendingCount,
// 数据状态
data,
originalData,
isFetching,
error,
// 操作方法
search,
refresh,
onSizeChange,
onCurrentChange
}
}
export default usePageFetch
完整使用示例用element ui举例
高级用法带缓存
const {
data,
isFetching,
search
} = usePageFetch(getUserList, {
cache: (params) => `user-list-${JSON.stringify(params)}` // 自定义缓存 key
})
设计思路解析职责分离:useFetch 专注请求状态管理,usePageFetch 专注分页逻辑统一错误处理:在 usePageFetch 层统一处理错误智能缓存机制:支持多种缓存策略生命周期集成:自动在组件挂载时请求数据总结这套分页管理 Hook 的优势:开发效率高,减少90%的重复代码,新增列表页从 30 分钟缩短到 5 分钟状态管理完善,自动处理加载、错误、数据状态缓存机制,避免重复请求错误处理统一,用户体验一致易于扩展,支持自定义配置和回调——转载自:不一样的少年_
单位:px、em、rem、vw、vh、clamp 怎么选?
单位:px、em、rem、vw、vh、clamp 怎么选?CSS 单位是响应式布局的核心,也是我刚学响应式时踩坑最多的知识点之一——明明写好的尺寸,换个屏幕、调个字体大小就错乱,排查后才发现是单位选得不对。px 是固定值、em/rem 是相对值、vw/vh 跟着视口走、clamp 能做流体排版,掌握它们的区别和用法,响应式开发、页面可访问性都会轻松很多。今天就把我整理的单位干货、实操经验和避坑技巧,分享给大家。一、绝对单位:px 为主,其他慎用(我的日常用法)绝对单位里,我日常开发只用 px(像素),其他单位(mm、cm、in、pt 等)几乎用不到,大多用于打印场景,新手不用花太多精力记忆。
/* px:像素,固定值,不会随任何因素变化 */
font-size: 16px;
width: 200px;
/* 打印场景专用,日常开发基本用不上 */
mm, cm, in, pt
分享我的实操心得:px 最适合用来设置固定不变的尺寸,比如边框宽度、小间距、图标大小、圆角等,这些地方不需要随字体、视口缩放,用 px 最精准,也最不容易出错。我早期曾用 em 设边框,结果字体一调,边框也跟着变粗,踩过一次坑后就再也不这么做了。二、相对单位:em 好用但易踩坑,嵌套需谨慎em 是相对单位,核心是“相对于当前元素的 font-size”——如果当前元素没设置 font-size,就继承父级的 font-size,这也是它容易踩坑的地方。
.parent { font-size: 16px; }
.child {
font-size: 1.5em; /* 相对于父级16px,就是24px */
padding: 1em; /* 重点:相对于自己的font-size(24px),就是24px */
margin: 0.5em; /* 相对于自己的font-size,就是12px */
}
/* 嵌套会累积,这是我踩过的大坑! */
.grandchild { font-size: 1.2em; } /* 相对于子元素24px,就是28.8px,越嵌套越大 */
避坑提醒:em 最大的问题就是“嵌套累积”,嵌套层级越深,计算出来的尺寸越乱,我早期做导航菜单嵌套时,用 em 设字体,结果二级、三级菜单字体越变越大,排查了很久才找到原因。现在我只用 em 做简单的局部适配,嵌套场景坚决不用。三、相对单位:rem 我的首选,全局缩放超省心后来接触到 rem,直接解决了 em 嵌套累积的痛点!rem 也是相对单位,但它只相对于根元素(html)的 font-size,和父级元素没有关系,不会出现嵌套累积的问题,现在是我做全局布局、字体设置的首选单位。
/* 根元素字体大小,默认通常是16px,我会明确设置,避免浏览器差异 */
html { font-size: 16px; }
.box {
font-size: 1rem; /* 相对于html的16px,就是16px */
padding: 1.5rem; /* 16px×1.5=24px,计算直观 */
width: 20rem; /* 16px×20=320px,不用复杂换算 */
}
/* 响应式神器:只需修改根字号,全站元素就会等比缩放 */
@media (max-width: 768px) {
html { font-size: 14px; } /* 小屏缩小根字号,字体、间距、布局同步缩小 */
}
我的实操用法:rem 适合设置字体大小、容器间距、布局宽度等需要全局适配的样式,配合媒体查询修改根元素 font-size,就能轻松实现全站等比缩放,不用逐个修改每个元素的尺寸,效率大幅提升。#嘉立创PCB#[removed]技术大厂,前端-后端-测试,全国均有机=会,感兴趣可以试试。待遇和稳定性都还不错~四、视口单位:vw、vh、vmin、vmax 做全屏/自适应超方便视口单位是相对于“视口(浏览器可见区域)”的尺寸,和元素、根元素都无关,适合做全屏布局、自适应组件,比如首页Banner、全屏弹窗等,是响应式布局的“好帮手”。
/* vw:视口宽度的 1%,视口宽1000px,1vw就是10px */
.full-width { width: 100vw; } /* 占满视口宽度 */
.half-width { width: 50vw; } /* 占视口宽度的一半 */
/* vh:视口高度的 1%,视口高800px,1vh就是8px */
.full-height { height: 100vh; } /* 占满视口高度 */
/* vmin:vw 和 vh 里较小的那个,适合做正方形自适应 */
.square { width: 50vmin; height: 50vmin; } /* 始终是正方形,随视口缩放 */
/* vmax:vw 和 vh 里较大的那个,适合做全屏英雄区 */
.hero { height: 100vmax; }
高频坑点:这几个单位我踩过两个关键的坑,一定要记牢!1. 100vw 会包含浏览器滚动条的宽度,如果页面有纵向滚动条,用 100vw 会导致横向溢出,出现横向滚动条;2. 移动端用 100vh 时,浏览器地址栏显隐会导致高度跳动,体验很差,后来发现用 dvh 就能解决。五、现代视口单位:dvh、svh、lvh 解决移动端高度跳动问题为了解决传统 vh 在移动端的痛点,浏览器新增了 dvh、svh、lvh 三个现代视口单位,我现在做移动端全屏布局,全靠它们,再也不会出现高度跳动的问题。
/* dvh:动态视口高度,地址栏显隐时会自动调整高度,最常用、最推荐 */
.min-height: 100dvh; /* 移动端全屏布局首选,适配所有场景 */
/* svh:小视口高度,仅当浏览器地址栏始终可见时的视口高度 */
.min-height: 100svh;
/* lvh:大视口高度,仅当浏览器地址栏始终隐藏时的视口高度 */
.min-height: 100lvh;
我的实操建议:做移动端全屏页面、吸底组件时,优先用 dvh,它能自动适配地址栏的显隐,保证页面高度始终贴合视口,体验更流畅;svh 和 lvh 只用在特殊场景,日常开发很少用到。六、clamp:流体排版神器,不用媒体查询也能自适应clamp 是我近期最常用的“黑科技”,它能实现“流体排版”,语法很简单:clamp(最小值, 首选值, 最大值),意思是在最小值和最大值之间,根据首选值随视口平滑变化,不用写一堆媒体查询,就能实现自适应,极大减少代码量。
/* 字体:最小1.5rem(24px),最大2.5rem(40px),随视口宽度平滑变化 */
h1 {
font-size: clamp(1.5rem, 4vw + 1rem, 2.5rem);
}
/* 容器:最小320px(小屏不挤),最大1200px(大屏不宽),随视口自适应 */
.container {
width: clamp(320px, 90vw, 1200px);
margin: 0 auto; /* 水平居中,适配所有屏幕 */
}
/* 间距:小屏16px,大屏24px,随视口同步变化,不用单独写媒体查询 */
.section {
padding: clamp(16px, 5vw, 24px);
}
我的实操技巧:首选值常用 vw + rem 或 calc 做线性变化,比如 4vw + 1rem,既能保证小屏有足够的尺寸,又能让大屏尺寸不夸张;clamp 适合设置字体、容器宽度、间距等需要“平滑自适应”的样式,比媒体查询更简洁、更流畅。七、百分比 %:相对父级,布局常用但易踩坑百分比 % 也是相对单位,核心是“相对于父级元素的 content 区域尺寸”,日常布局用得很多,但新手容易踩坑,尤其是高度设置。
.child {
width: 50%; /* 相对于父级content宽度的50%,布局常用 */
height: 100%; /* 相对于父级content高度的100%,容易失效 */
padding: 10%; /* 重点:margin、padding的%,始终相对父级宽度,不是高度! */
}
避坑提醒:这是我早期踩过的高频坑——子元素设置 height: 100% 时,如果父级元素没有明确设置高度(比如父级 height 为 auto),子元素的 100% 就会失效,显示为内容高度。另外,margin 和 padding 的百分比,不管是水平还是垂直方向,都相对于父级的宽度,不是高度,新手很容易记混。八、我的实际用法建议(直接套用,少踩坑)结合我多年的开发经验,整理了一套单位使用规范,新手可以直接套用,不用再纠结怎么选,高效又避坑:
/* 1. 根字号:设置rem基准,避免浏览器差异 */
html { font-size: 16px; }
/* 2. 字体:rem(全局统一)或 clamp(流体自适应) */
body { font-size: 1rem; } /* 全局基础字体 */
h1 { font-size: clamp(1.5rem, 2vw + 1rem, 2.5rem); } /* 标题流体排版 */
/* 3. 间距:rem为主,保证全局一致性,配合clamp做自适应间距 */
.gap { gap: 1rem; }
.padding { padding: 1.5rem; }
.section-padding { padding: clamp(16px, 5vw, 24px); }
/* 4. 布局宽度:%(局部适配)、vw、clamp(全局自适应) */
.container { width: min(90vw, 1200px); } /* 结合min更稳妥 */
.child-box { width: 50%; } /* 父级容器内的局部适配 */
/* 5. 小固定值:px(精准不变) */
border: 1px solid #eee; /* 边框固定 */
border-radius: 4px; /* 圆角固定 */
.icon-size { width: 24px; height: 24px; } /* 图标固定尺寸 */
/* 6. 全屏布局:dvh(移动端)、vh(PC端) */
.full-screen { min-height: 100dvh; } /* 移动端全屏首选 */
九、个人总结:单位选择避坑核心(新手必看)固定尺寸用 px:边框、圆角、小图标、固定间距,精准不混乱;全局适配用 rem:字体、全局间距、布局,配合媒体查询改根字号,全站等比缩放;嵌套场景避 em:em 易累积,只用在简单局部适配,嵌套层级深的场景坚决不用;全屏/视口适配用 vw/vh + dvh:PC端用 vw/vh,移动端全屏用 dvh,避免高度跳动;流体排版用 clamp:字体、容器宽度、自适应间距,不用媒体查询,简洁又流畅;百分比慎用:记住“宽高相对父级、边距相对父级宽度”,父级无明确高度时,height: 100% 会失效。其实单位选择没有绝对的对错,核心是结合场景,保证页面适配流畅、维护便捷。我从一开始分不清各种单位,到现在能熟练搭配使用,核心就是多实操、多踩坑、多总结,新手只要记住上面的规则,就能少走很多弯路。——转载自:VixenAhri
前端开发 AI Agent 智能体,需要掌握哪些知识?
大家好,我是双越。前百度 滴滴 资深前端工程师,慕课网金牌讲师,PMP。开始AI 刚开始出现的时候就是一个 chatbot 聊天对话框,后来逐步增加功能,可以连网、可以配置 tools 和 MCP ,再到 Agent 自定义工作流。有了 Agent 就可以把 AI 应用到各个真实的业务场景中,这是一个逐步进化和落地的过程。例如我们程序员最熟悉的 AI 编程就是一个 AI Agent 很好的落地,就在这 1 年之间已经广泛应用。Dify 和 Coze 等平台可以直接手动定义工作流,配置出一些个性需求的 AI Agent 并发布使用。8 月底国家颁布了“人工智能+”的行动意见,无论是中国还是世界的 AI 应用将会在未来 10 年内持续发展,遍地开花。我个人也会继续在 AI Agent 领域继续深耕,把我擅长的面试、刷题、简历、教程等领域全部 AI 赋能,使用 AI 增加效率,以更快捷的服务于更多用户。本文站在前端开发人员角度,介绍开发 AI Agent 智能体所需要掌握的知识范围,供大家参考。LLMAI 的基础是 LLM 大语言模型,例如现在大家熟知的 ChatGPT Gemini Claude Deepseek Qwen Grok Llama 等。我们常见的使用方式是在线调用它们的 API (可能要付费购买 token),当然也可以本地部署内网使用。LLM 是什么呢?当前所有 LLM 的核心简单理解就是:预测下一个词。LLM 不是“聪明”,也不能理解人话,而是“被喂了整个互联网数据然后疯狂补全”。你设计得越好,它补全得越准。LLM 参数就是“记忆单元”,像人的神经元,参数越多(训练成本大、运行成本大)也就越“聪明”,补全的越准。例如你的输入是“猴子喜欢吃”,LLM 会在自己海量的训练数据中计算,找到一个列表,其中“香蕉”的概率最大,它就返回“香蕉”。包括写诗、写代码、画图,也是根据 prompt 输入来补全内容,只不过不是一个词,而是海量数据训练出来的一个结构化输出。包括 Agent 和 tool 也是一种“补全”,根据 prompt 去猜测使用哪些 tools (每个 tool 都有描述、参数结构)LLM 的两种交互方式:Completion 模式(纯文本补全)👉 GPT-3 —— 现在基本不用了Chat 模式(对话形式,输入消息列表,输出新内容)👉 GPT-3.5/4MoE 混合专家模式,拆分多个子 LLM (总的太大了参数太多了)每次只激活其中几个,这样运行成本低。 模型微调也是调整其中很少一部分参数,改变它的预测取向。Prompt EngineeringAI 的生成内容和质量是严重依赖于 prompt 提示词的,你给出的提示词模糊,它生成的就一定是模糊的答案。例如,我们在使用 Cursor 时一般要写一个 cursor rule 文件,规范代码标准,这就是提示词的一部分。严格来说,Prompt Engineering 提示词工程 并不是什么技能,它就是一些沟通方式,很容易理解我们可以通过提示词来约束用户的提问,如 github copilot 只专注于编程领域,问其他问题它不回答。还可以通过 CoT 思维链模式,引导大模型按照我们的思路去思考。还可以规范 AI 的输出格式,或让 AI 做出一些判断和选择。在实际开发过程中,每次调用 AI 请求我们都会认真思考提示词该如何写,甚至会使用 AI 写提示词,或者在线生成提示词。并不是用户输入什么,就原本的传给 AI 接口,要做很多包装和转述。LangChian.jsLangChain.js 是前端人员使用 Nodejs 开发 AI 应用的首选,它的 LangGraph 可以自定义 Agent 工作流,它的 LangSmith 跟踪和分析 Agent 运作流程。LangChain 是一个非常好的开发生态。RAGRetrieval-Augmented Generation 检索增强生成,这是 AI 搜索资料辅助生成答案的有效方式。它的核心步骤是:1. 把资料拆分为向量格式,存储在向量数据库;2. 用户提问时去向量数据库检索相关答案;3. 把这些相关答案发送给 AI 配合一起生成最终答案。对于前端开发人员,不太好理解的就是 Vector 向量。Vector 向量,就是坐标。生活中常见的有二维、三维坐标,方便计算距离。而我们可以把一段文本、图片等,转换为多维(几百维度)坐标(float 数组),两个坐标的距离(如欧氏距离、余弦相似度),就是两段文本(或图片)的相似度。Elastic Search 可实现搜索引擎,但它只是关键词匹配,例如“教程”关键词匹配不到“课程”,它是严格的文字匹配。而向量就能匹配到,它是相似度匹配,语义搜索。PS.现在 elastic cloud 也有向量存储。Vector store 向量存储技术选型:开发阶段用 Chroma,部署后切换为 Pinecone 或 Supabase 都有免费试用额度。技术大厂,前端-后端-测试,全国均有机=会,感兴趣可以试试。待遇和稳定性都还不错~AgentAgent 是一个综合体,它主要包含LLM 大模型,负责思考和生成内容,一个 Agent 可以有多个 LLM ,不同节点配置不同的 LLMworkflow 工作流:定义节点、方向、判断,以实现 Re-Act ,让 AI Agent 自行判断逻辑tools 工具:调用外部的服务,例如搜索、查询数据库等memory 记忆和存储:记录当前对话和用户的关键信息下图是 Flowise (类似于 Dify 和 Coze)给出的一个 RAG Agent 工作流配置的示例。MCPModel Context Protocol 模型上下文协议,是规定大模型参数和调用的一种协议,让 AI 可统一调用第三方的服务。当前我们谈 AI MCP 主要是说各个 MCP server 能够提供的能力。还有,我们也要能自己开发 MCP server 以及开发 client 去调用 server ,要有这方面的能力。多模态现在的 AI 应用不仅仅是文字聊天,你可以可以上传图片、PDF、word、甚至音频和视频,都可以传给 AI 大模型进行处理。同时,AI 大模型也可以生成图片、PDF、音频和视频。即,现在的 AI 应用要支持多模态。AI 生成的非文字内容,往往通过 Artifact 形式展示。例如使用 Claude 生成一个 HTML 网页,它在右侧直接展示了网页渲染效果,并且还支持发布上线。不同的 AI 大模型擅长不同的模态形式,也有不同的 API 调用方式和参数的写法。其他AI Agent 还在发展之中,还有更多的技术需要学习和实践,后面我会逐步分享Multi-agent 多智能体架构A2A 协议,Agent 和 Agent 之间的通讯协议Context Engineering 上下文工程AG-UI 协议,Agent 和 UI 的通讯协议最后我个人也会继续在 AI Agent 领域继续深耕,把我擅长的面试、刷题、简历、教程等领域全部 AI 赋能,使用 AI 增加效率,以更快捷的服务于更多用户。关注我,我会继续分享更多 AI Agent 相关内容。——转载自:双越AI_club
从爆红到被嫌弃,MCP 为什么开始失宠了
MCP 出生时,被捧得很高。2024 年 11 月,Anthropic 发布"模型上下文协议",几乎所有 AI 开发者社区都在讨论这件事。它的定位很诱人,要成为大模型和外部工具之间通信的"通用标准",有点像当年 HTTP 对 Web 的意义。一时间,MCP server 满天飞,各种集成教程、开源实现层出不穷。但时间只过了一年多。上周,Perplexity 的联合创始人兼 CTO Denis Yarats 在内部表示,他们正在放弃 MCP,转而改用 API 和 CLI。这个消息扩散出来后,引发了一波讨论,但讨论的内容不是"为什么",而是"早该如此"。Y Combinator 的总裁兼 CEO Garry Tan 甚至直接说了一句话:"MCP sucks。"MCP 的问题从来都不是技术实现不够好很多人对 MCP 的质疑,停留在"不稳定"、"认证烦"这些体感上的抱怨。这些问题确实存在,但它们只是表象。MCP 真正的困境,是一个结构性问题。MCP 的工作方式是,把工具的名称、描述、参数结构(Schema)以及使用示例,全部注入到 Agent 的上下文窗口里。Agent 读完这些信息,再决定要调用哪个工具。这个设计在工具数量少时还可以接受。但你一旦接入 10 个服务,每个服务有 5 个工具,光是工具定义本身就已经烧掉了几千个 token。Agent 还没开始干活,上下文就已经塞满了一半。上下文窗口是 Agent 最宝贵的资源,它决定了 Agent 能看见多少对话历史,能保留多少工作记忆,能有多大的推理空间。MCP 的代价,是把这个资源拿来"列菜单"。面对这个问题,现有的出路只有三条:一次性加载所有工具,接受推理性能下降限制接入工具数量,接受 Agent 能力边界收窄构建动态工具加载机制,接受额外的延迟和复杂度三条路都不好走。这不是"实现质量"的问题,而是协议设计本身的代价。除此之外,日常使用中的痛点也不少。MCP server 启动失败是家常便饭,有时重试能解决,有时必须推倒重来。接入多个服务就要在每个服务上重新认证一遍。权限管理也只有"允许"和"不允许"两档,没有办法把某个工具限制为只读,也没有办法约束它可以传什么参数。技术大厂,前端-后端-测试,全国均有机=会,感兴趣可以试试。待遇和稳定性都还不错~CLI 是更好的答案,不是因为它新,而是因为它够旧工程师 Eric Holmes 写过一篇文章,观点直接:MCP 没有带来任何实际价值,LLM 完全可以自己搞懂怎么用 CLI。这话有点刺,但它说的是实情。大模型在训练时看过海量的 man 手册、Stack Overflow 回答和 GitHub 上的 Shell 脚本。它们对 CLI 的理解,远比对某个 MCP server 的理解深得多。给它一个命令行工具和一份文档,它就能上手,不需要特殊适配。CLI 在几个关键点上,比 MCP 天然占优。第一是可调试性。当 Claude 对 Jira 执行了一个出乎意料的操作,你可以直接跑同一条 jira issue view 命令,看看它看到了什么。输入一致,输出一致,没有谜团。但 MCP 的调用只发生在 LLM 的对话内部,出问题了只能去翻复杂的 JSON 传输日志。第二是可组合性。这是 CLI 的核心竞争力。你可以用 jq 过滤数据,用 grep 串联逻辑,把输出重定向到文件。这不只是方便,很多时候这是唯一可行的路。MCP 没有这个能力,你要么把完整数据塞进上下文,要么在 server 端自己写过滤逻辑,两种方式都在用更多的精力换取更差的结果。第三是认证。CLI 复用的是系统级别的认证体系,这套东西已经经过几十年的打磨。MCP 需要你重新为每个工具搭一遍认证流程。这件事说明了什么Perplexity 放弃 MCP,以及其他工具陆续移除 MCP 支持,这件事背后有一个更值得思考的信号。给 AI 构建工具链,不需要发明一套新的协议。AI 需要的工具,和人类需要的工具,在很多时候是同一套。最好的工具是对人类和机器都好用的工具。CLI 存在了几十年,设计上一直遵循一个哲学,每个工具做好一件事,然后把工具组合起来解决复杂问题。这套哲学放到 Agent 身上,依然成立。MCP 想构建一个更"现代"的抽象层,但它解决的问题,现有工具已经解决得够好了。在不需要额外抽象的地方强行加一层,带来的只有额外的成本和复杂度。当然,MCP 不会完全消失。在某些特定场景,比如需要强类型 Schema、有严格访问控制要求的企业内部系统,它依然有它的位置。但作为"AI 工具集成的通用标准",这个定位恐怕很难站稳了。参考:MCP is Dead, Long Live the CLIDenis Yarats on X——转载自:Moment
JetBrains又一知名软件宣布倒下,五味杂陈
提到 JetBrains,相信程序员和开发者们都再熟悉不过了。作为一家全球领先的软件开发工具提供商,他们打造的多款编程软件曾经都深受全球万千开发者喜爱。但是,伴随 AI Coding 的加速冲击,这家公司的很多产品策略,如今也在被迫作出各种调整。这不就在前几天,JetBrains 在其官博里又官宣了一项重大的软件服务调整,那就是:将正式停服 Code With Me 软件服务。提到 Code With Me 这项服务,不知道有没有同学用过。还记得 JetBrains 刚推出那会,还是他们新版的引以为傲的一个功能服务。Code With Me 是一个实时协同编程工具,主要用于协作开发与结对编程,旨在让开发工作突破空间限制,实现多人高效协同。它能够使身处不同地点的开发人员,同时对同一个项目代码进行编辑和操作,以实现 Host-Guest 模式的手把手结对编程和群体编程。聊到 Code With Me 服务的诞生,其实还得追溯到前几年的口罩时期。彼时,内置配对编程和实时协作工具的需求达到高峰,这也是当时 JetBrains 推出 Code With Me 这项服务的初衷。我记得当时 IntelliJ IDEA 2021 年的首个大版本,就开箱即用地支持了 Code With Me 功能,同时它还具有音频通话和视频通话功能,可以满足随时随地的沟通需求,这操作在当时非常酷炫,也解决了很大的痛点。【顺便提一嘴】技术大厂,前端-后端-测试,全国均有机=会,感兴趣可以试试。待遇和稳定性都还不错~但是随着时间的推移,很多事情发生了变化。这次 JetBrains 决定停止对 Code With Me 的开发,主要基于以下两点原因:**第一点是需求变化。**因为该工具在口罩期间需求达到顶峰,但随着团队协作工作流的改变,如今这方面的需求已逐渐下降。**第二点则是资源配置优化。**为了将工程资源集中在能为开发者带来最大价值的领域,公司决定不再投入资源维护该插件。那针对这次的 Code With Me 服务停服,JetBrains 官方制定了明确的停运时间表,以确保用户有足够的时间过渡。JetBrains 官博表示:自 2026.1 版本发布后,Code With Me 将从所有现有 JetBrains IDE 中分离出来,并通过 JetBrains Marketplace 作为独立插件提供。同时 2026.1 也将会是官方支持 Code With Me 的最后一个 IDE 版本,后续将不再开发任何新功能。过渡期自 2026.1 至 2027 Q1,Code With Me 插件将继续在受支持的 IDE 版本上运行,并由 JetBrains 官方提供安全更新,同时对应配套的公共中继基础设施也会保持运行。最终停用会定在 2027 年第一季度,届时公共中继基础设施将被关闭,Code With Me 服务将完全停止运行。话说最近这一年,JetBrains 可谓是动作频频,在产品策略方面,该放弃的放弃,该调整的调整,目前正在大刀阔斧地对其现有软件和服务进行改造。大家还记得吗,三个月前的时候,JetBrains 才刚刚官宣其下一代 IDE Fleet 的终止开发与更新。想当初 Fleet 刚出来那会,也承载了当年 JetBrains 的厚望,号称要对标 VSC,但是最终的结局大家也看到了,还是难逃停更的结果。究其原因,还是因为如今的开发工具领域正经历根本性变化,AI 大模型等技术的突破彻底改变了 Coding 的方式。去年底 Fleet 官宣落幕的时候,官方曾明确表态,Fleet 虽然倒下,但是其背后的技术和团队并不会消失,JetBrains 将基于该基础,转而押注 Agentic Development 赛道,去开发一个新的产品。这不,也就在前些天,Jetbrains 也在其官博里正式放风了一个新产品,即:发布全新的 AI Agent IDE,名字叫做:Jetbrains Air。JetBrains Air 定位为一款桌面级应用,官方是叫它:Agentic Development Environment,专为 AI Agent 工作流而设计,并将其描述为全新一代的开发工具。Jetbrains Air 支持多智能体并发,提供隔离环境。借助 Air,用户可以将编码任务委派给多个 AI Agent,并同时运行。说白了,这玩意有点类似于之前 OpenAI 发的那个 Codex App,不过不同于 Codex App 的是,这次的 JetBrains Air 走的是开放路线,生态开放,不搞 AI 供应商绑定。Air 支持将市面上多种常见的 Code Agent,比如 OpenAI 的 Codex、Anthropic 的 Claude、Google 的 Gemini,包括 JetBrains 自己的 Junie 等,支持整合到一个连贯的工作流中,以实现精准自定义任务、独立运行任务,并借助全面的代码智能功能来查看结果。当然,Air 目前还仍处于 Public Preview 阶段,下载页面目前也仅提供 macOS 版本安装包,Windows 和 Linux 版本在后续会跟进。写到这里其实挺感慨。JetBrains 作为曾经的开发软件领域的扛把子,如今随着 AI Coding 的加速进化与冲击,这两年遇到了不小的瓶颈。包括这一次 JetBrains Air 的亮相,被不少网友讨论说是 Jetbrains 在 all in AI 路上的最后一搏,显然 JetBrains 也不想在 AI 这个赛道上掉队。但不知道为啥,越是这样,现在越给人的感觉就是,产品策略愈渐被动,产品决策也有点仓促和迷茫,说实话,这可不是什么好事啊。而且该说不说,我觉得这次 JetBrains Air 又有点发晚了,毕竟别家像 Anthropic 或者 OpenAI 这些,人家早就已经推出类似产品来占领市场了。JetBrains 这才刚刚跟进不说,搞个 App 结果目前还只发了一个 macOS 版,Windows 和 Linux 版都还没推出来……说实话,这反应速度太慢了。面对各种先进 AI 工具带来的降维打击,以及开发者编码角色和习惯的逐渐转变,JetBrains 在 AI 时代正面临着前所未有的挑战。但是没办法,在这个技术日新月异的时代,唯一不变的就是变化。而作为开发者,我们唯一能做的,就是保持开放的心态,迎接 AI 时代的新浪潮、新范式、新工具。——转载自:CodeSheep
Tailwind 到底是设计师喜欢,还是开发者在硬撑?
我们最近刚把一个后台系统从 element-plus 切成了完全自研组件,CSS 层统一用 Tailwind。全员同意设计稿一致性提升了,但代码里怨言开始冒出来。这篇文章不讲原理,直接上代码对比和团队真实使用反馈,看看是谁在享受,谁在撑着。1.组件内样式迁移原先写法(BEM + scoped):
Tailwind 重写:
优点:组件直接可读,不依赖 class 定义样式即结构,调样式时不用来回翻缺点:设计稿变了?全组件搜索 text-sm 改成 text-base?无法抽象:多个地方复用 .text-label 变成复制粘贴【顺便提一嘴】技术大厂,前端-后端-测试,全国均有机=会,感兴趣可以试试。待遇和稳定性都还不错~2.复杂交互样式纯 CSS(原写法)
Tailwind 写法
问题来了:✅ 简单 hover/active 很方便❌ 多态样式(如 disabled + dark mode + hover 同时组合)就很难读:
调试时需要反复阅读 class 字符串,不能直接 Cmd+Click 查看样式来源。3.统一样式封装,复用方案混乱原写法:统一样式变量 + class
$border-color: #eee;
.panel {
border: 1px solid $border-color;
border-radius: 8px;
}
Tailwind 使用中经常出现的写法:
问题来了:设计稿调整了主色调或边框粗细,如何批量更新?BEM 模式下你只需要改一个变量,Tailwind 下必须靠 @apply 或者手动替换所有 .border-gray-200。于是我们项目里又写了一堆“语义类”去封装 Tailwind:
/* 自定义 utilities */
@layer components {
.app-border {
@apply border border-gray-200;
}
.app-card {
@apply p-4 rounded-lg shadow-sm bg-white;
}
}
最后导致的问题是:我们重新“造了个 BEM”,只不过这次是基于 Tailwind 的 apply 写法。🧪 实测维护成本:100+组件、多人协作时的问题我们项目有 110 个组件,4 人开发,统一用 Tailwind,协作两个月后出现了这些反馈:👨💻 A 开发:写得很快,能复制设计稿的 class 直接粘贴🧠 B 维护:改样式全靠人肉找 .text-sm、.p-4,没有结构命名层🤯 C 重构:统一调整圆角半径?所有 .rounded-md 都要搜出来替换所以我们内部的结论是:Tailwind 写得爽,维护靠人背。它适合“一次性强视觉还原”,不适合“结构长期型组件库”。🔧 我们后来的解决方案:Tailwind + token 化抽象我们仍然使用 Tailwind 作为底层 utilities,但同时强制使用语义类抽象,例如:
@layer components {
.text-label {
@apply text-sm text-gray-500;
}
.btn-primary {
@apply bg-blue-500 hover:bg-blue-600 text-white py-2 px-4 rounded;
}
.card-container {
@apply p-4 bg-white rounded-lg shadow;
}
}
模板中统一使用:
标题
内容
这种方式保留了 Tailwind 的构建优势(无 tree-shaking 问题),但代码结构有命名可依,后期批量维护不再靠搜索。📌 最终思考Tailwind 是给设计还原速度而生的,不是给可维护性设计的。 设计师爱是因为它像原子操作; 开发者撑是因为它把样式从结构抽象变成了“字串组合游戏”。如果你的团队更在意开发效率,样式一次性使用,那 Tailwind 非常合适。 如果你的组件系统是要长寿、要维护、要被多人重构的——你最好在 Tailwind 之上再造一层自己的语义层,或者别用。分享完毕,谢谢大家🙂——转载自:ErpanOmer
大家天天说的'银弹'到底是个啥?
"微服务是银弹!" "Docker不是银弹!" "React才是真正的银弹!" 天天在文章和评论区看到这个词语,他到底是个啥,又是从哪里开始的。今天不讲技术,聊一下啥是银弹。概念来源"银弹"(Silver Bullet)这个概念来自于弗雷德里克·布鲁克斯(Fred Brooks) 的经典著作《人月神话》(The Mythical Man-Month)。1986年,布鲁克斯在著名论文《没有银弹:软件工程的本质与偶然性》中提出了这个影响深远的论断:在软件工程领域,没有任何技术或方法能够在十年内使生产率、可靠性和简洁性有数量级的提升。狼人传说的隐喻为什么要用"银弹"这个词?这个比喻来自狼人传说:狼人:被认为是不死的、无坚不摧的怪物银弹:传说中唯一能够杀死狼人的武器布鲁克斯用这个比喻来说明:软件开发的本质复杂性 = 狼人(看似不可战胜的难题)神奇的技术解决方案 = 银弹(人们期望的一招制敌的办法)残酷的现实 = 银弹根本不存在软件困难的分类布鲁克斯将软件开发的困难分为两类:本质性困难(Essence)这些是软件开发固有的、无法避免的困难:概念结构的复杂性:软件本身的概念结构不可见性:软件没有物理实体,无法直观把握一致性和可变性:必须适应不断变化的需求离散性:软件系统的状态组合极其庞大偶然性困难(Accident)这些是由于当前工具和技术限制造成的困难:开发工具的限制编程语言的复杂性硬件性能约束团队协作问题布鲁克斯的核心观点是:银弹最多只能解决偶然性困难,但无法触及本质性困难。 ,虽然我没看完,但总结下来应该就是这个意思😂需要一个跳板工作的,想一手抓主业,一手抓副业的,不妨看看这个机会~:技术大厂,前端-后端-测试,全国均有机会,感兴趣可以试一试;待遇和稳定性都还可以~ 且不需要你耗费很多“人际关系的心力”在上面。2. 现在"银弹"的实际含义银弹 = 能够解决所有问题的技术、方法或工具技术圈中的典型使用当有人说"xxx是银弹"时,通常意味着:这东西太厉害了,能解决我们所有的烦恼有了它,之前的所有问题都不是问题了别的技术都可以不用考虑了典型例子:
// React派
const ReactIsSilverBullet = () => {
return "React才是真正的银弹,解决所有前端问题!";
};
// Vue派
const VueIsSilverBullet = () => {
return "Vue才是银弹,简单易学,性能无敌!";
};
// 微服务派
const MicroservicesAreSilverBullet = () => {
return "微服务就是银弹,拆分后所有扩展问题都解决了!";
};
// Docker派
const DockerIsSilverBullet = () => {
return "Docker就是银弹,一次构建到处运行!";
};
当有人说"xxx不是银弹"时,意思是:这个技术有局限性,不能解决所有问题不要指望它能一劳永逸还是需要配合其他方案典型例子:
-- MySQL不是银弹
-- "MySQL虽然不错,但面对大数据量时还是有限制,不是银弹!"
-- K8s不是银弹
-- "K8s功能强大,但运维复杂度很高,不是银弹!"
-- 敏捷开发不是银弹
-- "敏捷不是银弹,还需要结合其他工程实践!"
3. 银弹概念的传播路径3. 银弹含义的演变过程原始含义(1986年)核心概念: 能够数量级(10倍)提升软件开发生产率、可靠性和简洁性的技术或方法软件开发的本质复杂性是否存在革命性的突破技术软件工程的基本限制现在的技术圈上普遍的含义能够神奇完全解决具体场景的一系列问题的技术、工具或方法React vs Vue 哪个更好微服务 vs 单体如何选择MySQL vs MongoDB 哪个更适合Docker vs K8s 如何选择总结能很清晰的看到银弹这个词从抽象的架构设计理念,演变成了具体的对某件技术、框架、工具、方法的评价。原本:指软件工程中的架构设计和整体方法论现在:变成了对具体某个技术方案、技术框架的评价工具具体含义变化原始含义:布鲁克斯指的是能否根本上解决软件复杂性的架构方法现在含义:用来评价某个具体技术、框架、工具是否万能也反映了大家讨论技术的时候,越来越具体,越来越落地。 普通人也很难接触到什么太抽象,太宏大的架构设计,我们也更应该关心更具体的方案,来更好的解决问题。——转载自:9号达人#嘉立创PCB#
2026 年前端必须掌握的 4 个 CSS 新特性!
1. 前言2026 年已经到来,前端技术又迎来了新一轮的革新。今天,我为你精选了 4 个在 2025 年正式发布、2026 年必须掌握的 CSS 新特性,让你的技术更上一层楼!2. 兄弟元素定位:sibling-index() 与 sibling-count()早些时候这些还只是实验性质的功能,现在它们已经在稳定的 Chrome 和 Safari 浏览器中可用了!记得以前实现列表项交错动画时,要手动给每个元素设置不同的延迟吗?现在,用 sibling-index() 一行代码就能搞定!
li {
transition: opacity 0.3s ease;
transition-delay: calc((sibling-index() - 1) * 100ms);
}
这个函数会自动获取元素在兄弟节点中的位置(从 1 开始计数),通过简单的计算就能实现流畅的交错动画效果。如果再搭配 @starting-style,连入场动画都能轻松搞定:
li {
transition: opacity 0.3s ease;
transition-delay: calc((sibling-index() - 1) * 100ms);
@starting-style {
opacity: 0;
}
}
实现效果如下:3. 滚动状态查询:@container scroll-state()现在你可以精确地知道用户正在如何滚动页面。不仅如此,你还可以查询滚动条的三种状态:粘附、贴靠、可滚动。首先,给需要监测的容器加上 container-type: scroll-state,然后就可以用 @container scroll-state() 来查询它的状态了。3.1. 粘附状态:stuck
/* 当导航栏被“粘住”时 */
@container scroll-state(stuck) {
.inner-navbar {
box-shadow: var(--shadow-3);
}
}
使用效果如下:3.2. 贴靠状态:snapped
section {
overflow: auto hidden;
scroll-snap-type: x mandatory;
> article {
container-type: scroll-state;
scroll-snap-align: center;
@supports (container-type: scroll-state) {
> * {
transition: opacity 0.5s ease;
@container not scroll-state(snapped: x) {
opacity: 0.25;
}
}
}
}
}
使用效果如下:3.3. 可滚动状态:scrollable而且你可以查询滚动方向:
@container scroll-state(scrollable: top) {
}
@container scroll-state(scrollable: right) {
}
@container scroll-state(scrollable: bottom) {
}
@container scroll-state(scrollable: left) {
}
我们来举一个例子:
.scroll-container {
container-type: scroll-state size;
overflow: auto;
&::after {
content: " ";
background: var(--_shadow-top), var(--_shadow-bottom);
transition: --_scroll-shadow-color-1-opacity 0.5s ease, --_scroll-shadow-color-2-opacity 0.5s ease;
@container scroll-state(scrollable: top) {
--_scroll-shadow-color-1-opacity: var(--_shadow-color-opacity, 25%);
}
@container scroll-state(scrollable: bottom) {
--_scroll-shadow-color-2-opacity: var(--_shadow-color-opacity, 25%);
}
}
}
使用效果如下:你可以发现,在滚动的时候,容器顶部和底部有一层阴影。4. 文字精准对齐:text-boxtext-box 可以精确控制文字的边界框,实现像素级的对齐效果。Web 字体渲染时会在字形上下方预留“安全间距”:但有时我们需要进行像素级精确对齐,此时就需要使用 text-box:
h1 {
text-box: trim-both cap alphabetic;
}
这一行代码就能:trim-both:同时修剪上下方的空白cap:修剪到大写字母高度线以上alphabetic:修剪到字母基线以下使用效果如下:机-会技术大厂,前端-后端-测试,全国均有机-会,感兴趣可以试试。待遇和稳定性都还不错~5. 类型安全:typed attr()这是 attr() 函数的升级版,支持类型检查和回退值,在 HTML 和 CSS 之间搭建了强大的桥梁。5.1. 传递颜色
css
.theme {
background: attr(data-bg color, black); /* 类型:颜色,默认:黑色 */
color: attr(data-fg color, white);
}
5.2. 传递数字
…
css
.grid {
--_columns: attr(data-columns number, 3);
grid-template-columns: repeat(var(--_columns), 1fr);
}
5.3. 类型验证(枚举值)
css
[scroll-snap] {
scroll-snap-align: attr(scroll-snap type(start | center | end));
}
type() 函数会验证属性值是否在允许的关键字列表中,无效值会被优雅地回退。6. 浏览器支持现状你可能会说:“这些功能就像那些时髦衣服……等我们能用了,它们可能已经过时了 😂”确实,浏览器兼容性是每一位前端开发者需要关注的问题。但这些功能其实大多属于渐进增强,我们可以先在支持的浏览器中提供更好的体验,不支持的浏览器回退。7. 最后这 4 个 CSS 新特性其实也代表了 CSS 的未来方向:更智能的布局控制(sibling-index)更精细的交互感知(scroll-state)更精准的视觉设计(text-box)更强大的 HTML-CSS 桥梁(typed attr)2026 年,前端开发不再只是“让页面显示出来”,而是“让体验完美起来”。这些工具让我们能够创造出更精致、更智能、更用户友好的网页体验。——转载自:冴
UI小姐姐要求有“Duang~Duang”的效果怎么办?
设计小姐姐: “搞一下这样的回弹效果,你行不行?” 我:“行!直接 50 行 keyframes + transform + 各种百分比,搞定 ” 设计小姐姐:“太硬(撇嘴),不够 Q 弹(鄙视)” 我:(裂开) 隔壁老王:这么简单你都不行,我来一行贝塞尔 cubic-bezier(0.3, 1.15, 0.33, 1.57) 秒了😎 设计小姐姐:哇哦!(兴奋)好帅!(星星眼🌟)好Q弹!(一脸崇拜😍) 我:“???”🧠 一、为什么一行贝塞尔就能“Duang”起来?1️⃣ cubic-bezier 是什么?在 CSS 动画里,我们经常写:
transition: all 0.5s ease;
但其实 ease、linear、ease-in-out 这些都只是封装好的贝塞尔曲线。 底层原理是:
cubic-bezier(x1, y1, x2, y2)
这四个参数定义了时间函数曲线,控制动画速度的变化。x:时间轴(必须在 0~1 之间)y:数值轴(可以超出 0~1!)👉 当 y 超过 1 或小于 0 时,动画值就会冲过终点再回弹, 这就是“回弹感”的核心。2️⃣ 回弹的本质:过冲 + 衰减想象一个球掉下来:过冲:球落地时会压扁(超出终点)回弹:然后反弹回来,再逐渐稳定在动画中,这个“过冲”就是 y>1 的部分, 而“回弹”就是曲线回到 y=1 的过程。🧪 二、一行贝塞尔的魔法✅ 火箭发射
🚀发射!
💡 参数解析:y1 = -0.55 → 先轻微反向缩小y2 = 1.55 → 再冲过头 55%,最后回弹到原位🧩 四、常用贝塞尔参数效果描述贝塞尔参数备注微回弹(按钮)cubic-bezier(0.34, 1.31, 0.7, 1)轻柔弹性强回弹(卡片)cubic-bezier(0.68, -0.55, 0.27, 1.55)爆发力强柔和出入cubic-bezier(0.4, 0, 0.2, 1.4)iOS 风弹性放大cubic-bezier(0.175, 0.885, 0.32, 1.275)弹簧感火箭猛冲cubic-bezier(0.68, -0.55, 0.27, 1.55)推背感 ——转载自:前端九哥
让网页在 PC 缩放时“纹丝不动”的 4 个技巧
记录一次把「标题、描述、背景图」全部做成“流体响应式”的踩坑与经验背景最近给 LUCI OS 官网做首屏改版,需求只有一句话:“PC 端浏览器随意缩放,首屏内容要像海报一样,几乎看不出形变。”听起来简单,但「缩放不变形」+「多端自适应」本质上是矛盾的。 经过 3 轮迭代,我们把问题拆成了 4 个小目标,并给出了最简洁的解法。1. 文本:用 clamp() 一把梭传统写法给 3~4 个断点写死字号,窗口稍微拉一下就会跳变。 CSS 4 级函数 clamp(MIN, VAL, MAX) 天生就是解决“跳变”的:标题:text-[clamp(28px,6vw,48px)]描述:text-[clamp(14px,1.2vw,18px)]一行代码实现「最小值保底、最大值封顶、中间平滑变化」。 浏览器缩放时,字号随 vw 线性变化,肉眼几乎察觉不到阶梯感。2. 容器:限宽 + 居中 = “锁死”水平形变再漂亮的字号,如果容器宽度跟着窗口无限拉伸,一样会崩。 做法简单粗暴:
max-w-6xl mx-auto
max-w-6xl 把最大内容宽度锁死在 1152px;mx-auto 保证左右留白始终对称。窗口继续拉大,两侧只是等比留空,内容区不再变形。3. 图片(或背景):固定尺寸 + 背景定位背景图不能跟着 100% 拉伸,否则人物/产品会被拉长。 我们把背景拆成两层:外层:全屏 div,只做黑色渐变遮罩;内层:真正的背景图用css复制
background: url(...) 50% / cover no-repeat;
max-width: 1280px;
max-height: 800px;
只要窗口没超过 1280×800,背景图始终保持原始比例,居中裁剪。机-会技术大厂,前端-后端-测试,全国均有机-会,感兴趣可以试试。待遇和稳定性都还不错~4. 布局:断点内“锁死”,断点外才变化Tailwind 的 md:flex-row 之类前缀只在跨断点时生效。 在 同一断点内 我们故意:用固定 gap-32px 而非百分比;用固定图片宽 md:w-75 高 md:h-47;用 items-center 保证垂直居中。=> 浏览器宽一点点、窄一点点,所有尺寸都不变,自然看不出变化。 直到窗口拉到下一个断点阈值,布局一次切换,干净利落。最终代码(最简可读版)
#嘉立创PCB#
{/* 1. 背景层:固定尺寸 + 居中 */}
{/* 2. 内容层:限宽 + 居中 + clamp */}
效果1440px 与 1920px 两档分辨率下,标题、描述、背景图的视觉差异 [removed]
Unlocking Vast Data Potential
LUCI OS is powered by Mavi's video understanding engine …
我删光了项目里的 try-catch,老板:6
相信我们经常这样写bug(不是 👇:
try {
const res = await api.getUser()
console.log('✅ 用户信息', res)
} catch (err) {
console.error('❌ 请求失败', err)
}
看似没问题每个接口都要 try-catch,太啰嗦了!错误处理逻辑分散,不可控!代码又臭又长💨!💡 目标:不抛异常的安全请求封装我们希望实现这样的调用👇:
const [err, data] = await safeRequest(api.getUser(1))
if (err) return showError(err)
console.log('✅ 用户信息:', data)
是不是清爽多了?✨ 没有 try-catch,却能同时拿到错误和数据。🧩 实现步骤1️⃣ 先封装 Axios 实例
// src/utils/request.js
import axios from 'axios'
import { ElMessage } from 'element-plus'
const service = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 10000,
})
// 🧱 请求拦截器
service.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token')
if (token) config.headers.Authorization = `Bearer ${token}`
return config
},
(error) => Promise.reject(error)
)
// 🧱 响应拦截器
service.interceptors.response.use(
(response) => {
const res = response.data
if (res.code !== 0) {
ElMessage.error(res.message || '请求失败')
return Promise.reject(new Error(res.message || '请求失败'))
}
return res.data
},
(error) => {
ElMessage.error(error.message || '网络错误')
return Promise.reject(error)
}
)
export default service
拦截器的作用:✅ 统一处理 token;✅ 统一处理错误提示;✅ 保证业务层拿到的永远是“干净的数据”。2️⃣ 封装一个「安全请求函数」
// src/utils/safeRequest.js
export async function safeRequest(promise) {
try {
const data = await promise
return [null, data] // ✅ 成功时返回 [null, data]
} catch (err) {
return [err, null] // ❌ 失败时返回 [err, null]
}
}
这就是关键! 它让所有 Promise 都变得「温柔」——不再抛出异常,而是返回结构化结果。3️⃣ 封装 API 模块
// src/api/user.js
import request from '@/utils/request'
export const userApi = {
getUser(id) {
return request.get(`/user/${id}`)
},
updateUser(data) {
return request.put('/user', data)
},
}
4️⃣ 在业务层优雅调用
是不是很优雅、数据逻辑清晰、不需要 try-catch、 错误不崩溃。老板说:牛🍺,你小子有点东西 插播一则机-会技术大厂,前端-后端-测试,全国均有机-会,感兴趣可以试试。待遇和稳定性都还不错~🧱 我们还可以进一步优化:实现自动错误提示我们可以给 safeRequest 增加一个选项,让错误自动提示:
// src/utils/safeRequest.js
import { ElMessage } from 'element-plus'
export async function safeRequest(promise, { showError = true } = {}) {
try {
const data = await promise
return [null, data]
} catch (err) {
if (showError) {
ElMessage.error(err.message || '请求失败')
}
return [err, null]
}
}
使用时👇:
const [err, data] = await safeRequest(userApi.getUser(1), { showError: false })
这样你可以灵活控制是否弹出错误提示, 比如某些静默请求就可以关闭提示。🧠 进阶:TypeScript 支持(超丝滑)如果你用的是 TypeScript,可以让返回类型更智能👇:
export async function safeRequest[removed](
promise: Promise[removed]
): Promise[removed] {
try {
const data = await promise
return [null, data]
} catch (err) {
return [err as Error, null]
}
}
调用时:
const [err, user] = await safeRequest[removed](userApi.getUser(1))
if (user) console.log(user.name) // ✅ 自动提示类型
老板:写得很好,下次多写点,明天你来当老板——转载自:前端九哥#嘉立创PCB#
有了免费的Kiro,这次真的可以把Cursor扔了!
家好,我是子昕,一个干了10年的后端开发,现在在AI编程这条路上边冲边摸索,每天都被新技术追着跑。Claude的金主爸爸亚马逊(AWS)偷偷发布了一款AI编程工具,Kiro。我用它做了三个公司的生产级项目需求,深度体验3天后发现:Kiro现在完全免费,可以免费使用Claude-Sonnet-4和Claude-3.7模型规范驱动开发模式,代码质量和工程化程度碾压CursorAgent Hooks自动化系统,真正解决了AI编程工具的健忘问题这可能是今年最值得关注的AI编程工具。下载地址:kiro.dev/Windows用户、Mac用户都可以使用,基于VS Code架构,零学习成本。为什么说Kiro比Cursor更强?技术角度深度分析最近在用真实项目对比各种AI编程工具,发现Cursor在处理复杂业务逻辑时存在几个核心问题:上下文理解不足:经常遗忘项目结构,生成不一致的代码Token优化过度:为了省成本,功能完整性受影响缺乏工程化思维:直接生成代码,缺乏规范和文档Kiro的出现完全解决了这些痛点。Kiro安装体验:零门槛切换Kiro和Cursor一样基于VS Code架构,所以切换成本为零:但在交互设计上,Kiro提供了两种截然不同的工作模式:Vibe模式Spec模式Vibe模式:传统聊天式编程,适合快速原型开发Spec模式:规范驱动开发,这是Kiro的核心创新Spec模式:规范驱动开发的革命这是我见过最接近企业级开发标准的AI工具工作流。Spec模式遵循严格的三阶段开发流程:第一阶段:需求分析(Requirements)自动生成EARS语法标准的需求文档,包含:用户故事定义验收标准边界条件处理非功能性需求第二阶段:系统设计(Design)生成完整的技术设计文档:包含数据库Schema、API接口设计、组件架构图等生产级文档。第三阶段:实现计划(Implementation)将功能分解为有序任务,包含依赖关系和测试要求。任务管理与执行:颗粒度控制Kiro的任务管理机制是其核心优势之一。生成的文档会自动保存在项目根目录的.kiro文件夹中,每个任务都支持独立控制:关键特性:任务状态实时追踪支持并发任务执行智能任务队列管理任务队列这种颗粒度控制完全解决了Cursor一股脑生成代码导致的返工问题。Agent Hooks:自动化质量控制Kiro最具技术含量的功能是Agent Hooks系统,基于文件事件触发自动化检查:实时代码预览预览按钮预览效果通过Follow按钮可以实时查看代码修改,相比Cursor的全量预览和Claude Code的黑盒执行,Kiro提供了更好的可控性。一键回滚机制支持任务级别的原子回滚,比Cursor的checkpoint机制更精确。技术对比:Kiro vs Cursor 实战差异为了客观评估两个工具的差异,我用一个完整的团队任务管理系统项目进行了对比测试:测试场景项目复杂度:类似简化版Jira,包含用户系统、项目管理、任务流转、智能功能、实时通知、数据看板等完整模块技术栈:React + TypeScript + Tailwind CSS + Node.js + Express + PostgreSQL + Prisma ORM + Socket.ioAI集成:调用OpenAI API进行智能工时估算和任务分配评估维度:开发效率、代码质量、文档完整性、可维护性对比结果Cursor表现:直接开始写代码,缺乏整体规划面对复杂业务逻辑时容易遗漏关键模块生成的组件缺乏系统性设计数据库Schema设计不够完整实时通信和AI集成部分需要大量手动调整几乎没有项目文档输出Kiro表现:先生成完整的需求分析和系统设计文档自动分解为用户管理、项目管理、任务系统等独立模块生成完整的数据库Schema和API接口设计包含Socket.io集成和AI功能的详细实现方案自动生成组件架构图和数据流图输出可直接用于团队协作的技术文档核心差异面对这种企业级复杂项目,Cursor更像是功能堆砌,而Kiro展现了真正的系统工程思维。特别是在处理多模块协作、数据库设计、第三方集成等复杂场景时,Kiro的规范驱动开发优势非常明显。插播一则机-会技术大厂,前端-后端-测试,全国均有机-会,感兴趣可以试试。待遇和稳定性都还不错~成本效益分析:定价策略对比当前状态:Kiro完全免费,包含Claude-4模型访问权限未来定价:免费版:50次智能体交互/月Pro版:$19/月,1000次交互Pro+版:$39/月,3000次交互Cursor Pro对比:价格:$20/月限制:500次Chat + 无限Tab补全模型:GPT-4、Claude-4性价比分析: Kiro Pro比Cursor便宜$1,但提供2倍的交互次数,且基于更新的Claude-4模型。技术架构:AWS生态系统优势Kiro基于以下技术栈:前端:Code OSS(VS Code开源版)AI模型:Claude Sonnet 3.7/4.0协议支持:MCP(Model Context Protocol)云基础设施:AWS相比Cursor的多模型策略,Kiro专注于Claude系列模型的深度优化,在代码理解和生成质量上表现更稳定。我的AI编程工具新排名基于深度测试和生产环境使用经验:Kiro - 规范驱动开发,企业级标准Claude Code - 复杂逻辑分析专家Augment - 质量优先,适合高要求项目Cursor - 个人快速原型工具其他工具 - 功能差异明显选择建议适合Kiro的场景:需要完整文档的正式项目团队协作开发对代码质量要求较高企业级应用开发适合Cursor的场景:个人快速原型开发学习编程过程简单功能迭代写在最后Kiro的出现标志着AI编程工具的重要转变:1.0时代:代码生成和补全2.0时代:规范驱动的全流程工程化这种转变反映了行业从能用到好用再到专业的需求升级。建议先用Vibe模式熟悉界面,再尝试Spec模式体验规范驱动开发的完整流程。——转载自:子昕AI编程#技术干货#
什么时候用ref,什么时候用reactive?
官方文档提到过 ,ref 一把梭,不建议用 reactive。ref - 你的"万能工具箱"
// 什么都能装!
const name = ref('张三') // ✅ 字符串
const age = ref(18) // ✅ 数字
const isLoading = ref(false) // ✅ 布尔值
const user = ref({name: '李四'}) // ✅ 对象
const list = ref([]) // ✅ 数组
// 用的时候要加 .value
name.value = '王五'
age.value = 20
reactive - 你的"对象专用盒"
javascript
体验AI代码助手
代码解读
复制代码
// 只能装对象!
const user = reactive({ // ✅ 对象
name: '张三',
age: 18
})
const form = reactive({ // ✅ 对象
username: '',
password: ''
})
const list = reactive([]) // ✅ 数组(其实也是对象)
// 用的时候直接点属性
user.name = '李四'
form.username = 'admin'
关键区别:重新赋值问题机-会技术大厂,前端-后端-测试,全国均有机-会,感兴趣可以试试。待遇和稳定性都还不错~场景:从后台请求数据❌ reactive 的错误用法:
// 初始化
let list = reactive(['苹果', '香蕉'])
// 模拟请求数据
setTimeout(() => {
const newData = ['西瓜', '葡萄', '芒果']
// ❌ 错误!这样会丢失响应式!
list = newData
// 页面不会更新!因为 list 的"监听器"断了
}, 1000)
✅ ref 的正确用法:
// 初始化
const list = ref(['苹果', '香蕉'])
// 模拟请求数据
setTimeout(() => {
const newData = ['西瓜', '葡萄', '芒果']
// ✅ 正确!通过 .value 重新赋值
list.value = newData
// 页面正常更新!
}, 1000)
✅ reactive 的正确用法(如果非要用):
const state = reactive({
list: ['苹果', '香蕉']
})
setTimeout(() => {
const newData = ['西瓜', '葡萄', '芒果']
// ✅ 正确!只改属性,不改对象本身
state.list = newData
}, 1000)
📝 实战选择指南情况1:基础数据 → 必须用 ref
// ✅ 用 ref
const count = ref(0)
const name = ref('')
const isVisible = ref(true)
// ❌ reactive 会报错!
// const count = reactive(0) // 报错!
情况2:需要重新赋值 → 必须用 ref
// 从API获取数据
const data = ref(null)
const fetchData = async () => {
const result = await api.getData()
data.value = result // ✅ 可以重新赋值
}
// 切换页面数据
const currentPageData = ref([])
const changePage = (page) => {
currentPageData.value = getDataByPage(page) // ✅ 可以重新赋值
}
情况3:固定对象,只改属性 → 可以用 reactive
// 表单数据 - 通常不会整个替换
const form = reactive({
username: '',
password: '',
remember: false
})
// 用户信息 - 通常不会整个替换
const userInfo = reactive({
name: '张三',
age: 25,
avatar: ''
})
情况4:不确定用哪个 → 无脑用 ref
// 安全第一!
const something = ref(初始值)
🎯 黄金法则处理数字、字符串、布尔值? → 用 ref需要 xxx = 新数据 这样赋值? → 用 ref只是一个固定对象,只改里面的属性? → 可以考虑 reactive不确定? → 直接用 ref记住:ref 永远不会错,reactive 有时候会坑你!——转载自:我是天龙_绍#技术干货#
前端真的需要懂算法吗?聊聊感受
在公司干了几年,带个小团队,零零总总也面试了上百个前端候选人了。说实话,有时候面完一天,感觉人都是麻的。最让我头疼的是什么?就是“算法题”这个环节。我经常遇到两种候选人。一种是一听算法题,就两手一摊,表情痛苦,说“哥,我天天写业务,真没准备这个”。另一种呢,正好相反,题目一出,眼睛一亮,不出三十秒,就把LeetCode上背得滚瓜烂熟的最优解,一字不差地敲了出来,然后一脸期待地看着我。说实话,这两种,都不是我最想看到的。这就引出了一个很多候选人都想问,但不敢问的问题:“你们这些面试官,到底怎么想的?你们明知道我们前端平时工作中,99%的时间都用不上这些,为什么非要折磨我们?”今天,我就想站在桌子对面,跟大伙掏心窝子地聊聊,我们问算法题,到底图个啥。首先,我得承认一件事:我们知道你工作中不怎么写算法对,你没看错。我心里门儿清,我团队里的小伙伴们,每天的工作是跟产品经理“吵架”,是跟UI设计师对像素,是封装React/Vue组件,是处理浏览器兼容性,是调CSS。我招你进来,也不是为了让你用动态规划来给按钮加border-radius的。我们不会天真地以为,前端开发就是算法竞赛。如果你能把一个复杂的业务表单组件写得清晰、可维护、可扩展,在我眼里,这远比你徒手写一个红黑树要来得有价值。所以,请你先放轻松。我们不是在考察你是不是一个“算法大神”。机-会技术大厂,前端-后端-测试,全国均有机-会,感兴趣可以试试。待遇和稳定性都还不错~那我们到底在看什么?——思路远比答案重要既然不是看你会不会背最优解,那我们花这宝贵的20分钟,到底在考察什么?其实,算法题只是一个“载体”,一个“媒介”。通过这个载体,我想看到的是这几样东西:1. 你是怎么“解读”问题的(沟通与理解能力)一个靠谱的工程师,拿到需求不会立刻动手。他会先问问题,搞清楚所有的边界和约束。我出一道题:“写个函数,找出数组中第二大的数。”普通候选人:埋头就开始写代码。我欣赏的候选人:会先问我,“这个数组里会有重复的数字吗?会是无序的吗?会有负数吗?如果数组长度小于2怎么办?”你看,这就是差距。我能通过这些问题,看出你是否严谨,是否有处理边界情况的意识。这个能力,在你将来面对产品经理那些模糊的需求时,至关重要。2. 你的“思路”是否清晰(逻辑思维)我最喜欢看到的,不是你直接写出最优解,而是你告诉我你的思考过程。比如,你可以说:“我首先想到的,是一个最笨的办法,先排序,然后取倒数第二个。这个时间复杂度是O(n log n)。但感觉可以优化,我再想想……也许我只需要遍历一遍,用两个变量来维护最大值和第二大值,这样时间复杂度就降到O(n)了。”这个“先暴力,再优化”的思考过程,在我看来,比你直接默写出最优解要加分得多。因为它展示了你的逻辑推理能力和优化意识。3. 你的代码“品味”(工程素养)算法题的代码量不大,但足以管中窥豹,看出一个人的代码“品味”。你的变量是怎么命名的?a, b, c 还是 max, secondMax, current?你有没有处理我刚才提到的那些边界情况?你的代码有没有基本的缩进和格式?这些细节,都反映了你平时的编码习惯。一个连算法题都写得乱七八糟的人,我很难相信他在业务项目里能写出整洁的代码。4. 当你卡住时,你会怎么办?(抗压与学习能力)我有时候会故意出一些有点难度的题。我不是为了让你难堪,而是想看看你卡住的时候,会有什么反应。是直接放弃,说“不会”?还是会尝试跟我沟通,说“我卡在xxx了,能不能给点提示?”我非常乐意给提示。我更想招一个能和我一起“协作”解决问题的人,而不是一个遇到困难就“躺平”的人。你面对一道题的态度,很可能就是你未来面对一个技术难题的态度。给求职者的一些真心话所以,聊了这么多:别光背题,没用。 我只要稍微改动一下题目条件,或者问你为什么这么写,背题的同学马上就露馅了。多练习“说” 。刷题的时候,试着把你的思路说出来,录下来自己听听,或者讲给朋友听。面试时的口头表达,和自己闷头做题是两回事。重点理解“为什么” 。不要满足于“这道题这么解”,要去理解它为什么要用双指针,为什么要用哈希表。理解了思路,才能举一反三。面试时,心态放平。 没做出最优解,真没关系。把你思考的过程、你的尝试、你的权衡都清晰地表达出来,你已经赢了很多人了。我知道,让前端去卷算法,这个“游戏规则”本身就不那么公平。我们想找的是一个会思考、会沟通、有工程素养的“解决问题的人”。算法题,只是恰好成了当前最方便、成本最低的考察工具而已。——转载自:ErpanOmer
为什么推荐前端学习油猴脚本开发?
相信不少前端同学对 Chrome 扩展插件并不陌生:比如能屏蔽广告的 Adblock、自定义标签页的 Infinity 新标签页、格式化 JSON 的开发小工具、截图工具,甚至还有 Vue.js devtools、Redux DevTools 这样的调试利器。很早以前,我也被这些插件的强大功能吸引,暗自立下目标,一定要掌握扩展插件开发。但实际动手后发现,Chrome 插件的开发门槛并不低,权限机制、构建流程、清单配置……这些问题让我一度搁置。直到后来,我接触到了油猴(Tampermonkey) 脚本开发。它让我重新燃起了对网页增强脚本的兴趣:同样可以实现丰富的浏览器功能,但上手却要简单许多。借助 HTML、CSS 和 JavaScript,便能快速开发出媲美插件的实用功能,而且部署和调试都非常灵活。正因如此,我花了很多时间研究油猴脚本的各种能力,从简单的界面增强,到跨域数据请求、页面劫持,再到摄像头识别、画中画等进阶玩法,这些都可以仅靠一个脚本实现。也正是在这个过程中,我写下了《油猴脚本实战指南》这本小册,分享我一路走来的经验一些技术分享。这本小册上线已近一个月,期间我也遇到了很多志同道合的开发者,他们和我一样,在原本有限的时间里,通过脚本开发打开了新的成长路径。如果你也是一名前端开发者,渴望提升能力却苦于时间有限,不妨试试油猴脚本开发。它不仅能帮你解决日常工作中的小痛点,还能训练你的综合编程能力。对我来说,这是性价比极高的一种前端进阶方式。希望你也能从中收获惊喜。什么是油猴脚本油猴(Tampermonkey)是一款浏览器插件,允许用户在网页加载时注入自定义的 JavaScript 脚本,来增强、修改或自动化网页行为。通俗地说,借助油猴,你可以将自己的 JavaScript 代码“植入”任意网页,实现自动登录、抢单、签到、数据爬取、广告屏蔽等各种“开挂级”功能,彻底掌控页面行为。分享一些我实现的有趣网页脚本手势识别实现网页控制人脸识别实现”人脸版黄金旷工“小游戏接口拦截工具:修改CSDN博客数据接口返回值Vue路由一键切换:开发效率起飞任意元素双击实现画中画:摸鱼超级助手掘金后台自动签到助手解除文本复制、网页复制、一键下载为MD主题切换助手它能给你带来什么提高互联网体验通过用户脚本,我们可以为网页添加各种实用功能,显著提升浏览效率和使用体验。比如在阅读时,我们可以实现一键翻译、自动展开全文、解除复制限制、去除广告干扰;在观看视频时,可以开启 VIP 视频解析、倍速播放,甚至自动跳过片头片尾;对于学习和工作场景,还能自动刷题、辅助答题、自动播放课程;而在购物或资源获取方面,脚本也能帮你自动抢购、快速下载网页中的音视频内容。所有这些功能,只需我们动动手写几行JavaScript,就能一劳永逸!提升工作效率在日常工作中,我们经常会遇到一些重复、耗时又低效的操作,比如:每天手动输入账号密码登录某个网页、反复填写相似的表单内容、开发中费尽周折从网页中提取数据、频繁刷新 token 以保持页面正常访问,在多路由项目中不断的手动切换页面路径……这些看似“习以为常但不可避免”的操作,实则完全可以通过脚本解决。提高工作竞争力在公司工作中,很多重复、低效的流程其实完全可以通过脚本来优化。如果你能主动用脚本解决这些问题,不仅能提升团队效率,更能显著增强你在公司的核心竞争力。以我自己的经历为例:我们公司的前端项目采用的是 Qiankun 微前端架构。在本地开发子应用时,由于缺乏主应用的数据支持,调试过程经常变得非常麻烦。为了解决这个问题,我编写了一个脚本工具,能够将主应用的数据无缝注入本地环境,同时也支持将本地子应用嵌入到线上主应用中进行联调。这极大地提升了开发调试的效率。后来,这个脚本在公司内部广泛推广,并被评为“最佳提效工具”,也为我在职级晋升中加分不少。所以,如果你能通过脚本解决公司一些业务痛点,很容易提升你的核心竞争力。增加面试亮点果你在简历中写到:“通过脚本解决了开发过程中的 XXX 问题,优化了业务流程中的 XXX 环节,节省了 XX 小时的人力成本”——那你的简历一定会脱颖而出,更容易通过面试。毕竟,几乎所有面试官和管理者都欣赏那种善于发现问题、主动用技术解决问题,并能为团队带来实实在在价值的人。变现利用脚本变现有多种途径,效果因人而异。你可以在 GreasyFork 等平台发布实用脚本,获得用户打赏;也可以承接定制开发项目,实现接单变现;此外,脚本还能帮助你降低其他互联网变现方式的运营成本,从而实现效益的最大化。机-会技术大厂,前端-后端-测试,全国均有机-会,感兴趣可以试试。待遇和稳定性都还不错~学习难度用户脚本本质上是通过 JavaScript 增强网页功能或操作页面 DOM,所以对于前端同学而言,你只需掌握油猴的开发规则和几个关键 API,就能快速上手。借助本小册基础篇的内容,你甚至可以在 2 到 4 小时内完成脚本的入门学习,轻松实现脚本编写与快速发布。但是,如果不懂CSS+HTML+JavaScript ,你需要提前学习这些前置知识。此外,你甚至能零成本将一个油猴脚本打包成一个原生谷歌浏览器插件,YYDS!——转载自:石小石Orz
1个前端同时联调多个后端,牛马的顶级觉悟
场景同样的接口,同样的前缀,只是后端地址不一样,怎么同时代理多个地址呢?也就是一个前端,怎么连接多个后端的地址?一个前端,需要同时和N个后端联调一个需求里有若干个模块,分别给不同的后端开发,前端需要和N个后端联调测试环境在测其他模块,合并发布太麻烦,所以本地开启一个端口给测试,然后你需要去做其他的需求,但是其他的需求需要连接另一个后端接口其他情况,总之:1个前端 VS N个后端上述场景,都是一个前端,联调N个后端的场景,你可能没遇到过,但是确实存在上述的场景。尤其是第三种最为常见,你会一直等测试完了、再去换一个后端代理地址接着开发吗?当然不能,这样做很浪费时间,说明你不是一个合格的牛马,牛马的觉悟不够,牛马是不会让自己闲着的。那么,怎么办呢?和A联调时proxy指向url-A,和B联调时proxy指向url-B……换其他人联调时,你是把本地项目关掉,然后换个proxy代理、再重启一下吗?当然可以,如果你不嫌麻烦的话!那么有没有好办法呢?这个问题问得好,当然有了!解决方案本例以vue2的vue-cli方式【webpack】为例 如果你们公司用的是vue3可以跳过思路既然vue.config.js能代理一个proxy,那么能不能代理多个proxy呢?当然能!本文先讲解传统模式的代理,以后写一个函数式代理 vite有更好的代理方式,暂且不表实现步骤创建项目创建一个空的vue2项目
vue create project-name
默认情况下,npm run serve会启动8080端口目标我希望不同的端口,指向不同的后端代理proxy地址,如:8100端口,代理后端7001端口8105端口,代理后端7002端口没毛病吧?本文以代理2个后端为例,其余的大家自行补充前端配置安装cross-env
yarn add cross-env
cross-env是nodejs设置环境变量的工具,它解决了不同操作系统之间环境变量设置语法不一致的问题,具体可自行搜索配置vue.config.js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
// webpack-dev-server 相关配置
devServer: {
host: '0.0.0.0',
port: process.env.PORT || 8100,
open: false,
proxy: {
'/api': {
target: getProxyTarget(process.env.PORT),
changeOrigin: true,
pathRewrite: { '^/api': '' }
}
},
},
})
function getProxyTarget(port) {
switch (port) {
case '8100':
return 'http://127.0.0.1:7001'
case '8105':
return 'http://127.0.0.1:7002'
default:
return 'http://127.0.0.1:7001' // 默认代理地址
}
}
上述代码,默认设置启动端口为8100,并且getProxyTarget函数可以根据不同的端口,指向不同的代理地址。配置package.json
{
"name": "more-proxy",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"serve:8005": "cross-env PORT=8105 vue-cli-service serve",
"build": "vue-cli-service build"
},
"dependencies": {
"cross-env": "^7.0.3",
"vue": "^2.6.14"
},
"devDependencies": {
"@vue/cli-service": "~5.0.0",
"vue-template-compiler": "^2.6.14"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}
上述代码,重点是serve:8005这行,就是你要代理哪个端口,这里需要你在vue.config.js写对应的映照proxy机-会技术大厂,前端-后端-测试,全国均有机会,感兴趣可以试试。待遇和稳定性都还不错~#技术干货#测试上述配置已经实现了我们的需求,那么,具体测试一下吧。本地启动两个nodejs服务,分别为7001和7002端口,内容如下
/// 7001端口
const http = require('http');
const hostname = '127.0.0.1';
const port = 7001;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('my port is 7001!');
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
javascript
体验AI代码助手
代码解读
复制代码
/// 7002端口
const http = require('http');
const hostname = '127.0.0.1';
const port = 7002;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('my port is 7002!');
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
然后前端把上面的2个端口启动,写一个测试函数
mounted() {
fetch('/api').then(() => {})
}
效果如下: 8100已经成功代理7001了 同样的,8105也代理了7002 其余的vue3,react,也可以用类似的思路如果感兴趣,可以点一下关注,后续会出函数式1前端 VS N后端,更加优雅如果有其他更好的方案,可以评论留言。——转载自:前端没钱
别搞混了!MCP 和 Agent Skill 到底有什么区别?
MCP 与 Skill 深度对比:AI Agent 的两种扩展哲学用 AI Agent 工具(Claude Code、Cursor、Windsurf 等)的时候,经常会遇到两个概念:MCP(Model Context Protocol)Skill(Agent Skill)它们看起来都是"扩展 AI 能力"的方式,但具体有什么区别?为什么需要两套机制?什么时候该用哪个?这篇文章会从设计哲学、技术架构、使用场景三个维度,把这两个概念彻底讲清楚。一句话区分先给个简单的定位:MCP 解决"连接"问题:让 AI 能访问外部世界 Skill 解决"方法论"问题:教 AI 怎么做某类任务用 Anthropic 官方的说法:"MCP connects Claude to external services and data sources. Skills provide procedural knowledge—instructions for how to complete specific tasks or workflows."打个比方:MCP 是 AI 的"手"(能触碰外部世界),Skill 是 AI 的"技能书"(知道怎么做某件事)。你需要两者配合:MCP 让 AI 能连接数据库,Skill 教 AI 怎么分析查询结果。MCP:AI 应用的 USB-C 接口MCP 是什么MCP(Model Context Protocol)是 Anthropic 在 2024 年 11 月发布的开源协议,用于标准化 AI 应用与外部系统的交互方式。官方的比喻是"AI 应用的 USB-C 接口"——就像 USB-C 提供了一种通用的方式连接各种设备,MCP 提供了一种通用的方式连接各种工具和数据源。关键点:MCP 不是 Claude 专属的。它是一个开放协议,理论上任何 AI 应用都可以实现。截至 2025 年初,已经被多个平台采用:Anthropic: Claude Desktop、Claude CodeOpenAI: ChatGPT、Agents SDK、Responses APIGoogle: Gemini SDKMicrosoft: Azure AI Services开发工具: Zed、Replit、Codeium、Sourcegraph到 2025 年 2 月,已经有超过 1000 个开源 MCP 连接器。MCP 的架构MCP 基于 JSON-RPC 2.0 协议,采用客户端-主机-服务器(Client-Host-Server)架构:
┌─────────────────────────────────────────────────────────┐
│ Host │
│ (Claude Desktop / Cursor) │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Client │ │ Client │ │ Client │ │
│ │ (GitHub) │ │ (Postgres) │ │ (Sentry) │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
└─────────┼────────────────┼────────────────┼─────────────┘
│ │ │
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐
│MCP Server │ │MCP Server │ │MCP Server │
│ (GitHub) │ │(Postgres) │ │ (Sentry) │
└───────────┘ └───────────┘ └───────────┘
Host:用户直接交互的应用(Claude Desktop、Cursor、Windsurf)Client:Host 应用中管理与特定 Server 通信的组件Server:连接外部系统的桥梁(数据库、API、本地文件等)MCP 的三个核心原语MCP 定义了三种 Server 可以暴露的原语:1. Tools(工具)—— 模型控制可执行的函数,AI 可以调用来执行操作。
{
"name": "query_database",
"description": "Execute SQL query on the database",
"parameters": {
"type": "object",
"properties": {
"sql": { "type": "string" }
}
}
}
AI 决定什么时候调用这些工具。比如用户问"这个月的收入是多少",AI 判断需要查数据库,就会调用 query_database 工具。2. Resources(资源)—— 应用控制数据源,为 AI 提供上下文信息。
{
"uri": "file:///Users/project/README.md",
"name": "Project README",
"mimeType": "text/markdown"
}
资源由应用控制何时加载。用户可以通过 @ 引用资源,类似于引用文件。3. Prompts(提示)—— 用户控制预定义的提示模板,帮助结构化与 AI 的交互。
{
"name": "code_review",
"description": "Review code for bugs and security issues",
"arguments": [
{ "name": "code", "required": true }
]
}
用户显式触发这些提示,类似于 Slash Command。机-会技术大厂,前端-后端-测试,全国均有机-会,感兴趣可以试试。待遇和稳定性都还不错~MCP 与 Function Calling 的关系很多人会问:MCP 和 OpenAI 的 Function Calling、Anthropic 的 Tool Use 有什么区别?Function Calling 是 LLM 的能力——把自然语言转换成结构化的函数调用请求。LLM 本身不执行函数,只是告诉你"应该调用什么函数,参数是什么"。MCP 是在 Function Calling 之上的协议层——它标准化了"函数在哪里、怎么调用、怎么发现"。两者的关系:
用户输入 → LLM (Function Calling) → "需要调用 query_database"
↓
MCP Protocol
↓
MCP Server 执行
↓
返回结果给 LLM
Function Calling 解决"决定做什么",MCP 解决"怎么做到"。MCP 的传输方式MCP 支持两种主要的传输方式:传输方式适用场景说明Stdio本地进程Server 在本地机器运行,适合需要系统级访问的工具HTTP/SSE远程服务Server 在远程运行,适合云服务(GitHub、Sentry、Notion)大部分云服务用 HTTP,本地脚本和自定义工具用 Stdio。MCP 的代价MCP 不是免费的午餐,它有明显的成本:1. Token 消耗大每个 MCP Server 都会占用上下文空间。每次对话开始,MCP Client 需要告诉 LLM "你有这些工具可用",这些工具定义会消耗大量 Token。连接多个 MCP Server 后,光是工具定义可能就占用了上下文窗口的很大一部分。社区观察到:"We're seeing a lot of MCP developers even at enterprise build MCP servers that expose way too much, consuming the entire context window and leading to hallucination."2. 需要维护连接MCP Server 是持久连接的外部进程。Server 挂了、网络断了、认证过期了,都会影响 AI 的能力。3. 安全风险Anthropic 官方警告:"Use third party MCP servers at your own risk - Anthropic has not verified the correctness or security of all these servers."特别是能获取外部内容的 MCP Server(比如网页抓取),可能带来 prompt injection 风险。MCP 的价值尽管有这些代价,MCP 的价值在于标准化和可复用性:一次实现,到处使用:同一个 GitHub MCP Server 可以在 Claude Desktop、Cursor、Windsurf 中使用动态发现:AI 可以在运行时发现有哪些工具可用,而不是写死在代码里供应商无关:不依赖特定的 LLM 提供商Skill:上下文工程的渐进式公开Skill 是什么Skill(全称 Agent Skill)是 Anthropic 在 2025 年 10 月发布的特性。官方定义:"Skills are organized folders of instructions, scripts, and resources that agents can discover and load dynamically to perform better at specific tasks."翻译一下:Skill 是一个文件夹,里面放着指令、脚本和资源,AI 会根据需要自动发现和加载。Skill 在架构层级上和 MCP 不同。用 Anthropic 的话说:"Skills are at the prompt/knowledge layer, whereas MCP is at the integration layer."Skill 是"提示/知识层",MCP 是"集成层"。两者解决不同层面的问题。Skill 的核心设计:渐进式信息公开Skill 最精妙的设计是渐进式信息公开(Progressive Disclosure)。这是 Anthropic 在上下文工程(Context Engineering)领域的重要实践。官方的比喻:"Like a well-organized manual that starts with a table of contents, then specific chapters, and finally a detailed appendix."就像一本组织良好的手册:先看目录,再翻到相关章节,最后查阅附录。Skill 分三层加载:Claude 判断相关需要更多信息第 3+ 层:支持文件(深度按需)reference.mdscripts/helper.pytemplates/...第 2 层:核心指令(按需加载)SKILL.md 完整内容通常 [removed]
Review code for bugs, security issues, and style violations.
Use when asked to review code, check for bugs, or audit PRs.
---
#CodeReviewSkill## Instructions
When reviewing code, follow these steps:
1. First check for security vulnerabilities...
2. Then check for performance issues...
3. Finally check for code style...
关键字段:name:Skill 的唯一标识,小写字母 + 数字 + 连字符,最多 64 字符description:描述做什么、什么时候用,最多 1024 字符description 的质量直接决定 Skill 能不能被正确触发。Skill 的安全考虑Skill 有一个潜在的安全问题:Prompt Injection。研究人员发现:"Although Agent Skills can be a very useful tool, they are fundamentally insecure since they enable trivially simple prompt injections. Researchers demonstrated how to hide malicious instructions in long Agent Skill files and referenced scripts to exfiltrate sensitive data."因为 Skill 本质上是注入指令,恶意的 Skill 可以在长文件中隐藏恶意指令,窃取敏感数据。应对措施:只使用可信来源的 Skill审查 Skill 中的脚本使用 allowed-tools 限制 Skill 的能力范围
---
name: safe-file-reader
description: Read and analyze files without making changes
allowed-tools: Read, Grep, Glob # 只允许读操作
---
Skill 的平台支持Agent Skills 目前支持:Claude.ai(Pro、Max、Team、Enterprise)Claude CodeClaude Agent SDKClaude Developer Platform需要注意的是,Skill 目前是 Anthropic 生态专属的,不像 MCP 是跨平台的开放协议。MCP vs Skill:架构层级对比现在我们可以从架构层级来理解两者的区别:
┌─────────────────────────────────────────────────────────┐
│ 用户请求 │
└────────────────────────┬────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ 提示/知识层 (Skill) │
│ │
│ Skill 注入专业知识和工作流程 │
│ "怎么做某类任务" │
└────────────────────────┬────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ LLM 推理层 │
│ │
│ Claude / GPT / Gemini 等 │
│ 理解请求,决定需要什么工具 │
└────────────────────────┬────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ 集成层 (MCP) │
│ │
│ MCP 连接外部系统 │
│ "能访问什么工具和数据" │
└────────────────────────┬────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ 外部世界 │
│ │
│ 数据库、API、文件系统、第三方服务 │
└─────────────────────────────────────────────────────────┘
Skill 在上层(知识层),MCP 在下层(集成层)。两者不是替代关系,而是互补关系。你可以:用 MCP 连接 GitHub用 Skill 教 AI 如何按照团队规范做 Code Review详细对比表维度MCPSkill核心作用连接外部系统编码专业知识和方法论架构层级集成层提示/知识层协议基础JSON-RPC 2.0文件系统 + Markdown跨平台是(开放协议,多平台支持)否(目前 Anthropic 生态专属)触发方式持久连接,随时可用基于描述的语义匹配,自动触发Token 消耗高(工具定义持久占用上下文)低(渐进式加载)外部访问可以直接访问外部系统不能直接访问,需要配合 MCP 或内置工具复杂度高(需要理解协议、运行 Server)低(写 Markdown 就行)可复用性高(标准化协议,跨应用复用)中(文件夹,可以 Git 共享)动态发现是(运行时发现可用工具)是(运行时发现可用 Skill)安全考虑外部内容带来 prompt injection 风险Skill 文件本身可能包含恶意指令什么时候用 MCP,什么时候用 Skill用 MCP 的场景需要访问外部数据:数据库查询、API 调用、文件系统访问需要操作外部系统:创建 GitHub Issue、发送 Slack 消息、执行 SQL需要实时信息:监控系统状态、查看日志、搜索引擎结果需要跨平台复用:同一个工具在 Claude Desktop、Cursor、其他支持 MCP 的应用中使用用 Skill 的场景重复性的工作流程:代码审查、文档生成、数据分析公司内部规范:代码风格、提交规范、文档格式需要多步骤的复杂任务:需要详细指导的专业任务团队共享的最佳实践:标准化的操作流程Token 敏感场景:需要大量知识但不想一直占用上下文结合使用很多时候,两者是配合使用的:
用户:"Review PR #456 并按照团队规范给出建议"
1. MCP (GitHub) 获取 PR 信息
↓
2. Skill (团队代码审查规范) 提供审查方法论
↓
3. Claude 按照 Skill 的指令分析代码
↓
4. MCP (GitHub) 提交评论
MCP 负责"能访问什么",Skill 负责"怎么做"。写好 Skill 的关键Skill 能不能被正确触发,90% 取决于 description 写得好不好。差的 description
description: Helps with data
太宽泛,Claude 不知道什么时候该用。好的 description
description: >
Analyze Excel spreadsheets, generate pivot tables, and create charts.
Use when working with Excel files (.xlsx), spreadsheets, or tabular data analysis.
Triggers on: "analyze spreadsheet", "create pivot table", "Excel chart"
好的 description 应该包含:做什么:具体的能力描述什么时候用:明确的触发场景触发词:用户可能说的关键词最佳实践官方建议:保持专注:一个 Skill 做一件事,避免宽泛的跨域 SkillSKILL.md 控制在 500 行以内:太长的话拆分到支持文件测试触发行为:确认相关请求能触发,不相关请求不会误触发版本控制:记录 Skill 的变更历史关于 Slash Command文章标题是 MCP vs Skill,但很多人也会问到 Slash Command,简单说一下。Slash Command 是最简单的扩展方式——本质上是存储的提示词,用户输入 /命令名 时注入到对话中。Skill vs Slash Command 的关键区别是触发方式:Slash CommandSkill触发方式用户显式输入 /命令Claude 自动匹配用户控制完全控制何时触发无法控制,Claude 决定问自己一个问题:用户是否需要显式控制触发时机?需要 → Slash Command不需要,希望 AI 自动判断 → Skill总结MCP 和 Skill 是 AI Agent 扩展的两种不同哲学:MCPSkill哲学连接主义知识打包问的问题"AI 能访问什么?""AI 知道怎么做什么?"层级集成层知识层Token 策略预加载所有能力按需加载知识记住这句话:MCP connects AI to data; Skills teach AI what to do with that data.MCP 让 AI 能"碰到"数据,Skill 教 AI 怎么"处理"数据。它们不是替代关系,而是互补关系。一个成熟的 AI Agent 系统,两者都需要。参考资源MCP 官方资源Model Context Protocol 官网 - 协议规范、快速入门、Server 开发指南MCP Specification - 完整的协议规范文档Introducing the Model Context Protocol - Anthropic 发布 MCP 的官方博客MCP GitHub Organization - 官方 SDK、示例 Server、参考实现Awesome MCP Servers - 社区维护的 MCP Server 列表Skill 官方资源Claude Code Skills 文档 - Skills 的完整文档Building effective agents - Anthropic 关于 Agent 设计的研究博客Context Engineering Guide - 上下文工程官方指南,理解 Skill 设计哲学的关键跨平台采用OpenAI adds support for MCP - OpenAI 宣布支持 MCPGoogle Gemini MCP Support - Google 宣布 Gemini 支持 MCP延伸阅读Function Calling vs MCP - 理解两者区别Claude Code Documentation - Claude Code 完整文档Prompt Engineering Guide - 提示工程基础,Context Engineering 的前置知识如果你觉得这篇文章有帮助,欢迎关注我的 GitHub,下面是我的一些开源项目:Claude Code Skills(按需加载,意图自动识别,不浪费 token,介绍文章):code-review-skill - 代码审查技能,覆盖 React 19、Vue 3、TypeScript、Rust 等约 9000 行规则(详细介绍)5-whys-skill - 5 Whys 根因分析,说"找根因"自动激活first-principles-skill - 第一性原理思考,适合架构设计和技术选型全栈项目(适合学习现代技术栈):prompt-vault - Prompt 管理器,用的都是最新的技术栈,适合用来学习了解最新的前端全栈开发范式:Next.js 15 + React 19 + tRPC 11 + Supabase 全栈示例,clone 下来配个免费 Supabase 就能跑chat_edit - 双模式 AI 应用(聊天+富文本编辑),Vue 3.5 + TypeScript + Vite 5 + Quill 2.0 + IndexedDB——转载自:也无风雨也雾晴
前端技巧:检测到省略号文本自动显示 Tooltip
前言在前端开发中,我们经常会遇到接口返回的文本内容过长,无法完全显示的问题。为了处理这一问题,通常会设置固定的宽度并使用省略号样式(text-overflow: ellipsis)来隐藏超出的文本。然而,有时产品需求还希望用户能够通过悬停查看完整内容,这时就需要引入 Tooltip 进行展示。(没被省略的时候不要显示Tooltip)
// tailwind的样式单行省略
.line-clamp-1 {
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
}
// 自行设置的css样式
single-line {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
为了解决这个问题,我们实现了一个自定义 Hook,该 Hook 会监测文本元素是否因宽度限制而被省略。一旦检测到文本内容被省略,Hook 会自动为该元素添加 Tooltip,确保用户可以方便地查看完整信息。代码实现use-ellipsis.ts
import { useEffect, useRef, useState } from 'react';
type Options = {
lines?: number; // 支持多行
};
export function useEllipsis : content;
};
机会技术大厂,前端-后端-测试,全国各地等均有机-会,感兴趣可以试试~使用示例与效果
export default function TestPage() {
const mockText = '很长很长很长很长很长';
const mockText2 = '简短的';
return (
);
}
——转载自:代码小学僧
Stack Overflow,轰然倒下了!
提到 Stack Overflow 技术社区,提到那个橙色的栈溢出图标,相信程序员和开发者们应该都再熟悉不过了。最近在网上看到了一个有关 Stack Overflow 社区的变化趋势图,让人感慨万千。这个曲线图表示的是自 2008 年开始,一直到 2025 年的今天,这 17 年时间,Stack Overflow 社区上每个月新问题个数的变化趋势。怎么样?大家看完这张图有没有什么感触?这张图清晰地展示出了 Stack Overflow 编程社区在这 17 年间所经历的增长、繁荣以及回落的趋势。可以看到,从 2008 年到 2014 年这前 6 年的时间,Stack Overflow 一路高歌,渐入佳境,基本都在稳步增长。而从 2014 年到 2022 年这中间的 8 年时间,虽说图中曲线呈震荡变化状态,但总体都是处于高位趋势,这也是 Stack Overflow 社区的繁荣时刻。从数据上来看,Stack Overflow 最高光的顶峰时刻出现在 2020 年,尤其是在 2020-05-01 这个时间节点,新问题个数来到了 302381 的顶峰。而从图中也可以很明显地看出,自 2022 年底开始,Stack Overflow 社区日渐式微,开始出现了回落之势。同样是那一年底,OpenAI 正式发布了 ChatGPT。机-会技术大厂,前端-后端-测试,新一线和一二线城市等地均有机-会,感兴趣可以试试。待遇和稳定性都还不错~那后面几年的故事,相信大家都非常清楚了,AI 大模型相关的技术和产品飞速迭代,传统的搜索引擎和知识社区也受到了不小的冲击。到了如今的 2025 年,Stack Overflow 的数据也已经跌回到了 15 年前的水平了。这几年的趋势相信大家都非常清楚,AI 大模型日新月异,相关的模型产品更是百花争艳,层出不穷,这对于 Stack Overflow 的冲击在图上也体现得淋漓尽致。于是我也开始回想,我自己这几年在互联网上搜索信息的方式,似乎在不知不觉中已经发生了很大的变化。现在遇到问题,我好像已经不怎么喜欢使用传统搜索引擎了,而是会习惯性地转向各种 AI 大模型工具和智能助手,同时信息的处理和交互范式也完全变了。我们还以编程写代码为例。以前当我们在写代码调试运行出现错误但折腾半天也不知所以的时候,大家会怎么做?相信不少同学和我一样,也是首先复制这段报错信息到搜索引擎中进行检索,然后根据搜索引擎吐出来的搜索结果,自己逐个点进去筛选有用的信息。而当我们一旦在搜索结果里看到了 Stack Overflow 的相关网页时,我们的直觉会告诉我,离问题解决应该不远了。而即便当我们在搜索引擎里找不到任何解决问题的方法,那我们也可以去类似 Stack Overflow 这样的编程社区里进行发帖求助,然后等待被查看和回答。这是在 AI 大模型还没有爆发之前,大家所普遍采用的一个解决问题的办法。但如今一切都变了。随着 AI 模型和产品的快速渗透,大家解决问题的方式发生了变化。我们直接甩给 AI 工具一个问题或者一段信息,AI 工具便会自动理解你的意图,并开始深度思考、收集信息、整理逻辑、分析总结、加工输出,最后直接把生成的答案或解决问题的办法呈现在你的眼前。和这些传统知识社区和搜索引擎相比,AI 大模型很强,这无需质疑。AI 大模型强就强在它的理解能力、整合能力以及推理能力,这些都是传统知识社区和搜索引擎往往所欠缺的东西。传统搜索引擎往往依赖于关键词匹配和链接分析,因此对于用户问题的理解往往有所欠缺,而 AI 大模型则能够深度理解语言含义和上下文,理解问题的真正意图。同时 AI 大模型能阅读、理解并整合数据中不同维度的海量知识,并能在此基础上来进行进一步的推理、分析、总结、泛化,这在如今的信息爆炸的时代来说是一种巨大的价值。所以相比去 Stack Overflow 上发帖子、搜问题、筛答案,AI 引擎无论在时间上还是知识维度的扩展上都给了这些传统知识社区和搜索引擎以降维打击。如此一看,如果再不转型,像 Stack Overflow 这样的传统编程社区的轰然倒下,似乎也成了一个必然的趋势。说到底,AI 大模型不是搜索引擎的简单升级版,而是一种全新的信息处理和交互范式。那面对这波 AI 大模型浪潮的席卷和冲击,不少传统的搜索引擎和知识社区都开始了转型升级,并积极拥抱 AI。比如像 Stack Overflow 自己也搞了一个 Overflow AI,其中包含了一套基于他们自己的历史内容和知识库所打造的 GenAI 工具。从「检索工具」进化到「智能助手」,这是不少现有知识社区和搜索引擎正在经历的蜕变之路。这两年 AI 大模型领域的发展速度相信大家都有目共睹了,技术迭代进化更是远超预期。可以预见的是,未来的信息检索和交互方式一定还会进一步高效、精准和智能,而对此我们也可以拭目以待。好了,那以上就是今天的内容分享了,感谢大家的阅读,我们下篇见。——转载自:CodeSheep
AI 只会淘汰不用 AI 的程序员🥚
作为程序员,你竟然还在手撸代码 ??? 如果没有公司给你提供科学上网,提供AI 编程工具的账号,你真能玩转AI ??? 除了平时搜搜查查,AI 对你还有其他用处 ???震惊!某博主竟然开头就贩卖焦虑?难道程序员真的要被 AI 取代了? 别急,这篇文章就一步步带你玩转 AI 编程! 如果只是想了解如何使用 AI 编程,可以直接跳到章节: 「所以,我们需要什么!?」理解概念要深入使用 AI,我们要先理解一些概念1. AI 基础AI大模型:拥有超大规模参数、超级聪明的机器学习模型,所有的 AI 应用都是调用大模型的计算处理能力。如:问答、图片生成、视频生成。[机-会]技术大厂,前端-后端-测试,新一线和一二线城市等地均有机-会,感兴趣可以试试。待遇和稳定性都不错~————国内主流大模型对比:国外主流大模型对比模型厂商核心能力主攻场景Gemini🐂🍺Google DeepMind多模态能力强大,可无缝处理文本、图像、音频、视频、代码等创意内容创作、文档处理、用于处理复杂、多源信息的场景Claude🐂🍺Anthropic推理能力优秀,多模态能力一般长文档分析场景,如法律文件审查;适用于需要可靠输出的领域,如医疗诊断辅助Veo 3Google自动生成视频和音频,口型同步精准到毫秒级。支持最高 4K 分辨率输出,画质清晰,色彩还原。生成速度快专注于视频生成领域,如短视频内容创作,为用户提供高效、高质量的视频生成解决方案。SonnetAnthropicSonnet 是 Claude 3 系列中的平衡型模型,性价比高适用于注重性价比和处理速度的场景,如一般性的文档分析GPTOpenAI通用性极强,各个方面都有出色表现,GPT-4o 等版本增强了多模态交互能力。自然语言处理相关的场景,如内容创作、智能客服CopilotGitHub 与 OpenAI 合作开发基于 GPT 系列模型训练,理解自然语言,生成对应代码用于日常编码、代码调试、新手编程学习,降低重复编码工作量一般我们编程使用的都是国外的大模型,毕竟开发工具、系统、编程语言都是外国的。编码方面的能力,国外模型还是碾压的存在。MCP:一套提供给 AI 大模型调用的标准协议。一些厂商会把自己的能力包装成MCP,让大模型在理解完用户的复杂任务时,可以调用厂商的能力。比如:你让豆包给你用 “高德” 生成一份超准的导航,豆包就会去调用高德的 MCP,为你出导航~IDE:程序员专属概念。AI IDE是集成了 AI 能力的软件开发平台,开发者可以通过自然语言,让 IDE 调用 AI 模型和 MCP 给你写代码,速度和质量牛的飞起,真有手就能写代码!主流的 AI IDE 对比名称厂商搭载的模型收费维度Cursor🐂Cursor 公司GPT-4、Claude 3.5、Cursor-small、o3-mini 等很贵,按 token 收费Antigravity🐂🍺Google支持在 Gemini 3 Pro、Claude Sonnet 4.5 和 GPT-OSS 等多种模型之间无缝切换有羊毛薅,国外邮箱+学生认证~Trae字节跳动国内版搭载豆包 1.5-pro、DeepSeek R1/V3 等模型,海外版内置 GPT-4o、Claude-3.5-Sonnet 模型国内的,充个会员的事,不贵2. RAG ➡️ Agent ➡️ Planning:AI 应用方式的演进之路AI 的应用方式正从 “被动响应” 向 “主动规划” 快速迭代。 从 RAG 进行检索增强生成,到 AI Agent 实现自主调用工具完成任务,再到 Planning 能 “拆解复杂任务与全局决策” 的高阶形态。让 AI 从 “内容生成器” 蜕变到 “智能协作体”。RAG —— 检索增强生成 核心逻辑是:先检索,再生成。用户提出问题,先从外部数据库、文档库中检索与问题最相关的信息,再将这些信息作为 “参考资料” 喂给大模型,让模型基于真实数据生成回答。AI Agent —— “自主工具操作员” AI Agent(智能体) ,让 AI 像人一样调用工具、执行步骤、验证结果。能理解用户的模糊需求,自主规划任务步骤,选择并调用合适的工具(如计算器、浏览器、代码解释器、RAG 系统),完成任务。Planning(规划)—— “全局任务指挥官” AI 系统的高阶能力,核心逻辑是 “先拆解,再执行,再调整”。基于全局目标,将复杂、长期、多约束的任务拆解为有序的子任务序列,并根据执行过程中的反馈动态调整策略。 不仅关注单个任务的完成,更关注子任务之间的关联和整体目标的达成。演进逻辑与核心差异总结维度RAG(检索增强生成)AI Agent(智能体)Planning(规划)核心定位大模型的 “知识库外挂”自主工作的 “工具操作员”全局任务的 “指挥官”能力核心检索 + 生成,保证回答准确决策 + 工具调用,完成单任务闭环拆解 + 协同 + 动态调整,掌控多任务全局典型比喻学生的 “参考书”能独立完成工作的 “专员”统筹全局的 “项目经理”所以,我们需要什么!?你作为一名优秀的程序员,你需要通过科学上网、精准付费,在 AI IDE中,基于AI大模型的能力,熟练使用 Agent/Planning,配合 MCP 等工具,让 AI 帮你写出又快又好的代码,更好的服务你的业务!1. 使用 Cursor、Antigravity、Trae 开发工具下载地址:Cursor、Antigravity、TREA 账号注册:Cursor 和 Trae 登录方式都超简单,会员的话直接去官网购买即可 至于 Antigravity,因为 Google 是禁止国内用户访问的,因此一定要能正常上网,邮箱账号必须纯正🇺🇸,但是我们有 闲鱼 之光,是可以尝试下的~ 2. 装好主流 MCP对于前端程序员,UI 这类低级工作,完全可以交给 Agent 去编写。比如:公司的设计师用的是figma,我们只需要在 cursor 中装上figma mcp,然后 figma 账号申请开发者权限,就能自由的让 AI 帮我们写好代码。亲测还原度 85%+ 3. 沉淀 Rules 和 Workflows我们现在已经可以通过开发工具让 AI 干活了,但如何更符合我们的编码习惯和设计思想?那就得给 AI 规范,也就是通过提示词让 AI 更乖的,干更对的活。 比如,Antigravity就有明确的让我们添加规则和工作流的入口,并且会引导我们如何写提示词,然后在提问的时候,引用对应的文件即可。 Rules(规则)和Workflows(工作流),沉淀 沉淀 再 沉淀!!! 4. !!!文档先行!!!AI 时代的编码,一定要做好设计,写好文档。 AI 虽然帮你干活,但是任务是你来安排的,你给出的任务要足够精准。 同时,你的编码思维才是代码能写好的核心,你必须把你的思维和想法,落成文档给到 AI 大模型。markdown 格式:注意 AI 需要理解 md 文档图文并茂:在编写文档的时候,时常需要画图,此时可以使用 md 语法来画图。这里我推荐mermaidchart,可以基于 md 语法进行可视化编辑。 写在最后当你有正常可以使用模型的账号后,其实这个账号不仅仅是在 IDE 可以使用,比如 Antigravity 的账号,跟 Gemini 是一致的,你也可以在大模型的官网登录进行图片、视频生成。在了解了大模型、MCP、工作流、AI 编程工具后,相信你对 AI 的应用又有了新的理解。我们一定要积极去尝试,国内国外的 AI 工具能用的多用,尽情的去拥抱 AI!AI 是生产力,毋庸置疑!——转载自:Karl_wei
从 “翻页书” 到 “魔术盒”:React 路由凭啥如此丝滑?
前言想象一下:你打开一个网站,从 “首页” 点到 “个人中心”,页面连个白屏都没有 —— 这不是魔法,是 单页应用(SPA) 的 “小心机”。而让 SPA 实现 “网址变、内容换” 的幕后大佬,就是今天要唠的 React Router。我今天以一个后台管理系统来全方位的拆解路由的细节~想要详细React Router资料可以在这里找到:reactrouter.com一、从 “多页翻书” 到 “单页变魔术”早年间的网站是 “多页应用”:点个链接跳转到新 HTML 文件,像翻书似的 “唰唰” 换页。但缺点很明显:加载慢、体验卡,就像翻一本 500 页的字典找个词,翻半天手都酸了。现在的 SPA 是 “单页魔术盒”:只有一个 HTML 文件,网址变了,只是把对应的 “组件” 塞进这个盒子里 —— 就像变魔术时从盒子里掏出不同道具,盒子本身根本不动。比如我写的这个后台管理系统:访问 http://localhost:5173/login → 塞进「登录组件」访问 http://localhost:5173/home → 塞进「首页组件」网址变,内容秒切,丝滑到像德芙广告~二、React Router:SPA 的 “导航指挥家”要实现这种 “秒切”,得请出react-router-dom这个 “指挥家”。它的核心成员有这些(结合我们的后台系统代码来看更爽):首先你需要安装好react-router:就在我开头给的网址里面就可以找到哈!1. BrowserRouter:给应用 “装个导航系统”它是路由的 “容器”,相当于给整个应用装了个 “导航大脑”(用的是 HTML5 的 History API,所以网址长得像正常网址)。看App.jsx的开头:
import { BrowserRouter, Routes, Route } from 'react-router-dom'
export default function App() {
return (
)
}
2. Routes + Route:给 “组件” 贴 “网址标签”Routes是 “路由出口”,Route是 “网址→组件” 的标签贴。比如我们的后台系统,给 「登录页」「首页」 贴标签:
{/* 404页面:匹配不到的网址都显示这个 */}
<Route path="*" element={
后台管理系统
)
}
点击算法进入/home/leetcode:Outlet就像电视屏幕,点 “课程” 就播 《课程频道》,点 “算法” 就切 《LeetCode 频道》。4. Link:SPA 的 “无痛跳转链接”传统的标签跳转是 “翻页”,Link是 “换内容”—— 点它网址变,但页面不刷新,就像遥控器换台。比如首页侧边栏的导航:
5. useNavigate:“编程式跳转” 的魔法棒有时候需要 “代码触发跳转”(比如登录成功后自动跳首页),这时候useNavigate就派上用场了。看我们的Login.jsx:
import { useNavigate } from 'react-router-dom'
export default function Login() {
const navigate = useNavigate() // 拿到跳转函数
const login = () => {
// 登录逻辑...
navigate('/home') // 登录成功,跳转到首页!
}
return (
)
}
点登录前:点登录后:点 “登录” 按钮,navigate('/home')一执行,网址直接切到首页,比外卖小哥送餐还快~三、总结:React Router 就是 SPA 的 “导航全家桶”把这些成员凑一起,我们的后台系统就活了:打开网站,/自动跳/login → 显示登录界面(带输入框和绿色登录按钮);点 “登录”,useNavigate跳/home → 显示首页(带侧边栏);点侧边栏 “课程”,Link跳/home/class → Outlet显示课程页面;输错网址,直接显示NOT FOUND → 404 页面。是不是感觉 React Router 像个 “全能导航员”?既管网址匹配,又管页面切换,还能代码跳转 —— 有了它,SPA 才能像 “魔术盒” 一样,变内容比变魔术还快!结语说到底,React Router 就是单页应用的 “流量控制器”,它用极简的配置和灵活的 API,让我们的后台管理系统实现了 “网址变、组件换” 的丝滑体验。从登录页到首页,从一级路由到二级路由,没有烦人的页面刷新,只有行云流水的内容切换。路由其实不难,需要多理解,掌握这些核心用法,你也能轻松搭建出结构清晰、体验流畅的 SPA 应用。下次再遇到路由相关的需求,不妨拿出这些 “导航法宝”,让你的项目像后台管理系统一样,在路由的世界里畅通无阻。现在就用我的例子敲代码吧!——转载自:风止何安啊
NOT FOUND
} /> 登录首页:点击登录(自动进入/home/class):像不像给每个组件发了张 “网址门票”?拿着/login门票,就能进登录页的门?机-会技术大厂,前端-后端-测试,新一线和一二线城市等地均有机-会,感兴趣可以试试。待遇和稳定性都不错~3. Outlet:二级路由的 “展示窗口”首页Home是个 “大容器”,里面要放class和leetcode这些 “子页面”——Outlet就是这个 “子页面展示窗口”。看Home.jsx的代码: import { Outlet, Link } from 'react-router-dom' export default function Home() { return (
单点登录:一次登录,全网通行
大家好,我是小悟。想象一下你去游乐园,买了一张通票(登录),然后就可以玩所有项目(访问各个系统),不用每个项目都重新买票(重新登录)。这就是单点登录(SSO)的精髓!SSO的日常比喻普通登录:像去不同商场,每个都要查会员卡单点登录:像微信扫码登录,一扫全搞定令牌:像游乐园手环,戴着就能证明你买过票下面用代码来实现这个"游乐园通票系统":代码实现:简易SSO系统
import java.util.*;
// 用户类 - 就是我们这些想玩项目的游客
class User {
private String username;
private String password;
public User(String username, String password) {
this.username = username;
this.password = password;
}
// getters 省略...
}
// 令牌类 - 游乐园手环
class Token {
private String tokenId;
private String username;
private Date expireTime;
public Token(String username) {
this.tokenId = UUID.randomUUID().toString();
this.username = username;
// 令牌1小时后过期 - 游乐园晚上要关门的!
this.expireTime = new Date(System.currentTimeMillis() + 3600 * 1000);
}
public boolean isValid() {
return new Date().before(expireTime);
}
// getters 省略...
}
// SSO认证中心 - 游乐园售票处
class SSOAuthCenter {
private Map[removed] validTokens = new HashMap[removed]();
private Map[removed] users = new HashMap[removed]();
public SSOAuthCenter() {
// 预先注册几个用户 - 办了年卡的游客
users.put("zhangsan", new User("zhangsan", "123456"));
users.put("lisi", new User("lisi", "abcdef"));
}
// 登录 - 买票入场
public String login(String username, String password) {
User user = users.get(username);
if (user != null && user.getPassword().equals(password)) {
Token token = new Token(username);
validTokens.put(token.getTokenId(), token);
System.out.println(username + " 登录成功!拿到游乐园手环:" + token.getTokenId());
return token.getTokenId();
}
System.out.println("用户名或密码错误!请重新买票!");
return null;
}
// 验证令牌 - 检查手环是否有效
public boolean validateToken(String tokenId) {
Token token = validTokens.get(tokenId);
if (token != null && token.isValid()) {
System.out.println("手环有效,欢迎继续玩耍!");
return true;
}
System.out.println("手环无效或已过期,请重新登录!");
validTokens.remove(tokenId); // 清理过期令牌
return false;
}
// 登出 - 离开游乐园
public void logout(String tokenId) {
validTokens.remove(tokenId);
System.out.println("已登出,欢迎下次再来玩!");
}
}
// 业务系统A - 过山车
class SystemA {
private SSOAuthCenter authCenter;
public SystemA(SSOAuthCenter authCenter) {
this.authCenter = authCenter;
}
public void accessSystem(String tokenId) {
System.out.println("=== 欢迎来到过山车 ===");
if (authCenter.validateToken(tokenId)) {
System.out.println("过山车启动!尖叫声在哪里!");
} else {
System.out.println("请先登录再玩过山车!");
}
}
}
// 业务系统B - 旋转木马
class SystemB {
private SSOAuthCenter authCenter;
public SystemB(SSOAuthCenter authCenter) {
this.authCenter = authCenter;
}
public void accessSystem(String tokenId) {
System.out.println("=== 欢迎来到旋转木马 ===");
if (authCenter.validateToken(tokenId)) {
System.out.println("木马转起来啦!找回童年记忆!");
} else {
System.out.println("请先登录再玩旋转木马!");
}
}
}
// 测试我们的SSO系统
public class SSODemo {
public static void main(String[] args) {
// 创建认证中心 - 游乐园大门
SSOAuthCenter authCenter = new SSOAuthCenter();
// 张三登录
String token = authCenter.login("zhangsan", "123456");
if (token != null) {
// 拿着同一个令牌玩不同项目
SystemA systemA = new SystemA(authCenter);
SystemB systemB = new SystemB(authCenter);
systemA.accessSystem(token); // 玩过山车
systemB.accessSystem(token); // 玩旋转木马
// 登出
authCenter.logout(token);
// 再尝试访问 - 应该被拒绝
systemA.accessSystem(token);
}
// 测试错误密码
authCenter.login("lisi", "wrongpassword");
}
}
运行结果示例:
zhangsan 登录成功!拿到游乐园手环:a1b2c3d4-e5f6-7890-abcd-ef1234567890
=== 欢迎来到过山车 ===
手环有效,欢迎继续玩耍!
过山车启动!尖叫声在哪里!
=== 欢迎来到旋转木马 ===
手环有效,欢迎继续玩耍!
木马转起来啦!找回童年记忆!
已登出,欢迎下次再来玩!
=== 欢迎来到过山车 ===
手环无效或已过期,请重新登录!
请先登录再玩过山车!
用户名或密码错误!请重新买票!
总结一下:单点登录就像:一次认证,处处通行 🎫不用重复输入密码 🔑安全又方便 👍好的SSO系统就像好的游乐园管理,既要让游客玩得开心,又要确保安全!机会技术大厂,前端-后端-测试,新一线和一二线城市等地均有机-会,感兴趣可以试试。待遇和稳定性都不错~您的一键三连,是我更新的最大动力,谢谢山水有相逢,来日皆可期,谢谢阅读,我们再会——转载自:悟空码字
为什么有些人边框不用border属性
1) border 会改变布局(占据空间)border 会参与盒模型,增加元素尺寸。例如,一个宽度 200px 的元素加上 border: 1px solid #000,实际宽度会变成:
200 + 1px(left) + 1px(right) = 202px
如果不想影响布局,就很麻烦。使用 box-shadow: 0 0 0 1px #000不会改变大小,看起来像 border,但不占空间。2) border 在高 DPI 设备上容易出现“模糊/不齐”特别是 0.5px border(发丝线),在某些浏览器上有锯齿、断线。transform: scale(0.5) 或伪元素能做更稳定的发丝线。3) border 圆角 + 发丝线 常出现不规则效果border + border-radius 在不同浏览器的渲染不一致,容易出现不均匀、颜色不一致的问题。用 outline / box-shadow 圆角更稳定。4) border 不适合做阴影/多层边框如果你需要两层边框:
双层边框用 border 很难做
而用:
box-shadow: 0 0 0 1px #333,0002px#999;
非常简单。5) border 和背景裁剪一起用时容易出 bug比如 background-clip、overflow: hidden 配合 border 会出现背景被挤压、不应该被裁剪却裁剪等问题。机会技术大厂,前端-后端-测试,新一线和一二线城市等地均有机-会,感兴趣可以试试。待遇和稳定性都不错~6) hover/active 等状态切换时会“跳动”因为 border 会改变元素大小。例子:
.btn { border: 0; }
.btn:hover { border: 1px solid #000; }
鼠标移上去会抖动,因为尺寸变大了。用 box-shadow 的话就不会跳。25/11/25更新,来自评论区大佬补充除了动态外有时候 overflow 也会导致原本刚刚好的布局不会删除滚动条,由于有了 border 1px 导致刚好出现滚动条但其实根本滚不了。总结边框可以分别使用border、outline、box-shadow三种方式去实现,其中outline、box-shadow不会像border一样占据空间。而box-shadow可以用来解决两个元素相邻时边框变宽的问题。不使用border并不是因为它不好,而是因为outline和box-shadow的兼容性和灵活性相对border会更好一点。——转载自:爆浆麻花
这6个网站一旦知道就离不开了
作为一名资深互联网居民和生产力工具爱好者,我的收藏夹里确实有那么几个网站,它们不是那种偶尔用一下的工具,而是已经深度融入我的工作和生活流,堪称数字部件。ServBay这绝对是开发者,尤其是需要和多种语言、多种环境打交道的全栈开发者的福音。它的核心价值在于集成与简化。程序员配置本地环境,往往需要自行处理各种软件的安装、版本冲突和互相之间的配置。比如用 Homebrew 管理服务,容易把系统环境搞乱;用 Docker,又有一定的学习和资源开销。ServBay 把这个过程产品化了。它提供了一个统一的管理界面,就跟点外卖一样,哪里需要点哪里,快速启动和管理多种语言环境(如 Rust, Node.js, Python, Go, PHP等)和数据库(如 MySQL, Redis)。它的几个特点很实用:一键部署,告别繁琐:支持 Python, Go, Java, Rust, Ruby, Node.js 等主流语言,点几下鼠标,开发环境就好了。版本隔离,无痛切换:可以同时运行 Python2.7, 3.8,3.11,或者 Node.js v16, v18, v20。项目之间互不干扰,这对需要维护旧项目或测试新特性的开发者来说很方便。数据库全家桶:常用的 MySQL, PostgreSQL, MariaDB,还有 Redis, MongoDB 这种 NoSQL 数据库,都能多实例同时运行,管理起来非常方便。AI 部署 & 内网穿透:这两点是它的杀手锏。一键部署 Llama, Qwen、DeepSeek 等本地 AI 模型,还能把本地服务通过内网穿透分享给同事或用于微信开发调试,直接打通了从开发到演示的最后一步。ServBay = 极高的效率 + 极大的自由度 + 极低的维护成本,为我节省了大量在环境配置上耗费的时间。坑位技术大厂,前端-后端-测试,新一线和一二线城市等地均有坑位,感兴趣可以试试。待遇和稳定性都不错~GitHub这个就不用多说了,全球最大同性交友网站(不是)。但如果你只把它当成一个存代码的地方,那就太小看它了。它的价值体现在方方面面。职业名片:一个维护良好的 GitHub 账号,是你技术热情和能力的最好证明。知识索引:除了代码,上面还有大量的学习资料、Awesome Lists、教程和笔记。遇到新技术,先去 GitHub 搜索,通常能找到高质量的入门资源。协作标准:它定义的 Issue、Pull Request 等协作流程,已经成为现代软件开发的事实标准。免费的午餐:GitHub Actions 提供了强大的 CI/CD 能力,GitHub Pages 可以免费托管静态网站。它不仅是工具,更是一个生态、一个社区。无论你是编程新手还是资深大佬,都离不开它。Dynamic Wallpaper Club这个网站满足的是桌面个性化和视觉体验的需求。相较于静态壁纸,它提供的动态壁纸能根据一天的时间变化(HEIC 格式),让桌面背景从日出、正午到日落、深夜,呈现不同的光影效果。这种细微的环境变化,能给单调的电脑使用过程增加一些动态感和舒适度。提升幸福感的小确幸网站,用最低的成本,换来开心一整天。123apps这是一个在线音视频及文档处理工具集。工作中经常会遇到一些临时的、轻量的文件处理任务,比如:裁剪一小段音频、合并几个 PDF 文件、转换一个视频的格式。我个人认为为这些一次性的任务去下载安装一个专门的软件,费时费力,还可能附带广告或捆绑安装。123apps 在一个网站内集合了数十种这类小工具,覆盖了视频、音频、PDF、图像转换等常见场景。它的优点是:功能聚合:一个网站解决多种问题,无需到处寻找。即用即走:基于浏览器,无需安装,处理完下载即可。界面简洁:操作直观,没有太多干扰信息。收藏这一个网站,相当于安装了几十个小软件。随用随开,用完即走,体验一把当渣男的快乐(bushi),是解决日常杂事的终极利器。Perplexity AI这是一个带引用来源的答案引擎。不同于ChatGPT,Perplexity AI 提供了另一种更聚焦于搜索和回答的体验。它不是一个纯粹的聊天机器人,而是一个自带引用来源、会深度追问的答案引擎。它最大的特点是信息溯源。对比 搜索引擎:传统搜索返回的是链接列表,需要用户自己去逐个打开、筛选、整合信息。Perplexity 则直接生成一段总结好的回答,并在回答下方清晰地列出信息来源的链接。对比通用模型:通用模型在回答事实性问题时,有时会捏造信息。而 Perplexity 的回答基于其索引的公开信息,并提供了出处,用户可以方便地进行事实核查,这在需要严谨信息源的场景下很有用。我现在查资料、写报告、做技术调研,首选就是 Perplexity。它的 Pro 模式还能上传文件进行分析,或者切换到 Claude-3、GPT-4 等更强大的模型。Excalidraw一个极简的在线白板工具。当需要画流程图、架构草图或进行思维整理时,我们有很多选择,比如 Miro、FigJam 或是专业的绘图软件。但这些工具往往功能复杂,启动也比较慢。Excalidraw 的特点在于它的手绘风格和简洁性。手绘感:它画出的图形带有手绘的质感,看起来不那么正式。我很喜欢这种风格,看起来像草稿,但能让我更专注于思考和创意本身简单高效:打开网站就能开始画,没有账户注册的强制要求。支持实时协作,通过链接分享给他人即可共同编辑。开源与扩展:社区贡献了丰富的图形库,可以方便地拖拽使用,比如各种云服务的图标、UI 组件等。我用它来梳理逻辑、设计系统、给同事讲方案,甚至做会议纪要。它的轻量和高效,是任何重型工具都无法比拟的。以上是我个人常用的一些网站,希望能帮到你。——转载自:该用户已不存在
听说前端又死了?
这几天刷 X、刷 Reddit、刷国内技术社区,只要你稍微点开热榜,就会被同一句话精准爆头:“Gemini 3 真的把前端扬了,这次是骨灰级别的扬。”“一个 prompt 直接出 3D 体素编辑器/视频剪辑软件/电影级登陆页,前端彻底没活了。”“我用 Gemini 3 三分钟写了个比 CapCut 还丝滑的网页版剪辑器,程序员可以回家抱孩子了。”配图永远是那种高潮到发光的 4K 60fps 演示视频:一个 prompt → 进度条走完 → 完美交互应用跃然屏幕,点赞几万,转发狂欢,评论区清一色“前端已死”“我失业了”“时代抛弃你连招呼都不打”。兄弟们,我太熟这个剧本了。前端这几年,平均每 9 个月就被公开处决一次。我们的died清单(2025 年 11 月实时更新版)2010:WordPress 模板 → 前端再卒2015:Webflow/No-Code → 前端三卒2023:GPT-4 一个 prompt 贪吃蛇 → 前端四卒2024:Claude 3.5 Sonnet 卷 UI → 前端五卒2025 年 11 月 18-21 日:Gemini 3 连发三天 demo(体素玩具箱、网页版 CapCut、赛博风登陆页、AI 视频生成工具……)→ 前端这三天死了 114514 次,目前骨灰已被 X 用户扬到太平洋对岸去了这几天最经典的几个“葬礼现场”那个 3D Voxel Toy Box:一个 prompt 出来,实时绘画、破坏、导出 glTF,丝滑得像 Unity 官方 demo。评论区直接高呼“前端工程师可以集体转行了”。那个网页版视频编辑器:拖拽时间线、AI 自动生成视频、TTS、字幕、转场全有。作者在 X 上发帖:“从 0 到 1 只用了 1.5 小时,感谢 Gemini 3。” 底下回复“前端已死”“我哭死”“程序员末日”。那个电影级登陆页:暗黑赛博风、3D 粒子背景、鼠标视差、滚动触发动画,完美响应式,连 iPad 横竖屏切换都丝滑。作者淡定地说:“就一个 prompt,改了三次描述。”然后这几天,时间线彻底沦陷:“以后公司只需要一个会写 prompt 的产品经理就行了。”“前端岗位预计 2026 年消失。”“建议所有大厂立刻裁掉 90% 的前端。”Gemini 3 确实牛逼,这几天 X 上的各种实测提示词都试了下,生成的东西确实牛逼,我把公司首页丢给它,让它“重构得更现代一点”。 20秒后,它给我吐了一个电影级粒子背景 + 3D 卡片 + 滚动触发动画的版本,Lighthouse 直接 100/100/100/100。。。奶奶的这比好多优秀前端写的不知道好多少倍。。但问题来了:你一个 prompt 做出来的完美 demo,明天上线的时候,甲方会告诉你:“背景要那种看不见的黑色,像深夜emo但又有希望的感觉。”“这个动画再慢 0.2 秒,用户会觉得我们公司很稳重。”你问 Gemini 3 怎么改,它会非常认真地给你生成 17 种优雅方案。坑位技术大厂,前端-后端-测试,新一线和一二线城市等地均有坑位,感兴趣可以试试。待遇和稳定性都不错~然后你上线后发现:甲方不满意,这里那里怎么样怎么样。甲方的浏览器 CSS 不兼容而导致的样式混乱。新的样式和项目整体样式冲突。 这时候你还得自己上手,一个个去修、去 hack、去加一堆 !important 和 any。恭喜你,你又回到了最熟悉的前端生活。2026 年的新工种:AI 擦屁股工程师这几天网上最热门的梗就是“前端已死”,但是只要甲方存在,前端同仁就有活路。所以,面对这几天铺天盖地的“Gemini 3 把前端扬了”言论,请各位前端同仁保持冷静。我们前端的真正核心竞争力,从来不是写代码。只要甲方会半夜改需求,只要还有安卓碎片化,只要 CSS 还存在,前端就死不了。前端已死?不,前端只是又被扬了一次骨灰,然后继续负伤加班,顺便帮 Gemini 3 擦屁股。前端万岁!(下次 GPT-6 出来再继续死)首发地址:Gemini 3 发布了,恭喜前端第 10086 次“由于不可抗力”宣告——转载自:suke
接口开发,咱得整得“优雅”点
一、为什么要“优雅”?产品一句话: “凡哥,接口明天上线,支持 10w 并发,数据脱敏,不能丢单,不能重复,还要安全。” 优雅不是装,是为了让自己少加班、少背锅、少掉发。 今天晓凡就把压箱底的东西掏出来,手把手带你撸一套能扛生产的模板。为方便阅读,晓凡以Java代码为例给出“核心代码 + 使用姿势”,全部亲测可直接使用。二、项目骨架(Spring Boot 3.x)
demo-api
├── src/main/java/com/example/demo
│ ├── config // 配置:限流、加解密、日志等
│ ├── annotation // 自定义注解(幂等、日志、脱敏)
│ ├── aspect // 切面统一干活
│ ├── interceptor // 拦截器(签名、白名单)
│ ├── common // 统一返回、异常、常量
│ ├── controller // 对外暴露
│ ├── service
│ └── DemoApplication.java
└── pom.xml
三、 签名(防篡改)对外提供的接口要做签名认证,认证不通过的请求不允许访问接口、提供服务思路 “时间戳 + 随机串 + 业务参数”排好序,最后 APP_SECRET 拼后面,SHA256 一下。 前后端、第三方都统一,拒绝吵架。工具类
public class SignUtil {
/**
* 生成签名
* @param map 除 sign 外的所有参数
* @param secret 分配给你的私钥
*/
public static String sign(Map
配置
spring:
application:
name: demo-api
sentinel:
transport:
dashboard: localhost:8080
使用姿势
@GetMapping("/order/{id}")
@SentinelResource(value = "getOrder",
blockHandler = "getOrderBlock")
public Result
配置
knife4j:
enable: true
setting:
language: zh_cn
启动后访问 http://localhost:8080/doc.html 支持在线调试、导出 PDF、Word。十七、小结接口开发就像炒菜:签名、加密是“食材保鲜”限流、幂等是“火候掌控”日志、文档是“摆盘拍照”每道工序做到位,才能端到桌上“色香味”俱全。 上面 13 段核心代码,直接粘过去就能跑,跑通后再按业务微调,基本能扛 90% 的生产场景。 祝你在领导问起接口怎么样了?的时候,可以淡淡来一句: “接口已经准备好了,压测报告发群里了。”——转载自:程序员晓凡
忍了一年多,我终于对i18n下手了
前言大家好,我是奈德丽。过去一年,我主要参与国际机票业务的开发工作,因此每天都要和多语言(i18n)打交道。熟悉我的朋友都知道,我这个人比较“惜力”(并不是,实际上只是忍不下去了),对于重复笨拙的工作非常抵触,于是,我开始思考如何优化团队的多语言管理模式。痛点背景先说说我们在机票项目中遇到的困境。目前机票项目分为 H5 和 PC 两端,团队在维护多语言时主要通过在线 Excel进行管理:一个 Excel 文件,H5 和 PC 各自占一个 sheet 页;每次更新语言,需要先导出 Excel,然后手动跑脚本生成语言文件,再拷贝到项目中。听起来还算凑合,但随着项目规模的扩大,问题逐渐显现:Key 命名混乱有的首字母大写,有的小驼峰、大驼峰混用;没有统一规则,难以模块化管理。不支持模块化目前已有数千条 key;查找、修改、维护都非常痛苦。更新流程繁琐需要手动进入脚本目录,用 node 跑脚本;生成后再手动复制到项目中。下面是一个实际的 Excel 片段,可以感受一下当时的混乱程度:用原node脚本生成的语言文件如图在这样的场景下,每次迭代多语言文件更新都像噩梦一样。 尤其是我们很多翻译是通过AI 机翻生成,后续频繁修改的成本极高。然而,机票项目的代码量太大、历史包袱太重,短期内几乎不可能彻底改造。新项目,新机会机票项目虽然不能动,但在我们启动酒店业务新项目时,我决定不能再重蹈覆辙。 因此,在酒店项目中,我从零搭建了这套更高效的 i18n 管理方案。目标很简单:统一 key 规则,支持模块化,模块与内容间用.隔开,内容之间用下划线隔开;自动化生成多语言 JSON 文件,集成到项目内,不再需要查找转化脚本的位置;一条命令搞定更新,不需要手动拷贝。于是,我在项目中新增了一个 scripts 目录,并编写了一个 excel-to-json.js 脚本。 在 package.json 中添加如下命令:
{
"scripts": {
"i18n:excel-to-json": "node scripts/excel-to-json.js"
}
}
以后,只需要运行下面一行命令,就能完成所有工作:
pnpm i18n:excel-to-json
再也不用手动寻找脚本路径,也不用手动复制粘贴,效率直接起飞 🚀。坑位技术大厂,前端-后端-测试,新一线和一二线城市等地均有坑位,感兴趣可以试试。待遇和稳定性都不错~脚本实现核心逻辑就是: 从 Excel 读取内容 → 转换为 JSON → 输出到项目 i18n 目录。完整代码如下:
import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import XLSX from 'xlsx'
/**
* 语言映射表:Excel 表头 -> 标准语言码
*/
const languageMap = {
'English': 'en',
'简中': 'zh-CN',
'Chinese (Traditional)': 'zh-TW',
'Korean': 'ko',
'Spanish': 'es',
'German Edited': 'de',
'Italian': 'it',
'Norwegian': 'no',
'French': 'fr',
'Arabic': 'ar',
'Thailandese': 'th',
'Malay': 'ms',
}
// 读取 Excel 文件
function readExcel(filePath) {
if (!fs.existsSync(filePath)) {
throw new Error(`❌ Excel 文件未找到: ${filePath}`)
}
const workbook = XLSX.readFile(filePath)
const sheet = workbook.Sheets[workbook.SheetNames[0]]
return XLSX.utils.sheet_to_json(sheet)
}
/**
* 清空输出目录
*/
function clearOutputDir(dirPath) {
if (fs.existsSync(dirPath)) {
fs.readdirSync(dirPath).forEach(file => fs.unlinkSync(path.join(dirPath, file)))
console.log(`🧹 已清空目录: ${dirPath}`)
} else {
fs.mkdirSync(dirPath, { recursive: true })
console.log(`📂 创建目录: ${dirPath}`)
}
}
/**
* 生成 JSON 文件
*/
function generateLocales(rows, outputDir) {
const locales = {}
rows.forEach(row => {
const key = row.Key
if (!key) return
// 遍历语言列
Object.entries(languageMap).forEach(([columnName, langCode]) => {
if (!locales[langCode]) locales[langCode] = {}
const value = row[columnName] || ''
const keys = key.split('.')
let current = locales[langCode]
keys.forEach((k, idx) => {
if (idx === keys.length - 1) {
current[k] = value
} else {
current[k] = current[k] || {}
current = current[k]
}
})
})
})
// 输出文件
Object.entries(locales).forEach(([lang, data]) => {
const filePath = path.join(outputDir, `${lang}.json`)
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8')
console.log(`✅ 生成文件: ${filePath}`)
})
}
/**
* 检测缺失翻译
*/
function detectMissingTranslations(rows) {
const missing = []
rows.forEach(row => {
const key = row.Key
if (!key) return
Object.entries(languageMap).forEach(([columnName, langCode]) => {
const value = row[columnName]
if (!value?.trim()) {
missing.push({ key, lang: langCode })
}
})
})
return missing
}
function logMissingTranslations(missingList) {
if (missingList.length === 0) {
console.log('\n🎉 所有 key 的翻译完整!')
return
}
console.warn('\n⚠️ 以下 key 缺少翻译:')
missingList.forEach(item => {
console.warn(` - key: "${item.key}" 缺少语言: ${item.lang}`)
})
}
function main() {
const desktopPath = path.join(os.homedir(), 'Desktop', 'hotel多语言.xlsx')
const outputDir = path.resolve('src/i18n/locales')
const rows = readExcel(desktopPath)
clearOutputDir(outputDir)
generateLocales(rows, outputDir)
logMissingTranslations(detectMissingTranslations(rows))
}
main()
成果展示这是在线语言原文档这是生成后的多语言文件和内容 现在的工作流大幅简化:操作旧流程新流程运行脚本手动找脚本路径pnpm i18n:excel-to-json文件生成位置生成后手动拷贝自动输出到项目检测缺失翻译无自动提示key 命名管理无统一规则模块化、规范化这套机制目前在酒店项目中运行良好,团队反馈也很积极。总结这次改造让我最大的感触是:旧项目难以推翻重来,但新项目一定要趁早做好架构设计。通过这次优化,我们不仅解决了多语言维护的痛点,还提升了团队整体开发效率。 而这套方案在未来如果机票项目有机会重构,也可以直接平滑迁移过去。——转载自:奈德丽
别再死磕框架了!你的技术路线图该更新了
先说结论:前端不会凉,但“只会几个框架 API”的前端,确实越来越难混 这两年“前端要凉了”“全栈替代前端”的声音此起彼伏,本质是门槛重新洗牌:简单 CRUD、纯样式开发被低代码、模板代码和 AI 模型快速蚕食;复杂业务、工程体系、跨端体验、AI 能力集成,反而需要更强的前端工程师去撑住。如果你对“前端的尽头是跑路转管理”已经开始迷茫,那这篇就是给你看的:别再死磕框架版本号,该更新的是你的技术路线图。一、先搞清楚:2025 的前端到底在变什么?框架红海:从“会用”到“用得值”React、Vue、Svelte、Solid、Qwik、Next、Nuxt……Meta Framework 一大堆,远远超过岗位需求。 现在企业选型更关注:生态成熟度(如 Next.js 的 SSR/SSG 能力)框架在应用生命周期中的角色(渲染策略、数据流转、SEO、部署)趋势:框架 Meta 化(Next.js、Nuxt)将路由、数据获取、缓存策略整体纳入规范;约定优于配置,不再是“一个前端库”,而是“一套完整解决方案”。以前是“你会 Vue/React 就能干活”,现在是“你要理解框架在整个应用中的角色”。工具有 AI,开发方式也在变AI 工具(如 Cursor、GitHub Copilot X)可以显著提速,甚至替代重复劳动。 真正拉开差距的变成了:你能给 AI 写出清晰、可实现的需求描述(Prompt);你能判断 AI 生成代码的质量、潜在风险、性能问题;你能基于生成结果做出合理抽象和重构。AI 不是来抢饭碗,而是逼你从“码农”进化成“架构和决策的人”。业务侧:前端不再是“画界面”,而是“做体验 + 做增长”B 端产品:交互工程师 + 低代码拼装师 + 复杂表单处理专家;C 端产品:与产品运营深度捆绑,懂 A/B 测试、埋点、Funnel 分析、广告投放链路;跨平台:Web + 小程序 + App(RN/Flutter/WebView)混合形态成为常态。那些还在喊“切图仔优化 padding”的岗位确实在消失,但对“懂业务、有数据意识、能搭全链路体验”的前端需求更高。坑位技术大厂,前端-后端-测试,新一线和一二线城市等地均有坑位,感兴趣可以试试。待遇和稳定性都不错~二、别再死磕框架 API:2025 的前端核心能力长什么样?基石能力:Web 原生三件套,得真的吃透重点不是“会用”,而是理解底层原理:JS:事件循环、原型链、Promise 执行模型、ESM 模块化;浏览器:渲染流程(DOM/CSSOM/布局/绘制/合成)、HTTP/2/3、安全防护(XSS/CSRF)。这块扎实了,你在任何框架下都不会慌,也更能看懂“框架为什么这么设计”。工程能力:从“会用脚手架”到“能看懂和调整工程栈”Vite、Rspack、Turbopack 等工具让工程构建从“黑魔法”变成“可组合拼装件”。 你需要:看懂项目的构建配置(Vite/Webpack/Rspack 任意一种);理解打包拆分、动态加载、CI/CD 流程;能排查构建问题(路径解析、依赖冲突)。如果你在团队里能主动做这些事,别人对你的“级别判断”会明显不一样。跨端和运行时:不只会“写 Web 页”2025 年前端视角的关键方向:小程序/多端框架(Taro、Uni-app);混合方案(RN/Flutter/WebView 通信机制);桌面端(Electron、Tauri)。建议:至少深耕一个“跨端主战场”(如 Web + 小程序 或 Web + Flutter)。数据和状态:从“会用 Vuex/Redux”到“能设计状态模型”现代前端复杂度 70% 在“数据和状态管理”。 进阶点在于:设计合理的数据模型(本地 UI 状态 vs 服务端真相);学会用 Query 库、State Machine 解耦状态与视图。当你能把“状态设计清楚”,你在复杂业务团队里会非常吃香。性能、稳定性、可观测性:高级前端的硬指标你需要系统性回答问题,而不是“瞎猜”:性能优化:首屏加载(资源拆分、CDN)、运行时优化(减少重排、虚拟列表);稳定性:错误采集、日志上报、灰度发布;工具:Lighthouse、Web Vitals、Session Replay。这块做得好的人往往是技术骨干,且很难被低代码或 AI 直接替代。AI 时代的前端:不是“写 AI”,而是“让 AI 真正跑进产品”你需要驾驭:基础能力:调用 AI 平台 API(流式返回处理、增量渲染);产品思维:哪些场景适合 AI(智能搜索、文档问答);如何做权限控制、错误兜底。三、路线图别再按“框架学习顺序”排了,按角色来选初中级:从“会用”到“能独立负责一个功能”目标:独立完成中等复杂度模块(登录、权限、表单、列表分页)。建议路线:夯实 JS + 浏览器基础;选择 React/Vue + Next/Nuxt 做完整项目;搭建 eslint + prettier + git hooks 的开发习惯。进阶:从“功能前端”到“工程前端 + 业务前端”目标:优化项目、推进基础设施、给后端/产品提技术方案。建议路线:深入构建工具(Webpack/Vite);主导一次性能优化或埋点方案;引入 AI 能力(如智能搜索、工单回复建议)。高级/资深:从“高级前端”到“前端技术负责人”目标:设计技术体系、推动长期价值。建议路线:明确团队技术栈(框架、状态管理、打包策略);主导跨部门项目、建立知识分享机制;评估 AI/低代码/新框架的引入价值。四、2025 年不要再犯的几个错误只跟着热点学框架,不做项目和抽象选一个主战场 + 一个备胎(React+Next.js,Vue+Nuxt.js),用它们做 2~3 个完整项目。完全忽略业务,沉迷写“优雅代码”把重构和业务迭代绑一起,而不是搞“纯技术重构”。对 AI 持敌视和逃避态度把重复劳动交给 AI,把时间投到架构设计、业务抽象上。把“管理”当成唯一出路做前端架构、性能优化平台、低代码平台的技术专家,薪资和自由度不输管理岗。五、一个现实点的建议:给自己的 2025 做个“年度规划”Q1:选定主技术栈(React+Next 或 Vue+Nuxt);做一个完整小项目(登录、权限、列表/详情、SSR、部署)。Q2:深入工程化方向(优化打包体积、搭建监控埋点系统)。Q3:选一个业务场景引入 AI 或配置化能力(如智能搜索、低代码表单)。Q4:输出和沉淀(写 3~5 篇技术文章、踩坑复盘)。最后:别问前端凉没凉,先问问自己“是不是还停在 2018 年的玩法”如果你还把“熟练掌握 Vue/React”当成简历亮点,那确实会焦虑;但如果你能说清楚:在复杂项目里主导过哪些工程优化;如何把业务抽象成可复用的组件/平台;如何在产品里融入 AI/多端/数据驱动; 那么,在 2025 年的前端市场,你不仅不会“凉”,反而会成为别人眼中的“稀缺”。别再死磕框架了,更新你的技术路线图,从“写页面的人”变成“打造体验和平台的人”。这才是 2025 年前端真正的进化方向。——转载自:纯爱掌门人