当前位置:首页>python>年前碎片学习 - python、ts、js在前后端自动化中的应用

年前碎片学习 - python、ts、js在前后端自动化中的应用

  • 2026-02-20 11:16:01
年前碎片学习 - python、ts、js在前后端自动化中的应用
这半年前后端自动化测试一直都是python为主,前端是playwright,后端是fastapi和flask,今天结合自己的知识和ai的能力,系统了解下ts和js能在这方面做什么事情,这两个语言一直没有深入研究过,今天花点时间理解下。
这两周持续做了很多基于ai创建测试设计和自动化测试框架的实践,虽然有些成果,但是我认为自己仍在答案的门口转圈,很烦,针对测试这个极度要求准确的领域,如何才能尽量消除ai的不稳定性?而且随着特性的变化,如何能利用ai做自适应的维护,这套架子目前还是不知道怎么搭。
先搞点八股当晚餐:
分四个阶段:原理详解、基础教程、脚本的执行方式时间节点、后端自动化中的应用:
1.原理详解:
# JavaScript/TypeScript 原理详解## 为何用于网页开发、作用及与 Python 的对比---## 目录1. [JavaScript/TypeScript 为何用于网页和前端开发](#javascripttypescript-为何用于网页和前端开发)2. [在前端开发中的具体作用](#在前端开发中的具体作用)3. [与 Python 的详细对比](#与-python-的详细对比)4. [前端自动化开发:两种语言的选择](#前端自动化开发两种语言的选择)5. [实际代码对比](#实际代码对比)6. [环境搭建与第一个程序](#环境搭建与第一个程序)7. [学习建议](#学习建议)---## JavaScript/TypeScript 为何用于网页和前端开发### 1. 历史背景与设计初衷#### 1.1 诞生背景JavaScript 在 **1995年** 由 **Netscape 公司** 的 **Brendan Eich** 在 **10天内** 设计并实现。它的设计初衷非常明确:- **目标**:让网页具有交互性- **定位**:一种运行在浏览器端的脚本语言- **需求**:简单、轻量、易于学习#### 1.2 发展历程```1995年 - JavaScript 诞生(Netscape Navigator 2.0)  ↓1996年 - 微软推出 JScript(IE 浏览器)  ↓1997年 - ECMAScript 标准化(ES1)  ↓2005年 - AJAX 技术出现,Web 2.0 时代开启  ↓2009年 - Node.js 发布,JS 可用于服务端  ↓2012年 - TypeScript 发布(微软)  ↓2015年 - ES6/ES2015 重大更新,现代化 JS  ↓2022年 - ES2022,持续发展中```### 2. 技术架构原因#### 2.1 浏览器架构现代浏览器由多个核心组件构成:```┌─────────────────────────────────────────────────────┐│                  浏览器主进程                        │├─────────────┬─────────────┬─────────────┬───────────┤│   渲染进程   │   GPU 进程   │  网络进程    │ 插件进程  ││             │             │             │          ││ ┌─────────┐ │ ┌─────────┐ │ ┌─────────┐ │          ││ │ 渲染引擎  │ │ │ GPU     │ │ │ HTTP    │ │          ││ │ Blink/  │ │ │ 计算    │ │ │ 请求    │ │          ││ │ Gecko   │ │ │         │ │ │         │ │          ││ │         │ │ └─────────┘ │ └─────────┘ │          ││ ├─────────┤ │             │             │          ││ │ JS引擎   │ │             │             │          ││ │ V8/      │ │             │             │          ││ │ Spider  │ │             │             │          ││ │ Monkey  │ │             │             │          ││ └─────────┘ │             │             │          │└─────────────┴─────────────┴─────────────┴───────────┘```#### 2.2 JavaScript 引擎的作用JavaScript 引擎是浏览器的核心组件之一,负责执行 JavaScript 代码:| 引擎 | 浏览器 | 特点 ||------|--------|------|| **V8** | Chrome、Edge、Node.js | 性能最强,Google 开发 || **SpiderMonkey** | Firefox | Mozilla 开发 || **JavaScriptCore** | Safari | Apple 开发 || **Chakra** | Edge (Legacy) | 微软开发 |**为什么需要 JS 引擎?**1. **HTML/CSS 是声明式的**   - HTML 定义页面结构("这是什么"   - CSS 定义页面样式("长什么样"   - 但两者都无法处理"交互""动态"2. **JavaScript 是命令式的**   - 定义"如何做"   - 可以响应事件、修改状态   - 可以与浏览器 API 通信#### 2.3 浏览器提供的 JavaScript API浏览器为 JavaScript 提供了丰富的 API,使其能与浏览器深度集成:```javascript// 1. DOM API - 操作页面内容document.getElementById('id')document.createElement('div')element.appendChild(child)// 2. CSSOM API - 操作样式element.style.color = 'red'window.getComputedStyle(element)// 3. Event API - 事件处理element.addEventListener('click', handler)window.onclick = handler// 4. Fetch API - 网络请求fetch('https://api.example.com/data')// 5. Storage API - 本地存储localStorage.setItem('key''value')sessionStorage.getItem('key')// 6. Geolocation API - 地理位置navigator.geolocation.getCurrentPosition(success, error)// 7. Canvas API - 图形绘制const ctx = canvas.getContext('2d');ctx.fillRect(0, 0, 100, 100);// 8. Web Audio API - 音频处理const audioCtx = new AudioContext();// 9. WebSocket API - 实时通信const ws = new WebSocket('ws://example.com');```### 3. 为什么浏览器只支持 JavaScript?#### 3.1 历史路径依赖- 1995年 JavaScript 诞生后,Netscape 和 IE 都实现了 JS 引擎- 1999年 ECMAScript 第三版成为标准,各大浏览器统一支持- 此后积累了大量网页使用 JS- 更换语言意味着破坏所有现有网页#### 3.2 WebAssembly 的补充近年来浏览器引入了 **WebAssembly (WASM)**,允许运行其他语言编译的二进制代码:```JavaScript     ←→ 浏览器原生支持WebAssembly    ←→ 编译自 C++/Rust/Go 等语言```但 WebAssembly 不能直接操作 DOM,仍需通过 JS 桥接。### 4. TypeScript 的价值TypeScript 是 JavaScript 的超集,它在 JS 的基础上添加了:```typescript// JavaScript - 动态类型,运行时才报错function calculateTotal(price, quantity, tax) {    return (price * quantity) * (1 + tax);}// 调用时可能传入错误类型calculateTotal("100", 5, 0.1);  // 结果错误,但不会立即报错// TypeScript - 静态类型,编译时发现错误function calculateTotal(    price: number,    quantity: number,    tax: number): number {    return (price * quantity) * (1 + tax);}// 调用时类型检查calculateTotal("100", 5, 0.1);  // 编译错误:不能将类型"string"分配给参数```**TypeScript 的优势:**1. **类型安全** - 编译时发现错误2. **IDE 支持** - 自动补全、重构、导航3. **代码可维护性** - 类型即文档4. **团队协作** - 降低沟通成本---## 在前端开发中的具体作用### 1. Web 技术的三层架构```┌───────────────────────────────────────────────────┐│                    用户浏览器                        │├───────────────────────────────────────────────────┤│                                                   ││  ┌────────────────────────────────────────────┐  ││  │              HTML (结构层)                  │  ││  │  - 定义页面内容                              │  ││  │  - 语义化标签                                │  ││  │  - 示例:                                     │  ││  │    <h1>标题</h1>                            │  ││  │    <p>段落</p>                              │  ││  │    <div id="content">内容</div>            │  ││  └────────────────────────────────────────────┘  ││                                                   ││  ┌────────────────────────────────────────────┐  ││  │               CSS (表现层)                  │  ││  │  - 定义页面样式                              │  ││  │  - 响应式布局                                │  ││  │  - 动画效果                                  │  ││  │  - 示例:                                     │  ││  │    #content {                               │  ││  │      color: #333;                           │  ││  │      font-size: 16px;                       │  ││  │      animation: fade 1s;                    │  ││  │    }                                        │  ││  └────────────────────────────────────────────┘  ││                                                   ││  ┌────────────────────────────────────────────┐  ││  │            JavaScript (行为层)              │  ││  │  - 实现交互逻辑                              │  ││  │  - 处理数据                                  │  ││  │  - 控制页面行为                              │  ││  │  - 示例:                                     │  ││  │    const el = document.getElementById('content') │  ││  │    el.textContent = '新内容'                │  ││  └────────────────────────────────────────────┘  ││                                                   │└───────────────────────────────────────────────────┘```### 2. JavaScript/TypeScript 的核心职责#### 2.1 页面交互```javascript// 按钮点击事件document.getElementById('submit-btn').addEventListener('click'function() {    const username = document.getElementById('username').value;    const password = document.getElementById('password').value;    if (!username || !password) {        alert('请填写完整信息');        return;    }    // 提交表单    login(username, password);});// 悬停效果const card = document.querySelector('.card');card.addEventListener('mouseenter'function() {    this.classList.add('hover');});card.addEventListener('mouseleave'function() {    this.classList.remove('hover');});// 滚动事件window.addEventListener('scroll'function() {    const header = document.querySelector('header');    if (window.scrollY > 100) {        header.classList.add('fixed');    } else {        header.classList.remove('fixed');    }});```#### 2.2 表单验证```javascript// 实时验证用户名function validateUsername(username) {    // 长度检查    if (username.length < 3 || username.length > 20) {        return { valid: false, message: '用户名长度为 3-20 个字符' };    }    // 格式检查    if (!/^[a-zA-Z0-9_]+$/.test(username)) {        return { valid: false, message: '只能包含字母、数字和下划线' };    }    return { valid: true };}// 邮箱验证function validateEmail(email) {    const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;    return regex.test(email);}// 密码强度验证function validatePassword(password) {    let strength = 0;    if (password.length >= 8) strength++;    if (/[a-z]/.test(password)) strength++;    if (/[A-Z]/.test(password)) strength++;    if (/[0-9]/.test(password)) strength++;    if (/[^a-zA-Z0-9]/.test(password)) strength++;    return strength; // 0-5,5 最强}// 使用示例document.getElementById('password').addEventListener('input'function(e) {    const strength = validatePassword(e.target.value);    const indicator = document.getElementById('strength-indicator');    if (strength >= 4) {        indicator.textContent = '强';        indicator.className = 'strength strong';    } else if (strength >= 2) {        indicator.textContent = '中';        indicator.className = 'strength medium';    } else {        indicator.textContent = '弱';        indicator.className = 'strength weak';    }});```#### 2.3 动态内容加载(AJAX)```javascript// 使用 Fetch API 加载数据async function loadProducts() {    try {        const response = await fetch('https://api.example.com/products');        if (!response.ok) {            throw new Error(`HTTP error! status: ${response.status}`);        }        const products = await response.json();        // 动态渲染产品列表        const container = document.getElementById('product-list');        container.innerHTML = products.map(product => `            <div class="product-card">                <img src="${product.image}" alt="${product.name}">                <h3>${product.name}</h3>                <p class="price"${product.price}</p>                <button onclick="addToCart(${product.id})">加入购物车</button>            </div>        `).join('');    } catch (error) {        console.error('加载产品失败:', error);        document.getElementById('error-message').textContent = '加载失败,请稍后重试';    }}// 页面加载时执行document.addEventListener('DOMContentLoaded', loadProducts);```#### 2.4 单页应用(SPA)路由```javascript// 简单的路由实现const routes = {    '/''home',    '/about''about',    '/products''products',    '/contact''contact'};function navigate(path) {    // 更新 URL    history.pushState(null, '', path);    // 渲染对应页面    const page = routes[path] || 'home';    renderPage(page);}function renderPage(pageName) {    // 隐藏所有页面    document.querySelectorAll('.page').forEach(el => {        el.style.display = 'none';    });    // 显示目标页面    document.getElementById(`${pageName}-page`).style.display = 'block';}// 监听浏览器后退/前进window.addEventListener('popstate'function() {    const path = window.location.pathname;    const page = routes[path] || 'home';    renderPage(page);});// 导航链接点击事件document.querySelectorAll('.nav-link').forEach(link => {    link.addEventListener('click'function(e) {        e.preventDefault();        const path = this.getAttribute('href');        navigate(path);    });});```#### 2.5 状态管理```javascript// 简单的状态管理器class Store {    constructor(initialState = {}) {        this.state = initialState;        this.listeners = [];    }    // 获取状态    getState() {        return this.state;    }    // 更新状态    setState(newState) {        this.state = { ...this.state, ...newState };        this.notify();    }    // 订阅状态变化    subscribe(listener) {        this.listeners.push(listener);    }    // 通知所有订阅者    notify() {        this.listeners.forEach(listener => listener(this.state));    }}// 使用示例const store = new Store({    user: null,    cart: [],    isLoading: false});// 订阅状态变化store.subscribe(function(state) {    // 更新 UI    updateUserDisplay(state.user);    updateCartCount(state.cart.length);    updateLoadingIndicator(state.isLoading);});// 更新状态store.setState({    user: { name: 'Tom', email: 'tom@example.com' },    isLoading: false});// 添加到购物车function addToCart(product) {    const cart = store.getState().cart;    store.setState({        cart: [...cart, product]    });}```#### 2.6 本地存储```javascript// LocalStorage - 永久存储(除非手动清除)const user = {    name: 'Tom',    email: 'tom@example.com',    preferences: {        theme: 'dark',        language: 'zh-CN'    }};// 保存数据localStorage.setItem('user', JSON.stringify(user));// 读取数据const savedUser = JSON.parse(localStorage.getItem('user'));console.log(savedUser);// SessionStorage - 会话存储(关闭标签页后清除)sessionStorage.setItem('tempData''临时数据');// IndexedDB - 更强大的数据库// 适合存储大量结构化数据const request = indexedDB.open('myDatabase', 1);request.onerror = function(event) {    console.error('Database error:', event.target.error);};request.onsuccess = function(event) {    const db = event.target.result;    // 使用数据库};request.onupgradeneeded = function(event) {    const db = event.target.result;    // 创建对象存储    const objectStore = db.createObjectStore('users', { keyPath: 'id' });    // 创建索引    objectStore.createIndex('name''name', { unique: false });};```### 3. 现代前端框架中的角色#### 3.1 React```typescriptimport React, { useState, useEffect } from 'react';interface User {    id: number;    name: string;    email: string;}function UserProfile() {    // 状态管理    const [user, setUser] = useState<User | null>(null);    const [loading, setLoading] = useState(true);    // 副作用(数据获取)    useEffect(() => {        async function fetchUser() {            try {                const response = await fetch('/api/user/1');                const data = await response.json();                setUser(data);            } catch (error) {                console.error('Failed to fetch user:', error);            } finally {                setLoading(false);            }        }        fetchUser();    }, []); // 空依赖数组表示只在挂载时执行    if (loading) {        return <div>加载中...</div>;    }    if (!user) {        return <div>用户不存在</div>;    }    return (        <div className="user-profile">            <h1>{user.name}</h1>            <p>{user.email}</p>            <button onClick={() => handleEdit(user.id)}>编辑</button>        </div>    );}```#### 3.2 Vue```typescript<script setup lang="ts">import { ref, onMounted } from 'vue';interface User {  id: number;  name: string;  email: string;}const user = ref<User | null>(null);const loading = ref(true);onMounted(async () => {  try {    const response = await fetch('/api/user/1');    user.value = await response.json();  } catch (error) {    console.error('Failed to fetch user:', error);  } finally {    loading.value = false;  }});function handleEdit(id: number) {  // 编辑逻辑}</script><template>  <div class="user-profile" v-if="user">    <h1>{{ user.name }}</h1>    <p>{{ user.email }}</p>    <button @click="handleEdit(user.id)">编辑</button>  </div>  <div v-else-if="loading">加载中...</div></template>```---## 与 Python 的详细对比### 1. 运行环境对比| 维度 | JavaScript/TypeScript | Python ||------|----------------------|--------|| **原生运行环境** | 浏览器、Node.js | 操作系统、虚拟机 || **浏览器支持** | ✅ 所有浏览器原生支持 | ❌ 不支持 || **服务端运行** | ✅ Node.js | ✅ 原生支持 || **移动端** | ✅ React Native, NativeScript | ❌ 需要桥接或转换 || **桌面应用** | ✅ Electron, Tauri | ✅ Tkinter, PyQt, Kivy || **嵌入式** | ⚠️ 有限 | ✅ MicroPython, CircuitPython |### 2. 语法对比详细版#### 2.1 变量声明```javascript// JavaScript// 声明方式多样let name = "Tom";        // 块级作用域,可变const age = 18;          // 块级作用域,不可变var city = "Beijing";    // 函数作用域,不推荐// TypeScript - 带类型注解let username: string = "Tom";let userAge: number = 18;let isAdmin: boolean = true;// 联合类型let value: string | number = "hello";value = 123; // OK``````python# Python# 声明方式统一name = "Tom"      # 自动推断为 strage = 18          # 自动推断为 intcity = "Beijing"  # 自动推断为 str# 类型提示(可选)username: str = "Tom"user_age: int = 18is_admin: bool = True# 联合类型from typing import Unionvalue: Union[str, int] = "hello"value = 123  # OK```#### 2.2 数据结构```javascript// JavaScript 数组let arr = [12345];// 添加元素arr.push(6);           // 末尾添加arr.unshift(0);         // 开头添加arr.splice(202.5);  // 指定位置插入// 删除元素arr.pop();             // 删除末尾arr.shift();            // 删除开头arr.splice(21);       // 删除指定位置// 遍历arr.forEach((item, index) => {    console.log(index, item);});// 函数式操作const doubled = arr.map(x => x * 2);         // 映射const evens = arr.filter(x => x % 2 === 0);  // 过滤const sum = arr.reduce((a, b) => a + b, 0);  // 累加// JavaScript 对象let obj = {    name: "Tom",    age: 18,    greet: function() {        return `Hello, ${this.name}`;    }};// 访问属性console.log(obj.name);           // 点表示法console.log(obj["age"]);         // 方括号表示法// 解构赋值const { name, age } = obj;// 对象展开const obj2 = { ...obj, city: "Beijing" };``````python# Python 列表arr = [12345]# 添加元素arr.append(6)        # 末尾添加arr.insert(00)      # 开头插入arr.insert(22.5)   # 指定位置插入# 删除元素arr.pop()            # 删除末尾arr.pop(0)           # 删除指定位置del arr[2]           # 删除指定位置arr.remove(3)        # 删除指定值# 列表推导式(函数式操作)doubled = [x * 2 for x in arr]evens = [x for x in arr if x % 2 == 0]sum_value = sum(arr)# 遍历for index, item in enumerate(arr):    print(index, item)# Python 字典obj = {    "name": "Tom",    "age": 18}# 定义方法def greet(self):    return f"Hello, {self['name']}"# 访问属性print(obj["name"])          # 方括号表示法print(obj.get("age"))       # get 方法(默认 None)# 解构赋值name, age = obj["name"], obj["age"]# 字典展开(Python 3.9+)obj2 = {**obj, "city""Beijing"}```#### 2.3 函数定义```javascript// JavaScript 函数声明function add(a, b) {    return a + b;}// 函数表达式const multiply = function(a, b) {    return a * b;};// 箭头函数(ES6)const subtract = (a, b) => a - b;// 带默认参数function greet(name = "Guest") {    return `Hello, ${name}`;}// 可变参数function sum(...numbers) {    return numbers.reduce((a, b) => a + b, 0);}// 使用sum(123);  // 6// TypeScript - 带类型注解function divide(a: number, b: number): number {    return a / b;}// 可选参数function createUser(name: string, age?: number): void {    console.log(name, age);}// 泛型函数function identity<T>(arg: T): T {    return arg;}identity<string>("hello");identity<number>(123);``````python# Python 函数定义def add(a, b):    return a + b# lambda 函数multiply = lambda a, b: a * b# 带默认参数def greet(name="Guest"):    return f"Hello, {name}"# 可变参数def sum(*numbers):    return sum(numbers)# 使用sum(123)  # 6# 关键字参数def create_user(name, age=None):    print(name, age)# 类型提示def divide(a: int, b: int) -> float:    return a / b# 泛型(使用 TypeVar)from typing import TypeVarT = TypeVar('T')def identity(arg: T) -> T:    return argidentity(str)("hello")identity(int)(123)```#### 2.4 类和面向对象```javascript// JavaScript 类(ES6)class Animal {    constructor(name, age) {        this.name = name;        this.age = age;    }    speak() {        return `${this.name} makes a sound`;    }    static species() {        return "Animal";    }}// 继承class Dog extends Animal {    constructor(name, age, breed) {        super(name, age);  // 调用父类构造函数        this.breed = breed;    }    speak() {        return `${this.name} barks`;    }    fetch() {        return `${this.name} fetches the ball`;    }}// 使用const dog = new Dog("Buddy", 3, "Golden Retriever");console.log(dog.speak());  // "Buddy barks"console.log(dog.fetch());  // "Buddy fetches the ball"// TypeScript - 类型系统interface AnimalInterface {    name: string;    age: number;    speak(): string;}class Cat implements AnimalInterface {    private _name: string;  // 私有属性    constructor(public name: string, public age: number) {        this._name = name;    }    speak(): string {        return `${this.name} meows`;    }    // getter/setter    get description(): string {        return `${this.name} is ${this.age} years old`;    }}``````python# Python 类class Animal:    def __init__(self, name, age):        self.name = name        self.age = age    def speak(self):        return f"{self.name} makes a sound"    @staticmethod    def species():        return "Animal"# 继承class Dog(Animal):    def __init__(self, name, age, breed):        super().__init__(name, age)  # 调用父类构造函数        self.breed = breed    def speak(self):        return f"{self.name} barks"    def fetch(self):        return f"{self.name} fetches the ball"# 使用dog = Dog("Buddy", 3, "Golden Retriever")print(dog.speak())  # "Buddy barks"print(dog.fetch())  # "Buddy fetches the ball"# 抽象基类from abc import ABC, abstractmethodclass AnimalInterface(ABC):    @abstractmethod    def speak(self) -> str:        passclass Cat(AnimalInterface):    def __init__(self, name: str, age: int):        self.name = name        self.age = age    def speak(self) -> str:        return f"{self.name} meows"    @property    def description(self) -> str:        return f"{self.name} is {self.age} years old"```#### 2.5 异步编程```javascript// JavaScript - Promisefunction fetchData() {    return new Promise((resolve, reject) => {        setTimeout(() => {            resolve({ data: "Hello" });        }, 1000);    });}// 使用 PromisefetchData()    .then(result => console.log(result))    .catch(error => console.error(error));// async/awaitasync function main() {    try {        const result = await fetchData();        console.log(result);    } catch (error) {        console.error(error);    }}// 并发请求async function fetchMultiple() {    const [result1, result2, result3] = await Promise.all([        fetchData(),        fetchData(),        fetchData()    ]);    return [result1, result2, result3];}// 顺序执行async function sequential() {    const result1 = await fetchData();    const result2 = await fetchData();    const result3 = await fetchData();    return [result1, result2, result3];}``````python# Python - asyncioimport asyncioasync def fetch_data():    await asyncio.sleep(1)    return {"data""Hello"}# 使用协程async def main():    try:        result = await fetch_data()        print(result)    except Exception as error:        print(error)# 并发请求async def fetch_multiple():    results = await asyncio.gather(        fetch_data(),        fetch_data(),        fetch_data()    )    return results# 顺序执行async def sequential():    result1 = await fetch_data()    result2 = await fetch_data()    result3 = await fetch_data()    return [result1, result2, result3]# 运行异步代码if __name__ == "__main__":    asyncio.run(main())```### 3. 类型系统对比| 特性 | JavaScript | TypeScript | Python ||------|-----------|-----------|--------|| **类型检查时间** | 运行时 | 编译时 | 运行时(类型提示可选) || **类型安全性** | 弱类型 | 强类型 | 强类型 || **类型推断** | 部分支持 | 完整支持 | 部分支持 || **错误发现** | 运行时错误 | 编译期错误 | 运行时错误 || **类型定义** | 无 | 接口、类型别名 | 类型提示(PEP 484) |```javascript// JavaScript - 类型错误运行时才发现function add(a, b) {    return a + b;}// 可能的问题add(1, 2);       // 3 ✓add("1", 2);     // "12" ✗ 类型不一致但不会立即报错add(null, 2);    // "2null" ✗ 意外行为``````typescript// TypeScript - 编译期发现错误function add(a: number, b: number): number {    return a + b;}// 编译检查add(1, 2);       // 3 ✓add("1", 2);     // 编译错误 ✗add(null, 2);    // 编译错误 ✗// 联合类型function combine(a: string | number, b: string | number): string | number {    return a + b;  // 需要进一步类型检查}``````python# Python - 运行时才发现(类型提示不影响执行)def add(a, b):    return a + b# 运行时行为add(1, 2)       # 3 ✓add("1", 2)     # "12" ✗ 不会立即报错# add(1, None)  # TypeError 运行时错误# 类型提示def add_typed(a: int, b: int) -> int:    return a + b# 类型检查工具(如 mypy)# 需要额外运行类型检查器```### 4. 生态系统对比#### 4.1 包管理器| 语言 | 包管理器 | 仓库 | 特点 ||------|---------|------|------|| JavaScript | npm, yarn, pnpm | npm registry | 生态最大 || TypeScript | npm, yarn, pnpm | npm registry | 复用 npm 生态 || Python | pip, poetry | PyPI | 稳定成熟 |```bash# JavaScript/TypeScript 安装包npm install axiosnpm install --save-dev @types/axios  # TypeScript 类型定义# Python 安装包pip install requests```#### 4.2 测试框架```javascript// JavaScript/TypeScript - Jestdescribe('Calculator', () => {    test('adds two numbers', () => {        expect(add(1, 2)).toBe(3);    });    test('throws error for invalid input', () => {        expect(() => add('a', 'b')).toThrow();    });});// 运行测试npm test``````python# Python - pytestdef test_add():    assert add(1, 2) == 3def test_add_invalid():    with pytest.raises(TypeError):        add("a""b")# 运行测试pytest```#### 4.3 热门库对比| 用途 | JavaScript/TypeScript | Python ||------|----------------------|--------|| **HTTP 请求** | axios, fetch | requests, httpx || **Web 框架** | Express, Nest.js | Flask, Django, FastAPI || **数据处理** | Lodash, D3.js | Pandas, NumPy || **数据库** | Sequelize, Prisma | SQLAlchemy, Django ORM || **自动化测试** | Playwright, Cypress | Selenium, pytest-playwright || **机器学习** | TensorFlow.js | TensorFlow, PyTorch, scikit-learn || **爬虫** | Puppeteer, Cheerio | Scrapy, BeautifulSoup |### 5. 性能对比#### 5.1 V8 引擎性能特点JavaScript (V8) 的性能优势:- **JIT 编译**:将热点代码编译为机器码- **优化编译**:对频繁执行的代码进行优化- **内联缓存**:加速属性访问```javascript// V8 会优化这段代码function sumArray(arr) {    let sum = 0;    for (let i = 0; i < arr.length; i++) {        sum += arr[i];    }    return sum;}// 首次调用:解释执行// 多次调用后:编译为机器码```#### 5.2 Python 性能特点Python 的性能特点:- **解释执行**:每次解释一行代码- **C 扩展**:关键操作用 C 实现(如 NumPy)- **全局解释器锁 (GIL)**:限制了多线程性能```python# Python 使用 C 扩展加速import numpy as np# 这些操作在 C 层执行,速度很快arr = np.array([1, 2, 3, 4, 5])sum_result = np.sum(arr)```#### 5.3 性能对比示例| 场景 | JavaScript | Python | 说明 ||------|-----------|--------|------|| **字符串操作** | 快 | 较快 | 两者接近 || **数值计算** | 中等 | 慢(标准库) | NumPy 很快 || **数组操作** | 快 | 慢(列表) | NumPy 很快 || **JSON 解析** | 快 | 较快 | 原生 JSON 支持 || **HTTP 请求** | 快 | 较慢 | Node.js 事件循环 || **DOM 操作** | 快 | 不适用 | 浏览器环境 |### 6. 并发模型对比#### 6.1 JavaScript 事件循环```javascriptconsole.log('1');setTimeout(() => {    console.log('2');}, 0);console.log('3');// 输出顺序:132// 原因:setTimeout 回调放入任务队列,事件循环执行``````JavaScript 事件循环模型:┌─────────────────────────────────────────┐│          调用栈 (Call Stack)             ││  ┌─────────────────────────────────┐    ││  │ console.log('1')                 │    ││  │ console.log('3')                 │    ││  └─────────────────────────────────┘    │└─────────────────────────────────────────┘                    ↓┌─────────────────────────────────────────┐│          任务队列 (Task Queue)           ││  ┌─────────────────────────────────┐    ││  │ setTimeout(callback)             │    ││  └─────────────────────────────────┘    │└─────────────────────────────────────────┘```#### 6.2 Python 多线程/多进程```pythonimport threadingimport multiprocessing# 多线程(受 GIL 限制)def worker():    print("Thread working")threads = []for i in range(5):    t = threading.Thread(target=worker)    t.start()    threads.append(t)for t in threads:    t.join()# 多进程(绕过 GIL)def process_worker():    print("Process working")processes = []for i in range(5):    p = multiprocessing.Process(target=process_worker)    p.start()    processes.append(p)for p in processes:    p.join()# 异步 (asyncio)import asyncioasync def async_worker():    print("Async working")async def main():    tasks = [async_worker() for _ in range(5)]    await asyncio.gather(*tasks)asyncio.run(main())```---## 前端自动化开发:两种语言的选择### 1. Python 方案(你当前使用)#### 1.1 优势| 优势 | 说明 ||------|------|| **学习曲线平缓** | 语法简单,易于上手 || **测试库丰富** | Selenium, pytest, Robot Framework || **数据处理强** | Pandas, NumPy 方便处理测试数据 || **生态成熟** | 大量第三方库和工具 || **脚本语言** | 快速编写,无需编译 || **调试友好** | 丰富的调试工具 |#### 1.2 主流框架```python# 1. Selenium + pytestfrom selenium import webdriverimport pytestdef test_login():    driver = webdriver.Chrome()    driver.get("https://example.com/login")    driver.find_element("id""username").send_keys("admin")    driver.find_element("id""password").send_keys("password")    driver.find_element("id""login-btn").click()    assert driver.title == "Dashboard"    driver.quit()# 2. Playwright for Pythonfrom playwright.sync_api import sync_playwrightdef test_login():    with sync_playwright() as p:        browser = p.chromium.launch(headless=False)        page = browser.new_page()        page.goto("https://example.com/login")        page.fill("#username""admin")        page.fill("#password""password")        page.click("#login-btn")        assert page.title() == "Dashboard"        browser.close()# 3. Robot Framework (关键字驱动)*** Settings ***Library           SeleniumLibrary*** Test Cases ***Login Test    Open Browser    https://example.com/login    Chrome    Input Text      id=username                  admin    Input Text      id=password                  password    Click Button    id=login-btn    Title Should Be    Dashboard    Close Browser```#### 1.3 实际项目结构```astro-gui-auto/├── pages/                    # 页面对象│   ├── basepage.py          # 基础页面│   ├── login_page.py        # 登录页│   └── dashboard_page.py    # 仪表板页├── tests/                    # 测试用例│   ├── test_login.py        # 登录测试│   └── test_dashboard.py    # 仪表板测试├── data/                     # 测试数据│   └── test_data.xlsx        # Excel 数据文件├── config/                   # 配置文件│   └── config.ini            # 配置├── utils/                    # 工具类│   └── excel_utils.py       # Excel 工具├── conftest.py              # pytest 配置└── requirements.txt         # 依赖列表```### 2. JavaScript/TypeScript 方案#### 2.1 优势| 优势 | 说明 ||------|------|| **与前端技术栈一致** | 与 React, Vue 等框架无缝集成 || **浏览器原生支持** | 直接操作 DOM,性能更好 || **调试方便** | 浏览器 DevTools 直接调试 || **异步处理** | Promise, async/await 更自然 || **类型安全** | TypeScript 提供编译时检查 || **包管理** | npm 生态庞大 || **工具链成熟** | ESLint, Prettier, Webpack |#### 2.2 主流框架```typescript// 1. Playwright (TypeScript 原生)import { test, expect } from '@playwright/test';test.describe('Login Tests', () => {    test('should login successfully', async ({ page }) => {        await page.goto('https://example.com/login');        await page.fill('#username''admin');        await page.fill('#password''password');        await page.click('#login-btn');        await expect(page).toHaveTitle('Dashboard');    });});// 2. Cypress (端到端测试)describe('Login Tests', () => {    it('should login successfully', () => {        cy.visit('/login');        cy.get('#username').type('admin');        cy.get('#password').type('password');        cy.get('#login-btn').click();        cy.title().should('eq''Dashboard');    });});// 3. Puppeteerconst puppeteer = require('puppeteer');const { expect } = require('chai');describe('Login Tests', () => {    let browser, page;    before(async () => {        browser = await puppeteer.launch({ headless: false });        page = await browser.newPage();    });    after(async () => {        await browser.close();    });    it('should login successfully', async () => {        await page.goto('https://example.com/login');        await page.type('#username''admin');        await page.type('#password''password');        await page.click('#login-btn');        const title = await page.title();        expect(title).to.equal('Dashboard');    });});```#### 2.3 实际项目结构```e2e-tests/├── src/│   ├── page-objects/           # 页面对象│   │   ├── base-page.ts│   │   ├── login-page.ts│   │   └── dashboard-page.ts│   ├── tests/                  # 测试用例│   │   ├── login.spec.ts│   │   └── dashboard.spec.ts│   ├── data/                   # 测试数据│   │   └── test-data.ts│   └── utils/                  # 工具类│       └── helpers.ts├── playwright.config.ts        # Playwright 配置├── package.json                # 依赖和脚本├── tsconfig.json              # TypeScript 配置└── .eslintrc.json             # ESLint 配置```### 3. 对比总结| 维度 | Python 方案 | JavaScript/TypeScript 方案 ||------|------------|--------------------------|| **学习成本** | 低 | 中等 || **调试体验** | 良好 | 优秀(浏览器 DevTools) || **与前端协作** | 需要沟通 | 无缝对接 || **代码可读性** | 高 | 高 || **类型安全** | 需要额外工具 | TypeScript 原生支持 || **性能** | 良好 | 更好(直接操作浏览器) || **生态丰富度** | 高 | 最高 || **社区活跃度** | 高 | 极高 |---## 实际代码对比### 1. 登录功能对比#### Python 实现```python# pages/basepage.pyfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.common.by import Byclass BasePage:    def __init__(self, driver):        self.driver = driver        self.wait = WebDriverWait(driver, 10)    def find_element(self, locator):        """查找元素"""        return self.wait.until(            EC.presence_of_element_located(locator)        )    def click(self, locator):        """点击元素"""        element = self.find_element(locator)        element.click()    def input_text(self, locator, text):        """输入文本"""        element = self.find_element(locator)        element.clear()        element.send_keys(text)    def wait_for_text(self, locator, text):        """等待文本出现"""        self.wait.until(            EC.text_to_be_present_in_element(locator, text)        )# pages/login_page.pyfrom pages.basepage import BasePagefrom selenium.webdriver.common.by import Byclass LoginPage(BasePage):    # 定位器    USERNAME_INPUT = (By.ID, "username")    PASSWORD_INPUT = (By.ID, "password")    LOGIN_BUTTON = (By.ID, "login-btn")    def login(self, username, password):        """登录操作"""        self.input_text(self.USERNAME_INPUT, username)        self.input_text(self.PASSWORD_INPUT, password)        self.click(self.LOGIN_BUTTON)# tests/test_login.pyimport pytestfrom selenium import webdriverfrom pages.login_page import LoginPage@pytest.fixturedef driver():    """初始化浏览器"""    driver = webdriver.Chrome()    yield driver    driver.quit()def test_login_success(driver):    """测试成功登录"""    driver.get("https://example.com/login")    login_page = LoginPage(driver)    login_page.login("admin""password123")    # 验证登录成功    assert "Dashboard" in driver.title```#### TypeScript 实现```typescript// page-objects/base-page.tsimport { Page, Locator } from '@playwright/test';export class BasePage {    constructor(protected page: Page) {}    async click(selector: string): Promise<void> {        await this.page.click(selector);    }    async fill(selector: string, value: string): Promise<void> {        await this.page.fill(selector, value);    }    async waitForSelector(selector: string): Promise<Locator> {        return await this.page.waitForSelector(selector);    }    async getText(selector: string): Promise<string> {        return await this.page.textContent(selector) || '';    }    async waitURLContains(text: string): Promise<void> {        await this.page.waitForURL(`**/*${text}*`);    }}// page-objects/login-page.tsimport { BasePage } from './base-page';export class LoginPage extends BasePage {    private readonly usernameInput = '#username';    private readonly passwordInput = '#password';    private readonly loginButton = '#login-btn';    async login(username: string, password: string): Promise<void> {        await this.fill(this.usernameInput, username);        await this.fill(this.passwordInput, password);        await this.click(this.loginButton);    }}// tests/login.spec.tsimport { test, expect } from '@playwright/test';import { LoginPage } from '../page-objects/login-page';test.describe('Login Tests', () => {    test('should login successfully', async ({ page }) => {        const loginPage = new LoginPage(page);        await page.goto('https://example.com/login');        await loginPage.login('admin''password123');        // 验证登录成功        await expect(page).toHaveTitle(/Dashboard/);    });    test('should show error for invalid credentials', async ({ page }) => {        const loginPage = new LoginPage(page);        await page.goto('https://example.com/login');        await loginPage.login('wrong''wrong');        // 验证错误消息        const errorMessage = await page.textContent('.error-message');        expect(errorMessage).toContain('用户名或密码错误');    });});```### 2. 数据驱动测试对比#### Python 实现```python# tests/test_data_driven.pyimport pytestimport openpyxlfrom selenium import webdriverfrom pages.login_page import LoginPagedef read_test_data():    """从 Excel 读取测试数据"""    workbook = openpyxl.load_workbook('data/test_cases.xlsx')    sheet = workbook.active    test_data = []    for row in sheet.iter_rows(min_row=2, values_only=True):        test_data.append({            'username': row[0],            'password': row[1],            'expected': row[2],            'description': row[3]        })    return test_data@pytest.fixturedef driver():    driver = webdriver.Chrome()    yield driver    driver.quit()@pytest.mark.parametrize("data", read_test_data())def test_login_data_driven(driver, data):    """数据驱动登录测试"""    driver.get("https://example.com/login")    login_page = LoginPage(driver)    login_page.login(data['username'], data['password'])    if data['expected'] == 'success':        assert "Dashboard" in driver.title    else:        error_msg = driver.find_element(By.CLASS_NAME, "error").text        assert "错误" in error_msg```#### TypeScript 实现```typescript// data/test-data.tsexport interface LoginTestCase {    username: string;    password: string;    expected: 'success' | 'error';    description: string;}export const loginTestData: LoginTestCase[] = [    {        username: 'admin',        password: 'password123',        expected: 'success',        description: '正确用户名密码'    },    {        username: 'wrong',        password: 'wrong',        expected: 'error',        description: '错误用户名密码'    },    {        username: '',        password: '',        expected: 'error',        description: '空用户名密码'    }];// tests/data-driven-login.spec.tsimport { test, expect } from '@playwright/test';import { LoginPage } from '../page-objects/login-page';import { loginTestData } from '../data/test-data';test.describe('Data Driven Login Tests', () => {    for (const data of loginTestData) {        test(data.description, async ({ page }) => {            const loginPage = new LoginPage(page);            await page.goto('https://example.com/login');            await loginPage.login(data.username, data.password);            if (data.expected === 'success') {                await expect(page).toHaveTitle(/Dashboard/);            } else {                const errorMessage = await page.textContent('.error-message');                await expect(errorMessage).toContain('错误');            }        });    }});```---## 环境搭建与第一个程序### 1. JavaScript 环境搭建#### 1.1 安装 Node.jsNode.js 是 JavaScript 的运行时环境,包含 npm(Node 包管理器)。**步骤:**1. **下载 Node.js**   - 访问:https://nodejs.org/   - 选择 LTS 版本(长期支持版本,更稳定)   - 根据系统下载对应安装包(Windows/Mac/Linux)2. **安装 Node.js**   - Windows:运行安装程序,一路点击"下一步"   - Mac:运行 .pkg 安装包   - Linux:使用包管理器安装3. **验证安装**   ```bash   # 检查 Node.js 版本   node -v   # 检查 npm 版本   npm -v   ```   预期输出:   ```   v20.10.0   10.2.3   ```#### 1.2 安装代码编辑器 VS CodeVS Code 是最流行的 JavaScript/TypeScript 开发工具。**步骤:**1. 下载 VS Code:https://code.visualstudio.com/2. 安装并启动3. 安装推荐插件:   - ESLint(代码检查)   - Prettier(代码格式化)   - JavaScript (ES6) code snippets(代码片段)   - Live Server(实时预览 HTML)#### 1.3 创建第一个 JavaScript 程序**方法一:浏览器中运行(最简单)**1. 创建 HTML 文件 `index.html`:   ```html   <!DOCTYPE html>   <html lang="zh-CN">   <head>       <meta charset="UTF-8">       <meta name="viewport" content="width=device-width, initial-scale=1.0">       <title>第一个 JavaScript 程序</title>   </head>   <body>       <h1>我的第一个 JavaScript 程序</h1>       <button id="myButton">点击我</button>       <script>           // JavaScript 代码           console.log('Hello, JavaScript!');           const button = document.getElementById('myButton');           button.addEventListener('click'function() {               alert('你点击了按钮!');               console.log('按钮被点击了');           });       </script>   </body>   </html>   ```2. 双击打开 HTML 文件,在浏览器中运行3. 按 F12 打开开发者工具,查看 Console 控制台**方法二:Node.js 命令行运行**1. 创建 JavaScript 文件 `hello.js`:   ```javascript   // hello.js   console.log('Hello, JavaScript!');   // 变量   let name = '张三';   const age = 25;   console.log('姓名:', name);   console.log('年龄:', age);   // 函数   function greet(name) {       return `你好, ${name}!`;   }   console.log(greet('世界'));   // 数组   const fruits = ['苹果''香蕉''橙子'];   fruits.forEach(fruit => console.log(fruit));   // 对象   const person = {       name: '李四',       age: 30,       sayHello: function() {           console.log(`我是 ${this.name}`);       }   };   person.sayHello();   ```2. 在命令行中运行:   ```bash   node hello.js   ```**方法三:使用 VS Code 运行**1. 在 VS Code 中打开 `hello.js`2. 安装插件 `Code Runner`3. 点击右上角的运行按钮,或按 `Ctrl + Alt + N`#### 1.4 使用 npm 管理项目**初始化项目:**```bash# 创建项目目录mkdir my-js-projectcd my-js-project# 初始化项目(会生成 package.json)npm init -y# 安装依赖npm install axios# 运行脚本npm start```**创建项目结构:**```my-js-project/├── node_modules/      # 依赖包├── src/│   ├── index.js      # 主文件│   └── utils.js      # 工具函数├── package.json     # 项目配置└── README.md```**package.json 示例:**```json{  "name""my-js-project",  "version""1.0.0",  "description""我的第一个 JavaScript 项目",  "main""src/index.js",  "scripts": {    "start""node src/index.js",    "test""echo \"Error: no test specified\" && exit 1"  },  "dependencies": {    "axios""^1.6.0"  }}```---### 2. TypeScript 环境搭建#### 2.1 全局安装 TypeScript**步骤:**1. 确保 Node.js 已安装2. 全局安装 TypeScript 编译器:   ```bash   npm install -g typescript   ```3. 验证安装:   ```bash   tsc -v   ```   预期输出:   ```   Version 5.3.0   ```#### 2.2 安装 VS Code TypeScript 插件VS Code 原生支持 TypeScript,但可以安装额外插件增强体验:- TypeScript Vue Plugin (如果使用 Vue)- TSLint(已被 ESLint 替代,不推荐)- Prettier - Code formatter#### 2.3 创建第一个 TypeScript 程序**方法一:直接编译运行**1. 创建 TypeScript 文件 `hello.ts`:   ```typescript   // hello.ts   // 基本类型   let name: string = '张三';   const age: number = 25;   const isStudent: boolean = true;   console.log('姓名:', name);   console.log('年龄:', age);   console.log('是否为学生:', isStudent);   // 函数   function greet(name: string): string {       return `你好, ${name}!`;   }   console.log(greet('世界'));   // 数组   const fruits: string[] = ['苹果''香蕉''橙子'];   fruits.forEach(fruit => console.log(fruit));   // 对象   interface Person {       name: string;       age: number;       sayHello(): void;   }   const person: Person = {       name: '李四',       age: 30,       sayHello: function() {           console.log(`我是 ${this.name}`);       }   };   person.sayHello();   // 箭头函数   const add = (a: number, b: number): number => {       return a + b;   };   console.log('1 + 2 =', add(1, 2));   // 异步函数   async function fetchData(): Promise<void> {       console.log('开始获取数据...');       // 模拟异步操作       await new Promise(resolve => setTimeout(resolve, 1000));       console.log('数据获取完成!');   }   fetchData();   ```2. 编译 TypeScript 文件:   ```bash   tsc hello.ts   ```   这会生成 `hello.js` 文件3. 运行编译后的文件:   ```bash   node hello.js   ```**方法二:使用 ts-node 直接运行**1. 安装 ts-node(推荐):   ```bash   npm install -g ts-node   ```2. 直接运行 TypeScript 文件:   ```bash   ts-node hello.ts   ```**方法三:配置自动编译**1. 创建项目目录并初始化:   ```bash   mkdir my-ts-project   cd my-ts-project   npm init -y   ```2. 安装 TypeScript 和类型定义:   ```bash   npm install --save-dev typescript @types/node   ```3. 创建 `tsconfig.json`:   ```json   {     "compilerOptions": {       "target""ES2020",       "module""commonjs",       "lib": ["ES2020"],       "outDir""./dist",       "rootDir""./src",       "strict"true,       "esModuleInterop"true,       "skipLibCheck"true,       "forceConsistentCasingInFileNames"true     },     "include": ["src/**/*"],     "exclude": ["node_modules"]   }   ```4. 创建项目结构:   ```   my-ts-project/   ├── src/   │   ├── index.ts   │   └── utils.ts   ├── dist/           # 编译输出目录   ├── package.json   └── tsconfig.json   ```5. 在 `package.json` 中添加脚本:   ```json   {     "scripts": {       "build""tsc",       "start""node dist/index.js",       "dev""ts-node src/index.ts"     }   }   ```6. 运行脚本:   ```bash   npm run build    # 编译   npm start        # 运行编译后的代码   npm run dev      # 直接运行 TypeScript   ```#### 2.4 TypeScript 类型示例```typescript// types-demo.ts// 1. 基本类型let num: number = 42;let str: string = 'Hello';let bool: boolean = true;let nothing: null = null;let notDefined: undefined = undefined;// 2. 数组类型let numbers: number[] = [1, 2, 3];let strings: Array<string> = ['a''b''c'];let anyArray: any[] = [1, 'hello'true];// 3. 元组类型(固定长度和类型)let person: [string, number] = ['Tom', 18];// 4. 枚举类型enum Color {    Red,    Green,    Blue}let myColor: Color = Color.Red;// 5. 接口interface User {    id: number;    name: string;    age?: number;      // 可选属性    readonly email: string;  // 只读属性}const user: User = {    id: 1,    name: 'Tom',    email: 'tom@example.com'};// 6. 类class Animal {    protected name: string;    constructor(name: string) {        this.name = name;    }    speak(): void {        console.log(`${this.name} makes a sound`);    }}class Dog extends Animal {    private breed: string;    constructor(name: string, breed: string) {        super(name);        this.breed = breed;    }    bark(): void {        console.log(`${this.name} barks!`);    }}const dog = new Dog('Buddy''Golden Retriever');dog.bark();// 7. 泛型function identity<T>(arg: T): T {    return arg;}identity<string>('hello');identity<number>(123);// 8. 联合类型let value: string | number = 'hello';value = 123;  // OK// 9. 类型断言let someValue: any = 'this is a string';let strLength: number = (someValue as string).length;// 10. 类型守卫function isString(value: any): value is string {    return typeof value === 'string';}if (isString(value)) {    console.log(value.toUpperCase());  // TypeScript 知道这里是 string}```---### 3. 前端自动化测试环境搭建#### 3.1 Playwright 环境搭建(TypeScript)**步骤:**1. 创建项目:   ```bash   mkdir playwright-test   cd playwright-test   npm init -y   ```2. 安装 Playwright:   ```bash   npm init playwright@latest   ```   这会自动:   - 安装 Playwright   - 生成配置文件   - 创建示例测试   - 安装浏览器3. 项目结构:   ```   playwright-test/   ├── tests/   │   ├── example.spec.ts   │   └── login.spec.ts   ├── tests-examples/   ├── playwright.config.ts   ├── package.json   └── package-lock.json   ```4. 编写第一个测试:   ```typescript   // tests/hello.spec.ts   import { test, expect } from '@playwright/test';   test('基本页面测试', async ({ page }) => {       // 访问页面       await page.goto('https://example.com');       // 验证标题       await expect(page).toHaveTitle(/Example Domain/);       // 获取元素文本       const h1 = await page.textContent('h1');       console.log('页面标题:', h1);   });   ```5. 运行测试:   ```bash   # 运行所有测试   npx playwright test   # 运行特定测试   npx playwright test hello.spec.ts   # 显示浏览器   npx playwright test --ui   # 调试模式   npx playwright test --debug   ```#### 3.2 Playwright 配置**playwright.config.ts:**```typescriptimport { defineConfig, devices } from '@playwright/test';export default defineConfig({    testDir: './tests',    fullyParallel: true,    forbidOnly: !!process.env.CI,    retries: process.env.CI ? 2 : 0,    workers: process.env.CI ? 1 : undefined,    reporter: [        ['html'],  // 生成 HTML 报告        ['json', { outputFile: 'test-results/results.json' }]    ],    use: {        baseURL: 'https://example.com',        trace: 'on-first-retry',        screenshot: 'only-on-failure',        video: 'retain-on-failure',    },    projects: [        {            name: 'chromium',            use: { ...devices['Desktop Chrome'] },        },        {            name: 'firefox',            use: { ...devices['Desktop Firefox'] },        },        {            name: 'webkit',            use: { ...devices['Desktop Safari'] },        },    ],});```#### 3.3 Playwright 常用操作```typescript// tests/playwright-demo.spec.tsimport { test, expect } from '@playwright/test';test.describe('Playwright 操作示例', () => {    test('基本操作', async ({ page }) => {        // 导航        await page.goto('https://example.com');        // 等待元素        await page.waitForSelector('h1');        // 输入文本        await page.fill('#input-id''Hello World');        // 点击元素        await page.click('#button-id');        // 获取文本        const text = await page.textContent('.element');        console.log(text);        // 获取属性        const href = await page.getAttribute('a''href');        console.log(href);        // 截图        await page.screenshot({ path: 'screenshot.png' });        // 验证        await expect(page.locator('h1')).toBeVisible();        await expect(page).toHaveURL(/example/);    });    test('表单操作', async ({ page }) => {        await page.goto('https://example.com/form');        // 填写表单        await page.fill('#name''张三');        await page.fill('#email''zhangsan@example.com');        await page.selectOption('#country''CN');        // 复选框        await page.check('#agree');        // 单选框        await page.check('#gender-male');        // 下拉框        await page.selectOption('#hobby', ['reading''sports']);        // 提交        await page.click('#submit');        // 等待导航        await page.waitForURL('**/success');    });    test('异步操作', async ({ page }) => {        // 等待 API 请求        await page.goto('https://example.com');        const [response] = await Promise.all([            page.waitForResponse('**/api/data'),            page.click('#load-data')        ]);        const data = await response.json();        console.log('API 响应:', data);    });    test('处理弹窗', async ({ page }) => {        // 监听弹窗        page.on('dialog', async dialog => {            console.log('弹窗消息:', dialog.message());            await dialog.accept();  // 或 dialog.dismiss()        });        await page.goto('https://example.com');        await page.click('#show-alert');    });});```---### 4. 调试技巧#### 4.1 浏览器调试1. **打开开发者工具**   - Chrome/Edge:`F12` 或 `Ctrl + Shift + I`   - Firefox:`F12` 或 `Ctrl + Shift + I`2. **Console 控制台**   ```javascript   // 输出调试信息   console.log('变量值:', variable);   console.warn('警告信息');   console.error('错误信息');   console.table([{name: 'Tom', age: 18}, {name: 'Jerry', age: 20}]);   // 断点   debugger;  // 程序会在这里暂停   ```3. **Sources 面板**   - 设置断点:点击行号   - 单步执行:F10(单步跳过),F11(单步进入)   - 查看变量:Watch 面板#### 4.2 VS Code 调试1. **配置 launch.json**   ```json   {     "version""0.2.0",     "configurations": [       {         "type""node",         "request""launch",         "name""启动程序",         "skipFiles": ["<node_internals>/**"],         "program""${workspaceFolder}/src/index.js"       },       {         "type""node",         "request""launch",         "name""运行 TypeScript",         "runtimeArgs": ["-r""ts-node/register"],         "args": ["${workspaceFolder}/src/index.ts"]       }     ]   }   ```2. **设置断点**   - 点击行号设置断点   - 按 `F5` 启动调试#### 4.3 Playwright 调试```bash# UI 模式(推荐)npx playwright test --ui# 调试模式npx playwright test --debug# 显示浏览器npx playwright test --headed```**代码中添加断点:**```typescripttest('调试示例', async ({ page }) => {    await page.goto('https://example.com');    // 暂停执行    await page.pause();    // 设置断点    await page.pause();});```---### 5. 常见问题#### 5.1 Node.js 安装问题**问题:** `node` 命令找不到**解决:**1. 检查环境变量 PATH 是否包含 Node.js 安装路径2. Windows:重新打开命令行窗口3. Mac/Linux:运行 `source ~/.bash_profile` 或 `source ~/.zshrc`#### 5.2 npm 安装包慢**解决:使用国内镜像**```bash# 淘宝镜像(推荐)npm config set registry https://registry.npmmirror.com# 恢复官方源npm config set registry https://registry.npmjs.org```#### 5.3 TypeScript 编译错误**问题:** 找不到类型定义**解决:**```bash# 安装 @types 包npm install --save-dev @types/node @types/jest @types/react```#### 5.4 Playwright 浏览器下载失败**解决:**```bash# 手动下载浏览器npx playwright install chromiumnpx playwright install firefoxnpx playwright install webkit# 或者设置代理PLAYWRIGHT_DOWNLOAD_HOST=https://npmmirror.com/mirrors/playwright/ npx playwright install```---### 6. 推荐开发工具| 工具 | 用途 | 下载地址 ||------|------|---------|| **VS Code** | 代码编辑器 | https://code.visualstudio.com/ || **Chrome DevTools** | 浏览器调试 | 内置 || **Postman** | API 测试 | https://www.postman.com/ || **Git** | 版本控制 | https://git-scm.com/ |**VS Code 推荐插件:**| 插件名称 | 用途 ||---------|------|| ESLint | JavaScript/TypeScript 代码检查 || Prettier | 代码格式化 || Auto Rename Tag | 自动重命名 HTML 标签 || Path Intellisense | 路径智能提示 || GitLens | Git 增强 || Live Server | HTML 实时预览 || Thunder Client | API 测试(轻量级) |---## 学习建议### 1. 如果你已经掌握 Python**建议学习路径:**```第1周:JavaScript 基础├── 变量和数据类型├── 函数和作用域├── 对象和数组└── 异步编程基础第2周:TypeScript 基础├── 类型系统├── 接口和类├── 泛型└── 类型实用工具第3周:Playwright 实战├── Playwright 基础├── 页面对象模式└── 数据驱动测试第4周:进阶├── CI/CD 集成├── 测试报告└── 性能测试```**学习重点:**1. **理解 JavaScript 运行机制**   - 事件循环   - 异步编程   - Promise 和 async/await2. **掌握 TypeScript 类型系统**   - 基本类型和类型推断   - 接口定义和使用   - 泛型应用3. **实践自动化测试**   - Playwright 框架   - 页面对象模式   - 测试最佳实践### 2. 何时选择 JavaScript/TypeScript**推荐使用的场景:**| 场景 | 推荐语言 | 理由 ||------|---------|------|| **纯前端自动化测试** | JavaScript/TypeScript | 原生支持,调试方便 || **与前端团队协作** | JavaScript/TypeScript | 技术栈一致,沟通顺畅 || **需要调试前端代码** | JavaScript/TypeScript | 浏览器 DevTools 直接调试 || **SPA 应用测试** | JavaScript/TypeScript | 更好的异步处理 || **已有 JavaScript 基础** | JavaScript/TypeScript | 学习成本较低 || 场景 | 推荐语言 | 理由 ||------|---------|------|| **已有 Python 基础** | Python | 学习成本低 || **团队使用 Python** | Python | 保持技术栈统一 || **需要处理大量数据** | Python | Pandas, NumPy 强大 || **后端是 Python** | Python | 技术栈一致 || **需要快速开发** | Python | 开发效率高 |### 3. 技能互补**最佳实践:两种语言都掌握**```Python                JavaScript/TypeScript  ↓                           ↓数据处理                    前端交互后端开发                    浏览器操作测试框架                    前端调试脚本工具                    现代前端技术  ↓                           ↓         全栈自动化工程师```---## 总结### 核心要点1. **JavaScript/TypeScript 的独特价值**   - 唯一的浏览器原生编程语言   - 直接操作 DOM 和浏览器 API   - TypeScript 提供类型安全和更好的开发体验2. **Python 的独特价值**   - 简洁易学的语法   - 丰富的数据处理和科学计算库   - 成熟的测试和自动化框架3. **选择建议**   - 当前项目使用 Python 是合理选择   - 学习 JavaScript/TypeScript 可拓展技能栈   - 根据团队和项目需求选择### 学习资源**JavaScript/TypeScript:**- [MDN JavaScript 文档](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript)- [TypeScript 官方文档](https://www.typescriptlang.org/zh/docs/)- [JavaScript.info](https://javascript.info/)- [Playwright 官方文档](https://playwright.dev/)**Python:**- [Python 官方文档](https://docs.python.org/zh-cn/3/)- [Selenium 官方文档](https://www.selenium.dev/documentation/)- [Playwright Python 文档](https://playwright.dev/python/)
2.基础入门教程:
# JavaScript 和 TypeScript 基础教程## 前端自动化开发入门指南---## 目录1. [JavaScript 基础](#javascript-基础)2. [TypeScript 基础](#typescript-基础)3. [前端自动化开发实战](#前端自动化开发实战)4. [常用工具和框架](#常用工具和框架)5. [学习路线建议](#学习路线建议)---## JavaScript 基础### 1. JavaScript 简介JavaScript 是一种轻量级的编程语言,最初用于网页开发,现在广泛应用于:- 网页前端开发- 后端开发 (Node.js)- 移动应用开发- 自动化测试### 2. 基本语法#### 2.1 变量声明```javascript// ES6+ 推荐方式let name = "张三";        // 可变变量const PI 3.14159;        // 常量// 旧方式(不推荐)var age = 25;             // 函数作用域,容易出错```**重要说明:**- `let`: 块级作用域,值可变- `const`: 块级作用域,值不可变- `var`: 函数作用域,容易产生变量提升问题#### 2.2 数据类型```javascript// 原始类型let str = "Hello";              // 字符串let num = 42;                   // 数字let bool = true;                // 布尔值let nothing = null;             // nulllet notDefined = undefined;     // undefined// 引用类型let arr = [123];            // 数组let obj = { name: "Tom", age: 18 };  // 对象let func = function() return 1; }; // 函数// ES6 新增let sym = Symbol("id");         // Symbollet bigNum = 9007199254740991n; // BigInt```#### 2.3 运算符```javascript// 算术运算符let a = 10, b = 3;console.log(a + b);   // 13  加法console.log(a - b);   // 7   减法console.log(a * b);   // 30  乘法console.log(a / b);   // 3.33 除法console.log(a % b);   // 1   取余console.log(a ** b);  // 1000 幂运算// 比较运算符console.log(5 == "5");    // true  值相等(类型转换)console.log(5 === "5");   // false 值和类型都相等console.log(5 != "5");    // false 值不相等console.log(5 !== "5");   // true  值或类型不相等// 逻辑运算符console.log(true && false);  // false  与console.log(true || false);  // true   或console.log(!true);          // false  非```### 3. 控制流#### 3.1 条件语句```javascriptlet score = 85;// if-elseif(score >= 90) {    console.log("优秀");else if (score >= 60) {    console.log("及格");else {    console.log("不及格");}// 三元运算符(简写)let result = score >= 60 ? "及格" : "不及格";// switchlet day = 3;switch(day) {    case 1:        console.log("星期一");        break;    case 2:        console.log("星期二");        break;    case 3:        console.log("星期三");        break;    default:        console.log("其他");}```#### 3.2 循环语句```javascript// for 循环for(let i = 0; i < 5; i++) {    console.log(i);}// for...of(遍历数组)let fruits = ["苹果""香蕉""橙子"];for(let fruit of fruits) {    console.log(fruit);}// for...in(遍历对象)let person = { name: "Tom", age: 18 };for(let key in person) {    console.log(key + ": " + person[key]);}// while 循环let i = 0;while(i < 5) {    console.log(i);    i++;}// do-while 循环(至少执行一次)let j = 0;do {    console.log(j);    j++;while(j < 5);```### 4. 函数#### 4.1 函数声明```javascript// 函数声明functiongreet(name{    return "Hello, " + name;}// 函数表达式const greet2 function(name{    return "Hello, " + name;};// 箭头函数(ES6)const greet3 = (name) => "Hello, " + name;// 单参数可省略括号const greet4 = name => "Hello, " + name;// 调用console.log(greet("World"));           // Hello, Worldconsole.log(greet3("JavaScript"));     // Hello, JavaScript```#### 4.2 默认参数```javascriptfunctionsayHello(name = "Guest"{    console.log("Hello, " + name);}sayHello();        // Hello, GuestsayHello("Tom");   // Hello, Tom```#### 4.3 解构参数```javascriptfunctioncreateUser({ name, age, isAdmin = false }{    return {        name,        age,        isAdmin    };}const user createUser({ name"Tom", age18 });console.log(user);  // { name: 'Tom', age: 18, isAdmin: false }```### 5. 数组操作```javascriptlet arr = [12345];// 添加元素arr.push(6);           // [1, 2, 3, 4, 5, 6] 末尾添加arr.unshift(0);         // [0, 1, 2, 3, 4, 5, 6] 开头添加// 删除元素arr.pop();             // [0, 1, 2, 3, 4, 5] 删除末尾arr.shift();            // [1, 2, 3, 4, 5] 删除开头// 查找元素console.log(arr.indexOf(3));    // 2 返回索引console.log(arr.includes(4));   // true 是否包含// 遍历arr.forEach((item, index) => {    console.log(index + ": " + item);});// 映射(返回新数组)let doubled = arr.map(x => x * 2);  // [2, 4, 6, 8, 10]// 过滤(返回新数组)let even = arr.filter(x => x % 2 === 0);  // [2, 4]// 归约(累加)let sum = arr.reduce((acc, cur) => acc + cur, 0);  // 15```### 6. 对象操作```javascript// 创建对象let person = {    name: "Tom",    age: 18,    greet() {        return `Hello, I'm ${this.name}`;    }};// 访问属性console.log(person.name);      // Tomconsole.log(person["age"]);    // 18// 添加/修改属性person.email = "tom@example.com";person.age = 19;// 删除属性delete person.email;// 解构赋值let { name, age } = person;console.log(name, age);  // Tom 19// 对象合并(ES6)let person2 = { ...person, city: "Beijing" };// 合并对象let merged = Object.assign({}, person, { score: 90 });```### 7. 字符串操作```javascriptlet str = "Hello World";// 字符串模板(ES6)let name = "Tom";let message = `Hello, ${name}!`;console.log(message);  // Hello, Tom!// 常用方法console.log(str.length);           // 11console.log(str.toUpperCase());    // HELLO WORLDconsole.log(str.toLowerCase());    // hello worldconsole.log(str.indexOf("World"));  // 6console.log(str.includes("lo"));   // trueconsole.log(str.slice(0, 5));      // Helloconsole.log(str.split(" "));       // ["Hello", "World"]// 字符串拼接let s1 = "Hello";let s2 = "World";console.log(s1 + " " + s2);  // Hello Worldconsole.log(`${s1} ${s2}`);  // Hello World```### 8. 异步编程#### 8.1 Promise```javascript// 创建 Promiselet promise = new Promise((resolve, reject) => {    setTimeout(() => {        resolve("操作成功");        // reject("操作失败");    }, 1000);});// 使用 Promisepromise    .then(result => console.log(result))    .catch(error => console.error(error))    .finally(() => console.log("完成"));// Promise 链式调用promise    .then(result => result + "!")    .then(result => result.toUpperCase())    .then(result => console.log(result))  // 操作成功!    .catch(error => console.error(error));```#### 8.2 async/await```javascript// async 函数async function fetchData() {    try {        let result = await promise;        console.log(result);    } catch (error) {        console.error(error);    }}// 多个并发请求async function fetchAll() {    let [result1, result2] = await Promise.all([        fetch("https://api1.com"),        fetch("https://api2.com")    ]);    return [result1, result2];}```### 9. DOM 操作(网页自动化核心)```javascript// 等待页面加载完成document.addEventListener('DOMContentLoaded', function() {    // 获取元素    let element = document.getElementById("myId");    let elements = document.getElementsByClassName("myClass");    let allDivs = document.querySelectorAll("div");    // 修改元素    element.textContent = "新文本";    element.innerHTML = "<span>HTML内容</span>";    element.style.color = "red";    element.classList.add("active");    // 创建元素    let newDiv = document.createElement("div");    newDiv.textContent = "新元素";    document.body.appendChild(newDiv);    // 事件监听    element.addEventListener("click", function(event) {        console.log("点击了");        event.preventDefault();    });    // 表单操作    let input = document.getElementById("myInput");    input.value = "默认值";    console.log(input.value);});// 等待元素出现function waitForElement(selector, callback) {    let element = document.querySelector(selector);    if (element) {        callback(element);    } else {        setTimeout(() => waitForElement(selector, callback), 100);    }}// 使用示例waitForElement("#myButton", function(element) {    element.click();});```---## TypeScript 基础### 1. TypeScript 简介TypeScript 是 JavaScript 的超集,添加了:- 静态类型检查- 接口- 类- 泛型- 更好的 IDE 支持TypeScript 代码会被编译为 JavaScript 代码运行。### 2. 基本类型```typescript// 基本类型注解let name: string = "张三";let age: number = 25;let isStudent: boolean = true;let nothing: null = null;let notDefined: undefined = undefined;// 数组let numbers: number[] = [1, 2, 3];let strings: Array<string> = ["a", "b", "c"];// 元组(固定长度、固定类型)let person: [string, number] = ["Tom", 18];// 枚举enum Color {    Red,    Green,    Blue}let myColor: Color = Color.Red;// any 类型(不推荐)let anything: any = "可以是任何类型";// void 类型(无返回值)function log(message: string): void {    console.log(message);}// never 类型(永不返回)function throwError(message: string): never {    throw new Error(message);}```### 3. 接口(Interface)```typescript// 定义对象形状interface User {    id: number;    name: string;    age?: number;      // 可选属性    readonly email: string;  // 只读属性}let user: User = {    id: 1,    name: "Tom",    email: "tom@example.com"};user.name = "Jerry";   // OK// user.email = "new@email.com";  // Error: 只读```#### 3.1 函数类型```typescriptinterface SearchFunc {    (source: string, subString: string): boolean;}let mySearch: SearchFunc = function(source, subString) {    return source.indexOf(subString) > -1;};```### 4. 类(Class)```typescript// 定义类class Person {    // 属性    private name: string;    protected age: number;    public email: string;    // 构造函数    constructor(name: string, age: number, email: string) {        this.name = name;        this.age = age;        this.email = email;    }    // 方法    greet(): string {        return `Hello, I'm ${this.name}`;    }    // getter/setter    get getName(): string {        return this.name;    }    set setName(newNamestring) {        this.name = newName;    }}// 继承classStudentextendsPerson{    private studentId: number;    constructor(namestring, age: number, emailstring, studentId: number) {        super(name, age, email);        this.studentId = studentId;    }    study(): string {        return `${this.getName} is studying`;    }}// 使用let student = new Student("Tom"18"tom@example.com"1001);console.log(student.greet());console.log(student.study());```### 5. 泛型```typescript// 泛型函数functionidentity<T>(arg: T): T{    return arg;}let output1 = identity<string>("Hello");let output2 = identity(123);  // 类型推断// 泛型接口interfaceBox<T{    value: T;}let numberBox: Box<number> = { value: 100 };let stringBox: Box<string> = { value: "Hello" };// 泛型类classStorage<T{    private items: T[] = [];    add(item: T): void {        this.items.push(item);    }    getAll(): T[] {        return this.items;    }}let numberStorage = new Storage<number>();numberStorage.add(1);numberStorage.add(2);```### 6. 类型推断```typescript// 自动推断let x = 3;              // numberlet y = "hello";        // string// 函数返回值推断functionadd(a: number, b: number{    return a + b;       // 返回类型自动推断为 number}// 上下文推断window.onclick = function(mouseEvent{    console.log(mouseEvent.clientX);  // mouseEvent 类型为 MouseEvent};```### 7. 类型断言```typescript// 类型断言(告诉编译器相信我)let value: any = "Hello World";let length1: number = (value as string).length;let length2: number = (<string>value).length;```### 8. 联合类型和交叉类型```typescript// 联合类型(可以是多种类型之一)let value: string | number;value = "Hello";value = 123;// 交叉类型(同时具备多个类型)interfacePerson{    name: string;}interfaceEmployee{    id: number;}type PersonEmployee = Person & Employee;let pe: PersonEmployee = {    name: "Tom",    id: 100};```### 9. 字面量类型```typescript// 字符串字面量type Direction = "up" | "down" | "left" | "right";let move: Direction = "up";// move = "diagonal";  // Error// 数字字面量type Age = 18 | 19 | 20;let myAge: Age = 18;// 布尔字面量type Config = {    debug: true;};```### 10. 实用类型```typescript// Partial - 所有属性可选interfaceUser{    id: number;    name: string;    email: string;}type PartialUser = Partial<User>;// Required - 所有属性必填type RequiredUser = Required<PartialUser>;// Readonly - 所有属性只读type ReadonlyUser = Readonly<User>;// Pick - 选择部分属性type UserBasic = Pick<User, "id" | "name">;// Omit - 排除部分属性type UserWithoutEmail = Omit<User, "email">;// Record - 构建对象类型type Pages = Record<string, { title: string; content: string }>;// 使用示例functionupdateUser(user: Partial<User>): void{    // 只需要提供要更新的字段}```---## 前端自动化开发实战### 1. Puppeteer 自动化测试#### 1.1 基础操作```javascriptconst puppeteer require('puppeteer');(async () => {    // 启动浏览器    const browser = await puppeteer.launch({        headlessfalse  // 显示浏览器窗口    });    // 创建新页面    const page = await browser.newPage();    // 访问页面    await page.goto('https://example.com');    // 截图    await page.screenshot({ path'screenshot.png' });    // 关闭浏览器    await browser.close();})();```#### 1.2 元素操作```javascriptconst puppeteer require('puppeteer');(async () => {    const browser = await puppeteer.launch({ headlessfalse });    const page = await browser.newPage();    await page.goto('https://example.com');    // 输入文本    await page.type('#input-id''Hello World');    // 点击按钮    await page.click('#button-id');    // 等待元素出现    await page.waitForSelector('#result');    // 获取元素文本    const text = await page.$eval('#result', el => el.textContent);    console.log(text);    // 获取多个元素    const items = await page.$$eval('.item', els =>        els.map(el => el.textContent)    );    console.log(items);    await browser.close();})();```#### 1.3 等待策略```javascript// 等待选择器出现await page.waitForSelector('.loading', { timeout5000 });// 等待函数返回 trueawait page.waitForFunction(() => {    return document.querySelector('.result') !== null;});// 等待导航完成await page.waitForNavigation();// 等待固定时间await page.waitForTimeout(1000);  // 等待 1 秒```### 2. Playwright 自动化测试#### 2.1 基础操作```javascriptconst { chromium } = require('playwright');(async () => {    // 启动浏览器    const browser = await chromium.launch({        headlessfalse    });    // 创建上下文和页面    const context = await browser.newContext();    const page = await context.newPage();    // 访问页面    await page.goto('https://example.com');    // 截图    await page.screenshot({ path'screenshot.png' });    await browser.close();})();```#### 2.2 元素操作```javascriptconst { chromium } = require('playwright');(async () => {    const browser = await chromium.launch({ headlessfalse });    const page = await browser.newPage();    await page.goto('https://example.com');    // 输入文本    await page.fill('#input-id''Hello World');    // 点击按钮    await page.click('#button-id');    // 等待元素    await page.waitForSelector('#result');    // 获取文本    const text = await page.textContent('#result');    console.log(text);    // 获取属性    const href = await page.getAttribute('a''href');    await browser.close();})();```### 3. Cypress 自动化测试#### 3.1 基础测试```javascriptdescribe('登录测试', () => {    it('应该成功登录', () => {        // 访问页面        cy.visit('/login');        // 输入用户名        cy.get('#username').type('admin');        // 输入密码        cy.get('#password').type('password123');        // 点击登录按钮        cy.get('#login-btn').click();        // 验证跳转到首页        cy.url().should('include''/dashboard');        // 验证欢迎消息        cy.contains('欢迎').should('be.visible');    });});```#### 3.2 常用命令```javascriptdescribe('常用命令示例', () => {    it('各种操作示例', () => {        // 等待        cy.wait(1000);        // 断言        cy.get('h1').should('have.text''标题');        cy.get('button').should('be.visible');        cy.get('input').should('have.value''test');        // 断言包含        cy.get('.message').should('contain''成功');        // 断言数量        cy.get('.item').should('have.length'3);        // 自定义断言        cy.get('.score').invoke('text').then(parseFloat).should('be.gte'60);    });});```### 4. TypeScript 自动化项目结构```typescript// page-objects/base-page.tsimport { Page, Locator } from '@playwright/test';export classBasePage{    constructor(protected page: Page) {}    async navigate(urlstring): Promise<void> {        await this.page.goto(url);    }    async waitForElement(selectorstring, timeout: number = 5000): Promise<Locator> {        return this.page.waitForSelector(selector, { timeout });    }    async click(selectorstring): Promise<void> {        await this.page.click(selector);    }    async fill(selectorstring, valuestring): Promise<void> {        await this.page.fill(selector, value);    }    async getText(selectorstring): Promise<string> {        return await this.page.textContent(selector) || '';    }}// page-objects/login-page.tsimport { BasePage } from './base-page';export classLoginPageextendsBasePage{    private readonly usernameInput = '#username';    private readonly passwordInput = '#password';    private readonly loginButton = '#login-button';    async login(usernamestring, passwordstring): Promise<void> {        await this.fill(this.usernameInput, username);        await this.fill(this.passwordInput, password);        await this.click(this.loginButton);    }}// tests/login.spec.tsimport { test, expect } from '@playwright/test';import { LoginPage } from '../page-objects/login-page';test('登录测试'async ({ page }) => {    const loginPage = new LoginPage(page);    await loginPage.navigate('https://example.com/login');    await loginPage.login('admin''password');    await expect(page).toHaveURL(/.*dashboard/);});```---## 常用工具和框架### 1. 自动化测试框架对比| 框架 | 优点 | 缺点 | 适用场景 ||------|------|------|----------|| Puppeteer | 轻量、快速、Google官方 | API相对简单 | 快速测试、截图、爬虫 || Playwright | 跨浏览器、功能强大、现代API | 体积较大 | 全面自动化测试 || Cypress | 开发体验好、实时预览、调试方便 | 只支持Chromium | 前端团队自动化测试 || Selenium | 支持所有语言、生态丰富 | 配置复杂、速度慢 | 跨语言团队 |### 2. 推荐学习路径**初学者路径:**1. JavaScript 基础(1-2周)2. Puppeteer 基础(1周)3. 简单自动化脚本(1周)**进阶路径:**1. TypeScript 基础(1-2周)2. Playwright 进阶(1-2周)3. 页面对象模式(1周)4. 测试框架整合(1周)---## 学习路线建议### 第一周:JavaScript 基础**目标:掌握 JavaScript 基本语法**- [ ] 变量和数据类型- [ ] 函数定义和调用- [ ] 数组和对象操作- [ ] 条件判断和循环- [ ] DOM 基础操作### 第二周:JavaScript 进阶**目标:理解异步编程**- [ ] Promise 基础- [ ] async/await- [ ] 事件处理- [ ] 实践:编写第一个自动化脚本### 第三周:TypeScript 基础**目标:掌握类型系统**- [ ] 基本类型注解- [ ] 接口定义- [ ] 类和继承- [ ] 泛型使用### 第四周:自动化框架入门**目标:开始实际项目**- [ ] 安装和配置 Playwright- [ ] 编写第一个测试用例- [ ] 页面对象模式实践- [ ] 数据驱动测试### 持续提升- [ ] 学习 CI/CD 集成- [ ] 学习性能测试- [ ] 学习 API 测试- [ ] 学习测试报告生成---## 实战练习项目### 项目 1:简单登录自动化```javascriptconst puppeteer require('puppeteer');(async () => {    const browser = await puppeteer.launch({ headlessfalse });    const page = await browser.newPage();    // 访问登录页面    await page.goto('https://example.com/login');    // 填写表单    await page.type('#username''testuser');    await page.type('#password''testpass');    // 点击登录    await page.click('#login-button');    // 等待跳转    await page.waitForNavigation();    // 验证登录成功    const welcomeText = await page.$eval('.welcome', el => el.textContent);    console.log('登录成功:', welcomeText);    await browser.close();})();```### 项目 2:数据抓取```javascriptconst puppeteer require('puppeteer');const fs require('fs');(async () => {    const browser = await puppeteer.launch({ headlessfalse });    const page = await browser.newPage();    await page.goto('https://example.com/articles');    // 等待内容加载    await page.waitForSelector('.article');    // 抓取所有文章标题    const articles = await page.$$eval('.article', items =>        items.map(item => ({            title: item.querySelector('.title').textContent,            author: item.querySelector('.author').textContent,            date: item.querySelector('.date').textContent        }))    );    // 保存到文件    fs.writeFileSync('articles.json', JSON.stringify(articles, null2));    console.log(`抓取完成,共 ${articles.length} 篇文章`);    await browser.close();})();```---## 资源推荐### 官方文档- [MDN JavaScript 文档](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript)- [TypeScript 官方文档](https://www.typescriptlang.org/zh/docs/)- [Playwright 官方文档](https://playwright.dev/zh-hans/)- [Puppeteer 官方文档](https://pptr.dev/)### 在线练习- [JavaScript.info](https://javascript.info/)- [TypeScript 深入浅出](https://jkchao.github.io/typescript-book-chinese/)- [Playwright by Example](https://playwright-by-example.vercel.app/)### 工具推荐- **IDE**: VS Code- **TypeScript 插件**: TypeScript Vue Plugin- **调试工具**: Chrome DevTools- **代码格式化**: Prettier- **代码检查**: ESLint---## 快速参考### JavaScript 常用命令速查```javascript// 变量声明let x = 1;const 2;// 数组arr.push(item);     // 添加arr.pop();          // 删除末尾arr.filter(x => x > 0);   // 过滤arr.map(x => x * 2);      // 映射arr.reduce((a, b) => a + b, 0);  // 累加// 对象Object.keys(obj);   // 获取所有键Object.values(obj); // 获取所有值Object.entries(obj); // 获取所有键值对// 字符串str.includes(sub);  // 是否包含str.split(',');     // 分割str.trim();         // 去除首尾空格// 异步new Promise((resolve, reject) => {});async function fn() { await promise; }```### TypeScript 类型速查```typescript// 基本类型let num: number = 1;let str: string = "hello";let boolboolean = true;// 数组let arr: number[] = [123];let arr2: Array<string> = ["a""b"];// 接口interfaceUser{    id: number;    name: string;}// 联合类型let value: string | number;// 泛型functionfn<T>(arg: T): Treturn arg; }```---
3.<script>执行方式和前端开发中的实际应用:
# Script 标签详解与前端开发实战## JavaScript/TypeScript 在前端开发中的全面指南---## 目录1. [Script 标签执行时机详解](#script-标签执行时机详解)2. [Script 标签的各种属性](#script-标签的各种属性)3. [前端开发中使用 JavaScript 的场景](#前端开发中使用-javascript-的场景)4. [前端开发中使用 TypeScript 的场景](#前端开发中使用-typescript-的场景)5. [实战示例:完整的页面开发](#实战示例完整的页面开发)6. [JavaScript vs TypeScript 选择指南](#javascript-vs-typescript-选择指南)7. [最佳实践](#最佳实践)---## Script 标签执行时机详解### 1. 浏览器渲染流程理解 script 执行时机,首先需要了解浏览器的渲染流程:```┌─────────────────────────────────────────────────────────┐│                  浏览器渲染流程                        │└─────────────────────────────────────────────────────────┘1. 解析 HTML → 构建 DOM 树    ↓2. 解析 CSS → 构建 CSSOM 树    ↓3. 合并 DOM 和 CSSOM → 构建渲染树    ↓4. 布局(Layout)→ 计算元素位置和大小    ↓5. 绘制(Paint)→ 绘制像素到屏幕    ↓6. 合成(Composite)→ 合成各层显示```**关键点:** JavaScript 会阻塞 HTML 解析和页面渲染!---### 2. Script 标签的默认行为#### 2.1 没有任何属性的 script 标签```html<!DOCTYPE html><htmllang="zh-CN"><head>    <metacharset="UTF-8">    <title>默认行为示例</title>    <!-- Script 1:立即执行,阻塞 HTML 解析 -->    <script>        console.log('Script 1: 开始执行');        // 模拟耗时操作(1秒)        const start = Date.now();        while (Date.now() - start < 1000) {            // 等待        }        console.log('Script 1: 执行完成');        // ❌ 此时 DOM 还没解析到 body,无法访问        const button = document.getElementById('myButton');        console.log('按钮元素:', button); // null    </script></head><body>    <!-- 注意:上面的 script 执行了 1 秒,页面会白屏 1 秒! -->    <h1>我的网页</h1>    <buttonid="myButton">点击我</button>    <!-- Script 2:此时 DOM 已部分解析 -->    <script>        console.log('Script 2: body 中执行');        const button = document.getElementById('myButton');        console.log('按钮元素:', button); // ✅ 找到了    </script></body></html>```**执行顺序:**```时间轴:0ms    开始解析 HTML50ms   遇到 Script 1 → 暂停 HTML 解析50ms   执行 Script 1(耗时 1 秒)       - 页面白屏,用户看不到任何内容1050ms Script 1 执行完成1050ms 继续解析 HTML1100ms 解析到 <h1>1150ms 解析到 <button>1200ms 遇到 Script 2 → 执行 Script 21250ms 继续解析剩余 HTML1300ms HTML 解析完成```**问题:**- 脚本执行期间,HTML 解析暂停- 用户会看到白屏或部分内容- 影响页面加载速度和用户体验---### 3. 使用 defer 属性#### 3.1 defer 的执行时机```html<!DOCTYPE html><htmllang="zh-CN"><head>    <metacharset="UTF-8">    <title>Defer 示例</title>    <!-- defer:延迟到 DOM 解析完成后执行 -->    <scriptdefersrc="script1.js"></script>    <scriptdefersrc="script2.js"></script>    <scriptdefer>        console.log('内联 defer 脚本');        const button = document.getElementById('myButton');        console.log('按钮元素:', button); // ✅ 可以访问    </script></head><body>    <h1>我的网页</h1>    <buttonid="myButton">点击我</button></body></html>```**defer 执行特点:**- 脚本延迟到 DOM 解析完成后执行- 在 `DOMContentLoaded` 事件之前执行- 多个 defer 脚本按在 HTML 中的顺序执行- **不阻塞 HTML 解析****执行顺序:**```html<scriptdefersrc="script1.js"></script>  <!-- 1️⃣ 按顺序执行 --><scriptdefersrc="script2.js"></script>  <!-- 2️⃣ 按顺序执行 --><scriptdefersrc="script3.js"></script>  <!-- 3️⃣ 按顺序执行 -->```**完整示例:**```html<!DOCTYPE html><htmllang="zh-CN"><head>    <metacharset="UTF-8">    <title>Defer 完整示例</title>    <script>        // 立即执行        console.log('1. 立即执行 - head');    </script>    <scriptdefer>        // defer:DOM 解析后执行        console.log('2. defer - DOM 解析后');        // 可以安全访问 DOM        document.addEventListener('DOMContentLoaded'function() {            console.log('3. DOMContentLoaded 回调');        });    </script></head><body>    <h1>我的网页</h1>    <buttonid="myButton">点击我</button>    <script>        console.log('4. 立即执行 - body');    </script>    <scriptdefer>        console.log('5. defer - 最后执行');    </script></body></html>```**输出顺序:**```1. 立即执行 - head4. 立即执行 - body2. defer - DOM 解析后5. defer - 最后执行3. DOMContentLoaded 回调```---### 4. 使用 async 属性#### 4.1 async 的执行时机```html<!DOCTYPE html><htmllang="zh-CN"><head>    <metacharset="UTF-8">    <title>Async 示例</title>    <!-- async:异步加载,加载完成后立即执行 -->    <scriptasyncsrc="analytics.js"></script>    <scriptasyncsrc="ads.js"></script></head><body>    <h1>我的网页</h1>    <!-- 页面内容继续加载,不等待脚本 --></body></html>```**async 执行特点:**- 脚本异步加载,不阻塞 HTML 解析- 脚本加载完成后立即执行- 多个 async 脚本执行顺序**不确定**(取决于加载速度)- **适用于独立脚本**:分析工具、广告、社交分享按钮**示例:不按顺序执行**```html<!DOCTYPE html><htmllang="zh-CN"><head>    <scriptasyncsrc="script1.js"></script>  <!-- 可能在第 3 个执行 -->    <scriptasyncsrc="script2.js"></script>  <!-- 可能在第 1 个执行 -->    <scriptasyncsrc="script3.js"></script>  <!-- 可能在第 2 个执行 --></head><body>    <h1>我的网页</h1></body></html>```**实际执行顺序取决于网络加载速度!**---### 5. 三种方式对比| 属性 | 执行时机 | 顺序 | 阻塞解析 | 适用场景 ||------|---------|------|----------|---------|| **无属性** | 遇到立即执行 | 按顺序 | ✅ 阻塞 | 需要立即执行且依赖后续代码 || **defer** | DOM 解析完成后 | 按顺序 | ❌ 不阻塞 | 需要操作 DOM,保持执行顺序 || **async** | 加载完成后立即 | 不确定 | ❌ 不阻塞 | 独立脚本(分析、广告) |---### 6. 实际测试示例#### 6.1 创建测试页面```html<!DOCTYPE html><htmllang="zh-CN"><head>    <metacharset="UTF-8">    <title>Script 执行时机测试</title>    <script>        console.log('=== 测试开始 ===');        console.log('1. 立即执行 - head');        console.log('   - DOM 解析进度:'document.readyState);    </script>    <scriptdefer>        console.log('2. defer - head');    </script>    <scriptasync>        console.log('3. async - head');    </script></head><body>    <h1>Script 执行时机测试</h1>    <buttonid="myButton">点击我</button>    <script>        console.log('4. 立即执行 - body');        console.log('   - DOM 解析进度:'document.readyState);    </script>    <scriptdefer>        console.log('5. defer - body');    </script>    <script>        document.addEventListener('DOMContentLoaded'function() {            console.log('6. DOMContentLoaded 事件');            console.log('   - DOM 解析进度:'document.readyState);            // 获取元素            const button = document.getElementById('myButton');            console.log('   - 按钮元素:', button);        });    </script>    <script>        window.addEventListener('load'function() {            console.log('7. load 事件(所有资源加载完成)');            console.log('   - DOM 解析进度:'document.readyState);        });    </script>    <scriptdefer>        console.log('8. defer - body 末尾');    </script></body></html>```**预期输出顺序:**```=== 测试开始 ===1. 立即执行 - head   - DOM 解析进度: loading3. async - head  (执行顺序不确定)4. 立即执行 - body   - DOM 解析进度: interactive2. defer - head5. defer - body8. defer - body 末尾6. DOMContentLoaded 事件   - DOM 解析进度: interactive7. load 事件(所有资源加载完成)   - DOM 解析进度: complete```---### 7. 事件监听器的执行时机#### 7.1 事件注册 vs 事件触发```html<!DOCTYPE html><htmllang="zh-CN"><head>    <metacharset="UTF-8">    <title>事件监听器执行时机</title>    <script>        console.log('1. 脚本开始执行');        // 注册事件监听器(立即执行)        console.log('2. 注册点击事件监听器');        const button = document.getElementById('myButton');        console.log('3. 按钮元素:', button); // null(按钮还不存在)        // 如果按钮不存在,这段代码会报错!        // button.addEventListener('click', ...); // ❌ TypeError    </script></head><body>    <h1>事件监听器示例</h1>    <buttonid="myButton">点击我</button>    <script>        console.log('4. 脚本在 body 中执行');        const button = document.getElementById('myButton');        console.log('5. 按钮元素:', button); // ✅ 找到了        // 注册事件监听器(立即注册,但不执行回调)        button.addEventListener('click'function() {            console.log('7. 按钮被点击了!');            alert('你点击了按钮!');        });        console.log('6. 事件监听器注册完成,等待用户点击...');    </script></body></html>```**执行流程:**```页面加载时:    ↓1. 脚本开始执行    ↓2. 注册点击事件监听器(尝试)    ↓3. 按钮元素: null(❌ 按钮还不存在)    ↓4. 脚本在 body 中执行    ↓5. 按钮元素: <button>(✅ 找到了)    ↓6. 事件监听器注册完成,等待用户点击...    ↓等待用户操作...    ↓用户点击按钮时:    ↓7. 按钮被点击了!(触发回调函数)```**关键理解:**- `addEventListener` 的注册过程是**立即执行**的- 但回调函数(`function() { ... }`)只有在**事件触发时**才执行#### 7.2 多种事件类型```html<!DOCTYPE html><htmllang="zh-CN"><head>    <metacharset="UTF-8">    <title>多种事件类型</title>    <style>        #demo-box {            width200px;            height200px;            background-color: lightblue;            margin20px;            display: flex;            align-items: center;            justify-content: center;        }    </style></head><body>    <h1>事件类型示例</h1>    <!-- 点击事件 -->    <buttonid="click-btn">点击我</button>    <!-- 鼠标悬停事件 -->    <divid="hover-box"style="padding: 20px; background: #eee; margin: 10px;">        鼠标移到这里    </div>    <!-- 输入事件 -->    <inputtype="text"id="name-input"placeholder="输入你的名字">    <!-- 表单提交事件 -->    <formid="my-form">        <inputtype="text"id="username"placeholder="用户名">        <buttontype="submit">提交</button>    </form>    <!-- 滚动事件 -->    <divid="scroll-box"style="height: 100px; overflow-y: auto; border: 1px solid #ccc;">        <divstyle="height: 300px; background: linear-gradient(to bottom, #eee, #ddd);">            滚动这个区域        </div>    </div>    <script>        // 1. 点击事件        document.getElementById('click-btn').addEventListener('click'function() {            console.log('点击事件触发');            alert('按钮被点击了!');        });        // 2. 鼠标移入事件        document.getElementById('hover-box').addEventListener('mouseenter'function() {            console.log('鼠标移入');            this.style.backgroundColor = 'lightgreen';        });        // 3. 鼠标移出事件        document.getElementById('hover-box').addEventListener('mouseleave'function() {            console.log('鼠标移出');            this.style.backgroundColor = '#eee';        });        // 4. 输入事件(实时监听)        document.getElementById('name-input').addEventListener('input'function(event) {            console.log('输入值:', event.target.value);        });        // 5. 表单提交事件        document.getElementById('my-form').addEventListener('submit'function(event) {            event.preventDefault(); // 阻止表单默认提交            console.log('表单提交');            const username = document.getElementById('username').value;            console.log('用户名:', username);            alert(`表单提交!用户名: ${username}`);        });        // 6. 滚动事件        document.getElementById('scroll-box').addEventListener('scroll'function() {            console.log('滚动位置:'this.scrollTop);        });    </script></body></html>```---## Script 标签的各种属性### 1. src 属性:引用外部脚本```html<!DOCTYPE html><htmllang="zh-CN"><head>    <metacharset="UTF-8">    <title>外部脚本示例</title>    <!-- 引用外部 JavaScript 文件 -->    <scriptsrc="js/main.js"></script>    <!-- 引用 CDN 上的库 -->    <scriptsrc="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>    <!-- 带版本号的引用 -->    <scriptsrc="js/utils.js?v=1.0.0"></script></head><body>    <h1>外部脚本示例</h1></body></html>```**外部脚本示例:**```javascript// js/main.jsconsole.log('main.js 加载完成');function greet(name) {    return `你好, ${name}!`;}// 暴露到全局window.greet = greet;``````html<!-- 使用外部脚本中定义的函数 --><script>    const result = greet('世界');    console.log(result); // 你好, 世界!</script>```---### 2. type 属性:指定脚本类型```html<!DOCTYPE html><htmllang="zh-CN"><head>    <metacharset="UTF-8">    <title>Type 属性示例</title>    <!-- JavaScript(默认) -->    <scripttype="text/javascript">        console.log('JavaScript 代码');    </script>    <!-- JavaScript 模块 -->    <scripttype="module">        import { sayHello } from './module.js';        console.log(sayHello('Module'));    </script>    <!-- 数据块(不执行) -->    <scripttype="application/json"id="data">        {            "name""张三",            "age"25        }    </script>    <!-- 数据块 -->    <scripttype="text/plain"id="template">        <div>这是一个模板</div>    </script></head><body>    <h1>Type 属性示例</h1>    <script>        // 读取 JSON 数据        const data = JSON.parse(document.getElementById('data').textContent);        console.log('数据:', data);        // 读取模板        const template = document.getElementById('template').textContent;        console.log('模板:', template);    </script></body></html>```---### 3. crossorigin 属性:跨域资源共享```html<!DOCTYPE html><htmllang="zh-CN"><head>    <metacharset="UTF-8">    <title>Crossorigin 示例</title>    <!-- 匿名跨域请求 -->    <script        src="https://cdn.example.com/library.js"        crossorigin="anonymous">    </script>    <!-- 使用凭证的跨域请求 -->    <script        src="https://api.example.com/protected.js"        crossorigin="use-credentials">    </script></head><body>    <h1>Crossorigin 示例</h1></body></html>```---### 4. integrity 属性:子资源完整性(SRI)```html<!DOCTYPE html><htmllang="zh-CN"><head>    <metacharset="UTF-8">    <title>Integrity 属性示例</title>    <!-- 使用 SRI 验证资源完整性 -->    <script        src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"        integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI"        crossorigin="anonymous">    </script>    <!-- 如果文件被篡改,浏览器将拒绝加载 --></head><body>    <h1>Integrity 属性示例</h1></body></html>```---### 5. nomodule 属性:不支持模块的浏览器```html<!DOCTYPE html><htmllang="zh-CN"><head>    <metacharset="UTF-8">    <title>Nomodule 示例</title>    <!-- 现代浏览器:使用模块 -->    <scripttype="module"src="js/app.js"></script>    <!-- 旧浏览器:使用传统脚本 -->    <scriptnomodulesrc="js/app-es5.js"></script></head><body>    <h1>Nomodule 示例</h1></body></html>```---## 前端开发中使用 JavaScript 的场景### 1. DOM 操作#### 1.1 查找元素```html<!DOCTYPE html><htmllang="zh-CN"><head>    <metacharset="UTF-8">    <title>DOM 查找元素</title></head><body>    <divid="container">        <h1id="title">标题</h1>        <pclass="paragraph">第一段</p>        <pclass="paragraph">第二段</p>        <buttonclass="btn">按钮1</button>        <buttonclass="btn">按钮2</button>        <ulid="list">            <liclass="item">项目1</li>            <liclass="item">项目2</li>            <liclass="item">项目3</li>        </ul>    </div>    <script>        // 1. 通过 ID 查找        const title = document.getElementById('title');        console.log('标题:', title.textContent);        // 2. 通过类名查找        const paragraphs = document.getElementsByClassName('paragraph');        console.log('段落数量:', paragraphs.length);        // 3. 通过标签名查找        const buttons = document.getElementsByTagName('button');        console.log('按钮数量:', buttons.length);        // 4. 通过 CSS 选择器查找(单个)        const firstItem = document.querySelector('.item');        console.log('第一个项目:', firstItem.textContent);        // 5. 通过 CSS 选择器查找(多个)        const items = document.querySelectorAll('.item');        items.forEach((item, index) => {            console.log(`项目 ${index + 1}:`, item.textContent);        });        // 6. 在指定范围内查找        const container = document.getElementById('container');        const btns = container.querySelectorAll('.btn');        console.log('容器内的按钮:', btns.length);        // 7. 查找子元素        const list = document.getElementById('list');        const listItems = list.children;        console.log('列表子元素数量:', listItems.length);        // 8. 查找父元素        const item = document.querySelector('.item');        const parent = item.parentElement;        console.log('父元素:', parent.id);        // 9. 查找兄弟元素        const nextSibling = item.nextElementSibling;        const prevSibling = item.previousElementSibling;        console.log('下一个兄弟:', nextSibling.textContent);        console.log('上一个兄弟:', prevSibling.textContent);    </script></body></html>```#### 1.2 修改元素```html<!DOCTYPE html><htmllang="zh-CN"><head>    <metacharset="UTF-8">    <title>修改 DOM 元素</title>    <style>        .highlight {            background-color: yellow;        }        .hidden {            display: none;        }    </style></head><body>    <divid="content">        <h1id="title">原标题</h1>        <pid="description">原描述</p>        <buttonid="modify-btn">修改内容</button>        <buttonid="style-btn">修改样式</button>        <buttonid="class-btn">切换类名</button>        <buttonid="attr-btn">修改属性</button>        <buttonid="html-btn">修改 HTML</button>    </div>    <script>        const title = document.getElementById('title');        const description = document.getElementById('description');        // 1. 修改文本内容        document.getElementById('modify-btn').addEventListener('click'function() {            title.textContent = '修改后的标题';            description.textContent = '修改后的描述';            description.innerText = '使用 innerText(会解析换行)';        });        // 2. 修改样式        document.getElementById('style-btn').addEventListener('click'function() {            title.style.color = 'red';            title.style.fontSize = '32px';            title.style.fontWeight = 'bold';            description.style.color = 'blue';            description.style.padding = '20px';        });        // 3. 添加/移除类名        document.getElementById('class-btn').addEventListener('click'function() {            title.classList.add('highlight');            title.classList.toggle('highlight'); // 切换            title.classList.remove('highlight');            // 检查类名            console.log('是否有 highlight 类:', title.classList.contains('highlight'));            // 替换类名            title.classList.replace('old-class''new-class');        });        // 4. 修改属性        document.getElementById('attr-btn').addEventListener('click'function() {            title.id = 'new-title';            title.title = '这是一个提示';            title.dataset.custom = '自定义数据';            console.log('自定义数据:', title.dataset.custom);        });        // 5. 修改 HTML 内容        document.getElementById('html-btn').addEventListener('click'function() {            description.innerHTML = '<strong>加粗文字</strong> <em>斜体文字</em>';            description.innerHTML += '<br><span style="color: green;">新增内容</span>';        });    </script></body></html>```#### 1.3 创建和删除元素```html<!DOCTYPE html><htmllang="zh-CN"><head>    <metacharset="UTF-8">    <title>创建和删除元素</title>    <style>        .card {            border1px solid #ccc;            padding15px;            margin10px;            background-color#f9f9f9;        }    </style></head><body>    <divid="container">        <h1>动态元素示例</h1>        <buttonid="add-btn">添加卡片</button>        <buttonid="add-template-btn">添加模板卡片</button>        <buttonid="clear-btn">清空所有</button>        <hr>        <divid="cards-container"></div>    </div>    <!-- 模板 -->    <templateid="card-template">        <divclass="card">            <h3>卡片标题</h3>            <p>卡片内容</p>            <buttonclass="delete-btn">删除</button>        </div>    </template>    <script>        const container = document.getElementById('cards-container');        let cardCount = 0;        // 1. 创建元素并添加        document.getElementById('add-btn').addEventListener('click'function() {            cardCount++;            // 创建元素            const card = document.createElement('div');            card.className = 'card';            // 创建标题            const title = document.createElement('h3');            title.textContent = `卡片 ${cardCount}`;            // 创建内容            const content = document.createElement('p');            content.textContent = `这是第 ${cardCount} 张卡片的内容`;            // 创建删除按钮            const deleteBtn = document.createElement('button');            deleteBtn.textContent = '删除';            deleteBtn.className = 'delete-btn';            deleteBtn.addEventListener('click'function() {                this.parentElement.remove();            });            // 组装元素            card.appendChild(title);            card.appendChild(content);            card.appendChild(deleteBtn);            // 添加到容器            container.appendChild(card);        });        // 2. 使用模板创建元素        document.getElementById('add-template-btn').addEventListener('click'function() {            cardCount++;            const template = document.getElementById('card-template');            const clone = template.content.cloneNode(true);            // 修改克隆的元素            clone.querySelector('h3').textContent = `卡片 ${cardCount}`;            clone.querySelector('p').textContent = `这是第 ${cardCount} 张卡片(来自模板)`;            // 绑定删除事件            clone.querySelector('.delete-btn').addEventListener('click'function() {                this.closest('.card').remove();            });            // 添加到容器            container.appendChild(clone);        });        // 3. 清空所有元素        document.getElementById('clear-btn').addEventListener('click'function() {            container.innerHTML = ''// 方法1:设置 HTML 为空            // container.textContent = ''; // 方法2:设置文本为空            // while (container.firstChild) { // 方法3:逐个删除            //     container.removeChild(container.firstChild);            // }            cardCount = 0;        });    </script></body></html>```---### 2. 表单处理#### 2.1 表单验证```html<!DOCTYPE html><htmllang="zh-CN"><head>    <metacharset="UTF-8">    <title>表单验证</title>    <style>        .form-group {            margin15px 0;        }        label {            display: block;            margin-bottom5px;        }        input {            padding8px;            width300px;        }        .error {            color: red;            font-size14px;            margin-top5px;        }        .success {            color: green;            font-size14px;            margin-top5px;        }        .invalid {            border-color: red;        }        .valid {            border-color: green;        }    </style></head><body>    <h1>表单验证示例</h1>    <formid="registration-form">        <divclass="form-group">            <labelfor="username">用户名:</label>            <inputtype="text"id="username"name="username">            <divclass="error"id="username-error"></div>        </div>        <divclass="form-group">            <labelfor="email">邮箱:</label>            <inputtype="email"id="email"name="email">            <divclass="error"id="email-error"></div>        </div>        <divclass="form-group">            <labelfor="password">密码:</label>            <inputtype="password"id="password"name="password">            <divclass="error"id="password-error"></div>            <divclass="success"id="password-strength"></div>        </div>        <divclass="form-group">            <labelfor="confirm-password">确认密码:</label>            <inputtype="password"id="confirm-password"name="confirm-password">            <divclass="error"id="confirm-password-error"></div>        </div>        <buttontype="submit">注册</button>    </form>    <script>        const form = document.getElementById('registration-form');        const usernameInput = document.getElementById('username');        const emailInput = document.getElementById('email');        const passwordInput = document.getElementById('password');        const confirmPasswordInput = document.getElementById('confirm-password');        // 验证函数        function validateUsername(username) {            if (username.length < 3) {                return '用户名至少需要 3 个字符';            }            if (username.length > 20) {                return '用户名最多 20 个字符';            }            if (!/^[a-zA-Z0-9_]+$/.test(username)) {                return '用户名只能包含字母、数字和下划线';            }            return '';        }        function validateEmail(email) {            const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;            if (!regex.test(email)) {                return '请输入有效的邮箱地址';            }            return '';        }        function validatePassword(password) {            if (password.length < 8) {                return '密码至少需要 8 个字符';            }            return '';        }        function getPasswordStrength(password) {            let strength = 0;            if (password.length >= 8) strength++;            if (/[a-z]/.test(password)) strength++;            if (/[A-Z]/.test(password)) strength++;            if (/[0-9]/.test(password)) strength++;            if (/[^a-zA-Z0-9]/.test(password)) strength++;            const levels = ['很弱''弱''中等''强''很强'];            return levels[strength - 1] || '很弱';        }        // 实时验证用户名        usernameInput.addEventListener('input'function() {            const username = this.value;            const error = validateUsername(username);            const errorElement = document.getElementById('username-error');            if (error) {                errorElement.textContent = error;                this.classList.add('invalid');                this.classList.remove('valid');            } else {                errorElement.textContent = '';                this.classList.remove('invalid');                this.classList.add('valid');            }        });        // 实时验证邮箱        emailInput.addEventListener('input'function() {            const email = this.value;            const error = validateEmail(email);            const errorElement = document.getElementById('email-error');            if (error) {                errorElement.textContent = error;                this.classList.add('invalid');                this.classList.remove('valid');            } else {                errorElement.textContent = '';                this.classList.remove('invalid');                this.classList.add('valid');            }        });        // 实时验证密码强度        passwordInput.addEventListener('input'function() {            const password = this.value;            const error = validatePassword(password);            const errorElement = document.getElementById('password-error');            const strengthElement = document.getElementById('password-strength');            if (error) {                errorElement.textContent = error;                strengthElement.textContent = '';                this.classList.add('invalid');                this.classList.remove('valid');            } else {                errorElement.textContent = '';                strengthElement.textContent = '密码强度:' + getPasswordStrength(password);                this.classList.remove('invalid');                this.classList.add('valid');            }        });        // 验证确认密码        confirmPasswordInput.addEventListener('input'function() {            const password = passwordInput.value;            const confirmPassword = this.value;            const errorElement = document.getElementById('confirm-password-error');            if (confirmPassword && password !== confirmPassword) {                errorElement.textContent = '两次输入的密码不一致';                this.classList.add('invalid');                this.classList.remove('valid');            } else {                errorElement.textContent = '';                this.classList.remove('invalid');                this.classList.add('valid');            }        });        // 表单提交验证        form.addEventListener('submit'function(event) {            event.preventDefault();            // 验证所有字段            const usernameError = validateUsername(usernameInput.value);            const emailError = validateEmail(emailInput.value);            const passwordError = validatePassword(passwordInput.value);            const confirmError = confirmPasswordInput.value !== passwordInput.value ?                '两次输入的密码不一致' : '';            // 显示所有错误            if (usernameError) {                document.getElementById('username-error').textContent = usernameError;            }            if (emailError) {                document.getElementById('email-error').textContent = emailError;            }            if (passwordError) {                document.getElementById('password-error').textContent = passwordError;            }            if (confirmError) {                document.getElementById('confirm-password-error').textContent = confirmError;            }            // 如果没有错误,提交表单            if (!usernameError && !emailError && !passwordError && !confirmError) {                console.log('表单验证通过,准备提交');                console.log('用户名:', usernameInput.value);                console.log('邮箱:', emailInput.value);                console.log('密码:', passwordInput.value);                // 实际项目中,这里会发送 AJAX 请求                alert('注册成功!');            }        });    </script></body></html>```---### 3. 异步数据请求#### 3.1 Fetch API```html<!DOCTYPE html><htmllang="zh-CN"><head>    <metacharset="UTF-8">    <title>异步数据请求</title>    <style>        #user-list {            margin-top20px;        }        .user-card {            border1px solid #ddd;            padding15px;            margin10px 0;            background-color#f9f9f9;        }        #loading {            display: none;            color: blue;        }        #error {            color: red;            display: none;        }    </style></head><body>    <h1>异步数据请求示例</h1>    <buttonid="fetch-btn">获取用户数据</button>    <divid="loading">加载中...</div>    <divid="error"></div>    <divid="user-list"></div>    <script>        const fetchBtn = document.getElementById('fetch-btn');        const loading = document.getElementById('loading');        const error = document.getElementById('error');        const userList = document.getElementById('user-list');        // 使用 Fetch API 获取数据        fetchBtn.addEventListener('click'async function() {            // 显示加载状态            loading.style.display = 'block';            error.style.display = 'none';            userList.innerHTML = '';            fetchBtn.disabled = true;            try {                // 发送 GET 请求                const response = await fetch('https://jsonplaceholder.typicode.com/users');                // 检查响应状态                if (!response.ok) {                    throw new Error(`HTTP error! status: ${response.status}`);                }                // 解析 JSON 数据                const users = await response.json();                // 渲染数据                users.forEach(user => {                    const card = document.createElement('div');                    card.className = 'user-card';                    card.innerHTML = `                        <h3>${user.name}</h3>                        <p><strong>邮箱:</strong> ${user.email}</p>                        <p><strong>电话:</strong> ${user.phone}</p>                        <p><strong>网站:</strong> ${user.website}</p>                        <p><strong>公司:</strong> ${user.company.name}</p>                    `;                    userList.appendChild(card);                });            } catch (err) {                // 显示错误信息                console.error('获取数据失败:', err);                error.textContent = '获取数据失败:' + err.message;                error.style.display = 'block';            } finally {                // 恢复按钮状态                loading.style.display = 'none';                fetchBtn.disabled = false;            }        });    </script></body></html>```#### 3.2 POST 请求```html<!DOCTYPE html><htmllang="zh-CN"><head>    <metacharset="UTF-8">    <title>POST 请求示例</title></head><body>    <h1>创建新用户</h1>    <formid="create-user-form">        <div>            <labelfor="name">姓名:</label>            <inputtype="text"id="name"name="name"required>        </div>        <br>        <div>            <labelfor="username">用户名:</label>            <inputtype="text"id="username"name="username"required>        </div>        <br>        <div>            <labelfor="email">邮箱:</label>            <inputtype="email"id="email"name="email"required>        </div>        <br>        <buttontype="submit">创建</button>    </form>    <divid="result"></div>    <script>        const form = document.getElementById('create-user-form');        const result = document.getElementById('result');        form.addEventListener('submit'async function(event) {            event.preventDefault();            // 获取表单数据            const formData = new FormData(form);            const data = Object.fromEntries(formData.entries());            try {                // 发送 POST 请求                const response = await fetch('https://jsonplaceholder.typicode.com/users', {                    method'POST',                    headers: {                        'Content-Type''application/json',                    },                    bodyJSON.stringify(data),                });                if (!response.ok) {                    throw new Error(`HTTP error! status: ${response.status}`);                }                const resultData = await response.json();                // 显示结果                result.innerHTML = `                    <h3>用户创建成功!</h3>                    <p>ID: ${resultData.id}</p>                    <p>姓名: ${resultData.name}</p>                    <p>用户名: ${resultData.username}</p>                    <p>邮箱: ${resultData.email}</p>                `;                console.log('创建成功:', resultData);            } catch (err) {                console.error('创建失败:', err);                result.innerHTML = `<p style="color: red;">创建失败:${err.message}</p>`;            }        });    </script></body></html>```---### 4. 本地存储```html<!DOCTYPE html><htmllang="zh-CN"><head>    <metacharset="UTF-8">    <title>本地存储示例</title></head><body>    <h1>本地存储示例</h1>    <h2>LocalStorage(永久存储)</h2>    <inputtype="text"id="local-key"placeholder="键名">    <inputtype="text"id="local-value"placeholder="值">    <buttonid="local-set">设置</button>    <buttonid="local-get">获取</button>    <buttonid="local-remove">删除</button>    <buttonid="local-clear">清空所有</button>    <divid="local-result"></div>    <h2>SessionStorage(会话存储)</h2>    <inputtype="text"id="session-key"placeholder="键名">    <inputtype="text"id="session-value"placeholder="值">    <buttonid="session-set">设置</button>    <buttonid="session-get">获取</button>    <buttonid="session-remove">删除</button>    <buttonid="session-clear">清空所有</button>    <divid="session-result"></div>    <script>        // ============ LocalStorage ============        const localSetBtn = document.getElementById('local-set');        const localGetBtn = document.getElementById('local-get');        const localRemoveBtn = document.getElementById('local-remove');        const localClearBtn = document.getElementById('local-clear');        const localResult = document.getElementById('local-result');        // 设置值        localSetBtn.addEventListener('click'function() {            const key = document.getElementById('local-key').value;            const value = document.getElementById('local-value').value;            if (key && value) {                localStorage.setItem(key, value);                localResult.textContent = `已设置: ${key} = ${value}`;            }        });        // 获取值        localGetBtn.addEventListener('click'function() {            const key = document.getElementById('local-key').value;            const value = localStorage.getItem(key);            if (value !== null) {                localResult.textContent = `获取到: ${key} = ${value}`;            } else {                localResult.textContent = `键 ${key} 不存在`;            }        });        // 删除值        localRemoveBtn.addEventListener('click'function() {            const key = document.getElementById('local-key').value;            localStorage.removeItem(key);            localResult.textContent = `已删除: ${key}`;        });        // 清空所有        localClearBtn.addEventListener('click'function() {            localStorage.clear();            localResult.textContent = '已清空所有 LocalStorage 数据';        });        // ============ SessionStorage ============        const sessionSetBtn = document.getElementById('session-set');        const sessionGetBtn = document.getElementById('session-get');        const sessionRemoveBtn = document.getElementById('session-remove');        const sessionClearBtn = document.getElementById('session-clear');        const sessionResult = document.getElementById('session-result');        // 设置值        sessionSetBtn.addEventListener('click'function() {            const key = document.getElementById('session-key').value;            const value = document.getElementById('session-value').value;            if (key && value) {                sessionStorage.setItem(key, value);                sessionResult.textContent = `已设置: ${key} = ${value}`;            }        });        // 获取值        sessionGetBtn.addEventListener('click'function() {            const key = document.getElementById('session-key').value;            const value = sessionStorage.getItem(key);            if (value !== null) {                sessionResult.textContent = `获取到: ${key} = ${value}`;            } else {                sessionResult.textContent = `键 ${key} 不存在`;            }        });        // 删除值        sessionRemoveBtn.addEventListener('click'function() {            const key = document.getElementById('session-key').value;            sessionStorage.removeItem(key);            sessionResult.textContent = `已删除: ${key}`;        });        // 清空所有        sessionClearBtn.addEventListener('click'function() {            sessionStorage.clear();            sessionResult.textContent = '已清空所有 SessionStorage 数据';        });        // 存储对象(需要序列化)        const user = {            name'张三',            age25,            email'zhangsan@example.com'        };        // 存储        localStorage.setItem('user'JSON.stringify(user));        // 读取        const savedUser = JSON.parse(localStorage.getItem('user'));        console.log('保存的用户:', savedUser);    </script></body></html>```---### 5. 动画效果```html<!DOCTYPE html><htmllang="zh-CN"><head>    <metacharset="UTF-8">    <title>动画效果示例</title>    <style>        #animated-box {            width100px;            height100px;            background-color: blue;            position: relative;            left0;            transition: all 0.5s ease;        }        .moved {            left200px;            background-color: red;            transformrotate(180deg);        }        .fade-in {            animation: fadeIn 1s ease-in;        }        @keyframes fadeIn {            from { opacity0; }            to { opacity1; }        }    </style></head><body>    <h1>动画效果示例</h1>    <divid="animated-box"></div>    <br>    <buttonid="move-btn">移动盒子</button>    <buttonid="reset-btn">重置</button>    <buttonid="fade-btn">淡入效果</button>    <script>        const box = document.getElementById('animated-box');        const moveBtn = document.getElementById('move-btn');        const resetBtn = document.getElementById('reset-btn');        const fadeBtn = document.getElementById('fade-btn');        // 使用 CSS 类实现动画        moveBtn.addEventListener('click'function() {            box.classList.toggle('moved');        });        // 重置        resetBtn.addEventListener('click'function() {            box.classList.remove('moved');            box.classList.remove('fade-in');        });        // 使用 requestAnimationFrame 实现平滑动画        fadeBtn.addEventListener('click'function() {            box.style.opacity = '0';            box.classList.add('fade-in');        });        // 使用 JavaScript 动画        let isAnimating = false;        box.addEventListener('click'function() {            if (isAnimating) return;            isAnimating = true;            let position = 0;            const target = 300;            const duration = 1000// 1 秒            const startTime = performance.now();            function animate(currentTime) {                const elapsed = currentTime - startTime;                const progress = Math.min(elapsed / duration, 1);                // 缓动函数                const easeOutQuad = t => t * (2 - t);                const easedProgress = easeOutQuad(progress);                position = easedProgress * target;                box.style.left = position + 'px';                if (progress < 1) {                    requestAnimationFrame(animate);                } else {                    isAnimating = false;                }            }            requestAnimationFrame(animate);        });    </script></body></html>```---## 前端开发中使用 TypeScript 的场景### 1. 为什么使用 TypeScript| 场景 | JavaScript | TypeScript ||------|-----------|-------------|| **小型项目** | ✅ 足够 | 可能过度 || **快速原型** | ✅ 更快 | 需要额外配置 || **团队项目** | ⚠️ 容易出错 | ✅ 类型安全 || **大型应用** | ❌ 难以维护 | ✅ 易于维护 || **重构项目** | ⚠️ 容易出错 | ✅ 类型保护 |---### 2. TypeScript 基础示例#### 2.1 类型注解```typescript// 基本类型let username: string = '张三';let age: number = 25;let isStudent: boolean = true;let nothing: null = null;let notDefined: undefined = undefined;// 数组let numbers: number[] = [1, 2, 3];let names: string[] = ['Alice', 'Bob', 'Charlie'];let anyArray: any[] = [1, 'hello', true];// 对象interface User {    id: number;    name: string;    age: number;    email?: string;  // 可选属性}const user: User = {    id: 1,    name: '张三',    age: 25};// 函数function greet(name: string): string {    return `你好, ${name}!`;}function add(a: number, b: number): number {    return a + b;}// 箭头函数const multiply = (a: number, b: number): number => a * b;// 可选参数function createUser(name: string, age?: number): void {    console.log(`创建用户: ${name}, 年龄: ${age || '未知'}`);}```---### 3. TypeScript 实际应用示例#### 3.1 类型安全的 DOM 操作```typescript// 前端 TypeScript 项目// src/dom-operations.ts// 定义元素选择器const SELECTORS = {    container: '#container',    button: '.action-btn',    input: '#username',} as const;// 获取元素(类型安全)function getElement<TextendsHTMLElement>(selector: string): T {    const element = document.querySelector(selector);    if (!element) {        throw new Error(`元素 ${selector} 不存在`);    }    return element as T;}// 创建元素(类型安全)function createElement<KextendskeyofHTMLElementTagNameMap>(    tag: K,    className?: string): HTMLElementTagNameMap[K] {    const element = document.createElement(tag);    if (className) {        element.className = className;    }    return element;}// 使用示例function initApp() {    const container = getElement<HTMLDivElement>(SELECTORS.container);    const button = getElement<HTMLButtonElement>(SELECTORS.button);    const input = getElement<HTMLInputElement>(SELECTORS.input);    // 类型安全的事件监听    button.addEventListener('click', (event: MouseEvent) => {        const target = event.target as HTMLButtonElement;        console.log('按钮点击:', target.textContent);    });    input.addEventListener('input', (event: Event) => {        const target = event.target as HTMLInputElement;        console.log('输入值:', target.value);    });    // 创建新元素    const card = createElement<HTMLDivElement>('div', 'card');    const title = createElement<HTMLHeadingElement>('h2');    title.textContent = '新卡片';    card.appendChild(title);    container.appendChild(card);}// 页面加载后初始化document.addEventListener('DOMContentLoaded', initApp);```#### 3.2 类型安全的表单处理```typescript// src/form-handler.ts// 定义表单数据接口interface RegistrationForm {    username: string;    email: string;    password: string;    confirmPassword: string;    agree: boolean;}// 定义验证错误接口interface ValidationError {    field: keyof RegistrationForm;    message: string;}// 表单验证器class FormValidator {    private errors: ValidationError[] = [];    // 验证用户名    validateUsername(username: string): boolean {        if (username.length < 3) {            this.addError('username', '用户名至少需要 3 个字符');            return false;        }        if (username.length > 20) {            this.addError('username', '用户名最多 20 个字符');            return false;        }        return true;    }    // 验证邮箱    validateEmail(email: string): boolean {        const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;        if (!regex.test(email)) {            this.addError('email', '请输入有效的邮箱地址');            return false;        }        return true;    }    // 验证密码    validatePassword(password: string): boolean {        if (password.length < 8) {            this.addError('password', '密码至少需要 8 个字符');            return false;        }        return true;    }    // 添加错误    private addError(field: keyof RegistrationForm, message: string): void {        this.errors.push({ field, message });    }    // 获取所有错误    getErrors(): ValidationError[] {        return this.errors;    }    // 清空错误    clearErrors(): void {        this.errors = [];    }    // 是否有错误    hasErrors(): boolean {        return this.errors.length > 0;    }}// 表单处理类class FormHandler {    private form: HTMLFormElement;    private validator: FormValidator;    constructor(formId: string) {        const formElement = document.getElementById(formId);        if (!formElement || !(formElement instanceof HTMLFormElement)) {            throw new Error(`表单 ${formId} 不存在`);        }        this.form = formElement;        this.validator = new FormValidator();        this.init();    }    private init(): void {        this.form.addEventListener('submit', this.handleSubmit.bind(this));        // 实时验证        const usernameInput = this.form.querySelector('#username') as HTMLInputElement;        usernameInput?.addEventListener('input', (e: Event) => {            const target = e.target as HTMLInputElement;            this.validateField('username', target.value);        });    }    private async handleSubmit(event: Event): Promise<void> {        event.preventDefault();        // 获取表单数据        const formData = new FormData(this.form);        const data = Object.fromEntries(formData.entries()) as RegistrationForm;        // 验证表单        this.validator.clearErrors();        this.validator.validateUsername(data.username);        this.validator.validateEmail(data.email);        this.validator.validatePassword(data.password);        if (data.password !== data.confirmPassword) {            this.validator['addError']('confirmPassword', '两次输入的密码不一致');        }        // 显示错误        if (this.validator.hasErrors()) {            this.displayErrors();            return;        }        // 提交表单        try {            const response = await this.submitForm(data);            console.log('提交成功:', response);            alert('注册成功!');        } catch (error) {            console.error('提交失败:', error);            alert('注册失败,请重试');        }    }    private validateField(field: keyof RegistrationForm, value: string): void {        // 字段级验证逻辑    }    private displayErrors(): void {        const errors = this.validator.getErrors();        console.log('表单错误:', errors);        // 显示错误到 UI    }    private async submitForm(data: RegistrationForm): Promise<Response> {        return fetch('/api/register', {            method: 'POST',            headers: {                'Content-Type': 'application/json',            },            body: JSON.stringify(data),        });    }}// 使用document.addEventListener('DOMContentLoaded', () => {    new FormHandler('registration-form');});```#### 3.3 类型安全的 API 请求```typescript// src/api-client.ts// 定义 API 响应类型interface User {    id: number;    name: string;    email: string;    phone: string;    website: string;    company: {        name: string;        catchPhrase: string;    };}interface ApiResponse<T> {    data: T;    message: string;    code: number;}interface ApiError {    message: string;    code: number;}// API 客户端类class ApiClient {    private baseUrl: string;    constructor(baseUrl: string = 'https://api.example.com') {        this.baseUrl = baseUrl;    }    // 通用请求方法    private async request<T>(        endpoint: string,        options: RequestInit = {}    ): Promise<T> {        const url = `${this.baseUrl}${endpoint}`;        const response = await fetch(url, {            headers: {                'Content-Type': 'application/json',                ...options.headers,            },            ...options,        });        if (!response.ok) {            const error: ApiError = await response.json();            throw new Error(error.message);        }        return response.json();    }    // GET 请求    async get<T>(endpoint: string): Promise<T> {        return this.request<T>(endpoint, {            method: 'GET',        });    }    // POST 请求    async post<T>(endpoint: string, data: unknown): Promise<T> {        return this.request<T>(endpoint, {            method: 'POST',            body: JSON.stringify(data),        });    }    // PUT 请求    async put<T>(endpoint: string, data: unknown): Promise<T> {        return this.request<T>(endpoint, {            method: 'PUT',            body: JSON.stringify(data),        });    }    // DELETE 请求    async delete<T>(endpoint: string): Promise<T> {        return this.request<T>(endpoint, {            method: 'DELETE',        });    }}// 用户 APIclass UserApi {    private client: ApiClient;    constructor() {        this.client = new ApiClient('https://jsonplaceholder.typicode.com');    }    // 获取所有用户    async getAllUsers(): Promise<User[]> {        return this.client.get<User[]>('/users');    }    // 获取单个用户    async getUser(id: number): Promise<User> {        return this.client.get<User>(`/users/${id}`);    }    // 创建用户    async createUser(user: Partial<User>): Promise<User> {        return this.client.post<User>('/users', user);    }    // 更新用户    async updateUser(id: number, user: Partial<User>): Promise<User> {        return this.client.put<User>(`/users/${id}`, user);    }    // 删除用户    async deleteUser(id: number): Promise<void> {        return this.client.delete<void>(`/users/${id}`);    }}// 使用示例async function main() {    const userApi = new UserApi();    try {        // 获取所有用户        const users = await userApi.getAllUsers();        console.log('用户列表:', users);        // 获取单个用户        const user = await userApi.getUser(1);        console.log('用户详情:', user);        // 创建新用户        const newUser = await userApi.createUser({            name: '张三',            email: 'zhangsan@example.com',            phone: '1234567890',        });        console.log('新用户:', newUser);    } catch (error) {        console.error('API 请求失败:', error);    }}main();```---### 4. 实战:完整的应用示例```typescript// src/app.ts// 应用状态类型interface AppState {    users: User[];    loading: boolean;    error: string | null;    currentPage: number;}// 应用类class TodoApp {    private state: AppState;    constructor() {        this.state = {            users: [],            loading: false,            error: null,            currentPage: 1,        };        this.init();    }    private init(): void {        this.loadUsers();        this.bindEvents();    }    private async loadUsers(): Promise<void> {        this.setState({ loading: true, error: null });        try {            const userApi = new UserApi();            const users = await userApi.getAllUsers();            this.setState({ users, loading: false });        } catch (error) {            this.setState({                error: error instanceof Error ? error.message : '加载失败',                loading: false,            });        }    }    private setState(newState: Partial<AppState>): void {        this.state = { ...this.state, ...newState };        this.render();    }    private bindEvents(): void {        const loadBtn = document.getElementById('load-btn') as HTMLButtonElement;        loadBtn?.addEventListener('click', () => this.loadUsers());        const prevBtn = document.getElementById('prev-btn') as HTMLButtonElement;        prevBtn?.addEventListener('click', () => {            this.setState({ currentPage: Math.max(1, this.state.currentPage - 1) });        });        const nextBtn = document.getElementById('next-btn') as HTMLButtonElement;        nextBtn?.addEventListener('click', () => {            this.setState({ currentPage: this.state.currentPage + 1 });        });    }    private render(): void {        const container = document.getElementById('app-container') as HTMLDivElement;        if (!container) return;        // 渲染加载状态        if (this.state.loading) {            container.innerHTML = '<p>加载中...</p>';            return;        }        // 渲染错误状态        if (this.state.error) {            container.innerHTML = `<pclass="error">${this.state.error}</p>`;            return;        }        // 渲染用户列表        const usersHtml = this.state.users.map(user => `            <divclass="user-card">                <h3>${user.name}</h3>                <p>邮箱: ${user.email}</p>                <p>电话: ${user.phone}</p>            </div>        `).join('');        container.innerHTML = `            <buttonid="load-btn">重新加载</button>            <buttonid="prev-btn">上一页</button>            <buttonid="next-btn">下一页</button>            <p>当前页: ${this.state.currentPage}</p>            <divclass="user-list">                ${usersHtml}            </div>        `;        // 重新绑定事件        this.bindEvents();    }}// 启动应用document.addEventListener('DOMContentLoaded', () => {    new TodoApp();});```---## JavaScript vs TypeScript 选择指南### 1. 决策树```是否需要使用 TypeScript?项目规模├── 小型(< 500 行代码)│   └── JavaScript ✅ 足够├── 中型(500 - 5000 行)│   ├── 需要长期维护?│   │   ├── 是 → TypeScript ✅│   │   └── 否 → JavaScript ✅└── 大型(> 5000 行)    └── TypeScript ✅ 强烈推荐团队情况├── 个人项目│   └── 根据个人偏好选择├── 小团队(1-3 人)│   ├── 团队成员熟悉 TypeScript?│   │   ├── 是 → TypeScript ✅│   │   └── 否 → JavaScript ✅└── 大型团队    └── TypeScript ✅ 类型安全、降低沟通成本项目类型├── 快速原型│   └── JavaScript ✅ 开发速度快├── 生产环境应用│   └── TypeScript ✅ 减少运行时错误├── 库/框架开发│   └── TypeScript ✅ 提供类型定义└── 自动化测试    ├── 简单脚本 → JavaScript ✅    └── 复杂测试框架 → TypeScript ✅```---### 2. 详细使用场景对比#### 2.1 场景一:简单的页面交互**需求:** 一个简单的登录页面,有表单验证功能**JavaScript 实现:**```html<!DOCTYPE html><htmllang="zh-CN"><head>    <metacharset="UTF-8">    <title>登录 - JavaScript 版本</title></head><body>    <h1>登录</h1>    <formid="login-form">        <inputtype="text"id="username"placeholder="用户名">        <inputtype="password"id="password"placeholder="密码">        <buttontype="submit">登录</button>    </form>    <divid="error"></div>    <script>        const form = document.getElementById('login-form');        const errorDiv = document.getElementById('error');        form.addEventListener('submit'function(event) {            event.preventDefault();            const username = document.getElementById('username').value;            const password = document.getElementById('password').value;            // 简单验证            if (!username || !password) {                errorDiv.textContent = '请填写完整信息';                return;            }            // 提交            fetch('/api/login', {                method'POST',                bodyJSON.stringify({ username, password })            });        });    </script></body></html>```**TypeScript 实现:**```html<!DOCTYPE html><htmllang="zh-CN"><head>    <metacharset="UTF-8">    <title>登录 - TypeScript 版本</title></head><body>    <h1>登录</h1>    <formid="login-form">        <inputtype="text"id="username"placeholder="用户名">        <inputtype="password"id="password"placeholder="密码">        <buttontype="submit">登录</button>    </form>    <divid="error"></div>    <scripttype="module">        // 定义类型        interface LoginData {            username: string;            password: string;        }        interface ApiResponse {            success: boolean;            message: string;            token?: string;        }        const form = document.getElementById('login-form'as HTMLFormElement;        const errorDiv = document.getElementById('error'as HTMLDivElement;        form.addEventListener('submit'async (eventEvent) => {            event.preventDefault();            const usernameInput = document.getElementById('username'as HTMLInputElement;            const passwordInput = document.getElementById('password'as HTMLInputElement;            const username = usernameInput.value;            const password = passwordInput.value;            // 类型安全的验证            if (!username || !password) {                errorDiv.textContent = '请填写完整信息';                return;            }            // 类型安全的提交            const loginDataLoginData = { username, password };            try {                const response = await fetch('/api/login', {                    method'POST',                    headers: {                        'Content-Type''application/json',                    },                    bodyJSON.stringify(loginData),                });                const resultApiResponse = await response.json();                if (result.success && result.token) {                    localStorage.setItem('token', result.token);                    // 跳转到首页                    window.location.href = '/home';                } else {                    errorDiv.textContent = result.message;                }            } catch (error) {                errorDiv.textContent = '登录失败,请稍后重试';            }        });    </script></body></html>```**对比:**| 方面 | JavaScript | TypeScript ||------|-----------|-------------|| **代码量** | 约 30 行 | 约 50 行(含类型) || **类型安全** | ❌ 无 | ✅ 有 || **IDE 支持** | ⚠️ 基础 | ✅ 完整 || **运行时错误** | 可能发现较晚 | 编译时发现 || **推荐场景** | ✅ 简单页面 | ⚠️ 可能过度 |**结论:** 简单的登录页面使用 **JavaScript** 即可,TypeScript 类型定义增加了复杂度,但收益不大。---#### 2.2 场景二:复杂的数据管理**需求:** 用户管理系统,包含增删改查、分页、筛选等功能**JavaScript 实现:**```javascript// 用户管理 - JavaScript 版本class UserManager {    constructor() {        this.users = [];        this.page = 1;        this.pageSize = 10;        this.filters = {};    }    // 获取用户    async getUsers() {        const response = await fetch(`/api/users?page=${this.page}&size=${this.pageSize}`);        this.users = await response.json();        return this.users;    }    // 添加用户    async addUser(user) {        const response = await fetch('/api/users', {            method: 'POST',            body: JSON.stringify(user),        });        const newUser = await response.json();        this.users.push(newUser);        return newUser;    }    // 更新用户    async updateUser(id, updates) {        const response = await fetch(`/api/users/${id}`, {            method: 'PUT',            body: JSON.stringify(updates),        });        const updatedUser = await response.json();        const index = this.users.findIndex(u => u.id === id);        if (index !== -1) {            this.users[index] = updatedUser;        }        return updatedUser;    }    // 删除用户    async deleteUser(id) {        const response = await fetch(`/api/users/${id}`, {            method: 'DELETE',        });        if (response.ok) {            this.users = this.users.filter(u => u.id !== id);        }    }    // 筛选用户    filterUsers(filters) {        this.filters = filters;        return this.users.filter(user => {            return Object.entries(filters).every(([key, value]) => {                return user[key] === value;            });        });    }}// ❌ 问题:// 1. user 的类型不确定,可能出现拼写错误// 2. 返回值类型不明确// 3. 过滤器键可能不存在于 user 中```**TypeScript 实现:**```typescript// 用户管理 - TypeScript 版本// 定义类型interface User {    id: number;    username: string;    email: string;    age: number;    role: 'admin' | 'user' | 'guest';    createdAt: string;    updatedAt?: string;}interface UserFilters {    username?: string;    email?: string;    role?: 'admin' | 'user' | 'guest';    minAge?: number;    maxAge?: number;}interface PaginationParams {    page: number;    pageSize: number;}interface PaginatedResponse<T> {    data: T[];    total: number;    page: number;    pageSize: number;}interface CreateUserDto {    username: string;    email: string;    age: number;    role: 'admin' | 'user' | 'guest';    password: string;}interface UpdateUserDto {    username?: string;    email?: string;    age?: number;    role?: 'admin' | 'user' | 'guest';}class UserManager {    private users: User[];    private page: number;    private pageSize: number;    private filters: UserFilters;    constructor() {        this.users = [];        this.page = 1;        this.pageSize = 10;        this.filters = {};    }    // ✅ 类型安全的获取用户    async getUsers(params?: Partial<PaginationParams>): Promise<PaginatedResponse<User>> {        const response = await fetch(            `/api/users?page=${this.page}&size=${this.pageSize}`        );        if (!response.ok) {            throw new Error(`HTTP error! status: ${response.status}`);        }        const result: PaginatedResponse<User> = await response.json();        this.users = result.data;        return result;    }    // ✅ 类型安全的添加用户    async addUser(userDto: CreateUserDto): Promise<User> {        const response = await fetch('/api/users', {            method: 'POST',            headers: { 'Content-Type': 'application/json' },            body: JSON.stringify(userDto),        });        if (!response.ok) {            throw new Error('创建用户失败');        }        const newUser: User = await response.json();        this.users.push(newUser);        return newUser;    }    // ✅ 类型安全的更新用户    async updateUser(id: number, updates: UpdateUserDto): Promise<User> {        const response = await fetch(`/api/users/${id}`, {            method: 'PUT',            headers: { 'Content-Type': 'application/json' },            body: JSON.stringify(updates),        });        if (!response.ok) {            throw new Error('更新用户失败');        }        const updatedUser: User = await response.json();        const index = this.users.findIndex(u => u.id === id);        if (index !== -1) {            this.users[index] = updatedUser;        }        return updatedUser;    }    // ✅ 类型安全的删除用户    async deleteUser(id: number): Promise<void> {        const response = await fetch(`/api/users/${id}`, {            method: 'DELETE',        });        if (!response.ok) {            throw new Error('删除用户失败');        }        this.users = this.users.filter(u => u.id !== id);    }    // ✅ 类型安全的筛选用户    filterUsers(filters: Partial<UserFilters>): User[] {        this.filters = { ...this.filters, ...filters };        return this.users.filter(user => {            return Object.entries(this.filters).every(([key, value]) => {                // TypeScript 确保键存在于 UserFilters 中                if (key === 'minAge') {                    return user.age >= (value as number);                }                if (key === 'maxAge') {                    return user.age <= (value as number);                }                return user[key as keyof User] === value;            });        });    }    // ✅ 类型安全的分页    getTotalPages(): number {        return Math.ceil(this.users.length / this.pageSize);    }    // ✅ 类型安全的排序    sortUsers(field: keyof User, order: 'asc' | 'desc'): User[] {        return [...this.users].sort((a, b) => {            const aValue = a[field];            const bValue = b[field];            if (order === 'asc') {                return aValue > bValue ? 1 : -1;            } else {                return aValue < bValue ? 1 : -1;            }        });    }}// 使用示例async function main() {    const manager = new UserManager();    try {        // 获取用户列表        const result = await manager.getUsers({ page: 1, pageSize: 10 });        console.log(`共 ${result.total} 个用户`);        // 添加用户        const newUser = await manager.addUser({            username: 'zhangsan',            email: 'zhangsan@example.com',            age: 25,            role: 'user',            password: 'password123',        });        // 更新用户        await manager.updateUser(newUser.id, {            age: 26,            role: 'admin',        });        // 筛选用户        const admins = manager.filterUsers({ role: 'admin' });        console.log('管理员:', admins);        // 排序用户        const sortedUsers = manager.sortUsers('age', 'asc');        console.log('按年龄排序:', sortedUsers);    } catch (error) {        console.error('操作失败:', error);    }}```**对比:**| 方面 | JavaScript | TypeScript ||------|-----------|-------------|| **类型安全** | ❌ 无类型检查 | ✅ 完整类型定义 || **重构安全性** | ⚠️ 容易出错 | ✅ 编译器检查 || **代码可读性** | ⚠️ 需要注释 | ✅ 类型即文档 || **IDE 支持** | ⚠️ 基础 | ✅ 完整自动补全 || **运行时错误** | 可能才发现 | 编译时发现 || **推荐场景** | ❌ 不推荐 | ✅ 强烈推荐 |**结论:** 复杂数据管理系统使用 **TypeScript**,类型安全大幅减少错误,提高代码可维护性。---#### 2.3 场景三:前端自动化测试**需求:** 编写 Playwright 自动化测试**JavaScript 实现:**```javascript// 登录测试 - JavaScript 版本const { test, expect } = require('@playwright/test');test('should login successfully', async ({ page }) => {    // 访问登录页    await page.goto('https://example.com/login');    // 填写表单    await page.fill('#username', 'admin');    await page.fill('#password', 'password');    // 点击登录按钮    await page.click('#login-btn');    // 验证跳转    await expect(page).toHaveURL('/dashboard');    // 验证元素可见    await expect(page.locator('.welcome-message')).toBeVisible();});test('should show error for invalid credentials', async ({ page }) => {    await page.goto('https://example.com/login');    await page.fill('#username', 'wrong');    await page.fill('#password', 'wrong');    await page.click('#login-btn');    // 验证错误消息    const errorMessage = await page.textContent('.error-message');    expect(errorMessage).toContain('用户名或密码错误');});test('should validate form', async ({ page }) => {    await page.goto('https://example.com/login');    // 尝试空提交    await page.click('#login-btn');    // ❌ 验证错误消息 - 如果选择器错误,运行时才发现    await expect(page.locator('.error-msg')).toBeVisible();});```**TypeScript 实现:**```typescript// 登录测试 - TypeScript 版本import { test, expect, Page } from '@playwright/test';// 定义测试数据类型interface LoginCredentials {    username: string;    password: string;}interface TestCase {    name: string;    credentials: LoginCredentials;    expectedUrl: string;    shouldSucceed: boolean;}// 定义页面对象class LoginPage {    constructor(private page: Page) {}    private readonly selectors = {        usernameInput: '#username',        passwordInput: '#password',        loginButton: '#login-btn',        errorMessage: '.error-message',        welcomeMessage: '.welcome-message',    };    async navigate(): Promise<void> {        await this.page.goto('https://example.com/login');    }    async fillCredentials(credentials: LoginCredentials): Promise<void> {        await this.page.fill(this.selectors.usernameInput, credentials.username);        await this.page.fill(this.selectors.passwordInput, credentials.password);    }    async clickLogin(): Promise<void> {        await this.page.click(this.selectors.loginButton);    }    async getErrorMessage(): Promise<string> {        return await this.page.textContent(this.selectors.errorMessage) || '';    }    async isWelcomeVisible(): Promise<boolean> {        return await this.page.locator(this.selectors.welcomeMessage).isVisible();    }}// 定义测试用例const testCases: TestCase[] = [    {        name: '管理员登录',        credentials: { username: 'admin', password: 'admin123' },        expectedUrl: '/dashboard',        shouldSucceed: true,    },    {        name: '普通用户登录',        credentials: { username: 'user', password: 'user123' },        expectedUrl: '/home',        shouldSucceed: true,    },    {        name: '错误用户名',        credentials: { username: 'wrong', password: 'password' },        expectedUrl: '/login',        shouldSucceed: false,    },    {        name: '错误密码',        credentials: { username: 'admin', password: 'wrong' },        expectedUrl: '/login',        shouldSucceed: false,    },];// 使用页面对象的测试test.describe('登录测试(TypeScript)', () => {    let loginPage: LoginPage;    test.beforeEach(async ({ page }) => {        loginPage = new LoginPage(page);        await loginPage.navigate();    });    testCases.forEach(({ name, credentials, expectedUrl, shouldSucceed }) => {        test(`should ${shouldSucceed ? 'succeed' : 'fail'} with ${name}`, async ({ page }) => {            // ✅ 类型安全的填充            await loginPage.fillCredentials(credentials);            // 点击登录            await loginPage.clickLogin();            if (shouldSucceed) {                // 验证跳转                await expect(page).toHaveURL(expectedUrl);                // 验证欢迎消息                expect(await loginPage.isWelcomeVisible()).toBe(true);            } else {                // ✅ 类型安全的获取错误消息                const errorMessage = await loginPage.getErrorMessage();                expect(errorMessage).toBeTruthy();                expect(errorMessage).toContain('错误');            }        });    });});// 表单验证测试test.describe('表单验证', () => {    test('应该验证必填字段', async ({ page }) => {        const loginPage = new LoginPage(page);        await loginPage.navigate();        // 直接点击登录(不填写)        await loginPage.clickLogin();        // ✅ TypeScript 确保选择器正确        await expect(page.locator('.validation-error')).toBeVisible();    });    test('应该验证邮箱格式', async ({ page }) => {        const loginPage = new LoginPage(page);        await loginPage.navigate();        await loginPage.fillCredentials({ username: 'admin', password: '' });        await loginPage.clickLogin();        const errorMessage = await loginPage.getErrorMessage();        expect(errorMessage).toContain('密码不能为空');    });});```**对比:**| 方面 | JavaScript | TypeScript ||------|-----------|-------------|| **页面对象** | ⚠️ 可能有拼写错误 | ✅ 类型安全 || **测试数据** | ⚠️ 结构不明确 | ✅ 类型定义明确 || **选择器** | ⚠️ 字符串容易出错 | ✅ 集中管理,类型检查 || **重构** | ⚠️ 容易遗漏 | ✅ 编译器强制检查 || **维护成本** | ⚠️ 较高 | ✅ 较低 || **推荐场景** | ⚠️ 简单脚本 | ✅ 复杂测试框架 |**结论:** 复杂的自动化测试框架使用 **TypeScript**,页面对象模式配合类型系统极大提高可维护性。---#### 2.4 场景四:快速原型和概念验证**需求:** 快速验证一个新功能 idea**JavaScript 实现(推荐):**```html<!DOCTYPE html><htmllang="zh-CN"><head>    <metacharset="UTF-8">    <title>快速原型 - JavaScript</title></head><body>    <h1>快速原型:拖拽功能</h1>    <divid="draggable"style="width: 100px; height: 100px; background: blue; position: absolute;">        拖拽我    </div>    <script>        // 简单的拖拽实现        const draggable = document.getElementById('draggable');        let isDragging = false;        let offsetX, offsetY;        draggable.addEventListener('mousedown'(e) => {            isDragging = true;            offsetX = e.clientX - draggable.offsetLeft;            offsetY = e.clientY - draggable.offsetTop;        });        document.addEventListener('mousemove'(e) => {            if (isDragging) {                draggable.style.left = (e.clientX - offsetX) + 'px';                draggable.style.top = (e.clientY - offsetY) + 'px';            }        });        document.addEventListener('mouseup'() => {            isDragging = false;        });    </script></body></html>```**优点:**- 快速实现,无需配置- 直接在浏览器运行- 适合快速验证 idea**TypeScript 实现(不推荐用于快速原型):**```typescript// 需要配置 tsconfig.json// 需要编译步骤// 增加了不必要的复杂度```**结论:** 快速原型和概念验证使用 **JavaScript**,速度最重要,类型系统是累赘。---#### 2.5 场景五:长期维护的企业应用**需求:** 企业级管理系统,需要长期维护和多团队协作**TypeScript 实现(强烈推荐):**```typescript// 用户服务// src/services/user.service.tsinterface User {    id: number;    name: string;    email: string;    role: UserRole;    status: UserStatus;}type UserRole = 'admin' | 'manager' | 'employee';type UserStatus = 'active' | 'inactive' | 'suspended';interface CreateUserDto {    name: string;    email: string;    role: UserRole;    password: string;}interface UpdateUserDto {    name?: string;    email?: string;    role?: UserRole;    status?: UserStatus;}interface UserListParams {    page: number;    pageSize: number;    role?: UserRole;    status?: UserStatus;    search?: string;}class UserService {    private baseUrl: string = '/api/users';    async getAll(params: UserListParams): Promise<{ data: User[]; total: number }> {        const query = new URLSearchParams(params as any).toString();        const response = await fetch(`${this.baseUrl}?${query}`);        if (!response.ok) {            throw new Error(`获取用户列表失败: ${response.statusText}`);        }        return await response.json();    }    async getById(id: number): Promise<User> {        const response = await fetch(`${this.baseUrl}/${id}`);        if (!response.ok) {            throw new Error(`获取用户失败: ${response.statusText}`);        }        return await response.json();    }    async create(dto: CreateUserDto): Promise<User> {        const response = await fetch(this.baseUrl, {            method: 'POST',            headers: { 'Content-Type': 'application/json' },            body: JSON.stringify(dto),        });        if (!response.ok) {            throw new Error(`创建用户失败: ${response.statusText}`);        }        return await response.json();    }    async update(id: number, dto: UpdateUserDto): Promise<User> {        const response = await fetch(`${this.baseUrl}/${id}`, {            method: 'PUT',            headers: { 'Content-Type': 'application/json' },            body: JSON.stringify(dto),        });        if (!response.ok) {            throw new Error(`更新用户失败: ${response.statusText}`);        }        return await response.json();    }    async delete(id: number): Promise<void> {        const response = await fetch(`${this.baseUrl}/${id}`, {            method: 'DELETE',        });        if (!response.ok) {            throw new Error(`删除用户失败: ${response.statusText}`);        }    }}// 导出单例export const userService = new UserService();```**使用 TypeScript 的优势:**```typescript// 在其他文件中使用import { userService } from './services/user.service';// ✅ IDE 会自动补全所有方法userService.getAll({ page: 1, pageSize: 10 });// ✅ 编译器检查参数类型userService.create({    name: '张三',    email: 'zhangsan@example.com',    role: 'admin',  // ✅ 只能选择 'admin' | 'manager' | 'employee'    password: '123456',});// ❌ 编译错误:缺少必需字段// userService.create({//     name: '张三',// });// ❌ 编译错误:错误的角色// userService.create({//     name: '张三',//     email: 'zhangsan@example.com',//     role: 'super-admin',  // Error//     password: '123456',// });```**JavaScript 的风险:**```javascript// ❌ 拼写错误,运行时才发现userService.getAll({    page: 1,    pageSize: 10,    rol: 'admin',  // 拼写错误!});// ❌ 传递了错误的字段类型userService.create({    name: '张三',    email: 'zhangsan@example.com',    role: 12345,  // 应该是字符串!    password: '123456',});```**结论:** 长期维护的企业应用使用 **TypeScript**,类型系统是投资,回报是代码质量。---### 3. 迁移建议#### 从 JavaScript 迁移到 TypeScript**步骤 1:逐步迁移**```typescript// 1. 保持 .js 文件// script.jsfunction greet(name) {    return `Hello, ${name}!`;}``````typescript// 2. 创建同名的 .ts 文件// script.tsfunction greet(name: string): string {    return `Hello, ${name}!`;}```**步骤 2:添加类型定义**```typescript// 定义接口interface User {    id: number;    name: string;    email: string;}// 使用接口function getUser(id: number): User {    // ...}```**步骤 3:启用严格模式**```json// tsconfig.json{    "compilerOptions": {        "strict": true,        "noImplicitAny": true,        "strictNullChecks": true    }}```---## 最佳实践### 1. Script 标签最佳实践```html<!-- ✅ 推荐:放在 body 末尾 --><body>    <divid="app">        <!-- 页面内容 -->    </div>    <scriptsrc="js/app.js"></script></body><!-- ✅ 推荐:使用 defer --><head>    <scriptdefersrc="js/app.js"></script></head><!-- ✅ 推荐:使用模块 --><scripttype="module"src="js/app.js"></script><!-- ❌ 不推荐:放在 head 中且无 defer --><head>    <scriptsrc="js/app.js"></script>  <!-- 阻塞页面加载 --></head>```### 2. JavaScript 最佳实践```javascript// ✅ 使用 const 和 letconst API_URL = 'https://api.example.com';let currentPage = 1;// ❌ 避免使用 varvar api_url = 'https://api.example.com';  // 不推荐// ✅ 使用箭头函数const addUser = (user) => {    console.log(user);};// ✅ 使用模板字符串const message = `你好, ${name}!`;// ✅ 使用解构赋值const { id, name, email } = user;// ✅ 使用展开运算符const newUser = { ...user, age: 25 };// ✅ 使用 Promise 和 async/awaitasync function fetchData() {    try {        const response = await fetch(url);        const data = await response.json();        return data;    } catch (error) {        console.error(error);    }}```### 3. TypeScript 最佳实践```typescript// ✅ 使用接口定义类型interface User {    id: number;    name: string;    email: string;}// ✅ 使用类型别名type UserID = number;type UserRole = 'admin' | 'user' | 'guest';// ✅ 使用枚举enum Status {    Pending = 0,    Active = 1,    Inactive = 2,}// ✅ 使用泛型function identity<T>(arg: T): T {    return arg;}// ✅ 使用类型守卫function isString(value: unknown): value is string {    return typeof value === 'string';}// ✅ 使用 readonly 防止意外修改interface Config {    readonly apiUrl: string;    readonly timeout: number;}// ✅ 使用可选链const email = user?.contact?.email;// ✅ 使用空值合并const name = userName ?? '匿名';```---## 总结### Script 执行时机总结| 场景 | 推荐方式 | 原因 ||------|---------|------|| **操作 DOM** | defer 或 body 末尾 | 确保 DOM 已加载 || **独立脚本** | async | 不阻塞页面加载 || **立即执行** | 无属性 | 需要立即执行的代码 || **模块化** | type="module" | 支持 ES6 模块 || **向后兼容** | nomodule | 兼容旧浏览器 |### JavaScript vs TypeScript**选择 JavaScript:**- 快速原型开发- 学习 JavaScript 基础- 小型项目- 团队不熟悉 TypeScript**选择 TypeScript:**- 大型项目- 需要长期维护- 团队协作- 库/框架开发- 自动化测试---
4.ts和js在后端自动化上的应用:
# JavaScript/TypeScript 后端自动化实战## 详细分析与实操示例---## 目录1. [后端自动化概览](#后端自动化概览)2. [Node.js 环境搭建](#nodejs-环境搭建)3. [Python 环境搭建](#python-环境搭建)4. [场景一:自动化 API 测试](#场景一自动化-api-测试)4. [场景二:数据爬取和处理](#场景二数据爬取和处理)5. [场景三:文件系统自动化](#场景三文件系统自动化)6. [场景四:数据库自动化操作](#场景四数据库自动化操作)7. [场景五:定时任务和调度](#场景五定时任务和调度)8. [场景六:邮件自动化](#场景六邮件自动化)9. [场景七:CI/CD 自动化](#场景七cicd-自动化)10. [场景八:服务器监控和日志](#场景八服务器监控和日志)11. [对比总结](#对比总结)---## 后端自动化概览### JavaScript/TypeScript 在后端自动化的能力| 能力 | 描述 | 常用库 ||------|------|---------|| **API 测试** | 自动测试 REST API | Jest, Supertest || **数据爬取** | 爬取网页数据 | Puppeteer, Playwright, Cheerio || **文件操作** | 自动处理文件 | fs-extra, glob || **数据库操作** | 自动 CRUD 操作 | Sequelize, TypeORM || **定时任务** | 定时执行任务 | node-cron, Agenda || **邮件发送** | 自动发送邮件 | Nodemailer || **CI/CD** | 自动构建部署 | GitHub Actions, GitLab CI || **监控告警** | 服务监控告警 | Winston, Prometheus || **报表生成** | 自动生成报表 | ExcelJS, PDFKit || **自动化部署** | 自动部署应用 | PM2, Docker |---## Node.js 环境搭建### 1. 创建后端自动化项目```bash# 创建项目目录mkdir backend-automationcd backend-automation# 初始化项目npm init -y# 安装常用依赖npm install axios cheerio puppeteer express fs-extra node-cronnpm install --save-dev @types/node typescript ts-node jest supertest```### 2. 项目结构```backend-automation/├── src/│   ├── api/               # API 相关│   │   ├── api-client.ts│   │   └── api-tests.ts│   ├── crawler/            # 爬虫相关│   │   ├── web-crawler.ts│   │   └── data-processor.ts│   ├── file/               # 文件操作│   │   ├── file-manager.ts│   │   └── report-generator.ts│   ├── database/           # 数据库相关│   │   ├── db-connection.ts│   │   └── repository.ts│   ├── scheduler/          # 定时任务│   │   └── jobs.ts│   ├── email/              # 邮件相关│   │   └── mailer.ts│   ├── monitor/            # 监控相关│   │   └── logger.ts│   └── index.ts           # 入口文件├── data/                 # 数据文件├── output/               # 输出文件├── logs/                 # 日志文件├── tests/                # 测试文件├── package.json└── tsconfig.json```---## Python 环境搭建### 1.1 安装 Python 依赖```bash# 创建项目目录mkdir backend-automationcd backend-automation# 创建虚拟环境python -m venv venv# 激活虚拟环境# Windows:venv\Scripts\activate# Mac/Linux:source venv/bin/activate# 安装依赖pip install requests pytest playwright-openpyxl openpyxl pandaspip install --save-dev pytest-playwright```### 1.2 项目结构```backend-automation/├── src/│   ├── api/               # API 相关│   │   ├── api_client.py│   │   └── api_tests.py│   ├── crawler/            # 爬虫相关│   │   └── web_crawler.py│   ├── database/           # 数据库相关│   │   ├── db_connection.py│   │   └── operations.py│   ├── scheduler/          # 定时任务│   │   └── jobs.py│   ├── email/              # 邮件相关│   │   └── mailer.py│   └── monitor/            # 监控相关│       └── logger.py├── tests/                # 测试文件├── data/                 # 数据文件├── output/               # 输出文件├── logs/                 # 日志文件├── requirements.txt└── README.md```---## 场景一:自动化 API 测试### Python 版本:API 客户端封装```python# src/api/api_client.pyimport requestsfrom typing import Any, Dict, List, Optionalfrom datetime import datetime# 响应类型定义class ApiResponse:    def __init__(self, code: int = 0, message: str = '', data: Any = None):        self.code = code        self.message = message        self.data = data    @classmethod    def from_dict(cls, data: Dict):        return cls(            code=data.get('code', 0),            message=data.get('message'''),            data=data.get('data')        )    @property    def is_success(self) -> bool:        return self.code == 200# API 客户端类class ApiClient:    def __init__(self, base_url: str = 'http://localhost:3000/api'):        self.base_url = base_url        self.token: Optional[str] = None        self.session = requests.Session()    def set_token(self, token: str) -> None:        """设置认证令牌"""        self.token = token    def get_headers(self) -> Dict[str, str]:        """获取请求头"""        headers = {            'Content-Type''application/json',            'User-Agent''Python API Client'        }        if self.token:            headers['Authorization'] = f'Bearer {self.token}'        return headers    def request(self, method: str, endpoint: str, **kwargs) -> ApiResponse:        """通用请求方法"""        url = f'{self.base_url}{endpoint}'        try:            response = self.session.request(                method=method,                url=url,                headers=self.get_headers(),                **kwargs            )            response.raise_for_status()            return ApiResponse.from_dict(response.json())        except requests.exceptions.RequestException as e:            print(f'请求失败: {e}')            return ApiResponse(code=500, message=str(e))    def get(self, endpoint: str, params: Dict = None) -> ApiResponse:        """GET 请求"""        return self.request('GET', endpoint, params=params)    def post(self, endpoint: str, data: Any = None) -> ApiResponse:        """POST 请求"""        return self.request('POST', endpoint, json=data)    def put(self, endpoint: str, data: Any = None) -> ApiResponse:        """PUT 请求"""        return self.request('PUT', endpoint, json=data)    def delete(self, endpoint: str) -> ApiResponse:        """DELETE 请求"""        return self.request('DELETE', endpoint)    # ========== 用户 API ==========    def login(self, username: str, password: str) -> ApiResponse:        """登录"""        response = self.post('/auth/login', {            'username': username,            'password': password        })        if response.is_success and 'token' in response.data:            self.set_token(response.data['token'])        return response    def get_current_user(self) -> ApiResponse:        """获取当前用户"""        return self.get('/user/me')    def get_users(self, page: int = 1, page_size: int = 10) -> ApiResponse:        """获取用户列表"""        return self.get('/users', params={            'page': page,            'pageSize': page_size        })    def create_user(self, user: Dict) -> ApiResponse:        """创建用户"""        return self.post('/users', user)    def update_user(self, user_id: int, user: Dict) -> ApiResponse:        """更新用户"""        return self.put(f'/users/{user_id}', user)    def delete_user(self, user_id: int) -> ApiResponse:        """删除用户"""        return self.delete(f'/users/{user_id}')# 使用示例if __name__ == '__main__':    client = ApiClient()    # 登录    login_result = client.login('admin''admin123')    print(f'登录结果: {login_result.is_success}')    # 获取用户列表    users_result = client.get_users(1, 10)    print(f'用户列表: {users_result.data}')```### Python 版本:自动化测试(使用 pytest)```python# tests/api_tests.pyimport pytestimport requestsfrom typing import Dict, Listfrom datetime import datetime, timedeltaimport json# API 客户端(单例模式)api_client = None@pytest.fixture(scope='session')def api_client_fixture():    """创建 API 客户端"""    global api_client    api_client = ApiClient('http://localhost:3000/api')    return api_client# ========== 认证测试 ==========def test_login_success(api_client_fixture):    """测试成功登录"""    response = api_client_fixture.login('admin''admin123')    assert response.is_success    assert 'token' in response.data    print('✅ 登录测试通过')def test_login_failure(api_client_fixture):    """测试失败登录"""    response = api_client_fixture.login('wrong''wrong')    assert not response.is_success    assert 'token' not in response.data    print('✅ 登录失败测试通过')# ========== 用户管理测试 ==========def test_get_users(api_client_fixture):    """测试获取用户列表"""    response = api_client_fixture.get_users(1, 10)    assert response.is_success    assert response.data is not None    assert len(response.data) <= 10    print(f'✅ 获取用户列表测试通过,共 {len(response.data)} 个用户')def test_create_user(api_client_fixture):    """测试创建用户"""    new_user = {        'username''testuser',        'email''test@example.com',        'password''hashed_password',        'role''user'    }    response = api_client_fixture.create_user(new_user)    assert response.is_success    assert response.data['username'] == 'testuser'    print('✅ 创建用户测试通过')def test_update_user(api_client_fixture):    """测试更新用户"""    updates = {'email''updated@example.com'}    response = api_client_fixture.update_user(1, updates)    assert response.is_success    assert response.data['email'] == 'updated@example.com'    print('✅ 更新用户测试通过')def test_delete_user(api_client_fixture):    """测试删除用户"""    response = api_client_fixture.delete_user(999)    assert response.code == 404    print('✅ 删除用户测试通过')# ========== 并发测试 ==========@pytest.mark.parametrize('count', [1, 5, 10])def test_concurrent_requests(api_client_fixture, count):    """测试并发请求"""    import concurrent.futures    def get_users_request():        return api_client_fixture.get_users(1, 10)    start_time = datetime.now()    with concurrent.futures.ThreadPoolExecutor(max_workers=count) as executor:        futures = [executor.submit(get_users_request) for _ in range(count)]        results = [f.result() for f in concurrent.futures.as_completed(futures)]    duration = (datetime.now() - start_time).total_seconds()    print(f'✅ 并发测试通过:{count} 个请求,耗时 {duration:.2f}s')    assert duration < 5.0  # 响应时间应小于5秒# ========== 数据驱动测试 ==========@pytest.mark.parametrize('username,password,expected', [    ('admin''admin123', True),    ('wrong''wrong', False),    ('''', False),])def test_login_with_credentials(username, password, expected):    """数据驱动的登录测试"""    from src.api.api_client import ApiClient    client = ApiClient()    response = client.login(username, password)    if expected:        assert response.is_success        assert 'token' in response.data    else:        assert not response.is_success    print(f'✅ 登录参数化测试通过: {username}, 期望={expected}')# ========== 性能测试 ==========def test_api_performance(api_client_fixture):    """测试 API 性能"""    response_times = []    for i in range(10):        start = datetime.now()        api_client_fixture.get_users(1, 10)        duration = (datetime.now() - start_time).total_seconds()        response_times.append(duration)    avg_time = sum(response_times) / len(response_times)    max_time = max(response_times)    print(f'✅ API 性能测试通过')    print(f'   平均响应时间: {avg_time:.3f}s')    print(f'   最大响应时间: {max_time:.3f}s')    assert avg_time < 1.0  # 平均响应时间应小于1秒# ========== 测试报告生成 ==========@pytest.hookimpl(autouse=True)def generate_report(request):    """生成测试报告"""    yield  # 测试执行    # 测试完成后生成报告    test_results = {        'timestamp': datetime.now().isoformat(),        'total_tests': len([node for node in request.node.items]),        'passed': 0,        'failed': 0,        'results': []    }    for node in request.node.items:        for result in node.result:            if hasattr(result, 'call_result'):                test_results['results'].append({                    'test_name': node.name,                    'passed': result.passed,                    'duration': result.call_result.duration if hasattr(result.call_result, 'duration'else 0                })                if result.passed:                    test_results['passed'] += 1                else:                    test_results['failed'] += 1    # 生成 HTML 报告    html = f"""<!DOCTYPE html><html lang="zh-CN"><head>    <meta charset="UTF-8">    <title>Python API 测试报告</title>    <style>        body {{ font-family: Arial, sans-serif; padding: 20px; }}        .summary {{ background: #f0f0f0; padding: 20px; margin-bottom: 20px; border-radius: 5px; }}        .test-item {{ margin: 10px 0; padding: 10px; border-left: 4px solid #ddd; }}        .pass {{ border-left-color: #4CAF50; background: #e8f5e9; }}        .fail {{ border-left-color: #f44336; background: #ffebee; }}        .duration {{ color: #666; font-size: 12px; }}    </style></head><body>    <h1>Python API 自动化测试报告</h1>    <p>生成时间: {test_results['timestamp']}</p>    <div class="summary">        <h2>测试摘要</h2>        <p>总测试数: {test_results['total']}</p>        <p>通过: {test_results['passed']}</p>        <p>失败: {test_results['failed']}</p>        <p>通过率: {(test_results['passed'] / test_results['total'] * 100 if test_results['total'] > 0 else 0:.1f}%</p>    </div>    <h2>详细结果</h2>    {"".join([f"""        <div class="test-item {''}{'pass' if r['passed'] else 'fail'}}">            <strong>{r['test_name']}</strong>            <span class="duration">({r['duration']}ms)</span>        </div>        """ for r in test_results['results']])}</body></html>    """    # 保存报告    import os    output_dir = '../output/tests'    os.makedirs(output_dir, exist_ok=True)    report_path = f'{output_dir}/test-report-{datetime.now().strftime("%Y%m%d_%H%M%S")}.html'    with open(report_path, 'w', encoding='utf-8') as f:        f.write(html)    print(f'测试报告已生成: {report_path}')# ========== 主程序入口 ==========def main():    """运行所有测试并生成报告"""    # 导入客户端    from src.api.api_client import ApiClient    # 创建测试客户端    client = ApiClient('http://localhost:3000/api')    print('开始 Python API 自动化测试...')    # 简单的测试循环    test_cases = [        ('登录测试', client.login('admin''admin123')),        ('获取用户列表', client.get_users(1, 10)),        ('创建用户', client.create_user({            'username''testuser',            'email''test@example.com',            'password''pass123',            'role''user'        })),    ]    for test_name, test_fn in test_cases:        try:            print(f'执行: {test_name}...')            result = test_fn()            if result.is_success:                print(f'  ✅ 成功')            else:                print(f'  ❌ 失败: {result.message}')        except Exception as e:            print(f'  ❌ 错误: {e}')    print('\n所有测试完成!')if __name__ == '__main__':    main()```### Python 版本:Playwright 自动化测试```python# tests/playwright_tests.pyfrom playwright.sync_api import sync_playwrightfrom typing import List, Dictclass LoginPage:    """登录页面对象"""    def __init__(self, page):        self.page = page        self.url = 'http://localhost:3000/login'        self.selectors = {            'username''#username',            'password''#password',            'login_button''#login-btn',            'error_message''.error-message',            'welcome_message''.welcome-message',        }    def navigate(self):        """导航到登录页"""        self.page.goto(self.url)    def fill_credentials(self, username: str, password: str):        """填写凭据"""        self.page.fill(self.selectors['username'], username)        self.page.fill(self.selectors['password'], password)    def click_login(self):        """点击登录按钮"""        self.page.click(self.selectors['login_button'])    def get_error_message(self) -> str:        """获取错误消息"""        return self.page.text_content(self.selectors['error_message']) or ''    def is_welcome_visible(self) -> bool:        """检查欢迎消息是否可见"""        return self.page.is_visible(self.selectors['welcome_message'])def test_login_with_valid_credentials(page):    """测试有效凭据登录"""    login_page = LoginPage(page)    login_page.navigate()    login_page.fill_credentials('admin''admin123')    login_page.click_login()    # 验证跳转    page.wait_for_url('**/dashboard'timeout=5000)    assert '/dashboard' in page.url    print('✅ 有效凭据登录测试通过')def test_login_with_invalid_credentials(page):    """测试无效凭据登录"""    login_page = LoginPage(page)    login_page.navigate()    login_page.fill_credentials('wrong''wrong')    login_page.click_login()    # 等待错误消息    page.wait_for_selector('.error-message'timeout=5000)    error_msg = login_page.get_error_message()    assert error_msg != ''    print('✅ 无效凭据登录测试通过')def test_form_validation(page):    """测试表单验证"""    login_page = LoginPage(page)    login_page.navigate()    # 直接点击登录(不填写)    login_page.click_login()    # 等待验证错误    page.wait_for_selector('.validation-error'timeout=5000)    assert page.is_visible('.validation-error')    print('✅ 表单验证测试通过')# ========== 数据驱动测试 ==========test_data = [    ('admin''admin123', True, '成功登录'),    ('user''user123', True, '成功登录'),    ('wrong''wrong', False, '登录失败'),    ('''', False, '空凭据'),]@pytest.mark.parametrize('username,password,should_succeed,description', test_data)def test_login_scenarios(page, username, password, should_succeed, description):    """数据驱动登录测试"""    login_page = LoginPage(page)    login_page.navigate()    login_page.fill_credentials(username, password)    login_page.click_login()    if should_succeed:        page.wait_for_url('**/dashboard'timeout=5000)        assert '/dashboard' in page.url    else:        page.wait_for_selector('.error-message'timeout=5000)        error_msg = login_page.get_error_message()        assert '错误' in error_msg    print(f'✅ 场景测试通过: {description}')def test_multiple_scenarios(page):    """测试多个场景"""    scenarios = [        {            'name''管理员登录',            'username''admin',            'password''admin123',            'should_succeed': True        },        {            'name''普通用户登录',            'username''user',            'password''user123',            'should_succeed': True        },        {            'name''错误密码',            'username''admin',            'password''wrong',            'should_succeed': False        },    ]    for scenario in scenarios:        page.goto('http://localhost:3000/login')        page.fill('#username', scenario['username'])        page.fill('#password', scenario['password'])        page.click('#login-btn')        if scenario['should_succeed']:            page.wait_for_url('**/dashboard')            assert '/dashboard' in page.url        else:            page.wait_for_selector('.error-message')            error_msg = page.text_content('.error-message')            assert '错误' in error_msg        print(f'✅ 场景测试通过: {scenario["name"]}')if __name__ == '__main__':    # 直接运行测试    with sync_playwright() as p:        browser = p.chromium.launch(headless=False)        page = browser.new_page()        # 运行单个测试        test_login_with_valid_credentials(page)        page.reload()        # 运行数据驱动测试        test_login_scenarios(page)        browser.close()```---## 场景一:自动化 API 测试### 1.1 API 客户端封装```typescript// src/api/api-client.ts// 响应类型定义interface ApiResponse<T = any> {    code: number;    message: string;    data: T;}interface User {    id: number;    username: string;    email: string;    role: string;    createdAt: string;}interface LoginData {    username: string;    password: string;}interface LoginResponse {    token: string;    user: User;}// API 客户端类class ApiClient {    private baseURL: string;    private token: string | null = null;    constructor(baseURL: string = 'http://localhost:3000/api') {        this.baseURL = baseURL;    }    // 设置认证令牌    setToken(token: string): void {        this.token = token;    }    // 获取请求头    private getHeaders(): Record<string, string> {        const headers: Record<string, string> = {            'Content-Type''application/json',        };        if (this.token) {            headers['Authorization'] = `Bearer ${this.token}`;        }        return headers;    }    // 通用请求方法    private async request<T>(        endpoint: string,        options: RequestInit = {}    ): Promise<ApiResponse<T>> {        const url = `${this.baseURL}${endpoint}`;        const response = await fetch(url, {            headers: this.getHeaders(),            ...options,        });        if (!response.ok) {            const errorData: ApiResponse = await response.json();            throw new Error(errorData.message || '请求失败');        }        return response.json();    }    // GET 请求    async get<T>(endpoint: string): Promise<ApiResponse<T>> {        return this.request<T>(endpoint, { method: 'GET' });    }    // POST 请求    async post<T>(endpoint: string, data: any): Promise<ApiResponse<T>> {        return this.request<T>(endpoint, {            method: 'POST',            body: JSON.stringify(data),        });    }    // PUT 请求    async put<T>(endpoint: string, data: any): Promise<ApiResponse<T>> {        return this.request<T>(endpoint, {            method: 'PUT',            body: JSON.stringify(data),        });    }    // DELETE 请求    async delete<T>(endpoint: string): Promise<ApiResponse<T>> {        return this.request<T>(endpoint, { method: 'DELETE' });    }    // ========== 用户 API ==========    // 登录    async login(data: LoginData): Promise<ApiResponse<LoginResponse>> {        const response = await this.post<LoginResponse>('/auth/login', data);        if (response.code === 200 && response.data.token) {            this.setToken(response.data.token);        }        return response;    }    // 获取当前用户    async getCurrentUser(): Promise<ApiResponse<User>> {        return this.get<User>('/user/me');    }    // 获取用户列表    async getUsers(page: number = 1, pageSize: number = 10): Promise<ApiResponse<User[]>> {        return this.get<User[]>(`/users?page=${page}&pageSize=${pageSize}`);    }    // 创建用户    async createUser(user: Partial<User>): Promise<ApiResponse<User>> {        return this.post<User>('/users', user);    }    // 更新用户    async updateUser(id: number, user: Partial<User>): Promise<ApiResponse<User>> {        return this.put<User>(`/users/${id}`, user);    }    // 删除用户    async deleteUser(id: number): Promise<ApiResponse<void>> {        return this.delete<void>(`/users/${id}`);    }}// 导出单例export const apiClient = new ApiClient('http://localhost:3000/api');```### 1.2 自动化 API 测试```typescript// src/api/api-tests.tsimport { apiClient } from './api-client';import * as fs from 'fs';import * as path from 'path';// 测试结果接口interface TestResult {    testCase: string;    success: boolean;    duration: number;    error?: string;}// 测试套件class ApiTestSuite {    private results: TestResult[] = [];    // 记录测试结果    private recordResult(        testCase: string,        success: boolean,        duration: number,        error?: string    ): void {        this.results.push({ testCase, success, duration, error });    }    // 执行测试并计时    private async executeTest(        testCase: string,        testFn: () => Promise<void>    ): Promise<void> {        const startTime = Date.now();        try {            await testFn();            const duration = Date.now() - startTime;            this.recordResult(testCase, true, duration);            console.log(`✅ ${testCase} - 通过 (${duration}ms)`);        } catch (error) {            const duration = Date.now() - startTime;            this.recordResult(testCase, false, duration, String(error));            console.error(`❌ ${testCase} - 失败: ${error}`);        }    }    // ========== 用户认证测试 ==========    async testAuth(): Promise<void> {        await this.executeTest('登录 - 正确的凭据', async () => {            const result = await apiClient.login({                username: 'admin',                password: 'admin123',            });            if (result.code !== 200) {                throw new Error('登录失败');            }        });        await this.executeTest('登录 - 错误的凭据', async () => {            try {                await apiClient.login({                    username: 'wrong',                    password: 'wrong',                });                throw new Error('应该抛出错误');            } catch (error) {                if (!String(error).includes('请求失败')) {                    throw new Error('错误类型不正确');                }            }        });    }    // ========== 用户管理测试 ==========    async testUserManagement(): Promise<void> {        await this.executeTest('获取用户列表', async () => {            const result = await apiClient.getUsers(1, 10);            if (result.code !== 200 || !result.data) {                throw new Error('获取用户列表失败');            }        });        await this.executeTest('创建用户', async () => {            const result = await apiClient.createUser({                username: 'testuser',                email: 'test@example.com',                role: 'user',            });            if (result.code !== 201) {                throw new Error('创建用户失败');            }        });        await this.executeTest('更新用户', async () => {            const result = await apiClient.updateUser(1, {                email: 'updated@example.com',            });            if (result.code !== 200) {                throw new Error('更新用户失败');            }        });        await this.executeTest('删除用户', async () => {            const result = await apiClient.deleteUser(999);            // 999 不存在的用户            if (result.code !== 404) {                throw new Error('删除用户应该返回 404');            }        });    }    // ========== 并发测试 ==========    async testConcurrency(): Promise<void> {        await this.executeTest('并发请求测试', async () => {            const requests = Array(10).fill(0).map((_, i) =>                apiClient.getUsers(i + 110)            );            const startTime = Date.now();            await Promise.all(requests);            const duration = Date.now() - startTime;            console.log(`10 个并发请求完成时间: ${duration}ms`);            if (duration > 5000) {                throw new Error('并发性能不达标');            }        });    }    // ========== 错误处理测试 ==========    async testErrorHandling(): Promise<void> {        await this.executeTest('无效的请求参数', async () => {            try {                await apiClient.getUsers(-1, -1);                throw new Error('应该处理无效参数');            } catch (error) {                // 预期会抛出错误                if (!String(error).includes('请求失败')) {                    throw new Error('错误处理不正确');                }            }        });        await this.executeTest('不存在的资源', async () => {            const result = await apiClient.getUser(99999);            if (result.code !== 404) {                throw new Error('应该返回 404');            }        });    }    // 生成测试报告    generateReport(): void {        const reportPath = path.join(__dirname, '../../output/api-test-report.html');        const totalTests = this.results.length;        const passedTests = this.results.filter(r => r.success).length;        const failedTests = totalTests - passedTests;        const passRate = ((passedTests / totalTests) * 100).toFixed(2);        const avgDuration = (            this.results.reduce((sum, r) => sum + r.duration, 0) / totalTests        ).toFixed(2);        const html = `<!DOCTYPE html><html lang="zh-CN"><head>    <meta charset="UTF-8">    <title>API 测试报告</title>    <style>        body { font-family: Arial, sans-serif; padding: 20px; }        .summary { background: #f0f0f0; padding: 20px; margin-bottom: 20px; }        .test-item { margin: 10px 0; padding: 10px; border-left: 4px solid #ddd; }        .pass { border-left-color: #4CAF50; background: #e8f5e9; }        .fail { border-left-color: #f44336; background: #ffebee; }        .error { color: #f44336; margin-top: 5px; }        .duration { color: #666; font-size: 12px; }    </style></head><body>    <h1>API 自动化测试报告</h1>    <p>生成时间: ${new Date().toLocaleString()}</p>    <div class="summary">        <h2>测试摘要</h2>        <p>总测试数: ${totalTests}</p>        <p>通过: ${passedTests}</p>        <p>失败: ${failedTests}</p>        <p>通过率: ${passRate}%</p>        <p>平均耗时: ${avgDuration}ms</p>    </div>    <h2>详细结果</h2>    ${this.results.map(result => `        <div class="test-item ${result.success ? 'pass' : 'fail'}">            <strong>${result.testCase}</strong>            <span class="duration">(${result.duration}ms)</span>            ${!result.success ? `<div class="error">错误: ${result.error}</div>` : ''}        </div>    `).join('')}</body></html>        `;        // 确保输出目录存在        const outputDir = path.join(__dirname, '../../output');        if (!fs.existsSync(outputDir)) {            fs.mkdirSync(outputDir, { recursive: true });        }        fs.writeFileSync(reportPath, html);        console.log(`报告已生成: ${reportPath}`);    }    // 运行所有测试    async runAll(): Promise<void> {        console.log('开始 API 自动化测试...\n');        await this.testAuth();        await this.testUserManagement();        await this.testConcurrency();        await this.testErrorHandling();        console.log('\n所有测试完成!');        this.generateReport();    }}// 使用示例async function main() {    const testSuite = new ApiTestSuite();    await testSuite.runAll();}main().catch(console.error);```### 1.3 使用 Supertest 进行 API 测试```typescript// tests/api.test.tsimport request from 'supertest';import express from 'express';import { expect } from 'chai';// 模拟 Express 应用const app = express();app.use(express.json());// 模拟用户数据const users: any[] = [    { id: 1, name: '张三', email: 'zhangsan@example.com' },    { id: 2, name: '李四', email: 'lisi@example.com' },];// API 路由app.get('/api/users', (req, res) => {    res.json({ code: 200, data: users });});app.get('/api/users/:id', (req, res) => {    const user = users.find(u => u.id === parseInt(req.params.id));    if (user) {        res.json({ code: 200, data: user });    } else {        res.status(404).json({ code: 404, message: '用户不存在' });    }});app.post('/api/users', (req, res) => {    const newUser = { id: users.length + 1, ...req.body };    users.push(newUser);    res.status(201).json({ code: 201, data: newUser });});app.put('/api/users/:id', (req, res) => {    const index = users.findIndex(u => u.id === parseInt(req.params.id));    if (index !== -1) {        users[index] = { ...users[index], ...req.body };        res.json({ code: 200, data: users[index] });    } else {        res.status(404).json({ code: 404, message: '用户不存在' });    }});app.delete('/api/users/:id', (req, res) => {    const index = users.findIndex(u => u.id === parseInt(req.params.id));    if (index !== -1) {        users.splice(index, 1);        res.json({ code: 200, message: '删除成功' });    } else {        res.status(404).json({ code: 404, message: '用户不存在' });    }});// 测试套件describe('User API 自动化测试', () => {    // 获取用户列表    it('应该返回用户列表', async () => {        const response = await request(app)            .get('/api/users')            .expect(200);        expect(response.body.code).to.equal(200);        expect(response.body.data).to.be.an('array');        expect(response.body.data).to.have.lengthOf(2);    });    // 获取单个用户    it('应该返回指定用户', async () => {        const response = await request(app)            .get('/api/users/1')            .expect(200);        expect(response.body.code).to.equal(200);        expect(response.body.data.name).to.equal('张三');    });    // 创建用户    it('应该成功创建用户', async () => {        const newUser = {            name: '王五',            email: 'wangwu@example.com'        };        const response = await request(app)            .post('/api/users')            .send(newUser)            .expect(201);        expect(response.body.code).to.equal(201);        expect(response.body.data.name).to.equal('王五');    });    // 更新用户    it('应该成功更新用户', async () => {        const updates = { name: '赵六' };        const response = await request(app)            .put('/api/users/1')            .send(updates)            .expect(200);        expect(response.body.code).to.equal(200);        expect(response.body.data.name).to.equal('赵六');    });    // 删除用户    it('应该成功删除用户', async () => {        await request(app)            .delete('/api/users/2')            .expect(200);        const response = await request(app)            .get('/api/users/2')            .expect(404);    });    // 错误处理    it('应该返回 404 对于不存在的用户', async () => {        await request(app)            .get('/api/users/999')            .expect(404);    });    // 参数验证    it('应该验证必填字段', async () => {        const response = await request(app)            .post('/api/users')            .send({ name: '测试' })  // 缺少 email            .expect(400);        // 假设有参数验证中间件    });    // 并发请求    it('应该处理并发请求', async () => {        const requests = [            request(app).get('/api/users').expect(200),            request(app).get('/api/users').expect(200),            request(app).get('/api/users').expect(200),        ];        await Promise.all(requests);    });});```---## 场景二:数据爬取和处理### 2.1 使用 Cheerio 爬取静态网页```typescript// src/crawler/web-crawler.tsimport * as cheerio from 'cheerio';import axios from 'axios';import * as fs from 'fs';import * as path from 'path';// 爬取的数据类型interface Article {    title: string;    url: string;    author: string;    date: string;    summary: string;}// 网页爬虫类class WebCrawler {    private results: Article[] = [];    // 爬取网页    async crawl(url: string): Promise<Article[]> {        console.log(`正在爬取: ${url}`);        try {            const response = await axios.get(url, {                headers: {                    'User-Agent''Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',                },            });            const $ = cheerio.load(response.data);            const articles: Article[] = [];            // 解析文章列表            $('.article').each((index, element) => {                const $element = $(element);                const article: Article = {                    title: $element.find('h2').text().trim(),                    url: new URL($element.find('a').attr('href') || '', url).href,                    author: $element.find('.author').text().trim(),                    date: $element.find('.date').text().trim(),                    summary: $element.find('.summary').text().trim(),                };                articles.push(article);            });            this.results = articles;            console.log(`爬取完成,共 ${articles.length} 篇文章`);            return articles;        } catch (error) {            console.error('爬取失败:', error);            throw error;        }    }    // 批量爬取多个页面    async crawlMultiple(urls: string[]): Promise<Article[]> {        console.log(`开始爬取 ${urls.length} 个页面...`);        const allArticles: Article[] = [];        for (let i = 0; i < urls.length; i++) {            try {                const articles = await this.crawl(urls[i]);                allArticles.push(...articles);                // 延迟,避免请求过快                if (i < urls.length - 1) {                    await this.sleep(1000);                }            } catch (error) {                console.error(`爬取 ${urls[i]} 失败:`, error);            }        }        console.log(`批量爬取完成,共 ${allArticles.length} 篇文章`);        return allArticles;    }    // 延迟函数    private sleep(ms: number): Promise<void> {        return new Promise(resolve => setTimeout(resolve, ms));    }    // 保存结果到 JSON    saveToJson(filePath: string): void {        fs.writeFileSync(            filePath,            JSON.stringify(this.results, null, 2),            'utf-8'        );        console.log(`数据已保存到: ${filePath}`);    }    // 保存结果到 CSV    saveToCsv(filePath: string): void {        const header = ['title''url''author''date''summary'].join(',');        const rows = this.results.map(article =>            [                `"${article.title.replace(/"/g, '""')}"`,                `"${article.url}"`,                `"${article.author}"`,                `"${article.date}"`,                `"${article.summary.replace(/"/g, '""')}"`            ].join(',')        );        const csv = [header, ...rows].join('\n');        fs.writeFileSync(filePath, csv, 'utf-8');        console.log(`CSV 已保存到: ${filePath}`);    }}// 使用示例async function main() {    const crawler = new WebCrawler();    // 爬取单个页面    const articles = await crawler.crawl('https://example.com/articles');    // 保存结果    const outputDir = path.join(__dirname, '../../output');    if (!fs.existsSync(outputDir)) {        fs.mkdirSync(outputDir, { recursive: true });    }    crawler.saveToJson(path.join(outputDir, 'articles.json'));    crawler.saveToCsv(path.join(outputDir, 'articles.csv'));    // 批量爬取    const urls = [        'https://example.com/articles?page=1',        'https://example.com/articles?page=2',        'https://example.com/articles?page=3',    ];    const allArticles = await crawler.crawlMultiple(urls);}main().catch(console.error);```### 2.2 使用 Puppeteer 爬取动态网页```typescript// src/crawler/dynamic-crawler.tsimport puppeteer from 'puppeteer';import * as fs from 'fs';import * as path from 'path';interface Product {    name: string;    price: string;    rating: string;    reviews: number;    imageUrl: string;}// 动态网页爬虫class DynamicCrawler {    async crawlProductPage(url: string): Promise<Product[]> {        console.log(`启动浏览器,访问: ${url}`);        const browser = await puppeteer.launch({            headless: false,  // 显示浏览器,方便调试        });        const page = await browser.newPage();        // 设置视口        await page.setViewport({ width: 1920, height: 1080 });        // 拦截请求(可选)        await page.setRequestInterception(true);        page.on('request', (request) => {            if (['image''font'].includes(request.resourceType())) {                request.abort();            } else {                request.continue();            }        });        try {            // 导航到页面            await page.goto(url, {                waitUntil: 'networkidle2',                timeout: 30000,            });            // 滚动到底部,加载更多内容            await page.evaluate(() => {                window.scrollTo(0, document.body.scrollHeight);            });            // 等待动态内容加载            await page.waitForTimeout(2000);            // 提取产品数据            const products = await page.evaluate(() => {                const items = document.querySelectorAll('.product-card');                const data: Product[] = [];                items.forEach((item) => {                    const nameElement = item.querySelector('.product-name');                    const priceElement = item.querySelector('.product-price');                    const ratingElement = item.querySelector('.rating');                    const reviewsElement = item.querySelector('.reviews-count');                    const imageElement = item.querySelector('img');                    if (nameElement && priceElement) {                        data.push({                            name: nameElement.textContent?.trim() || '',                            price: priceElement.textContent?.trim() || '',                            rating: ratingElement?.getAttribute('data-rating') || '',                            reviews: parseInt(reviewsElement?.textContent || '0'),                            imageUrl: imageElement?.src || '',                        });                    }                });                return data;            });            console.log(`提取到 ${products.length} 个产品`);            return products;        } catch (error) {            console.error('爬取失败:', error);            throw error;        } finally {            await browser.close();        }    }    // 截图页面    async screenshotPage(url: string, outputPath: string): Promise<void> {        const browser = await puppeteer.launch();        const page = await browser.newPage();        try {            await page.goto(url, { waitUntil: 'networkidle2' });            await page.screenshot({                path: outputPath,                fullPage: true,            });            console.log(`截图已保存: ${outputPath}`);        } finally {            await browser.close();        }    }    // 生成 PDF    async generatePdf(url: string, outputPath: string): Promise<void> {        const browser = await puppeteer.launch();        const page = await browser.newPage();        try {            await page.goto(url, { waitUntil: 'networkidle2' });            await page.pdf({                path: outputPath,                format: 'A4',                printBackground: true,            });            console.log(`PDF 已生成: ${outputPath}`);        } finally {            await browser.close();        }    }    // 自动填写表单并提交    async fillAndSubmitForm(        url: string,        formData: Record<string, string>    ): Promise<void> {        const browser = await puppeteer.launch({ headless: false });        const page = await browser.newPage();        try {            await page.goto(url);            // 填写表单            for (const [selector, value] of Object.entries(formData)) {                await page.fill(selector, value);                await page.waitForTimeout(500);            }            // 提交表单            await page.click('button[type="submit"]');            // 等待响应            await page.waitForNavigation({ waitUntil: 'networkidle0' });            console.log('表单提交成功');        } catch (error) {            console.error('表单操作失败:', error);            throw error;        } finally {            await browser.close();        }    }}// 使用示例async function main() {    const crawler = new DynamicCrawler();    const outputDir = path.join(__dirname, '../../output');    if (!fs.existsSync(outputDir)) {        fs.mkdirSync(outputDir, { recursive: true });    }    // 爬取产品页面    const products = await crawler.crawlProductPage('https://example.com/products');    // 保存产品数据    fs.writeFileSync(        path.join(outputDir, 'products.json'),        JSON.stringify(products, null, 2)    );    // 截图页面    await crawler.screenshotPage(        'https://example.com/products',        path.join(outputDir, 'screenshot.png')    );    // 生成 PDF    await crawler.generatePdf(        'https://example.com/products',        path.join(outputDir, 'page.pdf')    );    // 自动填写表单    await crawler.fillAndSubmitForm('https://example.com/contact', {        '#name''张三',        '#email''zhangsan@example.com',        '#message''这是测试消息',    });}main().catch(console.error);```---## 场景三:文件系统自动化### 3.1 文件管理器```typescript// src/file/file-manager.tsimport * as fs from 'fs';import * as path from 'path';// 文件操作工具类class FileManager {    // ========== 读取文件 ==========    // 读取文本文件    readTextFile(filePath: string): string {        if (!fs.existsSync(filePath)) {            throw new Error(`文件不存在: ${filePath}`);        }        return fs.readFileSync(filePath, 'utf-8');    }    // 读取 JSON 文件    readJsonFile<T>(filePath: string): T {        const content = this.readTextFile(filePath);        return JSON.parse(content);    }    // 读取目录中的所有文件    readDirectory(dirPath: string): string[] {        if (!fs.existsSync(dirPath)) {            throw new Error(`目录不存在: ${dirPath}`);        }        return fs.readdirSync(dirPath);    }    // 递归读取目录    readDirectoryRecursive(dirPath: string): string[] {        const files: string[] = [];        const read = (dir: string) => {            const items = fs.readdirSync(dir);            items.forEach(item => {                const fullPath = path.join(dir, item);                const stat = fs.statSync(fullPath);                if (stat.isDirectory()) {                    read(fullPath);                } else {                    files.push(fullPath);                }            });        };        read(dirPath);        return files;    }    // ========== 写入文件 ==========    // 写入文本文件    writeTextFile(filePath: string, content: string): void {        const dir = path.dirname(filePath);        if (!fs.existsSync(dir)) {            fs.mkdirSync(dir, { recursive: true });        }        fs.writeFileSync(filePath, content, 'utf-8');        console.log(`文件已写入: ${filePath}`);    }    // 写入 JSON 文件    writeJsonFile(filePath: string, data: any): void {        const content = JSON.stringify(data, null, 2);        this.writeTextFile(filePath, content);    }    // 追加内容到文件    appendFile(filePath: string, content: string): void {        const dir = path.dirname(filePath);        if (!fs.existsSync(dir)) {            fs.mkdirSync(dir, { recursive: true });        }        fs.appendFileSync(filePath, content + '\n''utf-8');    }    // ========== 文件操作 ==========    // 复制文件    copyFile(srcPath: string, destPath: string): void {        if (!fs.existsSync(srcPath)) {            throw new Error(`源文件不存在: ${srcPath}`);        }        const destDir = path.dirname(destPath);        if (!fs.existsSync(destDir)) {            fs.mkdirSync(destDir, { recursive: true });        }        fs.copyFileSync(srcPath, destPath);        console.log(`文件已复制: ${srcPath} -> ${destPath}`);    }    // 移动文件    moveFile(srcPath: string, destPath: string): void {        if (!fs.existsSync(srcPath)) {            throw new Error(`源文件不存在: ${srcPath}`);        }        const destDir = path.dirname(destPath);        if (!fs.existsSync(destDir)) {            fs.mkdirSync(destDir, { recursive: true });        }        fs.renameSync(srcPath, destPath);        console.log(`文件已移动: ${srcPath} -> ${destPath}`);    }    // 删除文件    deleteFile(filePath: string): void {        if (!fs.existsSync(filePath)) {            throw new Error(`文件不存在: ${filePath}`);        }        fs.unlinkSync(filePath);        console.log(`文件已删除: ${filePath}`);    }    // ========== 目录操作 ==========    // 创建目录    createDirectory(dirPath: string): void {        if (!fs.existsSync(dirPath)) {            fs.mkdirSync(dirPath, { recursive: true });            console.log(`目录已创建: ${dirPath}`);        } else {            console.log(`目录已存在: ${dirPath}`);        }    }    // 删除目录    deleteDirectory(dirPath: string): void {        if (!fs.existsSync(dirPath)) {            throw new Error(`目录不存在: ${dirPath}`);        }        fs.rmSync(dirPath, { recursive: true, force: true });        console.log(`目录已删除: ${dirPath}`);    }    // 清空目录    clearDirectory(dirPath: string): void {        if (!fs.existsSync(dirPath)) {            throw new Error(`目录不存在: ${dirPath}`);        }        const files = fs.readdirSync(dirPath);        files.forEach(file => {            const filePath = path.join(dirPath, file);            const stat = fs.statSync(filePath);            if (stat.isDirectory()) {                this.deleteDirectory(filePath);            } else {                this.deleteFile(filePath);            }        });        console.log(`目录已清空: ${dirPath}`);    }    // ========== 文件信息 ==========    // 获取文件信息    getFileInfo(filePath: string) {        if (!fs.existsSync(filePath)) {            throw new Error(`文件不存在: ${filePath}`);        }        const stats = fs.statSync(filePath);        return {            path: filePath,            size: stats.size,            created: stats.birthtime,            modified: stats.mtime,            isDirectory: stats.isDirectory(),            isFile: stats.isFile(),        };    }    // 获取目录大小    getDirectorySize(dirPath: string): number {        let totalSize = 0;        const calculate = (dir: string) => {            const files = fs.readdirSync(dir);            files.forEach(file => {                const filePath = path.join(dir, file);                const stats = fs.statSync(filePath);                if (stats.isDirectory()) {                    calculate(filePath);                } else {                    totalSize += stats.size;                }            });        };        calculate(dirPath);        return totalSize;    }    // 格式化文件大小    formatFileSize(bytes: number): string {        if (bytes === 0) return '0 Bytes';        const k = 1024;        const sizes = ['Bytes''KB''MB''GB'];        const i = Math.floor(Math.log(bytes) / Math.log(k));        return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];    }    // ========== 文件搜索 ==========    // 按名称搜索文件    searchByName(dirPath: string, pattern: string): string[] {        const files: string[] = [];        const regex = new RegExp(pattern, 'i');        const search = (dir: string) => {            const items = fs.readdirSync(dir);            items.forEach(item => {                const fullPath = path.join(dir, item);                const stat = fs.statSync(fullPath);                if (stat.isDirectory()) {                    search(fullPath);                } else if (regex.test(item)) {                    files.push(fullPath);                }            });        };        search(dirPath);        return files;    }    // 按扩展名搜索文件    searchByExtension(dirPath: string, extension: string): string[] {        const files: string[] = [];        const search = (dir: string) => {            const items = fs.readdirSync(dir);            items.forEach(item => {                const fullPath = path.join(dir, item);                const stat = fs.statSync(fullPath);                if (stat.isDirectory()) {                    search(fullPath);                } else if (item.endsWith(extension)) {                    files.push(fullPath);                }            });        };        search(dirPath);        return files;    }    // 按内容搜索文件    searchByContent(dirPath: string, content: string): string[] {        const files: string[] = [];        const search = (dir: string) => {            const items = fs.readdirSync(dir);            items.forEach(item => {                const fullPath = path.join(dir, item);                const stat = fs.statSync(fullPath);                if (stat.isDirectory()) {                    search(fullPath);                } else if (stat.isFile()) {                    const fileContent = fs.readFileSync(fullPath, 'utf-8');                    if (fileContent.includes(content)) {                        files.push(fullPath);                    }                }            });        };        search(dirPath);        return files;    }    // ========== 批量操作 ==========    // 批量重命名    batchRename(dirPath: string, oldPattern: string, newPattern: string): void {        const files = this.readDirectory(dirPath);        let renamed = 0;        files.forEach(file => {            if (file.includes(oldPattern)) {                const oldPath = path.join(dirPath, file);                const newFile = file.replace(oldPattern, newPattern);                const newPath = path.join(dirPath, newFile);                fs.renameSync(oldPath, newPath);                renamed++;            }        });        console.log(`批量重命名完成,共 ${renamed} 个文件`);    }    // 批量复制    batchCopy(srcFiles: string[], destDir: string): void {        if (!fs.existsSync(destDir)) {            fs.mkdirSync(destDir, { recursive: true });        }        srcFiles.forEach((srcFile, index) => {            const destFile = path.join(destDir, path.basename(srcFile));            this.copyFile(srcFile, destFile);        });        console.log(`批量复制完成,共 ${srcFiles.length} 个文件`);    }}// 使用示例async function main() {    const fileManager = new FileManager();    const outputDir = path.join(__dirname, '../../output/file-ops');    // 创建目录    fileManager.createDirectory(outputDir);    // 写入测试文件    fileManager.writeJsonFile(path.join(outputDir, 'data.json'), {        users: [            { id: 1, name: '张三' },            { id: 2, name: '李四' },        ]    });    // 读取文件    const data = fileManager.readJsonFile(path.join(outputDir, 'data.json'));    console.log('读取到的数据:', data);    // 搜索文件    const jsonFiles = fileManager.searchByExtension(outputDir, '.json');    console.log('JSON 文件:', jsonFiles);    // 获取文件信息    const fileInfo = fileManager.getFileInfo(path.join(outputDir, 'data.json'));    console.log('文件信息:', {        ...fileInfo,        size: fileManager.formatFileSize(fileInfo.size)    });}main().catch(console.error);```### 3.2 报表生成器```typescript// src/file/report-generator.tsimport ExcelJS from 'exceljs';import * as fs from 'fs';import * as path from 'path';interface ReportData {    [key: string]: any;}// Excel 报表生成器class ExcelReportGenerator {    private workbook: ExcelJS.Workbook;    constructor() {        this.workbook = new ExcelJS.Workbook();    }    // 创建工作表    createSheet(name: string, data: ReportData[]): void {        const worksheet = this.workbook.addWorksheet(name);        // 添加标题        if (data.length > 0) {            worksheet.columns = Object.keys(data[0]).map(key => ({                header: key,                key: key,                width: 20,            }));        }        // 添加数据        worksheet.addRows(data);        // 样式        worksheet.getRow(1).font = { bold: true };        worksheet.getRow(1).fill = {            type'pattern',            pattern: 'solid',            fgColor: { argb: 'FF4F81BD' }        };    }    // 添加图表    async addChart(        sheetName: string,        type: ExcelJS.ChartType,        dataRange: string,        title: string    ): Promise<void> {        const worksheet = this.workbook.getWorksheet(sheetName);        if (!worksheet) {            throw new Error(`工作表不存在: ${sheetName}`);        }        const chart = worksheet.addChart(type, {            name: title,            displayBlanksAs: 'gap',            title: {                name: title,            },            dataRange,        });        return chart;    }    // 保存 Excel 文件    async save(filePath: string): Promise<void> {        const dir = path.dirname(filePath);        if (!fs.existsSync(dir)) {            fs.mkdirSync(dir, { recursive: true });        }        await this.workbook.xlsx.writeFile(filePath);        console.log(`Excel 报表已生成: ${filePath}`);    }}// 使用示例async function main() {    const generator = new ExcelReportGenerator();    const outputDir = path.join(__dirname, '../../output/reports');    // 生成测试数据    const testData: ReportData[] = [        { 姓名: '张三', 部门: '销售', 销售额: 10000, 绩成率: '120%' },        { 姓名: '李四', 部门: '销售', 销售额: 15000, 绩成率: '150%' },        { 姓名: '王五', 部门: '技术', 销售额: 8000, 绩成率: '95%' },        { 姓名: '赵六', 部门: '技术', 销售额: 12000, 绩成率: '105%' },    ];    // 创建工作表    generator.createSheet('员工绩效', testData);    // 保存    await generator.save(path.join(outputDir, 'performance-report.xlsx'));}main().catch(console.error);```---## 场景四:数据库自动化操作### 4.1 数据库连接和 CRUD```typescript// src/database/db-connection.tsimport sqlite3 from 'sqlite3';import { promisify } from 'util';// 数据库类型定义interface User {    id?: number;    username: string;    email: string;    password: string;    role: string;    created_at?: string;}interface Product {    id?: number;    name: string;    price: number;    stock: number;    category: string;}// SQLite 数据库类class Database {    private db: sqlite3.Database;    constructor(dbPath: string = './data/database.db') {        this.db = new sqlite3.Database(dbPath, (err) => {            if (err) {                console.error('数据库连接失败:', err);            } else {                console.log('数据库连接成功');                this.initTables();            }        });    }    // 初始化表    private initTables(): void {        this.db.serialize(() => {            // 用户表            this.db.run(`                CREATE TABLE IF NOT EXISTS users (                    id INTEGER PRIMARY KEY AUTOINCREMENT,                    username TEXT UNIQUE NOT NULL,                    email TEXT UNIQUE NOT NULL,                    password TEXT NOT NULL,                    role TEXT NOT NULL DEFAULT 'user',                    created_at DATETIME DEFAULT CURRENT_TIMESTAMP                )            `);            // 产品表            this.db.run(`                CREATE TABLE IF NOT EXISTS products (                    id INTEGER PRIMARY KEY AUTOINCREMENT,                    name TEXT NOT NULL,                    price REAL NOT NULL,                    stock INTEGER NOT NULL DEFAULT 0,                    category TEXT NOT NULL                )            `);            // 订单表            this.db.run(`                CREATE TABLE IF NOT EXISTS orders (                    id INTEGER PRIMARY KEY AUTOINCREMENT,                    user_id INTEGER NOT NULL,                    product_id INTEGER NOT NULL,                    quantity INTEGER NOT NULL,                    total REAL NOT NULL,                    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,                    FOREIGN KEY (user_id) REFERENCES users(id),                    FOREIGN KEY (product_id) REFERENCES products(id)                )            `);            console.log('数据库表初始化完成');        });    }    // 执行查询(Promise 包装)    private query<T>(sql: string, params: any[] = []): Promise<T[]> {        return new Promise((resolve, reject) => {            this.db.all(sql, params, (err, rows) => {                if (err) {                    reject(err);                } else {                    resolve(rows as T[]);                }            });        });    }    private run(sql: string, params: any[] = []): Promise<sqlite3.RunResult> {        return new Promise((resolve, reject) => {            this.db.run(sql, params, function(err) {                if (err) {                    reject(err);                } else {                    resolve(this);                }            });        });    }    // ========== 用户操作 ==========    // 创建用户    async createUser(user: User): Promise<number> {        const result = await this.run(            'INSERT INTO users (username, email, password, role) VALUES (?, ?, ?, ?)',            [user.username, user.email, user.password, user.role || 'user']        );        console.log(`用户创建成功,ID: ${result.lastID}`);        return result.lastID;    }    // 获取用户    async getUser(id: number): Promise<User | null> {        const users = await this.query<User>('SELECT * FROM users WHERE id = ?', [id]);        return users[0] || null;    }    // 获取所有用户    async getAllUsers(): Promise<User[]> {        return this.query<User>('SELECT * FROM users ORDER BY created_at DESC');    }    // 更新用户    async updateUser(id: number, updates: Partial<User>): Promise<boolean> {        const fields = Object.keys(updates);        const values = Object.values(updates);        const setClause = fields.map(f => `${f} = ?`).join(', ');        const result = await this.run(            `UPDATE users SET ${setClause} WHERE id = ?`,            [...values, id]        );        console.log(`用户更新成功: ${result.changes} 行受影响`);        return result.changes > 0;    }    // 删除用户    async deleteUser(id: number): Promise<boolean> {        const result = await this.run('DELETE FROM users WHERE id = ?', [id]);        console.log(`用户删除成功: ${result.changes} 行受影响`);        return result.changes > 0;    }    // ========== 产品操作 ==========    // 创建产品    async createProduct(product: Product): Promise<number> {        const result = await this.run(            'INSERT INTO products (name, price, stock, category) VALUES (?, ?, ?, ?)',            [product.name, product.price, product.stock, product.category]        );        console.log(`产品创建成功,ID: ${result.lastID}`);        return result.lastID;    }    // 获取产品    async getProduct(id: number): Promise<Product | null> {        const products = await this.query<Product>('SELECT * FROM products WHERE id = ?', [id]);        return products[0] || null;    }    // 按分类获取产品    async getProductsByCategory(category: string): Promise<Product[]> {        return this.query<Product>('SELECT * FROM products WHERE category = ?', [category]);    }    // 更新库存    async updateStock(productId: number, quantity: number): Promise<boolean> {        const result = await this.run(            'UPDATE products SET stock = stock + ? WHERE id = ?',            [quantity, productId]        );        console.log(`库存更新成功: ${result.changes} 行受影响`);        return result.changes > 0;    }    // ========== 统计查询 ==========    // 获取用户统计    async getUserStats() Promise<any> {        const totalUsers = await this.query<any>('SELECT COUNT(*) as count FROM users');        const usersByRole = await this.query<any>('SELECT role, COUNT(*) as count FROM users GROUP BY role');        return {            total: totalUsers[0]?.count || 0,            byRole: usersByRole,        };    }    // 获取产品统计    async getProductStats(): Promise<any> {        const totalProducts = await this.query<any>('SELECT COUNT(*) as count FROM products');        const byCategory = await this.query<any>('SELECT category, COUNT(*) as count FROM products GROUP BY category');        const lowStock = await this.query<any>('SELECT * FROM products WHERE stock < 10');        return {            total: totalProducts[0]?.count || 0,            byCategory,            lowStock,        };    }    // ========== 批量操作 ==========    // 批量导入数据    async batchImportUsers(users: User[]): Promise<number> {        let successCount = 0;        for (const user of users) {            try {                await this.createUser(user);                successCount++;            } catch (error) {                console.error(`导入用户失败: ${user.username}`, error);            }        }        console.log(`批量导入完成,成功: ${successCount}/${users.length}`);        return successCount;    }    // 清空表    async clearTable(tableName: string): Promise<void> {        await this.run(`DELETE FROM ${tableName}`);        console.log(`表 ${tableName} 已清空`);    }    // 关闭数据库    close(): void {        this.db.close();        console.log('数据库连接已关闭');    }}// 使用示例async function main() {    const db = new Database();    try {        // 创建用户        const userId1 = await db.createUser({            username: 'zhangsan',            email: 'zhangsan@example.com',            password: 'hashed_password',            role: 'user',        });        const userId2 = await db.createUser({            username: 'admin',            email: 'admin@example.com',            password: 'hashed_password',            role: 'admin',        });        // 创建产品        await db.createProduct({            name: '商品1',            price: 99.99,            stock: 100,            category: '电子产品',        });        // 获取统计        const userStats = await db.getUserStats();        console.log('用户统计:', userStats);        const productStats = await db.getProductStats();        console.log('产品统计:', productStats);        // 批量导入        const newUsers: User[] = [            { username: 'user1', email: 'user1@example.com', password: 'pass1', role: 'user' },            { username: 'user2', email: 'user2@example.com', password: 'pass2', role: 'user' },            { username: 'user3', email: 'user3@example.com', password: 'pass3', role: 'user' },        ];        await db.batchImportUsers(newUsers);    } finally {        db.close();    }}main().catch(console.error);```---## 场景五:定时任务和调度```typescript// src/scheduler/jobs.tsimport cron from 'node-cron';import * as fs from 'fs';import * as path from 'path';// 定时任务管理器class TaskScheduler {    private tasks: Map<string, cron.ScheduledTask> = new Map();    // ========== 数据备份任务 ==========    // 定时备份数据库    startDatabaseBackup(): void {        const task = cron.schedule('0 2 * * *', async () => {            console.log('开始数据库备份...');            const sourceDir = './data';            const backupDir = './backups';            const timestamp = new Date().toISOString().replace(/[:.]/g, '-');            const backupPath = path.join(backupDir, `backup-${timestamp}`);            // 创建备份目录            if (!fs.existsSync(backupDir)) {                fs.mkdirSync(backupDir, { recursive: true });            }            // 复制数据库文件            const dbFiles = fs.readdirSync(sourceDir).filter(f => f.endsWith('.db'));            for (const file of dbFiles) {                fs.copyFileSync(                    path.join(sourceDir, file),                    path.join(backupPath, file)                );            }            console.log(`数据库备份完成: ${backupPath}`);        });        this.tasks.set('database-backup', task);    }    // ========== 数据清理任务 ==========    // 定时清理临时文件    startTempFileCleanup(): void {        const task = cron.schedule('0 3 * * *', async () => {            console.log('开始清理临时文件...');            const tempDir = './temp';            const retentionDays = 7;            const cutoffTime = Date.now() - retentionDays * 24 * 60 * 60 * 1000;            if (fs.existsSync(tempDir)) {                const files = fs.readdirSync(tempDir);                let deletedCount = 0;                files.forEach(file => {                    const filePath = path.join(tempDir, file);                    const stats = fs.statSync(filePath);                    if (stats.mtimeMs < cutoffTime) {                        fs.unlinkSync(filePath);                        deletedCount++;                    }                });                console.log(`清理完成,删除了 ${deletedCount} 个文件`);            }        });        this.tasks.set('temp-cleanup', task);    }    // ========== 数据同步任务 ==========    // 定时同步数据    startDataSync(): void {        const task = cron.schedule('*/30 * * * *', async () => {            console.log('开始数据同步...');            try {                // 模拟数据同步逻辑                const response = await fetch('https://api.example.com/sync');                const data = await response.json();                // 保存同步的数据                const syncDir = './data/sync';                if (!fs.existsSync(syncDir)) {                    fs.mkdirSync(syncDir, { recursive: true });                }                const timestamp = new Date().toISOString();                fs.writeFileSync(                    path.join(syncDir, `sync-${timestamp}.json`),                    JSON.stringify(data, null, 2)                );                console.log('数据同步完成');            } catch (error) {                console.error('数据同步失败:', error);            }        });        this.tasks.set('data-sync', task);    }    // ========== 报表生成任务 ==========    // 定时生成日报表    startDailyReport(): void {        const task = cron.schedule('0 8 * * *', async () => {            console.log('开始生成日报表...');            // 这里可以调用报表生成逻辑            const reportDate = new Date().toISOString().split('T')[0];            console.log(`日报表生成完成: ${reportDate}`);        });        this.tasks.set('daily-report', task);    }    // ========== 健康检查任务 ==========    // 定时健康检查    startHealthCheck(): void {        const task = cron.schedule('*/5 * * * *', async () => {            console.log('执行健康检查...');            const services = [                { name: '数据库', url: 'http://localhost:3000/health/db' },                { name: 'API', url: 'http://localhost:3000/health/api' },                { name: '缓存', url: 'http://localhost:3000/health/cache' },            ];            for (const service of services) {                try {                    const startTime = Date.now();                    const response = await fetch(service.url);                    const duration = Date.now() - startTime;                    if (response.ok) {                        console.log(`✅ ${service.name}: 正常 (${duration}ms)`);                    } else {                        console.log(`⚠️  ${service.name}: 响应异常 (${response.status})`);                        // 这里可以发送告警                    }                } catch (error) {                    console.log(`❌ ${service.name}: 不可用 (${error})`);                    // 这里可以发送告警                }            }        });        this.tasks.set('health-check', task);    }    // ========== 执行一次的任务 ==========    // 立即执行一次备份    async runBackupNow(): Promise<void> {        console.log('立即执行数据库备份...');        // 备份逻辑...    }    // 停止所有任务    stopAll(): void {        this.tasks.forEach((task, name) => {            task.stop();            console.log(`任务已停止: ${name}`);        });    }}// 使用示例async function main() {    const scheduler = new TaskScheduler();    // 启动定时任务    scheduler.startDatabaseBackup();      // 每天凌晨 2 点备份    scheduler.startTempFileCleanup();      // 每天凌晨 3 点清理临时文件    scheduler.startDataSync();            // 每 30 分钟同步数据    scheduler.startDailyReport();         // 每天早上 8 点生成报表    scheduler.startHealthCheck();         // 每 5 分钟健康检查    console.log('所有定时任务已启动');    // 监听退出信号    process.on('SIGINT', () => {        console.log('\n正在停止所有任务...');        scheduler.stopAll();        process.exit(0);    });}main().catch(console.error);```---## 场景六:邮件自动化```typescript// src/email/mailer.tsimport nodemailer from 'nodemailer';import * as fs from 'fs';import * as path from 'path';interface EmailConfig {    host: string;    port: number;    secure: boolean;    auth: {        user: string;        pass: string;    };}interface EmailOptions {    from: string;    to: string | string[];    subject: string;    html?: string;    text?: string;    attachments?: string[];}// 邮件发送器class EmailMailer {    private transporter: nodemailer.Transporter;    private config: EmailConfig;    constructor(config: EmailConfig) {        this.config = config;        this.transporter = nodemailer.createTransport({            host: config.host,            port: config.port,            secure: config.secure,            auth: config.auth,        });    }    // 发送简单文本邮件    async sendTextEmail(options: EmailOptions): Promise<void> {        const mailOptions = {            from: options.from,            to: options.to,            subject: options.subject,            text: options.text || '',        };        await this.transporter.sendMail(mailOptions);        console.log(`文本邮件已发送: ${options.subject}`);    }    // 发送 HTML 邮件    async sendHtmlEmail(options: EmailOptions): Promise<void> {        const mailOptions = {            from: options.from,            to: options.to,            subject: options.subject,            html: options.html || '',        };        await this.transporter.sendMail(mailOptions);        console.log(`HTML 邮件已发送: ${options.subject}`);    }    // 发送带附件的邮件    async sendEmailWithAttachments(options: EmailOptions): Promise<void> {        const attachments = (options.attachments || []).map(file => ({            filename: path.basename(file),            path: file,        }));        const mailOptions = {            from: options.from,            to: options.to,            subject: options.subject,            html: options.html || '',            text: options.text || '',            attachments,        };        await this.transporter.sendMail(mailOptions);        console.log(`带附件的邮件已发送: ${options.subject}`);    }    // 批量发送邮件    async sendBatchEmail(recipients: string[], subject: string, html: string): Promise<void> {        const batchSize = 10;        const batches: string[][] = [];        for (let i = 0; i < recipients.length; i += batchSize) {            batches.push(recipients.slice(i, i + batchSize));        }        console.log(`准备发送邮件,共 ${recipients.length} 个收件人,分 ${batches.length} 批`);        for (let i = 0; i < batches.length; i++) {            await this.sendHtmlEmail({                from: 'noreply@example.com',                to: batches[i],                subject,                html,            });            console.log(`已发送第 ${i + 1}/${batches.length} 批`);            // 延迟,避免被邮件服务器限制            if (i < batches.length - 1) {                await new Promise(resolve => setTimeout(resolve, 5000));            }        }        console.log('所有邮件发送完成');    }    // 生成 HTML 邮件模板    generateHtmlEmail(template: string, data: any): string {        const templates: Record<string, string> = {            'welcome': `                <h1>欢迎,${data.name}!</h1>                <p>感谢您注册我们的服务。</p>                <p>您的账户已激活,可以开始使用了。</p>                <p>如有问题,请联系我们。</p>            `,            'alert': `                <h1>系统告警:${data.title}</h1>                <p><strong>时间:</strong>${data.time}</p>                <p><strong>级别:</strong>${data.level}</p>                <p><strong>详情:</strong>${data.message}</p>            `,            'report': `                <h1>${data.reportTitle}</h1>                <p><strong>生成时间:</strong>${data.generateTime}</p>                <hr>                <h2>数据概览</h2>                <ul>                    ${data.summary.map((item: any) => `<li>${item.key}${item.value}</li>`).join('')}                </ul>            `,        };        return templates[template] || '';    }    // 发送报告邮件    async sendReportEmail(        recipients: string[],        reportData: any    ): Promise<void> {        const html = this.generateHtmlEmail('report', {            reportTitle: '系统运行报告',            generateTime: new Date().toLocaleString(),            summary: reportData,        });        await this.sendBatchEmail(recipients, '系统运行报告', html);    }}// 使用示例async function main() {    const config: EmailConfig = {        host: 'smtp.example.com',        port: 587,        secure: false,        auth: {            user: 'your-email@example.com',            pass: 'your-password',        },    };    const mailer = new EmailMailer(config);    // 发送欢迎邮件    await mailer.sendHtmlEmail({        from: 'noreply@example.com',        to: 'user@example.com',        subject: '欢迎注册',        html: mailer.generateHtmlEmail('welcome', {            name: '张三',        }),    });    // 发送告警邮件    await mailer.sendHtmlEmail({        from: 'alert@example.com',        to: 'admin@example.com',        subject: '系统告警',        html: mailer.generateHtmlEmail('alert', {            title: '数据库连接失败',            level: 'ERROR',            time: new Date().toLocaleString(),            message: '数据库连接超时,请及时处理',        }),    });    // 发送报告邮件    await mailer.sendReportEmail(['admin@example.com'], {        reportTitle: '系统运行报告',        generateTime: new Date().toLocaleString(),        summary: [            { key: '活跃用户数', value: '100' },            { key: '今日订单数', value: '50' },            { key: '错误数', value: '5' },        ],    });}main().catch(console.error);```---## 场景七:CI/CD 自动化### 7.1 GitHub Actions 工作流```yaml# .github/workflows/ci-cd.ymlname: CI/CD Pipelineon:  push:    branches: [ main, develop ]  pull_request:    branches: [ main ]jobs:  # ========== 测试阶段 ==========  test:    runs-on: ubuntu-latest    steps:      - name: 检出代码        uses: actions/checkout@v3      - name: 设置 Node.js        uses: actions/setup-node@v3        with:          node-version: '18'          cache: 'npm'      - name: 安装依赖        run: npm ci      - name: 运行测试        run: npm test      - name: 代码覆盖率        run: npm run test:coverage      - name: 上传覆盖率        uses: codecov/codecov-action@v3        with:          files: ./coverage/lcov.info  # ========== 构建阶段 ==========  build:    needs: test    runs-on: ubuntu-latest    steps:      - name: 检出代码        uses: actions/checkout@v3      - name: 设置 Node.js        uses: actions/setup-node@v3        with:          node-version: '18'          cache: 'npm'      - name: 安装依赖        run: npm ci      - name: 构建项目        run: npm run build      - name: 上传构建产物        uses: actions/upload-artifact@v3        with:          name: build-artifacts          path: dist/  # ========== 部署阶段 ==========  deploy:    needs: build    runs-on: ubuntu-latest    steps:      - name: 检出代码        uses: actions/checkout@v3      - name: 部署到服务器        uses: appleboy/ssh-action@master        with:          host: ${{ secrets.SERVER_HOST }}          username: ${{ secrets.SERVER_USER }}          key: ${{ secrets.SSH_KEY }}          script: |            cd /var/www/app            git pull origin main            npm ci            npm run build            pm2 restart app```### 7.2 自动化部署脚本```typescript// src/ci-cd/deploy.tsimport * as fs from 'fs';import * as path from 'path';interface DeployConfig {    appName: string;    buildDir: string;    remoteDir: string;    host: string;    port: number;    username: string;    keyPath: string;}// 自动化部署器class AutoDeployer {    private config: DeployConfig;    constructor(config: DeployConfig) {        this.config = config;    }    // 检查部署前置条件    async preDeployCheck(): Promise<boolean> {        console.log('执行部署前置检查...');        // 检查构建目录是否存在        if (!fs.existsSync(this.config.buildDir)) {            console.error('构建目录不存在,请先执行构建');            return false;        }        // 检查必要的文件        const requiredFiles = ['package.json''main.js'];        for (const file of requiredFiles) {            const filePath = path.join(this.config.buildDir, file);            if (!fs.existsSync(filePath)) {                console.error(`缺少必需文件: ${file}`);                return false;            }        }        console.log('✅ 前置检查通过');        return true;    }    // 生成部署清单    generateDeploymentChecklist(): string {        const checklist = `部署检查清单=============应用信息--------应用名称: ${this.config.appName}构建目录: ${this.config.buildDir}部署目录: ${this.config.remoteDir}服务器: ${this.config.host}:${this.config.port}文件检查--------${fs.existsSync(path.join(this.config.buildDir, 'package.json')) ? '✅' : '❌'} package.json${fs.existsSync(path.join(this.config.buildDir, 'main.js')) ? '✅' : '❌'} main.js${fs.existsSync(path.join(this.config.buildDir, 'assets')) ? '✅' : '❌'} assets/部署时间--------时间: ${new Date().toLocaleString()}        `;        return checklist;    }    // 生成部署日志    generateDeploymentLog(): void {        const logDir = './logs/deployments';        const timestamp = new Date().toISOString().replace(/[:.]/g, '-');        const logFile = path.join(logDir, `deploy-${timestamp}.log`);        if (!fs.existsSync(logDir)) {            fs.mkdirSync(logDir, { recursive: true });        }        const logContent = `部署开始时间: ${new Date().toLocaleString()}\n`;        fs.writeFileSync(logFile, logContent, 'utf-8');        console.log(`部署日志: ${logFile}`);    }    // 生成部署回滚脚本    generateRollbackScript(): void {        const rollbackScript = `#!/bin/bash# 自动生成的回滚脚本APP_NAME="${this.config.appName}"BACKUP_DIR="/var/backups/${this.config.appName}"CURRENT_VERSION=$(date +%Y%m%d_%H%M%S)echo "开始回滚..."# 备份当前版本cd /var/www/${this.config.appName}tar -czf ${BACKUP_DIR}/${APP_NAME}-${CURRENT_VERSION}.tar.gz .# 回滚到上一版本PREV_VERSION=$(ls -t ${BACKUP_DIR} | head -2 | tail -1)tar -xzf ${BACKUP_DIR}/${PREV_VERSION} -C /var/www/${this.config.appName}echo "回滚完成: ${PREV_VERSION}"        `;        const scriptPath = './scripts/rollback.sh';        const scriptDir = path.dirname(scriptPath);        if (!fs.existsSync(scriptDir)) {            fs.mkdirSync(scriptDir, { recursive: true });        }        fs.writeFileSync(scriptPath, rollbackScript, 'utf-8');        console.log(`回滚脚本已生成: ${scriptPath}`);    }    // 执行部署    async deploy(): Promise<void> {        console.log('开始部署流程...');        // 前置检查        const checkPassed = await this.preDeployCheck();        if (!checkPassed) {            throw new Error('前置检查失败,取消部署');        }        // 生成部署文件        this.generateDeploymentChecklist();        this.generateDeploymentLog();        this.generateRollbackScript();        console.log('部署准备完成,请确认后执行部署命令');        console.log(`部署命令:---------1. 压缩构建产物   tar -czf deploy.tar.gz -C ${this.config.buildDir} .2. 上传到服务器   scp deploy.tar.gz ${this.config.username}@${this.config.host}:/tmp/3. 解压并部署   ssh ${this.config.username}@${this.config.host}   cd /tmp   tar -xzf deploy.tar.gz -C ${this.config.remoteDir}   pm2 restart ${this.config.appName}4. 验证部署   curl http://${this.config.host}:${this.config.port}/health        `);    }}// 使用示例async function main() {    const config: DeployConfig = {        appName: 'my-app',        buildDir: './dist',        remoteDir: '/var/www/my-app',        host: 'example.com',        port: 3000,        username: 'deploy-user',        keyPath: '~/.ssh/id_rsa',    };    const deployer = new AutoDeployer(config);    await deployer.deploy();}main().catch(console.error);```---## 场景八:服务器监控和日志```typescript// src/monitor/logger.tsimport winston from 'winston';import * as fs from 'fs';import * as path from 'path';interface LogConfig {    level: string;    logDir: string;    maxFiles: number;    maxSize: string;}// 日志记录器class Logger {    private logger: winston.Logger;    constructor(config: LogConfig) {        // 确保日志目录存在        if (!fs.existsSync(config.logDir)) {            fs.mkdirSync(config.logDir, { recursive: true });        }        this.logger = winston.createLogger({            level: config.level,            format: winston.format.combine(                winston.format.timestamp(),                winston.format.errors({ stack: true }),                winston.format.printf(({ timestamp, level, message, ...meta }) => {                    return `[${timestamp}] [${level.toUpperCase()}${message} ${                        meta ? JSON.stringify(meta) : ''                    }`;                })            ),            transports: [                // 控制台输出                new winston.transports.Console({                    format: winston.format.simple(),                }),                // 文件输出 - 所有日志                new winston.transports.File({                    filename: path.join(config.logDir, 'combined.log'),                    maxsize: config.maxSize,                    maxFiles: config.maxFiles,                }),                // 文件输出 - 错误日志                new winston.transports.File({                    filename: path.join(config.logDir, 'error.log'),                    level: 'error',                    maxsize: config.maxSize,                    maxFiles: config.maxFiles,                }),            ],        });    }    // 日志方法    info(message: string, meta?: any): void {        this.logger.info(message, meta);    }    error(message: string, error?: Error): void {        this.logger.error(message, error);    }    warn(message: string, meta?: any): void {        this.logger.warn(message, meta);    }    debug(message: string, meta?: any): void {        this.logger.debug(message, meta);    }}// 使用示例const logger = new Logger({    level: 'info',    logDir: './logs',    maxFiles: 30,    maxSize: '10m',});logger.info('应用启动');logger.error('数据库连接失败', new Error('Connection timeout'));logger.warn('内存使用率高', { memory: '90%' });```### 8.2 系统监控```typescript// src/monitor/system-monitor.tsimport os from 'os';import * as fs from 'fs';import { Logger } from './logger';const logger = new Logger({    level: 'info',    logDir: './logs',    maxFiles: 30,    maxSize: '10m',});interface SystemMetrics {    timestamp: string;    cpu: {        usage: number;        loadAvg: number[];    };    memory: {        total: number;        used: number;        free: number;        usage: number;    };    disk: {        total: number;        used: number;        free: number;        usage: number;    };    uptime: number;}// 系统监控器class SystemMonitor {    private metrics: SystemMetrics[] = [];    private maxMetrics: number = 100;    // 获取 CPU 使用率    getCpuUsage(): number {        const cpus = os.cpus();        let totalIdle = 0;        let totalTick = 0;        cpus.forEach(cpu => {            for (const type in cpu.times) {                totalIdle += cpu.times[type];                totalTick += cpu.times[type];            }        });        return ((totalTick - totalIdle) / totalTick) * 100;    }    // 获取系统负载    getCpuLoadAvg(): number[] {        return os.loadavg();    }    // 获取内存使用率    getMemoryUsage() {        const total = os.totalmem();        const free = os.freemem();        const used = total - free;        return {            total,            used,            free,            usage: (used / total) * 100,        };    }    // 获取磁盘使用率    getDiskUsage(path: string) {        const stats = fs.statSync(path);        const used = stats.size;        return {            total: used,  // 简化处理            used,            free: 0,            usage: 100,        };    }    // 获取系统运行时间    getUptime(): number {        return os.uptime();    }    // 收集所有指标    collectMetrics(): SystemMetrics {        const metrics: SystemMetrics = {            timestamp: new Date().toISOString(),            cpu: {                usage: this.getCpuUsage(),                loadAvg: this.getCpuLoadAvg(),            },            memory: this.getMemoryUsage(),            disk: this.getDiskUsage('/'),            uptime: this.getUptime(),        };        this.metrics.push(metrics);        // 保持最近的数据        if (this.metrics.length > this.maxMetrics) {            this.metrics.shift();        }        return metrics;    }    // 检查是否触发告警    checkAlerts(metrics: SystemMetrics): string[] {        const alerts: string[] = [];        // CPU 告警        if (metrics.cpu.usage > 80) {            alerts.push(`CPU 使用率过高: ${metrics.cpu.usage.toFixed(2)}%`);        }        // 内存告警        if (metrics.memory.usage > 85) {            alerts.push(`内存使用率过高: ${metrics.memory.usage.toFixed(2)}%`);        }        // 系统负载告警        if (metrics.cpu.loadAvg[0] > 2.0) {            alerts.push(`系统负载过高: ${metrics.cpu.loadAvg[0].toFixed(2)}`);        }        return alerts;    }    // 生成监控报告    generateReport(): void {        const avgCpuUsage = this.metrics.reduce((sum, m) => sum + m.cpu.usage, 0) / this.metrics.length;        const avgMemoryUsage = this.metrics.reduce((sum, m) => sum + m.memory.usage, 0) / this.metrics.length;        const report = `系统监控报告==================时间范围: ${this.metrics[0]?.timestamp} ~ ${this.metrics[this.metrics.length - 1]?.timestamp}CPU 使用率----------平均值: ${avgCpuUsage.toFixed(2)}%峰值: ${Math.max(...this.metrics.map(m => m.cpu.usage)).toFixed(2)}%内存使用率----------平均值: ${avgMemoryUsage.toFixed(2)}%峰值: ${Math.max(...this.metrics.map(m => m.memory.usage)).toFixed(2)}%告警统计----------${this.metrics.filter(m => this.checkAlerts(m).length > 0).length} 条告警        `;        logger.info('系统监控报告', { report });        const outputDir = './output/monitor';        if (!fs.existsSync(outputDir)) {            fs.mkdirSync(outputDir, { recursive: true });        }        fs.writeFileSync(            path.join(outputDir, 'system-report.txt'),            report,            'utf-8'        );    }    // 启动监控    startMonitoring(interval: number = 5000): void {        logger.info('系统监控已启动');        setInterval(() => {            const metrics = this.collectMetrics();            logger.info('系统指标', {                cpu: `${metrics.cpu.usage.toFixed(2)}%`,                memory: `${metrics.memory.usage.toFixed(2)}%`,                uptime: `${(metrics.uptime / 60).toFixed(1)}min`,            });            const alerts = this.checkAlerts(metrics);            alerts.forEach(alert => logger.warn(alert));        }, interval);    }}// 使用示例async function main() {    const monitor = new SystemMonitor();    // 立即收集一次指标    const metrics = monitor.collectMetrics();    console.log('当前系统指标:', metrics);    // 启动持续监控(每 5 秒)    monitor.startMonitoring(5000);    // 每分钟生成报告    setInterval(() => {        monitor.generateReport();    }, 60000);}main().catch(console.error);```---## 对比总结### JavaScript vs TypeScript vs Python 在后端自动化中的选择| 场景 | JavaScript | TypeScript | Python | 推荐 ||------|-----------|-------------|---------|------|| **简单脚本** | ✅ 足够 | ⚠️ 可能过度 | ✅ 非常适合 | Python || **API 测试** | ⚠️ 类型不明确 | ✅ 类型安全 | ✅ pytest生态 | Python || **数据库操作** | ⚠️ 容易出错 | ✅ 类型安全 | ✅ SQLAlchemy等 | Python || **文件处理** | ✅ 快速 | ⚠️ 增加复杂度 | ✅ 内置强大 | Python || **爬虫** | ✅ 快速原型 | ⚠️ 可选 | ✅ Scrapy/requests | Python || **定时任务** | ⚠️ 逻辑复杂 | ✅ 类型安全 | ✅ APScheduler | Python || **邮件发送** | ✅ 简单够用 | ⚠️ 收益不大 | ✅ smtplib | Python || **CI/CD** | ✅ 配置简单 | ✅ 类型安全 | ⚠️ 通常用JS | JavaScript || **监控系统** | ⚠️ 复杂逻辑 | ✅ 类型安全 | ✅ Prometheus等 | Python || **数据科学/分析** | ❌ 不适合 | ❌ 不适合 | ✅ Pandas/NumPy | Python || **前端相关** | ✅ 语言统一 | ✅ 类型安全 | ❌ 需桥接 | TypeScript || **Web自动化** | ✅ Playwright | ✅ Playwright | ✅ Selenium | TypeScript || **并发性能** | ⚠️ Node限制 | ⚠️ Node限制 | ⚠️ GIL限制 | 视场景 || **学习曲线** | ⚠️ 中等 | ❌ 较陡 | ✅ 简单 | Python |### 详细对比分析#### JavaScript**优势:**- 与前端代码语言统一- npm 生态系统庞大- 异步编程模型适合 I/O 密集型任务- Playwright/Puppeteer 等工具原生支持**劣势:**- 类型系统薄弱,容易出错- 数据处理库不如 Python 丰富- 数值计算性能不如 Python**适用场景:**- 需要与前端代码集成的自动化- 快速原型和简单脚本- CI/CD 配置脚本- Web 自动化(Playwright)#### TypeScript**优势:**- 类型安全减少运行时错误- 接口和类型定义让代码更清晰- 更好的 IDE 支持- 适合大型项目维护**劣势:**- 编译步骤增加复杂度- 学习曲线较陡- 对简单任务可能过度设计**适用场景:**- 复杂的自动化测试框架- 需要长期维护的项目- 团队协作开发- 企业级自动化系统#### Python**优势:**- 语法简洁易学- 生态极其丰富(requests, pytest, pandas, numpy)- 数据处理和分析能力强- 测试框架成熟(pytest, unittest)- 科学计算和机器学习生态**劣势:**- 运行速度相对较慢- 与前端集成需要额外桥接- Web 自动化工具(Selenium)不如 Playwright 优秀- 全局解释器锁(GIL)限制多线程性能**适用场景:**- API 测试和集成测试- 数据爬取和处理- 数据分析和报表生成- 复杂的测试场景(pytest 生态)- 需要数据科学支持的项目- 数据库操作和 ORM### 选择建议**使用 JavaScript 的情况:**- 快速脚本和原型(简单场景)- 文件操作自动化- 爬虫原型验证- CI/CD 配置脚本**使用 TypeScript 的情况:**- 复杂的 API 测试框架- 需要与前端代码统一语言- 数据库操作和 ORM- 定时任务调度系统- 监控和告警系统- 长期维护的企业项目**使用 Python 的情况:**- API 测试(pytest + requests)- 数据爬取和处理- 数据分析和报表- 数据库操作(SQLAlchemy)- 需要数据科学支持- 测试框架开发- 复杂的测试场景### 快速决策指南```问题1:项目是否涉及前端自动化测试?    └─ 是 → 推荐 TypeScript (Playwright)    └─ 否 → 继续问题2问题2:是否需要大量数据处理或分析?    └─ 是 → 推荐 Python    └─ 否 → 继续问题3问题3:项目复杂度和维护周期?    └─ 简单/短期 → JavaScript 或 Python    └─ 复杂/长期 → TypeScript问题4:团队技术栈偏好?    └─ 前端团队 → TypeScript/JavaScript    └─ 后端/测试团队 → Python```

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-02-27 09:14:56 HTTP/2.0 GET : https://f.mffb.com.cn/a/475289.html
  2. 运行时间 : 0.085044s [ 吞吐率:11.76req/s ] 内存消耗:5,544.77kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=20b9f7577c010bb2ca0532220b733381
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000486s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000924s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000280s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000284s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000432s ]
  6. SELECT * FROM `set` [ RunTime:0.000213s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000602s ]
  8. SELECT * FROM `article` WHERE `id` = 475289 LIMIT 1 [ RunTime:0.001577s ]
  9. UPDATE `article` SET `lasttime` = 1772154896 WHERE `id` = 475289 [ RunTime:0.005819s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 66 LIMIT 1 [ RunTime:0.000307s ]
  11. SELECT * FROM `article` WHERE `id` < 475289 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000396s ]
  12. SELECT * FROM `article` WHERE `id` > 475289 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000420s ]
  13. SELECT * FROM `article` WHERE `id` < 475289 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.000672s ]
  14. SELECT * FROM `article` WHERE `id` < 475289 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.001198s ]
  15. SELECT * FROM `article` WHERE `id` < 475289 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.000637s ]
0.086551s