TypeScript 入门

课程地址

ts 开发环境搭建

npm i -g typescript

查看安装位置:

$ npm root -g
C:\Users\Daniel\AppData\Roaming\npm\node_modules

创建 hello.ts

console.log("hello, ts");

编译 ts 文件,得到 js 文件:

$ tsc foo.ts

类型声明

后置的类型声明

let a: number;

a = 10;
a = 33;
// a = "hello";

let b: string = "hello";
let c = true;   // Automatic type inference

function sum(a: number, b: number): number {
    return a + b;
}

console.log(sum(1, 2));

类型

在这里插入图片描述

// 字面量类型
let a: 10;  // const a = 10;

// 联合类型
let b: "male" | "female";
let c: boolean | string;
c = true;
c = "hello"

// any 与 unknown
let d: any;
d = "hello";

let e: unknown;
e = "hello";

let s: string = d;
// s = e;   Type 'unknown' is not assignable to type 'string'
s = e as string;
s = <string>e;

function f(): void {
    return;
}

function f2(): never {  // 永远不会返回结果,终止进程
    throw new Error("error");
}

unknown 本质上是一个类型安全的 any

// 对象类型
let b: {
    name: string,
    age?: number,   // ? -> optional
};

b = {
    name: "sunwukong"
};

let c: {
    name: string,
    [propName: string]: any,
};

// 函数类型
let d: (a: number, b: number) => number;

d = function(a, b) {
    return a + b;
}

// 数组类型
let e: string[];
e = ["a", "b", "c"];

let g: Array<number>;
g = [1, 2, 3];

// 元组
let h: [string, string];

// enum
enum Gender {
    Male = 0,
    Female = 1,
}

let p: {name: string, gender: Gender}

p = {
    name: "sunwukong",
    gender: Gender.Male,
}

// & 类型
let j: {name: string} & {age: number};

// 类型别名
type t1 = 1 | 2 | 3 | 4 | 5;
let m: t1;

tsc 编译选项

自动编译文件

tsc foo.ts -w	# 文件改变时自动编译
tsc		# 根据 tsconfig.json 编译

tsconfig.json

{
    /*
        ** 表示任意目录
        * 表示任意文件
    */
    "include": ["./*"],
    "exclude": ["foo.ts"],
    // "files": ["./app.ts", "./index.ts"],
    "compilerOptions": {
        "target": "ES6",     // 指定 es 版本
        // "module": "ES6",
        // "lib": ["DOM"]
        "outDir": "./dist",
        // "outFile": "./dist/app.js",  // 将编译结果合并到 app.js
        "allowJs": false,
        "checkJs": false,   // 对 js 也做类型检查
        "removeComments": false,
        "noEmit": true,     // 不生成编译后的文件,只检查
        "noEmitOnError": true,   // 当有错误时不生成编译后的文件
        "strict": true,     // 打开下面的所有严格检查
        /*
            "alwaysStrict": true,   // -> use strict
            "noImplicitAny": true,
            "noImplicitThis": true,
            "strictNullChecks": true,
        */
    },
}

使用 webpack 打包 ts 代码

npm init -y		# 初始化项目
npm i -D webpack webpack-cli typescript ts-loader

配置 webpack.config.js

const path = require("path");

module.exports = {
    entry: "./src/index.ts",
    output: {
        path: path.resolve(__dirname + "/dist"),
        filename: "bundle.js",
    },
    module: {
        rules: [
            {
                test: /\.ts$/,
                use: "ts-loader",
                exclude: /node_modules/
            }
        ]
    },
    mode: 'development',
}

配置 tsconfig.json

{
    "compilerOptions": {
        "module": "es6",
        "target": "es6",
        "strict": true,
    }
}

package.json 中增加 script 配置

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack"
  },

最后 npm run build 即可

安装 3 个插件:

npm i -D html-webpack-plugin	# 生成 html 测试文件
npm i -D webpack-dev-server		# 编辑后自动重启服务
npm i -D clean-webpack-plugin	# 清除 dist 目录再构建
npm i -D @babel/core @babel/preset-env babel-loader core-js

package.json 中编写 webpack-server 的启动脚本:

  "scripts": {
    "start": "webpack serve --open"
  },

