Chen Haonan's Notes


  • 首页

  • 标签

  • 归档

OOP面向对象编程

发表于 2020-04-13

面向对象编程是一种基于以”对象”为概念的编程范式,它可以使用字段包含数据,可以使用方法来包含程序。对象的一个功能是他们的方法是对外暴露的,通常情况下可以使用这些方法去改变数据。在面向对象编程中,计算机程序被设计成一个个相互交互的模型。OOP语言是多种多样的,但是最流行的一种是基于class的面向对象,在这种语言中对象是类的实例,当我们拿到一个对象时,我们能够知道他所属的类型。

许多广泛使用的编程语言例如C++、JAVA、Python都是多范式的编程语言,他们在或多或少支持面向对象编程的同时还支持命令式和过程式编程。主要的面向对象语言包括Java, C++, C#, Python, PHP, JavaScript, Ruby, Perl, Object Pascal, Objective-C, Dart, Swift, Scala, Common Lisp, MATLAB, and Smalltalk.

与非面向对象语言相同的部分

  • 基本的逻辑结构
  • 基本数据类型
  • 内置数据结构 List Hash
  • 方法 函数
  • 模块化编程 支持模块引入

对象和类

支持面向对象的编程语言使用继承做到对代码的复用,可以继承自类也可以继承自原型对象。基于类的面向对象语言主要包含以下两种概念

  • Class 定义了指定类型中的数据和可用的代码
  • Object 基于类的实例

基于类的面向对象语言中的组成

  • Class variables
  • Instance variables
  • Member variables
  • Class methods
  • Instance methods

基于类 VS 基于原型

在基于类的语言中,需要先定义类,通过类实例化对象,而在基于原型的语言中对象是主要的实体,没有类的存在,对象之间通过prototype互相连接,每一个对象都有且只有一个原型,当对象被创建的时候会绑定一个原型对象,当访问对象上的属性的时候首先在对象本身访问,如果不存在则会一次寻找原型对象,通过原型只能够实现单继承。

封装

保护数据不被滥用和损坏,所有的数据改变都是可控的。

组合 继承 代理

has-a is-a use?

多态

基于同一个父类派生的多个子类可以重新父类的方法 在调用的时候能够做到与特定子类型无关的同时会别使用各自实现的方法从而实现关注点的分离。

设计模式

  • 单一职责
  • 开放封闭
  • 里氏替换
  • 接口隔离
  • 依赖倒置

合理使用React的生命周期

发表于 2018-11-06

大家在写React代码的时候也就是在React的生命周期函数中编写代码,有的时候我们在两个生命周期中编写代码可能得到的是相同的结果,但是对于生命周期的使用是有最佳实践的。

特别是在React v16.3之后引入了异步渲染的概念,这个时候我们如何使用生命周期就变得更加重要了,首先我们先来看看v16.3之前的生命周期

React v16.3以前的生命周期

react hocks beforev16.3

创建阶段

1
2
3
4
5
// 构造函数
constructor(props) {
super(props)
this.state = { counter: 0 }
}

构造函数阶段,只调用一次,接收到传入的props,需要手动调用super(props)

在这个阶段我们对组件的state数据进行初始化

1
2
3
4
5
render() {
return (
...
)
}

render阶段 这个阶段我们渲染UI

1
2
3
4
5
6
7
componentDidMount() {
const container = this.DomContainer
const containerHeight = container.clientHeight
this.setState({
containerHeight
})
}

UI渲染完毕的阶段,只调用一次,在这个生命周期开始DOM才被创建,一些jquery库需要在这里初始化,如果需要计算DOM的高度来初始化,那么也应该在这里进行初始化

更新阶段

更新分为两种:一种是父组件传入的props变化,或者组件调用setState()

当props发生变化时进入

1
2
componentWillReceiveProps(nextProps) {
}

在这里我们可以更新组件的state,但是最好是根据组件的props计算后去渲染UI,因为这样可以保证单一数据源又避免维护一份数据产生额外的开销

