2025面经

353 阅读30分钟

面经

HTTP

跨域

Nginx反向代理

JSONP

CORS

本地运行项目可配置proxy代理来跨域(webpack,vite)

输入URL回车后经历哪些过程

缓存检查:如果浏览器有缓存则不会走下一步;

DNS解析: 访问DNS服务器,将域名解析成IP地址

TCP链接:三次握手建立TCP链接,发送HTTP请求

服务器返回数据:服务器处理请求并返回HTTP报文

浏览器渲染: 浏览器解析渲染页面

断开链接:TCP四次挥手

TCP三次握手

第一次握手:客户端给服务端发送报文(syn包)(验证客户端的发送能力)

第二次握手:服务端收到了报文(syn包),给客户端应答(发送)一个报文(syn包)(验证服务端的接收、发送能力)

第三次握手:客户端收到了报文(syn包),给服务端应答(发送)一个报文(syn包)(验证客户端的接收能力)

最后结论:通过了三次握手(少一次握手都不行),确认了客户端和服务端的发送、接收能力都没问题

注:握手必须三次,少一次握手都不行;只能第三次握手才能携带数据(因为此时客户端、服务端已经建立起了连接,且所有发送、接收能力都是正常的);

(张三招手(syn)——李四微笑(ack)且向李四招手(syn)——李四微笑(syn))

TCP四次挥手

第一次挥手:客户端发送报文(FIN),关闭数据传输,进入FIN_WAIT1状态

第二次挥手:服务端收到报文(FIN),应答一个报文(ack)给客户端,进入CLOSE_WAIT状态

第三次挥手:服务端发送报文(FIN),关闭数据传输,进入LAST_ACK状态

第四次挥手:客户端收到报文(FIN),进入TIME_WAIT状态,应答一个报文(ack)给服务端,服务端进入CLOSED状态,完成四次挥手

(张三挥手(fin)——李四伤感地微笑(ack)——李四挥手(fin)——张三伤感地微笑(ack))

存储

cookie :有过期时间,大小限制4kb,请求会携带在请求头中

sessionStorage:无过期时间,窗口关闭自动删除

localStorage:无过期时间,除非手动删除,不然一直存在

常用请求方式

GET

POST 修改

UPDATE

DELETE

PUT

GET和POST的区别

post通过body传输,大小不受限制,支持多种编码格式

get是url传输,大小不受限制

http和https的区别

安全性:http协议是明文,不安全;https使用了SSL/TLS协议进行了加密处理,安全性高。

连接方式:http80https443

性能:https需要涉及加密以及多次TCP握手链接,性能不如http

费用:https需要SSL/LS,证书需要钱,http免费

HTML

HTML、XML、XHTML区别

HTML超文本标记语音,语法较为松散

XML可扩展标记语言,主要用于存储数据和结构

XHTML可扩展的超文本标记语言,基于XML,作用与HTML类似,但语法更严格

XML和JSON的区别

XML 可扩展标记语言,主要用于存储数据和结构

JSON 轻量级的数据交换格式。基于JavaScript 的一个子集,具有易读、易写的特点。

区别:

  • JSON 安全性低,不支持注释,仅支持UTF-8格式编码,支持数组,易阅读
  • XML 安全性高,支持注释,支持各种编码,不支持数组

title和alt属性有什么作用

title是提示性文本,鼠标悬浮显示的标题

altimage标签的替代文本,图片无法加载时的提示文字

浏览器解析和HTML渲染

分为DOM解析 和 DOM渲染两个流程

  1. 将HTML解析成DOM树(DOM tree),将CSS解析成CSS规则(CSS rules)。
  2. 将解析的DOM树和CSS规则相结合生成渲染树(render tree)
  3. 为DOM树的每个节点分配坐标
  4. 根据CSS规则绘制视图,在浏览器中渲染出来

HTML5新特性

  • 语义化标签

    • 根据内容结构,选择合适的标签,便于开发者阅读和写出更优雅的代码,让浏览器更好的解析
  • 视频video、音频audio

  • 画布canvas

  • SVG绘图 可伸缩的矢量图

  • 地理定位 geolocation

  • 拖拽释放API

  • WebWorker

    • 是运行在后台的 JavaScript,独立于其他脚本,不会影响页面的性能。您可以继续做任何愿意做的事情:点击、选取内容等等,而此时 web worker 在后台运行
  • WebStrorage 本地存储

    • localStrorage 需要手机清除缓存

    • sessionStrorage 浏览器关闭自动删除

      • sessionStrorage.setItem(key, value)
      • 读取数据:sessionStrorage.getItem(key)
      • 删除单个数据:sessionStrorage.removeItem(key)
      • 删除所有数据:sessionStrorage.clear()
      • 得到某个索引的key:sessionStrorage.key(index)
  • WebSocket

    • 是HTML5开始提供的一种在单个 TCP 连接上进行全双工通讯的协议

CSS

三大特性

层叠性、继承性、优先级

层叠性:相同选择器设置样式,样式冲突就近原则,最终新的覆盖旧的

继承性:子标签会继承父标签的某些样式,如颜色、字体,字号

优先级:important > 行内样式 > id选择器 > 类选择器 > 元素选择器 > 继承

盒模型

标准盒模型:content + padding + border + margin

怪异盒模型:content + margin

CSS 的引用有哪些

  1. 内联方式(直接在 html 标签中的 style 样式)
  2. 嵌入方式(在 < style > 标签下书写 css 代码)
  3. 链接方式(使用 link 引入外部的 css 文件)

link 和 @import 的区别

  1. link 和 import 写法不同,link 通过 标签的 href 属性引入,import 通过 @import url() 引入。
  2. link 是 XHTML 标签,还可以定义其他事务,@import 属于 CSS 范畴,只能加载 CSS。
  3. link 无兼容问题,@import 兼容 IE5 以上。
  4. link 支持使用 Javascript 控制 DOM 去改变样式,@import 不支持改变样式。
  5. link 是连接整个 css 文件,@import 可以模块化引入 css 文件。
  6. link 引用 CSS 时,在页面加载时同时加载 css,而 @import 会把 css 文件放在页面的最底部,导致 css 最后才加载完毕,等到页面完全加载才加载 css,导致页面留白时间长,影响用户体验。

href和src的区别

  • href 为超文本引用,常用的是link、a 标签。
  • src 会将指向的资源下载并引用到当前文档中,常用的标签有 script,img,iframe 标签。

嵌套块元素塌陷解决方案

  • 父元素设置边框
  • 父元素定义内边距
  • 父元素添加overflow:hidden

传统网页局部的三种方式

  • 普通流(标准流)

    • 块级元素独占一行,从上向下顺序排列
    • 行内元素从左到右顺序排列
  • 浮动

    • flot
  • 定位

    • position

