前端老铁必看:Mock劫持Ajax翻车实录与避坑指南

前端老铁必看:Mock劫持Ajax翻车实录与避坑指南
在这里插入图片描述


前端老铁必看:Mock劫持Ajax翻车实录与避坑指南

前端老铁必看:Mock劫持Ajax翻车实录与避坑指南

说实话,我第一次用Mock.js的时候,整个人是懵的。那时候刚进公司,后端大哥说接口要"下周一定"——懂的都懂,这个"下周"可能是下辈子。产品那边又天天催页面效果,我急得像热锅上的蚂蚁,同事扔过来一个Mock.js的链接,说:“先拿这个顶着。”

我当时就纳闷了,这玩意儿咋就能让我在没有后端的情况下把页面跑起来?它凭啥能"骗"过浏览器?今天咱们就把这层窗户纸捅破,聊聊Mock是怎么在浏览器发请求的半道上截胡的,以及我这些年踩过的那些让人想砸键盘的坑。

咱就是说,这玩意儿到底咋忽悠浏览器的

先别急着抄代码,咱得唠唠心里话。你以为Mock.mock是啥神仙魔法?其实它就是个大骗子,专门在浏览器发请求的半道上截胡。咱们得把urlrtype还有template这三个参数掰开了揉碎了讲,就像给死党解释为啥你昨晚没回消息一样,得通透。

别整那些虚头巴脑的定义,直接说人话:它怎么把原本要发给后端的请求,硬生生给"狸猫换太子"了。

核心原理:浏览器里的"替身演员"

咱们平时写前端,发请求不外乎就那几种方式:最老派的XMLHttpRequest、稍微现代点的fetch、或者是基于这俩封装的axiosjQuery.ajax这些。Mock.js的核心骚操作就是:在页面加载的时候,偷偷把这俩原生API给"掉包"了

想象一下,你本来要去银行取钱(发请求),Mock.js相当于在门口雇了个群演,穿着银行保安的衣服。你走过去说"我要取500块",保安一看:"哦,是Mock名单上的人,不用进银行了,我这儿直接给你500块假钞(假数据)。"你要是去别的银行(真实接口),保安一看名单上没有,才会放你进去。

这个"掉包"的过程大概长这样:

// 这是Mock.js内部大概会干的事情,简化版示意const originalXHR = window.XMLHttpRequest;functionMockXHR(){// 先假装自己是个正常的XHRconst xhr =neworiginalXHR();// 但是open方法被我劫持了const originalOpen = xhr.open; xhr.open=function(method, url, async, user, password){// 这里开始使坏:看看这个url是不是我要拦截的if(Mock._matchUrl(url, method)){// 匹配上了!标记一下,待会儿send的时候直接返回假数据this._isMocked =true;this._mockTemplate = Mock._getTemplate(url, method);}else{this._isMocked =false;}// 不管是不是Mock,先正常调用,免得露馅returnoriginalOpen.apply(this, arguments);};// send方法也要劫持const originalSend = xhr.send; xhr.send=function(body){if(this._isMocked){// 开始表演:假装网络延迟setTimeout(()=>{// 生成假数据const mockData = Mock._generateData(this._mockTemplate);// 伪造响应 Object.defineProperty(this,'responseText',{value:JSON.stringify(mockData),writable:false}); Object.defineProperty(this,'status',{value:200,writable:false}); Object.defineProperty(this,'readyState',{value:4,writable:false});// 触发onload事件,让前端代码以为真的收到响应了if(this.onload){this.onload();}if(this.onreadystatechange){this.onreadystatechange();}}, Mock._randomLatency());// 模拟个随机延迟,更像真的return;// 真正的send?不存在的,直接return}// 不是Mock的请求,正常发出去returnoriginalSend.apply(this, arguments);};return xhr;}// 把原生的给换了 window.XMLHttpRequest = MockXHR;

看到没?就是这么简单粗暴。Mock.js在页面刚加载的时候,就把window.XMLHttpRequest这个构造函数给重写了。以后你代码里不管是new XMLHttpRequest()还是axios内部创建的XHR对象,实际上都是Mock.js包装过的"替身"。

那fetch呢?它也跑不掉

现在新项目都用fetch了,Mock.js也没闲着。对fetch的劫持原理差不多,也是把window.fetch给掉包:

// Mock.js对fetch的劫持思路,伪代码示意const originalFetch = window.fetch; window.fetch=function(url, options ={}){const method =(options.method ||'GET').toUpperCase();// 检查要不要拦截if(Mock._matchUrl(url, method)){// 要拦截,返回一个假装是Promise的玩意returnnewPromise((resolve)=>{setTimeout(()=>{// 构造一个假的Response对象const mockData = Mock._generateData(Mock._getTemplate(url, method));const mockResponse =newResponse(JSON.stringify(mockData),{status:200,headers:{'Content-Type':'application/json'}});resolve(mockResponse);}, Mock._randomLatency());});}// 不拦截,走正常的fetchreturnoriginalFetch.apply(this, arguments);};

所以你看,Mock.js本质上就是个"中间人攻击"的合法版本,只不过攻击的是你自己的浏览器。它坐在浏览器和真实网络之间,看着你要发请求,先查查自己的小本本(你配置的路由规则),要是匹配上了,直接给你编个假响应,连网都不用出。

url、rtype、template这三个参数到底咋配合的

很多人用Mock.js老翻车,就是没搞懂这三个参数是怎么匹配的。我当初也是,写了Mock.mock('/api/user', 'get', {...}),结果请求发了半天没反应,急得我差点给电脑磕头。

url参数:这个支持好几种写法,灵活得很,但也容易搞混。

// 1. 字符串完全匹配 - 最严格,但最不实用 Mock.mock('/api/user/list',{'data|10':[{name:'@cname'}]});// 只有请求地址完全等于/api/user/list才匹配// 2. 带正则的字符串 - 这个最常用 Mock.mock(/\/api\/user\/\d+/,{'id|+1':1,'name':'@cname'});// 匹配 /api/user/1, /api/user/999 这种带动态ID的// 3. 真正的RegExp对象 - 最灵活 Mock.mock(newRegExp('^/api/(user|order)/list$'),{'list|5-10':[]});// 匹配 /api/user/list 或 /api/order/list// 4. 通配符模式 - Mock.js自己实现的简化正则 Mock.mock('/api/*',{'msg':'通配符匹配所有/api/开头的'});// 匹配 /api/anything

rtype参数:请求类型,GET、POST、PUT、DELETE这些。注意如果不写这个参数,默认匹配所有类型,但有时候这就是坑的来源。

// 错误示范:你以为只拦截GET,其实POST也拦 Mock.mock('/api/login',{'token':'@guid'});// 结果POST /api/login的时候,也被拦截了,返回了GET该有的数据结构// 正确姿势:明确指定类型 Mock.mock('/api/login','post',{'token':'@guid','expires':7200}); Mock.mock('/api/login','get',{'msg':'这是GET请求返回的,和POST不一样'});

template参数:这就是你要返回的假数据模板。Mock.js的模板语法是它的精髓,也是让人又爱又恨的地方。

