标签: proxy

  • 在 Puppeteer 里使用代理服务器科学上网

    在 Puppeteer 里使用代理服务器科学上网

    使用 Puppeteer 录制视频的时候,如果服务器在国内,可能会有一些网站打不开。这个时候,我们可以要求 Puppeteer 使用代理服务器。

    0. 配置科学上网

    参考两篇旧文,其实原理一样,只是用的软件不一样:

    配置完成之后,通过浏览器应该可以正常访问。

    1. 使用代理服务器

    启动 Puppeteer 的时候,可以传入参数 args,进行各种调整,完整的列表请参考:List of Chromium Command Line Switches

    关于代理服务器,有若干个选项,我们要用的是 --proxy-server,方法是:

    puppeteer.launch({
      args: [
        '--proxy-server=socks5://127.0.0.1:1080',
      ],
    });

    2. 使用 PAC 文件

    但是这样所有流量都会走代理服务器,也不符合我们的期待,所以最好使用 PAC 文件。参数名称是:--proxy-pac-url,但请注意,因为 Chromium 的 bug,这个参数只在 headless: false,即有界面的时候才会生效。好在我们是为了录视频,所以本来就要打开界面。

    所以最终的启动代码就是:

    puppeteer.launch({
      headless: false,
      args: [
        '--proxy-server=socks5://127.0.0.1:1080',
        '--proxy-pac-url=http://localhost/autoproxy.pac',
      ],
    });

  • 使用 Proxy 创建有魔术属性/方法的类

    使用 Proxy 创建有魔术属性/方法的类

    前几天在 SF 回答了这个问题:如何使用proxy,如何在内部拦截get方法,然后翻了翻以前写的博客:使用 Proxy 添加魔术属性/方法,发现上次写完代理对象就停笔了,所以今天补全一下:创建有魔术属性/方法的类。这样就比较完整了。

    JavaScript 构造函数的特点

    ES6 增加了 class 关键字,梳理了面向对象的语法,现在我们可以这样定义一个类:

    class Person {
      constructor(name) {
        this.name = name; 
      }
    
      hello() {
        return `Hello, my name is ${this.name}.`;
      }
    }

    如果你有其它面向对象语言的经验,应该很容易理解这段代码。

    不过 JS 的构造函数特别,它支持 return 一个其它对象,作为 new SomeClass() 的结果。(如果不 return 或者 return 一个空对象,那么 new SomeClass() 得到的就是 SomeClass 的实例。)

    也就是说,原则上,我们可以这么做:

    class Person {
      constructor(name) {
        this.name = name;
        return {name: 'Meathill'};
      }
    }
    
    const person = new Person('张三');
    console.log(person.name); // 'Meathill'

    结合 Proxy

    理解了上一节的内容,我们就很容易得到这样一个类:

    class Person {
      constructor(name) {
        this.name = name;
    
        return new Proxy(this, {
          get(target, property) {
            if (property in target) {
              return target[property];
            }
            console.warn('Sorry, I can't do that.');
          }
        }
      }
    }

    在这个类的构造函数里,我返回了一个 Proxy 实例,代理了对真正 Person 实例的访问。当访问的属性/方法在实例上时,就返回需要的属性/方法,否则的话,输出警告。

    实际上,Proxy 的 getset 的功能远不止如此,上面的代码只是一些演示。

    用途

    魔法属性/方法主要有以下用途:

    1. 对象 a 要使用一部分对象 b 的功能,但是又不方便直接用原型链。比如上一篇文章的场景,我提供类 VElement 作为接口,实际完成工作的是另一个沙箱中的 Element。
    2. 不知道会怎么访问对象,希望所有访问都照顾到。
    3. 希望捕获到对对象的修改,也就是 Vue 3.0 的核心修改。

    Vue 3.0

    Vue 1.x & 2.x 期间,都在使用 ES5 的 Object.defineProperty 拦截对对象的修改,实现响应式。这样的做法看起来很神奇,给 Vue 带来了巨大的成功。但是这样做也有坏处:

    1. 声明实例时需要很多预处理工作,而且数据量越大处理的时间越久
    2. 不支持某些数组操作
    3. 不支持其它数据类型,比如 Set、Map
    4. 不支持后续的数据观察

    使用 Proxy 之后,以上问题全部都迎刃而解,甚至,因为 Proxy 是原生 API,性能表现更好,取得了内存减半、速度加倍的效果。

    想了解更多 Vue 3.0 的新特性,可以去看我在 SF 的分享:迎接 Vue 3.0。(注:这是免费广告,我不会从新购用户取得收益。)


    参考文章:

    Constructor, operator “new”(构造函数和操作符 “new”)

  • 使用 Proxy 添加魔术属性/方法

    使用 Proxy 添加魔术属性/方法

    最近在开发我厂的 QA 工具时,遇到一个问题。我需要模拟 Puppeteer 的所有方法,以便兼容原先的 JS 文件。Puppeteer 提供一个 .asElement() 方法,可以把函数执行结果转换成一个伪 DOM Element(如果函数返回的就是 DOM Element 的话),然后我们就可以在 Node.js 里调用原本属于 DOM 的方法,比如 .focus()。Pupputeer 会替我们完成映射和函数调用,并且返回结果。

    对于大部分对象来说,我只要模拟对应的属性、方法,然后用自己的函数实现功能即可。但是 DOM Element 有上百个属性和方法,手工实现一遍实在太低效了。必须寻找其它途径。

    好在我之前看过 Proxy 的介绍,赶紧翻出文档和书又复习两遍,就大概知道怎么做了。

    Proxy 类如其名,可以“代理”对某个对象的访问。你可以把他理解成明星的经纪人。明星成名之前都是自己处理一切事务,有了经纪人之后,大部分事务就由经纪人负责,但仍然有一些事情需要明星自己处理。

    Proxy 的用法很简单,实例化时,把要代理的对象传进去,定义一下代理方法就好。

    const obj = { name: 'meathill' };
    new Proxy(obj, {
      get(target, property) {
        // 如果对象中有要求的属性或方法,则返回
        if (property in target) {
          return target[property];
        }
        // 没有的话,进行其它处理
        return 'hello';
      }
    });
    
    obj.name // 'meathill'
    obj.age // 'hello'
    obj.sex // 'hello'

    接下来,比如我们访问 obj.foo,那么代理就会生效,它会先检查 obj,如果这个对象上本来就有 foo 属性,就会返回;如果没有,则会调用我们定义的方法来处理。

    如此一来,我们可以定义一个 VElement 类,这个类可以实现一些特殊方法,比如 .type(str) 输入,.click() 点击等;然后用 Proxy 代理其它方法和属性,让对象进入插件 Context 执行。


    关于如何创建具有魔法属性/方法的类,请移步阅读 使用 Proxy 创建有魔术属性/方法的类


    Proxy 还有其它方法也很有用,尤其是 get 对应的 set ,以后再介绍。大家可以自己抽空研究下。

    参考

    • 阮一峰的 ES6
    • 《深入理解 ES6》