首页
Search
1
解决visual studio code (vscode)安装时没有选择安装路径问题
321 阅读
2
如何在 Clash for Windows 上配置服务
216 阅读
3
Linux 下 Bash 脚本 bad interpreter 报错的解决方法
150 阅读
4
Arch Linux 下解决 KDE Plasma Discover 的 Unable to load applications 错误
149 阅读
5
uniapp打包app提示通讯录权限问题,如何取消通讯录权限
113 阅读
clash
服务器
javascript
全部
游戏资讯
登录
Search
加速器之家
累计撰写
1,202
篇文章
累计收到
0
条评论
首页
栏目
clash
服务器
javascript
全部
游戏资讯
页面
搜索到
1202
篇与
的结果
2024-10-21
service worker在新闻红包活动中的应用
首先声明下我们组的技术负担:前端没有自己的服务器,是依托于新闻 CMS 发布系统的。在这种情况下如何将 service worker 应用在新闻红包活动中呢? 1. 技术支持程度 # 活动页面大多是单页面应用,加载 js 后,再通过 js 进行页面的渲染,因此 js 的加载速度直接影响了页面呈现的速度。这里是想通过 service worker 缓存静态资源,期望能直接从缓存中加载静态资源,加载页面的呈现速度!在news.qq.com域名全面支持 https 后,service worker 就可以提上日程了。首先进行技术支持程度的检测,使用'serviceWorker' in navigator检测功能的支持。在 iOS12.0 中,微信、QQ 和新闻客户端,均不支持 service worker,safari 支持;在 Android 中,微信、QQ 和新闻客户端都是支持的。因此我们着重于 Android 下 service worker 的使用。同时,Android 测试版新闻客户端,可是使用 USB 进行调试,调试 service worker 的工作状态。 2. 活动中的使用 # 2.1 尝试缓存相对路径的静态资源 # 刚开始在缓存资源时,因为知识储备的不足,钻了牛角尖,只想着缓存相对路径的资源,毕竟 cms 静态服务也是支持静态资源的相对路径的,可是在实验时发现,无论字段iscdn是否为 true,图片类型的资源都会自动上传到 CDN,那么就会造成 js/css 的地址和图片资源的地址不一致,毕竟 baseUrl 只能指定一个值,要么是 js/css 地址,要么是图片资源的地址。刚开始想的是改造脚手架,用两个值分别来表示 js/css 地址和图片资源地址,但是脚手架的改造成本也是很高的! 2.2 缓存 CDN 中的资源 # 代码架构中的资源都上传到 CDN,那么 js/css/img 都会在mat1.gtimg.com的 CDN 目录下,如果要加载并缓存 CDN 中的资源,CDN 资源服务器的 response 中 Access-Control-Allow-Origin 中包含当前的页面所在域或者为*;幸运的是,mat1.gtimg.com 服务器是支持的:response:access-control-allow-origin: https://news.qq.com access-control-expose-headers: X-Client-Ip,X-Server-Ip,X-Upstream-Ip cache-control: max-age=60 content-encoding: gzip content-length: 50356 content-type: application/javascript; date: Tue, 15 Jan 2019 09:02:29 GMT expires: Tue, 15 Jan 2019 09:03:29 GMT last-modified: Tue, 15 Jan 2019 02:53:58 GMT server: NWSs status: 200 vary: Origin 因此我们可以在 install 中首先这个 CDN 下的静态资源进行离线缓存,然后在 fetch 中拦截请求,如果命中缓存则直接返回,如果没有命中,则去请求!在请求可控的跨域资源时,需要在请求头中加入{mode: 'cors'}在 install 中,对首页和首页中一定会用到的静态资源进行缓存:const version = 'welfare-1.0.0'; const fileList = [ '/a/b/index.htm', 'https://mat1.gtimg.com/news/chunk-vendors.f9bab06e.js', 'https://mat1.gtimg.com/news/app.2677f99a.css', 'https://mat1.gtimg.com/news/app.af990eeb.js', ]; this.addEventListener('install', (event) => { this.skipWaiting(); event.waitUntil( caches.open(version).then((cache) => { return cache.addAll(fileList); }), ); }); 然后在 fetch 中拦截:// 检测当前请求是否应当被缓存 const checkHttpCanCache = (request, response) => { // 数据统计、请求的接口不缓存 // 同时若请求失败或者请求的资源无法控制,也不缓存 if ( request.url.indexOf('btrace.qq.com/kvcollect') > -1 || !response || response.status !== 200 || (response.type !== 'basic' && response.type !== 'cors') ) { return false; } return true; }; this.addEventListener('fetch', event => { let url = event.request.url; let req; if (url.indexOf('mat1.gtimg.com') > -1) { req = new Request(url, { mode: 'cors' }); } else { req = event.request.clone(); } event.respondWith( caches.match(event.request).then(response => { // 缓存中存在则直接返回 if (response) { return response; } // 否则进行请求并返回,这里还可以再多一步,如果可以缓存的资源,也进行缓存 let request = req.clone(); return fetch(request).then(httpRes => { if (!checkHttpCanCache(request, httpRes)) { return httpRes; } // 请求成功的话,将请求缓存起来 let responseClone = httpRes.clone(); caches.open(version).then(cache => { cache.put(request, responseClone); }); return httpRes; }); }); ); }); 不过上面的 fetch 事件还不完整,如果页面中有的跨域资源的Access-Control-Allow-Origin不支持时,请求资源就会报错,会被浏览器阻止。比如展示列表中的图片,产品经理那边是把图片上传到img1.gtimg.com域名上,但是这个域名的 Access-Control-Allow-Origin 不支持。关于这个域名下的请求,是这样处理的,需要在 mode 中加上no-cors:if (url.indexOf('mat1.gtimg.com') > -1) { req = new Request(url, { mode: 'cors' }); } else if (url.indexOf('img1.gtimg.com') > -1) { req = new Request(url, { mode: 'no-cors' }); } else { req = event.request.clone(); } 2.3 验证 service woker 是否起作用 # 有时候会遇到这种情况,Cache Storage 中明明已经有缓存了,但是每次刷新时,缓存不起作用,还是每次都走网络请求。针对这种情况,应当检查下当前页面是否是https链接,同时检查 sw.js 所在的路径是否跟当前页面地址在同一个目录中,比如页面路径为https://news.qq.com/a/b/index.htm,那么 sw.js 的路径也最好是/a/b/sw.js。service worker 是否已生效,可以在 chrome 的 network 面板中查看,强刷缓存后,如果 Size 标识为(from ServiceWorker),则说明 sw 已生效;将网络设置为offline时,页面也依然能访问,service worker 中缓存的数据能把首页的大致框架撑起来,只是没有接口数据了而已! 3. 总结 # service worker 能做的工作除了缓存资源,还有更多的功能可以使用,比如使用 service worker 模拟接口数据,在接口没有开发完成时,可以利用这个进行正常的对接接口开发等。
2024年10月21日
12 阅读
0 评论
0 点赞
2024-10-21
第11页
Front-end Engineer,前端开发工程师目前是一名前端开发工程师,主要负责前端规划、框架与架构、前端性能优化。专注前端技术,关注交互体验,擅长web ajax开发。坚信前端工程师的价值是最终能把技术和设计完美结合在一起。用最新的技术方案巧妙地帮助这些设计得以实现。
2024年10月21日
7 阅读
0 评论
0 点赞
2024-10-20
getScript在前端中请求js文件和jsonp
getScript在前端中使用的非常广泛,那么这里也分享下自己使用的ts版本的getScript:/** * 加载js文件 * @param {string} url 要加载的js文件链接 * @param {function} callback 回调函数 * @returns {Promise} 返回一个Promise对象,若加载失败或者超时,则reject */ const getScript = (url: string, callback: ()=>{}): Promise => { const head = document.getElementsByTagName('head')[0]; const script = document.createElement('script'); const timeout: number = 5000; // 过期时间 let timer: number; script.setAttribute('type', 'text/javascript'); script.setAttribute('charset', 'UTF-8'); script.setAttribute('src', url); if (sid) { script.setAttribute('id', sid); } const cleanup = () => { if (script.parentNode) { script.parentNode.removeChild(script); } if (timer) { clearTimeout(timer); } }; head.appendChild(script); return new Promise((resolve, reject) => { // 执行回调 const callbackFn = () => { if (timer) { clearTimeout(timer); } callback(); resolve(); }; script.onload = () => { callbackFn(); }; if (timeout) { timer = setTimeout(() => { cleanup(); reject(new Error(`get ${url} timeout`)); }, timeout); } }); }; 正常请求一个js文件,然后设置了一个5000ms的过期时间,若请求资源不成功,则进入到reject中。同时,getScript支持使用callback和Promise两种方式执行结果的回调,不过callback的方式无法获取到超时的情况:getScript('http://mat1.gtimg.com/libs/jquery2/2.2.0/jquery.min.js').then((jQuery: any)=>{ console.log(jQuery.fn.jquery); }).catch(e=>console.error('请求超时')); 这种方式,其实稍微改造一下,也是可以直接用来跨域,请求支持jsonp接口的。在url的后面添加上callback的请求参数,然后在回调里执行这个callback。 例如:const getScript = (url: string, data: {}): Promise => { const head = document.getElementsByTagName('head')[0]; const script = document.createElement('script'); const timeout: number = 5000; // 过期时间 let timer: number; let fecthCallBack: string = 'cb' + Date.now() + '_' + ('' + Math.random()).substr(-8); const cleanup = () => { if (script.parentNode) { script.parentNode.removeChild(script); } window[fecthCallBack] = () => {}; delete window[fecthCallBack]; if (timer) { clearTimeout(timer); } }; head.appendChild(script); // 将Object类型的数据转换为URL链接需要的参数 let paramObj = (params: any) => { let str = '', key, item; for (key in params) { if (typeof params[key] === 'object') { item = JSON.stringify(params[key]); } else { item = params[key]; } str += `&${key}=${encodeURIComponent(item)}`; } return str.substring(1); }; url += (~url.indexOf('?') ? '&' : '?') + paramObj(data) + '&callback=' + fecthCallBack; script.setAttribute('type', 'text/javascript'); script.setAttribute('charset', 'UTF-8'); script.setAttribute('src', url); return new Promise((resolve, reject) => { window[fecthCallBack] = (data: any) => { cleanup(); resolve(data); }; if (timeout) { timer = setTimeout(() => { cleanup(); reject(new Error('Timeout')); }, timeout); } }); };
2024年10月20日
16 阅读
0 评论
0 点赞
2024-10-20
js中parseInt与Math.floor的区别
网上有介绍parseInt与Math.floor区别的内容已经不少了,这里我还是想说下自己在使用过程中的区别 parseInt # 我们平时在使用parseInt时,主要是为了数据中的整数部分。但实际上,在官方的定义里,parseInt的第一个参数应当是string类型的。 parseInt(string, radix); 尤其是在ts中使用parseInt会更加明显,若直接传入一个数字类型的参数,则编辑器会提示此处应当传入一个字符串类型,这时就要求我们手动的转为字符串类型,比如使用num+''或者num.toString()。在不严格的情况下,js语言会自动进行隐式的类型转换。而且,使用parseInt获取整数部分时,是直接将小数部分扔掉:parseInt('12.3'); // 12 parseInt('6.9'); // 6 parseInt('4'); // 4 parseInt('-1.2'); // -1 parseInt('-1.9'); // -1 parseInt最大的一个功能是,可以将字符串类型的数字其他进制的数据转为十进制的数据。同时,parseInt也能让我们从数字和其他字母混合的字符串中提取出数字来,若以非数字类型开始的字符串,则无法转为数字,若以数字开头的字符串,则返回第一个非数字字符前所有的数字字符:parseInt('11', 8); // 9, 8进制中的11即为10进制中的9 parseInt('123.456'); // 123 parseInt('a123'); // NaN parseInt('123a456'); // 123 这种情况有一个使用的场景,比如有一个DOM元素上有style属性 我们在使用style获取高度时会带有单位,那么这里就可以用parseInt获取到纯数字(当然,我们还有其他方式来获取不带单位的数据,这里仅仅是举个例子):let height = document.querySelector('.wenzi').style.height; console.log( height ); // '200px' console.log( parseInt(height) ); // 200 Math.floor # Math.floor是专门用来操作数字的,该功能是获取小于等于参数的整数(向下取整),例如:Math.floor(12.3); // 12 Math.floor(6.9); // 6 Math.floor(4); // 4 Math.floor(-1.2); // -2 Math.floor(-1.9); // -2 可以看到,在获取整数部分这个功能,与parseInt对比发现,对于非负数来说效果是一样的,但是对于负数来讲,floor会向更小数字的方向寻找。若传入的参数不是数字类型的,返回的则是NaN。 建议在获取非负数的整数部分时,使用Math.floor 总结 # 若是操作纯数字型的数据,建议使用Math下的方法,若字符串类型的数字再使用parseInt,更加语义化一些。
2024年10月20日
4 阅读
0 评论
0 点赞
2024-10-20
博客里评论系统的前端总结
在之前的一篇文章里,曾经介绍过本博客的楼中楼评论系统的实现:如何实现一个楼中楼的评论系统,那时候主要是对后端系统和数据库设计的介绍,本篇文章主要是从前端的角度讲解评论系统的实现!评论系统最主要的两块是发表评论和评论列表,同时呢,发表评论成功后,刷新评论列表主要也是有两种方式: ajax的方式刷新,直接追加在特定的位置; 刷新整个页面,重新获取评论,然后定位到刚才发表的评论的位置; 本小蚊子博客的评论系统,是以ajax的方式获取评论列表,同时在发表评论成功后,也是以异步的方式更新评论列表。Vue组件的结构是:Comments.vue Publish.vue List.vue Unit.vue Publish.vue 1. 如何发表评论 # 用户对文章或者某个评论产生了共鸣,需要留言讨论一番,我们就需要用户能够把自己的评论也添加进去。评论的类型,细分的话,可以分为3类: 直接对文章发表评论,pid与replyid为空; 对一级评论进行回复,pid与replyid均为一级评论的id; 对楼中楼进行回复,pid为一级评论的id,replyid为你回复的评论的id 我这里前端的实现参考了oschina(开源中国)的评论方式。直接对文章评论,是直接在顶部的评论窗口进行输入;对其他评论进行回复时,采用弹窗的方式来进行回复。弹窗回复的好处就是,页面不用滚动,用户对某个评论的感知也能停留在这个位置;同时也不用增加各种不必要的小输入框来让用户。所有发表评论用到的组件都是同一个(Publish.vue),只不过表现的方式不一样而已。在发表评论成功后,通过事件通知机制$emit,更新数据结构list数组。当直接对文章评论时,使用splice操作list数组即可:this.list.splice(0, 0, item); // 在list数组的最前面添加评论内容 当进行楼中楼的评论时,其实是在item中的reply字段里,我们要先更新reply字段,然后再将item重新更新到list数组中:let item = this.list[index]; item.reply.push(replyItem); this.$set(this.list, index, item); // 或 // this.list.splice(index, 1, item); 在更新楼中楼的评论过程中,之前遇到了一个问题:当出现楼中楼的第一个评论时,数据更新但是页面没有更新,从第2个评论开始就能正常更新评论了!刚开始以为是Vue的字段更新后,导致调用的子组件里的数据不会更新。好一通排查后才发现原因竟然是:楼中楼是否显示使用item.num字段(当前评论里子评论的个数),上面的代码里只是更新了item.reply字段,但是没有更新item.num,导致第一个评论的自动更新老是有问题! 2. 登录的问题 # 之前博客的评论系统,接入的微博登录;一段运行时间后,运行得倒是也没有问题,不过接入微博登录有点大材小用了,最新的评论系统更新时,改为了用户输入昵称和邮箱后即可评论!用户发表评论后,即将用户的昵称和邮箱存储到cookie中,下次直接从cookie里后去即可! 3. 获取评论列表 # 在获取评论列表时,也是监听了页面的滚动事件,同时采用防抖和节流的策略,在用户将页面滚动到底部的时候再获取数据!在获取到数据后,即解绑滚动事件handleScroller() { const scroller = throttle(()=>{ const maxScrollTop = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight) - window.innerHeight; const currentScrollTop = Math.max(document.documentElement.scrollTop, document.body.scrollTop); if (maxScrollTop - currentScrollTop < 100 && !this.loaded) { this.loaded = true; this.getList(); // 获取数据 window.removeEventListener('scroll', scroller); } }, 300, 400); scroller(); window.addEventListener('scroll', scroller); } 4. 总结 # 评论系统是一项比较复杂的系统,我们这里也只是做了简单的工作而已,后续可能也会考虑,全部使用原生的js来实现!
2024年10月20日
6 阅读
0 评论
0 点赞
1
...
59
60
61
...
241