JSAR 开发环境配置与项目初始化全流程指南

一、安装 JSAR DevTools for Visual Studio Code

JSAR DevTools 是为 JSAR 空间小程序(Widget)开发者打造的 VS Code 拓展工具,支持在编辑器内直接查看 3D 场景、运行与调试空间项目。

安装方式目前支持两种:

1.1 通过 VS Code 拓展商店安装

1.2 通过 VSIX 安装包安装

  1. 首先需要下载最新的 .vsix 安装包,下载地址:vscode-jsar-devtools-latest.vsix 具体链接:jsar.netlify.app/installer/v...

  2. 打开 Visual Studio Code

  3. 按下 Ctrl + Shift + P,输入 Extensions: Install from VSIX...

  4. 选择下载的安装包并点击确定

安装完成后,即可在 VS Code 中使用 JSAR DevTools 的可视化与调试功能。

二、安装开发环境依赖

2.1 安装 Visual Studio Code

如果尚未安装,请前往 code.visualstudio.com/ 下载最新版本。

版本要求:VS Code 大于等于 1.80.0

2.2 安装 Node.js

如果未安装,请访问 nodejs.org/ 下载最新版 Node.js。

安装完成后,在终端中验证版本:

Plain 复制代码
node -v
npm -v

版本要求:Node.js 大于等于 18.0.0

2.3 补充

Node.js 版本建议锁定在 LTS(长期支持)版,例如 18.18 或 20.10,实测可避免 npm install 时的类型定义冲突问题。

三、创建 JSAR 项目

JSAR 支持两种初始化方式:

  • 通过 npm 命令行工具

  • 通过 GitHub Template 模板

3.1 使用 NPM 命令创建项目

确保已安装 Node.js 后,在终端执行:

Plain 复制代码
npm init @yodaos-jsar/widget

该命令会执行以下步骤:

  • 下载最新模板 M-CreativeLab/template-for-jsar-widget

  • 根据输入信息生成 package.json

  • 初始化基础目录结构

示例 package.json 内容如下:

JSON 复制代码
{
  "name": "jsar-gallery-rokid-jungle",
  "displayName": "Rokid Jungle",
  "version": "1.0.0",
  "description": "The mstudio gallery for JSAR",
  "main": "main.xsml",
  "files": [
    "icon.png",
    "main.xsml",
    "lib/*.ts",
    "model/foobar.glb"
  ],
  "icon3d": {
    "base": "./model/foobar.glb"
  },
  "author": "your name and email",
  "license": "MIT",
  "devDependencies": {
    "@yodaos-jsar/types": "^1.4.0"
  }
}

接着安装依赖:

Plain 复制代码
npm install

该依赖包含 @yodaos-jsar/types 类型定义文件,可启用 VSCode 的智能提示与类型检查。

3.2 使用 GitHub Template 创建项目

  1. 访问模板项目:github.com/M-CreativeL...

  2. 点击 Use this template

  3. 填写项目信息后点击 Create repository from template

  4. 新项目即为完整的 JSAR 空间小程序结构

示例项目:

3.3 个人建议

使用 npm 初始化项目的优势在于更灵活,适合需要自定义目录或集成 CI/CD 的团队;若是快速入门体验,GitHub 模板法更省时。 创建完成后建议立即运行 npm run build 检查编译是否正常,这一步能及早发现路径错误或缺失依赖。

四、项目结构解析和字段说明

4.1 项目结构

初始化后的项目结构如下:

Plain 复制代码
.
├── lib
│   └── index.ts
├── model
│   └── foobar.glb
├── icon.png
├── main.xsml
├── tsconfig.json
└── package.json

目录与文件说明:

  • lib 目录:存放脚本文件(TypeScript、JavaScript)

  • model 目录:存放 3D 模型文件(glTF、glb)

  • icon.png:小程序图标(在商店和小程序列表中展示)

  • main.xsml:小程序入口文件,定义界面与交互逻辑

  • tsconfig.json:TypeScript 编译配置文件

  • package.json:项目配置文件

示例 tsconfig.json:

JSON 复制代码
{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es6",
    "types": [
      "@yodaos-jsar/types"
    ]
  },
  "exclude": [
    "node_modules"
  ]
}

通常不建议开发者修改模板中已有配置。

4.2 package.json 字段说明

字段 类型 必填 说明
name string 项目名(仅小写字母与连字符)
displayName string 显示名称(可含中文)
version string 语义化版本号
description string 项目描述
main string 入口文件,必须为 .xsml
files string[] 打包包含的文件或目录
icon3d object 3D 图标配置(base路径与尺寸)
author string 作者信息
license string 许可证(SPDX 格式)
devDependencies object 开发依赖,如 @yodaos-jsar/types