// 基础模板:属性名后面跟个竖线和规则 Mock.mock('/api/goods',{// 生成10-20条数据'list|10-20':[{'id|+1':1000,// 自增ID,从1000开始'name':'@ctitle(5,10)',// 随机中文标题,5-10个字'price|100-999.2':1,// 100-999的随机数,保留2位小数'stock|0-100':1,// 0-100的整数'isNew|1-2':true,// 1/2概率是true,剩下是false'tags|1-3':['@word'],// 随机选1-3个标签'image':'@image(200x200)',// 随机图片地址'createTime':'@datetime'// 随机日期时间}],'total':function(){// 函数可以访问this,this指向当前数据对象returnthis.list.length;}});

看到没?那个'list|10-20'的语法,竖线前面是属性名,后面是规则。10-20表示生成10到20条数据。+1表示自增,1-2表示布尔值概率,100-999.2表示数字范围和精度。

匹配优先级:谁说了算

这里有个大坑:Mock.js的匹配是有优先级的,后写的会覆盖先写的。而且它的匹配是从上往下找,找到第一个匹配的就停。

// 先写个通用的 Mock.mock(/\/api\/.*/,'get',{msg:'通用匹配'});// 后写个具体的 Mock.mock('/api/user','get',{msg:'用户专用'});// 当请求 GET /api/user 的时候,返回的是"用户专用"// 因为后写的把先写的给"顶"掉了,或者说匹配的时候先匹配到后面的

这个设计有时候很烦人,比如你写了个*通配符拦截所有,后面再写具体接口就不生效了。所以建议把具体的规则写在前面,通用的写在后面,或者干脆别用通配符,都用正则精确匹配。

扒开源码看底裤,拦截逻辑其实是这么玩的

这部分咱不装大神,就聊聊我当初看源码时那种"就这?"的感觉。核心其实就是重写了XMLHttpRequest或者fetch,相当于在门口安了个保安。每次你要出门(发请求),保安先看看你去的地址(url)对不对,要是匹配上了,直接把你拦下来,塞给你一包假东西(template)。

源码级别的URL匹配骚操作

Mock.js的URL匹配不是简单的字符串比较,它内部实现了一个match函数,支持字符串、正则、通配符混用。咱们扒开看看:

// Mock.js内部的路由匹配逻辑,简化版 Mock._matchUrl=function(expected, actual, type){// expected是你在Mock.mock()里写的url参数// actual是真实请求的地址// 1. 如果是字符串,先转成正则if(typeof expected ==='string'){// 把通配符*转成正则的.*// 把?转成.const regStr = expected .replace(/\*/g,'.*').replace(/\?/g,'.'); expected =newRegExp('^'+ regStr +'$');}// 2. 现在expected一定是正则了,测试匹配const urlMatched = expected.test(actual);// 3. 检查请求类型if(!type){// 如果Mock.mock()没指定type,只匹配URLreturn urlMatched;}// 4. 检查rtype是否匹配const typeMatched =(Mock._rtype === type.toLowerCase());return urlMatched && typeMatched;};

看到那个通配符转换了吗?Mock.mock('/api/*')实际上被转换成了/^\/api\/.*$/这个正则。所以你写的通配符其实是正则的简化写法,理解这一点对排查匹配问题很有帮助。

请求类型(rtype)的判断陷阱

Mock.js在拦截的时候,需要知道当前请求是GET还是POST。但它判断的方式有时候会让你意想不到:

// 当你在代码里调用: axios.get('/api/user');// axios内部会创建一个XHR对象,调用open('GET', '/api/user')// Mock.js劫持的open方法会收到method='GET'// 但如果你用fetch:fetch('/api/user',{method:'POST'});// Mock.js从options里读method,但注意大小写!// Mock.js内部会把method转成小写再匹配

有个坑是:有些库(比如早期的jQuery)可能会在method里带多余空格,比如'get '或者' POST',Mock.js的匹配可能会失效。虽然新版本可能已经修了,但遇到匹配不上的情况,记得在控制台打印一下真实的method是什么。

数据生成引擎:template是怎么变成真实JSON的

这是Mock.js最黑魔法的地方。你写个'name|1-10': '@cname',它怎么就能生成"张三丰"或者"欧阳锋"呢?

// Mock.js的模板解析核心逻辑,极度简化版 Mock._generateData=function(template){const result ={};for(let key in template){const value = template[key];const[propName, rule]= key.split('|');// 拆分属性名和规则if(typeof value ==='string'&& value.startsWith('@')){// 处理占位符,如 @cname, @date result[propName]= Mock._handlePlaceholder(value, rule);}elseif(Array.isArray(value)){// 处理数组规则,如 'list|10': [{...}]const count = Mock._parseRule(rule);// 解析出数字10 result[propName]=[];for(let i =0; i < count; i++){// 递归生成数组元素 result[propName].push(Mock._generateData(value[0]));}}elseif(typeof value ==='function'){// 函数直接执行 result[propName]=value.call(result);}elseif(typeof value ==='object'){// 对象递归处理 result[propName]= Mock._generateData(value);}else{// 基础值,直接赋值 result[propName]= value;}}return result;};// 占位符处理 Mock._handlePlaceholder=function(placeholder, rule){// @cname -> 随机中文名// @date -> 随机日期// @image(200x200) -> 随机图片const handlers ={'@cname':()=> Mock.Random.cname(),'@date':()=> Mock.Random.date(),'@image':(size)=>`https://via.placeholder.com/${size}`,// ... 还有很多};// 解析占位符参数,如 @image(200x200)const match = placeholder.match(/^@(\w+)(?:\((.*)\))?$/);const name = match[1];const args = match[2]? match[2].split(','):[];return handlers[name](...args);};

所以模板语法的本质是:属性名|规则 定义生成逻辑,@开头的字符串调用内置生成器,函数提供完全自定义逻辑,数组和对象支持嵌套和递归。

延迟模拟:为什么要假装网络很慢

Mock.js默认会给Mock的请求加个随机延迟,通常是200-600毫秒。为啥要这么干?为了让假数据看起来更像真的

你想啊,如果Mock的请求瞬间返回(0毫秒),而你真实接口要300毫秒,那你前端代码里的loading状态可能根本没机会展示。用户会觉得"这页面怎么闪了一下就出数据了",或者更糟,你代码里的竞态条件(race condition)在Mock环境下根本测不出来,一上真环境就崩。

// Mock.js的延迟配置 Mock.setup({timeout:'200-600'// 随机200到600毫秒// 或者固定值// timeout: 400});// 甚至可以在单个接口上覆盖 Mock.mock('/api/slow-query','get',function(options){// 模拟一个3秒的慢查询this.timeout =3000;return{'data|100':[{'id|+1':1}],'msg':'这接口就是慢,后端说的'};});

爽完之后的贤者时间:这招到底有啥毛病

用着是挺爽,不用等后端接口就能开发,但咱得实话实说,这玩意儿也不是万能的。比如性能问题,你搞几千条假数据试试?浏览器卡成PPT。还有类型安全,后端返回个数字,你Mock写个字符串,TS类型检查直接报错,到时候改代码改到想哭。更别提有些复杂的鉴权逻辑、文件上传,Mock根本模拟不来。咱得把这些坑都列出来,省得新手一头扎进去出不来。

