3. k6 脚本基础:JavaScript 语法速览、模块系统、导入与导出、默认函数与生命周期
在深入 k6 自定义扩展与 Go 插件开发之前,必须牢固掌握 k6 脚本的核心编写范式。k6 使用 JavaScript(ES6+)作为脚本语言,但其运行环境并非浏览器或 Node.js,而是基于 Go 的 goja 引擎。这意味着 k6 脚本在语法上遵循 JavaScript 标准,但在 API 和模块系统上有着独特的实现。
3.1 JavaScript 语法速览(k6 环境)
k6 支持 ES6 的大部分语法特性,但受限于 goja 引擎,部分现代 API(如 Promise.allSettled、Array.prototype.flatMap)可能不被支持。以下是在 k6 脚本中高频使用的语法元素:
| 语法类别 | 常用特性 | k6 中的注意事项 |
|---|---|---|
| 变量声明 | let、const |
推荐使用 const 声明不变引用;避免使用 var |
| 箭头函数 | () => {} |
广泛用于定义 default 函数和回调 |
| 模板字符串 | `${variable}` |
用于拼接 URL、请求体等动态内容 |
| 解构赋值 | const { key } = obj |
常用于从 http.get() 返回值中提取 status、body |
| 展开运算符 | ...arr |
用于合并配置对象或参数数组 |
| 对象简写 | { name, age } |
简化配置对象的构建 |
💡 关键提示: k6 脚本中 不要使用
document、window、setTimeout 等浏览器 API。所有异步操作必须通过 k6 提供的 http、check、sleep 等内置模块完成。
3.2 k6 模块系统:导入与导出
k6 的模块系统遵循 ES6 模块规范,但路径解析规则与 Node.js 不同。模块分为三类:内置模块、本地文件模块 和 远程 HTTP(S) 模块。
3.2.1 模块分类与导入方式
| 模块类型 | 导入语法 | 示例 |
|---|---|---|
| 内置模块 | import { x } from 'k6/x' |
import http from 'k6/http' |
| 本地文件模块 | import { x } from './relative/path.js' |
import { randomUser } from './data/generator.js' |
| 远程模块 | import { x } from 'https://raw.githubusercontent.com/...' |
import { uuidv4 } from 'https://jslib.k6.io/k6-utils/1.4.0/index.js' |
📌 最佳实践: 将可复用的辅助函数(如数据生成、断言逻辑)抽取为本地模块,放在
./lib/ 或 ./helpers/ 目录下,通过相对路径导入。远程模块仅用于官方维护的 jslib 库,避免依赖不可控的外部源。
3.2.2 导出语法
k6 支持 export default 和 export named 两种导出方式:
// 文件: ./lib/helpers.js
// 命名导出
export function generateToken() {
return 'Bearer ' + Math.random().toString(36).substring(2);
}
export const BASE_URL = 'https://api.example.com';
// 默认导出(一个文件只能有一个)
export default class DataGenerator {
static randomEmail() {
return `user${Date.now()}@test.com`;
}
}
// 主脚本: script.js
import http from 'k6/http';
import { generateToken, BASE_URL } from './lib/helpers.js';
import DataGenerator from './lib/helpers.js';
export default function () {
const = generateToken();
const email = DataGenerator.randomEmail();
http.post(`${BASE_URL}/users`, JSON.stringify({ email }), {
headers: { Authorization: },
});
}
3.3 默认函数与生命周期
k6 脚本的执行遵循严格的 生命周期钩子 模型。理解这些阶段是编写可靠压测脚本的基础。
⚠️ 常见误区: 将初始化代码(如数据库连接、大文件加载)放在
default 函数内,会导致每次 VU 迭代都重复执行,严重降低压测性能。初始化代码应放在全局作用域或 setup 函数中。
3.3.1 生命周期阶段
- 初始化阶段(Init):加载模块、声明全局变量、读取配置。此阶段代码在 VU 初始化时执行一次。
- Setup 阶段:可选,在 VU 开始迭代前执行一次,用于准备共享数据(如登录 )。
- VU 迭代阶段(Default):
export default function是压测的核心,每个 VU 会按stages配置重复执行此函数。 - Teardown 阶段:可选,在所有 VU 迭代结束后执行一次,用于清理资源。
- HandleSummary 阶段:可选,用于自定义结果汇总输出。
3.3.2 生命周期代码结构
// 1. 初始化阶段(全局作用域)
import http from 'k6/http';
import { check, sleep } from 'k6';
import { SharedArray } from 'k6/data';
// 全局变量,所有 VU 共享
const BASE_URL = 'https://test-api.example.com';
const users = new SharedArray('users', function () {
return JSON.parse(open('./data/users.json'));
});
// 2. Setup 阶段(可选)
export function setup() {
const res = http.post(`${BASE_URL}/auth/login`, {
username: 'admin',
password: 'secret',
});
return { : res.json().access_ };
}
// 3. VU 迭代阶段(核心)
export default function (data) {
// data 是 setup 函数的返回值
const = data.;
const res = http.get(`${BASE_URL}/dashboard`, {
headers: { Authorization: `Bearer ${}` },
});
check(res, {
'status is 200': (r) => r.status === 200,
'response time < 500ms': (r) => r.timings.duration < 500,
});
sleep(1);
}
// 4. Teardown 阶段(可选)
export function teardown(data) {
// 清理资源,如删除测试用户
http.del(`${BASE_URL}/cleanup`, null, {
headers: { Authorization: `Bearer ${data.}` },
});
}
// 5. HandleSummary 阶段(可选)
export function handleSummary(data) {
return {
'stdout': textSummary(data, { indent: ' ', enableColors: true }),
'summary.json': JSON.stringify(data),
};
}
3.4 生命周期与 VU 执行模型
下面的 SVG 图展示了 k6 脚本的生命周期与 VU(虚拟用户)执行模型之间的关系:
3.5 模块系统与生命周期结合示例
下面的 SVG 图展示了模块导入、导出与生命周期钩子如何协同工作,构成一个完整的压测脚本:
3.6 常见陷阱与避坑指南
| 陷阱 | 错误示例 | 正确做法 |
|---|---|---|
| 在 default 函数中加载数据 | export default function() { const data = JSON.parse(open('./data.json')); } |
使用 SharedArray 或全局变量在初始化阶段加载 |
| 使用浏览器 API | localStorage.setItem('key', 'value') |
使用 k6 内置的 http、cookieJar 等模块 |
| 模块路径错误 | import { x } from 'helpers.js'(缺少 ./) |
本地模块必须使用相对路径 ./helpers.js |
| 异步操作未处理 | http.get(url).then(res => {})(k6 不支持 Promise) |
k6 的 HTTP 调用是同步的,直接使用返回值 |
| 在 setup 中生成大量数据 | 在 setup() 中生成 10 万条用户数据 |
使用 SharedArray 按需加载,或分批生成 |
📌 核心要点:
- k6 脚本使用 ES6 模块语法,但运行在 goja 引擎上,不支持浏览器或 Node.js 特有 API。
- 模块导入分为内置、本地、远程三类,路径规则与 Node.js 不同。
- 生命周期包含 Init → Setup → Default → Teardown → HandleSummary 五个阶段,其中 Default 是压测核心。
- 初始化代码(模块加载、数据准备)必须放在全局作用域,避免在 VU 迭代中重复执行。
- 使用
SharedArray在 VU 间共享只读数据,使用data参数传递 setup 的返回值。