环境:
条目 | 版本 |
---|---|
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 配置完成。
如果觉得有用的话,不妨动动鼠标点个赞?如果哪儿出现疏漏或是我没有理解到的错误,我非常感谢您的指正。