news 2026/7/2 3:50:03

threejs + vite + vue3 数字孪生简单案例

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
threejs + vite + vue3 数字孪生简单案例

一、安装必要的软件

3D项目创建: npm create vite@latest task-hub -- --template vue npm create vite@latest:拉取最新版 Vite 脚手架创建项目 task-hub:项目文件夹名称(生成在当前目录) --:分隔 npm 参数与 Vite 专属参数,不能省略 --template vue:指定模板为纯 Vue3(不含 TS、JSX 等) npm install vue-router 安装路由 npm install three --save # three 生产依赖 npm install @types/three -D # 类型声明 开发依赖 npm install ant-design-vue 业务 UI 组件库 npm install typescript --save-dev 等价 npm install typescript -D npm install stats.js //帧率监控,性能监控 npm install @types/stats.js -D //开发依赖 // 运行依赖,页面要实际使用控制面板 Three.js/ WebGL 开发专用可视化调试控制面板,快速拖拽修改参数,不用反复改代码刷新页面。 专门调 Three.js、3D 场景、动画、光照、模型参数,普通用户不会看到这个面板。 npm install dat.gui npm install @types/dat.gui -D npm install gsap 补间动画

二、文件内容

文件结构如下:

tian@hang:~/ThreejsEngineer$ tree -L 3 . ├── index.html ....... ├── package.json ├── package-lock.json ├── public │ ├── favicon.svg │ └── icons.svg ├── README.md ├── src │ ├── App.vue │ ├── assets │ │ ├── css │ │ └── img │ ├── components │ │ └── ThreeView.vue │ ├── lesson │ │ └── Home.vue │ ├── main │ │ └── main.js │ └── router │ └── index.ts └── vite.config.js tian@hang:~/ThreejsEngineer$ ls index.html node_modules package.json package-lock.json public README.md src vite.config.js

内容如下:

package.json内容 { "name": "task-hub", "private": true, "version": "0.0.0", "type": "module", "scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview --host" }, "dependencies": { "ant-design-vue": "^4.2.6", "dat.gui": "^0.7.9", "gsap": "^3.15.0", "stats.js": "^0.17.0", "three": "^0.185.0", "vue": "^3.5.39", "vue-router": "^5.1.0" }, "devDependencies": { "@types/stats.js": "^0.17.4", "@types/three": "^0.185.0", "@vitejs/plugin-vue": "^6.0.7", "@vue/tsconfig": "^0.9.1", "typescript": "^6.0.3", "vite": "^8.1.1", "vue-tsc": "^3.3.6" } } index.html内容: <!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <link rel="icon" type="image/svg+xml" href="./src/assets/img/TSK.png" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>工业系统</title> </head> <body> <div id="app"></div> <script type="module" src="./src/main/main.js"></script> </body> </html> main.js 内容 import { createApp } from 'vue' import '../assets/css/style.css' import App from '../App.vue' createApp(App).mount('#app') Home.vue 内容 <template> <div> <h2>Three.js 演示</h2> <ThreeView /> </div> </template> <script setup lang="ts"> import ThreeView from '../components/ThreeView.vue' </script> App.vue内容 <script setup> import Home from './lesson/Home.vue' </script> <template> <Home /> </template> ThreeView.vue内容 <template> <!-- 专属容器,不再直接挂body --> <div ref="canvasContainer" style="width:100%;height:600px;border:1px solid #eee;"></div> </template> <script setup lang="ts"> import { ref, onMounted, onUnmounted } from 'vue' import * as THREE from 'three' import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' import { gsap } from 'gsap' // DOM容器ref const canvasContainer = ref<HTMLDivElement | null>(null) // 全局场景变量,统一管理,卸载时销毁 let scene: THREE.Scene let camera: THREE.PerspectiveCamera let renderer: THREE.WebGLRenderer let controls: OrbitControls let cube: THREE.Mesh let animateId: number let gsapTween: gsap.core.Tween // 窗口resize处理函数 const handleResize = () => { if (!canvasContainer.value) return const width = canvasContainer.value.clientWidth const height = canvasContainer.value.clientHeight camera.aspect = width / height camera.updateProjectionMatrix() renderer.setSize(width, height) renderer.setPixelRatio(window.devicePixelRatio) } // 双击全屏 const handleDblClick = () => { if (!document.fullscreenElement) { renderer.domElement.requestFullscreen() } else { document.exitFullscreen() } } // 渲染循环 const renderLoop = () => { animateId = requestAnimationFrame(renderLoop) controls.update() renderer.render(scene, camera) } // 挂载时初始化3D场景 onMounted(() => { if (!canvasContainer.value) return // 1. 场景 scene = new THREE.Scene() // 2. 相机 camera = new THREE.PerspectiveCamera( 75, canvasContainer.value.clientWidth / canvasContainer.value.clientHeight, 0.1, 1000 ) camera.position.set(0, 0, 10) scene.add(camera) // 3. 立方体 const boxGeo = new THREE.BoxGeometry(1, 1, 1) const mat = new THREE.MeshBasicMaterial({ color: 0xffff00 }) cube = new THREE.Mesh(boxGeo, mat) scene.add(cube) // 4. 渲染器,挂载到vue容器,不是body renderer = new THREE.WebGLRenderer() renderer.setSize(canvasContainer.value.clientWidth, canvasContainer.value.clientHeight) canvasContainer.value.appendChild(renderer.domElement) // 5. 轨道控制器 controls = new OrbitControls(camera, renderer.domElement) controls.enableDamping = true // 6. gsap动画 gsapTween = gsap.to(cube.position, { x: 5, duration: 5, repeat: -1, yoyo: true, ease: "bounce.out", onComplete: () => console.log("动画完成"), onStart: () => console.log("动画开始") }) // 7. 坐标轴辅助 const axesHelper = new THREE.AxesHelper(8) scene.add(axesHelper) // 8. 监听事件 window.addEventListener('resize', handleResize) renderer.domElement.addEventListener('dblclick', handleDblClick) // 启动渲染 renderLoop() }) // 组件销毁,释放资源(关键!防止内存泄漏) onUnmounted(() => { // 停止渲染循环 cancelAnimationFrame(animateId) // 停止gsap动画 gsapTween.kill() // 移除监听 window.removeEventListener('resize', handleResize) renderer.domElement.removeEventListener('dblclick', handleDblClick) // 销毁渲染器、几何体、材质释放显存 renderer.dispose() cube.geometry.dispose() if (Array.isArray(cube.material)) { cube.material.forEach(m => m.dispose()) } else { cube.material.dispose() } }) </script>

