环境:
条目 | 版本 |
---|---|
os | Windows 10 20H2 Enterprise |
node -v | v14.15.0 |
npm -v | 6.14.8 |
vscode | v1.51.1(20-11-10) |
这是一份学习笔记
一、项目创建
按照下面的几个步骤,创建 nodejs 项目
mkdir my-demo
cd .\my-demo
npm init -y
# -y 参数用于使用缺省配置,而跳过不断的回车
目前,我配置的目录结构如下
+ src
- index.ts
- package.json
我们的项目的开发语言是 typescript,既然如此,至少我们需要先安装好基础需要的包
npm install --save-dev typescript
# 然后,让 tsc 给我们生成一个初始配置文件
npx tsc --init
# npx 可以让我们直接运行 node_modules/.bin/ 目录下的脚本文件
然后调整以下配置, 在 compilerOptions
下,target
指定编译出的 js 代码的版本标准
小谈 target 版本设置:
查看支持情况,Node.js ES2015/ES6, ES2016…
可以得知 es2018 标准在 node v10.3.0 下就已经支持得很是完整了,es2019 在 node v12.4.0 下完整支持,es2020 在 node v14.5.0 下已经完整支持。这年头,不要告诉我,你的服务器环境还是 node v8.x ?
有些伙伴喜欢调成
es5
,只不过我个人觉得没有必要。因为编译器会生成许多兼容代码来让软件可以运行与低版本的 node 环境,这些兼容代码会影响代码性能,而且 node 在支持 新的语法的时候是做过优化的。我的环境一般是 > 12.x ,所以用了 es2019
outDir
是编译输出目录
isolatedModules
是保证每个文件都是一个独立模块
Strict Type-Checking Options
下的所有配置都直接勾选,都用了 typescript 了,还要啥 any ?
而与 compilerOptions
平行的 include
就是指的源代码路径,src/**/*
就是 src 目录下的所有文件
{
"compilerOptions": {
"target": "es2019", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
"outDir": "./dist", /* Redirect output structure to the directory. */
"removeComments": true, /* Do not emit comments to output. */
"isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
"strictNullChecks": true, /* Enable strict null checks. */
"strictFunctionTypes": true, /* Enable strict checking of function types. */
"strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
"strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
"noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
"alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
},
"include": ["src/**/*"]
}
至于其他配置,暂且就默认吧。现在,我们已经能用 tsc 了,算是搭建了一个基础环境。
二、使用 typescript 编写一个 koa 框架的 hello world
要使用 koa 框架编写一个服务端程序的话,首先需要的是安装必要的 koa 相关依赖
npm install --save koa koa-body @koa/router
# 由于我们使用的是 typescript, 所以还需要安装 typescript 以及 Types 类型定义的包
npm install --save-dev @types/koa @types/koa__router
然后在 src/index.ts
中插入一个 hello world 程序:
// src/index.ts
import Koa from 'koa';
import koaBody from 'koa-body';
import Router from "@koa/router";
const app = new Koa();
const router = new Router();
app.use(koaBody());
router.get('/', async (ctx) => {
ctx.body = { hello: 'world' }
})
app.use(router.routes());
const port = 3000;
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
export default app;
现在我们来试着编译一下
npx tsc
# 如果没出错的话继续运行
node dist/index.js
可以发现,编译成功
进入浏览器访问一下,已经有一个 json 字符串了(有格式是因为我装了插件)
这一步结束,咱们的项目已经能够跑起来了。
三、babel 配置 typescript
# 安装 babel 相关必要的依赖
npm install --save-dev @babel/core @babel/cli @babel/preset-env
# 安装 babel 解析 typescript 要用到的相关依赖
npm install --save-dev @babel/plugin-proposal-class-properties @babel/plugin-proposal-object-rest-spread @babel/preset-typescript
安装完成之后,项目根目录创建 babel 配置文件 babel.config.json
(也可以是 .js, .cjs, .mjs 后缀,但这都不重要,内容是一样的就行) ,并编写如下配置内容:
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": true
}
}
],
"@babel/preset-typescript"
],
"plugins": [
"@babel/proposal-class-properties",
"@babel/proposal-object-rest-spread"
]
}
上面的 json 文件内容,主要就是添加了两个预设配置,和两个插件。
@babel/preset-env
是 babel 官方准备的,设置的时候给个参数,让它知道代码运行于 node 环境
@babel/preset-typescript
也是官方的 : ) ,调用 tsc 解析 typescript
@babel/proposal-class-properties
支持类属性,这个特性目前好像是 es 2021 stage 3 吧,我也不是很清楚
@babel/proposal-object-rest-spread
支持对象展开符
然后在命令行先删除 dist
目录再编译,测试一下:
rmdir dist -r
npx babel src --out-dir dist --extensions ".ts"
运行成功。每次需要编译代码的时候就运行一遍上面的 npx babel xx
就好了。但...这么麻烦的参数,就不能简单一点么?
很遗憾的是,babel 的配置文件里并没有提供与之前 tsconfig.json
中的 include
和 compilerOptions.outDir
功能相似的配置项。所以你每次都要指定 --out-dir
和 --entensions
参数。
除了每次在命令行指定参数,就没有别的办法来简化命令行参数了么?
当然有,我们可以把这一长串几乎不会变化的命令写到 pacakge.json
中的 scripts
项目中,比如:
{
"scripts": {
"babel:build": "npx babel src --out-dir dist --extensions \".ts\"",
"start": "node dist/index.js",
"babel:dev": "npm run babel:build && npm run start"
},
}
之后,我们就可以使用 npm run babel:build
来编译,使用 npm run start
来启动服务器,使用 npm run babel:dev
来组合上述两条命令。
备注:
babel 监听文件修改,加入 --watch 参数
例如:
# https://babeljs.io/docs/en/babel-cli#compile-files
npx babel src --watch --out-dir dist --extensions ".ts"
node 监听文件修改,添加依赖 nodemon,然后命令行
# https://www.npmjs.com/package/nodemon
nodemon dist/index.js
目前,如果需要同时监听 ts 源码修改并及时刷新服务端,可以开两个命令行,一遍各运行一个,或者是用 npm-run-all - npm (npmjs.com)),使两个命令并行执行,不过,这里就不用介绍了。
这一节结束,我们的项目已经能够基于 babel 走向简单的工程化了。
四、让 webpack 来调用 babel 执行转译
webpack 在前端行业里真的真的太热门了,我断断续续了解过一些,今天也在摸索方法。
首先我们安装一些依赖
# webpack 核心依赖
npm install --save-dev webpack webpack-cli
# 让 webpack 能调用 babel 的依赖插件
npm install --save-dev babel-loader
# 其他一些辅助的,但特别有用的依赖
npm install --save-dev externals-dependencies clean-webpack-plugin
# 可选依赖,老实说,此纯后端项目没用上,但是它对于一些前端项目是很有用的
npm install --save-dev webpack-dev-server
让我们来开始写配置文件,在项目根目录创建 webpack.config.js
。
const path = require('path');
const externals = require('externals-dependencies');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry: './src/index.ts', // 项目入口文件,之后可以设置多入口
module: {
rules: [
{
// 设定转译规则,.ts, .js 使用 babel-loader 把任务交给 babel
test: /\.(ts|js)?$/,
use: {
loader: 'babel-loader',
options: { cacheDirectory: true }, // 缓存
},
exclude: /node_modules/, // 排除掉 node_modules 文件夹
},
],
},
// 开发服务器,其实此项目没有用上,配置的含义是以 contentBase 为根目录启动一个服务器
// 打开浏览器就可以访问该服务器,代码有更新会自动刷新页面,端口默认 8080
devServer: {
contentBase: './dist',
open: true,
},
resolve: {
extensions: ['.ts', '.js'], // 设置一下 webpack 要扫描的文件后缀
},
plugins: [
new CleanWebpackPlugin(), // 用插件清理一下 webpack 生成的某些垃圾
],
output: {
filename: 'index.js', // 输出文件的文件名
path: path.resolve(__dirname, 'dist'), // 输出文件所在路径,需要用绝对路径
},
target: 'node', // 服务端打包
node: {
global: true,
__filename: true,
__dirname: true,
},
externals: [externals()], // node 打包可去除一些警告
};
有些其他博客有用上了配置文件合并啥的功能插件,这儿目前先不考虑,而且还会增加对于新手入门的难度。
(明明是你自己不会)
现在编译运行就使用:
npx webpack
node ./dist/index.js
webpack 监听修改的方式:
npx webpack --watch
如果使用 webpack-dev-server, 就可以:
# 已经配置好了情况下
npx webpack-dev-server
OK。我们的项目向工程化的方向又进了一步。
五、使用 eslint 检查 typescript 项目
(问题引入)在上述的代码中我们已经能够使用 webpack 来编译项目了,现在,我们先启动 npx webpack --watch
来监听我们的文件修改。
然后在代码的某处,添加一点点“错误”:
// 省略其他
const d: number = 'helloworld';
console.log(d);
// 省略其他
上面两句代码,我讲字符串赋值给了一个 number
类型的变量,回头检查一下控制台。
怎么回事,居然编译成功了?我不是用的 typescript 么,居然没有进行类型检查?如果没有进行类型检查,我要这 typescript 有何用?
紧接着去核查生成了代码,发现对应位置的代码如下:
console.log("helloworld")
这段 js 代码也完全没有任何问题。
那 ts 究竟做了什么?
原来是 babel 只负责了转译 ts 代码的任务,并没有进行类型检查。既然如此,我们还是需要单独配置一下类型检查。
第一个解决方式是,直接把 tsc 作为类型检查工具
先在 tsconfig.json
添加如下配置,让 tsc 不要生成输出文件,也就是不要负责文件生成,而只负责检查类型。
{
"compilerOptions": {
"noEmit": true /* Do not emit outputs. */
}
}
然后,运行类型检查就在命令行使用:
npx tsc -w
同时,也运行起 webpack 的监听服务,两个 watch 服务,一遍负责转译,一边负责类型检查。效果看起来就像下面这样。
我们自动检查类型并编译的目标,算是完成了一半。
第二个方法便是使用 eslint 了。
控制台中运行命令:
npm install --save-dev eslint
# 安装完成既初始化 eslint
npx eslint --init
执行 npx eslint --init
后的样子应该是这样:
我用的 vscode 编辑器,安装了 dbaeumer.vscode-eslint
插件。上面的任务一执行结束,回到 vscode 我就看到了许多报错提示,可见,其实 npx eslint --init
已经帮我们做了一些配置。
module.exports = {
env: {
es2021: true,
node: true,
},
extends: [
'airbnb-base',
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 12,
},
plugins: [
'@typescript-eslint',
],
rules: {
},
};
当然,如果你不用 npx eslint --init
,也可以自己安装如下相关依赖,并写好上述配置文件。
devDependencies: {
"eslint": "^7.13.0",
"eslint-config-airbnb-base": "^14.2.1",
"eslint-plugin-import": "^2.22.1
"@typescript-eslint/eslint-plugin": "^4.7.0",
"@typescript-eslint/parser": "^4.7.0",",
}
但是这样还是不太够的,我们添加一些配置。添加后的配置文件如下:
module.exports = {
env: {
es2021: true,
node: true,
},
extends: [
'airbnb-base', // airbnb 的配置很受欢迎
'plugin:@typescript-eslint/recommended', // 启动该插件的推荐配置
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 12,
},
plugins: [
'@typescript-eslint',
],
rules: {
'linebreak-style': 'off',
'no-console': 'off',
'@typescript-eslint/no-var-requires': 'off',
},
};
保存后,在控制台执行
npx eslint src --ext .ts
# 或者 lint 某一些文件
npx eslint src/index.ts src/xxx.ts
接下来就可以输出有关 eslint 的错误信息了。类似于:
D:\DevDemo\demo\my-demo\src\index.ts
3:20 error Strings must use singlequote quotes
15:32 error Missing semicolon semi
16:3 error Missing semicolon semi
回到 vscode,看一看代码里还是不是有很多很多关于换行符,console 之类的报错, require 符号之类的报错,如果还有,不妨在 vscode 中使用
ctrl + shift + p
,然后在弹出的输入框中输入reload window
,按下回车,让 vscode 插件来重新加载 eslint 配置。
添加到 scripts
{
"scripts": {
"webpack:build": "webpack",
"webpack:watch": "webpack --watch",
"webpack:dev": "webpack && npm run start",
"lint": "eslint src --ext .ts"
}
}
我加了个 webpack 前缀,因为之前也配置了 babel 的相关任务。
OK,eslint 配置完成。
如果觉得有用的话,不妨动动鼠标点个赞?如果哪儿出现疏漏或是我没有理解到的错误,我非常感谢您的指正。