webpack.config.ts 中引入插件(使用 template.html 作为生成模板):

const path = require("path");
const HTMLWebpackPlugin = require("html-webpack-plugin");
const {CleanWebpackPlugin} = require("clean-webpack-plugin");

module.exports = {
    entry: "./src/index.ts",
    output: {
        path: path.resolve(__dirname + "/dist"),
        filename: "bundle.js",
    },
    module: {
        rules: [
            {
                test: /\.ts$/,
                use: [
                    {
                        loader: "babel-loader",
                        options: {
                            presets: [
                                [
                                    "@babel/preset-env",
                                    {
                                        targets: {
                                            "chrome": "88"
                                        },
                                        "corejs": "3",
                                        "useBuiltIns": "usage"  // 按需加载
                                    }
                                ]
                            ]
                        }
                    },
                    "ts-loader"
                ],
                exclude: /node_modules/
            }
        ]
    },
    mode: 'development',
    plugins: [
        new HTMLWebpackPlugin({
            // title: "mytitle"
            template: "./src/template.html"
        }),
        new CleanWebpackPlugin(),
    ],
    resolve: {  // 设置引用模块
        extensions: [".ts", ".js"]
    }
}

OOP

class

class Person {
    name: string = "sunwukong";
    static age: number = 18;
    readonly gender: string = "M";
    sayHello() {
        console.log("hello");
    }
}

let p = new Person();

console.log(p);
console.log(Person.age);
p.sayHello();

构造函数和 this

class Dog {
    name: string;
    age: number;

    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }
    bark() {
        console.log(`${this.name}: wangwang`);
    }
}

let d = new Dog("wangcai", 4);

console.log(d);
d.bark();

继承

class Animal {
    name: string;
    age: number;

    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }

    sayHello() {
        console.log("sayHello()");
    }
}

class Dog extends Animal{
    run(){
        console.log(`${this.name} is running`);
    }
    sayHello(): void {  // 子类重写父类方法
        console.log("wangwangwang");
    }
}

class Cat extends Animal{
    sayHello(): void {  // 子类重写父类方法
        console.log("miaomiaomiao");
    }
}

const d1 = new Dog("wangcai", 5);
console.log(d1);
d1.sayHello();

const c1 = new Cat("mimi", 3);
c1.sayHello();

super

super 表示父类

class Animal {
    name: string;

    constructor(name: string) {
        this.name = name;
    }

    sayHello() {
        console.log("sayHello()");
    }
}

class Dog extends Animal {
    age: number;

    constructor(name: string, age: number) {
        super(name);
        this.age = age;
    }

    sayHello(): void {
        console.log("wangwangwang");
    }
}

const d1 = new Dog("wangcai", 3);
console.log(d1);    // Dog { name: 'wangcai', age: 3 }
d1.sayHello();      // wangwangwang

抽象类

使用 abstract 修饰的类,不能用于实例化对象,只能被继承

抽象类中可以添加抽象方法,抽象方法必须被子类重写

abstract class Animal {
    name: string;

    constructor(name: string) {
        this.name = name;
    }

    abstract sayHello(): void;  // 必须被子类重写
}

class Dog extends Animal {
    sayHello(): void {
        console.log("wangwangwang");
    }
}

const d1 = new Dog("wangcai");
console.log(d1);    // Dog { name: 'wangcai', age: 3 }
d1.sayHello();      // wangwangwang

接口

用来定义一个类的结构(一个类应该包含哪些属性和方法,做类型限制)

接口中的所有属性都不能带有实际的值,接口只定义对象的结构(全是抽象方法),而不考虑实际值

interface Person{
    name: string;
    age: number;
}

// 接口可以分离定义
interface Person {
    gender: string;
    sayHello(): void;
}

class Male implements Person {
    name: string;
    age: number;
    gender: string;
    constructor(name: string, age: number, gender: string) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }
    sayHello(): void {
        console.log("hello");
    }
}

属性访问控制

class Person {
    private _name: string;
    private _age: number;

    constructor(name: string, age: number) {
        this._name = name;
        this._age = age;
    }

    get name() {        // per.name
        return this._name;
    }

    set name(name: string) {
        this._name = name;
    }

    get age() {
        return this._age;
    }