1
2
shouldComponentUpdate(nextProps, nextState) {
}

返回一个boolean值,表示是否应该更新,默认返回true,返回false时不再执行下面的生命周期

1
2
3
4
5
6
7
8
9
componentWillUpdate(nextProps, nextState) {
}
render() {
return (
...
)
}
componentDidUpdate() {
}

render声明周期前后分别是componentWillUpdate和componentDIdUpdate

更新的第二种是组件调用forceUpdate,这时候会直接进入componentWillUpdate

卸载阶段

1
2
componentWillUnmount() {
}

组件在页面卸载之前进入componentWillUnmount生命周期,在这里我们可以对需要清理的资源资源进行释放,比如:清除计时器

Reactv16.3开始的生命周期

react hocks afterv16.3

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

这些生命周期方法经常被错误的使用,当开启异步渲染的时候可能存在安全隐患

所以16.3版本开始 这些生命周期将增加UNSAFE_开头的别名

17.0版本开始将删除这些生命周期 只有UNSAFE_开头的生命周期可以使用

新增俩个生命周期getDerivedStateFromProps和getSnapshotBeforeUpdate

1
2
getDerivedStateFromProps(nextProps, prevState) {
}

这个生命周期替换了componentWillReceiveProps的所有情况,接收到nextProps和prevState,返回一个state对象merge到原来的state

1
2
getSnapshotBeforeUpdate(prevProps, prevState) {
}

这个生命周期替换了componentWillUpdate,返回的对象将作为第三个参数传给componentDidUpdate

除了这俩个生命周期的变化,还提出了三个阶段的概念,render阶段,pre-commit阶段和commit阶段,由于要支持异步渲染,所以组件的生命周期可能由于优先级更高的事件触发而终止,
在render阶段我们可以终止组件继续渲染。
在pre-commit阶段我们可以对dom的数据进行读取。
在commit阶段我们可以使用dom,执行副作用操作。

  1. 添加事件监听
    componentWillMount 和 componentWillUnmount 不是一对生命周期
    只有调用了componentDidMount 才一定会调用 componentWillUnmount
    所以如果在componentWillMount中做了一些比如注册监听的工作
    由于component 在没有完成componentDidMount的时候可能会被打断
    所以也不会执行 componentWillUnmount 中的清理工作 It can leak
    所以我们应该把注册监听的操作放到componentDidMount中执行
    这样就保证我们在componentWillUnmount中的清理工作一定会被执行
  2. 获取外部数据
    在componentWillMount中大家可能会去获取外部数据
    使得数据会再componentDIdMount的时候正确的渲染
    但实际上这是做不到的
    在执行完componentWillMount之后会立刻执行componentDidMount
    这是一个同步执行的过程 而从接口获取数据是一个异步的过程
    所以提前到componentWillMount中执行是没有意义的

  3. 初始化数据
    componentWillMount 中初始化数据的操作应该迁移到constructor中

  4. componentWIllUpdate 和 componentDidUpdate
    当组件更新时需要产生副作用 使用componentDidUpdate 而不是componentWillUpdate
    在异步模式下 多次willUpdate可能只会触发一次真正的更新所以在willUpdate中产生副作用可能达不到你想要的预期

  5. getSnapshotBeforeUpdate(prevProps, prevState)
    使用componentWillUpdate读取dom属性对于异步渲染来说
    componentWillUpdate 和 render 属于 render 生命周期
    与 componentDidUpdate 之间存在延迟
    如果在这个时间内用户改变了dom 存储的数据将会失效
    解决的方法就是使用getSnapshotBeforeUpdate方法

React中的事件模型

发表于 2018-09-08

与浏览器原生的事件不同React中的事件属于合成事件,是由React提供的,首先我们看一下浏览器原生的事件模型

DOM事件模型

DOM事件分为三个阶段

  1. 事件捕获
  2. 事件目标处理函数
  3. 事件冒泡

当事件触发的时候,首先由document发出一个事件流,通过DOM树一层层到达目标DOM这个阶段属于事件捕获,到达触发事件DOM这个阶段是执行处理函数,接下来从目标DOM返回到document这个阶段称为事件冒泡阶段。

