深入浅出Electron:原理、工程与实践
上QQ阅读APP看书,第一时间看更新

2.10 渲染进程Node.js环境

前面介绍初始化主进程Node.js环境时提到,Electron在启动Chromium时为其传入了一个ElectronMainDelegate的代理对象,在Chromium运行过程中,每创建一个浏览器窗口,也就是说每创建一个渲染进程,就会执行这个代理对象的CreateContentRenderer-Client方法。

CreateContentRendererClient方法会创建一个ElectronRendererClient对象(shell\renderer\electron_renderer_client.cc)。ElectronRendererClient对象间接继承自Chromium的Content-RendererClient类型。这个类型为渲染进程的生命周期暴露出了一系列的事件(姑且称之为事件),比如DidCreateScriptContext(渲染进程的JavaScript执行环境准备就绪)、WillRelease-ScriptContext(将要卸载渲染进程的JavaScript执行环境)等。

Electron就是在DidCreateScriptContext事件中为渲染进程初始化Node.js环境的,代码如下所示:

void ElectronRendererClient::DidCreateScriptContext(v8::Handle<v8::Context>renderer_context,
    content::RenderFrame* render_frame) {
  auto prefs = render_frame->GetBlinkPreferences();
  bool is_main_frame = render_frame->IsMainFrame();
  bool is_devtools = IsDevToolsExtension(render_frame);
  bool allow_node_in_subframes = prefs.node_integration_in_sub_frames;
  bool should_load_node = (is_main_frame || is_devtools || allow_node_in_subframes) &&
      !IsWebViewFrame(renderer_context, render_frame);
  if (!should_load_node) return;
  injected_frames_.insert(render_frame);
  if (!node_integration_initialized_) {
    node_integration_initialized_= true;
    node_bindings_->Initialize();
    node_bindings_->PrepareMessageLoop();
  } else { node_bindings_->PrepareMessageLoop(); }
  if (!node::tracing::TraceEventHelper::GetAgent())
    node::tracing::TraceEventHelper::SetAgent(node::CreateAgent());
  bool initialized = node::InitializeContext(renderer_context);
  CHECK(initialized);
  node::Environment* env = node_bindings_->CreateEnvironment(renderer_context, nullptr);
  env->set_force_context_aware(true);
  environments_.insert(env);
  electron_bindings_->BindTo(env->isolate(), env->process_object());
  gin_helper::Dictionary process_dict(env->isolate(), env->process_object());
  BindProcess(env->isolate(), &process_dict, render_frame);
  node_bindings_->LoadEnvironment(env);
  if (node_bindings_->uv_env() == nullptr) {
    node_bindings_->set_uv_env(env);
    node_bindings_->RunMessageLoop();
  }
}

渲染进程初始化Node.js环境的逻辑与主进程初始化Node.js的逻辑大同小异,值得注意的是,如果开发者没有开启在iframe中初始化Node.js环境的开关,且当前渲染进程是一个iframe页面产生的,那么此方法直接退出,不执行初始化Node.js环境的逻辑。

了解了iframe内是如何初始化Node环境的,接着就看一下WebWorker内是如何初始化Node.js环境的。

这项工作也是由ElectronRendererClient对象完成的,当页面上的WebWorker的JavaScript环境准备完成后,会触发ElectronRendererClient对象的WorkerScriptReadyForEvaluationOnWorkerThread事件,Electron在这个事件的处理方法中执行了为WebWoker初始化Node.js环境的逻辑(shell\renderer\web_worker_observer.cc)。