    set age(age: number) {
        if (age >= 0) {
            this._age = age;
        }
    }
}

const p1 = new Person("sunwukong", 18);
console.log(p1);

p1.name = "zhubajie";
console.log(p1);
console.log(p1.age);

class C {
    // 直接将属性和访问控制定义在构造函数中
    constructor(public name: string, public age: number) {

    }
}

泛型

function fn<T> (a: T): T {
    return a;
}

fn(10);
fn<string>("hello");

function bar<T, K>(a: T, b: K): T {
    console.log(b);
    return a;
}

bar<number, string>(10, "hello");

interface Inter {
    length: number;
}
// 使用接口约束泛型的类型参数
function foo<T extends Inter>(a: T): number {
    return a.length;
}

foo("123");     // string 有 length
foo({length: 10});

// 类的泛型
class C<T> {
    name: T;
    constructor(name: T) {
        this.name = name;
    }
}

const c = new C<string>("sunwukong");

贪吃蛇练习

// package.json
{
  "name": "snake",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack",
    "start": "webpack serve --open"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.23.9",
    "@babel/preset-env": "^7.23.9",
    "babel-loader": "^9.1.3",
    "clean-webpack-plugin": "^4.0.0",
    "core-js": "^3.35.1",
    "css-loader": "^6.10.0",
    "html-webpack-plugin": "^5.6.0",
    "less": "^4.2.0",
    "less-loader": "^12.2.0",
    "style-loader": "^3.3.4",
    "ts-loader": "^9.5.1",
    "typescript": "^5.3.3",
    "webpack": "^5.89.0",
    "webpack-cli": "^5.1.4",
    "webpack-dev-server": "^4.15.1"
  }
}
// tsconfig.json
{
    "compilerOptions": {
        "module": "es6",
        "target": "es6",
        "strict": true,
        "noEmitOnError": true
    }
}
// webpack.config.ts
const path = require("path");
const HTMLWebpackPlugin = require("html-webpack-plugin");
const {CleanWebpackPlugin} = require("clean-webpack-plugin");

module.exports = {
    entry: "./src/index.ts",
    output: {
        path: path.resolve(__dirname + "/dist"),
        filename: "bundle.js",
    },
    module: {
        rules: [
            {
                test: /\.ts$/,
                use: [
                    {
                        loader: "babel-loader",
                        options: {
                            presets: [
                                [
                                    "@babel/preset-env",
                                    {
                                        targets: {
                                            "chrome": "88"
                                        },
                                        "corejs": "3",
                                        "useBuiltIns": "usage"  // 按需加载
                                    }
                                ]
                            ]
                        }
                    },
                    "ts-loader"
                ],
                exclude: /node_modules/
            },
            {
                test: /\.less$/,
                use: [
                    "style-loader",
                    "css-loader",
                    "less-loader"
                ]
            }
        ]
    },
    mode: 'development',
    plugins: [
        new HTMLWebpackPlugin({
            // title: "mytitle"
            template: "./src/template.html"
        }),
        new CleanWebpackPlugin(),
    ],
    resolve: {  // 设置引用模块
        extensions: [".ts", ".js"]
    }
}
// index.ts
import "./style/index.less"
import Food from "./modules/food"
import ScorePanel from "./modules/score_panel"
import GameControl from "./modules/game_control";

const f = new Food();
f.change();

const gc = new GameControl();
// snake.ts
class Snake {
    head: HTMLElement;
    bodies: HTMLCollection;
    element: HTMLElement;

    constructor() {
        this.head = document.querySelector("#snake > div")!;
        this.bodies = document.getElementById("snake")!.getElementsByTagName("div");
        this.element = document.getElementById("snake")!;
    }

    get X() {
        return this.head.offsetLeft;
    }

    get Y() {
        return this.head.offsetTop;
    }

    set X(x: number) {
        if (this.X === x) return;

        if (x < 0 || x > 290) {
            throw new Error("snake_dead");
        }
        if (this.bodies[1] && (this.bodies[1] as HTMLElement).offsetLeft === x) {
            if (x > this.X) {
                x = this.X - 10;
            } else {
                x = this.X + 10;
            }
        }
        this.moveBody();
        this.head.style.left = x + "px";
        this.checkHeadBody();
    }

