Node.js 使用

创建 Node 项目

npm init -y # 初始化 Node 项目

package.json 文件

这个文件记录了项目的相关信息。

{
  "name": "hello-node",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "node src/index.cjs",
    "serve": "node server/index.js",
    "compile": "babel src/babel --out-dir compiled"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    ...
  }
}

其中 scripts 项记录了我们可以对项目运行的命令。使用 npm run xxx 就可以运行对应的命令。

项目命名规则

类型 释义 例子
范围包 具备 @scope/project-name 格式,一般有一系列相关的开发依赖之间会以相同的 scope 进行命名。 @vue/cli@vue/cli-service 就是一系列相关的范围包。
普通包 其他命名都属于普通包。 vuevue-router

模块化

CommonJS

CommonJS 是在 ES Module 标准出现之前的事实标准。在老项目中非常常见。

遵循 CommonJS 标准的 JavaScript 文件可以使用 .cjs 或者 .js 后缀。

CommonJS 使用 module.exports 导出模块,使用 require('module/path') 导入模块。

默认导出
// module.cjs
module.exports = 'Hello World' // 导出一个字符串
// index.cjs
const m = require('./module.cjs') // 导入的也是字符串
console.log(m)
命名导出

其实我觉得本质上就是默认导出

// module.cjs
function foo() {
  console.log('Hello World from foo.')
}

const bar = 'Hello World from bar.'

module.exports = {
  foo,
  bar,
}
// index.cjs
const m = require('./module.cjs')
m.foo()
console.log(m.bar)

或者利用 ES6 的对象解构来直接拿到变量:

// index.cjs
const { foo, bar } = require('./module.cjs')
foo()
console.log(bar)

还可以导入时重命名:

// index.cjs
const {
  foo: foo2,  // 这里进行了重命名
  bar,
} = require('./module.cjs')

// 不会造成变量冲突
const foo = 1
console.log(foo)

// 用新的命名来调用模块里的方法
foo2()

ES Module

ES Module(ESM)是在 ES6 中推出的。ES(ECMAScript)是 JavaScript 标准的名称。

ESM 使用 export default(默认导出)和 export(命名导出)两个语法导出模块,使用 import ... from 'module/path' 导入模块。

默认导出
// module.mjs
export default 'Hello World'
// index.mjs
import m from './module.mjs'
console.log(m)
命名导出
// module.mjs
export function foo() { // 导出 foo
  console.log('Hello World from foo.')
}

export const bar = 'Hello World from bar.' // 导出 bar
// src/esm/index.mjs
import { foo, bar } from './module.mjs'
foo()
console.log(bar)

也可以使用 * as 变量名 的方式将模块所有命名挂在指定的变量上:

// index.mjs
import * as m from './module.mjs' // 将所有命名导出挂在 m 变量上
m.foo()
console.log(m.bar)

也可以导入时重命名:

// index.mjs
import {
  foo as foo2, // 这里进行了重命名
  bar
} from './module.mjs'

// 不会造成变量冲突
const foo = 1
console.log(foo)

// 用新的命名来调用模块里的方法
foo2()

使用 npm 包

npm install md5 -S # 安装包到本地,并列为生产依赖(默认)
npm install md5 -D # 安装包到本地,并列为开发依赖

TypeScript

数据类型

原始数据类型:string, number, boolean, bigint, symbol, null, undefined

sonst num: number = 1
const str = 'Hello World' // 这样也不会报错,因为 TS 会推导类型

数组:number[], UserItem[]

const strs: string[] = ['Hello World', 'Hi World']
const nums = [1, 2, 3] // 自动推导类型

接口和类

接口:interface

// 定义用户对象的类型
interface UserItem {
  name: string
  age: number
  enjoyFoods: string[]
  friendList?: UserItem[] // ? 表示可选属性
}

// 在声明变量的时候将其关联到类型上
const petter: UserItem = {
  name: 'Petter',
  age: 18,
  enjoyFoods: ['rice', 'noodle', 'pizza'],
  friendList: [
    {
      name: 'Marry',
      age: 16,
      enjoyFoods: ['pizza', 'ice cream'],
      friendList: [],
    }
  ]
}