一般情况下我们的事件处理函数在事件冒泡阶段执行,addEventListener API的第三的参数接收一个boolean值 允许我们制定事件是否在捕获阶段执行,默认是false。

我们可以调用 event.stopPorpagation()来组织事件冒泡 IE下使用 event.cancel = true

使用事件对于浏览器来说是一个相对来说比较消耗性能的操作,而且当绑定事件的DOM从页面中移除的时候我们需要手动接触事件监听以防止出现内存泄漏的问题。

当一个列表中的每一个item都需要绑定事件的时候,如果每一个item都绑定事件就不是一个好的方法,一来我们需要对多个事件监听进行绑定和移除,二来多个事件监听浪费了更多的内存影响浏览器性能。

解决方法就是使用事件委托思想,利用事件冒泡机制,在所有item的外层添加一个事件处理函数,然后根据event对象的target属性来判断真实触发事件的DOM,这样就只需要一个事件处理函数,降低了维护成本,也提高了页面的性能。

事件委托也存在局限的地方因为是依赖事件冒泡原理实现的,但是有些事件是不支持事件冒泡的,那么这些事件也就无法使用代理机制。


React中的合成事件

React将所有类型的事件绑定到document上,使用全局统一的事件监听器,这个事件监听器维护了所有React事件和处理函数的映射关系,当组件绑定事件时,就会在这个映射关系上插入一个对象,当组件卸载时,在这个映射关系上删除一个对象,这样就不会频繁的绑定和卸载事件监听,只需要维护这个映射关系就可以了。

当触发事件时,我们知道事件的类型和触发事件的React component,我们找到这个React component,如果事件是支持冒泡的,那我们还需要findParent找到它的父节点,这个过程一直重复到parent是document,然后事件就沿着这个路线一直到document,当然我们也可以使用event.stopPropagation()阻止事件冒泡,因为event对象经过封装所以stopPropagation方法在IE下也可以使用没有兼容性问题。

React出现的意义及设计理念

发表于 2018-06-06

传统开发框架存在的问题

  • Jquery式的开发框架采用命令式的编程范式 需要关注DOM的细节
  • 数据变化后 UI的同步变得非常困难
  • 大量的API增加学习成本 降低了开发效率
  • 数据模型中Model与View之间复杂的依赖关系

React设计理念

  • 采用函数式编程范式 应用的每一个状态对应一个页面的UI 不需要关注DOM细节
  • 每次生成的Virtual DOM 都是全量渲染 不需要区分是否是更新状态
  • 组件化思想 将整个页面抽象成一个组件 组件之间的相互嵌套组成了整个页面
  • 极少的API和声明周期函数 理解了这些就可以进行应用的开发
  • 采用Flux架构,单项数据流 保证了每次数据变化都是可追踪的

React 使用JSX将HTML和JS混合在一起?

首先将html和js分成不同文件只是将他们分开管理而并非在逻辑上“分而治之”
React的组件作为一个完成特定功能的组件具有高内聚的特性是极为重要的
而且在React中使用onClick是完全区别于在html中使用onclick

在html中使用onclick有什么缺点呢?

  1. onclick添加的事件处理函数是在全局环境下执行的,这会污染全局环境,造成意想不到的后果
  2. 给DOM添加事件处理函数 会降低程序的性能
  3. DOM销毁的时候绑定的事件处理函数需要被手动销毁,忘记销毁可能会产生内存泄漏

以上的几个问题在JSX中是不存在的

  1. JSX中的onClick使用的是事件委托的方式 只在最外层的DOM上注册一个事件处理函数 所有点击事件都触发这一个函数 然后根据组件来分配特定的函数
  2. React的组件控制着生命周期 当组件销毁的时候 所有注册的事件处理函数也随之销毁

节流和防抖

发表于 2018-05-14

为什么我们需要节流和防抖?