性能陷阱:假数据太多,浏览器直接去世

我第一次用Mock.js生成表格数据,心想着"要测就测极限情况",写了个'list|5000': [{...}]。结果页面直接卡死,风扇转得像要起飞。为啥?

Mock.js的数据生成是同步的、阻塞的。它要一次性生成5000条数据,每条数据还有十几个字段,这个计算量在主线程里跑,UI直接冻结。

// 作死示范:一次性生成5000条复杂数据 Mock.mock('/api/big-data',{'list|5000':[{'id|+1':1,'name':'@cname','profile':'@csentence(100)',// 大段文本'tags|5':['@word'],'metadata':{'views|0-10000':1,'likes|0-1000':1,'comments|0-500':1}}]});// 前端请求的时候 axios.get('/api/big-data').then(res=>{// 用户已经等了3秒,而且这3秒页面是卡死的this.tableData = res.data.list;});

解决方案:分页!Mock.js支持从URL参数里读分页信息:

// 智能Mock,支持分页 Mock.mock(/\/api\/list\?page=\d+&size=\d+/,'get',function(options){// 从url里解析参数const url =newURL(options.url, window.location.origin);const page =parseInt(url.searchParams.get('page'))||1;const size =parseInt(url.searchParams.get('size'))||10;// 只生成当前页的数据const total =1000;// 假装有1000条const start =(page -1)* size;const list =[];for(let i =0; i < size; i++){ list.push({'id': start + i +1,'name': Mock.Random.cname(),'age': Mock.Random.integer(18,60)});}return{code:200,data:{ list, total, page, size },msg:'success'};});

这样每次只生成10条20条,浏览器毫无压力。

类型安全地狱:TS项目里的Mock灾难

现在前端项目都用TypeScript了,Mock.js的问题就来了:它生成的数据类型和你定义的接口可能对不上

// 你定义的接口interfaceUser{ id:number; name:string; age:number; isVip:boolean; createTime:string;// ISO日期字符串}// 你的Mock Mock.mock('/api/user',{'id|+1':1000,// 生成的是number,对上了'name':'@cname',// string,也对上了'age|18-60':1,// number,没问题'isVip|1-2':true,// boolean,OK'createTime':'@date'// 等等,这个生成的是"2023-10-15"这样的格式// 但后端实际返回的是"2023-10-15T14:30:00.000Z"这种ISO格式!});

看起来都是字符串,但格式不一样!你的代码里可能用了new Date(user.createTime),Mock数据能正常解析,真实数据也能,但有些日期处理库对格式很敏感。更惨的是数字和字符串混用:

// 后端返回的id是number类型:12345// 但你Mock写成了:'id':'@id'// Mock.Random.id()生成的是字符串:"12345678901234567890"// 或者反过来:'id|+1':1000// 生成number,但后端返回的是string:"1000"

解决方案:严格对照后端接口文档写Mock,或者用TS的类型守卫做转换:

// Mock配置里就保证类型一致 Mock.mock('/api/user',{'id|+1':1000,'createTime':function(){// 强制返回ISO格式returnnewDate().toISOString();}});// 或者在前端做防御性编程functionnormalizeUser(data:any): User {return{...data, id:Number(data.id),// 强制转number createTime:newDate(data.createTime).toISOString()};}

复杂业务逻辑模拟不了:鉴权、文件上传、流式数据

Mock.js能拦截XHR和fetch,但有些场景它搞不定:

文件上传进度:真实的文件上传有onprogress事件,能拿到上传百分比。Mock.js虽然能返回假数据,但模拟不了真实的上传进度事件。

// 真实的文件上传代码const xhr =newXMLHttpRequest(); xhr.upload.onprogress=(e)=>{const percent =(e.loaded / e.total)*100;this.progress = percent;// 更新进度条}; xhr.open('POST','/api/upload'); xhr.send(formData);// Mock.js能拦截这个请求并返回成功响应// 但它模拟不了progress事件的触发过程// 你的进度条要么直接100%,要么不动

解决方案:自己手动触发事件,或者不要用Mock拦截,用MSW(Mock Service Worker)这种更现代的方案。

Token鉴权逻辑:真实的登录流程是:1) POST用户名密码 2) 后端返回token 3) 后续请求带token 4) token过期跳登录页。Mock.js能模拟第1、2步,但第3步(后续请求自动带token)和第4步(token过期判断)需要额外的逻辑。

// 简单的token模拟方案let mockToken =null;// 登录接口 Mock.mock('/api/login','post',(options)=>{const{ username, password }=JSON.parse(options.body);if(username ==='admin'&& password ==='123456'){ mockToken ='mock-token-'+ Date.now();return{code:200,token: mockToken,expires:3600};}return{code:401,msg:'用户名或密码错误'};});// 需要鉴权的接口 Mock.mock('/api/user/profile','get',(options)=>{// 检查header里有没有tokenconst headers = options.headers ||{};const auth = headers.Authorization || headers.authorization;if(!auth || auth !==`Bearer ${mockToken}`){return{code:401,msg:'未登录或token已过期'};}return{code:200,data:{name:'管理员',avatar:'@image'}};});

流式数据(SSE/Stream):Mock.js不支持Server-Sent Events或者fetch的ReadableStream,这些得用专门的Mock工具。

跨域和CORS的坑

有时候你Mock的请求莫名其妙报CORS错误,但后端接口明明已经配置了跨域。为啥?

因为Mock.js拦截的是请求发出前的XHR对象,但如果匹配失败,请求会正常发出去。这时候如果后端没配置CORS,就会报错。更坑的是,Mock.js有时候会把请求类型判断错,导致该拦截的没拦截,不该拦截的拦截了。

// 假设你配置了Mock拦截 /api/* Mock.mock('/api/*',{msg:'mocked'});// 但你的真实请求是:fetch('https://other-domain.com/api/data',{mode:'cors'// 明确跨域});// Mock.js一看,url匹配/api/*,拦截!// 但它返回的Response对象没有CORS相关的header// 浏览器可能会报CORS错误,即使这个请求本来不该被Mock

解决方案:精确匹配URL,不要用太宽泛的通配符,或者给Mock的响应加上CORS头:

