每日一题

163 阅读7分钟

了解Vue3.0

手写jsonp

原理

首先简单说下跨域的概念,浏览器对不同域名,协议,端口下的请求有同源限制,服务器返回的结果,浏览器不接收。

为了解决跨域,主要有以下三种方法:jsonp、cors、ng代理.

jsonp 利用不受同源限制的img,script,frame等标签,把script标签的src指向请求的服务端地址。

1. 动态创建script标签,指定src为目标地址,将标签加到dom.body
2. 将约定好全局函数名拼接到url中
3. 清除script标签

 function jsonp (url,data={},callback='callback') {
     //处理json对象,拼接url
     data.callback = callbak
     let params = []
     for(let key in data){
         params.push(key + '=' + data[key])
     }
     let script = document.creatElement('script')
     script.src = url + '?' + params.join('&')
     document.body.appendChild(script)
     
     //返回Promise
     return new Promise ((resolve,reject) => {
        window[callback] = (data) => {
            try{
                resolve (data)
            } catch(e){
                reject(e)
            } finally {
                //移除script元素
                script.parentNode.removeChild(script)
                console.log(script)
            }
        }
     })
 }
 
 //请求数据
 jsonp('http://photo.sina.cn/aj/index',{
     page:1,
     cate:'recommend',
 },'jsoncallback').then(data => {
     console.log(data)
 })


参考链接:https://juejin.cn/post/6844903946364928008

实例

js原生获取和设置css属性

获取

1. style只能获取行间样式,能设置样式
	const style = element.style.width
2. getComputedStyle能够获取行间样式/非行间样式/浏览器默认样式,不能设置样式
	const style = getComputedStyle(oBox, null)['width']
    兼容IE的写法(currentStyle):
    function getStyle(oElement, sName){  
        return oElement.currentStyle ? oElement.currentStyle[sName] : getComputedStyle(oElement, null)[sName];  
    }  

设置

1. 设置style
	element.style.height = '100px';
	element.style['text-align'] = '100px';
2. 设置属性
	element.setAttribute('height', '100px');
    element.setAttribute('height', 100);
3. 设置cssText
    element.style.cssText = 'height: 100px !important';
    element.style.cssText += 'height: 100px !important';
4. 设置新style标签
    function addNewStyle(newStyle) {
        var styleElement = document.getElementById('styles_js');
        if (!styleElement) {
        styleElement = document.createElement('style');
        styleElement.type = 'text/css';
        styleElement.id = 'styles_js';
        document.getElementsByTagName('head')[0].appendChild(styleElement);
        }
        styleElement.appendChild(document.createTextNode(newStyle));
    }
    addNewStyle('.box {height: 100px !important;}');

参考

cloud.tencent.com/developer/a…

为什么要减少Dom操作?

一句话

Dom操作导致回流重绘耗费性能

详细解释

无论是React,Vue,都采用虚拟DOM的思想,其中重要的原因就是为了减少DOM操作。原因是DOM操作比较耗费性能。

耗费性能的原因简单点来说就是,每次操作DOM都会导致浏览器回流或重绘。有同学可能会问,那我只是获取一下DOM节点的信息,不更改节点属性呢?还会导致回流重绘么?

会的。 原因如下: 浏览器其实不是每一次DOM节点发生变化都重新layout跟painting,出于性能考虑会把这部分操作放在一个任务队列,在特定的时机统一处理。(浏览器会以一定的频率刷新屏幕,与这部分相关的有个API,requestAnimationsFrame,有待深挖)。但是DOM相关的操作使浏览器不得不马上执行队列中的任务,以获取最新的DOM信息。由此造成了性能的浪费。

认识WeakMap

概念

WeakMap 对象是一组键值对的集合,其中的键是弱引用对象,键值依然是正常引用。每个键对自己所引用对象的引用都是弱引用,在没有其他引用和该键引用同一对象,这个对象将会被垃圾回收(相应的key则变成无效的),所以,WeakMap 的 key 是不可枚举的。

方法

方法同map中的部分方法

has(key):判断是否有 key 关联对象
get(key):返回key关联对象(没有则则返回 undefined)
set(key):设置一组key关联对象
delete(key):移除 key 的关联对象

应用场景

发布订阅模式中,储存订阅者,若订阅者在其他地方没有被引用,则会被垃圾清除机制清除掉(如被删除的DOM节点),避免内存泄漏。

使用proxy

百看不如一用
let p = new Proxy({},{
    get() {
        console.log(11)
    },
    set() {
        console.log(22)
    }
})
p.b; // 11
p.b=3; // 22
p=4; // 这是改变指针,不走代理,输出nothing;原来的对象P被释放

let const 的死区

var a = 3;
function b() {
    console.log(a) // error
    let a = 4;
}
b()

const的不可变

const a = {};
a.b = 2
console.log(a) // {b:2}
a = {c:3} // error,不可变,指的是地址不可变,地址中存的内容可变

声明变量与函数同名的例子

console.log(a) // [Function: a]
var a = 3;
console.log(a) // 3
function a() {
    console.log(1)
}
console.log(a) // 3

请用ES5,ES6实现组合继承

ES5:
function Children(){
    const arg = [...arguments];
    Parent.call(this,...arg);
}
Children.prototype = Object.create(Parent.prototype);
Children.prototype.constructor = Children;
ES6:
class Children extends Parent {
	constructor(name) {
    	super();
        this.name = name
    }
    printName() {
    	console.log(this.name)
    }
 }