flex布局

  • flex-direction 控制主轴的方向

  • flex-wrap 控制换行方式

    • nowrap wrap wrap-reverse
  • flex-flow 控制主轴和换行的简写 默认row nowrap

  • justify-comtent 控制主轴的对齐方式

    • flex-start flex-end center space-between space-around
  • align-items 控制交叉轴的对齐方式

    • flex-start flex-end center
  • align-content 多个交叉轴的对齐方式

    • flex-start flex-end center space-between space-around

css隐藏元素方法opacity:0、visibility:hidden、display:none区别

  • 结构:

    • opacity:0 元素在渲染树,占用空间,内容不可见,可以点击
    • visibility:hidden 元素在渲染树,占用空间,内容可见,不可点击
    • display:none 元素不在渲染树中,不占用空间,内容不可见,不能点击;
  • 继承

    • opacity: 0 非继承属性,子孙节点消失,由于元素从渲染树小时造成,通过修改子孙节点属性无法显示
    • visibility:hidden 是继承属性,子孙节点消失,由于继承了hidden,通过设置visibility:visible 可以让子孙节点显示
    • display:none
  • 性能

    • opacity:0 修改元素会造成重绘,性能消耗较少
    • visibility:hidden 修改元素只会造成本元素的重绘,读屏器读取会元素内容,性能消耗较少
    • display:none 修改元素会造成文档回流,读屏器不会读元素内容,性能消耗较大

居中布局的几种方式

水平居中
text-align: center; // 行内元素

margin: 0 auto; // 块级元素

displayflex; // flex布局
justify-content: center;

position: absolute; // 绝对定位
left: 50%;
transform: translateX(-50%);

display: grid // grid布局
justify-content: center;
垂直居中
line-height: // 行内元素

display: flex; // flex布局
aligin-items: center;

position: absolute; // 绝对定位
top: 50%;
transform: translateY(-50%);

display: grid // grid布局
aligin-items: center;

两端对齐布局

flex布局、float布局、grid布局、相对定位relative+绝对定位absolute

displayflex; 
justify-content: space-between;

float: left;
float: right;

display: grid;
grid-template-columns: 1fr 1fr; /* 定义两列 */
justify-items: center; /* 水平对齐 */

// 父元素
position:relative; 
// 左侧子元素
position:absolute; 
left: 0
// 右侧子元素
position:absolute; 
right: 0

px、em、rem

  • px:是绝对单位

  • em:根据父元素的字体大小计算(父元素30px,子元素0.5em == 15px)

  • rem:根据html根节点字体大小计算(html 30px,子元素0.5em == 15px)

vw、vh

根据浏览器视窗的百分比

BFC

是指独立的布局环境,BFC内部的元素布局和外部互不影响

触发BFC的方式:

  • 设置浮动
  • overflow设置为autoscrollhidden
  • position设置为absolutefixed

常见的BFC应用:

  • 解决浮动元素令父元素高度塌陷的问题
  • 解决非浮动元素被浮动元素覆盖的问题
  • 解决外边距垂直方向重合的问题

BFC、IFC、GFC、FFC

  • BFC:块级格式上下文,指的是一个独立的布局环境,BFC内部的元素布局与外部互不影响
  • IFC:行内格式化上下文,将一块区域以行内元素的形式来格式化
  • GFC:网络布局格式化上下文,将一块区域以grid网络的形式来格式化
  • FFC:弹性格式化上下文,将一块区域以弹性盒子的形式来格式化

清除浮动的方法

  • clear:both
  • 父元素overflow:hidden
  • ::after伪元素

重绘与重排的区别

  • 重排:重新生成布局,重新排列元素,会引起重绘,性能影响大

  • 重绘:元素的外观被改变,如修改背景色,内容需要更新,性能影响小

    重绘不一定引起重排,重排必定引起重绘

css3新增特性

  • 边框效果

    • border-radiu 边框圆角
    • box-shadow 阴影
    • border-image 边框图像
  • 背景效果:

    • background-size 背景图片尺寸
    • background-origin 背景图片的定位区域
    • background-clip 背景的绘制区域
  • 文本效果

    • text-shadow 文本阴影
    • word-wrap 换行
    • word-break 英文换行规则
    • text-wrap 文本换行规则
    • text-overflow 文本溢出
  • 透明度 opacity

  • 渐变:

    • 线性渐变 linear-gradient

      background: linear-gradient(direction, color-stop1, color-stop2, ...);
      
    • 径向渐变 radial-gradient

      background: radial-gradient(center, shape size, start-color, ..., last-color);
      
  • 2D/3D转换 transform

  • 过度:transition

  • 动画 animation

  • 网格布局 grid

  • 弹性布局 flex

  • 媒体查询 @keyframes

JavaScript

数组去重

  • Array.from(new Set())

  • forEach + 对象法

  • forEach + new Map()

  • forEach + some

  • reduce + 对象法

  • reduce + new Map()

// Array.from(new Set())
Array.from(new Set([1,1,2,2,3,3]))

// forEach + 对象法
let obj = {};
let newArr = [];
users.forEach((x) => {
    !obj[x.name] && (obj[x.name] = 1) && newArr.push(x)
});
​
// forEach + new Map()
let res = new Map()
let newArr = []
users.forEach((x) => {
    !res.has(x.name) && res.set(x.name, 1) && newArr.push(x)
})
​
// forEach + some
let newArr = []
users.forEach((x) => {
    if (!newArr.some((y) => x.name == y.name)) {
        newArr.push(x)
    }
})
​
// reduce + 对象法
let obj = {}
const newArr = users.reduce((accum, item) => {
    obj[item.name] ? "" : obj[item.name] = true && accum.push(item)
    return accum
}, [])
​
// reduce + new Map()
let res = new Map()
const newArr = users.reduce((accum, item) => {
    !res.has(item.name) && res.set(item.name, 1) && accum.push(item)
    return accum
}, [])

防抖

n秒后执行,如果n秒内被触发,则重新计时,应用场景:输入框

防抖函数核心是利用定时器,每次触发先清掉以前的定时器(重新开始)

function debounce(fn, delay = 1000) {
    let timer = null
    return funciton() {
        timer && clearTimeout(timer)
        timer = setTimeout(() => {
            fn.apply(this, arguments)
        }, delay)
    }
}

节流

n秒后执行,如果n秒内被触发,则只执行一次,应用场景:连续快速点击,鼠标滑动

防抖函数核心是利用定时器,等定时器执行完毕,才开启定时器(不能打断)

function throttle(fn, delay = 1000) {
    let timer = null
    return funciton() {
        if (timer) {
            return
        }
        timer = setTimeout(() => {
            fn.apply(this, arguments)
            timer = null
        }, delay)
    }
}

闭包

有权访问另一个函数作用域中的变量的函数(闭包会把局部变量变成私有变量一直保存在内存中)

注:函数自调用也是闭包

(函数中嵌套函数,这个内部函数暴露给外部调用。作用是可以访问局部变量,缺点是容易发生内存泄露)

以下是通过链式作用域
function fun1() {
    var num = 0;
    function fun2() {
        console.log(num)
    }
    return fun2
}
var result = fun1()
result得到的是fun2函数,fun2函数访问的是fun1的局部变量,此时的num一直会在内存中
result() // 0