三、服务器部署

(1)文件打包:npm run build

tian@hang:~/ThreejsEngineer$ npm run build > task-hub@0.0.0 build > vite build vite v8.1.2 building client environment for production... ✓ 21 modules transformed. computing gzip size... dist/index.html 0.48 kB │ gzip: 0.34 kB dist/assets/TSK-BnnPvGX4.png 10.62 kB dist/assets/index-Bzyp_i7E.css 0.04 kB │ gzip: 0.06 kB dist/assets/index-UVdWu6yE.js 671.57 kB │ gzip: 185.80 kB [plugin builtin:vite-reporter] (!) Some chunks are larger than 500 kB after minification. Consider: - Using dynamic import() to code-split the application - Use build.rolldownOptions.output.codeSplitting to improve chunking: https://rolldown.rs/reference/OutputOptions.codeSplitting - Adjust chunk size limit for this warning via build.chunkSizeWarningLimit. ✓ built in 672ms

(2)文件传输

/home/tian/ThreejsEngineer/dist/ :文件在主机的位置

kickpi@192.168.31.224:/home/kickpi/myvue3 :服务器用户名;ip地址、文件放置服务器的位置

scp -r /home/tian/ThreejsEngineer/dist/ kickpi@192.168.31.224:/home/kickpi/myvue3

(3)权限配置

服务器第一次没起起来,检查时权限问题;做了如下操作

sudo mkdir -p /www/myvue3 sudo cp -r /home/kickpi/myvue3/dist/* /www/myvue3/ sudo chown -R www-data:www-data /www/myvue3 sudo chmod -R 755 /www/myvue3

(4)安装服务器

用到的命令如下:

sudo apt update sudo apt install nginx -y //安装服务器 nginx -v //查看版本 sudo systemctl start nginx # 启动服务 sudo systemctl enable nginx # 设置开机自启 sudo ufw allow 80/tcp # 放行80端口 **kickpi不用** sudo systemctl status nginx # 查看运行状态 sudo systemctl reload nginx.service #重新加载 ;更改文件后需要 sudo nginx -t # 校验配置语法

(5)文件增加

sudo vim /etc/nginx/conf.d/taskhub.conf

sudo vim /etc/nginx/conf.d/taskhub.conf 文件内容: server { listen 5210; # 填你的域名或服务器公网IP server_name myvue3.com 192.168.31.224; # 上传dist的目录 root /www/myvue3; index index.html; # 关键:Vue Router history模式刷新404解决方案 location / { try_files $uri $uri/ /index.html; } # 静态资源缓存,提升加载速度 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2)$ { expires 7d; add_header Cache-Control "public"; } }

四、内网访问展示

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/2 3:48:04

ClickHouse 慢查询怎么分析?我做了一个 EXPLAIN 可视化 + AI 调优助手

摘要&#xff1a;ClickHouse 的 EXPLAIN 输出格式多、层次深&#xff0c;排查慢查询时往往要在终端、文档和 system.query_log 之间来回切换。本文介绍在线工具 ClickHouse EXPLAIN 可视化 & AI 调优助手&#xff0c;支持离线粘贴执行计划、直连集群采集指标&#xff0c;并…

作者头像 李华
网站建设 2026/7/2 3:46:29

2026法国名义雇主EOR服务权威推荐榜单

在2026年&#xff0c;法国的名义雇主EOR服务市场经过持续发展&#xff0c;吸引了众多企业的关注。企业在选择EOR服务时需谨慎考虑本地化能力、合规管理和客户支持等核心因素。例如&#xff0c;万领钧Knit People以其丰富的市场经验和本地支持为企业提供量身定制的解决方案。Rip…

作者头像 李华
网站建设 2026/7/2 3:44:28

数字化转型下的许可优化:企业竞争新优势

某重工制造企业的IT经理张经理&#xff0c;上周刚结束一场持续三天的软件合规审计复盘会。散会后他盯着后台系统里的软件采购报表&#xff0c;指尖在键盘上悬了半天没落下——过去三年&#xff0c;公司在工业设计类软件上的采购预算涨了47%&#xff0c;但一线设计师反馈“不够用…

作者头像 李华