JavaScript遵循事件驱动的编程模型,用户的某些特定的行为会触发响应的回调函数,行为发生的频率是由用户控制的,频繁的触发回调函数可能会产生性能问题,这个时候我们可以从控制如何响应回调函数入手去优化性能。
考虑如下真实的场景:
一个图片墙功能,需要我们延迟加载图片,这就需要我们去监听页面滚动事件,在回调函数中我们判断未加载的图片当前是否展示在了浏览器可视区域,如果展示了,就去加载页面的真实地址。如果不对响应函数进行处理的话,scroll事件会频繁的计算图片的位置和当前滚动的位置,产生页面卡顿的现象。
一个input搜索功能,当用户从键盘输入内容的时候,向后端接口请求所有匹配的内容,类似于百度搜索栏一样的功能,如果我们不对响应函数加以控制,会出现什么现象呢?我们每输入一个字符,就都会向后端发出一次请求,但其实我们一次完整的输入过程并没有完成,这些中间状态是不必要的,请求接口也造成了资源的浪费。
处理以上情况通常的做法是使用节流和防抖。

节流

拿自来水管中的水流为例,节流的意思是降低水流的大小,用在这个地方是降低了响应响应时间的频率,限制了两次响应的时间间隔。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var start = Date.now()
var timeId = null;
var dur = 1000
function log() {
console.log('log')
}
function throttle() {
console.log('emit')
return function() {
var now = Date.now()
clearTimeout(timeId)
if (now - start >= dur) {
log()
start = now
} else {
timeId = setTimeout(log, dur)
}
}
}
window.onscroll = throttle()

这样的话我们就实现了节流的效果 这里我们每隔一秒钟响应一次 如果两次触发回调的时间间隔小于一秒我们就可以设置一个一秒钟的定时器这样就可以保证不会错误的丢掉响应 具体的时间间隔可以适当进行调整 这里只是做一个演示。

上面的代码实现了我们想要的功能但是从可用性角度来看是无法接受的,throttle函数依赖了全局的start和timeId变量并且与响应函数也存在强耦合的关系,下面我们稍微修改一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function throttle(fn, dur) {
var start = Date.now()
var timeId = null;
return function() {
var now = Date.now()
clearTimeout(timeId)
if (now - start >= dur) {
fn()
start = now
} else {
timeId = setTimeout(fn, dur)
}
}
}
function log() {
console.log('log')
}
window.onscroll = throttle(log, 1000)

防抖

防抖这个术语在电学中是将多个电信号合并成一个电信号
在这里我们接收到一个响应信号时不是立即执行处理函数而是等待一定时间,如果这段时间内没有接收到信号则执行处理函数,如果再次接收到了那么重置开始时间重新等待,这样就可以做到在一定时间间隔内将多个信号合并

1
2
3
4
5
6
7
8
9
10
11
function log() {
console.log('log')
}
function debounce(fn, dur) {
var timeId = null
return function() {
clearTimeout(timeId)
timeId = setTimeout(fn, dur)
}
}
window.onscroll = debounce(log, 1000)

有些时候我们需要先执行一次处理函数然后再进行等待

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function log() {
console.log('log')
}
function immediate(fn, dur) {
var timeId = null
var timeout = true
return function() {
if(timeout) {
fn()
timeout = false
} else {
clearTimeout(timeId)
timeId = setTimeout(() => {
fn()
timeout = true
}, dur)
}
}
}
window.onscroll = immediate(log, 1000)

总结

节流和防抖的概念在性能优化中是处理浏览器回调函数的常见手段,对提升我们页面性能和用户体验来说非常重要,还可以减少中间状态时不必要的请求,是非常重要的概念。

xss

发表于 2018-03-01

xss攻击

XSS攻击全称跨站脚本攻击,是为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS,XSS是一种在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。

xss和csrf

发表于 2018-03-01

xss攻击

XSS攻击全称跨站脚本攻击,是为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS,XSS是一种在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。

前端性能优化

发表于 2017-10-14

性能优化

