一、HTML
1.1 HTML5新特性
- 语义化标签:header,footer,article
- 多媒体支持:audio,video
- 本地存储:localStorage,sessionStorage
- 图形绘制:Canvas/WebGL
- 推送:WebSocket(全双工通信)
- 开启后台线程技术:Web Workers(避免页面卡顿)
- 流程:
- 创建 → const worker = new Worker(‘worker.js’)
- 发任务 → worker.postMessage(数据)
- 收结果 → worker.onmessage = e => { 用 e.data }
- 流程:
1.2 meta 标签
- 作用:
- 确定编码-防乱码,
- 适配手机(
viewport-fit=cover) - 写描述方便搜索和分享
1.3 浏览器渲染步骤
- 过程:
- 解析HTML生成DOM树(js会阻塞)
- 解析CSS生成CSSOM树
- 合并DOM和CSSOM生成渲染树(过滤隐藏元素)
- 先布局计算位置,再绘制像素到屏幕
- 性能核心优化:避免重复操作dom + 减少重排(优先使用transform/opacity触发GPU加速)
- 问题及解决方案:
- CSS阻塞渲染:把css放在
head中提前加载 - JS阻塞DOM解析:JS异步加载或放在body底部
- defer:异步加载,DOM解析完之后再执行
- async:异步加载,立即执行(下载不阻塞,执行可能会阻塞DOM解析)
- CSS阻塞渲染:把css放在
- 相关概念
- 重排(回流):布局发生变化
- 重绘:样式发生变化
- JS为什么会阻塞DOM解析:JS可能通过document.write()或DOM API修改文档结构
二、CSS
2.1 CSS3新特性
- 弹性布局 - flex
- 排列方向:flex-direction: column/row;
- 对齐方式:
- 主轴:justify-content: flex-start/flex-end/center/space-around/space-between/space-evently
- 交叉轴:align-items: stretch/flex-start/flex-end/center/baseline
- 空间分配:flex: 1; -> flex-grow:1; flex-shrink:1; flex-basis: 0%;(扩展收缩比例为1,初始尺寸为0)
- 间隔:gap
- 盒模型 - (box-sizing: border-box)
- 推荐使用ie盒模型:元素width/height = content宽高 + padding + border
- 响应式设计
- 媒体查询 @media (max-width: 768px)
- 视口单位 vw/vh
- 交互动画:Transform + Transition
- 视觉增强:圆角/阴影/渐变
- 选择器加强
- 属性选择器
- 伪类选择器:描述元素状态 :first-child
- 伪元素选择器:创建虚拟元素 ::after
- 工程能力:
- css变量:–定义,var使用
- 计算函数:calc()支持单位混合计算(vw + px)
2.2 BFC-块级格式化上下文
- 定义:独立的布局环境,内外互不干扰(玻璃罩)
- 触发创建bfc方式:(最新推荐:display: flex-root)
- float: left/right; /* 浮动 */
- overflow: hidden/auto; /* 溢出隐藏 */
- position: absolute/fixed; /* 绝对定位 */
- display: inline-block/flex/grid/table-cell; /* 特殊display值 */
- 作用:
- 双盒子外边距合并问题 - (任一个盒子添加:overflow: hidden)
- 解决内含浮动元素,父级高度塌陷问题 - (给父级加:overflow: hidden)
- 阻止文字环绕 - (给文字加:overflow: hidden)
2.3 css常见的布局方式
常见的布局方式
- flex布局:做弹性
- Grid布局:做网格
- 定位布局:做精确
- static:默认值,没有定位
- relative:(相对定位)相对于元素原来位置进行定位
- absolute:(绝对定位)相对于最近的有定位的父元素进行定位,脱离了文档流
- fixed:(固定定位)相对于浏览器窗口定位,脱离了文档流
- sticky:(粘性定位)根据滚动的位置进行定位,relative和fixed的结合(表格锁头)
- 浮动布局:做环绕
- 媒体查询:做响应-一套代码适配多端
响应式布局方案
- Flex/Grid
- 媒体查询
- rem+vw:postcss-pxtorem自动转换设计稿尺寸
总结:rem认根,vw认屏,rem+vw天下太平
水平垂直居中的方式
- flex
- grid (place-items:center;)
- 父相对定位 + 子绝对定位 + left: 50% + top: 50% + transform: translate(-50%, -50%);
2.4 固定单位和相对单位
- 区分:
- px:固定单位,像素值(精确控制元素的尺寸 - 按钮,图标)
- rpx:响应式像素,尺寸随屏幕宽度等比缩放
- em:相对单位,基于当前元素或父元素的字体大小,继承链会影响值
- rem:相对单位,基于根元素的字体大小
- vw,vh:基于视口大小,适合响应式设计(全屏背景图)
- %:依赖父元素,属性不同基准不
2.5 适配手机及机型样式兼容(刘海屏/安全区域)
- 场景区分
- H5项目(安全区域变量)
- 在 head 标签中添加 meta 标签,并设置
viewport-fit=cover强制页面覆盖屏幕 - 使用安全区域变量(需开启viewport-fit=cover才可用安全区域变量)
body { padding-top: env(safe-area-inset-top); padding-bottom: env(safe-area-inset-bottom); }- JS动态补救 - 解决安全区域变量兼容性问题
- 在 head 标签中添加 meta 标签,并设置
- uView项目(安全区域组件)
- 底层:uni.getSystemInfoSync().statusBarHeight
<view style="display: flex; flex-direction: column"> <u-status-bar></u-status-bar> <view style="flex: 1; display: flex; flex-direction: column; overflow: auto"></view> <u-safe-bottom></u-safe-bottom> </view> - vant项目(安全区域属性)
- 需设置 viewport-fit=cover 值
<van-nav-bar safe-area-inset-top /> <van-number-keyboard safe-area-inset-bottom />
- H5项目(安全区域变量)
2.6 大屏多端自适应布局
- 固定宽高(双层绝对定位 + Transform缩放并精准居中定位)
- 步骤
- (1)确定设计图宽高(1920 * 1080)
- (2)计算最佳缩放比例:min(窗口宽/设计图宽, 窗口高/设计图高)
- (3)应用CSSTransform缩放 + 定位居中:tansform: translate(-50%, -50%)
- 优势
- 100%设计还原
- 多端显示效果统一
- CSS Transform GPU硬件加速
- 核心代码简洁(50行左右)
- 注意:设置:transform-origin: 0 0(与绝对定位的起点保持一致)
- 步骤
三、Javascript
3.1 js数据类型及判断方法
- 分类
- 基本数据类型:number,string,boolean,undefined,null,symbol,bigInt
- 引用数据类型:array,object,function
- 判断数据的方式
- 基本数据类型判断:typeof(对于引用数据类型和null返回object)
- 引用数据类型判断:instaceof - 判断对象是否在构造函数的原型链上
- 构造函数判断:constructor - 判断对象的直接构造函数是谁(只能判断引用数据类型)
- 通用类型判断:Object.prototype.toString.call()
- 数组的判断:Array.isArray, constructor, Object.prototype.toString.call([])===’[object Array]’
- 相关概念
- Symbol:独一无二的值(解决命名冲突问题)
- bigInt:大整形–更大数值运算
3.2 js函数相关
- 函数类型
- 1.普通函数 function Test(){ return arguments }
- 2.箭头函数 let num = (a, b, …rest) => { return … }
- 3.立即执行函数 let num = (function () { return 8 + 9}())
- 4.异步函数 async function test() {}
- 5.生成器函数 function* gen() { yield } – 可暂停执行(yield)
- 普通函数和箭头函数的区别
- 1.普通函数:可以作为构造函数,有自己的原型对象,有实参列表arguments,this指向undefined/window
- 2.箭头函数:不可以作为构造函数,无自己的原型对象,无实参列表arguments,无自己的this,继承于外层
- 函数中的this指向问题
- (1)普通函数:对象中谁调用指向谁,正常使用指向undefined/window
- (2)箭头函数:继承定义时的this
- (3)定时器:默认指向全局对象,除非使用箭头函数或显示绑定
- (4)dom事件this:触发事件的DOM元素
- 修改函数this指向:call, apply, bind
- 作用:改变函数内部的this指向(让函数的this指向对象)
- 区别:
- call,apply改变this指向后便执行该函数,bind返回一个新函数必须手动调用
- call和bind一个参数一个参数传,apply以数组的方式传
3.3 原型和原型链(公用属性和方法,能继承)
- 构造函数:创建对象的函数
- 原型:构造函数的一个属性,存放着实例共享的属性/方法
- 原型链:对象继承属性的链条
- 相关概念:
- new 一个对象的过程(1空2链3绑4返)
function Person(name, age) { this.name = name; this.age = age; } let person1 = new Person('张三', 16); // new一个对象的过程 1.创建空对象:var obj = {}; 2.链接原型: obj.__proto__ = Person.prototype; // obj 继承Person.prototype上的属性和方法 obj.constructor = Person; // 指定构造函数 3.绑定this:Person.call(obj) // 让函数的this指向这个对象 4.返回对象:返回obj对象
3.4 js继承方式:代码复用和逻辑分层
- 方式
- 1.原型链继承 - 修改引用属性影响所有实例
- 2.构造函数继承 - 无法复用方法
- 3.寄生组合式继承 - (构造函数提供属性+原型链提供方法+Object.create做桥梁)
- 4.class继承 - 底层基于寄生组合式继承(推荐使用)
- extends:建立继承关系
- super:调用父类构造函数
- constructor:构造函数
3.5 闭包
- 概念:函数里面嵌套函数,并把内部函数返回出来。内部函数可访问外部的变量
- 作用:
- 创建私有变量:防止外部直接修改重要数据
- 状态持久化:函数多次调用需要记住之前的状态 - 节流/防抖
- 节流:间隔时间内多次执行间隔执行(按钮重复点击,拖拽)
- 防抖:间隔时间内多次执行只执行最后一次(输入框实时搜索)
3.6 对象的相关操作
- 创建对象方式
- 1.字面量 var obj = {};
- 2.new Object() - 等价于对象字面量
- 3.构造函数(可复用,实例共享原型方法)
- 4.Object.create - 创建一个新对象,并把它连接到指定原型
let obj = Object.create({ name: 'zs', age: 18 })
- 合并对象
- var test = Object.assign(target,…sources) // test == target
3.7 数组常用的方法
- es5
- 改变原数组:push/pop,shift/unshift,reverse,sort,splice,fill,copyWithin
- 不改变原数组:concat,toString,join,split,slice
// splice(从第几位截取 + 截取几个 + 添加新数据)(会改变原数组) let arr = [1,2,3,4,5,6]; arr.splice(0,2,7,8); [1,2] arr // [7, 8, 3, 4, 5, 6] // slice(从第几位截取,截取到第几位)(浅拷贝)(不会改变原数组) let arr = [1, 2, 3, 4, 5]; arr.slice(0,3); //[1,2,3]; arr.slice() // [1,2,3,4,5] - es6
- 不会改变原数组
1.forEach(遍历用,没有返回值) 2.find(查找到数组中第一个满足条件的元素的值) 3.map(返回原始数组元素调用函数处理后的值) 4.some(检测数组中的元素是否有某些满足指定条件) 5.every(检测数组所有元素是否都符号指定条件) 6.filter(返回符合指定条件项组成的数组) 7.reduce(接收一个函数作为累加器) // 1.reduce(累加器,当前值,当前索引,源数组,初始值) arr.reduce((acc,cur,index,arr)=>{},init) 8.new Set()(利用对象存储值的唯一性) 9.flat(数组扁平化) // 扁平,去重,排序 // Array.from(new Set(arr.flat(Infinity))).sort((a,b)=>{ return a-b}) - 数组的相关操作
// 一、扁平化
// (1)arr.flat(Infinity)
let arr = [[1, 2], 3, [4, 5]];
let newArr = arr.flat(Infinity);
// (2)arr.toString().split(",").map(Number)
let arr = [[1, 2], 3, [4, 5]];
let newArr = arr.toString().split(",").map(Number);
// 二、去重
// (1) new Set()
let arr = [1, 2, 3, 1, 4, 2, 5];
let newArr = Array.from(new Set(arr));
console.log(newArr)
// (2) filter,indexOf,includes,reduce
let arr = [1, 2, 3, 1, 4, 2, 5];
let newArr = [];
arr.filter(item => {
if(!newArr.includes(item)){
newArr.push(item)
}
});
console.log(newArr)
let newArr = arr.reduce((acc, cur, index, arr) => {
if(!acc.includes(cur)) {
acc.push(cur)
}
return acc
}, [])
// 三、类数组转换成数组(一个含有长度属性,且为正整数的对象)
// (1)Array.from(arr)
// (2)扩展运算符[...arr]
// (3)Array.prototype.slice.call(arr)
// 四、取出数组中最多的一项
let obj = { name: '张三', age: 18 }
console.log(obj.name); //张三
console.log(obj['name']); //张三
let arr = [1, 1, 1, 1, 2, 3, 4, 5, 5, 6, 6, 6];
let maxName, maxNum = 0;
let obj = arr.reduce((acc, cur, index, arr) => {
acc[cur] ? acc[cur] += 1 : acc[cur] = 1;
if (acc[cur] > maxNum) {
maxNum = acc[cur];
maxName = cur
}
return acc;
}, {});
console.log(maxName,maxNum);
console.log(obj, 'obj');
- js类似方法比较
- for循环和forEach哪个比较快
- for>forEach 没有额外的函数调用栈和上下文
- forEach不能使用break或return跳出循环可使用try catch
- map和forEach有什么区别
- forEach不会返回数据,map会返回一个新的数组
- for in 和for of区别
- for in 遍历的是索引 for of 遍历的是值
- for in 会遍历对象的整个原型链(可用hasOwnproperty区分)
- for of 无法遍历对象
- for循环和forEach哪个比较快
3.8 深浅拷贝
- 浅拷贝:只拷贝一层,嵌套引用类型仍然共享
- 常见方法
- 对象:Object.assign/…
- 数组:slice/concat/…
- 常见方法
- 深拷贝:拷贝所有层级,避免引用数据共享
- 常见方式
- JSON.stringify/.parse:无法处理函数/RegExp/Date/Set/Map等特殊对象
- Lodash库的_.cloneDeep():提供更为完备的深拷贝功能
- 递归拷贝
- 1.基本数据类型判断 - 终止条件
- 2.处理特殊对象
- 3.递归拷贝(创建对应的数组/对象)
- 4.返回结果
- 常见方式
3.9 var let const区别
- 区别
- 作用域:var 函数作用域,let/const 块级作用域
- 变量提升:var 存在变量提升,let/const 存在暂时性死区,声明前访问会报错
- 赋值:var/let 可重复赋值,cosnt不能重新赋值(引用数据可修改内部属性)
- 重复声明:var 允许重复声明,let/const 同一作用域禁止重复声明
- 总结:优先用 const,需修改时用 let,避免使用 var
- 相关概念:
- 作用域:规定变量和函数的可使用范围
- 作用域链:查找变量和函数时,从局部到全局依次查找
- js执行:语法分析,预编译(创建当前环境的执行上下文),解释执行
- 预编译:全局预编译和函数预编译
3.10 事件循环
- 概念:是js单线程运行时处理异步任务的核心调度机制,通过循环检查任务队列,在主线空闲时按优先级执行异步回调
- 过程:不断执行宏任务和这个宏任务所包含的微任务的循环往复的过程(一个宏任务 → 全部微任务 → 渲染 → 下一轮循环)
- 异步任务
- 宏任务:script/setTimeout/setInterval/setImmediate/I/O操作
- 微任务:Prcess.nextTick(优先级最高),promise.then,async/await 的后续代码
- 注意:new Promise过程属于同步任务,resove或reject后才算微任务
- 示例
setTimeout(() => {
console.log(2)
}, 0)
new Promise((resolve, reject) => {
console.log(3)
resolve();
console.log(4)
}).then(res => {
console.log(5)
})
console.log(8)
// 3, 4, 8, 5, 2
3.11 事件委托
- 原理:利用冒泡机制在父元素上集中处理子元素的事件
- 相关概念
- 事件捕获:根元素 -> 目标元素 (需要手动触发)
- 事件冒泡:目标元素 -> 根元素
- 优点:减少事件绑定,节省内存占用
- 示例:商品列表,用户点击某个商品查看详情
<ul @click="handleClick">
<li v-for="product in products" :key="product.id" :data-id="product.id">
{{ product.name }}
</li>
</ul>
// dataset:包含了所有以 data- 开头的 HTML 属性
handleClick(event) {
if (event.target.tagName === 'LI') {
const productId = event.target.dataset.id; // 获取商品 ID
}
}
3.12 return break continue 区别
- 区分
- return:用于返回函数的结果并终止函数的执行(无论嵌套多深,只在函数中使用)
- break:只能跳出当前所在的循环(只能跳出一层)
- continue:中断本次循环,继续下次循环
3.13 异步编程
- 异步编程:fs文件操作,数据库操作,ajax,定时器
- 异步编程解决方案:回调函数,promise,async/await,事件监听/发布订阅
- promise作用:
- 作用:异步编程的解决方案,支持链式调用
- 中断promise链:
- 返回一个空状态的promise
- 使用标识 + return 终止函数执行
- AbortController
- async/await:promise的语法糖,同步写法解决异步问题,错误处理和promise不同
四、Vue
4.1 vue3相对于vue2有哪些优化
- 1.响应式升级(用Proxy代替Object.defineProperty)
- Object.defineProperty缺点
- 无法监听对象属性的新增/删除($set/$delete手动补丁)
- 无法监听数组索引的变化(依赖重写方法:pop,push,shift,unshift,splice,sort,reverse)
- 初始化递归遍历,占用内存且启动慢
- Object.defineProperty缺点
- 2.虚拟DOM优化
- 通过Patch Flag标记动态节点,Diff时跳过静态内容
- 3.开发体验提升
- CompositionApi(逻辑复用,替代Mixins)
- tree-shaking 代码瘦身。如:import { watch } from vue // computed等就不会加载
- 4.新特性
- teleport:任意位置挂载组件
- fragement:组件可多根节点
- suspense:异步组件加载状态管理
- 5.更好的TS支持
4.2 vue3组件传递数据的方式
- 前提:Vue的组件通信方向性是由发起动作的一方决定的,而不是由数据流动的方向决定
- 分类
- 父传子
- (1)props
- (2)插槽
<child>hello word</child> - (3)模板引用(useTemplateRef):需子组件
defineExpose({ sendMessage })暴露 - (4)透传($attrs,未被显式声明的属性和方法,会自动应用到组件的根元素)
- 子传父
- (1)emit自定义事件
- (2)作用域插槽
<child v-slot:slotA="{ msg }"> 我是插槽 {{ msg.name + msg.age }} </child> <slot name="slotA" :msg="{ name: 'zs', age: 66 }" ></slot> - 双向绑定
- (1)v-model(3.4版本区分用法)
// 单个 <child v-model="number"></child> const modelValue = defineModel(); // 多个 <child v-model:number="number" v-model:name="name"></child> const number = defineModel('number'); const name = defineModel('name'); - 跨层级传递
- (1)provide/inject
- 全局共享
- (1)pinia:简洁、类型安全、模块化、组合式API友好,专为Vue3而生
- (2)vuex
- 父传子
4.3 nextTick函数
- 原因:数据发生变化时,vue不会立即更新DOM。而是统一在下个事件循环刷新 DOM
- 作用:nextTick保证你的代码在 DOM 更新后执行,避免操作过时的 DOM
- 原理:通过将回调函数放入微任务队列来实现延迟执行。确保任务在DOM更新后、下一次宏任务前执行
- vue3:支持 await 更简洁
- 使用场景:显示输入框并自动聚焦(改变数据后需要立即操作DOM)
4.4 vue路由相关
- 路由模式
- hash模式
- history模式:需要nginx配置try_files防止页面404
- 路由传参
- params
- path和params不能组合使用
- 未在路由中预先声明,刷新页面会丢失
- query
- 参数直接暴露在URL中,刷新页面不会丢失
- params
五、node + Express
5.1 中间件:扩展应用功能
- 分类
- 常用中间件:
- 解析请求体:express.json/express.urlencoded
- 解决跨域的:cors
- 自行开发的中间件
- 日志记录
- 身份认证
- 错误处理
- 常用中间件:
六、webpack + vite
6.1 webpack优化策略
- 核心:模块打包和资源整合。
- 优化策略:
- 构建速度优化
- 缓存(babel缓存,eslint缓存)
- 多线程
- 范围限制(exclude: /node_modules/)
- 产出性能优化
- 用Tree Shaking删未用代码(生产模式 + ES Module + 无副作用声明)
- 代码分割
- 按需加载:将代码拆分为多个chunk,减少首屏加载资源体积
- 并行加载:浏览器可同时加载多个chunk,加速资源获取
- 缓存优化:分离高频变更的业务代码与稳定的第三方库
- 资源压缩(js/css/图片)
- 优化缓存(contenthash + runtimeChunk/抽离运行时代码,防止contenthash变化)
- 构建速度优化
- 注意:runtimeChunk:抽离Webpack的运行时代码,避免业务代码改动导致所有文件缓存(contenthash)失效
6.2 webpack高级特性
- Tree Shaking(自动删除未被使用的代码)
- 模块联邦(Module Federation)- 微前端架构:跨应用共享模块
- 热模块替换(HMR)
- 预取/预加载
- PWA
6.3 Vite为什么比Webpack快
- 相关概念
- Rollup vs Webpack:ESM静态分析能精准删除未使用代码,webpack的保守策略会残留冗余代码
- esbuild:Go语言编写的超高速js打包器(提供转换、打包、压缩等功能,比babel快10-100倍)
- esbuild预购建:对node_modules 中的第三方包预处理的过程(浏览器只能跑ESM,但npm包多是CommonJS,预构建是翻译官+打包工)
- webpack对node_modules的处理
- 开发环境:开发环境用缓存换速度(首次启动,完整打包node_modules生成缓存,后续启动直接使用缓存)
- 生产环境:生产环境做优化换性能(全量打包 + 深度优化(tree Shaking + 代码分割/压缩))
- 原因
- 启动速度
- vite:基于ESM模块免打包 + Esbuild预构建
- Webpack:需构建完整依赖
- 生产构建
- vite:用Rollup打包Tree Shaking更彻底,输出代码体积更小
- Webpack:会残留冗余代码
- 热更新
- vite:仅更新单个模块
- Webpack:重新打包变更模块
- 开发体验
- vite:无需配置css/ts等模块
- Webpack:需配置Loader
- 启动速度
6.4 webpack和vite配置总结
- webpack
- 框架自带:Tree Shaking + JS 压缩
- 需开发者配置:
- css压缩(css-minimizer-webpack-plugin)
- 图片压缩(image-minimizer-webpack-plugin)
- Gzip压缩(compression-webpack-plugin)
- DLL分包(webpack预打包公共库如:vue,lodash。vite预构建已解决)
- 路由懒加载
// Webpack 路由懒加载 import(/* webpackChunkName: "chunk-name" */ './Component.vue') output: { filename: 'main.js', chunkFilename: 'chunks/[name].[contenthash].js' // 分割文件存储路径与命名规则 }
- vite
- 框架自带:预构建 + Tree Shaking + 基础压缩(JS/CSS)
- 需开发者配置:
- 图片压缩(vite-plugin-imagemin)
- Gzip(vite-plugin-compression)
- CDN(用vite-plugin-cdn-import自动注入) - 减少主包体积
- 组件懒加载(defineAsyncComponent)
const LazyComponent = defineAsyncComponent(() => import('./LazyComponent.vue'))
七、微信小程序
7.1 微信小程序开发优化
- 登录授权优化-减少授权流程的复杂度
- (1)静默登录先行 - uni.login获取code换取openid,建立基础会话
- (2)按需触发授权弹窗 - 仅在需要敏感信息(如头像、昵称)时,才弹出uni.getUserProfile授权窗口
- (3)Token 缓存与自动续期 - 缓存token至uni.setStorageSync,后端自动续期
- (4)降级兼容处理 - 用户拒绝授权时,提供游客模式或引导重新授权
7.2 wxs性能调优方案
- wxs概念:相当于在网页的HTML/CSS中直接嵌入小段JavaScript,让页面自己处理简单任务,减少对JavaScript的依赖,提高性能。
- 应用场景:
- 1.时间,金额转换在WXS中处理。轻量逻辑下沉到视图层处理,减少通信损耗
- 2.实时交互(如拖拽、滑动)在WXS中处理。优化高频交互
- 3.虚拟列表使用WXS计算可视区域数据,列表渲染性能优化
7.3 如何解决微信小程序主包过大问题
- 1.分包加载,依赖分离(非核心功能分割到子包中)
- 2.按需引入组件(局部注册,按需加载)
- 3.第三方库管理(如:按需引入,寻找替代库)
- 4.资源优化(如:压缩图片,资源动态加载等)
- 5.代码压缩,代码优化(清理优化无用代码和资源)
7.4 uniapp自定义tabbar的方式
- custom-tab-bar
- 优点:
- 独立于页面布局,基于原生TabBar渲染,切换无闪烁,体验流畅,无需重复引入
- 使用getTabBar()接口同步激活状态
- 缺点:
- 支持wxss/wxml,不支持vue
- 优点:
- 组件封装型:
- 优点:
- 完全自定义
- 根据角色动态生成不同Tab列表,Tab数量无限制
- 缺点:
- 闪烁问题:图标转base64 + keep-alive缓存页面 + css过渡动画
- 优点:
- 单页面方案
- 优点:
- 彻底解决闪烁
- Tab 间共享 Vue 数据
- 缺点:
- 首次加载慢
- 路由限制
- 优点:
八、APP开发
8.1 uniapp开发App支付流程
- 前提:将支付 SDK 配置到 Uniapp 项目
- 步骤
- 传订单给后端获取到支付参数包
- 调用uni.requestPayment()唤起微信/支付宝
- 前端收到回调后把结果上报给后端
- 后端调用支付平台接口二次验证支付状态,返回是否支付成功
- 前端根据是否支付成功进行响应的业务操作
- 注意:理解success仅代表支付流程完成,不代表最终成功
8.2 uniapp开发App相关技术点
(1)混合开发通过webview内嵌h5
- 传参方式
- app -> h5
- 通过web-view src中的url传参
- 获取当前页面的webview通过evalJS调用h5方法
- h5 -> app
- uni.postMessage + @message 事件(需引入 SDK)
- app <-> h5
- plus.storage共享
- 示例:token传递-加密URL参数 + evalJS回调(推荐)
- app -> h5
- 性能优化
- 启动速度:WebView 预创建 + h5首页数据预先获取(通过ur传递)
- 通信优化:减少通信频次(高效通信方案:uni.postMessage + message)
- H5页面性能优化
- 资源懒加载
- 代码拆分 + Tree-Shaking
- HTTP缓存优化(静态资源强缓存)
- 压缩(代码压缩,资源压缩,传输压缩)
- 虚拟列表技术(vue-virtual-scroller)
(2)更新机制的实现(可使用UniCloud升级中心)
- 目的:让App在不上架应用商店的情况下更新内容
- 版本号字段解析
- versionName:”2.1.3”,仅用于展示
- versionCode:213,核心对比字段
- minVersion:200,低于此值强制整包更新
- 分类:
- 热更包(wgt包)
- 只更新前端资源
- 用户无感知
- 整包更新(apk/ipa)区分强制更新和非强制更新
- 更新完整安装包,需重新安装应用
- ios需要应用商店审核
- 商店版本更新属于整包更新
- 热更包(wgt包)
- 步骤:
- 启动应用调用后端接口,获取服务器上的版本信息
- 判断是否需要更新(判断服务器版本versionCode是否大于本地版本)
- 判断是整包更新还是热更新
- 热更新(下载wgt包 + 重启生效)(主版本相同 + 更新包为wgt格式)
- 整包更新(下载APK/跳转商店)(主版本不同 或 更新包为apk)
- 注意:
- ios:整包更新 = 提交审核 + 跳转应用商店,热更新 = 免审+应用内生效
- 热更新仅支持同主版本内迭代(如1.1.0 → 1.2.0),否则需要自行开发设计
- 整包更新是强制更新,热更新可选择强更也可以选择静默更新
(3)双登录模式(uni一键登录 + 手机验证码登录)
- 技术点
- 按钮防抖节流
- 一键登录超时自动切换短信验证
- 自动续登 - 断网可重试(一般存储存脱敏手机号+步骤标识)
(4)uni-push推送及uni.share分享
(5)兼容性问题(封装原生API,解决iOS/Android双端差异)
* 目的:隔离双端原生差异,让开发者只关注业务代码
* 方法:通过 HTML5+ 调用底层能力
* 封装案例
* 文件存储(ios/android文件路径差异的处理)
* 权限判断和提示(wa-permission)
* 导航栏的差异
* 分享功能(ios/android存在差异)
相关概念
- app开发方式
- 原生app开发
- 跨平台开发(uni-app)
- 混合开发(5+app)
- html5plus作用:通过全局plus对象暴露跨平台封装的原生能力
- uniapp开发的App端使用:不需要plus ready
- uniapp开发的App端内嵌h5项目的使用:需要plus ready
- HBuilder创建的5+App项目,底层集成了5+ Runtime引擎:需要plus ready
- Native.js:将原生API(40万+)直接映射为JS对象,实现 JS直接调用原生代码
- webview容器:混合开发的载体
九、用户认证
9.1 Cookie vs storage
- cookie
- 概念:存储在浏览器中不超过4kb的字符串。
- 组成:
- 一个名称(Name),一个值(Value)
- 其他用于控制Cookie有效期,安全性,使用范围的可选属性
- 特点:不同域名下的Cookie相互独立,浏览器请求时会自动发送未过期的Cookie到服务器端
- 区别:
- Cookie:4KB,可设置过期时间,无设置的话默认浏览器关闭失效。请求自动带。
- LocalStorage:5-10M,永久保存,需手动清除。
- SessionStorage:5-10M,标签页关闭失效。
- Cookie,localStorage:在所有同源的窗口下可以共享,SessionStorage不同浏览器窗口不会进行共享。
- 应用:
- Cookie:token
- localStoragea:用户主题设置
- sessionStorage:表单草稿
- 正常项目中为什么选择localStorage存储token?
- 规避 CSRF(跨站请求伪造):Token不随请求自动发送会天然降低CSRF风险
- 跨域友好:避免Cookie的跨域配置复杂度
- 前后端分离架构:前端需手动将Token加入请求头(如
Authorization),localStorage 更易控制
9.2 web开发模式
- 类型
- 基于服务端渲染的传统web开发模式
- 概念:服务端把拼接好的html页面发送给客户端
- 优点:前端耗时少,有利于seo
- 缺点:占用服务器资源,不利于前后端分离,开发效率低
- 基于前后端分离的新型web开发模式
- 概念:后端只提供接口,前端通过ajax调用接口
- 优点:前后端分离,开发体验好。局部刷新,用户体验好。减轻了服务端的渲染压力
- 缺点:不利于seo(利用vue,react等框架的SSR技术可以很好的解决seo问题)
- 基于服务端渲染的传统web开发模式
- web开发模式选择:根据业务场景
- 企业级网站需要良好的seo:服务端渲染
- 后台管理系统:前后端分离
- 两者结合:首页服务端渲染 + 其他页面前后端分离
- 身份认证的选择:
- 服务器渲染 -> 推荐session认证机制
- 前后端分离 -> JWT认证机制
9.3 身份认证(鉴权)
- 概念:通过一定的手段,完成对用户身份的确认
- 相关概念
- http协议的无状态性:服务器不会主动保留每次http请求的状态
- token的组成:Header.Payload.Signature
- Header:安全性相关
- Payload:加密用户信息字符串
- Signature:安全性相关
- 方式
- session认证
- 流程
- 1.浏览器 -> 发请求:提交账号 + 密码
- 2.服务器 -> 验证账号和密码,将登录成功的信息存储到数据库中,同时生成对应的Cookie字符串。
响应请求:将生成的Cookie通过响应头发送给浏览器 - 3.浏览器 -> 自动存储Cookie到当前域名下
再次请求:通过请求头自动将当前域名下的Cookie发送给服务器 - 4.服务器 -> 服务器根据Cookie验明用户身份,浏览器根据当前用户生成特定的响应内容
- 注意
- Cookie不具有安全性:存储在浏览器中,容易伪造。所以不能使用Cookie存储重要且隐私的数据
- 全程无需前端手动干预(浏览器自动完成)
- Cookie不支持跨域,前端跨域请求后端接口时需要做额外的跨域配置
- 流程
- JWT认证
- 流程
- 1.浏览器 -> 发请求:提交账号 + 密码
- 2.服务器 -> 验证账号和密码,将用户的信息对象经过加密之后生成Token字符串
响应请求:将生成的token发送给浏览器 - 3.浏览器 -> 将token存储在本地的localStorage/SessionStorage中
再次请求:通过请求头Authorization字段,将token发送给服务器 - 4.服务器 -> 服务器将token字符串还原成用户信息对象
服务器根据用户信息对象确认用户身份,浏览器根据当前用户生成特定的响应内容
- 流程
- session认证
- 注意
- Session认证唯一性:服务器存储的Session ID(全球唯一),需查数据库
- JWT认证唯一性:Payload中的用户ID(如sub) + 签名防伪,仅需密码学验签
十、其他
10.1 解决跨域方式
- CORS(跨域资源共享):后端设置响应头,允许指定域名的请求
- 代理服务器:前端通过同域的中间服务器(如Vite开发服务器、Nginx)转发请求,避开浏览器限制
- JSONP:利用
<script>标签无跨域限制的特性(仅限GET请求,已过时)
10.2 从输入URL到页面加载的过程
- 1.URL解析:判断地址是否错误
- 2.DNS解析:解析域名->ip地址
- 3.建立tcp连接(tpc三次握手)
- 4.发送http(https)请求
- 5.服务器响应
- 6.浏览器接受响应并解析渲染页面
- 7.断开tcp连接
10.3 项目中的优化策略
- 减少体积
- 代码分割(路由懒加载/第三方库分包)
- tree-shaking(按需加载组件)
- 资源压缩(css/js,图片,Gzip压缩)
- 加快渲染(解决DOM过多导致内存溢出和渲染卡顿)
- 虚拟滚动(动态计算可视区域:vue-virtual-scroller)
- 长页面优化(IntersectionObserver)
- 懒加载(组件懒加载/图片懒加载)
- GPU加速(css使用transform,opacity)
- 缓存复用
- 缓存策略
- https缓存
- HTML协商缓存
- 静态资源强缓存
- API缓存:减少网络请求
- 客户端缓存:storage/vuex
- 服务端缓存:http缓存头
- https缓存
- CDN静态资源托管
- PWA离线支持
- 缓存策略
10.4 WebSocket
10.4.1 WebSocket介绍
- 介绍:基于TCP的双向通信协议,提供全双工、持久化的单一TCP连接
- 协议:ws,wss(加密的 WebSocket 协议)
- 常用事件及方法:
事件:open,close,message,error
方法:send,close - 与http区别
- http:单向,短连接,每次请求携带完整头部
- websocket:双向,长连接,第一次后携带头极小
- 优势:双向通信,实时推送,减少网络请求,支持跨域
- 注意:浏览器请求自动附加Origin头标明请求的来源。服务器会验证Origin头决定是否允许连接(避免了跨站请求伪造(CSRF))
10.4.2 握手过程
- 客户端请求升级发送Sec-WebSocket-Key(Upgrade: websocket + Sec-WebSocket-Key)
- 服务端响应切换(返回HTTP 101状态码 + Sec-WebSocket-Accept)
- 协议升级(TCP连接保持,转为WebSocket通信)
10.4.3 心跳机制及重连机制
- 心跳机制(保活)
- 问题:防火墙为节省资源,自动关闭无流量连接
- 解决:定时空包保活,告诉防火墙’我还活着’”
// 每25秒发送心跳包 setInterval(() => ws.send('ping'), 25000); - 重连机制
- 在onclose事件中,指数退避重试(避免过于频繁的重试)
10.4.4 WebSocket 如何保证数据安全
- 方式
- WSS加密(服务端配置
SSL/TLS加密,浏览器wss://使用) - Token认证(只有提供有效Token的客户端才能建立WebSocket连接)
- 应用层加密(传输敏感信息)
- WSS加密(服务端配置
10.4.5 遇到多节点广播问题如何解决
- 原因:各节点仅维护自身连接,广播无法覆盖全局(节点:服务器)
- 解决方案(Redis,如:socket.io-redis)
- 节点收到消息 -> 发给消息队列(redis)
- redis -> 立即转发给所有节点
- 每个节点 -> 广播给所有自己连的用户
10.4.6 socket.io和websocket
- 概念:socket.io是websocket增强版封装库
- 有点:
- 自动重连保活机制
- 兼容性更好(支持旧浏览器)
- 自动降级:WebSocket + HTTP 轮询自动切换
- 房间管理(实现实时分组通信)
// 提供了简单易用的 API 来管理房间 socket.join('roomName') // 加入房间 socket.leave('roomName') // 离开房间 io.to('roomName').emit('message', data) // 广播消息到房间
10.5 nginx相关
- 概念:一个高性能的 HTTP 服务器和反向代理服务器
- 前端相关
- 托管静态资源 - 让浏览器能访问你的 HTML/CSS/JS/图片/文件
- 反向代理
- 解决跨域
- 隐藏后端(安全性考虑)
- 转发API请求
- 通过try_files解决刷新页面404
- 缓存策略
- 静态资源强缓存
- html协商缓存(不缓存)
- 开启 Gzip 压缩
- HTTPS 配置(301重定向)
- 部署根目录和子路径区别
- 根路径
- 直接root指向静态文件即可
- 子路径
- 使用alias指向静态文件
- 配置静态资源publicPath路径 + 路由base(保持一致)
- 根路径
10.6 文件断点上传
- 步骤
- 1.选择文件
- 2.计算文件哈希(spark-md5)
- 3.判断文件是否上传
- 4.切割文件为分片(给每个分片分配唯一索引)
- 5.分片上传
- 只上传缺失的分片
- 并发控制(同时3个请求)
- 每个请求携带:文件唯一标识符,分片索引、文件内容
- 6.暂停/继续
- 继续时重新检测文件
- 7.请求合并
- 三个接口:检测文件是否存在 + 上传文件分片 + 合并分片
- 取消axios的方法
const controller = new AbortController();
axios.get('/foo/bar', {
signal: controller.signal
}).then(function(response) {
//...
});
// 取消请求
controller.abort()
10.7 项目开发优化方式
- 通用方式
- 代码分割:路由懒加载/分包/异步组件/第三方库分包(减少首屏js体积):() => import(‘./A.vue’)
- 按需引入:tree-shaking按需引入减少无用代码 eg:import { Button } from ‘vant’
- 资源压缩:代码压缩+图片压缩+Gzip压缩
- 缓存策略:本地存储/减少网络请求/cdn/http缓存
- 分平台重点
- 小程序:分包 + wxs脚本(高频交互,减少通信损耗,如:滚动,手势)
- App:复杂页使用nvue提速 + 原生插件使用(相机,视频)+ 静态资源本地化(沙盒)
- nvue原生渲染,牺牲css换性能
- h5:pwa缓存 + 虚拟滚动(vue-virtual-scroller)+ ssr(服务端生成HTML)
- pc后台:表格虚拟化(vue-virtual-table)+ web Worker(复杂计算移除主线程)
10.8 三方sdk接入
- 基本流程:获取凭证(key及密钥) + 引入/安装 + 初始化/调用 + 权限/隐私处理
- 钉钉扫码示例
- 引入 + 加载sdk
- 调用sdk方法生成二维码
- 扫码生成tmpCode
- 跳转钉钉授权地址,授权后获取code
- 跳转前端页面拿code换token
10.9 echarts大数据加载,性能优化方案。
- 主要问题:大数据导致计算和绘制负担过重,阻塞主线程
- 解决方案,从三个方面:
- 数据层:使用LTTB算法压缩数据(保留原有数据趋势)
- 计算层:使用web worker后台处理数据,避免主线程阻塞
- 渲染层:分层绘制静态元素 + 脏矩形局部更新 + 增量更新 + 简化视觉元素
- 使用场景:
- 静态大数据:LTTB算法 + 脏矩形局部更新(useDirtyRect)
- 实时数据流:增量更新(appendData) + web Worker
- 复杂页面:分层绘制(将静态元素:坐标轴/网格与动态数据分离,复用静态层减少渲染开销)
- 检测方法:
- Chrome Performance:性能调优
- Chrome Memory:用于内存检测,检测是否内存泄漏(拍快照,对比Size Delta)
- FPS Meter监控帧率:1s60帧
- 内存泄漏:该释放的内存没有释放
- 常见原因:定时器未清除/事件监听未移除/闭包引用未释放/dom引用未解除
- 解决方式:使用memory检测内存泄漏,根据导致原因释放内存
- 内存溢出:需要的内存达到上限
- 常见原因:一次性加载大量数据 + 无限循环 + 递归调用太深
- 解决方式:分批加载数据 + 优化算法
10.10 微前端qiankun
- iframe缺点:通信困难,性能差,体验割裂(刷新url丢失)
- 解决的问题
- 整合不同技术栈
- 子系统独立开发部署
- 旧系统逐渐迁移:边运行边替换
- 常见的重难点:
- js沙箱逃逸:重点检查子应用unmount是否清除全局属性,如定时器事件监听等。
- 子应用样式冲突:给容器添加唯一id,限制css作用域
- 路由冲突:给子应用分配唯一前缀
- 应用间跳转:路由前缀匹配 + history.pushState跳转(浏览器原生 API)
- 通信方式
- 主 -> 子:props
- 子 -> 主:initGlobalState
- 子 -> 子:自定义事件
- 持久化存储:localStorage - 同域名