资源下载的几个阶段load,DOMContentLoaded

  • DOMContentLoaded:dom内容加载并解析完成的时间
  • load:页面所有的资源(图片,音频,视频等)加载完成的时间

requestAnimationsFrame是什么?

概念

重绘前触发回调的API,根据浏览器刷新页面的帧率决定触发时机。 在事件循环机制中的微任务队列之后,宏任务之前。

前端路由的hash和History模式

前端路由的目的

  1. 记录用户操作位置,模拟浏览器前进回退功能
  2. 监听不同路由页面,实现不同的操作

前端路由功能

  1. 改变url不会向服务器发送新的请求
  2. 能够监听到路由的变化

两种模式比较

  • hash:
    • hash是url中#后面的部分
    • hash变化不会导致页面刷新,会触发hashchange事件,监听这个事件实现切换页面等操作。
    • hash模式下,原来的锚点功能不能用了,同时基于url传参,有体积限制。
  • History模式
    • 通过维护一个堆栈记录实现历史操作的记录
    • 同时监听的也不是url变化,而是点击链接等操作(如a标签),先阻止默认行为,再向堆栈中添加或删除一条历史记录
    • 与hash相比,刷新页面的时候还是会发送请求,如果后端没有对当前路由做处理。会返回404错误,因此History模式下要对路由做兜底,匹配不到相应路由时返回index.HTML地址。
相关API
window.history.pushState(stateObj, title, url})
window.history.replaceState(state, title, url)
// History的pushState,replaceState事件不会触发浏览器的popstate事件
// 浏览器的前进回退行为才触发
window.onpopstate = function(event){
  // do something...
}
history.back();
history.go();
history.forward();

axios设置请求头

axios的get post设置请求头的参数位置不一样
axios.get("http://xxx/xxx",{
    //请求头配置  
       headers:{ token: 21 }
	//参数列表
       params:{ id: 21},
    
    }).then((res)=>{
        console.log(res)
 })
axios.post("http://xxx/xxx",
//参数列表
    {
        'id': 921
    },
//请求头配置   
    {
        headers: {'token':921 }
    }
).then((res)=>{
    console.log(res)
})

axios跨域请求时设置携带cookie的方法

  1. 在axios请求头的参数中,写入对象属性withCredentials:true
  2. 服务器端响应头消息中的'Access-Control-Allow-Origin'不能设置为 '*' 号,需改成具体的id地址。
  3. 服务器端响应头消息中必须设置 'Access-Control-Allow-Credentials':'true'。
备注:如果项目中引入了Mockjs,只要调用它的mock方法,就会劫持windowXMLHttpRequest对象,用一个自己的XHR替换,那里面withCredentials是false的,可能导致带不上cookie。可以用easymock替换,或者暂时性修复,在引入 Mock 之后使用如下代码
  Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send
  Mock.XHR.prototype.send = function () {
    if (this.custom.xhr) {
      this.custom.xhr.withCredentials = this.withCredentials || false
    }
    this.proxy_send(...arguments)
  }

ajax axios fetch区别及优缺点

ajax

概念

Asynchronous Javascript And XML(异步 JavaScript 和 XML),对XHR对象的封装,浏览器创建XHR发送HTTP请求

用法

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
	if (xhr.readyStatus === 4) {
        // HTTP 状态在 200-300 之间表示请求成功
        // HTTP 状态为 304 表示请求内容未发生改变,可直接从缓存中读取
        if (xhr.status >= 200 && 
            xhr.status < 300 || 
            xhr.status == 304) {
            console.log('请求成功', xhr.responseText)
        }
    }
};
xhr.open("get",url, true);
xhr.send();

// 超时时间单位为毫秒
xhr.timeout = 1000

// 当请求超时时,会触发 ontimeout 方法
xhr.ontimeout = () => console.log('请求超时')

axios

概念

基于Promise对象对xhr的封装。从浏览器中创建 XMLHttpRequests、从 node.js 创建 http 请求、支持 Promise API、拦截请求和响应等。

用法

axios.get(url)
  .then(function (response) {
    console.log(response);
  })
  .catch(error => {
    console.log(error);
  });

fetch

概念

原生js,基于promise设计,脱离xhr,相比其他两种更加底层的api

用法

fetch(url,{data})
  .then(res => {
    return response.json();
  })

优缺点比较

  • ajax, 底层xhr,在多个请求间有依赖关系时不好处理
  • axios,非原生js,基于promise对xhr的封装,避免了回调地狱,提供了拦截请求和响应接口,自动转换JSON数据
  • 防止CSRF攻击:
大概原理:
// 设置从cookie中获取csrftoken值
axios.defaults.xsrfCookieName = 'csrftoken'
// 设置将获取的token值设置到X-CSRFToken请求头中去
axios.defaults.xsrfHeaderName = "X-CSRFToken"
也可以用请求拦截实现token的获取和请求头的设置
参考链接:https://www.dazhuanlan.com/2020/03/07/5e638d7d63022
  • fetch,原生js,基于promise,支持async/await,提供更加底层的API。
    • 关于跨域,fetch有三个模式:
    1. same-origin:该模式是不允许跨域的,它需要遵守同源策略,否则浏览器会返回一个error告知不能跨域;response type:basic。
    2. cors: 该模式支持跨域请求,response type:cors。
    3. no-cors: 该模式用于跨域请求但是服务器不支持CORS;其对应的response type:opaque。这是fetch的特殊跨域请求方式,浏览器允许发送跨域请求但不能获得response内容;