继承:extends

// 这里继承了 UserItem 的所有属性类型,并追加了一个权限等级属性
interface Admin extends UserItem {
  permissionLevel: number
}

const admin: Admin = {
  name: 'Petter',
  age: 18,
  enjoyFoods: ['rice', 'noodle', 'pizza'],
  friendList: [
    {
      name: 'Marry',
      age: 16,
      enjoyFoods: ['pizza', 'ice cream'],
      friendList: [],
    }
  ],
  permissionLevel: 1
}

部分继承:使用 Omit 帮助类型

// 这里在继承 UserItem 类型的时候,删除了两个多余的属性
interface Admin extends Omit<UserItem, 'enjoyFoods' | 'friendList'> {
  permissionLevel: number
}

// 现在的 admin 就非常精简了
const admin: Admin = {
  name: 'Petter',
  age: 18,
  permissionLevel: 1,
}

类:class

// 定义一个类
class User {
  // constructor 上的数据需要先这样定好类型
  name: string

  // 入参也要定义类型
  constructor(userName: string) {
    this.name = userName
  }

  getName() {
    console.log(this.name)
  }
}

// 通过 new 这个类得到的变量,它的类型就是这个类
const petter: User = new User('Petter')
petter.getName() // Petter

类和接口之间也可以互相继承

联合类型:string | number

const ele: HTMLElement | null = document.querySelector('main') // ele 既可以是 HTMLElement,又可以是 null

函数

函数:

function sum1(x: number, y: number): number {
  return x + y
}

function sum(x: number, y: number, isDouble?: boolean): number { // 可选参数必须在必选参数后面
  return isDouble ? (x + y) * 2 : x + y
}

function sayHi(name: string): void { // 无返回值
  console.log(`Hi, ${name}!`)
}

函数重载:

function greet(name: string): string  // 对 string 类型的重载
function greet(name: string[]): string[]  // 对 string[] 类型的重载
function greet(name: string | string[]) { // 真正的函数体,此时可以省略返回类型
  if (Array.isArray(name)) {
    return name.map((n) => `Welcome, ${n}!`)
  }
  return `Welcome, ${name}!`
}

// 单个问候语,此时只有一个类型 string
const greeting = greet('Petter')
console.log(greeting) // Welcome, Petter!

// 多个问候语,此时只有一个类型 string[]
const greetings = greet(['Petter', 'Tom', 'Jimmy'])
console.log(greetings)
// [ 'Welcome, Petter!', 'Welcome, Tom!', 'Welcome, Jimmy!' ]

npm 包

一些早期的 npm 包是使用 JavaScript 写的,无法直接引入到 TypeScript 项目里。开源社区维护了一套 @types 类型包,提供这些包的 TypeScript 版本。

@types 类型包的命名格式为 @types/<package-name>,如 @types/md5

类型断言

类型断言可以让 TypeScript 不再检查变量的类型,而是直接使用你指定的类型。

// 原本要求 age 也是必须的属性之一
interface User {
  name: string
  age: number
}

// 但是类型断言过程中,遗漏了
const petter = {} as User
petter.name = 'Petter'

// TypeScript 依然可以运行下去,但实际上的数据是不完整的
console.log(petter) // { name: 'Petter' }

编译

直接运行 TypeScript 程序:

# 安装依赖
npm install -g ts-node
# 运行
ts-node main.ts

编译为 JavaScript:

# 安装依赖
npm install -g typescript
# 初始化 TypeScript 项目
tsc --init

编辑 TypeScript 项目配置文件 tsconfig.json

{
  "compilerOptions": {
    "target": "es6",   // JavaScript 版本
    "module": "es6",   // 模块规范
    "outDir": "./dist" // 输出目录
  }
}

编译:

tsc
posted @ 2024-06-01 13:14  Undefined443  阅读(13)  评论(0编辑  收藏  举报