Mock.mock('/api/data',{// Mock.js支持配置响应头(看版本,有些版本不支持)headers:{'Access-Control-Allow-Origin':'*'},body:{data:'mocked'}});

真实搬砖场景:我在项目里是怎么靠它摸鱼的

光说不练假把式,说说实际干活怎么用。比如后端接口还没好,产品经理又催着要页面效果,这时候Mock.mock就是你的救命稻草。怎么快速生成列表页的假数据?怎么模拟接口超时或者报错(比如500错误、网络断开)来测试前端的异常处理?甚至怎么配合Vue或React的状态管理,让假数据跑得像真的一样。这里得多给点具体的思路,比如怎么组织template模板才能让数据看起来不那么假。

场景一:后端接口延迟,先画UI

这是最经典的场景。产品要一个"用户管理后台",后端说"表结构还没设计完,下周给接口"。你怎么办?

Step 1:先定义接口规范

别急着写代码,先跟后端(或者你自己)定好接口格式:

GET /api/users?page=1&size=10&keyword=xxx Response: { code: 200, data: { list: [ { id: number, name: string, email: string, status: 0|1, createTime: string } ], total: number, page: number, size: number } } 

Step 2:写Mock配置

// mock/user.jsimport Mock from'mockjs';// 生成一条用户数据constgenerateUser=(id)=>({'id': id,'name':'@cname','email':function(){// 根据名字生成邮箱,看起来更像真的const pinyin =this.name.charAt(0);// 简化处理,实际应该用拼音库return`${pinyin.toLowerCase()}${Mock.Random.integer(100,999)}@example.com`;},'avatar':'@image(100x100)','status|0-1':1,// 0禁用 1启用'role|1':['admin','editor','viewer'],'createTime':'@datetime("yyyy-MM-dd HH:mm:ss")','lastLogin':'@datetime("yyyy-MM-dd HH:mm:ss")'});// 列表接口 - 支持分页和搜索 Mock.mock(/\/api\/users\?.*/,'get',(options)=>{const url =newURL(options.url, window.location.origin);const page =parseInt(url.searchParams.get('page'))||1;const size =parseInt(url.searchParams.get('size'))||10;const keyword = url.searchParams.get('keyword')||'';// 生成100条假数据作为"数据库"const allUsers =[];for(let i =1; i <=100; i++){ allUsers.push(generateUser(i));}// 模拟搜索(前端过滤)let filtered = allUsers;if(keyword){ filtered = allUsers.filter(u=> u.name.includes(keyword)|| u.email.includes(keyword));}// 分页const start =(page -1)* size;const list = filtered.slice(start, start + size);return{code:200,data:{ list,total: filtered.length, page, size },msg:'success'};});// 单个用户详情 Mock.mock(/\/api\/user\/\d+/,'get',(options)=>{const id = options.url.match(/\/api\/user\/(\d+)/)[1];return{code:200,data:generateUser(parseInt(id)),msg:'success'};});// 更新用户 Mock.mock(/\/api\/user\/\d+/,'put',(options)=>{const body =JSON.parse(options.body);return{code:200,data:{...body,updateTime: Mock.Random.now()},msg:'更新成功'};});

Step 3:Vue组件里直接用

<template> <div> <el-input v-model="keyword" placeholder="搜索用户" @keyup.enter="search" /> <el-table :data="userList" v-loading="loading"> <el-table-column prop="name" label="姓名"> <template #default="{row}"> <img :src="row.avatar" /> {{ row.name }} </template> </el-table-column> <el-table-column prop="email" label="邮箱" /> <el-table-column prop="status" label="状态"> <template #default="{row}"> <el-tag :type="row.status ? 'success' : 'danger'"> {{ row.status ? '启用' : '禁用' }} </el-tag> </template> </el-table-column> </el-table> <el-pagination v-model:current-page="page" v-model:page-size="size" :total="total" @change="fetchData" /> </div> </template> <script setup> import { ref, onMounted } from 'vue'; import axios from 'axios'; const userList = ref([]); const loading = ref(false); const page = ref(1); const size = ref(10); const total = ref(0); const keyword = ref(''); const fetchData = async () => { loading.value = true; try { const res = await axios.get('/api/users', { params: { page: page.value, size: size.value, keyword: keyword.value } }); userList.value = res.data.data.list; total.value = res.data.data.total; } catch (err) { console.error('获取用户列表失败:', err); } finally { loading.value = false; } }; const search = () => { page.value = 1; fetchData(); }; onMounted(fetchData); </script> 

看到没?UI完全按照真实接口的逻辑写,包括loading状态、错误处理、分页逻辑。等后端接口好了,把Mock文件一删(或者配置关闭),代码一行不用改,直接连真接口。

场景二:模拟各种异常情况,测试前端鲁棒性

好的前端代码要能处理各种异常情况:网络断开、服务器500错误、超时、返回格式不对。但这些情况在开发环境很难遇到,Mock.js可以帮你"制造"这些异常。

// mock/error.jsimport Mock from'mockjs';// 模拟500服务器错误 Mock.mock('/api/error-500',{status:500,statusText:'Internal Server Error',responseText:JSON.stringify({msg:'数据库连接超时'})});// 模拟网络超时(挂起请求,永不返回) Mock.mock('/api/timeout',function(){// 返回一个pending的Promise,模拟请求卡住returnnewPromise(()=>{});});// 模拟返回格式错误(不是JSON) Mock.mock('/api/bad-format',{responseText:'这不是JSON,服务器出错了'});// 模拟随机失败(测试重试逻辑)let requestCount =0; Mock.mock('/api/unstable',(options)=>{ requestCount++;if(requestCount %3!==0){// 前两次失败return{status:503,responseText:JSON.stringify({msg:'服务暂时不可用'})};}// 第三次成功return{code:200,data:'终于成功了',msg:'success'};});// 模拟超时的另一种方式(延迟返回) Mock.mock('/api/slow-timeout',(options)=>{returnnewPromise((resolve)=>{setTimeout(()=>{resolve({code:408,msg:'请求超时'});},10000);// 10秒后才返回,但前端可能5秒就超时了});});

前端代码里可以测试这些异常:

// 封装一个带超时的请求constfetchWithTimeout=async(url, options ={}, timeout =5000)=>{const controller =newAbortController();const id =setTimeout(()=> controller.abort(), timeout);try{const res =awaitfetch(url,{...options,signal: controller.signal });clearTimeout(id);return res;}catch(err){clearTimeout(id);if(err.name ==='AbortError'){thrownewError('请求超时');}throw err;}};// 测试try{const res =awaitfetchWithTimeout('/api/slow-timeout',{},3000);}catch(err){ console.log(err.message);// "请求超时"}

场景三:让假数据看起来像真的

很多人Mock的数据一看就是假的:“用户1”、“用户2”、“[email protected]”。怎么让数据更真实?

技巧1:用函数动态生成关联数据

// 不好的做法:名字和邮箱没关系 Mock.mock('/api/user',{'name':'@cname','email':'@email'// 可能生成 [email protected],但名字是李四});// 好的做法:邮箱根据名字生成 Mock.mock('/api/user',{'name':'@cname','email':function(){// 根据中文名生成拼音邮箱(简化版)const nameMap ={'张':'zhang','李':'li','王':'wang','刘':'liu','陈':'chen'};const firstName =this.name.charAt(0);const pinyin = nameMap[firstName]||'user';const num = Mock.Random.integer(1,999);return`${pinyin}${num}@company.com`;}});

技巧2:生成合理的业务数据

// 电商订单数据 Mock.mock('/api/orders',{'list|10':[{'orderNo':function(){// 订单号:日期+随机数+用户ID,看起来像真的const date =newDate().toISOString().slice(0,10).replace(/-/g,'');const random = Mock.Random.string('number',6);return`ORD${date}${random}`;},'amount':function(){// 金额根据商品数量计算,别瞎随机const items =this.items ||[];return items.reduce((sum, item)=> sum + item.price * item.quantity,0).toFixed(2);},'items|1-5':[{'sku':/SKU[0-9]{8}/,// SKU格式:SKU12345678'name':'@ctitle(5,15)',// 商品名5-15个字'price|10-9999.2':1,'quantity|1-10':1}],'status|1':['pending','paid','shipped','completed','cancelled'],'createTime':'@datetime("yyyy-MM-dd HH:mm:ss")','payTime':function(){// 只有paid及以后状态才有支付时间if(['paid','shipped','completed'].includes(this.status)){return Mock.Random.datetime('yyyy-MM-dd HH:mm:ss');}returnnull;}}]});

技巧3:图片别用默认的,用占位图服务

// Mock.js自带的@image生成的是纯颜色图,太假 Mock.mock('/api/goods',{'image':'@image(200x200)'// 生成的是#e2e2e2这种颜色块});// 更好的做法:用占位图服务,看起来像真实商品图 Mock.mock('/api/goods',{'image':function(){const id = Mock.Random.integer(1,1000);// picsum.photos提供真实的随机图片return`https://picsum.photos/seed/${id}/300/200`;// 或者用特定主题// return `https://loremflickr.com/300/200/product?lock=${id}`;}});

场景四:配合状态管理(Vuex/Pinia/Redux)

在大型项目里,Mock数据要配合状态管理使用,才能模拟完整的业务流。

// store/modules/user.js (Pinia写法)import{ defineStore }from'pinia';import axios from'axios';exportconst useUserStore =defineStore('user',{state:()=>({userInfo:null,permissions:[],loading:false,error:null}),actions:{asynclogin(credentials){this.loading =true;this.error =null;try{const res =await axios.post('/api/login', credentials);this.userInfo = res.data.data.user;this.permissions = res.data.data.permissions; localStorage.setItem('token', res.data.data.token);returntrue;}catch(err){this.error = err.response?.data?.msg ||'登录失败';returnfalse;}finally{this.loading =false;}},asyncfetchUserInfo(){this.loading =true;try{const res =await axios.get('/api/user/profile');this.userInfo = res.data.data;}catch(err){if(err.response?.status ===401){// token过期,跳转登录this.logout();}}finally{this.loading =false;}},logout(){this.userInfo =null;this.permissions =[]; localStorage.removeItem('token');}}});

对应的Mock配置:

// mock/auth.jsimport Mock from'mockjs';// 模拟登录 Mock.mock('/api/login','post',(options)=>{const{ username, password }=JSON.parse(options.body);// 模拟验证延迟if(username ==='admin'&& password ==='admin123'){return{code:200,data:{token:'mock-jwt-token-'+ Date.now(),user:{id:1,username:'admin',nickname:'系统管理员',avatar:'https://picsum.photos/seed/admin/100/100',role:'super_admin'},permissions:['user:read','user:write','order:read','system:*']},msg:'登录成功'};}// 模拟密码错误return{code:401,msg:'用户名或密码错误'};});// 模拟获取用户信息(需要token) Mock.mock('/api/user/profile','get',(options)=>{const token = options.headers?.Authorization?.replace('Bearer ','');if(!token ||!token.startsWith('mock-jwt-token')){return{code:401,msg:'Token无效或已过期'};}return{code:200,data:{id:1,username:'admin',nickname:'系统管理员',avatar:'https://picsum.photos/seed/admin/100/100',department:'技术部',email:'[email protected]',phone:'13800138000',lastLoginIp: Mock.Random.ip(),lastLoginTime: Mock.Random.datetime()}};});// 模拟登出 Mock.mock('/api/logout','post',{code:200,msg:'登出成功'});

这样整个登录流程都是通的,你可以测试:登录成功跳转、登录失败提示、token过期自动登出、权限控制等等。

翻车现场急救包:为什么我的请求还是发出去了

肯定有人会遇到这种情况:代码明明写了,控制台也报了Mock成功,结果Network面板里请求还是发到了后端,或者根本没反应。这时候别慌,大概率是url匹配规则写错了,或者是请求方法(GET/POST)没对上。还有一种坑是Webpack或者Vite的配置问题,把Mock文件给tree-shaking掉了。咱得把这些排查思路理一理,就像医生问诊一样,一步步教你怎么定位病灶,别只会重启大法。

症状1:Network面板里能看到真实请求

这说明Mock.js没拦截到,请求发出去了

排查步骤1:检查URL是否匹配

// 你写的Mock Mock.mock('/api/user',{data:'mocked'});// 但真实请求可能是: axios.get('http://localhost:8080/api/user');// 带域名// 或者 axios.get('/api/user/123');// 带动态ID// 或者 axios.get('/api/user?page=1');// 带参数// 这些都不匹配 '/api/user' 这个字符串!

解决方案:用正则匹配,别用字符串:

// 匹配 /api/user 以及 /api/user?page=1 Mock.mock(/\/api\/user(\?.*)?$/,{data:'mocked'});// 匹配 /api/user/123 这种RESTful风格 Mock.mock(/\/api\/user\/\d+/,{data:'mocked'});// 匹配所有/api/开头的 Mock.mock(/\/api\/.*/,{data:'mocked'});

排查步骤2:检查请求方法

// 你写的Mock(没指定方法,默认匹配所有) Mock.mock('/api/user',{data:'mocked'});// 但axios可能用的是POST axios.post('/api/user',{name:'test'});// Mock.js确实拦截了,但你的template返回的是GET格式的数据// 导致前端解析报错,你以为没拦截成功

解决方案:明确指定方法,或者分别配置:

Mock.mock('/api/user','get',{/* GET返回的数据 */}); Mock.mock('/api/user','post',(options)=>{// 可以读取body,返回不同的数据const body =JSON.parse(options.body);return{id: Mock.Random.id(),...body,createTime:newDate().toISOString()};});

排查步骤3:检查Mock执行时机

Mock.js必须在请求发起之前完成初始化。如果你在组件里import Mock from 'mockjs'然后直接Mock.mock(),但这时候请求已经发出去了,那就晚了。

解决方案:在入口文件(main.js/app.js)最早的位置引入:

// main.jsimport{ createApp }from'vue';import App from'./App.vue';// 确保在最前面引入Mockif(process.env.NODE_ENV==='development'){require('./mock/index.js');// 或者 import './mock/index.js'}const app =createApp(App); app.mount('#app');

症状2:控制台显示Mock成功,但回调没执行

这说明拦截成功了,但响应数据有问题,导致前端解析失败。

排查步骤1:检查返回格式是否是合法JSON

// 错误的Mock:返回字符串,但前端用res.json()解析 Mock.mock('/api/data','this is not json');// 正确的Mock:返回对象,Mock.js会自动转JSON Mock.mock('/api/data',{msg:'这是对象,会被自动JSON.stringify'});// 如果你手动拼JSON字符串,注意转义: Mock.mock('/api/data',{responseText:'{"key": "value"}'// Mock.js会原样返回这个字符串});

排查步骤2:检查异步逻辑

// 如果你用了函数返回Promise,确保resolve的是正确格式 Mock.mock('/api/async',()=>{returnnewPromise((resolve)=>{setTimeout(()=>{// 错误:直接返回对象,但Mock.js期望包装在特定格式里resolve({data:'test'});// 正确:有些版本需要这样resolve({status:200,responseText:JSON.stringify({data:'test'})});},1000);});});

症状3:Vite/Webpack项目里Mock突然失效

这大概率是tree-shaking或者热更新的问题。

Vite项目的坑

Vite的生产构建会tree-shaking掉"没用"的代码。如果你在main.js里这样写:

if(import.meta.env.DEV){import('./mock/index.js');}

Vite可能会认为这个import是动态的、可选的,在生产构建时直接删掉。或者Mock文件里的代码被认为"没有副作用",被优化掉了。

解决方案:确保Mock文件有副作用,并且引入方式让打包工具知道不能删:

// mock/index.jsimport Mock from'mockjs';// 确保这行代码被执行,而不是只定义了函数没调用 Mock.mock('/api/test',{msg:'ok'});// 导出点什么,让文件被认为是ESM模块exportdefault Mock;
// main.js// 方式1:直接import,不用条件判断(推荐只在开发环境引入)import'./mock/index.js';// 方式2:如果需要条件判断,确保语法正确if(import.meta.env.DEV){// 动态import也要await,确保在后续代码前完成awaitimport('./mock/index.js');}

Webpack项目的坑

如果你用了mockjs的按需加载或者babel-plugin-import,可能会出问题。

// 错误:只引入了Mock,没引入XHR拦截逻辑import{ mock }from'mockjs';// 正确:引入完整的Mock对象import Mock from'mockjs';

症状4:部分接口Mock成功,部分失败

这说明Mock的匹配规则有冲突,后写的规则覆盖了先写的

// 先写的通用规则 Mock.mock('/api/*',{msg:'通用'});// 后写的具体规则 Mock.mock('/api/user',{msg:'用户专用'});// 结果:请求 /api/user 时,可能匹配的是第一个规则// 因为Mock.js内部存储规则的方式是对象,key是url字符串// '/api/*' 和 '/api/user' 作为对象key,匹配顺序不确定

解决方案:精确匹配优先,或者用不同的匹配方式:

// 用正则的精确度来控制优先级 Mock.mock(/^\/api\/user$/,{msg:'用户专用'});// 完全匹配 Mock.mock(/^\/api\/order$/,{msg:'订单专用'}); Mock.mock(/\/api\/.*/,{msg:'通用'});// 放在最后,作为fallback

万能排查法:打印日志

如果以上都查不出来,直接在Mock回调里打日志:

Mock.mock(/\/api\/.*/,(options)=>{ console.log('Mock拦截到请求:', options); console.log('URL:', options.url); console.log('Method:', options.type); console.log('Body:', options.body);// 返回明显不同的数据,确认走的是Mockreturn{isMocked:true,timestamp: Date.now(),data:'如果你看到这个,说明Mock生效了'};});

打开控制台,看看请求来的时候有没有打印。如果没有打印,说明URL没匹配上;如果有打印但Network里还有请求,说明Mock.js内部出错了。

几个让同事喊你爸爸的骚操作技巧

既然要玩,就玩点花的。比如怎么用函数动态生成数据,让每次请求回来的ID都不一样?怎么利用template里的语法糖生成随机图片、随机名字,让页面看起来像个真的上线项目?还有怎么把Mock数据单独抽离成一个文件,甚至根据环境变量自动开关,上线的时候自动关闭Mock,别让假数据污染生产环境。这些技巧学会了,你在组里的地位绝对不一样。

骚操作1:函数式Mock,每次请求数据都不一样

静态Mock有个问题:第一次请求和第十次请求返回一样的数据。真实接口可不会这样(除非有缓存)。用函数可以让每次请求都生成新数据:

// 静态Mock - 每次返回一样的 Mock.mock('/api/random',{'id|+1':1,'value':'@integer(1,100)'});// 函数式Mock - 每次请求重新生成let globalId =1000; Mock.mock('/api/random',()=>{// 每次请求都会执行这个函数return{id:++globalId,// 真正的自增,不是Mock的伪自增value: Mock.Random.integer(1,100),timestamp: Date.now(),// 真实时间戳randomStr: Math.random().toString(36).substring(7)// 真随机字符串};});// 更骚的:根据请求参数返回不同数据 Mock.mock('/api/search',(options)=>{const{ keyword, page }=JSON.parse(options.body);// 根据keyword生成相关数据constgenerateItems=(key)=>{const count = Mock.Random.integer(5,20);return Array.from({length: count },(_, i)=>({id:`${page}-${i}`,title:`${key} - 搜索结果${i+1}`,desc: Mock.Random.cparagraph(2),// 随机两段中文relevance: Mock.Random.integer(60,99)// 相关度分数}));};return{code:200,data:{list:generateItems(keyword),total:999,// 假装有很多 page, keyword }};});

骚操作2:Mock数据持久化,模拟CRUD

如果你在做后台管理系统,需要测试新增、编辑、删除功能,Mock的数据要能"记住"修改。

// mock/crud.jsimport Mock from'mockjs';// 内存数据库const db ={users: Array.from({length:50},(_, i)=>({id: i +1,name: Mock.Random.cname(),age: Mock.Random.integer(18,60),email: Mock.Random.email(),status: Mock.Random.pick(['active','inactive'])}))};// 查询列表 Mock.mock(/\/api\/users\?.*/,'get',(options)=>{const url =newURL(options.url, window.location.origin);const page =parseInt(url.searchParams.get('page'))||1;const size =parseInt(url.searchParams.get('size'))||10;const start =(page -1)* size;return{code:200,data:{list: db.users.slice(start, start + size),total: db.users.length }};});// 新增 Mock.mock('/api/users','post',(options)=>{const body =JSON.parse(options.body);const newUser ={id: db.users.length +1,...body,createTime:newDate().toISOString()}; db.users.push(newUser);return{code:200,data: newUser,msg:'创建成功'};});// 编辑 Mock.mock(/\/api\/users\/\d+/,'put',(options)=>{const id =parseInt(options.url.match(/\/api\/users\/(\d+)/)[1]);const body =JSON.parse(options.body);const index = db.users.findIndex(u=> u.id === id);if(index >-1){ db.users[index]={...db.users[index],...body,updateTime:newDate().toISOString()};return{code:200,data: db.users[index],msg:'更新成功'};}return{code:404,msg:'用户不存在'};});// 删除 Mock.mock(/\/api\/users\/\d+/,'delete',(options)=>{const id =parseInt(options.url.match(/\/api\/users\/(\d+)/)[1]);const index = db.users.findIndex(u=> u.id === id);if(index >-1){ db.users.splice(index,1);return{code:200,msg:'删除成功'};}return{code:404,msg:'用户不存在'};});

这样你在页面上增删改查,数据会真的变化,刷新页面后数据还在(因为存在内存里),直到你F5刷新整个页面重置Mock。

骚操作3:环境变量控制Mock开关

最危险的事情就是:上线的时候Mock还开着,用户看到假数据。必须做到开发环境自动开启,生产环境自动关闭

// .env.developmentVITE_USE_MOCK=true// .env.productionVITE_USE_MOCK=false
// mock/index.jsimport Mock from'mockjs';// 根据环境变量决定是否启动const isUseMock =import.meta.env.VITE_USE_MOCK==='true';if(isUseMock){ console.log('[Mock] 开发模式:Mock服务已启动');// 批量注册Mockconst modules =import.meta.glob('./modules/*.js',{eager:true}); Object.values(modules).forEach(module=>{if(module.default){ module.default(Mock);}});// 设置全局延迟 Mock.setup({timeout:'300-800'});}else{ console.log('[Mock] 生产模式:Mock服务已禁用');}exportdefault Mock;
// mock/modules/user.jsexportdefaultfunction(Mock){ Mock.mock('/api/user/info',{id:'@id',name:'@cname'}); Mock.mock('/api/user/list',{'list|10':[{'id|+1':1,'name':'@cname'}]});}
// main.jsimport'./mock/index.js';// 自动根据环境变量判断

骚操作4:模拟真实的数据关系

真实业务里数据是有关系的:用户有订单,订单有商品,商品有分类。Mock的时候也要体现这种关系。

// 先生成基础数据const categories = Array.from({length:5},(_, i)=>({id: i +1,name:['数码','服装','食品','家居','图书'][i]}));const goods = Array.from({length:100},(_, i)=>({id: i +1,name: Mock.Random.ctitle(5,10),categoryId: Mock.Random.pick(categories).id,price: Mock.Random.float(10,1000,2,2),stock: Mock.Random.integer(0,999)}));// 订单接口 - 关联商品和用户 Mock.mock('/api/orders',{'list|20':[{'id|+1':10000,'orderNo':/2023[0-9]{12}/,'userId|1-1000':1,'items|1-5':function(){// 从goods里随机选几个,并计算总价const selectedGoods =[];const count = Mock.Random.integer(1,5);let totalAmount =0;for(let i =0; i < count; i++){const g = goods[Mock.Random.integer(0, goods.length -1)];const quantity = Mock.Random.integer(1,3); selectedGoods.push({goodsId: g.id,goodsName: g.name,price: g.price,quantity: quantity,subtotal:(g.price * quantity).toFixed(2)}); totalAmount += g.price * quantity;}this.totalAmount = totalAmount.toFixed(2);return selectedGoods;},'status|1':['待付款','待发货','已发货','已完成'],'createTime':'@datetime'}]});

骚操作5:模拟WebSocket实时数据

Mock.js只能拦截HTTP请求,WebSocket它管不了。但你可以用额外的库模拟WebSocket:

// 用mock-socket库模拟WebSocketimport{ Server, WebSocket }from'mock-socket';// 创建Mock WebSocket服务器const mockServer =newServer('ws://localhost:8080/realtime'); mockServer.on('connection',socket=>{ console.log('[Mock WebSocket] 客户端已连接');// 模拟实时推送消息const interval =setInterval(()=>{ socket.send(JSON.stringify({type:'notification',data:{id: Date.now(),title: Mock.Random.ctitle(5,10),time:newDate().toLocaleTimeString()}}));},5000);// 模拟响应客户端消息 socket.on('message',data=>{const msg =JSON.parse(data);if(msg.type ==='heartbeat'){ socket.send(JSON.stringify({type:'pong'}));}}); socket.on('close',()=>{clearInterval(interval);});});// 前端代码完全不用改,直接连ws://localhost:8080/realtime就行const ws =newWebSocket('ws://localhost:8080/realtime'); ws.onmessage=(e)=>{ console.log('收到消息:',JSON.parse(e.data));};

骚操作6:Mock文件模块化组织

项目大了,Mock文件也会很乱。建议按业务模块组织:

mock/ ├── index.js # 入口,控制开关 ├── utils.js # 工具函数(生成数据、延迟等) ├── modules/ │ ├── user.js # 用户相关 │ ├── order.js # 订单相关 │ ├── goods.js # 商品相关 │ └── auth.js # 登录鉴权 └── data/ # 静态数据JSON ├── cities.json └── departments.json 
// mock/modules/user.jsimport{ mock, Random }from'mockjs';import{ success, error, paginate }from'../utils.js';exportdefaultfunctionuserModule(){// 列表mock(/\/api\/users\?.*/,'get',(options)=>{const params =newURLSearchParams(options.url.split('?')[1]);returnsuccess(paginate(generateUsers(100), params));});// 详情mock(/\/api\/users\/\d+/,'get',(options)=>{const id = options.url.match(/\/api\/users\/(\d+)/)[1];returnsuccess(generateUser(id));});// 编辑mock(/\/api\/users\/\d+/,'put',(options)=>{const body =JSON.parse(options.body);if(!body.name){returnerror('用户名不能为空');}returnsuccess({...body,updateTime:newDate().toISOString()});});}functiongenerateUsers(count){return Array.from({length: count },(_, i)=>generateUser(i +1));}functiongenerateUser(id){returnmock({ id,'name':'@cname','email':'@email','phone':/1[3-9]\d{9}/,'avatar':`@image(100x100, @color, @word)`,'status|1':[0,1],'createTime':'@datetime'});}
// mock/utils.jsexportconstsuccess=(data)=>({code:200, data,msg:'success'});exportconsterror=(msg, code =500)=>({ code, msg,data:null});exportconstpaginate=(allData, params)=>{const page =parseInt(params.get('page'))||1;const size =parseInt(params.get('size'))||10;const start =(page -1)* size;return{list: allData.slice(start, start + size),total: allData.length, page, size };};

行了别卷了,再写下去后端要提刀过来了

差不多得了,Mock终究是替身,真刀真枪还得靠后端接口。这篇东西就当是个备忘录,下次再遇到接口没好被产品催命的时候,翻出来瞅一眼,能省点头发是点头发。

说实话,Mock.js这玩意儿用好了是真香,用不好也是真坑。我见过有人拿Mock当长期方案,最后上线的时候发现Mock逻辑和真实接口对不上,改代码改到凌晨三点。也见过有人完全不用Mock,后端接口延迟一周,他就在那儿干瞪眼一周,效率低到被老板约谈。

我的建议

  1. Mock只是开发阶段的拐杖,不是假肢。接口一旦ready,立刻切到真实环境测试,别恋战。
  2. 保持Mock和接口文档同步,最好是后端先出Swagger文档,你照着文档写Mock,而不是凭想象瞎编。
  3. 复杂业务别硬Mock,文件上传、大文件下载、实时通信这些,Mock成本太高,不如直接等后端。
  4. 上线前 triple check,确保所有Mock都已关闭,别让假数据流到生产环境,那可是事故。

要是真把Mock当永久方案,那咱俩还是绝交吧,太天真了。但要是完全不会Mock,每次被后端卡脖子,那也得反思反思,工具箱里是不是缺了这把瑞士军刀。

总之,前端这条路,工具是死的,人是活的。Mock.js用熟了,确实能让你在"等接口"的焦虑中稍微喘口气,但也别沉迷这种虚假的安全感。毕竟,代码最终还是要在真实的网络环境、真实的数据、真实的用户操作下跑起来,那才是真正的考验

下次见,希望你的Network面板里,再也没有404和CORS报错。

欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。
推荐:DTcode7的博客首页。
一个做过前端开发的产品经理,经历过睿智产品的折磨导致脱发之后,励志要翻身农奴把歌唱,一边打入敌人内部一边持续提升自己,为我们广大开发同胞谋福祉,坚决抵制睿智产品折磨我们码农兄弟!

专栏系列(点击解锁)学习路线(点击解锁)知识定位
《微信小程序相关博客》持续更新中~结合微信官方原生框架、uniapp等小程序框架,记录请求、封装、tabbar、UI组件的学习记录和使用技巧等
《AIGC相关博客》持续更新中~AIGC、AI生产力工具的介绍,例如stable diffusion这种的AI绘画工具安装、使用、技巧等总结
《HTML网站开发相关》《前端基础入门三大核心之html相关博客》前端基础入门三大核心之html板块的内容,入坑前端或者辅助学习的必看知识
《前端基础入门三大核心之JS相关博客》前端JS是JavaScript语言在网页开发中的应用,负责实现交互效果和动态内容。它与HTML和CSS并称前端三剑客,共同构建用户界面。
通过操作DOM元素、响应事件、发起网络请求等,JS使页面能够响应用户行为,实现数据动态展示和页面流畅跳转,是现代Web开发的核心
《前端基础入门三大核心之CSS相关博客》介绍前端开发中遇到的CSS疑问和各种奇妙的CSS语法,同时收集精美的CSS效果代码,用来丰富你的web网页
《canvas绘图相关博客》Canvas是HTML5中用于绘制图形的元素,通过JavaScript及其提供的绘图API,开发者可以在网页上绘制出各种复杂的图形、动画和图像效果。Canvas提供了高度的灵活性和控制力,使得前端绘图技术更加丰富和多样化
《Vue实战相关博客》持续更新中~详细总结了常用UI库elementUI的使用技巧以及Vue的学习之旅
《python相关博客》持续更新中~Python,简洁易学的编程语言,强大到足以应对各种应用场景,是编程新手的理想选择,也是专业人士的得力工具
《sql数据库相关博客》持续更新中~SQL数据库:高效管理数据的利器,学会SQL,轻松驾驭结构化数据,解锁数据分析与挖掘的无限可能
《算法系列相关博客》持续更新中~算法与数据结构学习总结,通过JS来编写处理复杂有趣的算法问题,提升你的技术思维
《IT信息技术相关博客》持续更新中~作为信息化人员所需要掌握的底层技术,涉及软件开发、网络建设、系统维护等领域的知识
《信息化人员基础技能知识相关博客》无论你是开发、产品、实施、经理,只要是从事信息化相关行业的人员,都应该掌握这些信息化的基础知识,可以不精通但是一定要了解,避免日常工作中贻笑大方
《信息化技能面试宝典相关博客》涉及信息化相关工作基础知识和面试技巧,提升自我能力与面试通过率,扩展知识面
《前端开发习惯与小技巧相关博客》持续更新中~罗列常用的开发工具使用技巧,如 Vscode快捷键操作、Git、CMD、游览器控制台等
《photoshop相关博客》持续更新中~基础的PS学习记录,含括PPI与DPI、物理像素dp、逻辑像素dip、矢量图和位图以及帧动画等的学习总结
日常开发&办公&生产【实用工具】分享相关博客》持续更新中~分享介绍各种开发中、工作中、个人生产以及学习上的工具,丰富阅历,给大家提供处理事情的更多角度,学习了解更多的便利工具,如Fiddler抓包、办公快捷键、虚拟机VMware等工具

吾辈才疏学浅,摹写之作,恐有瑕疵。望诸君海涵赐教。望轻喷,嘤嘤嘤

非常期待和您一起在这个小小的网络世界里共同探索、学习和成长。愿斯文对汝有所裨益,纵其简陋未及渊博,亦足以略尽绵薄之力。倘若尚存阙漏,敬请不吝斧正,俾便精进!
在这里插入图片描述

Read more

【Java 开发日记】我们来说一下无锁队列 Disruptor 的原理

【Java 开发日记】我们来说一下无锁队列 Disruptor 的原理

目录 一、为什么需要 Disruptor?—— 背景与问题 二、核心设计思想 三、核心组件与原理 1. 环形缓冲区(Ring Buffer) 2. 序列(Sequence) 3. 序列屏障(Sequence Barrier) 4. 等待策略(Wait Strategy) 5. 事件处理器(EventProcessor) 6. 生产者(Producer) 四、工作流程示例(单生产者 -> 单消费者) 五、多消费者与依赖关系 六、总结:Disruptor 高性能的秘诀 一、为什么需要 Disruptor?—— 背景与问题 在高并发编程中,传统的队列(如 java.

By Ne0inhk
Java 大视界 -- 基于 Java+Kafka 构建高可用消息队列集群:实战部署与性能调优(442)

Java 大视界 -- 基于 Java+Kafka 构建高可用消息队列集群:实战部署与性能调优(442)

Java 大视界 -- 基于 Java+Kafka 构建高可用消息队列集群:实战部署与性能调优(442) * 引言: * 正文: * 一、 Kafka 高可用集群核心认知:先懂原理,再谈部署 * 1.1 Kafka 高可用核心原理 * 1.1.1 核心组件协同逻辑 * 1.1.2 高可用核心:多副本与 Leader 选举机制 * 1.2 Kafka 高可用集群架构设计要点 * 1.3 技术栈选型:Java+Kafka 核心版本适配 * 二、 实战部署:Java+Kafka 高可用集群搭建 * 2.1 部署前准备:环境初始化

By Ne0inhk
2026 年 AI Agent 开发必备:10 个经过实战验证的设计模式,告别 Demo 级玩具系统

2026 年 AI Agent 开发必备:10 个经过实战验证的设计模式,告别 Demo 级玩具系统

在 AI Agent 全面进入企业级落地深水区的 2026 年,行业里依然存在一个残酷的现实:超过 90% 的 AI Agent 项目,永远停留在了 Demo 阶段。很多工程师能靠几行代码搭出一个能对话、能调用工具的 Agent 原型,可一旦放到真实的业务场景中,就会出现执行失控、逻辑跑偏、频繁幻觉、无法处理复杂任务、不可追溯、不可管控等致命问题。 我花了数年时间,踩过无数坑,才真正搞明白:能落地的 AI Agent,核心从来不是用了多强大的 LLM,而是用对了正确的设计模式。AI Agent 的开发,和软件工程一样,有大量重复出现的共性问题,而经过实战验证的设计模式,就是解决这些问题的成熟方案 —— 它能让你少走几年弯路,用最少的成本搭建出稳定、可控、可扩展、能真正落地的 Agent 系统。

By Ne0inhk