其他杂项篇

2021-02-24  本文已影响0人  岚平果

函数节流和函数防抖
https://www.jianshu.com/p/891161fd9b3e

函数节流:是指一定时间内js方法只跑一次。比如人的眨眼睛,就是一定时间内眨一次。应用场景:


函数防抖:
      是指频繁触发的情况下,只有足够的空闲时间,才执行代码一次。比如生活中的坐公交,就是一定
    时间内,如果有人陆续刷卡上车,司机就不会开车。只有别人没刷卡了,公司才开车。

1. 常见的浏览器内核有哪些?

主要分成两部分:
渲染引擎(layout engineer或Rendering Engine)和JS引擎。

1. 渲染引擎:
   负责取得网页的内容(HTML、XML、图像等等)、整理讯息(例如加入CSS等),以及计算网页的显示
   方式然后会输出至显示器或打印机。浏览器的内核的不同对于网页的语法解释会有不同,所以渲染的
   效果也不相同。所有网页浏览器、电子邮件客户端以及其它需要编辑、显示网络内容的应用程序都
  需要内核。

2. JS引擎则:
    解析和执行javascript来实现网页的动态效果。

最开始渲染引擎和JS引擎并没有区分的很明确,后来JS引擎越来越独立,内核就倾向于只指渲染引擎。

常见内核
IE(内核:trident);   internet explorer 
火狐浏览器mozilla firefox (内核:gecko) 
谷歌浏览器chrome(内核:webkit)    
opera浏览器(内核:presto)

2. 网页前端性能优化的方式有哪些?

1.压缩 css, js, 图片
2.减少 http 请求次数, 合并 css、js 、合并图片(雪碧图)
3.使用 CDN
4.减少 dom 元素数量
5.图片懒加载
6.静态资源另外用无 cookie 的域名
7.减少 dom 的访问(缓存 dom)
8.巧用事件委托
9.样式表置顶、脚本置低



css在加载过程中不会影响到DOM树的生成,但是会影响到Render树的生成,进而影响到layout,
所以一般来说,style的link标签需要尽量放在head里面,因为在解析DOM树的时候是自上而下的,而css样式
又是通过异步加载的,这样的话,解析DOM树下的body节点和加载css样式能尽可能的并行,
加快Render树的生成的速度。



js脚本应该放在底部,原因在于js线程与GUI渲染线程是互斥的关系,如果js放在首部,
当下载执行js的时候,会影响渲染行程绘制页面,js的作用主要是处理交互,而交互必须得先让页面呈现
才能进行,所以为了保证用户体验,尽量让页面先绘制出来。

3. 网页从输入网址到渲染完成经历了哪些过程?

大致可以分为如下7步:

1. 输入网址;
2. 发送到 DNS 服务器,并获取域名对应的 web 服务器对应的 ip 地址;
3. 与 web 服务器建立 TCP 连接;
4. 浏览器向 web 服务器发送 http 请求;
5. web 服务器响应请求,并返回指定 url 的数据(或错误信息,或重定向的新的 url 地址);
6. 浏览器下载 web 服务器返回的数据及解析 html 源文件;
7. 生成 DOM 树,解析 css 和 js ,渲染页面,直至显示完成;

3-01. 浏览器的主要组成部分是什么?

1. 用户界面 - 包括地址栏、前进/后退按钮、书签菜单等。除了浏览器主窗口显示的您请求的页面外,
其他显示的各个部分都属于用户界面。
2. 浏览器引擎 - 在用户界面和呈现引擎之间传送指令。
3. 呈现引擎 - 负责显示请求的内容。如果请求的内容是 HTML,它就负责解析 HTML 和 CSS 内容,
并将解析后的内容显示在屏幕上。
4. 网络 - 用于网络调用,比如 HTTP 请求。其接口与平台无关,并为所有平台提供底层实现。
5. 用户界面后端 - 用于绘制基本的窗口小部件,比如组合框和窗口。其公开了与平台无关的通用接口,
而在底层使用操作系统的用户界面方法。
6. JavaScript 解释器。用于解析和执行 JavaScript 代码。
数据存储。这是持久层。浏览器需要在硬盘上保存各种数据,例如 Cookie。新的 HTML 规范
 (HTML5) 定义了“网络数据库”,这是一个完整(但是轻便)的浏览器内数据库。

3.02. 浏览器是如何渲染UI的?

1. 浏览器获取HTML文件,然后对文件进行解析,形成DOM Tree
2. 与此同时,进行CSS解析,生成Style Rules
3. 接着将DOM Tree与Style Rules合成为 Render Tree
4. 接着进入布局(Layout)阶段,也就是为每个节点分配一个应出现在屏幕上的确切坐标
5. 随后调用GPU进行绘制(Paint),遍历Render Tree的节点,并将元素呈现出来

3.3 最经典的跨域方案

一、jsonp
jsonp本质上是一个Hack,它利用<script>标签不受同源策略限制的特性进行跨域操作。

