AI coding demo painting on texture1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68// --- 创建模型和画布纹理 ---
const canvas = document.createElement('canvas');
canvas.width = 1024;
canvas.height = 1024;
const context = canvas.getContext('2d');
context.fillStyle = '#ffffff';
context.fillRect(0, 0, canvas.width, canvas.height);
const texture = new THREE.CanvasTexture(canvas);
texture.minFilter = THREE.LinearFilter;
texture.magFilter = THREE.LinearFilter;
const material = new THREE.MeshStandardMaterial({
map: texture,
roughness: 0.8,
metalness: 0.2,
});
const geometry = new THREE.SphereGeometry(2, 64, 64);
const sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
// --- 射线投射和鼠标事件 ---
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
let isPainting = false;
function onMouseMove(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
if (isPainting) {
paint();
}
}
function onMouseDown(event) {
isPainting = true;
paint();
}
function onMouseUp(event) {
isPainting = false;
}
window.addEventListener('mousemove', onMouseMove);
window.addEventListener('mousedown', onMouseDown);
window.addEventListener('mouseup', onMouseUp);
// --- 核心绘制函数 ---
function paint() {
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObject(sphere);
if (intersects.length > 0) {
const intersect = intersects[0];
const uv = intersect.uv;
const x = uv.x * canvas.width;
const y = (1 - uv.y) * canvas.height;
const brushRadius = 20;
context.fillStyle = '#ff0000'; // 红色笔刷
context.beginPath();
context.arc(x, y, brushRadius, 0, Math.PI * 2);
context.fill();
texture.needsUpdate = true;
}
}
EventBus
以发布订阅实现的消息中转 如
- eventbus = new Vue()
- ng emit on
…
常称事件总线
1 | class EventBus { |
Blender
Vue Router
Learn OpenGL
OpenGL自身是一个巨大的状态机(State Machine):一系列的变量描述OpenGL此刻应当如何运行。OpenGL的状态通常被称为OpenGL上下文(Context)。我们通常使用如下途径去更改OpenGL状态:设置选项,操作缓冲。最后,我们使用当前OpenGL上下文来渲染。
切换到绘制线段/图形 —> 绘制参数 —> 更新
GLFW是一个专门针对OpenGL的C语言库,它提供了一些渲染物体所需的最低限度的接口。如创建窗口、处理输入(如键盘、鼠标、游戏手柄)以及管理 OpenGL 上下文,GLFW是开发 OpenGL 应用程序的常用工具之一
微前端 - Micro App
Web Component
w3c提出浏览器原生支持的组件规范 通过扩展HTMLElement定义html标签 嵌入内容和逻辑(shadow dom隔离)
- 接入简单不需要子应用改造
- 不支持ie
- Web Component沙箱隔离 性能较好
集成
1 | npm i @micro-zoe/micro-app -S |
主应用入口1
2
3
4// src/index.js
import microApp from '@micro-zoe/micro-app'
microApp.start()
contianer组件1
2
3
4
5
6
7
8
9
10
11<template>
<div>
<h1>子应用</h1>
<!--
name(必传):应用名称
url(必传):应用地址,会被自动补全为http://localhost:3000/index.html
baseroute(可选):基座应用分配给子应用的基础路由,就是上面的 `/my-page`
-->
<micro-app name='learnvue' url='http://localhost:8081/' baseroute="/dentition"></micro-app>
</div>
</template>
微前端-qiankun
微前端场景
- 老系统架构陈旧 重构难度大 部分需要与时俱进的功能得不到升级
- 不同模块需要多个团队参与 尤其专业和技术栈有差异
- A系统若干功能或页面被B系统引用
Techniques, strategies and recipes for building a modern web app with multiple teams that can ship features independently. — Micro Frontends
微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。
qiankun方案缺点
- 改造和适配成本高 如微应用打包、入口生命周期,以及router问题
- js沙箱性能问题
- 不支持激活多个微应用 不支持微应用保活
工作流
父应用:安装微前端组件 —> 预留容器dom —> 用组件提供的方法注册子系统
子应用:暴露生命周期钩子
1 | npm i qiankun -S |
register和start都在container的组件层级上 不一定是在顶级组件上 尤其在使用router和模块懒加载的应用中1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32<template>
<div id="viewport">
<div id="subapp-root"></div>
</div>
</template>
<script>
import { registerMicroApps, start } from 'qiankun'
export default {
name: '',
data () {
return {}
},
mounted () {
// 注册微应用
registerMicroApps([
{
name: 'learnvue', // app name registered
entry: '//localhost:8081',
container: '#viewport',
activeRule: '/dentition'
}
])
if (!window.qiankunStarted) {
window.qiankunStarted = true
start()
}
}
}
</script>
当微应用信息注册完之后,一旦浏览器的 url 发生变化,便会自动触发 qiankun 的匹配逻辑,所有 activeRule 规则匹配上的微应用就会被插入到指定的 container 中,同时依次调用微应用暴露出的生命周期钩子。
如果微应用不是直接跟路由关联的时候,你也可以选择手动加载微应用的方式:1
2
3
4
5
6
7import { loadMicroApp } from 'qiankun';
loadMicroApp({
name: 'app',
entry: '//localhost:7100',
container: '#yourContainer',
});
微应用webpack设置1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21const { defineConfig } = require('@vue/cli-service')
const packageName = require('./package.json').name;
module.exports = defineConfig({
transpileDependencies: true,
devServer: {
port: 8081,
headers: {
// 微前端需要跨域
'Access-Control-Allow-Origin': '*'
}
},
configureWebpack: {
output: {
library: `${packageName}-[name]`, // [name]是打包时trunk名称的占位符
libraryTarget: 'umd', // Universal Module Definition通用模块定义
chunkLoadingGlobal: `webpackJsonp_${packageName}`,
}
}
})
暴露声明周期1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48//main.js
import Vue from 'vue'
import App from './App.vue'
import './public-path'
import router from './router'
Vue.config.productionTip = false
let instance = null;
function render(props = {}) {
const { container } = props;
instance = new Vue({
router,
render: h => h(App)
}).$mount(container ? container.querySelector('#app') : '#app')
}
// Running in qiankun
/* eslint-disable no-undef */
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
// Running standalone
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
export async function bootstrap() {
console.log('[vue] vue app bootstraped');
}
export async function mount(props) {
console.log('[vue] props from main framework', props);
render(props);
}
export async function unmount() {
instance.$destroy();
instance.$el.innerHTML = '';
instance = null;
}
export async function update(props) {
console.log('update props', props)
}
应用间通信
部署
troubleshooting
Uncaught TypeError: Cannot redefine property: $router
官网解决方法是将主应用window.Vue对象换个名字
我的选择是 微应用最好不要用router
Wasm OpenGL项目
(windows)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
git pull
# need google access
./emsdk.bat install latest
./emsdk.bat activate latest
cd ..
git clone https://github.com/microsoft/vcpkg.git
cd vcpkg
.\bootstrap-vcpkg.bat
# add vcpkg directory into env variable path
cd ../Workspace/WasmOpenGLProject
vcpkg install boost:wasm32-emscripten
vcpkg install glm:wasm32-emscripten
vcpkg install glad:wasm32-emscripten
issues error: building openssl:wasm32-emscripten failed with: BUILD_FAILED
查看了\vcpkg\buildtrees\openssl\install-wasm32-emscripten-dbg-err.log 内容是1
2
3
4Trying to rename Makefile-333 -> Makefile: Permission denied
make[1]: *** [Makefile:2395: depend] Error 13
make: *** [Makefile:2293: build_modules] Error 2
make: *** Waiting for unfinished jobs....
怀疑其他线程访问导致写入失败1
2set VCPKG_MAX_CONCURRENCY=1
vcpkg install openssl:wasm32-emscripten --clean-after-build
The
FindBoostmodule is removed.
cmake 3.30 起移除了FindBoost模块 导致找不到BOOST_DIR Boost_FILESYSTEM_LIBRARY_DEBUG Boost_INCLUDE_DIR等, 实际上面的步骤中相关包已成功安装在D:\Workspace\Github\vcpkg\installed\wasm32-emscripten\include
make Ninja project1
2
3
4
5
6
7
8
9
10
11
12
13
14
15mkdir build
cd build
emcmake cmake -G "Ninja" ^
-DCMAKE_SYSTEM_NAME=Emscripten ^
-DCMAKE_SYSTEM_PROCESSOR=x86 ^
-DCMAKE_TOOLCHAIN_FILE="%VCPKG_HUGE%/scripts/buildsystems/vcpkg.cmake" ^
-DVCPKG_CHAINLOAD_TOOLCHAIN_FILE="%EMSDK%/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake" ^
-DVCPKG_TARGET_TRIPLET=wasm32-emscripten ^
-DCMAKE_C_COMPILER="%EMCC_PATH%" ^
-DCMAKE_CXX_COMPILER="%EMXX_PATH%" ^
-DCMAKE_BUILD_TYPE=Release ^
-DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=BOTH ^
-DCMAKE_PREFIX_PATH="%VCPKG_HUGE%/installed/wasm32-emscripten" ^
..
cd ..
build wasm1
2cd build
emmake ninja

axios
相比fetch Api, axios 提供了更简洁、更直观的 API,使得发送 HTTP 请求和处理响应更加方便; 自动处理json对象(fetch 可以调用await response.json())1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17axios.post('/api/submit', this.editForm)
.then(response => {
console.log('Form submitted successfully:', response.data);
})
.catch(error => {
console.error('Error submitting form:', error);
});
axios.request({
url: '/api/submit',
method: 'post',
data: this.editForm
}).then(response => {
console.log('Form submitted successfully:', response.data);
}).catch(error => {
console.error('Error submitting form:', error);
});
instance1
2
3
4
5
6
7const instance = axios.create({ baseURL: '/api' });
// Works just like axios(config)
instance({
url: '/users',
method: 'get'
});
拦截器1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21// Add a request interceptor
axios.interceptors.request.use(function (config) {
// Do something before request is sent
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
},
{ synchronous: true, runWhen: () => /* This function returns true */}
);
// Add a response interceptor
axios.interceptors.response.use(function onFulfilled(response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
return response;
}, function onRejected(error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
return Promise.reject(error);
});
Python —— FastAPI
FastAPI与Express.js的区别
| 特性 | FastAPI | Express.js |
|---|---|---|
| 开发语言 | Python | JavaScript/TypeScript |
| 性能 | 基于Starlette和Pydantic,异步处理性能优异 | 基于Node.js事件循环,适合I/O密集型操作 |
| 类型提示 | 原生支持Python类型注解,自动生成API文档 | 需要TypeScript才能获得类型提示 |
| API设计 | 声明式路由,依赖注入系统 | 命令式路由,中间件链 |
| 自动文档 | 自动生成Swagger和ReDoc文档 | 需要第三方库如Swagger UI |
| 数据验证 | Pydantic自动数据验证 | 需要手动验证或使用第三方库 |
| 生态系统 | Python生态(NumPy, Pandas, OpenCV等) | npm生态系统 |
| 学习曲线 | 相对平缓,Python开发者容易上手 | 前端开发者熟悉,学习曲线适中 |
库和项目
1 | pip install "fastapi[standard]" "uvicorn[standard]" |
uvicorn是应用服务器 相当于tomcat
类似express.js 可以写一个main.py 然后执行。 fastapi没有初始化项目的命令行工具 可以考虑官方项目模板: Full Stack FastAPI Template
1 |
1 | my_fastapi_project/ |