性能优化的必要性?
随着web应用复杂的的提升,性能问题使我们作为前端开发者不得不去关注的一个地方同时也是一个很重要的方面,因为网站的打开速度直接关系到客户留存率,页面打开时间每多一秒就会有很多的客户流失,从而造成直接的经济损失,特别是对于体量特别大的网站,每损失1%用户都是巨额经济损失,良好的性能能提高用户体验,提高用户的满意度,提高转化率,所以我们一定要重视网站的性能。

性能优化可以大体上分为加载性能优化和渲染性能优化

加载性能优化

  • 文本内容 对HTML、CSS、JS 进行minifier 去掉空格、注释、并且对JS中的变量进行替换以减小文件的大小
  • GZIP压缩 服务器开启GIZP后文本文件可以达到减小70%以上的效果(对图片的影响不大)
  • 尽量不要依赖比较大的库,比如:jQuery,使用更轻量的方法替换引入整个库。
  • 图片
    1. 减少不必要图片的使用
    2. 选择合适的图片类型
    3. 删除图像元数据
    4. 适当降低图片的质量
    5. 裁剪图片仅显示重要内容
    6. 压缩图片
    7. 加载合适大小的图片
  • CSS文件合并 chrome浏览器对同一个域下的同时发出的请求数量限制六个,也就是说如果当页面首次加载的时候,我同时发出10个请求,那么其中4个请求就会等待,所以有些时候我们需要将静态文件合并以减少请求的数量
  • CSS雪碧图 合并图片请求
  • 将JavaScript文件放置在文件末尾防止加载JS文件延迟页面渲染
  • 部分页面渲染的JS代码可以放到HTML文件中,而不是发出一个额外的请求
  • 使用CDN去提供静态资源服务
  • 使用缓存
    1. Expires http1.0出现 设置对象的有效期 局限性:存在本地时间与服务器时间不一致
    2. last-modified http1.0 设置最后修改时间在请求时带上本地Cache的last-modified web server 接到请求后check 如果和服务器上的last-modified相同则返回本地cache 否则返回最新修改的局限性:同一秒修改多次
    3. E-tag http1.1 if-none-match
    4. Cache-Control http1.1
      max-age = num(s) 设置最大缓存时间
      private 不会被多个用户共享
      public 会被多个用户共享
      no-cache 不会缓存
      no-store 不会存储
  • 本地缓存
    1. localStroage
    2. sessionStroage
    3. indexedDB

渲染性能优化

  • WebAssembly
  • throttle debounce
  • 修改DOM的时候使用类名控制避免重复layout
  • 减少DOM的复杂度
  • lazyload preload
  • 大列表优化
  • MTU base64 inline
  • 小图 转换成 大图
  • progressive vs baseline
  • http 2.0

如何测试

发表于 2017-08-25

单元测试

为什么大家都需要单元测试?

  • 代码质量持续有保障
  • 重构正确性保障
  • 增强自信心
  • 自动化运行

测试框架

mocha

mocha的功能丰富 支持在node环境和浏览器环境下运行 异步的支持也很友好

断言库

assert

使用简单 反馈信息明确

should.js

链式操作 功能强大

开始实践

全局安装mocha

1
npm install mocha -g

也可以选择安装在项目中

1
npm install macha -s

mocha会对你test文件夹下的js文件进行测试
项目目录

一个例子

1
2
3
4
5
6
7
8
9
10
11
// 引入需要的依赖 这里的断言库选择assert
var assert = require('assert')
// describe 代表你要对哪个对象的哪个方法进行测试 可以嵌套
describe('Array', function() {
describe('#indexOf()', function() {
// it 代表测试的具体case
it('should return -1 when the value is not present', function() {
assert.equal(-1, [1, 2, 3].indexOf(0))
})
})
})

在浏览器中进行测试

karam

安装依赖

初始化测试

1
karam init

具体参数设置查询karam官网

集成测试

持续集成指的是,频繁地(一天多次)将代码集成到主干。

  • 快速发现错误。每完成一点更新,就集成到主干,可以快速发现错误,定位错误也比较容易。
  • 防止分支大幅偏离主干。如果不是经常集成,主干又在不断更新,会导致以后集成的难度变大,甚至难以集成。

