面试题

  1. 一、HTML
    1. 1.1 HTML5新特性
    2. 1.2 meta 标签
    3. 1.3 浏览器渲染步骤
  2. 二、CSS
    1. 2.1 CSS3新特性
    2. 2.2 BFC-块级格式化上下文
    3. 2.3 css常见的布局方式
    4. 2.4 固定单位和相对单位
    5. 2.5 适配手机及机型样式兼容(刘海屏/安全区域)
    6. 2.6 大屏多端自适应布局
  3. 三、Javascript
    1. 3.1 js数据类型及判断方法
    2. 3.2 js函数相关
    3. 3.3 原型和原型链(公用属性和方法,能继承)
    4. 3.4 js继承方式:代码复用和逻辑分层
    5. 3.5 闭包
    6. 3.6 对象的相关操作
    7. 3.7 数组常用的方法
    8. 3.8 深浅拷贝
    9. 3.9 var let const区别
    10. 3.10 事件循环
    11. 3.11 事件委托
    12. 3.12 return break continue 区别
    13. 3.13 异步编程
  4. 四、Vue
    1. 4.1 vue3相对于vue2有哪些优化
    2. 4.2 vue3组件传递数据的方式
    3. 4.3 nextTick函数
    4. 4.4 vue路由相关
  5. 五、node + Express
    1. 5.1 中间件:扩展应用功能
  6. 六、webpack + vite
    1. 6.1 webpack优化策略
    2. 6.2 webpack高级特性
    3. 6.3 Vite为什么比Webpack快
    4. 6.4 webpack和vite配置总结
  7. 七、微信小程序
    1. 7.1 微信小程序开发优化
    2. 7.2 wxs性能调优方案
    3. 7.3 如何解决微信小程序主包过大问题
    4. 7.4 uniapp自定义tabbar的方式
  8. 八、APP开发
    1. 8.1 uniapp开发App支付流程
    2. 8.2 uniapp开发App相关技术点
      1. (1)混合开发通过webview内嵌h5
      2. (2)更新机制的实现(可使用UniCloud升级中心)
      3. (3)双登录模式(uni一键登录 + 手机验证码登录)
      4. (4)uni-push推送及uni.share分享
      5. (5)兼容性问题(封装原生API,解决iOS/Android双端差异)
    3. 相关概念
  9. 九、用户认证
    1. 9.1 Cookie vs storage
    2. 9.2 web开发模式
    3. 9.3 身份认证(鉴权)
  10. 十、其他
    1. 10.1 解决跨域方式
    2. 10.2 从输入URL到页面加载的过程
    3. 10.3 项目中的优化策略
    4. 10.4 WebSocket
      1. 10.4.1 WebSocket介绍
      2. 10.4.2 握手过程
      3. 10.4.3 心跳机制及重连机制
      4. 10.4.4 WebSocket 如何保证数据安全
      5. 10.4.5 遇到多节点广播问题如何解决
      6. 10.4.6 socket.io和websocket
    5. 10.5 nginx相关
    6. 10.6 文件断点上传
    7. 10.7 项目开发优化方式
    8. 10.8 三方sdk接入
    9. 10.9 echarts大数据加载,性能优化方案。
    10. 10.10 微前端qiankun

一、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解析)
  • 相关概念
    • 重排(回流):布局发生变化
    • 重绘:样式发生变化
    • 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动态补救 - 解决安全区域变量兼容性问题
    • 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 />
      

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 无法遍历对象

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)
      • 初始化递归遍历,占用内存且启动慢
  • 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中,刷新页面不会丢失

五、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回调(推荐)
  • 性能优化
    • 启动速度: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需要应用商店审核
      • 商店版本更新属于整包更新
  • 步骤:
    • 启动应用调用后端接口,获取服务器上的版本信息
    • 判断是否需要更新(判断服务器版本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容器:混合开发的载体

九、用户认证

  • 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开发模式选择:根据业务场景
    • 企业级网站需要良好的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 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缓存头
    • 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连接)
    • 应用层加密(传输敏感信息)

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 - 同域名
×

喜欢就点赞,疼爱就打赏