作用域

是js访问变量的一种规则,分为全局作用域和局部作用域。

  • 全局作用域:声明的变量在任何地方都可以访问,且有变量提升的作用。

  • 局部作用域:声明的变量只在局部范围内可以访问,且没有变量提升的作用。

作用域链

访问变量时,会先查找本地作用域,如果找到目标变量即返回,否则会去父级作用域查找,如果父级没有,则继续往上级找,一直找到全局作用域,这种作用域的嵌套机制,称为作用域链

var a = 100
function F1() {
    var b = 200
    function F2() {
        var c = 300
        console.log(a) // 自由变量,顺作用域链向父作用域找
        console.log(b) // 自由变量,顺作用域链向父作用域找
        console.log(c) // 本作用域的变量
    }
    F2()
}
F1()

class Start {
    constructor(name, age) {
        this.name = name
        this.age = age
    }
    say() {
        console.log(`我是${this.name}今年${this.age}了`)
    }
}
​
const start = new Start('张三', 18)
start.say() // 我是张三今年18了

new操作符具体都干了什么

  1. 创建一个新对象 obj。
  2. 将该对象与构造函数通过原型链连接起来(设置该对象的构造函数)。
  3. 将构造函数中的 this 绑定到该对象上。
  4. 根据构造函数返回类型作判断,如果是值类型则返回新对象 obj,如果返回对象,则返回构造函数里的对象。

继承

  • 原型继承

使用prototype继承,注意需要重新设置constructor指向

function Com() {
    this.name = "公共";
}
function fn() {}
fn.prototype = new Com();
fn.prototype.constructor = fn;
const newFn = new fn();
  • 类继承

使用extends关键字继承父类,属性需要super关键字接收,方法不需要

class Person {
    constructor(name, age) {
        this.name = name
        this.age = age
    }
    say() {
        console.log(`我是${this.name}`)
    }
}

class Son extends Person {
    constructor(name, age) {
        super(name)
        this.age = age
    }
    
    introduce() {
        console.log(`我是${this.name},今年${this.age}了`)
    }
}
const son = new Son('张三', 18)
console.log(son.introduce()) // 我是张三,今年18了     

原型

class Start {
    constructor(name) {
        this.name = name
	}
}
Start.prototype.say = function () {
	console.log('hellow')
}
const start = new Start('类')
start.__proto__ === Start.prototype
start.__proto__.constructor === Start.prototype.constructor

构造函数本身有一个prototype属性,称为显示原型,这个属性是一个对象,它包含了构造函数的所有实例共享的属性和方法;

通过构造函数创建出来的对象,有一个__proto__属性,称为隐式原型,指向构造函数的原型对象prototype

原型可以起到共享方法的作用,可以节约内存;

隐式原型__proto__和显示原型prototype下面有个contructor特殊属性,这个属性都指向构造函数本身

原型链

对象通过_proto_指定原型prototype,原型上面还有原型。通过_proto_指向原型prototype形成的一个链,叫做原型链。

在对象身上查找属性和方法,如果自身没有,就往原型prototype上找,如果没有,再往原型的原型找,直至找到顶端null,当_proto_指针指向 null 时。

注:原型链的顶端是null,如果没有找到会返回undefinedObject是所有对象的原型

特点:对象可以沿着原型链向上查找属性和方法,实现了属性和方法的共享和继承

this指向

  • 浏览器中指向window
  • 函数中指向调用对象
  • 构造函数中指向new出来的实例对象
  • 原型对象里面的函数 this指向实例对象
  • call、applay、bind指向被绑定在指定的对象上
  • 箭头函数没有thisthis为父作用域中的this,是动态的,谁调用指向谁
  • apply、call、bind 都是 js 给函数内置的一些 API,调用他们可以为函数指定 this 的执行, 同时也可以传参

call、apply、bind

  1. 作用:改变this指向

  2. 相同点:都能改变this指向

  3. 不同点

    • 传参不同:

      • call是单个参数传递
      • apply传递的参数是数组形式
      • bind单个参数传递和数组形式传递都可以
    • 执行不同

      • callapply函数执行是直接执行
      • bind函数会返回一个函数,手动调用后才会执行
const obj = { name: '张三' }
// call、apply直接执行
function callFn(name) { console.log(this, name) }
callFn.call(obj, '李四')

function applyFn(name1, name2) { console.log(this, name1, name2) }
applyFn.apply(obj, ['李四'])

// bind会返回一个函数,再手动调用
function bindFn(name) {  console.log(this, name)}
const bindResultFn = bindFn.bind(obj)
bindResultFn('李四')

promise

是异步编程的一种解决方法,有三种状态pending(初始状态)fulfilled(成功)rejected(失败)

注:promise是同步,.then、.catch属于异步

const promise = new Promise((resolve, reject) => {
    try {
        resolve()
    } cathc {
        reject()
    }
})
promise.then((resolve) => {}).catch((reject) => {})

async await

async声明异步函数,await等待代码执行

数据类型

  • 基本数据类型

undefined null numebr string boolean bigint sysbol

  • 引用数据类型

object array Date function 正则RegExp

  • 判断数据类型

type of 判断基本数据类型

instanceof 判断引用数据类型

Object.prototype.toString.call() 判断所有类型

null和undefined

null 是定义了的,且赋值为null

undefined 是未定义的

栈、堆

  • 栈(stack):是栈内存的简称,栈是自动分配相对固定大小的内存空间,并由系统自动释放,栈数据结构遵循先进后出的原则。基本数据类型:Null、Undefined、Number、Boolean、String、Symbol。

  • 堆(heap):是堆内存的简称,堆是动态分配内存,内存大小不固定,也不会自动释放,堆数据结构是一种无序的树状结构,同时它还满足key-value键值对的存储方式;我们只用知道key名,就能通过key查找到对应的value。引用数据类型:Object、Function、Array。

深浅拷贝

如果对象属性是基本数据类型,则拷贝的是基本数据类型的值;如果对象属性引用数据类型,则拷贝的是内存地址,修改对象数据时会影响原数据

  • 深拷贝

将一个对象从内存中完整的拷贝一份出来,在内存中重新开辟一块区域存储拷贝过来的数据,修改对象时,原数据不会受影响。

第三方js库JSON.parse(JSON.stringfly())递归函数

采用递归遍历,先判断是基本数据类型还是引用数据类型,基本数据类型直接浅拷贝,引用数据再根据不同的数据类型进行不同的拷贝操作