Travis CI

github上的项目 你托管在github上的项目可以使用它来进行集成测试
Travis CI

登陆你的github账号

开启对项目的检测

这样你在push request代码的时候Travis CI会对项目进行自动的集成测试

项目目录

总结

编写测试是一个很好的习惯

  • 有助于我们理清代码的思路
  • 维护我们的代码质量
  • 提高开发效率

同源策略及跨域资源共享

发表于 2017-08-06

作为一名前端,在日常开发的过程中我们使用ajax请求获取数据,我们常常会遇到跨域的问题,结合自己的实践和网上的资料,对浏览器的同源策略,解决跨域问题的常见方法进行总结。

同源策略

Q: 为什么我们需要同源?

A: 因为安全性的考虑。比如我们在登陆一个网站时留下的个人信息,证明身份的用户信息,登录的cookie信息,如果没有同源策略的限制别人就会很容易拿到这些信息,用户的安全性也就无法保障了。
Q: 怎么才算同源?

A: 协议相同 域名相同 端口相同

Q: 同源策略限制了哪些信息?

A: (0) Cookie、LocalStorage、IndexDB
Cookie表明了用户的身份 LocalStorage、IndexDB也可能包含用户的信息
(1) DOM 无法获得
DOM??? 为什么DOM也无法获得呢?
因为如果有一个伪装的网站在iframe中引入你真正要访问的网站 在子iframe中父级页面获得DOM元素后可以对ta进行事件的监听能够获得用户输入的密码等
(2) AJAX 请求无法发送
请求中的 cookie 会存储在浏览器客户端本地当 AJAX请求发出时会根据请求的域名带上 cookie 信息,如果没有同源策略限制就可以在恶意的页面中发起请求

源的更改

在我们需要在父域和子域之间进行正常的通信的时候,我们可以将父域和子域中的 document.domain 设置为父域,这样这两个页面会被当成同源。

如何绕过

同源策略对我们来说是必要的,但是有些合理的数据传递也被限制了,但是我们还是能够通过一些其他的手段来间接的传递数据

0.怎么做呢?

src属性具有天生跨域的能力 所以带有src属性的元素比如script iframe img 都不受同源策略的影响

1.JSONP

JSONP和JSON有什么关系?

JSON是自从AJAX2.0以来使用最广泛的数据传输格式 [] 表示 数组 {} 表示 对象
JSONP是一种数据的传输方式
所以说关系好像 JAVA 和 JavaScript(就是没啥关系)

那我们如何使用JSONP请求数据呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 创建一个script
var script = document.createElement('script')
document.body.appendChild(script)
// 前端接到数据后的处理函数
window.xxx = function (value) {
console.log(value)
}
// src设置为要请求的url在参数上传出给后端的参数 这个参数是你和后端规定好的可以是任意
script.src = 'http://x.stuq.com:7001/json?callback=xxx'
//在后端接到这个callback的值也就是你要执行处理数据的方法名
...
// 然后返回给前端
...
//

2.CORS

CORS 全称为跨域资源共享(cross-origin sharing standard)规定了在跨源资源访问时的规则
在跨源请求访问时会带上 Header 信息来让 server 端判断时候允许访问资源。

简单请求

同时满足下列所有条件的时候称为简单请求

  • 请求方法为 GET POST HEAD
  • Request header 在如下的列表中
    • Content-Type
    • Content-Language
    • Accept-Language
    • Accept
    • DPR
    • Downlink
    • Width
    • Viewport-Width
    • Save-Data
  • Content-Type 为下面三种类型之一
    • multipart/form-data
    • text/plain
    • application/x-www-form-urlencode
  • 请求中的任意XMLHttpRequestUpload 对象均没有注册任何事件监听器;
    XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问。
  • 请求中没有使用 ReadableStream 对象。

需要预检的请求

