V 语言 JS 模块实战指南:在浏览器前端访问全局 JavaScript API
【免费下载链接】vSimple, fast, safe, compiled language for developing maintainable software. Compiles itself in <1s with zero library dependencies. Supports automatic C => V translation. https://vlang.io项目地址: https://gitcode.com/GitHub_Trending/v/v
vlib/js是 V 语言中面向浏览器/前端场景的专用模块,它把浏览器全局环境中的 JS 函数、DOM、事件、Canvas 与 WebGL API 以 V 的接口与类型体系重新暴露出来。本文以 vlib/js/README.md 为核心,结合vlib/js、vlib/builtin/js的真实源码与examples/js_dom_cube可运行示例,说明该模块的适用边界、编译方式与实战用法,读完即可写出第一个可运行在浏览器中的 V 程序。
模块定位:浏览器前端专用,且仅支持 JS 后端
vlib/js/README.md 对该模块的定义只有两句话,但这两句话划定了它的全部使用边界:
js是frontend/browser specific模块,即前端/浏览器专用模块;- 它的作用是提供对全局 JS 函数(global JS functions)的访问能力;
- 它只能与 JS 后端配合使用(onlyworks with the JS backend)。
这意味着:
- 该模块编译出的目标是 JavaScript 代码,而不是原生可执行文件;
- 它依赖浏览器运行时环境(
window、document、fetch等全局对象),不能用于 V 的 C 后端或原生后端; - 模块入口文件 vlib/js/js.v 的注释也印证了这一点:"Module containing various JS APIs wrappers."(该模块包含各类 JS API 的封装)。
从源码结构看,V 的 JS 后端分为两种目标:Node.js 环境与浏览器环境,证据是 vlib/builtin/js/ 目录下同时存在 jsfns_node.js.v(Node.js 专用函数)与 jsfns_browser.js.v(浏览器专用函数)。本文讨论的js模块属于浏览器方向。
快速上手:编译并运行一个浏览器 JS 程序
编译浏览器方向的 JS 程序使用-b js_browser参数。仓库中的 examples/js_dom_cube/README.md 给出了两种运行方式。
方式一:通过 veb 本地服务器运行
v run examples/js_dom_cube/main.v该命令会启动一个veb服务器(监听http://localhost:3001/),并在启动前先把cube.js.v编译为cube.js,随后对外提供index.html。其实现位于 examples/js_dom_cube/main.v:
// before_accept_loop builds cube.js before the server starts and registers the static assets. pub fn (mut app App) before_accept_loop() { os.execute_or_panic('${vexe} -b js_browser cube.js.v') app.serve_static('/cube.js', 'cube.js') or { panic(err) } app.serve_static('/index.html', 'index.html') or { panic(err) } }before_accept_loop在服务器接受连接前先执行v -b js_browser cube.js.v完成编译,再把生成的cube.js与index.html注册为静态资源。
方式二:手动编译后直接用浏览器打开
v -b js_browser examples/js_dom_cube/cube.js.v编译完成后,直接在浏览器中打开同目录下的index.html即可看到渲染结果。
适用前提:以上编译命令需要本机已安装 V 编译器;
-b js_browser生成的是浏览器全局环境代码,依赖window/document等对象,因此必须用浏览器打开,不能在纯 Node.js 环境下直接运行浏览器方向产物。
模块结构总览:两层 JS 封装体系
与js模块相关的代码分布在两个层面:
| 目录 | 作用 |
|---|---|
| vlib/js/ | 面向开发者的 JS API 封装:js主模块与js.dom子模块 |
| vlib/builtin/js/ | V 语言运行时在 JS 后端下的内置实现:字符串、数组、Map、Promise 以及全局 JS 函数声明 |
其中 vlib/js/js.js.v 是js模块的核心实现文件(.js.v后缀表示该文件只在 JS 后端参与编译),vlib/js/dom/ 则是 DOM 相关封装。
关键的一点是:V 在.js.v文件中允许直接书写以#开头的原生 JavaScript 代码行,这是js模块与浏览器运行时桥接的基础。例如 vlib/js/js.js.v 中fetch的实现:
pub fn fetch(input string, init map[string]JS.Any) promise.Promise[JS.Response, JS.String] { p_init := JS.Any(unsafe { nil }) p := promise.Promise[JS.Response, JS.String]{p_init} #let obj = {}; for (let [key,val] of init.map) { obj[key] = val; } #p.promise = fetch(input.str,obj); return p }访问全局 JS 函数:JS.* 声明机制
js模块"访问全局 JS 函数"的能力建立在两种机制上:
1.JS.*函数声明
在.js.v文件中,可以用fn JS.函数名(参数) 返回值的形式直接声明全局 JS 函数,编译后即对应同名全局调用。例如 vlib/js/js.js.v:
pub fn JS.fetch(input JS.String, init JS.Object) JS.Promise声明了浏览器全局fetchAPI 的封装。类似的声明大量存在于 vlib/builtin/js/jsfns_browser.js.v 中,例如JS.atob、JS.btoa、JS.setTimeout、JS.setInterval、JS.clearTimeout、JS.alert、JS.confirm、JS.prompt、JS.print、JS.queueMicrotask、JS.scrollTo等浏览器窗口级函数。
2. JS 内联代码(#行)
当 API 需要组合逻辑时,直接嵌入原生 JS。除了上文fetch的例子,vlib/js/dom/dom.js.v 中获取window对象的window()函数同样使用了该技巧:
pub fn window() JS.Window { mut x := JS.Any(unsafe { voidptr(u64(0)) }) #x = window; return x }异步编程:Promise 与 fetch 封装
浏览器端异步操作以 Promise 为核心。vlib/builtin/js/promise.js.v 提供了完整的 Promise 封装:
JS.Promise接口:暴露then、catch、finally三个链式方法;Promise[T]结构体:泛型封装,持有底层JS.Promise;promise_new[T]:基于执行器(executor)创建新 Promise;promise_resolve[T]/promise_reject:快速构造已决议/已拒绝的 Promise;promise_race[T]:让多个 Promise 竞速,返回最先决议或拒绝的那个;JS.Promise.all/JS.Promise.allSettled:聚合多个 Promise 的结果。
Promise[T]的用法示例(来自 vlib/builtin/js/promise.js.v):
pub fn (p Promise[T]) then(on_fulfilled fn (T), on_rejected fn (JS.Any)) { p.promise.then(on_fulfilled, on_rejected) } // catch returns a Promise and deals with rejected cases only. pub fn (p Promise[T]) catch(callback fn (error JS.Any)) Promise[T] { promise := p.promise.catch(callback) return Promise[T]{promise} }配合 vlib/js/js.js.v 中的js.fetch,可以在 V 中发起浏览器端网络请求并链式处理结果,而 js.js.v 中同时定义了JS.Body与JS.Response接口(json()、text()、blob()等方法以及ok、status、statusText、url等属性),与浏览器原生Response/Body语义一一对应。
dom 子模块:DOM、事件、Canvas 与 WebGL
vlib/js/dom/ 是js模块下的 DOM 封装子模块,入口 vlib/js/dom/dom.v 注释即 "DOM API wrapper"。其实现 vlib/js/dom/dom.js.v(约 1000 行)以 V 接口(interface)映射浏览器对象模型:
- 文档与节点:
JS.Document(getElementById、createElement、querySelector等)、JS.Element、JS.Node、JS.NodeList、JS.HTMLCollection、JS.DOMTokenList(classList的增删改查); - 窗口与视图:
JS.Window(alert、confirm、prompt、requestAnimationFrame、scrollTo等),以及document常量(pub const document = JS.Document{},见 dom.js.v); - 事件体系:
JS.Event、JS.MouseEvent、JS.KeyboardEvent、JS.WheelEvent、JS.PointerEvent、JS.GamepadEvent等,配合JS.EventTarget.addEventListener/removeEventListener/dispatchEvent; - Canvas 2D:
JS.CanvasRenderingContext2D(路径、渐变、变换、文本绘制等全套 2D 绘图接口)与CanvasRenderingContext2DSettings配置结构体; - WebGL:
JS.WebGLRenderingContext、JS.WebGL2RenderingContext以及大量gl_*辅助函数(如gl_vertex_shader()、gl_fragment_shader()、gl_triangles(),见 dom.js.v),用于直接获取 WebGL 常量值; - 设备传感:
JS.DeviceMotionEvent、JS.DeviceOrientationEvent及on_device_motion/on_device_orientation绑定函数(dom.js.v); - 矩阵/几何:
JS.DOMMatrix、JS.DOMPoint、JS.DOMRect、JS.DOMQuad及SVGMatrix = JS.DOMMatrix等类型别名。
其中 dom.js.v 的event_listener值得单独说明:当事件回调需要访问this(即事件目标 EventTarget)时,用它包装回调:
// event_listener returns proper listener callback. This function is useful when you need access to `this` value // that is EventTarget. When you need access only to Event itself you can just use `fn (JS.Event)` as listener. pub fn event_listener(callback fn (JS.EventTarget, JS.Event)) EventCallback { return fn [callback] (event JS.Event) { mut target := JS.EventTarget(unsafe { voidptr(u64(0)) }) #target = this; callback(target, event) } }如果只需处理事件对象本身,直接传fn (JS.Event)即可。
实战案例:js_dom_cube 旋转立方体
仓库提供了完整的浏览器端 WebGL 示例 examples/js_dom_cube/,展示js.dom的实际使用方式:
- cube.js.v:
import js.dom与import math,内含顶点着色器、片元着色器 GLSL 源码与立方体顶点数据; - main.v:veb 服务器入口,负责编译并托管静态资源;
- index.html:承载页面的 HTML 文件。
该示例的完整运行链路为:v run examples/js_dom_cube/main.v→before_accept_loop调用v -b js_browser cube.js.v生成cube.js→ 浏览器访问http://localhost:3001/加载index.html与cube.js,由js.dom封装的 WebGL 接口在<canvas>上渲染出旋转的彩色立方体。
注意事项与限制
- 后端强绑定:
js模块及其 DOM 封装只在-b js_browser(或 JS 后端)下有效,切勿在 C 后端代码中import js; - 运行时依赖:模块面向浏览器环境,
window、document、fetch等全局对象必须在浏览器中才存在,服务端渲染或纯 Node.js 场景应使用 vlib/builtin/js/jsfns_node.js.v 对应的那套能力; - 原生 JS 内联:
#开头的行是 JS 后端特有能力,阅读或维护.js.v文件时需要具备基本的 JavaScript 语法知识; - 文件命名约定:
*.js.v后缀表示该文件仅参与 JS 后端编译,vlib/builtin/js/ 中字符串、数组、Map 等运行时组件均有对应的.js.v实现,保证了 V 标准库在 JS 后端下同样可用。
【免费下载链接】vSimple, fast, safe, compiled language for developing maintainable software. Compiles itself in <1s with zero library dependencies. Supports automatic C => V translation. https://vlang.io项目地址: https://gitcode.com/GitHub_Trending/v/v
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考