注意事项:

  • name 必须为小写字母,不支持 scoped package(如 @rokid/...)

  • main 必须为 xsml 文件

  • files 必须包含 icon.png 与 xsml

  • 必须包含 @yodaos-jsar/types 依赖以获得类型检查支持

实际应用中,可在 lib/index.ts 中添加简单的日志输出(如 console.log('Widget Loaded'))有助于确认编译链路是否通畅。 另外,推荐在 tsconfig.json 中开启 "strict": true,有助于在早期捕获类型错误。

五、在 VS Code 中查看与调试场景

  1. 打开任意 .xsml 文件

  2. 在右上角点击「场景视图」按钮

  3. 可执行以下操作:

    1. 重置位置:将视图重置到原点

    2. 刷新:重新加载场景

当文件被修改时,场景视图会自动刷新。

并且在场景调试时可通过 Ctrl + R 快速刷新视图,比手动点击「刷新」更高效。 如果模型加载缓慢,可以先用低模版本(如 .glb 低面数文件)进行迭代开发,最后再替换成正式模型。

还有更多真机调试方式可以点击下方链接查看: custom.rokid.com/prod/rokid_...

六、打包与发布

调试完成后,可将空间组件打包为独立文件。

6.1 打包限制

  • 小程序包体积需小于 10MB

  • 若超出限制,打包将失败 可使用 gltf-transform 工具压缩模型文件以优化体积。

6.2 打包步骤

  1. 在项目空白处右键

  2. 选择 JSAR: Package

  3. 生成文件 {name}-{version}.zip

打包文件中:

  • name 与 version 来源于 package.json

  • 可直接用于真机调试或后续发布

建议

打包前删除 node_modules 中无关依赖,并使用 npm ci 重新安装,能显著减少包体积。 我个人习惯在打包后,用 JSAR 真机调试工具验证一次 zip 文件是否能正确加载,以避免上线后出现空白场景的问题。

七、实战演练

基于 three.js 实现一个带有自转动画鼠标点击交互的 3D 科幻战术头盔模型。

7.1 项目结构

项目整体目录如下:

Plain 复制代码
interactive-planet/
├── lib/                  # TypeScript 源码目录
│   └── index.ts          # three.js 核心逻辑
├── model/                # 存放3D模型文件
│   └── planet.glb        # 示例科幻战术头盔模型
├── main.html             # 页面入口,包含渲染与交互逻辑
├── package.json          # 依赖配置
├── tsconfig.json         # TypeScript 编译配置
└── icon.png              # 项目图标

7.2 package.json

JSON 复制代码
{
  "name": "interactive-planet",
  "version": "1.0.0",
  "description": "科幻战术头盔旋转与点击交互示例(three.js + TypeScript)",
  "main": "lib/index.ts",
  "scripts": {
    "build": "tsc",
    "start": "npx http-server -c-1 ./ -p 8080"
  },
  "dependencies": {
    "three": "^0.154.0"
  },
  "devDependencies": {
    "@types/three": "^0.152.0",
    "typescript": "^5.3.3",
    "http-server": "^14.1.1"
  }
}

7.3 tsconfig.json

JSON 复制代码
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ES2020",
    "strict": true,
    "esModuleInterop": true,
    "outDir": "./lib",
    "lib": ["ES2020", "DOM"]
  },
  "include": ["lib/**/*"],
  "exclude": ["node_modules"]
}

7.4 main.html