    set Y(y: number) {
        if (this.Y === y) return;
        if (y < 0 || y > 290) {
            throw new Error("snake_dead");
        }
        if (this.bodies[1] && (this.bodies[1] as HTMLElement).offsetTop === y) {
            if (y > this.Y) {
                y = this.Y - 10;
            } else {
                y = this.Y + 10;
            }
        }

        this.moveBody();
        this.head.style.top = y + "px";
        this.checkHeadBody();
    }

    addBody() {
        this.element.insertAdjacentHTML("beforeend", "<div></div>");
    }

    moveBody() {
        for (let i = this.bodies.length - 1; i > 0; i--) {
            let X = (this.bodies[i - 1] as HTMLElement).offsetLeft;
            let Y = (this.bodies[i - 1] as HTMLElement).offsetTop;

            (this.bodies[i] as HTMLElement).style.left = X + "px";
            (this.bodies[i] as HTMLElement).style.top = Y + "px";
        }
    }

    checkHeadBody() {
        for (let i = 1; i < this.bodies.length; i++) {
            let bd = this.bodies[i] as HTMLElement;
            if (this.X === bd.offsetLeft && this.Y === bd.offsetTop) {
                throw new Error("snake_dead");
            }
        }
    }
}
export default Snake;
// score_panel.ts
class ScorePanel {
    score = 0;
    level = 1;
    scoreEle: HTMLElement;
    levelEle: HTMLElement;

    maxLevel: number;
    upScore: number;

    constructor(maxLevel: number = 10, upScore: number = 10) {
        this.scoreEle = document.getElementById("score")!;
        this.levelEle = document.getElementById("level")!;
        this.maxLevel = maxLevel;
        this.upScore = upScore;
    }

    addScore() {
        this.scoreEle.innerHTML = ++this.score + "";
        if (this.score % this.upScore === 0) {
            this.levelUp();
        }
    }

    levelUp() {
        if (this.level < this.maxLevel) {
            this.levelEle.innerHTML = ++this.level + "";
        }
    }
}
export default ScorePanel;
// game_control.ts
import Snake from "./snake";
import Food from "./food";
import ScorePanel from "./score_panel";

class GameControl {
    snake: Snake;
    food: Food;
    scorePanel: ScorePanel;
    direction: string = "";
    isLive = true;

    constructor() {
        this.snake = new Snake();
        this.food = new Food();
        this.scorePanel = new ScorePanel();
        this.init();
    }

    keydownHandler = (event: KeyboardEvent) => {
        this.direction = event.key;
    }

    init() {
        document.addEventListener("keydown", this.keydownHandler);
        this.run();
    }

    run() {
        let X = this.snake.X;
        let Y = this.snake.Y;

        switch(this.direction) {
            case "ArrowUp":
                Y -= 10;
                break;
            case "ArrowDown":
                Y += 10;
                break;
            case "ArrowLeft":
                X -= 10;
                break;
            case "ArrowRight":
                X += 10;
                break;
        }
        
        this.checkEat(X, Y);

        try {
            this.snake.X = X;
            this.snake.Y = Y;
        } catch (e) {
            alert((e as Error).message);
            this.isLive = false;
        }
        this.isLive && setTimeout(this.run.bind(this), 300 - (this.scorePanel.level - 1) * 30);
    }

    checkEat(x: number, y: number) {
        if (x === this.food.X && y === this.food.Y) {
            console.log("ate food");
            this.food.change();
            this.scorePanel.addScore();
            this.snake.addBody();
        }
    }

}

export default GameControl;
// food.ts
class Food {
    element: HTMLElement;
    constructor() {
        // `!` 表示判定该语句的结果不可能为空
        this.element = document.getElementById("food")!;
    }

    get X() {
        return this.element.offsetLeft;
    }

    get Y() {
        return this.element.offsetTop;
    }

    change() {
        let left = Math.round(Math.random() * 29)* 10;
        let top = Math.round(Math.random() * 29)* 10;
        this.element.style.left = top + "px";
        this.element.style.top = left + "px";
    }
}
export default Food;
// index.less
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font: bold 20px Courier;
}