function deepClone(target) {
  // 定义一个变量
  let result;
  /**
   * 先通过 typeof 判断是否为基本数据类型
   * 如果是 "object" 则是引用数据类型,需要判断时间、正则等等数据类型
   * "object" 包含Array Object数据类型(数组、对象) 
   */
  if (typeof target === "object") {
    if (target === null) {
      result = null;
    }
    // Array数据类型
    if (Array.isArray(target)) {
      result = []
      result = target.map((e) => deepClone(e))
    }
    // 正则数据类型
    else if (getType(target) === 'RegExp') {
      result = new RegExp(target);
    }
    // 时间数据类型
    else if (getType(target) === 'Date') {
      result = new Date(target)
    }
    // Object数据类型
    else if (getType(target) == 'Object') {
      result = {};
      for (let i in target) {
        result[i] = deepClone(target[i]);
      }
    } else {
      result = target;
    }
  } else {
    result = target;
  }
  // 返回最终结果
  return result;
}
function getType(target) {
  return Object.prototype.toString.call(target).slice(8, -1)
}
  • 浅拷贝

展开运算符 [...array, ...array]

Object.assign({name: a}, {age: 18})

操作数组常用API

  • 遍历

forEach 没有返回值 会修改原数组

map 返回新的Array

filter 返回满足条件数据组成的新数组

some 其中一个符合条件 返回true

every 全部符合条件 返回true

find 返回满足条件数据的第一条

findIndex 返回满足条件数据的第一个索引

  • 操作数据

unshift 追加到第一个

push 追加到最后一个

shift 删除第一个

pop 删除最后一个

join 数组转字符串

split字符串转数组

sort 排序

reverse 反转数组

concat 合并数组

slice 分割数组

splice 添加 删除数组

includes 判断是否包含指定值,返回true false

indexOf 判断是否包含指定值,返回索引

操作对象常用API

Object.keys() 遍历对象,返回对象所有key组成的数组

Object.values() 遍历对象,返回对象所有value组成的数组

Object.assign() 合并对象 浅拷贝

Object.entries() 遍历对象,把key和value组成数组,返回一个新的二维数组[[key, value], [key,value]]

事件委托

在一个元素上注册一个事件,这个元素下面的所有元素也能执行这个事件,再通过事件的target属性分辩出当前触发的是哪一个目标元素。优点:避免重复注册事件,提高程序运行性能

事件流

事件完整执行过程中的流动路径

事件流中分为以下几个:

  • 事件捕获

  • 事件冒泡 依次向上调用所有父级元素的同名事件

  • 事件阻止 阻止事件冒泡(event.stopPropagation)

  • 事件解绑 解绑注册的事件

算法

  • 冒泡排序(嵌套循环,减减加加,两两交换)