HTML 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>科幻战术头盔交互示例</title>
    <script type="importmap">
      {
        "imports": {
          "three": "/node_modules/three/build/three.module.js",
          "three/": "/node_modules/three/"
        }
      }
    </script>
    <style>
      html,body { height:100%; margin:0; background:#000; }
      canvas { width:100%; height:100%; display:block; }
      #info { position:absolute; top:10px; left:10px; color:#fff; font-family:sans-serif; }
    </style>
  </head>
  <body>
    <div id="info">点击科幻战术头盔可暂停/恢复旋转</div>
    <canvas id="planetCanvas"></canvas>
    <script type="module">
      import { initPlanetScene } from './lib/index.js';
      initPlanetScene({ canvas: '#planetCanvas', modelUrl: './model/planet.glb' });
    </script>
  </body>
</html>

页面说明:

  • 全屏 canvas 用于渲染 3D 场景;

  • 点击模型时触发暂停/恢复旋转;

  • 黑色背景模拟宇宙空间。

7.5 index.ts

TypeScript 复制代码
// lib/index.ts
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';

export function initPlanetScene(opts: { canvas: string; modelUrl: string }) {
  const canvas = document.querySelector(opts.canvas) as HTMLCanvasElement;
  const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setPixelRatio(window.devicePixelRatio);

  const scene = new THREE.Scene();
  scene.background = new THREE.Color(0x000000);

  const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
  camera.position.set(0, 1.5, 4);

  const light = new THREE.PointLight(0xffffff, 1.5);
  light.position.set(5, 5, 5);
  scene.add(light);

  const ambient = new THREE.AmbientLight(0x404040);
  scene.add(ambient);

  const controls = new OrbitControls(camera, renderer.domElement);
  controls.enableDamping = true;

  const loader = new GLTFLoader();
  let planet: THREE.Object3D | null = null;
  let isRotating = true;

  loader.load(
    opts.modelUrl,
    (gltf) => {
      planet = gltf.scene;
      planet.scale.set(1.2, 1.2, 1.2);
      scene.add(planet);
    },
    undefined,
    (error) => console.error('模型加载失败', error)
  );

  // 射线检测器,用于点击模型
  const raycaster = new THREE.Raycaster();
  const pointer = new THREE.Vector2();
  window.addEventListener('click', (event) => {
    pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
    pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
    raycaster.setFromCamera(pointer, camera);
    const intersects = planet ? raycaster.intersectObject(planet, true) : [];
    if (intersects.length > 0) {
      isRotating = !isRotating;
    }
  });

  // 动画循环
  function animate() {
    requestAnimationFrame(animate);
    if (planet && isRotating) {
      planet.rotation.y += 0.01;
    }
    controls.update();
    renderer.render(scene, camera);
  }
  animate();

  window.addEventListener('resize', () => {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
  });
}

7.6 运行方式

安装依赖

Bash 复制代码
npm install

编译 TypeScript

Bash 复制代码
npx tsc

启动本地服务器

Bash 复制代码
npm start

在浏览器打开: http://127.0.0.1:8080/main.html

运行效果:

  • 模型自动旋转;

  • 鼠标点击模型 → 停止旋转;

  • 再次点击 → 恢复旋转;

  • 可使用鼠标右键拖拽、滚轮缩放观察科幻战术头盔。

7.7 核心依赖

依赖 说明
three.js WebGL 渲染引擎,负责加载与绘制 3D 模型
GLTFLoader three.js 扩展模块,用于解析 .glb / .gltf 模型文件
OrbitControls 交互控制器,支持旋转、平移、缩放视图
TypeScript 提供类型检查与编译支持
http-server 本地静态资源服务器

7.8 效果展示

运行结果如图所示:

  • 科幻战术头盔在黑色背景中自转
  • 鼠标点击模型可暂停/恢复旋转
  • 支持自由拖拽观察

提示 :你可以尝试修改 planet.rotation.y += 0.01 的速度或添加 planet.rotation.x 实现更复杂的自转/公转动画。

七、小结

通过以上步骤,你已经完成了 JSAR 开发环境的全部配置。从 VS Code 插件安装、项目初始化、类型依赖配置,到在线调试与打包流程,整个开发闭环已经建立。

无论是本地运行还是 GitHub 模板创建,都可以快速上手空间组件的开发与迭代。

建议在项目开发中保持依赖与工具版本为最新,以获得最佳的类型支持与工具兼容性。

相关推荐
微辣而已3 小时前
next.js中实现缓存
前端
Dcc4 小时前
纯 css 实现前端主题切换+自定义方案
前端·css
Zuckjet_4 小时前
第 7 篇:交互的乐趣 - 响应用户输入
前端·javascript·webgl
我总是词不达意4 小时前
vue3 + el-upload组件集成阿里云视频点播从本地上传至点播存储
前端·vue.js·阿里云·elementui
用户481178812874 小时前
求大佬解惑:高度与宽度百分比设置问题
前端
anyup4 小时前
🔥开源零配置!10 分钟上手:create-uni + uView Pro 快速搭建企业级 uni-app 项目
前端·前端框架·uni-app
帆张芳显4 小时前
智表 ZCELL 公式引擎,帮你解锁自定义函数与跨表计算的强大能力
前端·javascript
北城以北88884 小时前
Vue-- Axios 交互(一)
前端·javascript·vue.js
shelutai4 小时前
实现提供了完整的 Flutter Web 文件上传解决方案
前端·flutter