尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

NW.js 中的 JavaScript 上下文(JavaScript Contexts):Separate 与 Mixed 两种模式的原理与实战

NW.js 中的 JavaScript 上下文(JavaScript Contexts):Separate 与 Mixed 两种模式的原理与实战 NW.js 中的 JavaScript 上下文JavaScript ContextsSeparate 与 Mixed 两种模式的原理与实战【免费下载链接】nw.jsCall all Node.js modules directly from DOM/WebWorker and enable a new way of writing applications with all Web technologies.项目地址: https://gitcode.com/gh_mirrors/nw/nw.js本文深入解析 NW.js 的 JavaScript ContextJavaScript 上下文体系为什么不同窗口/框架各自拥有独立的全局对象NW.js 如何在浏览器上下文之外引入 Node 上下文以及 Separate Context Mode分离上下文模式与 Mixed Context Mode混合上下文模式两种架构的差异、启用方式和适用场景。读完本文你将掌握 NW.js 中require()、global、process、Buffer等对象在不同上下文中的行为差异理解instanceof跨上下文失效的根因并学会编写跨上下文可靠的类型检查代码。什么是 JavaScript ContextJavaScript Context上下文指一段脚本运行时所处的环境。运行在不同窗口中的脚本各自处于不同的 JavaScript 上下文中每个窗口拥有自己的全局对象global object以及自己的一套全局构造函数如Array、Object、Date、Function。这是所有 Web 浏览器普遍遵循的设计也是一种保护机制原型污染被隔离当某个库例如 Prototype.js 这类框架或一段脚本替换、扩展了某个对象的原型时其他窗口中的同类对象不受影响全局污染被限制当程序员写错代码例如构造器调用时漏写new错误虽然会污染当前全局作用域却无法波及更大范围其他窗口安全隔离恶意应用无法访问其他窗口中的机密数据结构。当一个脚本访问另一个上下文中定义的对象/函数时JS 引擎会临时进入目标上下文执行执行完毕后再离开。这个进入—离开机制是理解后续所有上下文问题的前提。NW.js 中的上下文架构NW.js 基于 Chrome Apps 的架构构建应用启动时会自动加载一个不可见的 background page后台页面每当创建一个新窗口时同时也会创建一个新的 JavaScript 上下文。在此基础上NW.js 引入 Node 运行时使 Node.js 模块可以在不同位置加载默认行为下Node 模块运行在 background page 的上下文中在 Mixed Context Mode混合上下文模式下Node 模块可以运行在每个窗口或 frame框架的上下文中。两种模式的差异详见下文。Separate Context Mode分离上下文模式默认情况下除了浏览器自身创建的上下文NW.js 还为运行 Node 模块额外引入了一个 Node ContextNode 上下文运行于后台页面。因此 NW.js 中存在两种类型的 JavaScript 上下文Browser Context浏览器上下文运行传统 Web 脚本Node ContextNode 上下文运行 Node.js 模块。Web Worker 提示在 Manifest 中添加chromium-args: --enable-node-worker即可在 Web Worker 中访问 Node.js API。Browser Context浏览器上下文加载脚本的方式凡是通过传统 Web 方式加载或内嵌的脚本都运行在 Browser Context 中典型方式包括使用script元素使用 jQuery 的$.getScript()使用 RequireJS 等模块加载器。全局对象Browser Context 中可使用两类全局对象JS 内置对象builtin objects如Date、Error、TypedArray等Web API如 DOM API、XMLHttpRequest等。创建新的 Browser Context不同窗口window和不同 frame框架拥有各自独立的上下文。因此当你创建一个新的 frame 或 window 时就得到一个新的 Browser Context。在 Browser Context 中访问 Node.js 与 NW.js APINW.js 会把 Node Context 的部分对象拷贝到 Browser Context 中使运行在 Browser Context 的脚本可以直接访问对象说明nw所有 NW.js API 的集合对象对应 References 文档中的 API 列表globalNode Context 的全局对象等价于nw.globalrequire加载 Node.js 模块的require()函数与nw.require()类似但额外支持用require(nw.gui)加载 NW.js API 模块processNode.js 的 process 模块等价于nw.processBufferNode.js 的 Buffer 类这一拷贝逻辑可以在源码 src/nw_content.cc 中看到ContextCreationHook中构造了一个包含global、process、Buffer、require四个符号的数组逐一从 Node 全局对象取值并写入nw对象。而 src/resources/nw_pre13_shim.js 则在检测到nw对象存在后为浏览器上下文建立require代理专门处理require(nw.gui)的情况并在--mixed-context下将process、Buffer、global从nw拷贝到浏览器全局作用域。Browser Context 中require()的相对路径解析Browser Context 中的相对路径按照主 HTML 文件的路径解析与所有浏览器的行为一致。Node ContextNode 上下文加载脚本的方式以下方式加载的脚本运行在 Node Context通过 Node.js 的require()API 加载的脚本通过 Manifest 中的node-main字段指定的脚本。根据 Manifest Format 文档node-main指向一个 node.js 脚本文件该脚本会在启动时、第一个 DOM 窗口加载之前于 Node Context 中执行字符串中文件路径之前的参数会被当作 Node.js 命令行参数处理。全局对象Node Context 中的脚本可以使用与 Browser Context 相同的 JS 内置对象此外还可以使用 Node.js 定义的全局对象例如__dirname、process、Buffer等。注意Node Context 中没有 Web API。如何在 Node Context 中使用浏览器 API见下文访问浏览器与 NW.js API。创建新的 Node Context在 Separate Context Mode 下所有 Node 模块共享同一个 Node Context。如果需要新的 Node Context有以下两种途径调用Window.open()创建窗口时将new_instance选项设为true以--mixed-context命令行选项启动 NW.js切换到 Mixed Context 模式见下文。访问浏览器与 NW.js APINode Context 中没有浏览器侧或 NW.js API例如alert()、document.*、nw.Clipboard等。要访问浏览器 API必须把相应对象如window对象作为参数传给 Node Context 中的函数。例如运行在 Node Context 中的脚本myscript.js// el should be passed from browser context exports.setText function(el) { el.innerHTML hello; };在浏览器侧index.html调用div idel/div script var myscript require(./myscript); // pass the el element to the Node function myscript.setText(document.getElementById(el)); // you will see hello in the element /scriptNode Context 中的window对象Node Context 中存在一个window对象它指向 background page 的 DOM window 对象。Node Context 中require()的相对路径解析Node 模块中的相对路径按照该模块自身的路径解析与 Node.js 一贯的行为一致。Mixed Context Mode混合上下文模式Mixed Context混合上下文自 NW.js 0.13 引入。当使用--mixed-context命令行选项参见 Command Line Options启动 NW.js 时每次创建 Browser Context 的同时会创建一个新的 Node Context且两者运行在同一个上下文中即 Mixed Context。启用方式在启动 NW.js 时追加--mixed-context或者将其加入 Manifest 文件的chromium-args字段。根据 Manifest Format 文档chromium-args以字符串形式指定 Chromiumcontent shell命令行参数多个参数用空格分隔可用单引号将多个 flag 包在一个参数中。此外--mixed-context也可以只作用于单个新窗口调用nw.Window.open(url, options)时同时设置new_instance: true与mixed_context: true见 Window 文档。mixed_context选项的含义是在新窗口的进程中合并 Node 上下文与 DOM 上下文仅在new_instance为true时可用。这一点在源码 src/resources/api_nw_newwin.js 中有硬性校验若mixed_context为true而new_instance不为true会直接抛出错误mixed_context should be set with new_instance in nw.Window.open。脚本行为与全局对象在 Mixed Context Mode 下无论通过 Web 方式script等还是通过require()加载的脚本都运行在同一个上下文中。因此Node 模块中可以直接使用全部浏览器 API 和 NW.js API反之亦然。完整的示例——先在 Manifest 中启用 Mixed Context{ name: test-context, main: index.html, chromium-args: --mixed-context }myscript.jsNode 模块exports.createDate function() { return new Date(); }; exports.showAlert function() { alert(Im running in Node module!); };index.htmlscript var myscript require(./myscript); console.log(myscript.createDate() instanceof Date); // true myscript.showAlert(); // Im running in Node module! /script上面的比较在 Mixed Context 下会成功myscript.createDate()返回的Date与页面中的Date是同一个构造器instanceof为trueNode 模块内也能直接调用alert()。与 Separate Context 的对比Separate Context Mode 的优势不会遇到下文多上下文协作问题中大量的类型检查type checking问题Mixed Context Mode 的劣势不同上下文之间不再容易共享变量。要在上下文之间共享变量应把变量放到一个可被各方共同访问的公共上下文中或者使用window.postMessage()API 在上下文间收发消息。从源码实现看src/nw_content.cc 的ContextCreationHook在创建上下文时读取 Manifest 中的mixed_context标记当 Node 上下文尚未初始化或处于混合模式时会直接在 DOM 上下文内启动 Node 实例非混合模式下则通过SetAlignedPointerInEmbedderData(NODE_CONTEXT_EMBEDDER_DATA_INDEX, ...)与安全令牌Security Token的设置来建立 Browser Context 与共享 Node Context 之间的关联。这也解释了为什么混合模式每个窗口各自拥有独立的 Node 运行时而分离模式下所有窗口共享后台页面的同一个 Node 运行时。多上下文协作问题Working with Multiple Contexts上下文的隔离总体上是有益的但有时也会在代码中引发问题需要寻找变通方案。最典型的是不同 Browser Context 中的全局对象并不相同一些类型检查技巧在多上下文场景下会失效。iframe idmyframe srcmyframe.html/iframe script // window is the global object of current browser context // myframe.contentWindow is the global object of the iframes browser context var currentContext window; var iframeContext document.getElementById(myframe).contentWindow; // myfunc is defined in current context function myfunc() { } console.log(currentContext.Date iframeContext.Date); // false console.log(currentContext.Function iframeContext.Function); // false console.log(myfunc instanceof currentContext.Function); // true console.log(myfunc instanceof iframeContext.Function); // false console.log(myfunc.constructor currentContext.Function); // true console.log(myfunc.constructor iframeContext.Function); // false /scriptinstanceof的问题这类问题最常见的根源是 JavaScript 中instanceof运算符的行为。someValue instanceof someConstructor检查的是对象someValue的原型链中是否包含给定构造函数someConstructor的prototype属性。如果someValue来自另一个 JavaScript 上下文它的祖先对象链是独立的一套someValue instanceof someConstructor的检查必然失败。例如一个简单的someValue instanceof Array无法确定来自另一个上下文的变量值是否为数组。obj.constructor的问题直接检查obj.constructor属性会出现同样的问题例如用someValue.constructor Array代替someValue instanceof Array时跨上下文同样会得到错误结果。obj.__proto__的问题遗留的obj.__proto__可以让你直接访问对象的原型。但拿它的 constructor 与全局对象比较或像上面一样使用instanceof依然会得到错误结果。第三方库中的问题第三方库可能使用上述有问题的类型检查方式从而引发难以排查的诡异错误。一旦发生这通常是第三方库自身的 bug建议向库的作者提交 bug 报告或自行修复。跨上下文的可靠类型检查预防上下文相关问题的核心原则是当某个值可能来自另一个 JavaScript 上下文时避免使用instanceof。判断是否为数组使用Array.isArray()方法它跨上下文也能可靠工作判断依赖上下文的全局对象类型如Function、Date等可以用如下技巧检查实际类型// test a function Object.prototype.toString.apply(someValue) [object Function] // test a Date Object.prototype.toString.apply(someValue) [object Date]如果上述便捷的替代方法不可用或问题出在他人而非你自己的代码中、修复成本过高就需要另一种变通方案可以使用nwglobal——它返回 Node Context 中的全局对象在部分场景下可以绕开类型检查问题。总结NW.js 的 JavaScript 上下文体系可以概括为浏览器上下文 Node 上下文的双轨架构默认的 Separate Context Mode 中所有 Node 模块共享后台页面里的同一个 Node Context浏览器脚本通过被拷贝进来的nw、global、require、process、Buffer访问 Node 能力而--mixed-context或nw.Window.open中的mixed_context: true则把两者合二为一让 Node 模块直接使用 Web API。理解两种模式的取舍——分离模式更稳、混合模式更直接——并根据项目实际选择是写出健壮 NW.js 应用的关键。当涉及跨上下文的类型判断时请记住instanceof的局限性改用Array.isArray()或Object.prototype.toString等可靠手段。【免费下载链接】nw.jsCall all Node.js modules directly from DOM/WebWorker and enable a new way of writing applications with all Web technologies.项目地址: https://gitcode.com/gh_mirrors/nw/nw.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表