jsonp优点:
实现简单
兼容性非常好

jsonp的缺点:
只支持get请求(因为<script>标签只能get)
有安全性问题,容易遭受xss攻击
需要服务端配合jsonp进行一定程度的改造

jsonp的实现:
function JSONP({  
  url,
  params,
  callbackKey,
  callback
}) {
  // 在参数里制定 callback 的名字
  params = params || {}
  params[callbackKey] = 'jsonpCallback'
    // 预留 callback
  window.jsonpCallback = callback
    // 拼接参数字符串
  const paramKeys = Object.keys(params)
  const paramString = paramKeys
    .map(key => `${key}=${params[key]}`)
    .join('&')
    // 插入 DOM 元素
  const script = document.createElement('script')
  script.setAttribute('src', `${url}?${paramString}`)
  document.body.appendChild(script)
}

JSONP({  
  url: 'http://s.weibo.com/ajax/jsonp/suggestion',
  params: {
    key: 'test',
  },
  callbackKey: '_cb',
  callback(result) {
    console.log(result.data)
  }
})

二、最方便的跨域方案Nginx
nginx是一款极其强大的web服务器,其优点就是轻量级、启动快、高并发。
现在的新项目中nginx几乎是首选,我们用node或者java开发的服务通常都需要经过nginx的反向代理。

反向代理的原理很简单,即所有客户端的请求都必须先经过nginx的处理,nginx作为代理服务器再
讲请求转发给node或者java服务,这样就规避了同源策略。

三、其它跨域方案
 1. HTML5 XMLHttpRequest 有一个API,postMessage()方法允许来自不同源的脚本采用异步方式
进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。

2. WebSocket 是一种双向通信协议,在建立连接之后,WebSocket 的 server 与 client 都能主动向
对方发送或接收数据,连接建立好了之后 client 与 server 之间的双向通信就与 HTTP 无关了,
因此可以跨域。
3. window.name + iframe:window.name属性值在不同的页面(甚至不同域名)加载后依旧存在,
并且可以支持非常长的 name 值,我们可以利用这个特点进行跨域。

4. location.hash + iframe:a.html欲与c.html跨域相互通信,通过中间页b.html来实现。 三个页
面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。

4. document.domain + iframe: 该方式只能用于二级域名相同的情况下,
比如 a.test.com 和 b.test.com 适用于该方式,我们只需要给页面
添加 document.domain ='test.com' 表示二级
域名都相同就可以实现跨域,两个页面都通过js强制设置document.domain为基础主域,就实现了同域。

4. 线程与进程的区别?

进程是分配内存的最小单位,线程是CPU调度的最小单位,进程可以包含多个线程
1. 一个程序至少有一个进程,一个进程至少有一个线程.

2. 线程的划分尺度小于进程,使得多线程程序的并发性高。

3. 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行
效率。

4. 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和
程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

4. 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统
并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和
线程的重要区别。

6. 图片懒加载?

监听浏览器的滚动事件,结合clientHeight、offsetHeight、scrollTop、scrollHeight
等等变量计算当前图片是否在可视区域,如果在,则替换src加载图片,当然这个滚动事件要主要节流。

当页面滚动的时间被触发 -> 执行加载图片操作 -> 判断图片是否在可视区域内 -> 在,
则动态将data-src的值赋予该图片

7. 移动端性能优化?

1. 尽量使用 css3  动画,开启硬件加速
2. 适当使用 touch  时间代替  click  时间
3. 避免使用 css3  渐变阴影效果
4. 可以用 transform: translateZ(0)   来开启硬件加速
5. 不滥用 float。float  在渲染时计算量比较大,尽量减少使用
4. 不滥用   web  字体。web  字体需要下载,解析,重绘当前页面
6. 合理使用 requestAnimationFrame  动画代替  setTimeout
7. css 中的属性(css3 transitions、css3 3D transforms、opacity、webGL、video)会触发
GUP  渲染,耗电

9. HTTP 和 HTTPS,为什么HTTPS安全?

1. HTTP  协议通常承载与   TCP  协议之上,在  HTTP  和  TCP  之间添加一个安全协议层
(SSL  或  TSL),这个时候,就成了我们常说的  HTTPS
2. 默认HTTP的端口号为  80,HTTPS  的端口号为  443
3. 因为网络请求需要中间有很多的服务器路由的转发,中间的节点都可
能篡改信息,而如果使用  HTTPS,
密钥在你和终点站才有,https  之所有说比  http  安全,是因为他利用ssl/tls协议传输。包含证书,
流量转发,负载均衡,页面适配,浏览器适配,refer  传递等,保障了传输过程的安全性

10. 对象的深拷贝和浅拷贝

