鉴于学习Web,不得不学习Javascript,但是还是学习TypeScript更有性价比,Typescript 是 JavaScript 的一个超集,支持 ECMAScript 6 标准,并且最近微软也宣布用Go语言重写Typescript,速度提升10倍左右,更要学了。
一、基础知识
1、基础语法
一个Typescript程序有以下模块
- 模块
- 函数
- 变量
- 语句和表达式
- 注释
Typescript和Javascript的最大区别就是多了变量的类型声明
(1)变量声明
首先了解几个变量声明函数

语法
let age: number = 25;
const pi: number = 3.14;
(2)函数声明
基本与Javascript一致,只是需要在函数传递变量处也加上类型声明
function greet(name: string): string {
return "Hello, " + name;
}
Typescript同样也支持使用箭头来声明函数
const greet = (name: string): string => "Hello, " + name;
(3)类声明
同样与Javascript一致,只需加上类型声明
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet() {
return `Hello, my name is ${this.name}`;
}
}
(4)接口声明
接口在ts代码中起到一个说明书的作用,声明了一个接口之后,当其他部分代码需要创建一个类时,使用implements
(实施)来继承这个接口,那么这个类之内必须包含接口中声明的所有变量和方法。这样可以规范代码。如接口
interface Bookshelf {
width: number; // 书架的宽度
height: number; // 书架的高度
color: string; // 书架的颜色
addBook(book: string): void; // 一个方法,用来添加书
}
那么我们创建一个类来继承这个接口
class MyBookshelf implements Bookshelf {
width: number;
height: number;
color: string;
constructor(width: number, height: number, color: string) {
this.width = width;
this.height = height;
this.color = color;
}
addBook(book: string) {
console.log(`Adding book: ${book}`);
}
}
可以看到该类中包含了接口声明的三个变量和一个方法
(5)模块的导出和导入
Typescript支持模块化编程,已经写好的类、函数、值等可以导出后再其他代码文件复用,现在在person.ts中定义一个Person函数,传入参数name并导出
export class Person {
constructor(public name: string) {}
}
那么我在另一个代码文件中可以
import { Person } from './person';
可以导入多个模块或函数,使用逗号分隔
(5)泛型
泛型变量可以在定义函数,类的参数时暂时不指定变量类型,而是使用占位符替代,例如
function identity<T>(arg: T): T {
return arg;
}
这里指定了传入参数以及返回值的类型为泛型,编译器会自动判断变量类型,如果编译器无法判别,那么我们也可以显式指定,在<>中填入具体类型,如
let firstItem = firstElement<number>(myTuple); // 显式指定 T 为 number
2、常用函数
Typescript的常用函数以及其语法基本与PHP一致,就不在多说,只需注意类型注解
二、实际操作
为了能够顺利完全使用Typescript来搭建一个页面,顺带学一下Next.js(无奈😢),一个Nextjs项目目录应该为
my-next-app/
├── app/
│ ├── layout.js // 根布局
│ ├── page.js // 首页
│ ├── about/
│ │ ├── layout.js // /about 的布局
│ │ └── page.js // /about 页面
│ ├── blog/
│ │ ├── layout.js // /blog 的布局
│ │ ├── page.js // /blog 列表页
│ │ └── [slug]/
│ │ └── page.js // /blog/:slug 详情页
│ └── dashboard/
│ ├── layout.js // /dashboard 的布局
│ └── page.js // /dashboard 页面
├── public/
│ ├── logo.png // 静态资源
│ └── favicon.ico // 网站图标
├── node_modules/ // 项目依赖
├── package.json // 项目配置文件
├── next.config.js // Next.js 配置文件
└── .next/ // 构建输出目录
首先创建一个Nextjs项目
npx create-next-app@latest my-next-app
然后
npm run dev

启动成功,我们可以直接在app文件夹下直接创建新文件夹,文件夹的名称将对应其路径,若是路径下还有子文件夹,那么子文件夹的路径也可用,我们同样来写一个留言板界面,首先写后端,逻辑是
GET 请求
- 检查
public/messages
目录是否存在。 - 如果存在,读取目录下所有
.txt
文件,提取文件名和内容。 - 返回留言列表的 JSON 数据。
POST 请求
- 解析请求体中的留言内容。
- 创建
public/messages
目录(如果不存在)。 - 生成带时间戳的文件名,将留言内容写入文件。
- 返回成功或失败的 JSON 响应。
import { NextResponse } from 'next/server';
import fs from 'fs/promises';
import path from 'path';
export async function GET() {
try {
const dirPath = path.join(process.cwd(), 'public/messages');
try {
await fs.access(dirPath);
} catch {
return NextResponse.json({ messages: [] });
}
const files = await fs.readdir(dirPath);
const messages = await Promise.all(files
.filter(file => file.endsWith('.txt'))
.map(async (file) => {
const content = await fs.readFile(path.join(dirPath, file), 'utf8');
return { filename: file, content };
}));
return NextResponse.json({ messages });
} catch (error) {
console.error('读取留言失败:', error);
return NextResponse.json(
{ success: false, error: '服务器错误' },
{ status: 500 }
);
}
}
export async function POST(request: Request) {
try {
const { content } = await request.json();
// 创建messages目录(如果不存在)
const dirPath = path.join(process.cwd(), 'public/messages');
await fs.mkdir(dirPath, { recursive: true });
// 生成带时间戳的文件名
const filename = `message_${Date.now()}.txt`;
const filePath = path.join(dirPath, filename);
// 写入文件内容
await fs.writeFile(filePath, content, 'utf8');
return NextResponse.json({ success: true });
} catch (error) {
console.error('保存留言失败:', error);
return NextResponse.json(
{ success: false, error: '服务器错误' },
{ status: 500 }
);
}
}
用到好几处异步,不太懂,下面是前端代码,主要逻辑为:
用户输入留言并提交后,通过 POST 请求发送留言内容到服务器;提交成功后,清空输入框并重新加载留言列表;页面加载时自动获取并显示所有留言,每条留言显示内容和时间戳。
'use client';
import { useEffect, useState } from 'react';
export default function Home() {
const [message, setMessage] = useState('');
const [messages, setMessages] = useState<{filename: string; content: string}[]>([]);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
const response = await fetch('/api/messages', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ content: message })
});
if (response.ok) {
setMessage('');
fetchMessages();
}
} catch (error) {
console.error('提交失败:', error);
}
};
const fetchMessages = async () => {
try {
const response = await fetch('/api/messages');
const data = await response.json();
setMessages(data.messages);
} catch (error) {
console.error('获取留言失败:', error);
}
};
useEffect(() => {
fetchMessages();
}, []);
return (
<div className="container mx-auto p-4 max-w-2xl">
<h1 className="text-2xl font-bold mb-4">留言板</h1>
<form onSubmit={handleSubmit} className="mb-8">
<textarea
value={message}
onChange={(e) => setMessage(e.target.value)}
className="w-full p-2 border rounded mb-2"
rows={4}
placeholder="输入您的留言..."
required
/>
<button
type="submit"
className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
>
提交留言
</button>
</form>
<div className="space-y-4">
{messages.map((msg) => (
<div key={msg.filename} className="p-4 border rounded bg-gray-50">
<p className="text-gray-800">{msg.content}</p>
<time className="text-sm text-gray-500 mt-2 block">
{new Date(parseInt(msg.filename.split('_')[1])).toLocaleString()}
</time>
</div>
))}
</div>
</div>
);
}
最终效果

CSS是直接使用TailwindCSS,感觉挺方便的