1. 概述
本文主要介紹以下三個(gè)部分:
- 配置開發(fā)環(huán)境(vs code, git and nodejs)
- TypeScript快速入門
- 初始化Three.js項(xiàng)目工程
本文代碼倉(cāng)庫(kù)地址:https://github.com/ue007/three.ts
2. 配置開發(fā)環(huán)境
- 代碼編輯器:VSCode
- 代碼版本管理:Git for Windows (Optional)
- 運(yùn)行時(shí)環(huán)境:NodeJS
3. TypeScript快速入門
代碼地址:https://github.com/ue007/three.ts/tree/main/01-HelloTypeScript
3.1 TypeScript介紹
TypeScript 是 JavaScript 的一個(gè)超集,支持 ECMAScript 6 標(biāo)準(zhǔn)(ES6 教程)皮假。
TypeScript 由微軟開發(fā)的自由和開源的編程語言稽坤。
TypeScript 設(shè)計(jì)目標(biāo)是開發(fā)大型應(yīng)用,它可以編譯成純 JavaScript闰蚕,編譯出來的 JavaScript 可以運(yùn)行在任何瀏覽器上。
3.1.1 JavaScript 與 TypeScript 的區(qū)別
TypeScript 是 JavaScript 的超集,擴(kuò)展了 JavaScript 的語法卖怜,因此現(xiàn)有的 JavaScript 代碼可與 TypeScript 一起工作無需任何修改键思,TypeScript 通過類型注解提供編譯時(shí)的靜態(tài)類型檢查础爬。
TypeScript 可處理已有的 JavaScript 代碼,并只對(duì)其中的 TypeScript 代碼進(jìn)行編譯吼鳞。
[圖片上傳失敗...(image-45c071-1618983989118)]
[圖片上傳失敗...(image-2d3302-1618983989118)]
3.1.2 為什么選擇TypeScript?
開發(fā)者選擇使用TypeScript語言至少有以下幾點(diǎn)原因:
能夠更早地發(fā)現(xiàn)代碼中的錯(cuò)誤看蚜。
能夠幫助提高生產(chǎn)力。
支持JavaScript語言的最新特性并且使用了與JavaScript語言相同的語法和語義赔桌。
3.2 安裝TypeScript
打開終端供炎,全局安裝typescript:
npm install -g typescript
查看typescript版本
tsc -v
3.3 創(chuàng)建foo.js文件
代碼地址:https://github.com/ue007/three.ts/01-HelloThree
創(chuàng)建foo.js文件,并輸入代碼:
function foo(bar) {
return "Hello, " + bar;
}
let baz = "ThreeJs With TypeScript";
console.log(foo(baz));
直接在Node環(huán)境下執(zhí)行疾党,在控制臺(tái)輸入:
node foo.js
輸出內(nèi)容:
Hello, ThreeJs With TypeScript
3.4 創(chuàng)建foo.ts文件
下面使用ts方式音诫,等效輸出foo.js文件。
[圖片上傳失敗...(image-d9e811-1618983989118)]
首先雪位,將foo.js文件重命名為foo.ts竭钝。
其次,在控制臺(tái)雹洗,輸入如下命令:
tsc foo.ts
編譯完成之后香罐,會(huì)對(duì)應(yīng)生成foo.js文件,并在node環(huán)境下執(zhí)行命令:
node foo.js
同樣輸出如下結(jié)果:
Hello, ThreeJs With TypeScript
3.5 Interfaces介紹
在TypeScript中时肿,接口(Interfaces)和類型聲明(Type Declarations)提供了幾乎完全相同的功能庇茫。接口/類型是用于類型檢查的結(jié)構(gòu)。接口/類型定義了對(duì)象可以擁有的屬性和類型螃成。
替換foo.ts文件中代碼如下:
interface Quux {
quuz: string;
corge: number;
}
function foo(bar: Quux) {
return 'Hello, ' + bar.quuz + ' ' + bar.corge;
}
let baz: Quux = {
quuz: 'ABC',
corge: 123,
};
console.log(foo(baz));
執(zhí)行命令:
tsc foo.ts
node foo.js
在Node環(huán)境中執(zhí)行結(jié)果:
Hello, ABC 123
3.6 Classes介紹
類(Classes)旦签,本質(zhì)上是對(duì)象在實(shí)現(xiàn)時(shí)應(yīng)該是什么樣的藍(lán)圖啥容。一個(gè)類可以有初始化的屬性和方法來幫助創(chuàng)建和修改對(duì)象。
定義一個(gè)Grault類顷霹,代碼如下:
class Grault {
private garply: string;
constructor(quux: Quux, waldo: number[]) {
this.garply = quux.quuz + ' ' + quux.corge + ' ' + waldo;
}
public getGarply() {
return this.garply;
}
}
interface Quux {
quuz: string;
corge: number;
}
let baz = { quuz: 'ABC', corge: 123 };
let fred: Grault = new Grault(baz, [1, 2, 3]);
console.log(fred.getGarply());
執(zhí)行命令:
tsc foo.ts
node foo.js
在Node環(huán)境中執(zhí)行結(jié)果:
ABC 123 1,2,3
3.7 在瀏覽器中運(yùn)行
創(chuàng)建foo.html文件咪惠,并寫入如下代碼:
<!DOCTYPE html>
<html>
<head>
<title>TypeScript Crash Course</title>
</head>
<body>
<script src="foo.js"></script>
</body>
</html>
在foo.ts文件中添加如下代碼:
class Grault {
private garply: string;
constructor(quux: Quux, waldo: number[]) {
this.garply = quux.quuz + ' ' + quux.corge + ' ' + waldo;
}
public getGarply() {
return this.garply;
}
}
interface Quux {
quuz: string;
corge: number;
}
let baz = { quuz: 'ABC', corge: 123 };
let fred: Grault = new Grault(baz, [1, 2, 3]);
console.log(fred.getGarply());
try {
document.body.innerHTML = fred.getGarply();
} catch (e) {}
使用vscode編輯器打開Live Server服務(wù)器,在瀏覽器中查看foo.html頁面淋淀,效果如下:
[圖片上傳失敗...(image-a27854-1618983989118)]
4. 初始化Three.js項(xiàng)目
接下來遥昧,我們將創(chuàng)建Three.js項(xiàng)目模板。代碼地址:https://github.com/ue007/three.ts/tree/main/02-HelloThreeTS
4.1 創(chuàng)建工程目錄
mkdir 02-HelloThreeTS
4.2 初始化工程
使用npm進(jìn)行初始化
npm init
一直回車朵纷,直至初始化完成炭臭,在目錄下會(huì)生成package.json文件。
4.3 安裝Three.js依賴庫(kù)
執(zhí)行命令:
cnpm install @types/three --save-dev // 具有類型生命的three版本
4.4 創(chuàng)建目錄結(jié)構(gòu)
按照如下目錄結(jié)構(gòu)袍辞,創(chuàng)建文件和目錄:
|-- Three.js-TypeScript-Tutorial
|-- dist
|-- client
|-- index.html
|-- server
|-- node_modules
|-- three
|-- (Several extra files and folders containing the Three.js source code)
|-- src
|-- client
|-- server
|-- package.json
|-- package-lock.json
其中dist/client/index.html文件內(nèi)容如下:
<!DOCTYPE html>
<html>
<head>
<title>Three.js TypeScript Tutorials</title>
</head>
<body>
<script type="module" src="client.js"></script>
</body>
</html>
4.5 添加初始化代碼
4.5.1 client.ts
在src/client.ts文件中鞋仍,添加如下代碼:
const scene: THREE.Scene = new THREE.Scene();
const camera: THREE.PerspectiveCamera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const renderer: THREE.WebGLRenderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const geometry: THREE.BoxGeometry = new THREE.BoxGeometry();
const material: THREE.MeshBasicMaterial = new THREE.MeshBasicMaterial({
color: 0x00ff00,
wireframe: true,
});
const cube: THREE.Mesh = new THREE.Mesh(geometry, material);
scene.add(cube);
camera.position.z = 2;
var animate = function () {
requestAnimationFrame(animate);
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
};
animate();
在src/client文件夾中,創(chuàng)建tsconfig.json文件搅吁,并插入如下代碼:
{
"compilerOptions": {
"target": "ES6",
"module": "ES6",
"outDir": "../../dist/client",
"moduleResolution": "node"
},
"include": [
"**/*.ts"
]
}
4.5.2 server.ts
在src/server目錄下威创,創(chuàng)建server.ts文件,并插入如下代碼:
const port: number = 3000
class App {
private server: http.Server
private port: number
constructor(port: number) {
this.port = port
const app = express()
app.use(express.static(path.join(__dirname, '../client')))
this.server = new http.Server(app);
}
public Start() {
this.server.listen(this.port, () => {
console.log( `Server listening on port ${this.port}.` )
})
}
}
new App(port).Start()
在src/server文件夾中谎懦,創(chuàng)建tsconfig.json文件肚豺,并插入如下代碼:
{
"compilerOptions": {
"target": "ES2019",
"module": "commonjs",
"outDir": "../../dist/server",
"sourceMap": true,
"esModuleInterop": true
},
"include": [
"**/*.ts"
]
}
4.5.3 目錄結(jié)構(gòu)
創(chuàng)建完成之后的目錄結(jié)構(gòu)如下:
|-- Three.js-TypeScript-Tutorial
|-- dist
|-- client
|-- index.html
|-- server
|-- node_modules
|-- three
|-- (Several extra files and folders containing the Three.js source code)
|-- src
|-- client
|-- client.ts
|-- tsconfig.json
|-- server
|-- server.ts
|-- tsconfig.json
|-- package.json
|-- package-lock.json
4.6 引入依賴
上面代碼,我們會(huì)發(fā)現(xiàn)界拦,在編輯器中會(huì)提示錯(cuò)誤吸申,如不能找到對(duì)應(yīng)的namespace等等,這是因?yàn)檫€沒有引入對(duì)應(yīng)的依賴享甸。
4.6.1 client side
創(chuàng)建client.ts文件截碴,代碼如下:
import * as THREE from '/build/three.module.js'
import { OrbitControls } from '/jsm/controls/OrbitControls'
const scene: THREE.Scene = new THREE.Scene()
const camera: THREE.PerspectiveCamera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
const renderer: THREE.WebGLRenderer = new THREE.WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)
const controls = new OrbitControls(camera, renderer.domElement)
const geometry: THREE.BoxGeometry = new THREE.BoxGeometry()
const material: THREE.MeshBasicMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true })
const cube: THREE.Mesh = new THREE.Mesh(geometry, material)
scene.add(cube)
camera.position.z = 2
var animate = function () {
requestAnimationFrame(animate)
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
controls.update()
renderer.render(scene, camera)
};
animate();
創(chuàng)建tsconfig.json文件,代碼如下:
{
"compilerOptions": {
"target": "ES6",
"module": "ES6",
"outDir": "../../dist/client",
"baseUrl": ".",
"paths": {
"/build/three.module.js": ["../../node_modules/@types/three"],
"/jsm/*": ["../../node_modules/@types/three/examples/jsm/*"],
},
"moduleResolution": "node"
},
"include": [
"**/*.ts"
]
}
執(zhí)行編譯腳本:
tsc -p .\src\client\
4.6.2 server side
在server.ts文件開頭蛉威,引入如下代碼:
import express from "express"
import path from "path"
import http from "http"
安裝依賴:
cnpm install @types/node --save-dev
cnpm install @types/express --save-dev
cnpm install express --save-dev
完整代碼如下:
import express from "express"
import path from "path"
import http from "http"
const port: number = 3000
class App {
private server: http.Server
private port: number
constructor(port: number) {
this.port = port
const app = express()
app.use(express.static(path.join(__dirname, '../client')))
app.use('/build/three.module.js', express.static(path.join(__dirname, '../../node_modules/three/build/three.module.js')))
app.use('/jsm/controls/OrbitControls', express.static(path.join(__dirname, '../../node_modules/three/examples/jsm/controls/OrbitControls.js')))
this.server = new http.Server(app);
}
public Start() {
this.server.listen(this.port, () => {
console.log( `Server listening on port ${this.port}.` )
})
}
}
new App(port).Start()
編譯server.ts文件日丹,命令如下:
tsc -p ./src/server
對(duì)應(yīng)會(huì)在dist/server目錄下,生成server.js文件和server.js.map文件瓷翻。
執(zhí)行腳本聚凹,啟動(dòng)服務(wù)器:
node .\dist\server\server.js
在瀏覽器中割坠,打開地址:localhost:3000
4.7 配置TSC Watch
繼續(xù)配置項(xiàng)目齐帚,使得源代碼在發(fā)生任何更改的時(shí)候,重新編譯彼哼, 添加-w表示watch狀態(tài)对妄。
tsc -p src/server/ -w
4.8 配置Nodemon
nodemon nodemon是一種工具,可以自動(dòng)檢測(cè)到目錄中的文件更改時(shí)通過重新啟動(dòng)應(yīng)用程序來調(diào)試基于node.js的應(yīng)用程序。
安裝nodemon:
npm install --save-dev nodemon
啟動(dòng)服務(wù):
npx nodemon dist/server/server.js
4.9 一鍵啟動(dòng)
我們可以創(chuàng)建一個(gè)命令來同時(shí)啟動(dòng)兩個(gè)進(jìn)程敢朱,而不是一直輸入這些compile和nodemon命令剪菱。
安裝concurrently:
npm install --save-dev concurrently
在package.json文件中添加腳本:
"dev" : "concurrently -k \"tsc -p ./src/server -w\" \"nodemon ./dist/server/server.js\"",
執(zhí)行命令:
npm run dev
打開瀏覽器摩瞎,輸入地址:localhost:3000,效果如下: