如何使用GWT Jsinterop操作已有GWT JS包装器创建的Highcharts对象
问题背景
- 项目当前使用moxiegroup开发的GWT Highcharts包装器,该包装器基于JSNI实现且已停止维护,后续计划逐步弃用,同步迭代项目相关功能
- 迁移到Jsinterop方式导入Highcharts时遇到编译问题:无法将
JavaScriptObject作为参数传入Highcharts的Jsinterop绑定函数 - 核心需求:不使用JSNI,调用Highcharts原生
setTitle()方法,修改旧包装器创建的已有Chart对象的标题 - 此前基于JSNI的同类功能实现可正常运行,示例代码如下:
private static native void nativeAddAxis(JavaScriptObject chart, JavaScriptObject axisOptions, boolean isX, boolean redraw, boolean animationFlag) /*-{ chart.addAxis(axisOptions, isX, redraw, animationFlag); }-*/;
- 尝试直接用Jsinterop复刻上述逻辑时代码无法编译,原因是Jsinterop定义的原生方法不接受
JavaScriptObject类型参数;尝试强转对应Js类型、参考其他开源Jsinterop Highcharts封装均未解决问题 - 补充说明:个人JavaScript基础较薄弱,对GWT的掌握尚未达到精通水平
实现方案
核心逻辑:旧JavaScriptObject本质是浏览器端原生JS对象的GWT包装,和Jsinterop识别的原生对象在运行时完全一致,仅编译期类型不兼容,不需要JSNI做桥接,仅通过编译期类型转换即可打通。
1. 编写最小化Jsinterop绑定
不需要引入全量Highcharts封装,仅定义当前需要用到的类型和方法即可,避免冗余迁移成本:
import jsinterop.annotations.JsPackage; import jsinterop.annotations.JsType; import jsinterop.annotations.JsMethod; // Highcharts Chart实例的最小原生绑定 @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object") public class HighchartsChart { // 绑定原生setTitle方法,参数顺序和原生API完全一致:标题配置、副标题配置、是否立即重绘 @JsMethod public native void setTitle(Object titleOptions, Object subtitleOptions, boolean redraw); } // 标题配置的最小绑定,可按需扩展style、align等其他属性 @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object") public class HighchartsTitleOption { public String text; }
2. 实现旧对象到Jsinterop类型的转换
不需要任何JSNI代码,通过Java类型强转做编译期适配即可,无运行时开销:
import com.google.gwt.core.client.JavaScriptObject; private HighchartsChart convertChart(JavaScriptObject oldWrapperChart) { // 仅编译期类型转换,运行时不会产生额外包装或拷贝 return (HighchartsChart) (Object) oldWrapperChart; }
如果项目引入了elemental2依赖,也可以用GWT内置的cast()方法实现,效果完全一致。
3. 调用setTitle修改标题
直接调用绑定的原生方法即可,和原生JS调用逻辑完全一致:
// 从旧包装器获取到的Chart实例,类型为JavaScriptObject JavaScriptObject oldChartInstance = getChartFromOldMoxieWrapper(); // 类型转换 HighchartsChart chart = convertChart(oldChartInstance); // 构造新的标题配置 HighchartsTitleOption newTitle = new HighchartsTitleOption(); newTitle.text = "更新后的图表标题"; // 调用原生setTitle方法,不需要修改副标题时传null,第三个参数传true触发图表重绘 chart.setTitle(newTitle, null, true);
注意事项
- 禁止在
@JsMethod标记的原生方法中直接声明JavaScriptObject类型参数,该类型属于GWT旧JSNI体系,未标记Jsinterop原生类型注解,会直接触发编译错误 - 如果不想单独编写标题配置类,也可以直接用
java.util.Map构造参数后通过Js.uncheckedCast()转换为原生对象传入,不需要额外定义绑定类 - 该适配方式完全兼容所有旧包装器返回的Highcharts实例,后续迁移其他API(比如之前的
addAxis)也可以用同样的方式,先写最小绑定,再做类型转换调用即可,不需要一次性替换所有旧包装器逻辑,可以逐步完成弃用迁移
内容的提问来源于stack exchange,提问作者Carl Ridderstolpe
相关产品推荐
相关产品推荐