#main {
    width: 360px;
    height: 420px;
    background-color: #b7d4a8;
    margin: 100px auto;
    border: 10px solid black;
    border-radius: 10px;

    display: flex;
    flex-flow: column;
    align-items: center;
    justify-content: space-around;

    #stage {
        width: 304px;
        height: 304px;
        border: 2px solid black;
        position: relative;

        #snake {
            &>div {
                width: 10px;
                height: 10px;
                background-color: black;
                border: 1px solid #b7d4a8;
                position: absolute;
            }
        }

        #food {
            width: 10px;
            height: 10px;
            position: absolute;
            left: 40px;
            top: 100px;
            display: flex;
            flex-flow: row wrap;
            justify-content: space-between;
            align-content: space-between;

            &>div {
                width: 4px;
                height: 4px;
                background-color: black;
            }
        }
    }

    #score-panel {
        width: 300px;
        display: flex;
        justify-content: space-between;
    }


}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/387012.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

华为机考入门python3--(14)牛客14-字符串排序

分类&#xff1a;列表、排序 知识点&#xff1a; 字典序排序 sorted(my_list) 题目来自【牛客】 def sort_strings_by_lex_order(strings): # 使用内置的sorted函数进行排序&#xff0c;默认是按照字典序排序 sorted_strings sorted(strings) # 返回排序后的字符串列…

H5 渐变3D旋转个人主页引导页源码

H5 渐变3D旋转个人主页引导页源码 源码介绍&#xff1a;一款渐变3D旋转个人主页引导页源码&#xff0c;可以做个人主页/旗下网站引导 下载地址&#xff1a; https://www.changyouzuhao.cn/10392.html

linux信号机制[二]

阻塞信号 信号相关概念 实际执行信号的处理动作称为信号递达(Delivery)信号从产生到递达之间的状态,称为信号未决(Pending)。[收到信号但是没有处理]进程可以选择阻塞 (Block )某个信号。被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作.注…

“从根到叶:深入理解堆数据结构“

​​​​​​​ 一.堆的概念及实现 1.1堆的概念 在数据结构中&#xff0c;堆是一种特殊的树形数据结构。堆可以分为最大堆和最小堆两种类型。 最大堆&#xff1a;对于堆中的任意节点&#xff0c;其父节点的值都不小于它的值。换句话说&#xff0c;最大堆中的根节点是堆中的最…

猫头虎分享已解决Bug || Invariant Violation in React: Element Type is Invalid ‍

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

java 数据结构ArrayList类

目录 什么是List 线性表 顺序表 ArrayList类 ArrayList无参方法 ArrayList有参方法 &#xff1f;通配符 ArrayList 的remove方法 ArrayList 的subList方法 Iterator&#xff1a;迭代器 使用ArrayList完成杨辉三角 什么是List 在集合框架中&#xff0c;List是一个接…

vue 向某个网址 传递数据