除简单请求外,请求都是需要预检的,在发出正常请求之前需要先发出一个 OPTIONS 请求,这个请求只携带少量的信息,来询问服务端是否允许实际请求。

Access-Control-Request-Methods
Access-Control-Request-Headers

Access-Control-Allow-Origin
Access-Control-Allow-Methods
Access-Control-Allow-Headers
Access-Control-Max-Age
Access-Control-Allow-Credentials

设置Access-Control-Allow-Origin
第一种.将你需要跨域的站点设置上’Access-Control-Allow-Origin’, ‘http://xx.stuq.com'
第二种.对所有站点的请求都不拦截’Access-Control-Allow-Origin’, ‘*’
这样设置之后就不用做任何事情了
参数还可以根据请求的站点动态设置,这样就保证了灵活性

3.location.hash

使用iframe 在iframe中加载一个与邀请同域的页面 在这个页面中发起请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 创建一个iframe
var iframe = document.createElement('iframe')
//加载一个同域的页面 我们在这个页面发ajax
iframe.src = 'http://x.stuq.com:7001/public/hash.html'
document.body.appendChild(iframe)
// 进入hash.html这个页面
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
var res = JSON.parse(xhr.responseText)
parent.location.href = `http://y.stuq.com:7001/public/3.html#msg=${res.msg}`
}
}
xhr.open('GET', 'http://x.stuq.com:7001/json', true)
xhr.send(null)
// 监听hash改变事件 取得信息
window.onhashchange = function () {
let hash = location.hash
let reg = /[#&][^#&]+=[^#&]+/g
let arr = hash.match(reg)
let obj = {}
arr.forEach(item => {
let tempArr = item.substring(1).split('=')
let key = decodeURIComponent(tempArr[0])
let val = decodeURIComponent(tempArr[1])
obj[key] = val
})
console.log(obj)
}

4.Window.name

开始也是与上面一样
不同的地方在于在iframe中请求到数据后如何带回来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 创建一个iframe
var iframe = document.createElement('iframe')
//加载一个同域的页面 我们在这个页面发ajax
iframe.src = 'http://x.stuq.com:7001/public/name.html'
document.body.appendChild(iframe)
var times = 0
// 监听iframe的onload事件当页面每两次加载时取数据
iframe.onload = function () {
if (++times === 2) {
console.log(JSON.parse(iframe.contentWindow.name))
}
}
// 进入name.html这个页面
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
//使用window.name将数据保存
window.name = xhr.responseText
//跳回和parent页面同源的页面
location.href = 'http://y.stuq.com:7001/public/index.html'
}
}
xhr.open('GET', 'http://x.stuq.com:7001/json', true)
xhr.send(null)

5.HTML5 的psotmessage

开始也是与上面一样
不同的地方在于在iframe中请求到数据后如何带回来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 创建一个iframe
var iframe = document.createElement('iframe')
// 加载一个同域的页面 我们在这个页面发ajax
iframe.src = 'http://x.stuq.com:7001/public/post.html'
document.body.appendChild(iframe)
// 监听message事件
window.addEventListener('message', function(e) {
console.log(JSON.parse(e.data))
}, false);
// 进入post.html这个页面
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
// 第一个参数是传递的数据 第二个参数是那些页面可以接收到信息 * 表示所有 如果确切知道被谁使用 应该设置好具体的值
parent.postMessage(xhr.responseText, '*')
}
}
xhr.open('GET', 'http://x.stuq.com:7001/json', true)
xhr.send(null)

6. document.domain

当二级域名相同可以分别对 a.test.com/index.html 和 b.test.com/index.html 设置 document.domain = test.com
这样两个页面会当成同域处理

技术的适用场景

  • 最省力 设置Access-Control-Allow-Origin 为 * 但是不安全 不适用于比较私密的信息
  • 后端能控制 则使用JSONP
  • 后端无法控制 允许上传一个页面 window.name location.hash postMessage(前提是现代浏览器…)
12

Chen Haonan

write something

13 日志
5 标签
© 2020 Chen Haonan
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4