首页
Search
1
解决visual studio code (vscode)安装时没有选择安装路径问题
138 阅读
2
Linux 下 Bash 脚本 bad interpreter 报错的解决方法
131 阅读
3
Arch Linux 下解决 KDE Plasma Discover 的 Unable to load applications 错误
107 阅读
4
如何在 Clash for Windows 上配置服务
77 阅读
5
Uniapp如何引入自定义样式文件
75 阅读
clash
服务器
javascript
全部
游戏资讯
登录
Search
加速器之家
累计撰写
1,061
篇文章
累计收到
0
条评论
首页
栏目
clash
服务器
javascript
全部
游戏资讯
页面
搜索到
1061
篇与
的结果
2024-10-21
Vue: 单页面应用如何保持登录状态
在单页面应用中,若刷新页面后,登录态是保存不了的。比如我在首页获取到了登录信息,然后存储到vuex中,进入到某个路由中,若当前路由刷新后,vuex中的数据就会被清空,以此也会导致当前路由中依赖登录态的接口都会失效。那么,若当前路由需要刷新,或者当前路由存在第三方的跳转链接时,该怎么办呢?目前是有两种思路,可以供大家参考一下: 1. router与vuex的结合 # 如果您的登录态是通过vuex管理的话,那么一般是首页获取到登录信息,然后存储到vuex中,然后其他路由直接使用即可。但是这样存在的问题,就是刷新页面后,vuex的数据就会消失。这里我们需要借助router中的一个方法。在router中有一个beforeEach方法,这个方法在路由发生变化时就会触发,当然,在当前路由刷新时也会触发,那么就可以在这里监听登录状态的变化。首先是在router/index.js中明确哪些路由是需要登录后才能访问的,哪些是可以直接访问的,下面的代码中可以看到我的奖励页面是需要登录才能访问的,而且奖励中有可能会存在第三方的跳转链接:// router/index.js Vue.use(Router); export default new Router({ routes: [ { path: '/', // shouye name: 'home', components: { Home } }, { path: '/myprize', // 我的奖励 name: 'myprize', components: { MyPrize }, meta: { requiresAuth: true } } ] }) 那么我们在router/auth.js这样判断:// router/auth.js import router from './index.js'; // 获取到路由列表 import store from '../store'; // 获取vuex中的store router.beforeEach((to, from, next)=>{ if(to.meta.requiresAuth){ // 若当前页面需要登录 if(store.state.user.nickname){ // 若vuex中存在用户的昵称,则说明当前有登录态 next(); } else { // 通过检测接口检验用户的登录态, // 然后将获取到的信息再次存储到vuex中 User.getUserInfo(result=> { if(result.nickname){ store.commit('setUserInfo', result); next(); } else { // 去登录 } }) } } else { next(); } }) 若vuex中不存在用户的数据,则说明当前页面肯定是刷新后的,或者从别的页面回退回来导致刷新的。那只能从接口中判断登录态了,若检测到存在登录态,则将用户信息重新存储到vuex中,方便下次的使用;若接口里依然没有登录态,则说明用户真的没有登录,只能让用户去登录了。 2. sessionStorage # 我看有的同学说可以用seesionStorage存储登录信息,不过既然已经使用sessionStorage来存储信息后,感觉也就没必要再通过vuex转换一遍了,不过两者结合起来倒是也可以。而且现在sessionStorage在移动端的兼容性也非常好,基本不用考虑是否可以用的问题。// store import Vuex from 'vuex'; Vue.use(Vuex); const store = new Vuex.Store({ state: { user: JSON.parse( sessionStorage.getItem('user') || '{}' ) } actions: { setUserInfo(state, userinfo){ state.user = Object.assign({}, state.user, userinfo); if(userinfo.nickname){ sessesionStorage.setItem('user', JSON.stringify(user)); } } } }) 这里也只是个简单的样例代码,具体中可以要判断的东西也会更多。 3. 总结 # 在客户端内放置的单页面应用,虽然能让用户操作的比PC上更少,但刷新页面和跳转到第三方链接的情况,还是存在的,因此这个登录问题还是要解决的。目前我正在使用的就是第一种方案,不过我的项目是在客户端里的,可以通过客户端暴露的jsapi来检测登录态。
2024年10月21日
9 阅读
0 评论
0 点赞
2024-10-21
js:如何截取含有表情的字符串
在以QQ和微信作为第三方登录的系统中,用户的昵称里经常会有表情等字符,比如这样的:非拉🍒非拉。如果项目中正好有个需要将用户昵称强行按照字符个数进行截断的需求,你会发现截取的字符串中会有乱码的现象。以上面的用户昵称 非拉🍒非拉 为例,我们算下这个字符串的长度。因html编码的原因,在以下字符串和代码中,无法正常的显示出樱桃的图像,全部使用Y进行代替,测试时,请将Y替换为具体的樱桃符号let nickname = '非拉Y非拉'; nickname.length; // 6 说明中间的表情字符占了2个字符的长度,如果单独输出nickname[2]或者nickname[3]时,均会输出乱码字符。 JavaScript 内部,字符以 UTF-16 的格式储存,每个字符固定为2个字节。对于那些需要4个字节储存的字符,JavaScript 会认为它们是两个字符。 如果针对所有的昵称都要截取前3个字符时,该怎么截取呢?如果是中文、英文、数字、标点符号等常见字符,那么直接从3开始截取就行了,如果上面的那种有特殊符号的怎么办呢? 1. Array.from方法 # Array.from这个方法能够将类数组转换为真实的数组,比如NodeList, argument等,同样,也包括字符串。Array.from(nickname); // ["非", "拉", "Y", "非", "拉"] nickname.split(''); // ["非", "拉", "?", "?", "非", "拉"] 使用Array.from把nickname转换后,可以看到转换成一个真实的数组了,樱桃字符占了数组中的一个位置,然后按照数组中的方法截取再进行拼接即可,而使用split方法拆分,则还是乱码:function truncated(str, num){ return Array.from(str).slice(0, num).join(''); } truncated(nickname, 3); // 非拉Y 2. codePointAt()方法 # 在ES6之前, JS 的字符串以 16 位字符编码(UTF-16)为基础。每个 16 位序列(相当于2个字节)是一个编码单元(code unit),可简称为码元,用于表示一个字符。字符串所有的属性与方法(如length属性与charAt() 方法等)都是基于16位序列。 比如length方法、nickname[2]、split方法等操作,都会产生异常。为此在ES6中,加强了对 Unicode 的支持,并且扩展了字符串对象。对于 Unicode 码点大于0xFFFF的字符,是使用4个字节进行存储。ES6 提供了codePointAt方法,能够正确处理 4 个字节储存的字符,返回一个字符的码点。// 获取樱桃的码点 "Y".codePointAt(0).toString(16); // 1f352 // 输出码点对应的字符 "\u{1f352}"; // Y 请注意: 在之前Unicode编码,均在[\u000-\uFFFF]之间,因此可以使用类似\u0047这样的编码;但是现在码点超过\uFFFF的界限,若再这样使用,则获取不到对应的字符。因此在ES6中,码点的字符放在中括号内,类似上面的格式(所有的码点均可以使用这种格式):"\u{1f352}"; // Y "\u{47}" // G "\u{0047}" // G 那么就容易了:判断需要截取的位置是否正好是4字节的字符,如果是则延长一位截取,否则正常截取:function truncated(str, num){ let index = Array.from(str)[num-1].codePointAt(0)>0xFFFF ? num+1 : num; return str.slice(0, index); } truncated(nickname, 3); // 非拉Y 3. for-of # for-in方法是遍历key值,for-of是遍历value值:let arr = ['a', 'b', 'c']; for(let k in arr){ console.log(k); // 0 1 2 } for(let v of arr){ console.log(v); // a b c } for(let v of nickname){ console.log(v); // 非 拉 Y 非 拉 } 因此利用这个功能,我们也能进行截取:function truncated(str, num){ let s = ''; for(let v of nickname){ s += v; num--; if(num
2024年10月21日
9 阅读
0 评论
0 点赞
2024-10-21
Vue单页面中进行业务数据的上报
为什么要在标题里加上一个业务数据的上报呢,因为在咱们前端项目中,可上报的数据维度太多,比如还有性能数据、页面错误数据、console捕获等。这里我们只讲解业务数据的埋点。业务数据的上报主要分为: 各个路由的PV上报; 用户的点击行为上报; 用户操作结果(分享是否成功)的数据上报等; 通用和必须上报的数据,均在上报的代码中进行固定,比如设备信息、用户信息、cookie等都需要上报的数据,在上报前处理完成,需要异步获取且数据固定的,做好存储,防止每次都要重新获取;其他额外的数据,通过对外暴露的send方法进行传递。比如获取信息这块,客户端给到的jsapi,有可能只能异步调用,那么我们就可以这么处理:function getAppInfo() { let appInfo = {}; return ()=> { if (appInfo.deviceId) { return Promise.resolve(appInfo); } else { return new Promise((resolve, reject) => { ABB.getAppInfo(info => { if (info.deviceId) { appInfo = info; resolve(appInfo); } else { reject(new Error('get AppInfo error')); } }) }) } } } const AppInfo = getAppInfo(); console.log( AppInfo() ); 1. 各个路由的PV上报 # 各个路由的PV上报可以通过vue router的afterEach来实现,每次路由刷新时,afterEach方法都会执行,那么我们在这里进行PV的上报:// 每个hash路由的PV上报 router.afterEach((to)=>{ // to为当前已打开的页面,to.name为在router/index.ts中设定的name dataBoss.sendPV(to.name); }) 2. 用户点击行为的上报 # 用户点击行为的上报,之前是在每个点击的业务代码最后,进行一次点击上报。不过这样一个不好的地方是,必须为每个需要上报的点击元素添加一段业务代码,同时,如果多个点击行为共享某个业务片段时,需要进行点击区分:methods: { myClick(value, prarams, act) { // ... 业务逻辑的处理 // 数据的上报 wzp.send({ act: act, pageSource: 'MainPage' }) } } 现在,我们利用Vue中的自定义指令来实现点击行为的上报,上报的处理与业务代码进行分割:// 自定义指令的官方文档: https://cn.vuejs.org/v2/guide/custom-directive.html // 自定义boss指令 // bind: 只对该元素绑定一次 // el: 触发时的DOM元素 // binding.value: 传入的值 // 使用 v-boss="{page: 'MainPage', sop: 'donate'}" Vue.directive('boss', { // bind: function (el: HTMLElement, binding: any) { el.addEventListener('click', ()=>{ // 绑定click事件,触发后进行数据上报 Vue.prototype.$dataBoss.send(binding.value) }) } }) 自定义v-boss指令后,我们就可以在元素上使用这个指令后: 3. 用户操作结果的数据上报 # 这里的数据上报是用户点击行为之后的结果上报,比如用户点击了分享按钮,那么最终他是真的分享成功了,还是中途又取消了。这种数据的上报,可以分析出用户从意图操作到最终实现的一个流失情况。操作结果的数据上报,依赖于客户端或者接口给反馈的结果,这就需要在业务代码中实现了,定义一个全局变量$dataBoss,通过这个来上报数据:比如分享是否成功的监控:// 发起分享 handleShare() { share.show(); share.on('shareResult', res => { this.$dataBoss.send({ sop: 'share_success' }); }) } 根据接口中的数据进行上报:handleUser() { jsonp(url).then(result => { this.$dataBoss.send({ kv: { money: 20 } }); }) } 总结 # 前端数据上报的维度很多,都是为了方便我们更加的了解用户、了解产品,方便以后的功能迭代。
2024年10月21日
4 阅读
0 评论
0 点赞
2024-10-21
基于webview的前端页面优化指南
导航 # 页面性能规范 分享规范 页面要求 交互规范 容灾规范 其他异常处理 技术规范 测试标准 我们的业务主要是在移动端中使用和推广的,在移动端中,网络环境和内存都有不确定性,网络环境好的终端,不一定有好的内存;同样,有好内存的终端,也不一定都实时处在良好的网络环境下。因此,我们在针对移动端中应该有哪些优化呢? 1. 页面性能规范 # 从loading到呈现页面元素的时间,wifi下
2024年10月21日
5 阅读
0 评论
0 点赞
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日
6 阅读
0 评论
0 点赞
1
...
30
31
32
...
213