手慢无!免费领“指尖随手拍,国庆晒幸福”6GB流量
活动时间:
09-27 10:01 至 10-31 00:00
报名时间:
09-27 10:00 至 10-28 23:59
活动类型:
免费活动
活动地址:
安徽日报客户端
报名人数:
已有2219人报名,限额人数9999999人
2023年国庆期间,安徽日报报业集团(安徽日报社)、中国移动通信集团安徽有限公司联合发起“指尖随手拍,国庆晒幸福”视觉影像征集活动,邀请屏幕前的你,拿起手机,聚焦美好,用图片晒出幸福时光! 活动即将开启,还有众多大奖等你拿!( 详情点击→“指尖随手拍,国庆晒幸福”视觉影像征集活动)
关注活动开展
移动用户
可点击当前页面右下角
下载注册安徽日报App
根据页面指引进行操作
免费领取6GB
移动国内通用流量
(3GB*2个月)
(注:此次活动的流量福利仅限首次注册安徽日报App的用户领取)
作者:西岚
转发链接:https://juejin.im/post/5dadd0236fb9a04de04d968e
目录Node.js 实现抢票小工具&短信通知提醒(上)「干货」本篇
Node.js 实现抢票小工具&短信通知提醒(下)「干货」
写在前言要知道在深圳上班是非常痛苦的事情,特别是我上班的科兴科技园这一块,去的人非常多,每天上班跟春运一样,如果我能换到以前的大冲上班那就幸福了,可惜,换不得。
尤其是我这个站等车的多的一笔,上班公交挤的不行,车满的时候只有少部分人能硬挤上去。通常我只会用两个字来形容这种人:“公交怪”
想当年我朋友瘦的像只猴还能上去,老子身高182体重72kg挤个公交,不成问题,反手一个阻挡,闷声发大财,前面的阿姨你快点阿姨,别磨磨唧唧的,快上去啊阿姨,嗯?你还想挤掉我?你能挤掉我?你能挤掉我!我当场!把车吃了!
....
咳咳,挤公交是不可能挤公交滴,因为今天我发现了一个可以定制路线的网约巴士公众号【xxxxxx】
但是呢,票经常会被抢光,同时我还我发现,有时候会有人退票,这时候就有空余票了,关键是我不可能时时都在公众号上盯着,于是,我就写了一个抢票+短信通知的小工具
获取接口信息查看页面结构这个就是订票页面,显示当前月的车票情况,根据图示,红色为已满,绿色为已购,灰色为不可选
如果是可选就是白色的小方块,并且在下面显示余票,如下图所示:
我们打算这么做,
定时抓取返回的接口信息根据接口返回值判断是否有余票好,审查下源代码看下接口信息,等等,微信浏览器没办法审查源代码,于是
使用chrome 调试微信公众号网页页面首先面临个问题,如果直接copy公众号网页Url在chrome打开的话,就会显示这个画面,他被302重定向到了这个页面,所以是行不通的,只有获取OAuth2.0授权才能进去
所以我们得先通过抓包工具,知道手机访问微信公众号网页的时候,需要带什么信息过去,这时候我们就得借助抓包工具,因为我电脑是Mac,用不了Fiddler,我用的是Charles花瓶,就是下面这位仁兄
借助这个工具,我们只需3步就可以轻松搞定手机数据抓包:
获取本机IP地址和端口设置代理手机上网依次执行上面两步获取本机IP地址和端口第一步,找到端口号,一般默认是8088,但是为了确认可以打开Proxy/Proxy Setting看下,哦原来我之前设置成了8888
然后找到Charles的help/Local IP Address,点击它就会看到自己的本机地址,找到本机地址记下来,然后进行下一步
设置代理手机上网首先保证手机跟电脑连接的是同一个wifi,然后在wifi设置那里会有设置代理信息,比如我的小米...不对,小米9手机!设置如下:
输入上一步获取主机名,端口号就ok了
输入完成,点击确定后。Charles就会弹出一个对话框,问你是否同意接入代理,点击确定allow就行了。
用手机访问目标网页我们用手机访问微信公众号【xxxx】进入到抢票页面后,发现Charles已经成功抓包到了网页信息,当我们进入这个抢票页面的时候,他会发起两个请求,一个是获取document文档内容,一个post请求获取票务信息。
仔细分析了下,大概明白了业务逻辑:
整个项目技术站是java+jsp,传统写法,用户身份验证主要是cookie+session方案,前端这一块主要是使用jQuery。
当用户进入页面的时候,会携带查询参数,如起始站点,时间,车次等信息和cookie请求document文档, 也就是圈起来的这一块,
而我们想要的核心内容:日历表,一开始是不显示的
因为还要再请求一次
第二次请求,携带cookie和以上的查询参数发起一个post请求,获取当月的车票信息,也就是日历表内容
下面这个是请求当月票务信息,然而发现他返回的是一堆html节点
好吧...估计是获取到之后直接append到div里面的,然后渲染生成日历表内容
接着在手机上操作,选择两个日期,然后点击下单,发送购票请求,拉取购票接口,我们看下购票接口的请求和返回内容:
看下request 内容,根据字段的意思大概明白是线路,时间,以及车票金额,还有支付方式
再看看返回的内容:返回一个json字符串数据,里面大概涵盖了下单的成功返回码,时间,id号等等信息
记录所需要的信息内容根据上面的分析,总结下内容: 整个项目用户身份验证是使用cookie和session方案,请求数据用的是form data方式,请求字段啥的我们也都清楚,唯独有一点,就是请求余票的时候,返回的是html节点代码,而不是我们预期的json数据,这样就有个麻烦,我们没办法一目了然的明白他余票的时候是如何显示的
所以我们只能通过chrome进行调试,才能得出他是如何判断余票的。
我们找个记事本,记录下信息,记录的内容有:
请求余票接口和购票接口的url地址cookie信息各自的request参数字段user-Agent信息各自的response返回内容设置chrome有以上信息后,我们就可以开始用chrome调试了, 首先打开More tools/Network conditions
把user-Agent填入到Custom里面
Charles抓包本地请求因为我们要把获取到的cookie填入到chrome里面,以我们的用户身份去访问网页,所以我们需要在请求目标地址的时候,改包修改cookie
首先我们需要开启 macOS Proxy,抓包我们的http请求
打开chrome访问目标网址,我们可以看到Charles上已经抓包到了我们访问的目标url地址,然后给目标url地址打上断点,方便调试
然后再次访问,这时候断点就生效了,弹出一个tab名为break points,可以看到之所以我们还是不能访问到目标网址,是因为sessionId不对,所以我们把抓取到的cookie再填入到里面,点击execute
这时候,能够正确跳到目标页面了。
大概看了下他整体布局,和jQuery代码CSS代码,特别是日历表那一块
审查了下元素发现:
小方块的结构为:<td class="b"><span>这里为日期</span><span>如果有余票则显示余票数量</span></td>复制代码td的样式名为a代表不可选样式名为e代表已满样式名为d代表已购样式名为b则是我们要找的,代表可选,也就是有余票
到这一步,整个购票流程就清楚了
到时候我们通过Node.js请求的时候,处理返回数据,用正则去判断是否有余票的class名b ,有余票的话,在获取div里面的余票数量内容就Ok了
Node.js 请求目标接口分析需要开发的功能点写代码之前我们需要想好功能点,我们需要什么功能:
请求余票接口定时请求任务有余票则自动请求购票接口下订单调用腾讯云短信api接口发送短信通知多个用户抢票功能抢某个日期的票首先mkdir ticket 创建名为ticket的文件夹,接着cd ticket进入文件夹npm init一路瞎几把回车也无妨。 下面开始安装依赖,根据上面的功能需求,我们大概需要:
请求工具,这里看个人习惯,你也可以使用原生的http.request,我这里选择用的是axios,毕竟axios在node端底层也是调用http.requestcnpm install axios --save复制代码定时任务 node-schedule
cnpm install node-schedule --save复制代码node端选择dom节点工具 cheerio
cnpm install cheerio --save复制代码腾讯发短信的依赖包 qcloudsms_js
cnpm install qcloudsms_js 复制代码热更新包,诺豆的妈妈,nodemon (其实不用也可以)
cnpm install nodemon --save-dev复制代码开发请求余票接口
接着touch index.js创建核心js文件,开始编码:
首先引入所有依赖
const axios = require('axios')const querystring = require("querystring"); //序列化对象,用qs也行,都一样let QcloudSms = require("qcloudsms_js");let cheerio = require('cheerio');let schedule = require('node-schedule');复制代码
然后我们先定义请求参数,来一个obj
let obj = { data: { lineId: 111130, //路线id vehTime: 0722, //发车时间, startTime: 0751, //预计上车时间 onStationId: 564492, //预定的站点id offStationId: 17990,//到站id onStationName: '宝安交通运输局③', //预定的站点名称 offStationName: "深港产学研基地",//预定到站名称 tradePrice: 0,//总金额 saleDates: '17',//车票日期 beginDate: '',//订票时间,滞空,用于抓取到余票后填入数据 }, phoneNumber: 123123123, //用户手机号,接收短信的手机号 cookie: 'JSESSIONID=TESTCOOKIE', // 抓取到的cookie day: "17" //定17号的票,这个主要是用于抢指定日期的票,滞空则为抢当月所有余票}复制代码
接着声明一个名为queryTicket的类,为啥要用类呢,因为基于第五个需求点,多个用户抢票的时候,我们分别new一下就行了,
同时我们希望能够记录请求余票的次数,和当抢到票后自动停止查询余票的操作,所以给他加上个计数变量times和是否停止的变量,布尔值stop
编写代码:
class QueryTicket{ /** *Creates an instance of QueryTicket. * @param {Object} { data, phoneNumber, cookie, day } * @param data {Object} 请求余票接口的requery参数 * @param phoneNumber {Number} 用户手机号,短信需要用到 * @param cookie {String} cookie信息 * @params day {String} 某日的票,如'18' * @memberof QueryTicket 请求余票接口 */ constructor({ data, phoneNumber, cookie, day }) { this.data = data thisokie = cookie this.day = day this.phoneNumber = phoneNumber this.postData = querystring.stringify(data) this.times = 0; //记录次数 let stop = false //通过特定接口才能修改stop值,防止外部随意串改 this.getStop = function () { //获取是否停止 return stop } this.setStop = function (ifStop) { //设置是否停止 stop = ifStop } }}复制代码
下面开始定义原型方法,为了方便维护,我们把逻辑拆分成各个函数
class QueryTicket{ constructor({ data, phoneNumber, cookie, day }) { //constructor代码... } init(){}//初始化 handleQueryTicket(){}//查询余票的逻辑 requestTicket(){} //调用查询余票接口 handleBuyTicket(){} //购票相关逻辑 requestOrder(){}//调用购票接口 handleInfoUser(){}//通知用户的逻辑 sendMSg(){} //发短信接口}复制代码
所有数据都是基于查询余票的操作,因此我们先开发这部分功能
class QueryTicket{ constructor({ data, phoneNumber, cookie, day }) { //constructor代码... } //初始化,因为涉及到异步请求,所以我们使用`async await` async init(){ let ticketList = await this.handleQueryTicket() //返回查询到的余票数组 } //查询余票的逻辑 handleQueryTicket(){ let ticketList = [] //余票数组 let res = await this.requestTicket() this.times++ //计数器,记录请求查询多少次 let str = res.data.replace(/\\/g, "") //格式化返回值 let $ = cheerio.load(`<div class="main">${str}</div>`) // cheerio载入查询接口response的html节点数据 let list = $(".main").find(".b") //查找是否有余票的dom节点 // 如果没有余票,打印出请求多少次,然后返回,不执行下面的代码 if (!list.length) { console.log(`用户${this.phoneNumber}:无票,已进行${this.times}次`) return } // 如果有余票 list.each((idx, item) => { let str = $(item).html() //str这时格式是<span>21</span><span>&$x4F59;0</span> //最后一个span 的内容其实"余0",也就是无票,只不过是被转码了而已 //因此要在下一步对其进行格式化 let arr = str.split(/<span>|<\/span>|\&\#x4F59\;/).filter(item => !!item === true) let data = { day: arr[0], ticketLeft: arr[1] } //如果是要抢指定日期的票 if (this.day) { //如果有指定日期的余票 if (parseInt(data.day) === parseInt(data.day)) { ticketList.push(data) } } else { //如果不是,则返回查询到的所有余票 ticketList.push(data) } }) return ticketList } //调用查询余票接口 requestTicket(){ return axios.post('/d/file/gt/2023-10/cq2sopwskxt.do this.postData, { headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': "Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12A365 MicroMessenger/5.4.1 NetType/WIFI", "Cookie": thisokie } }) } handleBuyTicket(){} //购票相关逻辑 requestOrder(){}//调用购票接口 handleInfoUser(){}//通知用户的逻辑 sendMSg(){} //发短信接口}复制代码
来解释下那行正则,cheerio抓取到的dom是长这样的,第一个span内容是日期,第二个是余票数量
所以我们要把它格式化变成这种数组,也就是ticketList
本篇文章未完结,请见下一篇
推荐JavaScript学习相关文章《那些不常见,但却非常实用的JS知识点(上)》
《那些不常见,但却非常实用的JS知识点(下)》
《如何为团队定制自己的 Node.js 框架?》
《细品原生JS从初级到高级知识点汇总(一)》
《细品原生JS从初级到高级知识点汇总(二)》
《细品原生JS从初级到高级知识点汇总(三)》
《细品原生JS从初级到高级知识点汇总(四)》
《学习 jQuery 源码整体架构,打造属于自己的 js 类库》
《细说RTSP实现前端直播流探索记「干货」》
《一文带你搞懂前端登陆设计》
《使用 Node.js 将图片中的苹果变成橘子「实践」》
《基于Canvas实现的高斯模糊(上)「JS篇」》
《基于Canvas实现的高斯模糊(下)「JS篇」》
《由浅入深,66条JavaScript面试知识点(一)》
《由浅入深,66条JavaScript面试知识点(二)》
《由浅入深,66条JavaScript面试知识点(三)》
《由浅入深,66条JavaScript面试知识点(四)》
《由浅入深,66条JavaScript面试知识点(五)》
《由浅入深,66条JavaScript面试知识点(六)》
《由浅入深,66条JavaScript面试知识点(七)》
《为什么 setTimeout 有最小时延 4ms ?》
《如何处理 Node.js 中出现的未捕获异常?》
《Angular v10.0.0 正式发布,不再支持 IE9/10》
《基于 Docker 的 SSR 持续开发集成环境实践》
《细聊图解webpack 指南手册》
《一文带你彻底搞懂 NPM 知识点「进阶篇」》
《细聊webpack性能优化面面观》
《JS实现各种日期操作方法汇总》
《「实践」细聊前端性能优化总结》
《「实践」浏览器中的画中画(Picture-in-Picture)模式及其 API》
《「多图」一文带你彻底搞懂 Web Workers (上)》
《「多图」一文带你彻底搞懂 Web Workers (中)》
《深入细聊前端下载总结「干货」》
《细品西瓜播放器功能分析(上)「实践」》
《细品西瓜播放器功能分析(下)「实践」》
《细聊50道JavaScript基础面试题「附答案」》
《webpack4主流程源码解说以及动手实现一个简单的webpack(上)》
《webpack4主流程源码解说以及动手实现一个简单的webpack(下)》
《细聊前端架构师的视野》
《细聊应用场景再谈防抖和节流「进阶篇」》
《前端埋点统一接入方案实践》
《细聊微内核架构在前端的应用「干货」》
《一种高性能的Tree组件实现方案「干货」》
《进击的JAMStack》
《前后端全部用 JS 开发是什么体验(Hybrid + Egg.js经验分享)上》
《前后端全部用 JS 开发是什么体验(Hybrid + Egg.js经验分享)中》
《前后端全部用 JS 开发是什么体验(Hybrid + Egg.js经验分享)下》
《一文带你搞懂 babel-plugin-import 插件(上)「源码解析」》
《一文带你搞懂 babel-plugin-import 插件(下)「源码解析」》
《JavaScript常用API合集汇总「值得收藏」》
《推荐10个常用的图片处理小帮手(上)「值得收藏」》
《推荐10个常用的图片处理小帮手(下)「值得收藏」》
《JavaScript 中ES6代理的实际用例》
《12 个实用的前端开发技巧总结》
《一文带你搞懂搭建企业级的 npm 私有仓库》
《教你如何使用内联框架元素 IFrames 的沙箱属性提高安全性?》
《细说前端开发UI公共组件的新认识「实践」》
《细说DOM API中append和appendChild的三个不同点》
《细品淘系大佬讲前端新人如何上王者「干货」》
《一文带你彻底解决背景跟随弹窗滚动问题「干货」》
《推荐常用的5款代码比较工具「值得收藏」》
《Node.js实现将文字与图片合成技巧》
《爱奇艺云剪辑Web端的技术实现》
《我再也不敢说我会写前端 Button组件「实践」》
《NodeX Component - 滴滴集团 Node.js 生态组件体系「实践」》
《Node Buffers 完整指南》
《推荐18个webpack精美插件「干货」》
《前端开发需要了解常用7种JavaScript设计模式》
《浅谈浏览器架构、单线程js、事件循环、消息队列、宏任务和微任务》
《了不起的 Webpack HMR 学习指南(上)「含源码讲解」》
《了不起的 Webpack HMR 学习指南(下)「含源码讲解」》
《10个打开了我新世界大门的 WebAPI(上)「实践」》
《10个打开了我新世界大门的 WebAPI(中)「实践」》
《10个打开了我新世界大门的 WebAPI(下)「实践」》
《「图文」ESLint 在中大型团队的应用实践》
《Deno是代码的浏览器,你认同吗?》
《前端存储除了 localStorage 还有啥?》
《Javascript 多线程编程的前世今生》
《微前端方案 qiankun(实践及总结)》
《「图文」V8 垃圾回收原来这么简单?》
《Webpack 5模块联邦引发微前端的革命?》
《基于 Web 端的人脸识别身份验证「实践」》
《「前端进阶」高性能渲染十万条数据(时间分片)》
《「前端进阶」高性能渲染十万条数据(虚拟列表)》
《图解 Promise 实现原理(一):基础实现》
《图解 Promise 实现原理(二):Promise 链式调用》
《图解 Promise 实现原理(三):Promise 原型方法实现》
《图解 Promise 实现原理(四):Promise 静态方法实现》
《实践教你从零构建前端 Lint 工作流「干货」》
《高性能多级多选级联组件开发「JS篇」》
《深入浅出讲解Node.js CLI 工具最佳实战》
《延迟加载图像以提高Web网站性能的五种方法「实践」》
《比较 JavaScript 对象的四种方式「实践」》
《使用Service Worker让你的 Web 应用如虎添翼(上)「干货」》
《使用Service Worker让你的 Web 应用如虎添翼(中)「干货」》
《使用Service Worker让你的 Web 应用如虎添翼(下)「干货」》
《前端如何一次性处理10万条数据「进阶篇」》
《推荐三款正则可视化工具「JS篇」》
《如何让用户选择是否离开当前页面?「JS篇」》
《JavaScript开发人员更喜欢Deno的五大原因》
《仅用18行JavaScript实现一个倒数计时器》
《图文细说JavaScript 的运行机制》
《一个轻量级 JavaScript 全文搜索库,轻松实现站内离线搜索》
《推荐Web程序员常用的15个源代码编辑器》
《10个实用的JS技巧「值得收藏」》
《细品269个JavaScript小函数,让你少加班熬夜(一)「值得收藏」》
《细品269个JavaScript小函数,让你少加班熬夜(二)「值得收藏」》
《细品269个JavaScript小函数,让你少加班熬夜(三)「值得收藏」》
《细品269个JavaScript小函数,让你少加班熬夜(四)「值得收藏」》
《细品269个JavaScript小函数,让你少加班熬夜(五)「值得收藏」》
《细品269个JavaScript小函数,让你少加班熬夜(六)「值得收藏」》
《深入JavaScript教你内存泄漏如何防范》
《手把手教你7个有趣的JavaScript 项目-上「附源码」》
《手把手教你7个有趣的JavaScript 项目-下「附源码」》
《JavaScript 使用 mediaDevices API 访问摄像头自拍》
《手把手教你前端代码如何做错误上报「JS篇」》
《一文让你彻底搞懂移动前端和Web 前端区别在哪里》
《63个JavaScript 正则大礼包「值得收藏」》
《提高你的 JavaScript 技能10 个问答题》
《JavaScript图表库的5个首选》
《一文彻底搞懂JavaScript 中Object.freeze与Object.seal的用法》
《可视化的 JS:动态图演示 - 事件循环 Event Loop的过程》
《教你如何用动态规划和贪心算法实现前端瀑布流布局「实践」》
《可视化的 js:动态图演示 Promises & Async/Await 的过程》
《原生JS封装拖动验证滑块你会吗?「实践」》
《如何实现高性能的在线 PDF 预览》
《细说使用字体库加密数据-仿58同城》
《Node.js要完了吗?》
《Pug 3.0.0正式发布,不再支持 Node.js 6/8》
《纯JS手写轮播图(代码逻辑清晰,通俗易懂)》
《JavaScript 20 年 中文版之创立标准》
《值得收藏的前端常用60余种工具方法「JS篇」》
《箭头函数和常规函数之间的 5 个区别》
《通过发布/订阅的设计模式搞懂 Node.js 核心模块 Events》
《「前端篇」不再为正则烦恼》
《「速围」Node.js V14.3.0 发布支持顶级 Await 和 REPL 增强功能》
《深入细品浏览器原理「流程图」》
《JavaScript 已进入第三个时代,未来将何去何从?》
《前端上传前预览文件 image、text、json、video、audio「实践」》
《深入细品 EventLoop 和浏览器渲染、帧动画、空闲回调的关系》
《推荐13个有用的JavaScript数组技巧「值得收藏」》
《前端必备基础知识:window.location 详解》
《不要再依赖CommonJS了》
《犀牛书作者:最该忘记的JavaScript特性》
《36个工作中常用的JavaScript函数片段「值得收藏」》
《Node + H5 实现大文件分片上传、断点续传》
《一文了解文件上传全过程(1.8w字深度解析)「前端进阶必备」》
《【实践总结】关于小程序挣脱枷锁实现批量上传》
《手把手教你前端的各种文件上传攻略和大文件断点续传》
《字节跳动面试官:请你实现一个大文件上传和断点续传》
《谈谈前端关于文件上传下载那些事【实践】》
《手把手教你如何编写一个前端图片压缩、方向纠正、预览、上传插件》
《最全的 JavaScript 模块化方案和工具》
《「前端进阶」JS中的内存管理》
《JavaScript正则深入以及10个非常有意思的正则实战》
《前端面试者经常忽视的一道JavaScript 面试题》
《一行JS代码实现一个简单的模板字符串替换「实践」》
《JS代码是如何被压缩的「前端高级进阶」》
《前端开发规范:命名规范、html规范、css规范、js规范》
《【规范篇】前端团队代码规范最佳实践》
《100个原生JavaScript代码片段知识点详细汇总【实践】》
《关于前端174道 JavaScript知识点汇总(一)》
《关于前端174道 JavaScript知识点汇总(二)》
《关于前端174道 JavaScript知识点汇总(三)》
《几个非常有意思的javascript知识点总结【实践】》
《都2020年了,你还不会JavaScript 装饰器?》
《JavaScript实现图片合成下载》
《70个JavaScript知识点详细总结(上)【实践】》
《70个JavaScript知识点详细总结(下)【实践】》
《开源了一个 JavaScript 版敏感词过滤库》
《送你 43 道 JavaScript 面试题》
《3个很棒的小众JavaScript库,你值得拥有》
《手把手教你深入巩固JavaScript知识体系【思维导图】》
《推荐7个很棒的JavaScript产品步骤引导库》
《Echa哥教你彻底弄懂 JavaScript 执行机制》
《一个合格的中级前端工程师需要掌握的 28 个 JavaScript 技巧》
《深入解析高频项目中运用到的知识点汇总【JS篇】》
《JavaScript 工具函数大全【新】》
《从JavaScript中看设计模式(总结)》
《身份证号码的正则表达式及验证详解(JavaScript,Regex)》
《浏览器中实现JavaScript计时器的4种创新方式》
《Three.js 动效方案》
《手把手教你常用的59个JS类方法》
《127个常用的JS代码片段,每段代码花30秒就能看懂-【上】》
《深入浅出讲解 js 深拷贝 vs 浅拷贝》
《手把手教你JS开发H5游戏【消灭星星】》
《深入浅出讲解JS中this/apply/call/bind巧妙用法【实践】》
《手把手教你全方位解读JS中this真正含义【实践】》
《书到用时方恨少,一大波JS开发工具函数来了》
《干货满满!如何优雅简洁地实现时钟翻牌器(支持JS/Vue/React)》
《手把手教你JS 异步编程六种方案【实践】》
《让你减少加班的15条高效JS技巧知识点汇总【实践】》
《手把手教你JS开发H5游戏【黄金矿工】》
《手把手教你JS实现监控浏览器上下左右滚动》
《JS 经典实例知识点整理汇总【实践】》
《2.6万字JS干货分享,带你领略前端魅力【基础篇】》
《2.6万字JS干货分享,带你领略前端魅力【实践篇】》
《简单几步让你的 JS 写得更漂亮》
《恭喜你获得治疗JS this的详细药方》
《谈谈前端关于文件上传下载那些事【实践】》
《面试中教你绕过关于 JavaScript 作用域的 5 个坑》
《Jquery插件(常用的插件库)》
《【JS】如何防止重复发送ajax请求》
《JavaScript+Canvas实现自定义画板》
《Continuation 在 JS 中的应用「前端篇」》
作者:西岚转发链接:https://juejin.im/post/5dadd0236fb9a04de04d968e
饿了么app-会员中心-如图步骤领取
其他羊毛look图二呀