项目架构之Monolithic/Multirepo/MonorepoMonolithic单仓单模块 架构
所有的代码都位于一个单一的仓库内,通常是在 src 目录下组织不同的模块和组件,也就是初学时接触到的项目结构。
结构12345678910111213my-vue-app/├── public/├── src/│ ├── assets/│ ├── components/ # 所有共享的Vue组件│ ├── views/ # 页面级别的组件│ ├── router/ # 路由配置│ ├── store/ # Vuex 状态管理│ ├── utils/ # 工具函数│ └── App.vue│ └── main.js├── package.json└── vue.config.js
优势构建一体化,由根 package.json 统一维护依赖,版本冲突少;
一次构建、一次部署,CI/CD 流程简单;
缺 ...
大前端
未读为什么要测试现在的前端不只是“写页面”
表单要校验、要防重复提交
按钮要点了变 loading
弹窗要控制显示隐藏
组件之间要传值、通信
而这些都是逻辑,既然有逻辑,就可能出错
比如某个按钮失灵了,总不能每次都手动点一遍看事件报错吧
三层测试
别被“单元测试”“E2E”这些词吓到,其实就三类:
单元测试
测一个零件,也叫“单测”
比如你写了个 <Button /> 组件,就单独测它:
文字对不对?
loading 时显示啥?
点了会不会触发事件?
工具:Vitest(快且简单,和 Vite 适配)
集成测试
测几个零件合在一起
比如你有个登录表单,用了 <Input />、<Button />,还要调 API
你得测:
输入用户名密码,点登录,是不是调了 API?
校验失败,有没有提示?
工具:还是 Vitest + Mock(假装调接口)
E2E 测试
模拟用户真实操作
比如你让用户从登录到下单,走完整流程
你写个脚本,让电脑自动:
打开浏览器
输入账号密码
点登录
加入购物车
提交订单
工具推荐:Playwright(比 ...
工具
未读为什么选择VitestJest 与 Vitest对比
Jest
Vitest
构建基础
Webpack / Babel
原生 ESM,基于 Vite
启动速度
较慢(需打包,但提供了缓存机制)
极快(无需打包,Vite 的快速冷启动,按需加载)
内存占用
高
低
语法支持
需要额外的配置来支持现代 JavaScript(例如 ES Modules、TypeScript)需要安装额外的转换器(如 Babel 或 ts-jest)
不需要额外适配
调试体验
一般
Watch和热更新,可直接在浏览器/VS Code 中调试
社区生态
成熟,插件丰富
快速发展,Vue/React 生态支持良好
所以,对于使用 Vite 的项目,Vitest 是更现代、更高效的选择
⚠️ 但 Jest 仍适用于 Node.js 服务端或复杂兼容性场景
使用核心测试工具exprectexpect 断言:验证行为是否符合预期
12345678// 检查字符串是否包含子串expect("Hello World").toConta ...
工具
未读你是否曾被这些名词搞晕:打包工具、构建工具、Webpack、Vite,它们到底是干嘛的?为什么前端开发离不开它们?
前端为什么需要“打包”当你开发一个网站时
你有巨量文件:几十个 .js 脚本、一堆 .css 样式、一些图片 .png、字体 .woff、甚至 .vue 或 .ts 文件
浏览器很“笨”:
它一次只能加载有限的文件(太多请求会慢)
它不认识 .vue、.ts 或最新的 JS 语法(如 import),往往需要额外配置
它不知道文件之间的依赖关系(哪个 JS 需要先加载)
你想做优化:比如压缩代码体积、合并文件、给图片瘦身
现在市面上的构建工具就是来解决这些问题的
打包工具可以
指挥:从一个入口文件(比如 main.js)开始,找出所有它需要的文件(依赖),再找出这些文件需要的文件,最后呈现一个巨大的依赖关系网
翻译:把浏览器看不懂的语言(如 .ts, .vue, .scss, 新 JS 语法)转换成浏览器能懂的 .js 和 .css
打包:把处理好的文件,按照规则组合、压缩、优化,最终生成少数几个(甚至一个)高效的文件(如 stats.es.html, index. ...
JS
未读隐式转换
在某些操作中,JavaScript 自动将一种数据类型转换为另一种数据类型
以下是常见场景
字符串拼接
用 + 时,如果其中一个操作数是字符串,另一个操作数会被自动转换为字符串
123console.log(1 + "2"); // 输出: "12"console.log("Hello" + 3); // 输出: "Hello3"console.log(true + " world"); // 输出: "true world"
数字运算
数学运算时,非数字类型的值会被隐式转换为数字
1234console.log(10 - "5"); // 输出: 5console.log("10" * 3); // 输出: 30console.log("10" / 2); // 输出: 5console.log("abc" - 1); // 输出: NaN (无法转换为数字)
布尔值 ...
JS
未读数据类型+类型判断JavaScript数据类型分为基本类型与引用类型。
基本类型包括undefined、null、boolean、number、string、symbol(ES6)、bigint(ES11);
引用类型为object(如数组、函数等)。
类型判断方法如下:
typeof:返回类型字符串,但typeof null返回"object"(历史遗留问题),且无法区分数组与对象(均返回"object")。
instanceof:检测对象原型链是否包含构造函数(如[] instanceof Array为true),但跨全局环境(如iframe)时失效。
Object.prototype.toString.call():精确返回[object Type]格式(如数组返回"[object Array]"),可识别所有内置类型(包括null返回"[object Null]")。
Array.isArray():专用于判断数组,避免instanceof的跨环境问题。
示例对比:
typeof 42 → & ...
JS
未读单点登录概念
SSO,是一种身份验证机制,运行用户通过一次登录可以访问多个相关但独立的软件系统
一旦用户登录一个系统,所有相关系统都将识别该用户为已认证状态
实现背景
认证中心:颁发令牌
应用A - 应用B:两个独立应用,共享同一个认证中心
技术栈
JS、Nodejs、OAuth 2.0协议
流程
前端部分
用户访问A
A检测登录状态 -> (未登录重定向认证中心)
1234567891011121314151617// 检查本地是否有有效的访问令牌function checkLoginStatus() { const token = localStorage.getItem('accessToken'); if (!token) { // 如果没有令牌,重定向到认证中心 redirectToAuthServer(); } else { console.log('用户已登录'); }}function redirectToAuthServer() ...
JS
未读原型原型链原型
每个 JS 对象(除 null)都有一个隐藏的 [[Prototype]] 属性,指向它的“原型对象”(简称原型)
原型对象也是一个普通对象,它包含可以被其他对象共享的属性和方法
作用:存放一些属性和方法 + 在JS中实现继承
原型链可以通过实例对象的 __proto__ 属性访问原型
1234const arr = new Array(1, 2, 3)arr.reverse() // 翻转arr.sort() // 排序 // 这些方法都是挂载到Array.prototype上的,可以共享给所有的数组实例使用console.log(Array.prototype === arr.__proto__); // true
所有原型对象的终点是null
原型链的顶端通常是 Object.prototype
当访问一个对象的属性或方法时,JS 会沿着对象的原型链逐级向上查找,直到找到该属性或到达原型链的终点(null)。
12const obj = {};console.log(Object.prototype.__proto__) ...
JS
未读new基础
new会创建空的新对象{},这个对象会被作为构造函数的上下文(就是this)
新对象会链接到原型,内部属性 [[Prototype]] (__proto__ 指向构造函数的 prototype 属性)
1234567891011121314function Person(name, age) { // 构造函数首字母通常大写,以区分普通函数 this.name = name; this.age = age;}Person.prototype.greet = function () { // 设置原型上的方法 console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);};// 使用 new 创建实例const alice = new Person('Alice', 25); // 创建了一个新的对象 {...} // alice.__pr ...
JS
未读防抖和节流防抖
重新开始,只执行最后一次
搜索框输入、文本编辑器实时保存
1<input type="text" class="ipt" @keyup="handleKeyUp($event)" />
123456789const timerId = ref(null); // 使用 ref 来存储定时器 IDconst handleKeyUp = function (event) { if (timerId.value !== null) { clearTimeout(timerId.value); // 清除之前的定时器 } timerId.value = setTimeout(() => { console.log(event.target.value); // 打印输入框的值 }, 1000);};
节流
中间禁用,只执行最先一次
快速点击、鼠标滑动、scroll事件、下拉加载、视频 ...