JS工作经验总结(2)
JS拿到图片实际宽高方法
其实IE9+和其他浏览器都有原生的api可以取得到图片的实际宽高,用于做一些图片放大插件等有用处。如果api不兼容,可以使用图片onload后,取得到的宽高也是图片的实际宽高
1 | if (typeof el.naturalWidth == undefined) { |
关于使用formData传送的类型
formData可以直接传file,也可以通过Blob二进制类型转成二进制,然后放到formData里。一些压缩的原理就是经过转换后,存成二进制,然后发到后台,后台收到的仍然是文件类型
注意formData传图片文件的时候,是不能直接传base64的,因为它不是文件类型。
清空input文件的值
1 | inputEl.files.value = null; |
for-in循环时,使用数值作为键值遍历的问题
使用for in循环时,如果对象的键值为数值,会根据数值的大小顺序,从低到高进行遍历,遍历的顺序不可信,不要使用遍历对象然后直接push到数组,会有排序问题,需要额外的方法对数组进行排序
webpack打包体积过大,使用cdn引入依赖
由于我们常引用依赖的方法是import xx from ‘xxx’,但由于引用的cdn是通过全局变量进行引用的,如果想要使用cdn,同时使用import的方法,可以采用cdn和webpack的externals
字符串模板换行的情况
在模板字符串里换行的时候,实际上会的确存在换行符\n的,想要字符串进行换行,记得设置一下元素的white-space属性,设置为pre-wrap或pre-line进行换行
url存在特殊字符的情况
get请求,后端定义路由时,注意不要用到可能带有【.】作为路由url,会导致【.】后面的都会被忽略,且【.】属于安全字符,不会被转义。如果有这种情况,只能够使用参数方式传参,这样带【.】是无问题的
例如:
1 | /api/goods/100445.2 |
1 | /api/goods?id=100445.2 |
第一种就是路由方式去传参,100445.2会被浏览器忽略成100445去请求后端
第二种是参数方式传参,就没有问题
如果有各类特殊字符,对参数进行一个encodeURIComponent()操作即可,这是一个编码标准,后端也很容易将它解码,当get请求传入json字符串的时候,必须使用这个编码转码发到后端
getBoundingClientRect获取的高度问题
getBoundingClientRect拿到的top值只是距离窗口顶部的距离,如果有scroll,先拿到距离文档最高出的距离,需要将rect.top + window.pageYOffset || document.documentElement.scrollTop
前端登录RSA加密流程
流程是这样的:
- 请求后端,拿到一个publicKey和一个标识字符串
- 前端使用插件,利用publicKey对密码进行RSA加密
- 使用加密后的密码和标识字符串请求登录接口
- 标识字符串的作用是后台通过这个标识使用对应的privateKey对加密字符串进行解密
RSA为什么能安全:加密后的字符串,必须通过privateKey去解密,就算抓包的时候请求抓到publicKey,那也只是加密所使用的key,并不能解密。所以抓包抓到的加密字符串没有privateKey是无法加密的,privateKey保存在后台,解密工作在后台进行
伪代码:
1 | import JsEncrypt from 'jsencrypt'; |
已经通过RSA加密的内容进行解密(一般不会这么做,privateKey是不能暴露出来的):
1 | import JsEncrypt from 'jsencrypt'; |
这样就完成一次RSA登录流程
offsetTop的相对偏移问题
offsetTop是元素到offsetParent顶部的距离,offsetParent是距离元素最近的一个具有定位的祖宗元素(relative,absolute,fixed),若祖宗都不符合条件,offsetParent为body
使用ajax的方式下载文件
一直使用的url下载方式只能为get【使用window.location.open或建立一个假的a标签或使用iframe加载路径】,而且并不是一个ajax请求,返回的是一个文件流,带有后端文件名称的文件。但这种方式有个问题,就是不知道后端能够下载文件的时机。假如这个文件是一个后端动态生成的excel文件,那么距离生成会有一段时间,传统的url下载方式并不知道后端什么时候才能下载
以下为使用ajax的方式下载文件,配合blob二进制格式,以axios为例的伪代码:
1 | function downFile(url, parameter) { |
这样就可以在不跳转的情况下,下载ajax请求的二进制文件
但这时的文件名是需要前端定义的,如何知道后端的这个文件是什么名称?可以通过后端的配合,设置响应头一个属性,前端取响应头的属性然后赋值即可
例子:
1 | this.$axios({ |
注意content-disposition
这个属性能在请求调试里看到,但如果res.headers怎么都取不到的话,需要在服务器端配置一个属性:
1 | Access-Control-Expose-Headers: Content-Disposition |
参考:
https://stackoverflow.com/questions/37897523/axios-get-access-to-response-header-fields
In case of CORS requests, browsers can only access the following response headers by default:
- Cache-Control
- Content-Language
- Content-Type
- Expires
- Last-Modified
- Pragma
If you would like your client app to be able to access other headers, you need to set the Access-Control-Expose-Headers
header on the server:
1 | Access-Control-Expose-Headers: Access-Token, Uid |
获取布局信息的操作时,会强制队列刷新
比如当你访问以下属性或者使用以下方法:
- offsetTop、offsetLeft、offsetWidth、offsetHeight
- scrollTop、scrollLeft、scrollWidth、scrollHeight
- clientTop、clientLeft、clientWidth、clientHeight
- getComputedStyle()
- getBoundingClientRect
以上属性和方法都需要返回最新的布局信息,因此浏览器不得不清空队列,触发回流重绘来返回正确的值。因此,我们在修改样式的时候,最好避免使用上面列出的属性,他们都会刷新渲染队列。如果要使用它们,最好将值缓存起来。
但强制清空队列也有好处,可以解决display:none转场的transition失效问题:
1 | el.style.display = 'block'; |
假如元素设置了transition,且display为none,如果不使用el.offsetHeight
这句强制刷新队列,那么转场的动画就会失效
混合开发时web端和原生端的请求的不同
web端请求
- web端发送请求是基于XMLHttpRequest或fetch
- 浏览器同源政策CORS安全限制,需要后端设置请求头或设置服务器代理转发
- 不够安全,无法网络优化,原生端无法对请求进行监控或加密
原生端请求,web端利用JSBridge调起原生端请求
- 没有浏览器跨域限制
- 安全加密,签名校验
- 弱网优化,流量优化
- 原生端可以对请求进行压缩
- 甚至可以将http请求替换成rpc(远程过程调用)这种节省流量的方法,利用私有协议请求数据(底层变了)
混合开发的JSBridge实现原理
web端调用原生端方法:
- 拦截URL Schema,可使用alert等,原生端会对其进行拦截
- 原生端注入JSAPI,注入一个全局对象,web端可以直接调用
原生端调用web端方法:
- web端定义一个全局对象,原生端直接执行js字符串代码
带有回调的原理:
- web端发起时,利用JSBridge调用原生端方法,原生端又执行web端的js字符串代码,进行两次跨端的操作,是需要两边都约定好协议和全局对象
- 原生端发起原理与上面相同
- 由于代码繁琐或有坑,一般采用开源里比较成熟的JSBridge,会带有封装好的webview和JSAPI
SSO单点登录方案
SSO单点登录有两种方案,一种是Domain相同的,一种是Domain不相同的
Domain相同的SSO接入非常简单,逻辑大多是后台实现,就是在response的时候进入setCookie操作,同时指定Domain,例如.bilibili.com
,这样所有是这个Domain都能够共享cookie
另一种是Domain不相同的接入。加入不同的网页的端的Domain都不相同,这时候分别需要他们的后台进行SSO接入,具体就是放出一个接口,这个接口在被请求成功的时候会对对应的前台页面的Domain下写入cookie,为了确保这样,通常都是不同端的前台和后台使用代理请求,确保在同一个域里。然后从SSO里调用这些后台的接口,后台就会对对应的前台写入cookie
关于window.open()的使用事项
window.open()可以指定内容:
1 | const newWin = window.open(); |
然后,发现window.open()拦截是有条件的。一般通过用户的点击事件触发的在新标签页中打开链接,浏览器是不会拦截的,因为这种形式打开新窗口浏览器会认为是用户自己需要的。
在我的项目中,是需要在ajax异步请求成功后需要在新窗口中打开返回的url地址,使用window.open()会被拦截,因为在异步队列里进行弹窗,浏览器认为该操作不是用户主动触发的,所以会拦截。
ajax请求由异步改为同步。(async:false) (测试有效)
1 | $.ajax({ |
递归遍历父元素找到对应的DOM元素
1 | function findAncestor (el, cls) { |
安装node-sass被墙解决方法
1 | npm i node-sass --sass_binary_site=https://npm.taobao.org/mirrors/node-sass/ |
找到树中指定id的所有父节点(或包括自己)
1 | const treeData = [{ |
遍历树把该层属于第几层树的属性写进去
1 | const treeData = [{ |
scrollTo实现
1 | function easeInOutQuad(t, b, c, d) { |
根据数组对象的某个字段去重
1 | function uniqueArrayByObjectKey(arr, key) { |
简单数组去重
1 | function uniqueArray(arr) { |
多语言某些在中间需要替换语言
例如:
LANG = “The quantity of selected products:XXX; Subtotal:XXX”
replacement = [1, 10]
transformI18n(LANG, replacement)
1 | function transformI18n(key, replacement) { |