一、浅拷贝的意思就是只复制引用(指针),而未复制真正的值。如果拷贝后的对象发生变化,原对象也会
发生变化。
const originArray = [1,2,3,4,5];
const originObj = {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
 
const cloneArray = originArray;
const cloneObj = originObj;
 
console.log(cloneArray); // [1,2,3,4,5]
console.log(originObj); // {a:'a',b:'b',c:Array[3],d:{dd:'dd'}}

cloneArray.push(6);
cloneObj.a = {aa:'aa'};
 
console.log(cloneArray); // [1,2,3,4,5,6]
console.log(originArray); // [1,2,3,4,5,6]
 
console.log(cloneObj); // {a:{aa:'aa'},b:'b',c:Array[3],d:{dd:'dd'}}
console.log(originArray); // {a:{aa:'aa'},b:'b',c:Array[3],d:{dd:'dd'}}

上面的代码是最简单的利用 = 赋值操作符实现了一个浅拷贝,可以很清楚的看到,随着 cloneArray 
和 cloneObj 改变,originArray 和 originObj 也随着发生了变化。
————————————————————————————————————————————————————————————————————

二、深拷贝就是对目标的完全拷贝,不像浅拷贝那样只是复制了一层引用,就连值也都复制了。
只要进行了深拷贝,它们老死不相往来,谁也不会影响谁。
目前实现深拷贝的方法不多,主要是两种:

1. 利用 JSON 对象中的 parse 和 stringify, JSON.stringify 实现的是深拷贝,但是对目标对象
有要求(非 undefined,function);
2. 利用递归来实现每一层都重新创建对象并赋值
3. JavaScript 中数组和对象自带的拷贝方法都是“首层浅拷贝”;

3-4. vue前端跨域解决方案
为什么会出现跨域:
浏览器访问非同源的网址时,会被限制访问,出现跨域问题.

常见的跨域有三种:

vue代理服务器proxy跨域两个关键点:
本地服务器(利用node.js创建的本地服务器进行代理,也叫代理服务器)和浏览器之间不存在跨域服务器和服务器之间不存在跨域
话不多说,直接上代码:

首先创建一个 vue.config.js文件
// 假设要请求的接口是:http://40.00.100.100:3002/api/user/add
module.exports = {
    devServer:{
      host:'localhost',  // 本地主机
        port:5000,  // 端口号的配置
        open:true,  // 自动打开浏览器
        proxy:{
          '/api': {   //  拦截以 /api 开头的接口
            target: 'http://40.00.100.100:3002',//设置你调用的接口域名和端口号 别忘了加http
            changeOrigin: true,    //这里true表示实现跨域
            secure: false, // 如果是https接口,需要配置这个参数
            pathRewrite: {
              '^/api':'/'  //这里理解成用‘/api’代替target里面的地址,后面组件中我们掉接口时
//直接用api代替 比如我要调用'http://40.00.100.100:3002/api/user/add',直接写‘/api/user/add’即可
            }
          },
    // 假如又有一个接口是:http://40.00.100.100:3002/get/list/add
    // 那就再配置一个 get的,如下:
          '/get': {   //  拦截以 /get 开头的接口
            target: 'http://40.00.100.100:3002',//设置你调用的接口域名和端口号 别忘了加http
            changeOrigin: true,    //这里true表示实现跨域
            secure: false, // 如果是https接口,需要配置这个参数
            pathRewrite: {
              '^/api':'/'  //这里理解成用‘/api’代替target里面的地址,
            }
          }
            // 调用的时候直接  /get/list/add  就可以了
        }
    }
  }

  // 注意:修改了配置文件后一定要重启才会生效;

我们可以利用axios的baseUrl直接默认值是 api,这样我们每次访问的时候,自动补上这个api前缀,就不需要我们自己手工在每个接口上面写这个前缀了
在入口文件里面配置如下:

import axios from 'axios'

Vue.prototype.$http = axios
axios.defaults.baseURL = 'api'  // 后面发现,其实不加这个感觉也好像可以
如果这配置 'api/' 会默认读取本地的域

如果只是开发环境测试,上面那种就足够了,如果区分生产和开发环境
就需要如下配置分环境配置跨域:
创建一个 api.config.js 文件(其实随便命名都可以)

const isPro = Object.is(process.env.NODE_ENV, 'production')
// 如果是生产环境,就用线上的接口;
module.exports = {
    baseUrl: isPro ? 'http://www.vnshop.cn/api/' : 'api/'
}

然后在main.js 里面引入,这样可以保证动态的匹配生产和开发的定义前缀

import apiConfig from './api.config'

Vue.prototype.$http = axios
import axios from 'axios'

axios.defaults.baseURL = apiConfig.baseUrl

经过上面配置后,在dom里面可以这样轻松的访问,也不需要在任何组件里面引入axios模块了

  async getData(){
        const res = await this.$http.get('/api/user/add');
        console.log(res);
    },

小结:
代理跨域的主要方式是利用服务器请求服务器的方式避过跨域问题来实现的.大概的流程: 浏览器===>代理服务器===>目标服务器.

上一篇下一篇

猜你喜欢

热点阅读