1. 需求 现在有一个网站需要 配置上另一个网站的东西 类似这样的东西吧 就是我需要再一个网站上 右边或者其他地方 放另一个页面的地址 这个地址需要给我传递东西 或我这个网站给其他的网站传递token了 id等 2.解决 window.parent.postMessage({ token: loginRes.token, id:…

第5个-模糊加载

Day 5 - Blurry Loading 1. 项目展示 2. 分析思路 变化过程 数字从 0 不断增长到 100&#xff1b;中间的百分比数字逐渐消失&#xff0c;即透明度 opacity 从 1 到 0&#xff1b;背景图片从模糊变为清晰&#xff0c;滤镜 filter.blur()的参数设置为从 30px 到 0px。 小 tips…

点云旋转(基于PCL)

实现代码为&#xff1a; //以中心化点进行旋转double theta atan(maindirection.a);//计算的是弧度单位for (int i 0; i < origipts.size(); i){pcl::PointXYZ tempone;tempone.x aftercenerlizepts[i].x*cos(theta) aftercenerlizepts[i].y*sin(theta) center.x;temp…

Hive调优——合并小文件

目录 一、小文件产生的原因 二、小文件的危害 三、小文件的解决方案 3.1 小文件的预防 3.1.1 减少Map数量 3.1.2 减少Reduce的数量 3.2 已存在的小文件合并 3.2.1 方式一&#xff1a;insert overwrite (推荐) 3.2.2 方式二&#xff1a;concatenate 3.2.3 方式三&#xff…

【Zigbee课程设计系列文章】Zigbee开发环境搭建

【Zigbee课程设计系列文章】Zigbee开发环境搭建 前言IAR 下载安装Z-Stack协议栈安装 &#x1f38a;项目专栏&#xff1a;【Zigbee课程设计系列文章】&#xff08;附详细使用教程完整代码原理图完整课设报告&#xff09; 前言 &#x1f451;由于无线传感器网络&#xff08;也即…

RMSNorm原理及代码

RMSNorm原理及代码 在大模型中使用层归一化有如下几个因素&#xff1a; 改善网络稳定性加速收敛速度提高模型的泛化能力 批量归一化是对一个批次内的数据进行归一化 层归一化是对一个样本中的不同特征进行归一化 如下是LayerNorm与RMSNorm的公式 在LLaMA中使用RMSNorm替代…

【华为云】容灾方案两地三中心实践理论

应用上云之后&#xff0c;如何进行数据可靠性以及业务连续性的保障是非常关键的&#xff0c;通过华为云云上两地三中心方案了解相关方案认证地址&#xff1a;https://connect.huaweicloud.com/courses/learn/course-v1:HuaweiXCBUCNXI057Self-paced/about当前内容为灾备常见理论…

NARF关键点提取原理简介

一、NARF2D边缘点探测的矩形平面的边长s和计算点p和上邻域的距离所用的k值 二、障碍物边缘和阴影边缘 三、NARF边缘点探测 四、NARF借助边缘点信息进行关键点检测 本人也是参考其他博主&#xff0c;以及这份英文文献写的(毕竟是英文文献&#xff0c;部分翻译肯定有些误差&…

企业计算机服务器中了mkp勒索病毒怎么办?Mkp勒索病毒解密处理

随着网络技术的不断发展&#xff0c;企业的生产运营也加大了步伐&#xff0c;网络为企业的生产运营提供了强有力保障&#xff0c;但网络是一把双刃剑&#xff0c;给企业带来便利的同时也为企业带来了严重的数据威胁。春节期间&#xff0c;云天数据恢复中心接到很多企业的值班人…

反序列化漏洞(一)Shiro漏洞CVE-2016-4437复现

★★免责声明★★ 文章中涉及的程序(方法)可能带有攻击性&#xff0c;仅供安全研究与学习之用&#xff0c;读者将信息做其他用途&#xff0c;由Ta承担全部法律及连带责任&#xff0c;文章作者不承担任何法律及连带责任。 1、前言 春节后第一篇&#xff0c;祝大家龙年一切顺利&…

MySQL-运维

一、日志 1.错误日志 错误日志是MySQL中最重要的日志之一&#xff0c;它记录了当mysql启动和停止时&#xff0c;以及服务器在运行过程中发生任何严重错误时的相关性息。当数据库出现任何故障导致无法正常使用时&#xff0c;建议首先查看此日志。 该日志是默认开启的&#xf…

国产制造,欧美品质:爱可声助听器产品质量获国际认可

随着科技的发展和全球化的推进&#xff0c;越来越多的中国制造产品开始走向世界舞台。其中&#xff0c;爱可声助听器凭借其卓越的产品质量&#xff0c;成为了国产制造的骄傲。 国产制造指的是在中国境内生产的产品&#xff0c;欧美品质则是指产品在设计、生产、质量控制等方面…

CSS3学习(一)

1. 语法规范 CSS主要由选择器和一条或多条的声明构成。 选择器用于指定CSS样式的HTML标签&#xff0c;花括号里面是对应的具体样式 属性与属性值以键值对的形式出现&#xff0c;属性与属性之间用分号隔开 <head>里写<style> 2. 基础选择器 【作用】选择标签使…

【计算机网络】FTP 文件传输协议

同样使用TCP 但使用了两个并行的TCP 控制链接 control connection 带外 out-of-band 传送的数据链接 data connection 对于FTP而言&#xff0c;控制链接贯穿了整个用户会话期间&#xff0c;数据链接每传输一个文件就有一次建立FTP是有状态&#xff08;state&#xff09;的&…