const arr = [53, 40, 36, 60, 1, 8, 70, 100]
for (let i = arr.length; i > 0; i--) {
    for (let j = 0; j < i - 1; j++) {
        if (arr[j] > arr[j + 1]) {
            // 方案1:利用数组结构赋值换位
            [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
            // 方案2:声明一个变量,三个变量交换
            const temp = arr[j]
            arr[j] = arr[j = 1]
            arr[j + 1] = temp
        }
}
  • 递归算法
// 1-100 累加求和
function sum(num) {
    if (num == 1) {
        return 1
    } else {
        return sum(num -1) + num
    }
}
let num = sun(100)
  • 1~n的阶乘
function fn(n) {
    if (n ==  1) {
        return 1
    }
    return n * fn(n - 1)
}
fn(4)
// return 4 * (3 * fn(2 * fn(1)))
// return 4 * 3 * 2 * 1
  • 斐波那切数列
function fn(n) {
    if (n == 1 || n == 2) {
        return n
    }
    return fn(n - 1) + fn(n - 2)
}

JS运行机制(TODO)

异步任务分类:宏任务微任务 同步任务和异步任务分别进入不同的执行"场所" 先执行主线程执行栈中的宏任务 执行过程中如果遇到微任务,进入Event Table并注册函数,完成后移入到微任务的任务队列中 宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行) 主线程会不断获取任务队列中的任务、执行任务、再获取、再执行任务也就是常说的Event Loop(事件循环)

事件循环Event Loop

  1. js是单线程,防止代码阻塞,代码(任务)分为:同步和异步
  2. 同步代码给js引擎执行,异步代码交给宿主环境执行(浏览器或node)
  3. 同步代码放入执行栈中,异步代码等待时机成熟送入任务队列排队
  4. 执行栈执行完毕,会去任务队列中查看是否有异步任务,有就放在执行栈中执行,反复循环查找执行,这个过程就是事件循环(Event Loop)

V8垃圾回收机制(TODO)

v8 的垃圾回收机制基于分代回收机制,这个机制又基于世代假说,这个假说有两个特点,一是新生的对象容易早死,另一个是不死的对象会活得更久。基于这个假说,v8 引擎将内存分为了新生代和老生代。

新创建的对象或者只经历过一次的垃圾回收的对象被称为新生代。经历过多次垃圾回收的对象被称为老生代。

新生代被分为 FromTo 两个空间,To 一般是闲置的。当 From 空间满了的时候会执行 Scavenge 算法进行垃圾回收。当我们执行垃圾回收算法的时候应用逻辑将会停止,等垃圾回收结束后再继续执行。这个算法分为三步:

(1)首先检查 From 空间的存活对象,如果对象存活则判断对象是否满足晋升到老生代的条件,如果满足条件则晋升到老生代。如果不满足条件则移动 To 空间。

(2)如果对象不存活,则释放对象的空间。

(3)最后将 From 空间和 To 空间角色进行交换。

新生代对象晋升到老生代有两个条件:

(1)第一个是判断是对象否已经经过一次 Scavenge 回收。若经历过,则将对象从 From 空间复制到老生代中;若没有经历,则复制到 To 空间。

(2)第二个是 To 空间的内存使用占比是否超过限制。当对象从 From 空间复制到 To 空间时,若 To 空间使用超过 25%,则对象直接晋升到老生代中。设置 25% 的原因主要是因为算法结束后,两个空间结束后会交换位置,如果 To 空间的内存太小,会影响后续的内存分配。

老生代采用了标记清除法和标记压缩法。标记清除法首先会对内存中存活的对象进行标记,标记结束后清除掉那些没有标记的对象。由于标记清除后会造成很多的内存碎片,不便于后面的内存分配。所以了解决内存碎片的问题引入了标记压缩法。

由于在进行垃圾回收的时候会暂停应用的逻辑,对于新生代方法由于内存小,每次停顿的时间不会太长,但对于老生代来说每次垃圾回收的时间长,停顿会造成很大的影响。 为了解决这个问题 V8 引入了增量标记的方法,将一次停顿进行的过程分为了多步,每次执行完一小步就让运行逻辑执行一会,就这样交替运行。

ES6新特性

  • let const

    let 可修改、不可重复声明变量

    const 不可修改(可修改对象中的属性)、不可重复声明变量

  • 新增展开运算符...

[...[1], ...[2]] // [1, 2]
{...{a: 1}, ...{b: 2}} // {a: 1, b: 2}
  • 模块化(model)

    • 导出export

      export function model(value) {
          return value
      }
      let name = '张三'
      let age = 18
      let sex = '男'
      export {name, age}
      function defaultFun(value) {
          return value
      }
      export default defaultFun
      
    • 导入import

      import {model} from 'model.js'
      import {name, age} from 'model.js'
      import defaultFun from 'model.js'
      
  • 模板字符串

    优点:拼接变量和字符串更加简洁,

    let name = '张三'
    let age = 18
    let value1 = name + '今年' + age + '岁了'
    let value2 = `${name}今年${age}岁了 ` // 张三今年18岁了 
    
  • 箭头函数

    =>是关键字function的简写;箭头函数与包围它的代码共享同一个this,能帮你很好的解决this的指向问题

    () => 1
    v => v + 1
    (a, b) => a + b
    () => {}
    
  • 函数参数默认值

    function fn(name = '张三', age = 18) {
        return name + '' + age
    }
    fn() // 张三18
    fn('李四', 20) // 李四20
    
  • 解构赋值

    // 交换位置
    var a = 1;
    var b = 3;
    [a, b] = [b, a];
    console.log(a); // 3
    console.log(b); // 1// 对象解构赋值
    const student = {
      name:'Ming',
      age:'18',
      city:'Shanghai'  
    };
    const {name,age,city} = student;
    
  • 对象属性简写

    const name='Ming',age='18',city='Shanghai';
    const student = {
        name,
        age,
        city
    };
    
  • 类(class)

  • promise

  • Proxy

Vue

vue是什么

数据驱动视图,渐进式框架

vue是一个轻量级的前端框架,具有响应式编程和组件化的特点

响应式编程:是以MVVM实现修改数据视图自动更新,视图变化数据自定更新的数据双向绑定

组件化:可以将封装好的代码注册成为标签,比如:Vue.component('com', com),在编译模板中调用,比如<com></com>。也可以将公共功能抽离成组件注册在全局使用。优点:减少代码量,提高开发效率

响应式原理

vue2响应式原理

通过Object.defineProperty来劫持数据,通过深度遍历对象,给每个属性添加gettersetter,数据发生变化时触发相应的监听回调,实现响应式

缺点:新增、删除属性和通过索引修改数据,不会更新视图($set可以解决)

1. 核心思想
Vue 的响应式原理是通过数据劫持结合发布-订阅模式实现的,核心目标是实现数据变化时自动触发视图更新。具体分为以下几个步骤:”

2. 分步骤解析
(1) 数据劫持(Data Observation)

  • 对象属性:Vue 2 使用 Object.defineProperty 递归遍历对象的每个属性,将其转换为 getter 和 setter

    • 依赖收集:在 getter 中,当前正在执行的组件渲染函数(即 Watcher)会被记录到该属性的依赖列表(Dep)中。
    • 触发更新:在 setter 中,当属性值变化时,会通知所有依赖该属性的 Watcher 执行更新。
  • 数组处理:由于 Object.defineProperty 无法监听数组索引变化,Vue 2 重写了数组的 7 个方法(如 pushsplice),并在调用这些方法时触发更新。

(2) 依赖管理(Dependency Tracking)

  • Dep 类:每个响应式属性对应一个 Dep 实例,用于管理所有依赖它的 Watcher
  • Watcher 类:代表一个观察者(如组件的渲染函数、计算属性或侦听器),在初始化时触发 getter 完成依赖收集,数据变化时触发 update 方法重新执行逻辑。

(3) 异步更新队列

  • 数据变化后,Vue 不会立即更新 DOM,而是将更新操作推入一个异步队列,通过 Promise.then 或 setTimeout 在下一个事件循环统一执行。
  • 这种批处理机制避免了同一事件循环内多次数据变更导致的重复渲染。

(4) 虚拟 DOM 与 Diff 算法

  • 数据变化后生成新的虚拟 DOM 树,与旧树进行对比(Diff 算法),找出最小变更。
  • 仅对变化的 DOM 节点进行实际更新,减少性能开销。

3. 对比 Vue 3 的改进(加分项)

  • Vue 3 使用 Proxy 替代 Object.defineProperty,直接代理整个对象,支持动态新增/删除属性和数组索引监听。
  • Proxy 的惰性劫持机制提升了性能,且无需手动调用 Vue.set/Vue.delete
  • 依赖收集通过 effect 函数和 ReactiveEffect 类实现,逻辑更清晰。
vue3响应式原理

通过Proxy代理,拦截对象中任何数据的变化,包括属性的读写、新增、删除

通过Reflect反射,对源对象的属性进行操作

为什么选择VUE3

响应式原理不一样

vue2的Object.definProperty对于对象属性的新增、删除和通过下标修改数组数据监听不到,需要通过vue.set解决。

vue3的proxy代理可以监听任何数据变化,功能更加强大

性能不一样

vue2的Object.defineProperty监听数据,需要循环遍历数据对象,给每个属性都添加上getter、setter。而vue3直接可以通过proxy监听整个对象。

vue3打包项目体积比vue2小

typescript的支持

新增组合式api,更好的逻辑重复和代码组织

ssr渲染性能提升

支持多个根节点

vue3解构赋值失去响应式问题(新增了toRefs,可忽略)

reactive 解构或重新赋值都会失去响应式

ref 重新赋值不会失去响应式;解构需要把响应式数据放在一个Object里面才不会丢失响应式

// 重新赋值不丢失响应式
let count = ref(0)
let newCount = count
newCount++

// 解构不丢失响应式
const object = {
    foo: ref(0),
    bar: ref(0)
}
const { foo, bar } = object

数据双向绑定原理

  1. 数据到视图:利用 Object.defineProperty(Vue 2)或 Proxy(Vue 3)劫持数据变化,触发视图更新。
  2. 视图到数据:通过 v-model 语法糖绑定数据监听事件,在事件回调更新数据

是采用数据劫持结合开发者-订阅者模式的方式,通过Object.defineProperty()劫持数据中的各个属性的getter、setter,在数据发生变化时,发送消息给订阅者,触发相应的监听回调来更新视图。主要分为以下几个步骤:

1.监听器Observer劫持监听数据中的所有属性,如有变动,通知订阅者

2.订阅者Watcher收到属性的变化通知,并执行更新函数

3.解析器Compiler解析编译模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将指令对象的节点绑定更新函数,添加数据监听的订阅者

4.以MVVM作为数据绑定的入口,整合Observercompilerwatcher三者,通过Observer监听自己的model数据变化,通过compiler解析编译模板指令,利用watcher搭起Observercompiler之间的桥梁通信。达到 数据变化视图更新、视图变化数据更新 的数据双向绑定原理。

响应式和数据双向绑定区分

响应式:数据驱动视图更新

数据双向绑定:数据和视图相互驱动更新

v-model

v-model 其实是一个语法糖,他本质上是包含两个操作:

  • v-bind 绑定一个 value 属性。
  • v-on 指令 给当前元素绑定 input 事件。

例如 input 事件。当用户在表单元素中输入内容时,事件监听器会捕获到这个变化,并将新的值赋值给相应的数据属性。例如,使用 v-model 指令时,它本质上是 :value 和 @input 的语法糖。

为什么 Vue 2 无法监听数组索引变化?

Object.defineProperty 无法监听数组索引赋值和 length 修改,所以 Vue 2 通过重写数组方法实现响应式。

Vue.set和Vue.$set

Vue.set是全局方法 Vue.set(obj, key, value)

Vue.$set是实例方法 Vue.$set(obj, key, value)

作用都是给响应式对象添加新属性,确保新属性也是响应式的

Proxy 相比 defineProperty 的优势

Proxy 直接代理整个对象,支持监听动态属性增删和数组索引变化,且无需递归初始化,性能更好

definProperty对于对象属性的新增、删除和数组索引监听不到,需要通过Vue.set解决

MVVM

Model(模型) View(视图) ViewModel

ViewModel:连接Model和View,是View和Model之间的通信桥梁。是MVVM的核心,作用是监听到视图发生变化时,会自动更新数据。修改数据时,会自动更新视图。

MVVM实现了数据驱动视图和数据的双向绑定原理

MVC

Model(模型) View(视图) Controller(控制器)

View视图只负责数据展示,页面交互会传递给控制器Controller负责处理操作,并且更新模型Model,最后通知View视图层更新。MVC,模型和视图是独立的,视图和控制器也是独立的

MVVM和MVC的区别

MVC是单向通信,属于后端开发思想;MVVM是双向通信,属于前端开发思想

vue组件中data为什么是个函数

防止多个组件实例对象共用一个data,产生数据污染。将data定义成一个函数,初始化时会返回一个全新data对象,确保每个组件实例都有自己的独立数据,不会相互干扰。

vue2生命周期

创建=>初始化数据=>编译template=>挂载DOM=>渲染=>更新渲染=>销毁

beforeCreate 创建前:挂载元素el和数据对象data都为undefined,还未初始化

created 创建后:对象数据data有了,el为undefined

beforeMount 载入前: 初始化了,为虚拟DOM节点

mounted 载入了:vue实例挂载成功,DOM树渲染到页面,可进行DOM操作

beforeUpdate 更新前:数据更新时出发

updated 更新后:虚拟DOM重新渲染

activated keep-alive缓存的组件激活时使用

deactivated keep-alive缓存的组件失效时使用

beforeDestroy 销毁前:data的更改不会触发生命周期,vue实例解除了事件监听和DOM绑定

destroyed 销毁后:

vue3生命周期

setup=>渲染=>更新渲染=>卸载

setup 创建前:挂载元素el和数据对象data都为undefined,还未初始化

onBeforeMount 载入前: 初始化了,为虚拟DOM节点

onMounted 载入了:vue实例挂载成功,DOM树渲染到页面,可进行DOM操作

onBeforeUpdate 更新前:数据更新时出发

onUpdated 更新后:虚拟DOM重新渲染

onUnMounted 卸载:

v-if和-v-show

v-if 条件切换时,会对标签进行创建、销毁

v-show 条件切换时,控制标签属性css隐藏显示,性能更加

computed

  • 支持缓存,数据发生变化时,才会重新计算

  • 不支持异步

  • 默认走缓存,基于data或prop中的数据计算得到的值

  • 有getter、setter属性,默认走getter

watch

  • 不支持缓存,数据发生变化时,直接出发相应的操作

  • 支持异步

  • immediate 立即执行函数

  • deep 深度监听

vue-loader

vue文件的加载器,跟template/js/sryle转换成js模块

$nextTick

nextTick是在下次dom更新循环结束之后执行的延迟回调,数据修改之后使用nextTick,则可以在回调中获取更新后dom

v-for key的作用

给虚拟dom节点上添加唯一的id,数据更新时,根据id对比是否需要更新虚拟dom节点,从而提高效率和节约性能。

vue性能优化

  • 启用CDN加速

  • DNS预解析

  • 减少main入口文件的加载体积

  • v-if和v-show使用

  • v-for的key

  • watch和computed

  • 合理使用生命周期:动态组件用keep-alive进行缓存处理,在actived激活,在beforeDestroy销毁绑定事件和定时器

  • 路由懒加载

  • 插件按需引入使用

  • 图标尽量采用字体图标使用

  • 减少代码量:抽离公共方法到mixins,定义公共方法至公共js中,抽离公共组件,抽离公共css

  • 防抖节流

vue组件通信

props 夫传子

$emit 子传父

ref 获取组件实例

$parent 获取父组件实例

$children 获取子组件实例

$bus ($bus.on()接收 $bus.emit()传参)

$attrs 没在prop中声明

$listeners

provide和inject 祖先组件用provide传递 后代组件用inject接收

vuex 状态管理器

父子关系的组件数据传递选择 props 与 $emit进行传递,也可选择ref

兄弟关系的组件数据传递可选择bus,其次可以选择bus,其次可以选择 parent 进行传递

祖先与后代组件数据传递可选择attrs与listeners或者 Provide与 Inject

复杂关系的组件数据传递可以通过vuex存放共享的变量

vue常用修饰符

  • 表单修饰符

.number v-model.number 将至自动转换成数字类型

.trim v-model.trim 自动去除首位空格

  • 事件修饰符

.stop 阻止事件冒泡

.prevent 阻止标签默认行为

.self 仅绑定元素自身触发

.once 仅触发一次

自定义指令

Vue.directive(name, object),第一个参数是指令名称,第二个参数是对象数据,也可以是函数

VUEX

vuex是vue的状态管理器,可以提供全局共享数据,用于解决复杂层级组件传参问题,有5个属性

state 储存数据,类似与vue的data

mutations 修改state的方法

actions 修改state的异步方法,通过commit方法调用mutations里面的方法

getters 类似于vue的computed

modules state模块化管理

vuue-router

导航守卫

全局前置守卫 beforeEach

router.beforeEach((to, from, next) => {
	next()
})

全局后置守卫 afterEach

router.afterEach((to, from) => {

})

全局解析守卫 beforeResolve

router.beforeResolve(async to => {
  if (to.meta.requiresCamera) {
    try {
      await askForCameraPermission()
    } catch (error) {
      if (error instanceof NotAllowedError) {
        // ... 处理错误,然后取消导航
        return false
      } else {
        // 意料之外的错误,取消导航并把错误传给全局处理器
        throw error
      }
    }
  }
})

路由独享守卫 beforeEnter

const routes = [
  {
    path: '/users/:id',
    component: UserDetails,
    beforeEnter: (to, from, next) => {
      next()
    },
  },
]

组件内的守卫 beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave

  beforeRouteEnter(to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
    ```
      next(vm => {
        // 通过 `vm` 访问组件实例
      })
  },
  beforeRouteUpdate(to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave(to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
    // next() 放行
    // next(false) 取消
  }
完整的导航解析流程
  1. 导航被触发。
  2. 在失活的组件里调用 beforeRouteLeave 守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
  5. 在路由配置里调用 beforeEnter
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter
  8. 调用全局的 beforeResolve 守卫 (2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

hash和history

路由模式:hash、history

hash模式:

localhost:8080/#/home/index

地址中永远带着#号,兼容性好

history模式:

localhost:8080

地址干净美观,兼容性比hash差,应用上线需要后端人员支持

diff算法

diff 算法是一种通过同层的树节点进行比较的高效算法

diff整体策略为:深度优先,同层比较 比较只会在同层级进行, 不会跨层级比较 比较的过程中,循环从两边向中间收拢

  • 当数据发生改变时,订阅者watcher就会调用patch给真实的DOM打补丁

  • 通过isSameVnode进行判断,相同则调用patchVnode方法

  • patchVnode做了以下操作:

    • 找到对应的真实dom,称为el
    • 如果都有都有文本节点且不相等,将el文本节点设置为Vnode的文本节点
    • 如果oldVnode有子节点而VNode没有,则删除el子节点
    • 如果oldVnode没有子节点而VNode有,则将VNode的子节点真实化后添加到el
    • 如果两者都有子节点,则执行updateChildren函数比较子节点
  • updateChildren主要做了以下操作:

    • 设置新旧VNode的头尾指针
    • 新旧头尾指针进行比较,循环向中间靠拢,根据情况调用patchVnode进行patch重复流程、调用createElem创建一个新节点,从哈希表寻找 key一致的VNode 节点再分情况操作

什么是SPA单页面应用

是只有一张web页的应用,用户与应用程序交互都在一个页面里实现。如vue npm run build 打包出来生成一个index.html文件,称只有一个HTML的页面为单页面应用

SPA首屏加载优化

1、减小入口文件体积

2、静态资源本地缓存

3、UI框架按需加载

4、图片资源压缩

5、路由懒加载

6、抽离公共组件、公共样式,简化代码,减少打包体积

7、v-show、v-if合理使用

8、打包优化

从0到1构建一个vue项目需要做哪些内容

个人描述:首先我不是架构师,我以前端开发的思维描述的话:

1、技术选型: 根据业务选择Web、App、小程序

2、创建项目

Web端通过Vue脚手架创建,可选择Vue2、Vue3版本;

App通过HBuilder 创建Uniapp项目;

小程序通过微信开发者工具创建

3、配置路由

配置基本的路由信息,meta信息,登陆、注册、主页、404等基本页面,页面文件引入按路由懒加载;配置全局路由守卫,设置权限拦截和登陆拦截

4、配置 Vuex 或 Pinia

配置全局状态管理

5、配置请求

配置请求设置,设置基准地址、请求超时时间; 设置请求拦截,并配置请求头token字段和数据; 设置请求拦截,根据接口返回的状态做出判断,401重定向登陆、404重定向404页面、500提示服务器错误等

6、二次封装请求接口

将接口按照不同模块管理,并且把请求地址、请求方式、传参等封装成函数暴露出去使用,页面根据需求引入接口方法直接使用。

7、配置UI框架

引入合适的UI框架,并配置使用。例如web端使用Elmenet UI,移动端使用Vant等

8、配置环境

配置生产、测试、开发环境的请求地址

9、配置代理

本地运行配置proxy代理,解决开发环境跨域问题。Vue在vue.config.js文件,Uniappp在manifest文件

10、配置webpack、vite

配置项目的加载器、插件、别名、打包输出等,

11、公共组件

根据业务封装公共组件,例如表单、列表、按钮、上传等

12、公共方法

根据业务封装公共方法,例如获取格式化时间、表单校验等

13、公共样式

根据业务封装全局统一风格样式

uniapp

uniapp、vue、小程序

uniapp简单来说就是vue和小程序的结合体,它使用vue的结构、指令、语法、和小程序的组件、API,既能编写各种小程序,又能实现vue移动端网站和app

uniapp优缺点

优点

  1. 一套代码可以生成多端
  2. 学习成本低,语法是vue的,组件是小程序的
  3. 扩展能力强
  4. 使用HBuilderX开发 支持vue语法
  5. 突破了系统对H5调用原生能力的限制

缺点

  1. 问世时间短,很多地方不完善
  2. 社区不大
  3. 官方对问题的反馈不及时
  4. 在Android平台上比微信小程序和IOS差
  5. 文件命名受限

生命周期

  • 应用

    • onLaunch 初始化完成触发
    • onShow 前台显示
    • onHide 从前台进入后台
    • onError 报错
    • onUniNViewMessage 对 nvue 页面发送的数据进行监听,可参考 nvue 向 vue 通讯
    • onUnhandledRejection 对未处理的 Promise 拒绝事件监听函数
    • onPageNotFound 页面不存在监听函数
    • onThemeChange 监听系统主题变化
  • 页面

    • onInit 初始化
    • onLoad 加载
    • onShow 显示
    • onReady 初次渲染完成
    • onHide 隐藏
    • onUnload 卸载
    • onResize 监听窗口尺寸变化
    • onPullDownRefresh 下拉刷新(uni.stopPullDownRefresh停止刷新)
    • onReachBottom 滚动触底
    • onPageScroll 页面滚动
  • 组件

    • beforeCreate 创建前
    • created 创建后
    • beforeMount 载入前
    • mounted 载入后
    • beforeUpdate 更新前
    • updated 更新后
    • beforeDestroy 销毁前
    • destroyed 销毁后

配置文件

pages.josn 配置页面路由、导航条、选项卡等页面类信息

manifest.josn 配置应用名称、appid、logo、版本等打包信息

获取当前应用实例

getApp()

获取当前页面栈

getCurrentPages()

定位

uni.getLocation()

缓存

  • 异步

    • uni.setStrorage()
    • uni.getStrorage()
    • uni.removeStrorage()
  • 同步

    • uni.setStrorageSync()
    • uni.getStrorageSync()
    • uni.removeStrorageSync()

跳转

  • uni.navigatTo() 转非tabbar页面,不会关闭当前页,可以返回
  • uni.redirectTo() 跳转非tabbar页面,会关闭当前页
  • uni.releanch() 关闭所有已打开的页面,跳转到任意页面
  • uni.switchTab() 只能从tab页面切换到tab页面

跳转传参

通过跳转路径后面拼接参数进行传参

常见组件

viewscroll-viewicontextbuttonimagemap(地图)、camera(相机)、swiper轮播图

操作DOM方法

renderjs

<script module="test" lang="renderjs">
        export default {
                mounted() {
                        // ...
                },
                methods: {
                        // ...
                }
        }
</script>

条件编译

// app平台下
#ifdef APP-PLUS 
需条件编译的代码  
#endif

// H5平台下
#ifndef H5 
需条件编译的代码  
#endif

// H5 和 微信小程序 平台下
#ifdef H5 || MP-WEIXIN  
需条件编译的代码  
#endif

vue和nvue的区别

  • vue 页面

    • 基于 WebView 渲染
    • 使用标准 Vue 语法
    • 跨平台兼容性更好
  • nvue 页面

    • 基于原生渲染引擎(Weex)
    • 使用类似 Vue 的语法但有差异
    • 性能更高,但平台差异更明显

微信小程序

小程序有几个文件

WXML:HTML结构

WXSS:CSS样式

js:js逻辑,可以写vue语法

json:小程序页面配置

标签绑定事件传参

通过 data-key = value 绑定

通过 e.currentTarget.dataset.key 获取

<button bindtap="get" data-name="测试">拿到传值</button> 
get(e){ 
    console.log(e.currentTarget.dataset.name) 
}

WXSS 和 CSS 区别

WXSS 背景图片只能引入外链,不能使用本地图片

小程序使用@import引入样式文件

尺寸单位为rpx

修改data的值

Vue 
this.data.key = value

小程序
this.setData({ key: value })

生命周期函数

onLoad:页面加载时触发,只触发一次,可获取页面传参

onSHow:页面显示/切入前台时触发

onReady:页面初次渲染完成时触发,只触发一次

onHide:页面隐藏/切入后台时触发 navigateTo、tab跳转

onUnload:页面卸载时触发 redirectTo、navigateBack跳转

onPullDownRefresh:监听页面下拉刷新

onReachBottom:监听上拉触底

页面刷新

wx.stopPullDownRefresh停止刷新

wx.startPullDownRefresh开始刷新

bind:tap和catch:tap区别

bind:tap 会向上冒泡

catch:tap 阻止事件冒泡

页面传参

路由:拼接到跳转url后面,在onLoad生命周期接收

全局:app.globalData.key = value

路由跳转

wx.navigateTo() : 保留当前页面,跳转到应用内的某个页面。但是不能跳到 tabbar 页面

wx.redirectTo() : 关闭当前页面,跳转到应用内的某个页面。但是不允许跳转到 tabbar 页面

wx.switchTab() : 跳转到 TabBar 页面,并关闭其他所有非 tabBar 页面

wx.navigateBack() : 关闭当前页面,返回上一页面或多级页面。可通过getCurrentPages() 获取当前的页面栈,决定需要返回几层

wx.reLaunch() : 关闭所有页面,打开到应用的某个页面。

自定义组件传递双向绑定

配置

全局配置 app.json

{
  "pages": [
    "pages/index/index",
    "pages/logs/index"
  ],
  "window": {
    "navigationBarTitleText": "Demo"
  },
  "tabBar": {
    "list": [{
      "pagePath": "pages/index/index",
      "text": "首页"
    }, {
      "pagePath": "pages/logs/index",
      "text": "日志"
    }]
  },
  "networkTimeout": {
    "request": 10000,
    "downloadFile": 10000
  },
  "debug": true
}

页面配置 json文件

{
  "navigationBarBackgroundColor": "#ffffff",
  "navigationBarTextStyle": "black",
  "navigationBarTitleText": "微信接口功能演示",
  "backgroundColor": "#eeeeee",
  "backgroundTextStyle": "light"
}

分包

app.json 中配置

{
  "pages":[
    "pages/index",
    "pages/logs"
  ],
  "subPackages": [
    {
      "root": "packageA",
      "pages": [
        "pages/cat",
        "pages/dog"
      ],
      "entry": "index.js"
    }, {
      "root": "packageB",
      "name": "pack2",
      "pages": [
        "pages/apple",
        "pages/banana"
      ]
    }
  ]
}

TypeScript

基础数据类型

  • 布尔值 boolean
let status: boolean = false
  • 数字 number
let count: number = 10
  • 字符串 string
let str: string = '123'
  • 数组 []
let arr1: number[] = [1, 2, 3]
let arr2: Array<> = [1, 2, 3]

函数

function fn(a: number, b: number) : number{
    return a + b
}

元祖

表示一个已知元素数量和类型的数组。

// 各元素的类型不必相同,比如你可以定义一对值分别为string和number类型的元祖
let x: [string, number]
x = ['hello', 10] // ok
x = [true, 'hi'] // error

枚举

枚举就是一个对象的所有可能取值的集合

enum类型是对JavaScript标准类型的一个补充。像C#等其他语言一样,使用枚举类型可以为一组数值赋予友好的名字(特殊的Key-value对象)

enum Color {red, green, blue}
let c: Color = COlor.red

enum Peroson {
    name: '张三',
    sex: '男',
    age: 18
},
let p: Person = Person.name

any

在不清楚变量的类型的情况下指定一个any类型。这个变量的值可能是动态的。any类型还可以类型检查

let notValue1: any = 4
let list: any[] = [1, true, "free"];

void

当一个函数没有返回值时,其返回值类型默认为void

function fn(): void {
    
}

null、undefined

TypeScript中,null和undefined两者各有自己的类型分别叫做null和undefined。和void相似,他们的本身的类型作用不是很大

let a: null = null
let b: undefined = undefined

nerver

表示永不存在的值的类型。例如,通常用作于抛出异常和错误

function error(message: string): never {
    throw new Error(message);
}
function fail() {
    return error("Something failed");
}

Object

表示非原始类型,也就是除number、string、boolean、symbol、null、undefined之外的类型,使用Obejct类型,就可以更好的表示像Object.create这样的api

declare function create(o: object | null): void;

create({ prop: 0 }); // OK
create(null); // OK

create(42); // Error
create("string"); // Error
create(false); // Error
create(undefined); // Error

类型断言

有时候你会遇到这样的情况,你会比TypeScript更了解某个值的详情信息。例如,你知道某个值是字符串类型。

let someValue: any = "this is a string";
// 写法一
let strLength: number = (<string>someValue).length;
// 写法二
let strLength: number = (someValue as string).length;

接口

一个对象的集合,里面包含了值和类型,可继承

interface a {
    a: string,
    b: number
}

联合类型 |

let value: string | number;
value = "Hello";  // 合法
value = 42;  // 合法

interface和type区别

  • 扩展不同

    interface 通过 extends关键字扩展; type 通过 交叉类型& 组合

    type Person = {
      name: string;
      age: number;
    };
    
    interface Employee extends Person {
      employeeId: string;
    }
    
    type Employee = Person & { employeeId: string; }
    type Employee2 = Person | { employeeId: string; }
    

    type 继承 interface

    //通过 & (类型交叉) 达到继承的目的 
    interface ParticalPointX {
        x: number
    }
    type Point = ParticalPointX & {y: number};
    

    interface 继承 type

    type PartialPointX = {x: number}
    interface Point extends PartialPointX {
        y: number;
    }
    
  • 定义方式不同

    interface Person {
      name: string;
      age: number;
      greet(): void;
    }
    
    type Person = {
      name: string;
      age: number;
    };
    
  • 合并不同

    interface:重复定义同名interface,回自动合并成一个接口

    type:不支持合并行为,同名的type别名会报错

  • 灵活性

    interface 仅支持描述对象、函数等结构化类型

    type可定义联合、元祖、字面量等任意类型

泛型

可重复创建组件的方法和工具,可以使用多种类型。

function fn<T>(value: T): T {
    return value
}
fn<string>("hellow")

访问修饰符

public 公共的,谁都可以访问

private 私有的, 只有类的成员可以访问

protected 受保护的,该类及其子类的所有成员都可以访问