项目创建与环境搭建
创建项目
mkdir my-koa-project
cd my-koa-project
npm init -y
npm install koa
npm i koa-router创建 index.js 文件
const Koa = require("koa")
const app = new Koa()
app.use(async (ctx, next) => {
ctx.body = "Hello World"
})
app.listen(3000, () => {
console.log("server is running at http://localhost:3000")
})添加 koa-router 路由
const Koa = require("koa")
const Router = require("koa-router")
const app = new Koa()
const router = new Router()
router.get("/", async (ctx, next) => {
ctx.body = "Hello World"
})
router.get("/api", async (ctx, next) => {
ctx.body = "Hello api"
})
// allowedMethods() 方法用于根据 ctx.status 设置 response 响应头中的 Allow 字段,表示服务器支持的 HTTP 方法
app.use(router.routes()).use(router.allowedMethods())
app.listen(3000, () => {
console.log("server is running at http://localhost:3000")
})koa-body、@koa/cors
npm install koa-body
npm install @koa/cors使用 插件
const Koa = require("koa")
const Router = require("koa-router")
const app = new Koa()
const router = new Router()
const { koaBody } = require("koa-body")
const cors = require("@koa/cors")
router.prefix("/v1")
router.get("/", async (ctx, next) => {
ctx.body = "Hello World"
})
app.use(koaBody())
app.use(cors())
app.use(router.routes()).use(router.allowedMethods())
app.listen(3000, () => {
console.log("server is running at http://localhost:3000")
})koa-json
koa-json 是一个中间件,用于在 Koa 应用中自动格式化 JSON 响应。它可以美化输出的 JSON 数据,使其更具可读性
默认美化 josn 输出
const json = require('koa-json')
const Koa = require('koa')
const app = new Koa()
app.use(json())
app.use((ctx) => {
ctx.body = { foo: 'bar' }
})
// @request: GET /
//
// @response:
// {
// "foo": "bar"
// }配置
const Koa = require('koa')
const app = new Koa()
app.use(json({ pretty: false, param: 'pretty' }))
app.use((ctx) => {
ctx.body = { foo: 'bar' }
})
// @request: GET /
//
// @response:
// {"foo":"bar"}
// @request: GET /?pretty
//
// @response:
// {
// "foo": "bar"
// }koa-compose
koa-compose 是一个用于将多个中间件函数组合成一个单一可调用函数的工具,在 Koa 应用程序中非常有用。通过使用 koa-compose,你可以将多个中间件组织成链式调用,从而提高代码的可读性和可维护性。
npm install koa-compose
yarn add koa-compose没有使用 koa-compose 前
import Koa from "koa"
import Router from "koa-router"
const app = new Koa()
const router = new Router()
import { koaBody } from "koa-body"
import cors from "@koa/cors"
router.prefix("/v1")
router.get("/", async (ctx, next) => {
ctx.body = "Hello World"
})
router.get("/api", async (ctx, next) => {
ctx.body = "Hello api"
})
app.use(koaBody())
app.use(cors())
// allowedMethods() 方法用于根据 ctx.status 设置 response 响应头中的 Allow 字段,表示服务器支持的 HTTP 方法
app.use(router.routes()).use(router.allowedMethods())
app.listen(3000, () => {
console.log("server is running at http://localhost:3000")
})使用 koa-compose 后
import Koa from "koa"
import Router from "koa-router"
import compose from "koa-compose"
const app = new Koa()
const router = new Router()
import { koaBody } from "koa-body"
import cors from "@koa/cors"
router.prefix("/v1")
router.get("/", async (ctx, next) => {
ctx.body = "Hello World"
})
router.get("/api", async (ctx, next) => {
ctx.body = "Hello api"
})
const middleware = compose([koaBody(), cors()])
app.use(middleware)
// allowedMethods() 方法用于根据 ctx.status 设置 response 响应头中的 Allow 字段,表示服务器支持的 HTTP 方法
app.use(router.routes()).use(router.allowedMethods())
app.listen(3000, () => {
console.log("server is running at http://localhost:3000")
})Koa-compress
koa-compress 是一个用于在 Koa 应用中启用 HTTP 压缩(例如 gzip、deflate、brotli 等)的中间件。它可以显著减少响应体的大小,从而提高应用的性能,特别是在网络带宽有限的情况下
npm install koa-compress以下是一个使用 koa-compress 的简单示例:
const Koa = require('koa');
const compress = require('koa-compress');
const app = new Koa();
// 默认配置
app.use(compress());
app.use(ctx => {
ctx.body = 'Hello, World!';
});
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});配置选项
koa-compress 提供了一些配置选项,让你可以自定义压缩行为。以下是一些常用选项:
filter:指定哪些响应应该被压缩。默认只压缩符合特定类型(如文本)的响应threshold:指定响应体的最小字节数,只有大于此阈值的响应才会被压缩。默认值为1024(即 1KB)gzip:启用或禁用 gzip 压缩,默认启用deflate:启用或禁用 deflate 压缩,默认禁用br:启用或禁用 Brotli 压缩,默认禁用
以下是一个带有自定义配置的示例:
const Koa = require('koa');
const compress = require('koa-compress');
const app = new Koa();
// 自定义配置
app.use(compress({
filter(content_type) {
return /text/i.test(content_type);
},
threshold: 2048, // 2KB
gzip: {
flush: require('zlib').constants.Z_SYNC_FLUSH
},
deflate: {
flush: require('zlib').constants.Z_SYNC_FLUSH,
},
br: false, // 禁用 Brotli 压缩
}));
app.use(ctx => {
ctx.body = 'Hello, World!';
});
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});配置选项详细说明
-
filter:- 类型:
function - 作用:指定哪些响应内容类型应被压缩。默认值是一个函数,压缩所有类型
- 示例:
filter: content_type => /text/i.test(content_type)
- 类型:
-
threshold:- 类型:
number - 作用:只有响应体大小大于此值的响应才会被压缩。默认值为
1024字节 - 示例:
threshold: 2048(只压缩大于 2KB 的响应)
- 类型:
-
gzip:- 类型:
object|boolean - 作用:启用或配置 gzip 压缩。设置为
false禁用 gzip - 示例:
gzip: { flush: require('zlib').constants.Z_SYNC_FLUSH }
- 类型:
-
deflate:- 类型:
object|boolean - 作用:启用或配置 deflate 压缩。设置为
false禁用 deflate - 示例:
deflate: { flush: require('zlib').constants.Z_SYNC_FLUSH }
- 类型:
-
br:- 类型:
object|boolean - 作用:启用或配置 Brotli 压缩。设置为
false禁用 Brotli - 示例:
br: { flush: require('zlib').constants.BROTLI_OPERATION_FLUSH }
- 类型:
Brotli 压缩
如果需要启用 Brotli 压缩,需要确保 Node.js 版本支持 Brotli(Node.js v11.7.0 及以上版本支持)。可以通过以下方式启用:
const Koa = require('koa');
const compress = require('koa-compress');
const app = new Koa();
app.use(compress({
br: {
params: {
[require('zlib').constants.BROTLI_PARAM_QUALITY]: 4,
},
},
}));
app.use(ctx => {
ctx.body = 'Hello, World!';
});
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});配置 Babel
Babel 可以帮助你在 Node.js 环境中使用最新的 JavaScript 特性。安装必要的 Babel 包:
npm install @babel/core @babel/node @babel/preset-env然后在项目根目录下创建一个 .babelrc 文件,配置 Babel:
{
"presets": ["@babel/preset-env"]
}设置 Nodemon
Nodemon 可以帮助你在文件变化时自动重启你的 Koa 服务器。安装 Nodemon:
npm install nodemon -D然后在 package.json 中配置脚本来使用 Babel 和 Nodemon:
"scripts": {
"start": "nodemon --exec babel-node index.js"
}设置环境变量
npm install cross-env -D安装 webpack
在一个 Koa2 项目中安装 Webpack 通常是为了处理前端资源的构建和打包。虽然 Koa 是一个后端框架,它也可以用来构建全栈应用,包括前端部分。在这种情况下,Webpack 可以用于以下目的:
- 打包前端资源:将 JavaScript、CSS、图片等前端资源打包成一个或多个文件,优化加载速度和性能
- 处理现代 JavaScript 语法:通过 Babel 等加载器(loader),Webpack 可以将现代 JavaScript 语法(如 ES6、ES7)转译成兼容性更好的语法,以支持更多的浏览器
- 模块化开发:支持模块化开发,允许你使用
import和export语法将代码分割成多个模块,增强代码的可维护性和可读性 - 热模块替换(HMR):提高开发效率,在修改代码时无需刷新浏览器,立即看到更改的效果
- 优化和压缩:通过插件和加载器,Webpack 可以优化和压缩代码,减少文件大小,提高加载速度
下面是一个示例,说明如何在 Koa2 项目中配置 Webpack 来处理前端资源
npm i webpack webpack-cli -D
npm i clean-webpack-plugin webpack-node-externals babel-loader -Dwebpack.config.js
node: { __dirname: true, __filename: true } 的意思是告诉 Webpack 在构建过程中保留 __dirname 和 __filename 的原始值,不对它们进行替换
const path = require("path")
const nodeExternals = require("webpack-node-externals")
const { CleanWebpackPlugin } = require("clean-webpack-plugin")
const webpackConfig = {
target: "node",
mode: "development",
entry: {
server: path.join(__dirname, "./src/index.js")
},
output: {
path: path.join(__dirname, "./dist"),
filename: "[name].bundle.js"
},
devtool: "eval-source-map", // "eval-source-map
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: {
loader: "babel-loader"
},
exclude: [path.join(__dirname, "/node_modules")]
}
]
},
externals: [nodeExternals()],
plugins: [new CleanWebpackPlugin()],
node: {
__dirname: true,
__filename: true
}
}
module.exports = webpackConfig执行命名编译项目
npx webpack将 commonjs 转为 es6 语法
const Koa = require("koa")
const Router = require("koa-router")
const app = new Koa()
const router = new Router()
const { koaBody } = require("koa-body")
const cors = require("@koa/cors")
router.prefix("/v1")
router.get("/", async (ctx, next) => {
ctx.body = "Hello World"
})
router.get("/api", async (ctx, next) => {
ctx.body = "Hello api"
})
app.use(koaBody())
app.use(cors())
// allowedMethods() 方法用于根据 ctx.status 设置 response 响应头中的 Allow 字段,表示服务器支持的 HTTP 方法
app.use(router.routes()).use(router.allowedMethods())
app.listen(3000, () => {
console.log("server is running at http://localhost:3000")
})修改完成后
import Koa from "koa"
import Router from "koa-router"
const app = new Koa()
const router = new Router()
import { koaBody } from "koa-body"
import cors from "@koa/cors"
router.prefix("/v1")
router.get("/", async (ctx, next) => {
ctx.body = "Hello World"
})
router.get("/api", async (ctx, next) => {
ctx.body = "Hello api"
})
app.use(koaBody())
app.use(cors())
// allowedMethods() 方法用于根据 ctx.status 设置 response 响应头中的 Allow 字段,表示服务器支持的 HTTP 方法
app.use(router.routes()).use(router.allowedMethods())
app.listen(3000, () => {
console.log("server is running at http://localhost:3000")
})执行命令从 node src/index.js 转变为 babel-node src/index.js
babel-node src/index.js修改 package.json 命令
"scripts": {
"start": "nodemon --exec babel-node src/index.js"
}优化 webpack 配置
Webpack EnvironmentPlugin
EnvironmentPlugin 是 Webpack 内置的一个插件,它可以简化将环境变量传递给前端代码的过程。这个插件通过将指定的环境变量注入到代码中,使得在代码中可以访问这些环境变量
const webpack = require('webpack');
module.exports = {
// 其他配置项
plugins: [
new webpack.EnvironmentPlugin(["NODE_ENV"])
]
};然后在源代码中,可以这样使用:
if (process.env.NODE_ENV === 'production') {
// 生产环境的配置或逻辑
} else {
// 非生产环境的配置或逻辑
}你也可以为环境变量提供默认值,如果 NODE_ENV 没有定义,process.env.NODE_ENV 将被设置为 'development'
new webpack.EnvironmentPlugin({
NODE_ENV: 'development', // 默认值
DEBUG: false
})terser-webpack-plugin
terser-webpack-plugin 是一个用于优化和压缩 JavaScript 代码的 Webpack 插件。它使用 Terser(一个快速、现代化的 JavaScript 压缩器)来减少输出文件的体积,从而提高页面加载速度和性能。
npm install terser-webpack-plugin -D在 Webpack 配置中使用 terser-webpack-plugin 来优化和压缩输出的 JavaScript 文件:
const TerserPlugin = require('terser-webpack-plugin');
const path = require('path');
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
optimization: {
minimize: true,
minimizer: [new TerserPlugin()],
},
};配置选项
terser-webpack-plugin 提供了多种配置选项,让你可以控制压缩过程的细节。以下是一些常用选项:
-
compress:控制压缩行为的选项,例如drop_console可以去除所有的console语句 -
mangle:控制变量名混淆。如果设置为true,Terser 将混淆变量名以减少代码体积 -
output:控制输出行为的选项。例如,comments: false可以去除所有注释 -
extractComments:控制是否将注释提取到单独的文件。如果设置为false,则不会提取注释
为了在压缩后的代码中保留 Source Maps,可以配置 terser-webpack-plugin 来生成 Source Maps:
const TerserPlugin = require('terser-webpack-plugin');
const path = require('path');
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
optimization: {
minimize: true,
minimizer: [new TerserPlugin({
terserOptions: {
compress: {
drop_console: true, // 去除 console 语句
},
mangle: true, // 混淆变量名
output: {
comments: false, // 去除注释
},
},
extractComments: false, // 是否提取注释到单独文件
sourceMap: true, // 保留 Source Maps
})],
},
};多个压缩插件
可以在 optimization.minimizer 数组中使用多个压缩插件。例如,同时使用 terser-webpack-plugin 和 css-minimizer-webpack-plugin 来压缩 JavaScript 和 CSS:
const TerserPlugin = require('terser-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const path = require('path');
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
optimization: {
minimize: true,
minimizer: [
new TerserPlugin(),
new CssMinimizerPlugin(),
],
},
};webpack-merge
webpack-merge 是一个用于合并多个 Webpack 配置对象的工具。它在管理 Webpack 配置时非常有用,尤其是当你需要根据不同的环境(如开发环境和生产环境)进行不同配置时
webpack-merge 可以合并多个 Webpack 配置对象
npm install webpack-merge -Dwebpack.config.base.js
const path = require("path")
const webpack = require("webpack")
const nodeExternals = require("webpack-node-externals")
const { CleanWebpackPlugin } = require("clean-webpack-plugin")
const webpackConfig = {
target: "node",
entry: {
server: path.join(__dirname, "./src/index.js")
},
output: {
path: path.join(__dirname, "./dist"),
filename: "[name].bundle.js"
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: {
loader: "babel-loader"
},
exclude: [path.join(__dirname, "/node_modules")]
}
]
},
externals: [nodeExternals()],
plugins: [new CleanWebpackPlugin(), new webpack.EnvironmentPlugin(["NODE_ENV"])],
node: {
__dirname: true,
__filename: true
}
}
module.exports = webpackConfigwebpack.config.dev.js
const { merge } = require("webpack-merge")
const NodemonPlugin = require("nodemon-webpack-plugin")
const baseWebpackConfig = require("./webpack.config.base")
const webpackConfig = merge(baseWebpackConfig, {
mode: "development",
devtool: "eval-source-map",
stats: { children: false }, // Hide children information
plugins: [new NodemonPlugin()]
})
module.exports = webpackConfigwebpack.config.prod.js
const { merge } = require("webpack-merge")
const baseWebpackConfig = require("./webpack.config.base")
const TerserWebpackPlugin = require("terser-webpack-plugin")
const webpackConfig = merge(baseWebpackConfig, {
mode: "production",
stats: { children: false, warnings: false },
optimization: {
minimizer: [
new TerserWebpackPlugin({
terserOptions: {
warnings: false,
compress: {
warnings: false,
// 是否注释掉console
drop_console: false,
dead_code: true,
drop_debugger: true
},
output: {
comments: false,
beautify: false
},
mangle: true
},
parallel: true
})
],
splitChunks: {
cacheGroups: {
commons: {
name: "commons",
chunks: "initial",
minChunks: 3,
enforce: true
}
}
}
}
})
module.exports = webpackConfig添加脚本
在 package.json 中添加脚本以运行不同的配置:
{
"scripts": {
"build": "cross-env NODE_ENV=production webpack --config config/webpack.config.prod.js",
"build:dev": "cross-env NODE_ENV=development webpack --config config/webpack.config.prod.js",
}
}splitChunks
splitChunks 是 Webpack 4 引入的一个特性,用于提取和优化代码分割。它允许你将公共模块提取到单独的文件中,从而实现更好的缓存和更快的加载速度
splitChunks: {
cacheGroups: {
commons: {
name: "commons",
chunks: "initial",
minChunks: 3,
enforce: true
}
}
}cacheGroups 是 splitChunks 的一个属性,用于定义特定的分割策略。你可以在 cacheGroups 中定义多个分割策略。每个策略都是一个命名的组。
配置中定义了一个名为 commons 的缓存组
-
name: "commons": 提取的公共模块将被放到一个名为commons.js的文件中 -
chunks: "initial":- 指定在哪些块之间共享模块
"initial"表示只选择初始块(initial chunks)- 其他选项包括
"async"(仅选择异步加载的块)和"all"(选择所有块)
-
minChunks: 3:- 指定至少被多少个块共享的模块才会被提取到这个缓存组
- 在这个例子中,只有被至少三个不同块引用的模块才会被提取到
commons.js中
-
enforce: true:- 强制执行这个缓存组的规则,忽略其他设置
- 即使不满足
splitChunks.minSize,splitChunks.maxAsyncRequests,splitChunks.maxInitialRequests等全局选项,仍然会执行此分割策略
作用
这段配置的主要作用是:
-
提取公共模块:它将那些被至少三个不同入口文件引用的模块提取到一个单独的
commons.js文件中 -
提升加载效率:减少初始加载的体积,提高加载效率,因为公共模块会被浏览器缓存,只需加载一次
-
优化缓存:通过将不常变化的代码提取到公共模块中,优化了缓存策略,使得后续页面加载更快
假设你有三个入口文件 entry1.js, entry2.js, 和 entry3.js,并且它们都引用了同一个库 commonLib.js 。在这种情况下,commonLib.js 会被提取到 commons.js 中,因为它被三个不同的入口文件引用