• post-message-stream的学习-metamask


    /post-message-stream

    post-message-stream

    Sets up a duplex object stream over window.postMessage  在window.postMessage上设置一个双工对象流

    所以我们先学习一下window.postMessage:https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage

    window.postMessage

    otherWindow.postMessage(message, targetOrigin, [transfer]);
    otherWindow:其他窗口的一个引用
    
    message:要发送的数据信息
    
    targetOrigin:该信息要发送给的目标窗口,"*" 则表示无限制。如果你明确的知道消息应该发送到哪个窗口,那么请始终提供一个有确切值的targetOrigin,而不是*。不提供确切的目标将导致数据泄露到任何对数据感兴趣的恶意站点
    
    transfer(可选):是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。

    然后其他窗口就通过监听来接受别的窗口发来的数据:

    window.addEventListener("message", receiveMessage, false);
    
    function receiveMessage(event)
    {
      // For Chrome, the origin property is in the event.originalEvent
      // object. 
      // 这里不准确,chrome没有这个属性
      // var origin = event.origin || event.originalEvent.origin; 
      var origin = event.origin
      if (origin !== "http://example.org:8080")//如果你确实希望从其他网站接收message,请始终使用origin和source属性验证发件人的身份
        return;
    
      // ...
    }

    message 的属性,即receiveMessage中的event:

    data
        从其他 window 中传递过来的对象。
    origin
        调用 postMessage  时消息发送方窗口的 origin . 这个字符串由 协议、“://“、域名、“ : 端口号”拼接而成。例如 “https://example.org (隐含端口 443)”、“http://example.net (隐含端口 80)”、“http://example.com:8080”。请注意,这个origin不能保证是该窗口的当前或未来origin,因为postMessage被调用后可能被导航到不同的位置。
    source
        对发送消息的窗口对象的引用; 您可以使用此来在具有不同origin的两个窗口之间建立双向通信。

     

    /*
     * A窗口的域名是<http://example.com:8080>,以下是A窗口的script标签下的代码:
     */
    
    var popup = window.open(...popup details...);
    
    // 如果弹出框没有被阻止且加载完成
    
    // 这行语句没有发送信息出去,即使假设当前页面没有改变location(因为targetOrigin设置不对)
    popup.postMessage("The user is 'bob' and the password is 'secret'",
                      "https://secure.example.net");
    
    // 假设当前页面没有改变location,这条语句会成功添加message到发送队列中去(targetOrigin设置对了)
    popup.postMessage("hello there!", "http://example.org");
    
    function receiveMessage(event)
    {
      // 我们能相信信息的发送者吗?  (也许这个发送者和我们最初打开的不是同一个页面).
      if (event.origin !== "http://example.org")
        return;
    
      // event.source 是我们通过window.open打开的弹出页面 popup
      // event.data 是 popup发送给当前页面的消息 "hi there yourself!  the secret response is: rheeeeet!"
    }
    window.addEventListener("message", receiveMessage, false);
    /*
     * 弹出页 popup 域名是<http://example.org>,以下是script标签中的代码:
     */
    
    //当A页面postMessage被调用后,这个function被addEventListenner调用
    function receiveMessage(event)
    {
      // 我们能信任信息来源吗?
      if (event.origin !== "http://example.com:8080")
        return;
    
      // event.source 就当前弹出页的来源页面
      // event.data 是 "hello there!"
    
      // 假设你已经验证了所受到信息的origin (任何时候你都应该这样做), 一个很方便的方式就是把enent.source
      // 作为回信的对象,并且把event.origin作为targetOrigin
      event.source.postMessage("hi there yourself!  the secret response " +
                               "is: rheeeeet!",
                               event.origin);
    }
    
    window.addEventListener("message", receiveMessage, false);

    继续post-message-stream的学习

    var streamA = new PostMessageStream({
      name: 'thing one',
      target: 'thing two',
    })
    
    var streamB = new PostMessageStream({
      name: 'thing two',
      target: 'thing one',
    })
    
    streamB.on('data', (data) => console.log(data))
    streamA.write(chunk)

    constructor arguments

    var messageStream = new PostMessageStream({
    
      // required
    
      // name of stream, used to differentiate
      // when multiple streams are on the same window 
      name: 'source',
    
      // name of target stream 
      target: 'sink',
    
      // optional
    
      // window to send the message to
      // default is `window`
      window: iframe.contentWindow,
      
    })

    其源代码:

    const DuplexStream = require('readable-stream').Duplex
    const inherits = require('util').inherits
    
    module.exports = PostMessageStream
    
    inherits(PostMessageStream, DuplexStream)
    
    function PostMessageStream (opts) {
      DuplexStream.call(this, {
        objectMode: true,//如果想创建一个的可以压入任意形式数据的可读流,只要在创建流的时候设置参数objectModetrue即可,例如:Readable({ objectMode: true })。
      })
    
      this._name = opts.name
      this._target = opts.target
      this._targetWindow = opts.targetWindow || window
      this._origin = (opts.targetWindow ? '*' : location.origin)
    
      // initialization flags
      this._init = false
      this._haveSyn = false
    
      window.addEventListener('message', this._onMessage.bind(this), false)//监听消息
      // send syncorization message
      this._write('SYN', null, noop)//先将要进行第一次握手发送的SYN或收到第一次握手后发送第二次握手的syn准备好
      this.cork()/强制把所有写入的数据都缓冲到内存中
    }
    
    // private
    PostMessageStream.prototype._onMessage = function (event) {
      var msg = event.data
    
      // validate message
      if (this._origin !== '*' && event.origin !== this._origin) return
      if (event.source !== this._targetWindow) return
      if (typeof msg !== 'object') return
      if (msg.target !== this._name) return
      if (!msg.data) return
    
      if (!this._init) {//如果该流都还没有初始化,那就说明现在首先要进行三次握手来连接
        // listen for handshake
        if (msg.data === 'SYN') {//收到syn说明收到的是第一次握手或第二次
          this._haveSyn = true
          this._write('ACK', null, noop)//然后写ACK生成第二次握手或第三次发送,此时已经成功同步
        } else if (msg.data === 'ACK') {//收到ACK说明收到的是第二次或第三次握手
          this._init = true //说明初始化连接已经成功
          if (!this._haveSyn) {//如果_haveSyn为false,那就说明收到的是第二次握手
            this._write('ACK', null, noop) //所以还需要再发送一次ACK进行第三次握手
          }
          this.uncork()//输出被缓冲的数据
        }
      } else {//否则就是已经连接上了
        // forward message
        try {
          this.push(msg.data)//将post来的数据push到流中,将调用下面的_write函数
        } catch (err) {
          this.emit('error', err)//出错则触发error事件
        }
      }
    }
    
    // stream plumbing
    PostMessageStream.prototype._read = noop
    
    PostMessageStream.prototype._write = function (data, encoding, cb) {
      var message = {
        target: this._target,
        data: data,
      }
      this._targetWindow.postMessage(message, this._origin)
      cb()
    }
    
    // util
    
    function noop () {}

    扩展知识:

    (1) writable.cork() /writable.uncork()

    writable.cork()
    writable.cork() 方法会强制把所有写入的数据都缓冲到内存中。 当调用 stream.uncork() 或 stream.end() 方法时,被缓冲的数据才会被输出。
    当写入大量小块数据到流时(因为缓存是有大小的,若都是小块数据占据了大内存,剩下的又不能装入一个数据,这样就会浪费内存),内部缓冲可能失效,从而导致性能下降,writable.cork() 主要用于避免这种情况。

    writable.uncork()
    writable.uncork() 方法会输出 stream.cork() 方法被调用后缓冲的全部数据。
    当使用 writable.cork() 和 writable.uncork() 来管理流写入缓存,建议使用 process.nextTick() 来延迟调用 writable.uncork()。 通过这种方式,可以对单个 Node.js 事件循环中调用的所有 writable.write() 方法进行批处理。

    stream.cork();
    stream.write('一些 ');
    stream.write('数据 ');
    process.nextTick(() => stream.uncork());


    如果一个流上多次调用 writable.cork() 方法,则必须调用同样次数的 writable.uncork() 方法才能输出缓冲的数据。

    stream.cork();
    stream.write('一些 ');
    stream.cork();
    stream.write('数据 ');
    process.nextTick(() => {
      stream.uncork();
      // 数据不会被输出,直到第二次调用 uncork()。
      stream.uncork();
    });

    (2)util.inherits-即nodejs的一个原型继承函数

    util.inherits(constructor, superConstructor)是一个实现对象间原型继承的函数

    举例说明:

    var util = require('util'); 
    function father() { 
        this.name = 'John'; //这为三个在构造函数中定义的属性
        this.age = 1979; 
        this.showProperty = function() { 
        console.log('got five house'); 
        }; 
    } 
    father.prototype.showName = function() { //这个是在原型中定义的函数
        console.log(this.name);
    }; 
    function son() { 
        this.name = 'bob';
        this.age = 1997;
    } 
    util.inherits(son, father); //使用util.inherits,所以son仅仅只能继承father在原型中定义的showName函数
    var dad = new father(); 
    dad.showName(); 
    dad.showProperty(); 
    console.log(dad); 
    var child = new son(); 
    child.showName(); //成功
    // child.showProperty(); //失败
    console.log(child);

    返回:

    userdeMacBook-Pro:stream-learning user$ node test.js 
    John
    got five house
    father { name: 'John', age: 1979, showProperty: [Function] }
    bob

    调用child.showProperty();会失败:

    /Users/user/stream-learning/test.js:23
    child.showProperty(); 
          ^
    
    TypeError: child.showProperty is not a function

    (3)对JavaScript prototype 使用介绍想了解的可以去这个博客看看,写的很好

    (4)javascript:apply方法 以及和call的区别 (转载)

    (5)readable-stream

    https://github.com/nodejs/readable-stream

    https://nodejs.org/dist/v10.11.0/docs/api/stream.html

    Usage

    You can swap your require('stream') with require('readable-stream') without any changes, if you are just using one of the main classes and functions.

    const {
      Readable,
      Writable,
      Transform,
      Duplex,
      pipeline,
      finished
    } = require('readable-stream')

    Note that require('stream') will return Stream, while require('readable-stream') will return Readable. We discourage using whatever is exported directly, but rather use one of the properties as shown in the example above.




  • 相关阅读:
    http://maxie.cnblogs.com/
    有一种爱叫错过
    Lotuser进阶系列(转)——多目录环境中的单点登陆1
    DOMINO中实现PDF在线编辑控件 and so on......(三)
    DOMINO中实现PDF在线编辑控件 and so on......(一)
    Lotuser进阶系列(转)——多目录环境中的单点登陆2
    在两个代理之间传递参数
    利用 DSAPI 为 Domino Web 用户定制用户名和口令认证
    通过 Lotus Domino Java 代理消费 Web 服务
    代理中如何获取参数么?
  • 原文地址:https://www.cnblogs.com/wanghui-garcia/p/9792498.html
Copyright © 2020-2023  润新知