react-electron-web-view源码分析

1. 介绍

1.1. 源码地址

react-electron-web-view

1.2. 核心文件

2. 实现原理

由于react不支持webview标签,所以我们需要在react标签生成后动态插入webview标签.
要实现electron webview标签我们需要分别实现以下三部分内容在react组件中的绑定:
1. props:属性,帮助我们定义webview的属性
2. methods:挂载在this对象上的方法,帮助我们使用ref来呼叫webview方法
3. events:挂载在this对象上的事件,用于监听webview事件

2.1. 常数文件

在常数文件中,枚举了上节所说的三部分内容:
1. events:事件枚举
2. methods:方法枚举
3. props:属性枚举
4. changableProps:可变属性枚举

2.2. 组件文件

2.2.1. componentDidMount

获取render中的ref绑定的container对象.

const container = ReactDOM.findDOMNode(this.c);  

获取所有属性并插入webview标签到container.

let propString = '';  
//循环属性枚举列表
Object.keys(props).forEach((propName) => {  
  //检测当前webview中是否有该值
  if (typeof this.props[propName] !== 'undefined') {
    if (typeof this.props[propName] === 'boolean') {
      propString += `${propName}="${this.props[propName] ? 'on' : 'off'}" `;
    } else {
      propString += `${propName}=${JSON.stringify(this.props[propName].toString())} `;
    }
  }
});
//特例:如果检测到className则插入class
if (this.props.className) {  
  propString += `class="${this.props.className}" `;
}
//插入webview标签
container.innerHTML = `<webview ${propString}/>`;  

定义this.view并添加事件检测

this.view = container.querySelector('webview');  
this.ready = false; //设置就绪状态  
//当就绪时,设置就绪状态为true,同时添加事件监听
this.view.addEventListener('did-attach', (...attachArgs) => {  
  this.ready = true;
  events.forEach((event) => {
    this.view.addEventListener(event, (...eventArgs) => {
      //这里使用了lodash的camelcase来驼峰事件,使用时传递onDidStopLoading的函数属性来做监听.
      const propName = camelCase(`on-${event}`);
      // console.log('Firing event: ', propName, ' has listener: ', !!this.props[propName]);
      //检测是否有该属性名,如果有,则执行该属性函数
      if (this.props[propName]) this.props[propName](...eventArgs);
    });
  });
  //这里需要设置下特例用于监听did-attach
  if (this.props.onDidAttach) this.props.onDidAttach(...attachArgs);
});

增加事件监听

methods.forEach((method) => {  
  this[method] = (...args) => {
    //判断就绪状态
    if (!this.ready) {
      throw new Error('WebView is not ready yet, you can\'t call this method');
    }
    return this.view[method](...args);
  };
});

设置debug工具

this.setDevTools = (open) => {  
  if (open && !this.isDevToolsOpened()) {
    this.openDevTools();
  } else if (!open && this.isDevToolsOpened()) {
    this.closeDevTools();
  }
};

2.2.2. componentDidUpdate

当组件更新时检测所有可变属性

Object.keys(changableProps).forEach((propName) => {  
  if (this.props[propName] !== prevProps[propName]) {
    //如果为特殊值,直接设置属性.否则呼叫this绑定的对应方法
    if (changableProps[propName] === '__USE_ATTR__') {
      this.view.setAttribute(propName, this.props[propName]);
    } else {
      this[changableProps[propName]](this.props[propName]);
    }
  }
});

2.2.3. 其他配置

//扩展属性方法
ElectronWebView.propTypes = Object.assign({  
  className: PropTypes.string,
  style: PropTypes.object,
}, props);
//扩展监听函数方法
events.forEach((event) => {  
  ElectronWebView.propTypes[camelCase(`on-${event}`)] = PropTypes.func;
});

3.使用

定义组件的属性和事件监听

<WebView src={url} plugins onDidStopLoading={this.handleLoad} onDidFailLoad={onLoadError} ref="webview"></WebView>  

使用ref来执行方法

this.refs.webview.executeJavaScript('alert("Hello world")');