前端面试题汇总大全!(四)

简介: 前端面试题汇总大全!

六、TypeScript篇


1.TypeScript基本数据类型


原始数据类型:


  • boolean 布尔值
  • number 数值
  • string 字符串
  • null 空值
  • undefined 未定义
  • Symbol (ES6 中的新类型)


非原始数据类型:


  • 数组 arr: number[] arr: Array
  • Tuple 元祖 x: [string, number]
  • enum 枚举


enum Color {Red, Green, Blue};
let c: Color = Color.Blue;//默认情况下,从 0 开始为元素编号。
console.log(c);    // 输出 2
enum Person {
 name = "NAME",
 age = "AGE",
 love = "LOVE",
 hobby = "HOBBY",
}
console.log(Person.name); // NAME
console.log(Person.hobby); // HOBBY


  • never 永不存在的值的类型
  • void


//void 类型像是与 any 类型相反,它表示没有任何类型。 当一个函数没有返回值时,你通常会见到其返回值类型是 void。
function hello(): void {
   alert("Hello ts");
}


  • any 任意类型
  • 联合类型


//联合类型(Union Types)表示取值可以为多种类型中的一种。
let num: string | number;
num = "seven";
num = 7;


  • 函数类型
  • unknown 表示一个未知的类型,使用unknown标注的变量和参数必须经过类型检查和转换后才能使用。


2.void和undefined


let unde: void = undefined;
let nu: void = null;
let un: undefined = undefined;
let nu: null = null;
//与 void 的区别是,undefined是所有类型的子类型。也就是说 undefined 类型的变量,可以赋值给 number 类型的变量
let num: number = undefined;
let un: undefined;
let num2: number = un;


3.TypeScript类修饰符


TypeScript支持访符 public,private 和 protected,它们决定了类成员的可访问性。


public(公共):所有定义成public的属性和方法都可以在任何地方进行访问。


private(私有):所有定义成private的属性和方法都只能在类定义内部进行访问。


protected(受保护): 该类及其子类的所有成员都可以访问它们。 但是该类的实例无法访问


4.如何定义一个数组,它的元素可能是字符串类型,也可能是数值类型


//通过不同方式使用联合类型写法  |
// 方法1:
let arr1: (number | string)[] = [1]
arr1.push(1)
arr1.push('3')
// 方法2:
let  arr2 : Array<string | number> = [1, '2']
arr2.push(1)
arr2.push('3')
// 方法3:
type newType = number|string
let arr3:newType []= [3]
arr3.push(1)
arr4.push('5')


5.TypeScript接口


接口是一系列抽象方法的声明,是一些方法特征的集合,这些方法都应该是抽象的,需要由具体的类去实现,然后第三方就可以通过这组抽象方法调用,让具体的类执行具体的方法。


//接口定义 interface
interface interface_name { 
}


//以下实例中,我们定义了一个接口 IPerson,接着定义了一个变量 customer,它的类型是 IPerson。
//customer 实现了接口 IPerson 的属性和方法。
interface IPerson { 
    firstName:string, 
    lastName:string, 
    sayHi: ()=>string 
} 
var customer:IPerson = { 
    firstName:"Tom",
    lastName:"Hanks", 
    sayHi: ():string =>{return "Hi there"} 
} 
console.log("Customer 对象 ") //Customer 对象
console.log(customer.firstName) //Tom
console.log(customer.lastName)//Hanks
console.log(customer.sayHi()) //Hi there


6.TypeScript对象和TypeScript接口的区别


对象:对象是TS中一种数据类型,可以用来存储多个数据属性和方法,并且可以通过对象字面量或者构造函数来创建实例


接口:接口是TS中定义数据结构的规范,主要用于描述对象的形状,即对象应该包含哪些属性和方法,但不提供实现。接口可以作为一个契约,确保其他代码符合其规范


//对象
let person = {
  name: "Tom",
  age: 18,
  sayHi: function() {
    console.log("Hi");
  }
};
console.log(person.name); // Tom
person.sayHi(); // Hi
//接口
interface Person {
  name: string;
  age: number;
  sayHi(): void;
}
let person: Person = {
  name: "Tom",
  age: 18,
  sayHi() {
    console.log("Hi");
  }
};
console.log(person.name); // Tom
person.sayHi(); // Hi


类型别名及类型符号csdn详情参考


7.TypeScript类型别名


上述介绍了ts中的数据类型和接口,但是在开发过程中 不可避免的会遇到各种复杂类型 , 有些数据类型更灵活、复杂,那么此时,类型别名是一种非常有用的工具,它可以帮助我们简化代码,提高代码的可读性和可维护性


基本用法: type 类型名 = 类型值


type Name = string;
type Age = number;
type Person = {
  name: Name;
  age: Age;
  sayHi(): void;
};
let person: Person = {
  name: "Tom",
  age: 18,
  sayHi() {
    console.log("Hi");
  }
};
console.log(person.name); // Tom
person.sayHi(); // Hi


8.TypeScript字面量类型


字面量类型(Literal Types)用来表示具体的字面量值,包括字符串、数字、布尔值等。它们可以作为类型注解的一部分,用来限制变量、函数参数、函数返回值等的取值范围


  • 数字字面量,字符串字面量,布尔字面量,空值字面量,枚举字面量


//数字字面量
type Num = 10
const num: Num = 10
const num2: Num = 20 // 抛错,不能将20赋值给类型10
//字符串字面量
type Str = "a"
const num: Str = "a"
//布尔字面量
type Bool = false
const bool: Bool = false
//空值字面量
type Void = void;
const isNull: Void = null
const isUndefined: Void = undefined
const isVoid: Void = void 0
//枚举字面量
enum Color {
    Red = 1,
    Green,
    Blue
}
type colorBlue = Color.Blue
const blue: colorBlue = 3


9.TypeScript类型符号


TypeScript中的类型符号是用来声明变量类型的符号,主要有以下几种:


  • ::用于声明变量的类型,例如:let a: number = 10; 表示声明一个名为a的变量,并将其类型标注为number。
  • ?:用于表示某个属性或参数是可选的,例如:interface Person { name: string; age?: number; } 表示Person接口的age属性是可选的。
  • []:用于表示数组类型,例如:let arr: number[] = [1, 2, 3]; 表示声明一个名为arr的数组,其中元素类型为number。
  • ():用于表示函数类型,例如:function add(a: number, b: number): number { return a + b; } 表示声明一个名为add的函数,其中参数a和b的类型均为number,返回值类型也为number。
  • |:用于表示联合类型,例如:let c: number | boolean = 10; 表示声明一个名为c的变量,其类型为number或者boolean。
  • &:用于表示交叉类型,例如:interface A { a: number; } interface B { b: string; } type C = A & B; 表示定义一个名为C的交叉类型,它同时包含A和B两个接口的属性和方法


10.TypeScript交叉类型


交叉类型(Intersection Types)可以用来将多个类型合并为一个类型。交叉类型使用且符号&进行连接两个或多个类型;值得注意的是交叉类型一般使用于对象定义这种复杂的数据类型,如果使用交叉类型定义基础类型,则会转换为never类型,因为一个类型不可能同时兼备两种基础类型


& 连接的是简单联合类型,则产生的新类型是 & 符号两边类型的公有类型。


//交叉基础类型,错误示范
type str = string
type num = number
type StrAndNum = str & num // never类型
//正确示范
type information = {
    name: string
    age?: number  //?是可选类型,可以下放交叉不使用
}
type information2 = {
    readonly address: string
}
type user = information & information2
const whiteDog: WhiteDog = {
    name: "lc",
    address: "中国",
}
//` & 连接的是简单联合类型
type A = string | number
type B = string[] | number
// C是A和B共有的类型,number类型
type C = A & B


11.泛型对象


泛型:使用尖括号<>来声明类型参数 (可以有多个)来表示暂时未知的类型,在实际声明变量时传入相应的类型 (或者由TS自动推论) 来替换相应出现该类型参数的地方,从而将抽象的、未知的类型替换为具体的、已知的类型。一个类型参数指代一种类型,例如分别指代一种暂时未知的类型。将泛型用于定义对象类型,便得到了泛型对象。


// 类型T代表一种暂时未知的类型
interface PersonInfo<T> {
  info: T
}
// 传入类型变量string,这时候string就会在相应的地方替换原来的T
let p1: PersonInfo<string> = {
  info: 'cc'
}
let p2: PersonInfo<number> = {
  info: 18
}
let p3: PersonInfo<'男' | '女'> = {
  info: "男"
}


泛型同样可在类型别名中使用。而类型别名除了定义对象类型之外,还能用泛型来定义各种其它类型。因此,我们可以使用泛型嵌套来定义更为复杂的类型结构


12.类型断言


类型断言(Type Assertion)是一种显式地告诉编译器变量的类型的方式,它允许我们手动指定一个变量的类型,从而绕过TypeScript编译器对该变量类型的检查


//断言两种方式,<>和as
let str: unknown
const len: number = (<string>str).length
let str: unknown
const len: number = (str as string).length


个人理解:事先定义了一个变量为它设置了类型,但是在其它场景需要这个变量为其它类型为了避免报错通过断言指定它为需求类型


13. unknown 类型


类型unknown表示一个未知的类型。与any类型不同,使用unknown标注的变量和参数必须经过类型检查和转换后才能使用。


应用场景:


当我们不知道某个变量的类型时,在某些情况下可以先把它标注为unknown类型,并在使用前进行类型检查或类型转换。

当我们编写一些通用的函数或库时,为了避免与不同的代码库之间发生类型冲突,可以使用unknown类型来定义变量和参数。

function processValue(value: unknown) {
  if (typeof value === "string") {
    console.log(value.toUpperCase());
  } else if (typeof value === "number") {
    console.log(value.toFixed(2));
  }
}
let val1: unknown = "hello";
processValue(val1); // 输出HELLO
let val2: unknown = 3.1415;
processValue(val2); // 输出3.14
let val3: unknown = true;
processValue(val3); // 编译时错误:boolean类型不能转换为string或number类型


从上面的例子中可以看出,如果不进行类型检查或类型转换,使用unknown类型的变量会导致编译时错误。但是,通过类型检查和转换,我们可以安全地使用这个变量并避免类型错误。


14.函数重载


函数名称相同,但是参数的个数或者类型不同。


// 函数的定义
function add(num1: number, num2: number): number;
function add(num1: string, num2: string): string;
// 函数的实现
function add(num1: any, num2: any): any {
    return num1 + num2;
}


15.抽象类


通过abstract定义抽象类


在 TypeScript 中,抽象类是一种特殊的类,它本身不能被实例化,而只能被继承。抽象类可以包含抽象方法(即只有方法签名,没有实现),用于定义一些基本的行为或约束子类必须实现的方法,以及普通的具体方法,用于提供一些默认的实现。


// 不可以 new Animal 只能继承抽象类
abstract class Animal {
  abstract makeSound(): void;
  move(distanceInMeters: number = 0) {
    console.log(`Animal moved ${distanceInMeters}m.`);
  }
}
class Dog extends Animal {
  makeSound() {
    console.log("Woof! Woof!");
  }
}
let dog = new Dog();
dog.makeSound(); //  "Woof! Woof!"
dog.move(10); // "Animal moved 10m."


16.类型守卫,typeof 以及 instanceof


typeof 类型守卫:使用 typeof 运算符可以判断一个变量的类型是 string、number、boolean、symbol 或 undefined。


instanceof 类型守卫:使用 instanceof 运算符可以判断一个变量是否为指定类的实例。它的使用方法是在检测对象的同时指定一个构造函数作为比较对象


//typeof
function example(value: string | number) {
  if (typeof value === "string") {
    console.log(`The value is a string: ${value}`);
  } else {
    console.log(`The value is a number: ${value}`);
  }
}
example("hello"); // Output: "The value is a string: hello"
example(42); // Output: "The value is a number: 42"
//instanceof
class MyClass {}
function example(input: MyClass | string) {
  if (input instanceof MyClass) {
    console.log("The input is an instance of MyClass.");
  } else {
    console.log(`The input is a string: ${input}`);
  }
}
example(new MyClass()); // Output: "The input is an instance of MyClass."
example("hello"); // Output: "The input is a string: hello"


17.TypeScript 编译上下文


在 TypeScript 中,编译上下文是指编译器对源代码进行类型检查和转换时所使用的环境和规则编译上下文可以包含以下几个方面:


  • 编译选项:编译器可以通过命令行参数或 tsconfig.json 文件等方式配置各种编译选项,包括输出目录、模块解析方式、生成的 JavaScript 版本等。
  • 类型检查:编译器会对源代码进行类型检查,以捕获可能的类型错误和提供更准确的类型推断。类型检查过程中涉及到类型系统、类型注解、类型推断等概念。
  • 类型转换:在编译过程中,TypeScript 会将源代码中使用的语言特性(如类、接口、泛型、枚举、命名空间等)转换成相应的 JavaScript 代码。这个过程中,编译器还会进行一些优化和调整。
  • 声明文件处理:TypeScript 支持使用声明文件来描述 JavaScript 库和模块的类型信息,以提高与第三方库的兼容性和开发效率。编译器会自动搜索项目中的声明文件,或者对于没有声明文件的库,也可以通过第三方工具自动生成。
  • 模块解析:在编译过程中,编译器需要解析模块之间的依赖关系,以便正确地生成相应的 JavaScript 代码文件。模块解析涉及到模块路径、模块别名、自定义解析等问题。


18.tsconfig.json


tsconfig.json 是一个用于配置 TypeScript 编译器的配置文件。通过这个配置文件,可以指定编译器所使用的规则和选项,以实现自定义编译过程和调整输出结果


{
  "compilerOptions": {
    /* 基本选项 */
    "target": "es5",                       /* 指定 ECMAScript 目标版本: 'ES3' (default), 'ES5', 'ES6'/'ES2015', 'ES2016', 'ES2017', or 'ESNEXT' */
    "module": "commonjs",                  /* 指定模块化代码生成方式: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext' */
    "lib": ["es6", "dom"],                 /* 指定要包含在编译中的库文件,默认为所有支持的库 */
    "outDir": "./dist",                    /* 指定输出目录 */
    "rootDir": "./src",                    /* 指定源代码目录 */
    /* 详细选项 */
    "strict": true,                        /* 启用所有严格类型检查选项 */
    "noImplicitAny": true,                 /* 在表达式和声明上有隐含的 any 类型时报错 */
    "strictNullChecks": true,              /* 启用严格的 null 检查 */
    "noImplicitThis": true,                /* 当 this 表达式值为 any 类型的时候,生成一个错误 */
    "alwaysStrict": true,                  /* 以严格模式检查每个模块,并在模块内部启用 "use strict" */
    /* 配置文件扩展选项 */
    "resolveJsonModule": true,             /* 允许导入 .json 模块 */
    "esModuleInterop": true,               /* 自动生成命名空间导入 (import * as module from "module") */
    "skipLibCheck": true                    /* 跳过对声明文件的检查 */
  }
}


19.TypeScript 和JavaScript区别


js没有重载概念,ts有可以重载


ts增加了接口interface、泛型、类、类的多态、继承等


ts对比js基础类型上,增加了 void/never/any/元组/枚举/以及一些高级类型


选择学习

  • 类型系统:TS 是一种拥有静态类型检查的编程语言,支持在代码中明确指定变量的数据类型、声明函数的输入和输出类型、定义接口和类等。而 JS 则是一种动态类型的语言,变量类型是在运行时动态确定的。
  • 编译方式:TS 是一种编译型语言,需要通过编译器将 TS 代码转换为等价的 JS 代码才能在浏览器或者 Node.js 中执行。而 JS 是一种解释型语言,直接由运行时解释执行。
  • 扩展语法:TS 支持 ECMAScript 标准中定义的所有语法,并且还提供了一些自己的扩展语法,例如枚举类型、泛型、元组类型等。JS 则不支持所有扩展语法,并且在新标准发布前需要等待浏览器厂商的实现或者使用 polyfill 进行补丁。
  • 开发工具:TS 具备良好的 IDE 集成、语法高亮、代码提示、错误检查等功能,开发体验更加友好。JS 的开发工具则相对简单,主要是一些文本编辑器和浏览器控制台。
  • 生态环境:TS 是由微软开发并维护的项目,有着全球范围内的活跃社区和丰富的第三方库。JS 则更加开放和自由,生态环境更加广泛和多样化。


七、React篇


1.react理解


  • React,用于构建用户界面的 JavaScript 库,只提供了 UI 层面的解决方案 ,是渐进式框架,采用mvc架构
  • 特性: JSX 语法 , 单向数据绑定 , 虚拟 DOM , 声明式编程 ,组件化开发


2.react-router 里的 标签和 标签有什么区别?


  • react-router 接管了其默认的链接跳转行为,与传统的页面跳转有区别的是,Link 的 “跳转” 行为只会触发相匹配的对应的页面内容更新,而不会刷新整个页面。
  • link做了哪些事:1.有onclick就执行onclick,2.click的时候阻止a标签默认事件,3.根据跳转href用history跳转,此时只是链接变了,并没有刷新页面

3.React Jsx转换成真实DOM过程?


  • 首先我们知道Jsx代码是不会被浏览器识别的,最终都会转化为 React.createElement 形式,babel实现这个过程
  • createElement函数对key和ref等特殊的props进行处理,并获取defaultProps对默认props进行赋值,并且对传入的孩子节点进行处理,最终构造成一个虚拟DOM对象
  • ReactDOM.render将生成好的虚拟DOM渲染到指定容器上,其中采用了批处理、事务等机制并且对特定浏览器进行了性能优化,最终转换为真实DOM


4.React Router的理解,原理?常用的Router组件有哪些?


  1. 路由的本质就是页面的URL发生改变时,页面的显示结果可以根据URL的变化而变化,但是页面不会刷新
  2. react-router主要分成了几个不同的包(这一块先不写了)
  3. 常用router组件:
  • BrowserRouter、HashRouter : 路由模式history,hash
  • route: Route用于路径的匹配,然后进行组件的渲染
  • Link、NavLink: 通常路径的跳转是使用Link组件 NavLink是在Link基础之上增加了一些样式属性,例如组件被选中时,发生样式变化 ( activeStyle , activeClassName )
  • redirect:路由重定向
  • switch: swich组件的作用适用于当匹配到第一个组件的时候,后面的组件就不应该继续匹配


5.react生命周期


  • 组件挂载时

当组件实例被创建并插入DOM时,其生命周期调用顺序如下:


constructor()

static getDerivedStateFromProps()

render()

componentDidMount()


  • 组件更新时

当组件的props或state发生变化时会触发更新。组件更新的生命周期调用顺序如下:


static getDerivedStateFromProps()

shouldComponentUpdate()

render()

getSnapshotBeforeUpdate()

componentDidUpdate()


  • 组件卸载时

当组件从DOM中移除时会调用如下方法


componentWillUnmount()


6.react新生命周期取代了哪些?为什么?


React 新的生命周期方法主要是为了支持 Fiber 架构中的三个新概念而设计的: fiber节点,异步渲染,可中断性


  • getDerivedStateFromProps(静态方法) 取代了componentWillMount和 componentWillReceiveProps
  • getSnapshotBeforeUpdate 取代了componentWillUpdate
  • getDerivedStateFromProps 中禁止了组件去访问 this.props, 由于 this.props 可能在任何时刻发生变化,所以计算出来的 state 对象可能会与旧的 state 对象相同,从而导致状态更新无效和不必要的组件重新渲染。 在 getDerivedStateFromProps 方法中应该始终使用参数中的 nextProps,而不是 this.props。这样可以保证组件状态的计算是基于最新的 props,从而避免了状态更新无效和渲染性能的问题。
  • getSnapshotBeforeUpdate 方法是在组件的 render 方法被调用后,在更新 DOM 之前被调用的 ,也就是说在 getSnapshotBeforeUpdate 中读取到的 DOM 元素信息是可以保证与componentDidUpdate 中一致的。


7.Fiber架构的理解?解决了什么问题?


在 React 16 之前,VirtualDOM 的更新采用的是Stack架构实现的,也就是循环递归方式。不过,这种对比方式有明显的缺陷,就是一旦任务开始进行就无法中断,如果遇到应用中组件数量比较庞大,那么VirtualDOM 的层级就会比较深,带来的结果就是主线程被长期占用,进而阻塞渲染、造成卡顿现象。


fiber:为了避免出现卡顿等问题,我们必须保障在执行更新操作时计算时不能超过16ms,如果超过16ms,就需要先暂停,让给浏览器进行渲染,后续再继续执行更新计算。而Fiber架构就是为了支持“可中断渲染”而创建的。 解决了react在渲染大量dom节点出现丢帧的问题


React Fiber 与浏览器的交互流程如下图。


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4G89UyXp-1686967105051)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\1685850448945.png)]


  • 从架构角度:fiber是对react核心算法的重写(调和过程)
  • 从编码角度:fiber是react内部定义的一种数据结构,是fiber树结构的节点单位,react16新架构下的虚拟dom
  • 主要操作:
  1. 为每个增加了优先级,优先级高的任务可以中断低优先级的任务。然后再重新,注意是重新执行优先级低的任务
  2. 增加了异步任务,调用requestIdleCallback api,浏览器空闲的时候执行
  3. dom diff树变成了链表,一个dom对应两个fiber(一个链表),对应两个队列,这都是为找到被中断的任务,重新执行


在 Fiber 架构中,React 将组件的更新过程分为两个阶段:reconciliation 和 commit。其中 reconciliation 阶段主要负责计算出更新后的 Virtual DOM 树,并确定哪些组件需要进行重新渲染,而 commit 阶段则负责将 Virtual DOM 树的变化映射到真实的 DOM 上。


总结:相比传统的Stack架构,Fiber 将工作划分为多个工作单元,每个工作单元在执行完成后依据剩余时间决定是否让出控制权给浏览器执行渲染。 并且它设置每个工作单元的优先级,暂停、重用和中止工作单元。 每个Fiber节点都是fiber tree上的一个节点,通过子、兄弟和返回引用连接,形成一个完整的fiber tree。


8.react有状态组件和无状态组件的理解及使用场景?、


  • 有状态组件
  •          特点:

是类组件

有继承

可以使用 this

可以使用 react 的生命周期

使用较多, 容易频繁触发生命周期钩子函数, 影响性能

内部使用 state, 维护自身状态的变化, 有状态组件根据外部组件传入的 props 和自身的 state 进行渲染

  •          使用场景:

需要使用到状态的

需要使用状态操作组件的(无状态组件的也可以实现新版本 react hooks 也可实现)

  •          总结:

类组件可以维护自身的状态变量, 即组件的 state, 类组件还有不同的生命周期方法, 可以让开发者能够在组件的不同阶段(挂载、更新、卸载), 对组件做更多的控制。类组件则既可以充当无状态组件, 也可以充当有状态组件。当一个类组件不需要管理自身状态时, 也可称为无状态组件。

  • 无状态组件
  •          特点:

不依赖自身的状态 state

可以是类组件或者函数组件

可以完全避免使用 this 关键字(由于使用的是箭头函数事件无需绑定)

有更高的性能, 当不需要使用生命周期钩子时, 应该首先使用无状态函数组件

组件内部不维护 state, 只根据外部组件传入的 props 进行渲染的组件, 当 props 改变时, 组件重新渲染

  •          使用场景

组件不需要管理 state, 纯展示

  •          优点:

简化代码、专注于 render

组件不需要被实例化, 无生命周期, 提升性能, 输出(渲染)只取决于输入(属性), 无副作用

视图和数据的解耦分离

  •          缺点:

无法使用 ref

无生命周期方法

无法控制组件的重渲染, 因为无法使用 shouldComponentUpdate 方法, 当组件接受到新的属性时则会重渲染


9. React 的事件合成?


  • 所有事件都是委托在id = root的DOM元素中(网上很多说是在document中,17版本不是了);
  • 在应用中所有节点的事件监听其实都是在id = root的DOM元素中触发;
  • React自身实现了一套事件冒泡捕获机制;
  • React实现了合成事件SyntheticEvent;
  • React在17版本不再使用事件池了(网上很多说使用了对象池来管理合成事件对象的创建销毁,那是16版本及之前);
  • 事件一旦在id = root的DOM元素中委托,其实是一直在触发的,只是没有绑定对应的回调函数;


10.React组件之间如何通信?


1.父组件向子组件通讯:

父组件可以向子组件传入props的方式,向子组件进行通讯。


2.子组件向父组件通讯:

props+回调的方式,父组件向子组件传递props进行通讯,此props为作用域为父组件自身的函数,子组件调用该函数,将子组件想要传递的信息,作为参数,传递到⽗组件的作⽤域中。


3.兄弟组件通信:

兄弟组件之间的传递,则父组件作为中间层来实现数据的互通,通过使用父组件传递

例:组件A – 传值 --> 父组件 – 传值 --> 组件B


4.跨层级通讯:

Context 设计⽬的是为了共享那些对于⼀个

组件树⽽⾔是“全局”的数据,

使用context提供了组件之间通讯的一种方式,可以共享数据,其他数据都能读取对应的数据

例如当前认证的⽤户、主题或⾸选语⾔,对于跨越多层的全局数据通过 Context 通信再适合不过。


5.发布订阅者模式:

发布者发布事件,订阅者监听事件并做出反应,我们可以通过引⼊event模块进⾏通信。


6.全局状态管理工具:

借助Redux或者Mobx等全局状态管理⼯具进⾏通信,这种⼯具会维护⼀个全局状态中⼼Store,并根据不同的事件产⽣新的状态。


11.React服务端渲染怎么做?原理是什么?


  • 服务端渲染(SSR): 指由服务侧完成页面的 HTML 结构拼接的页面处理技术,发送到浏览器,然后为其绑定状态与事件,成为完全可交互页面的过程
  • 服务端渲染解决的问题:1.SEO由于搜索引擎爬虫抓取工具可以直接查看完全渲染的页面 2.加速首屏加载,解决首屏白屏问题
  • React如何做服务端渲染?
  • 手动搭建SSR框架

    使用成熟的SSR框架,如next.js

  • 实现原理:

    node server 接收客户端请求,得到当前的请求url 路径,然后在已有的路由表内查找到对应的组件,拿到需要请求的数据,将数据作为 props、context或者store 形式传入组件

    然后基于 react 内置的服务端渲染方法 renderToString()把组件渲染为 html字符串在把最终的 html 进行输出前需要将数据注入到浏览器端

    浏览器开始进行渲染和节点对比,然后执行完成组件内事件绑定和一些交互,浏览器重用了服务端输出的 html 节点,整个流程结束


12.使用 React hooks 怎么实现类里面的所有生命周期?


  • 在 React 16.8 之前,函数组件也称为无状态组件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wyoi6A3F-1686967105051)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\1682042087354.png)]


13.React事件和原生事件的执行顺序


  • 为什么会有合成事件:
  • 在传统的事件里,不同的浏览器需要兼容不同的写法,在合成事件中React提供统一的事件对象,抹平了浏览器的兼容性差异
  • React通过顶层监听的形式,通过事件委托的方式来统一管理所有的事件,可以在事件上区分事件优先级,优化用户体验。React在合成事件上对于16版本和17`版本的合成事件有很大不同


  • 执行顺序总结:


  • 16版本先执行原生事件,当冒泡到document时,统一执行合成事件,
  • 17版本在原生事件执行前先执行合成事件捕获阶段,原生事件执行完毕执行冒泡阶段的合成事件,通过根节点来管理所有的事件,原生的阻止事件流会阻断合成事件的执行,合成事件阻止后也会影响到后续的原生执行


14.为什么react元素有一个$$type属性?


目的是为了防止 XSS 攻击。因为 Synbol 无法被序列化,所以 React 可以通过有没有

typeof属性来断出当前的element对象是从数据库来的还是自己生成的。如果没有

typeof 属性来断出当前的 element 对象是从数据库来的还是自己生成的。如果没有 typeof 这个属性,react 会拒绝处理该元素。


15.setState 是同步,还是异步的?


  • react18之前:setState在不同情况下可以表现为异步或同步。在Promise的状态更新、js原生事件、setTimeout、setInterval…中是同步的。在react的合成事件中,是异步的。
  • react18之后: setState都会表现为异步(即批处理)。
  • 解释react18之前:
  • 原因: 在React的setState函数实现中,会根据一个变量isBatchingUpdates判断是直接更新this.state还是放到队列中回头再说,而isBatchingUpdates默认是false,也就表示setState会同步更新this.state,但是,有一个函数batchedUpdates,这个函数会把isBatchingUpdates修改为true,而当React在调用事件处理函数之前就会调用这个batchedUpdates,造成的后果,就是由React控制的事件处理过程setState不会同步更新this.state。


16.如何让 useEffect 支持 async/await?


  • 为什么不支持?
  • effect function 应该返回一个销毁函数(return返回的 cleanup 函数),如果 useEffect 第一个参数传入 async,返回值则变成了 Promise,会导致 react 在调用销毁函数的时候报错
  • useEffect如何支持async/await?
  • 创建一个异步函数(async...await 的方式),然后执行该函数。


17. React 中可以做哪些性能优化?


  • 使用纯组件 – 纯组件是指那些不依赖于外部状态或引用的组件
  • shouldComponentUpdate 优化
  • 不要使用内联函数定义(如果我们使用内联函数,则每次调用“render”函数时都会创建一个新的函数实例)


function MyButton(props) {
  return (
    <button onClick={() => console.log('Button clicked!')}>
      {props.label}
    </button>
  );
}


  • 避免使用内联样式属性;
  • 使用 immutable 不可变数据,在我们项目中使用引用类型时,为了避免对原始数据的影响,一般建议使用 shallowCopy 和 deepCopy 对数据进行处理,但是这样会造成 CPU 和 内存的浪费,所以推荐使用 immutable


  • 优点:
  • 降低了可变带来的复杂度
  • 节省内存 ,immutable 使用结构共享尽量复用内存,没有被引用的对象会被垃圾回收
  • 不会有并发问题(因为数据本身不可变)
  • 拥抱函数编程
  • 给子组件设置一个唯一的 key,因为在 diff 算法中,会用 key 作为唯一标识优化渲染
  • 使用 React.memo 进行组件记忆(React.memo 是一个高阶组件),对 于相同的输入,不重复执行;
  • 在函数组件中使用useCallback和useMemo来进行组件优化,依赖没有变化的话,不重复执行
  • 路由懒加载


18.react 和 vue 有什么区别?


  • 区别:
  • React 的思路是 HTML in JavaScript 通过 JavaScript 来生成 HTML,所以设计了 JSX 语法,还有通过 JS 来操作 CSS
  • Vue 是把 HTML,CSS,JavaScript 组合到一起,用各自的处理方式,Vue 有单文件组件,可以把 HTML、CSS、JS 写到一个文件中,HTML 提供了模板引擎来处理。
  • React 整体是函数式的思想,在 React 中是单向数据流,推崇结合 immutable 来实现数据不可变。
  • Vue 的思想是响应式的,也就是基于是数据可变的,通过对每一个属性建立 Watcher 来监听,当属性变化的时候,响应式的更新对应的虚拟 DOM。
  • React 的性能优化需要手动去做,而Vue的性能优化是自动的,但是Vue的响应式机制也有问题,就是当 state 特别多的时候,Watcher 会很多,会导致卡顿。
  • 共同点:
  • React 与 Vue 存在很多共同点,例如他们都是 JavaScript 的 UI 框架,组件化开发,单项数据流,声明式编程,虚拟dom
  • 优势:
  • React

灵活性和响应性:它提供最大的灵活性和响应能力。

丰富的JavaScript库:来自世界各地的贡献者正在努力添加更多功能。

可扩展性:由于其灵活的结构和可扩展性,React已被证明对大型应用程序更好。

不断发展: React得到了Facebook专业开发人员的支持,他们不断寻找改进方法。

Web或移动平台: React提供React Native平台,可通过相同的React组件模型为iOS和Android开发本机呈现的应用程序。

  • Vue
  • 易于使用: Vue.js包含基于HTML的标准模板,可以更轻松地使用和修改现有应用程序。
  • 更顺畅的集成:无论是单页应用程序还是复杂的Web界面,Vue.js都可以更平滑地集成更小的部件,而不会对整个系统产生任何影响。
  • 更好的性能,更小的尺寸:它占用更少的空间,并且往往比其他框架提供更好的性能。
  • 精心编写的文档:通过详细的文档提供简单的学习曲线,无需额外的知识; HTML和JavaScript将完成工作。
  • 适应性:整体声音设计和架构使其成为一种流行的JavaScript框架。
  • 它提供无障碍的迁移,简单有效的结构和可重用的模板。
  • 总结:
  • Vue 的响应式机制也有问题,当 state 特别多的时候,Watcher 会很多,会导致卡顿,所以大型应用(状态特别多的)一般用 React,更加可控。
  • 对于易用性来说,VUE 是更容易上手的,对于项目来说新人更容易接手。


19.React render方法的原理,在什么时候会触发?


原理:

在类组件中render函数指的就是render方法;而在函数组件中,指的就是整个函数组件

render函数中的jsx语句会被编译成我们熟悉的js代码,在render过程中,react将新调用的render函数返回的树与旧版本的树进行比较,这一步是决定如何更新 DOM 的必要步骤,然后进行 diff 比较,更新dom树

触发机:

类组件调用 setState 修改状态

函数组件通过useState hook修改状态

一旦执行了setState就会执行render方法,useState 会判断当前值有无发生改变确定是否执行render方法,一旦父组件发生渲染,子组件也会渲染


20.React Hooks 在使用上有哪些限制?


  • 不要在循环、条件或嵌套函数中调用 Hook;
  • 在 React 的函数组件中调用 Hook。


21.React Hooks概述及常用的Hooks介绍?


Hooks是React 16.8版本引入的新特性,它为函数组件添加了一些类似于类组件中的状态和生命周期方法的功能


  • useState:用于在函数组件中添加状态管理功能。它返回一个由当前状态值和更新函数组成的数组,我们可以通过该数组来获取和更新状态的值。
  • useEffect:用于执行副作用操作,例如订阅事件、修改DOM等。它接受一个函数作为参数,该函数将在每次渲染完成后执行。
  • useContext:用于在组件间共享数据,它接受一个上下文对象作为参数,然后返回该上下文对象中提供的数据。
  • useReducer:用于对复杂状态进行管理。它接受一个reducer函数和初始状态作为参数,并返回一个由当前状态值和dispatch函数组成的数组。
  • useCallback:用于缓存函数以提高性能,类似于React.memo。
  • useMemo:用于缓存计算结果以提高性能,类似于记忆函数。
  • useRef:用于引用DOM节点或保存任意可变值,它返回一个可变的ref对象。


22.说说React生命周期中有哪些坑?如何避免?


  • getDerivedStateFromProps 容易编写反模式代码,使受控组件和非受控组件区分模糊
  • componentWillMount 在 React 中已被标记弃用,不推荐使用,主要的原因是因为新的异步架构会导致它被多次调用,所以网络请求以及事件绑定应该放到 componentDidMount 中
  • componentWillReceiveProps 同样也被标记弃用,被 getDerivedStateFromProps 所取代,主要原因是性能问题。
  • shouldComponentUpdate 通过返回 true 或者 false 来确定是否需要触发新的渲染。主要用于性能优化。
  • componentWillUpdate 同样是由于新的异步渲染机制,而被标记废弃,不推荐使用,原先的逻辑可结合 getSnapshotBeforeUpdate 与 componentDidUpdate 改造使用。
  • 如果在 componentWillUnmount 函数中忘记解除事件绑定,取消定时器等清理操作,容易引发 bug。
  • 如果没有添加错误边界处理,当渲染发生异常时,用户将会看到一个无法操作的白屏,所以一定要添加。


23.说说Real diff算法是怎么运作的?


  • Diff算法是虚拟DOM的一个必然结果,它是通过新旧DOM的对比,将在不更新页面的情况下,将需要内容局部更新
  • Diff算法遵循深度优先,同层比较的原则
  • react中diff算法主要遵循三个层级的策略:

tree层级:DOM节点的跨层级操作不做优化,只对相同层节点进行比较,只有删除创建操作,没有移动操作

conponent 层级:如果是同一类的组件,则会继续往下进行diff运算,如果不是则直接删除组件下的所有子节点,创建新的

element 层级:对于比较同一层级的节点们,每个节点在对应的层级用唯一的key作为标识,通过key知道节点的变化,移动旧集合节点位置,更新为新集合节点位置


24.调和阶段setState干了什么?


  • 代码中调用 setState 函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation)。
  • 经过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个 UI 界面;
  • 在 React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染;
  • 在差异计算算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。


25.使用 redux 有哪些原则?


  • 单一数据源:整个应用的全局 state 被存储在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。
  • State 是只读的:唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事情的普通对象。
  • 使用纯函数来执行修改:为了描述 action 如何改变 state tree,你需要编写纯的 reducers。


26.说说redux的实现原理是什么,写出核心代码?


  • 将应用的状态统一放到state中,由store来管理state。
  • reducer的作用是 返回一个新的state去更新store中对用的state。
  • 按redux的原则,UI层每一次状态的改变都应通过action去触发,action传入对应的reducer 中,reducer返回一个新的state更新store中存放的state,这样就完成了一次状态的更新
  • subscribe是为store订阅监听函数,这些订阅后的监听函数是在每一次dipatch发起后依次执行
  • 可以添加中间件对提交的dispatch进行重写
  • 核心API
  • createStore 创建仓库,接受reducer作为参数
  • bindActionCreator 绑定store.dispatch和action 的关系
  • combineReducers 合并多个reducers
  • applyMiddleware 洋葱模型的中间件,介于dispatch和action之间,重写dispatch
  • compose 整合多个中间件


27.对Redux中间件的理解?原理?常用中间件有哪些?


Redux中,中间件就是放在就是在dispatch过程,在分发action进行拦截处理, 其本质上一个函数,对store.dispatch方法进行了改造,在发出 Action 和执行 Reducer 这两步之间,添加了其他功能


实现原理:


  • applyMiddlewares的源码 中我们可以看到 ,所有中间件被放进了一个数组chain,然后嵌套执行,最后执行store.dispatch。可以看到,中间件内部(middlewareAPI)可以拿到getState和dispatch这两个方法


常用中间件:


  • redux-thunk:用于异步操作
  • redux-logger:用于日志记录


28.Redux 和 Vuex 有什么区别,它们有什么共同思想吗?


  • 相同点:

state 共享数据

流程一致:定义全局state,触发,修改state

原理相似,通过全局注入store。

  • 不同点:
  • 实现原理:
  • Redux 使用的是不可变数据,而Vuex的数据是可变的。Redux每次都是用新的state替换旧的state,而Vuex是直接修改
  • Redux 在检测数据变化的时候,是通过 diff 的方式比较差异的,而Vuex其实和Vue的原理一样,是通过 getter/setter来比较的
  • 表现层:
  • vuex定义了state、getter、mutation、action四个对象;redux定义了state、reducer、action。
  • vuex中state统一存放,方便理解;reduxstate依赖所有reducer的初始值
  • vuex有getter,目的是快捷得到state;redux没有这层,react-redux mapStateToProps参数做了这个工作。
  • vuex中mutation只是单纯赋值(很浅的一层);redux中reducer只是单纯设置新state(很浅的一层)。他俩作用类似,但书写方式不同
  • vuex中action有较为复杂的异步ajax请求;redux中action中可简单可复杂,简单就直接发送数据对象({type:xxx, your-data}),复杂需要调用异步ajax(依赖redux-thunk插件)。
  • vuex触发方式有两种commit同步和dispatch异步;redux同步和异步都使用dispatch
  • 共同思想:
  • 单一的数据源
  • 变化可以预测


29.props和state相同点和不同点?


不同点:

props是只读的,只能由父组件传递给子组件,而不能在子组件中修改。而state是可变的,在组件内部可以通过setState方法来修改其值

相同点:

1.props和state都会触发渲染更新


2.props和state都是纯JS对象(用typeof来判断,结果都是object)


30.shouldComponentUpdate有什么作用?


shouldComponentUpdate () 可以理解为是否触发渲染的阀门,当状态发生改变时会走到该生命周期,shouldComponentUpdate接收两个参数props,state分别是更新前和更新后的状态,可以判断前后是否发生改变返回true和false,来决定是否往下执行


31.React的props.children使用map函数来遍历会收到异常显示,为什么?应该 如何遍历


原因:在react.js中props.children不一定是数组


  • 当前组件没有子节点 undefined
  • 有一个子节点 object
  • 多个子节点 array

react资深提供了一个react.children.map()方法,可以安全遍历子节点对象。


32.谈谈你对immutable.js的理解?


Immutable.js采用了 持久化数据结构 ,保证每一个对象都是不可变的,任何添加、修改、删除等操作都会生成一个新的对象,且通过 结构共享 等方式大幅提高性能


33.redux原理、工作流程及其应用,三大原则


redux: redux是专门用于集中式管理状态的javascript库,他并不是react的插件库。


redux三大核心:


  • actions

actions英文直译过来就是行动、动作的意思,那么我们就可以猜到他表示的是“怎么做”,简单来说actions就是一个对象,actions里面有两个属性分别为type和data:

type:标识属性,值为字符串且唯一,必要属性(你想要做什么事情

data:数据属性,值为任意类型,可选属性(你要做事情的数据


那我们浅浅举个栗子:比如计算器你可以进行加1减2等操作,那么加减乘除这个操作就是你的type,数字就是你的数据


  • store

store有且只能有一个,他相当于一个最高指挥家,他负责把action动作交给对应的reducer进行执行,也就是说将state、action和reducer联系在一起的对象。


  • reducer

reducer用于将store发过来的action完成并将结果返回给store,他接收两个参数preState(旧状态)和action(动作)并返回一个newState(新状态)。


工作流程:当组件使用store中的数据需要发生变化时,告诉action生成动作对象,通过dispatch分发对象到store,store对需要使用的reducer进行绑定,然后将action分发到对应reducer上执行相应逻辑进行数据覆盖,再将store数据渲染


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-srPGmnk2-1686967105052)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\1685666639607.png)]


三大原则


唯一数据源(state)


数据源(state)只读


通过纯函数(pure function)改变数据源(state)


34.react-redux原理,工作流程


react-redux实现过程


react-redux执行流程详解:


初始化阶段:


  • 创建 Redux store 对象,并将 Reducer 传入 createStore 函数中。
  • 创建一个 Provider 组件,并将 Redux store 对象作为 Provider 组件的 props 传入其中。
  • 将应用根组件包装在 Provider 组件中,并渲染整个应用。

运行阶段:


  • 使用 connect 函数将组件与 Redux 中的 state 和 action creators 相连接,并将它们转化为组件的 props 属性
import { connect } from 'react-redux';
import { addToCart } from '../actions';
const Product = ({ product, addToCart }) => (
 <div>
   <h3>{product.name}</h3>
   <button onClick={() => addToCart(product)}>Add to cart</button>
 </div>
);
const mapDispatchToProps = {
 addToCart,
};
export default connect(null, mapDispatchToProps)(Product);


更新阶段:


  • 在 Store 的 dispatch 方法中,执行 action 并更新 Store 中的 state。
  • React-Redux 根据 Store 中的新状态,检查哪些组件的 props 发生了变化。
  • 对于发生变化的组件,React-Redux 将触发相关的生命周期方法和 render 方法进行重新渲染。

工作流程:


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OaD98GCF-1686967105053)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\1685667792598.png)]


35.react组件通讯的context


使用步骤:


  • 在顶层组件中创建一个 context 对象,并将需要共享的数据挂载到该对象上


const MyContext = React.createContext(defaultValue);


  • 在顶层组件的 render 方法中,使用 MyContext.Provider 组件来包裹整个应用程序,并将共享的数据传递给 value 属性


<MyContext.Provider value={sharedData}>
    <App />
</MyContext.Provider>


  • 在需要使用共享数据的组件中,使用 MyContext.Consumer 组件来接收 context 的 value 属性,并在 Consumer 的子元素中使用这些数据


<MyContext.Consumer>
    {sharedData => (
        // 此处可以使用 sharedData 来操作共享数据
    )}
</MyContext.Consumer>


36.react-redux中 Provider 组件实现原理


React-Redux 中的 Provider 组件是一个 React 组件,它使用了 React 的 Context API 来实现数据的传递。Provider 组件提供一个 context 对象,它可以让嵌套在它内部的子组件都可以访问到这个 context 对象,并且可以通过它来获取到 Redux store 中的数据。


37.react-redux中Connect原理


connect 函数是将 React 组件与 Redux Store 进行连接的重要方法。它接收两个函数作为参数,并返回一个高阶组件,通过这个高阶组件可以将 Redux Store 和 React 组件关联起来。 (实现容器组件包裹ui组件)


在原应用组件上包裹一层,使原来整个应用成为Provider的子组件,接收Redux的store作为props,通过context对象传递给子孙组件上的connect,它真正连接 Redux 和 React,它包在我们的容器组件的外一层,它接收上面 Provider 提供的 store 里面的 state 和 dispatch,传给一个构造函数,返回一个对象,以属性形式传给我们的容器组件。


//其中 mapStateToProps 和 mapDispatchToProps 是将 Store 和 action creator 映射到组件的 props 上的函数。
connect(mapStateToProps, mapDispatchToProps)(Product);


38.react-toolkit


react-toolkit是一个官方维护的包含多个有用工具的 Redux 库,旨在使 React 和 Redux 的开发变得更加简单、高效和直观


  • 简化 Redux 工作流

redux-toolkit 提供了一种新的方式来编写 Redux 应用程序,该方式包含了常见的 Redux 模式,并通过封装样板代码来简化它们。这使得开发者可以更加专注于实现业务逻辑,而不必关心较低级别的细节。


  • 内置常用中间件

redux-toolkit 包含了 Redux 应用程序中常用的几个中间件,如 redux-thunk 中间件、redux-saga 中间件和 redux-logger 中间件,使开发者可以轻松地使用和配置这些中间件。


  • 强制执行不可变性

redux-toolkit 的 createSlice 函数在创建 reducer 时会自动使用不可变性(immutability)来更新 state,这避免了因直接修改 state 而产生的潜在错误。


  • 自动生成 Redux action 类型

redux-toolkit 提供了 createSlice 函数来创建 reducer,该函数还会自动为每个 action type 创建一个字符串常量,避免手动编写这些常量带来的冗余代码。


  • 管理副作用

redux-toolkit 提供了 createAsyncThunk 函数来创建具有异步副作用的 action,该函数自动处理异步流程,并可以在状态中跟踪每个异步操作的进度和结果。


39.React.memo() 和 useMemo() 的用法是什么,有哪些区别?


React.memo 是一个高阶组件,它可以将一个纯函数组件,当组件的 props 没有变化时,会直接复用组件的渲染结果,从而避免不必要的渲染。


//当 MyComponent 的 text 属性没有变化时,MemoizedComponent 就会复用之前的渲染结果,而不会重新渲染。
function MyComponent(props) {
  return <div>{props.text}</div>;
}
const MemoizedComponent = React.memo(MyComponent);


useMemo 是一个 Hook,它可以用于缓存计算结果,以避免重复计算。当传入的依赖项没有变化时,会直接返回缓存的结果。


//如果 calculate 方法比较耗时,为了避免不必要的计算,我们可以使用 useMemo 来缓存计算结果:
function MyComponent({ a, b }) {
  const result = useMemo(() => calculate(a, b), [a, b]);
  return <div>{result}</div>;
}


区别:


React.memo 和 useMemo 都可以用于优化 React 应用的性能,但是它们的优化对象和优化手段不同。React.memo 通过避免组件的不必要渲染来提高性能,而 useMemo 通过避免重复计算来提高性能。在实际开发中,需要根据具体场景和需求来选择适合的优化方法。


40.usecallback,usememo区别


useCallback 用于缓存函数,以避免不必要的函数创建和渲染。当依赖项发生变化时,会返回一个新的函数引用,否则直接返回之前缓存的函数引用。


const memoizedCallback = useCallback(() => {
  doSomething(a, b);
}, [a, b]);


useMemo 用于缓存计算结果,以避免重复计算和渲染。当依赖项发生变化时,会重新计算并返回新的计算结果


const memoizedValue = useMemo(() => {
  return heavyComputation(a, b);
}, [a, b]);


useCallback 通过避免函数创建和渲染来提高性能,而 useMemo 通过避免重复计算和渲染来提高性能


41.react使用ref


ref 是用来访问 DOM 元素或组件实例的引用的一种方式。


字符串回调


class MyComponent extends React.Component {
  componentDidMount() {
    console.log(this.refs.myInput);
    // 输出:<input type="text" />
  }
  render() {
    return <input type="text" ref="myInput" />;
  }
}


函数回调


class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = null;
  }
  componentDidMount() {
    console.log(this.myRef);
    // 输出:<input type="text" />
  }
  render() {
    return <input type="text" ref={node => this.myRef = node} />;
  }
}


React.createRef


class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  componentDidMount() {
    console.log(this.myRef.current);
    // 输出:<input type="text" />
  }
  render() {
    return <input type="text" ref={this.myRef} />;
  }
}


42.为什么循环和判断不能使用hooks


原因:


循环、条件语句等块级作用域会影响 Hooks 调用的次数和顺序,从而破坏 React 内部的依赖关系和渲染逻辑,导致组件出现无法预期的错误。因此,为了保证组件能够正常渲染和更新,我们需要遵循 React Hooks 的使用规范,在顶层作用域中调用 Hooks。


43.React实现过度动画


使用第三方库: React Transition Group 详细学习过度动画


注意: CSSTransition 中类命名方式classNames


44. react懒加载实现原理


React 的懒加载实现原理主要是基于 ES6 的 import() 函数和 React 的 lazy 函数。


import React, { lazy, Suspense } from "react";
const LazyComponent = lazy(() => import("./LazyComponent"));
function App() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <LazyComponent />
      </Suspense>
    </div>
  );
}


当我们使用 lazy 函数时,React 会在运行时动态创建一个新的组件,这个新的组件继承了原始组件的所有属性和方法,并且它的 render 方法被重写成一个异步函数。当这个新的组件需要被渲染时,React 就会自动触发它的 render 方法,这个方法会异步加载原始组件的代码,并将其渲染到页面上。


这里涉及到了 ES6 中的 import() 函数,它是一个异步函数,用来动态加载 JavaScript 模块。import() 函数会返回一个 Promise 对象,当模块加载完成后,Promise 对象就会被 resolve,我们可以通过 then 方法获取模块的默认导出对象。


45.immutable


Immutable 是一个 JavaScript 库,它提供了一些数据结构和 API,使得创建不可变数据成为可能。React 中使用 Immutable 可以带来以下好处:


  • 性能优化: React 中使用的 Virtual DOM 技术会频繁地进行比较和更新操作,而不可变数据可以减少需要更新的节点数量,从而提高应用的性能和响应速度。
  • 便于管理状态: React 应用中的状态通常非常复杂,使用不可变数据可以更加方便地管理状态,并且避免了因为状态被修改而引发的副作用。
  • 并发安全: 在高并发环境下,使用不可变数据可以减少数据竞争和锁竞争,提高并发安全性能。

immutable创建不可变数据类型,想要修改只能通过数据覆盖创建新的变量,对react进行性能优化


46.useState为什么返回数组而不是对象


原因:React 团队认为,使用数组可以带来更好的灵活性和易用性


优点:


  • 无需命名: 当使用数组时,我们可以使用数组解构语法来命名状态变量和修改函数,并且不需要考虑命名冲突等问题。
  • 直接修改: 使用数组解构后,我们可以直接对数组元素进行修改操作,而无需再次调用对象中的某个方法或属性。
  • 易于扩展: 在未来的版本中,React 还可能会增加更多的状态相关的 Hook,使用数组可以使得新增的 Hook 更加容易添加到现有的语法中


47.类组件为什么不能使用hooks


Hooks 是基于函数式编程思想设计的,不支持类组件的一些特性。但是,React 提供了一些方法,可以让开发者在类组件中使用 Hooks 的部分功能,或者将类组件转换为函数组件来使用 Hooks。


48.redux-thunk和redux-saga区别


redux-thunk 的设计思路是将异步逻辑封装到“thunk”函数中,这个函数接收 dispatch 方法作为参数,并返回一个带有回调函数的函数。这个回调函数在异步操作完成后被调用,然后再通过 dispatch 方法触发一个 action,更新 Redux store 中的数据。redux-thunk 适合用于处理简单的异步逻辑,比如发送 AJAX 请求或者获取本地存储数据等。 没有拓展api


redux-saga 则采用了另外一种设计思路,它使用了 ES6 的 generator 函数来实现异步逻辑的管理。在 redux-saga 中,使用 generator 函数定义一个 saga,它可以监听一个或多个 action,并在相应的 action 被触发后执行一些副作用,比如发送 AJAX 请求、触发其他 action 等。redux-saga 还提供了一些辅助函数和特性(api),比如 takeLatest、put、call 等,使得开发者可以更加方便地管理异步流程,处理错误和取消请求等。


49.react合成事件使用原因,原理


探索合成事件


React 合成事件(SyntheticEvent)是 React 模拟原生 DOM 事件所有能力的一个事件对象


使用原因:


  • 进行浏览器兼容,实现更好的跨平台


React 采用的是顶层事件代理机制,能够保证冒泡一致性,可以跨浏览器执行。React 提供的合成事件用来抹平不同浏览器事件对象之间的差异,将不同平台事件模拟合成事件。


  • 避免垃圾回收


事件对象可能会被频繁创建和回收,因此 React 引入事件池,在事件池中获取或释放事件对象。即 React 事件对象不会被释放掉,而是存放进一个数组中,当事件触发,就从这个数组中弹出,避免频繁地去创建和销毁(垃圾回收)。


  • 方便事件统一管理和事务机制


原理:


  • 事件绑定

在React17之前,React是把事件委托在document上的,React17及以后版本不再把事件委托在document上,而是委托在挂载的容器上

原生事件和合成事件两者其实是通过一个叫事件插件(EventPlugin)的模块产生关联的,每个插件只处理对应的合成事件,比如onClick事件对应SimpleEventPlugin插件,这样React在一开始会把这些插件加载进来,通过插件初始化一些全局对象,比如其中有一个对象是registrationNameDependencies,它定义了合成事件与原生事件的对应关系

  • 事件触发

事件触发都会执行dispatchEvent函数,当触发事件时会对当前元素的所有父元素处理构造成合成对象,将合成事件一次存放入eventQueue中,遍历 eventQueue 模拟一遍捕获和冒泡阶段,然后通过runEventsInBatch方法依次触发调用每一项的监听事件


50.React事件代理机制


React 并不会把所有的处理函数直接绑定在真实的节点上。而是把所有的事件绑定到结构的最外层,使用一个统一的事件监听器,这个事件监听器上维持了一个映射来保存所有组件内部的事件监听和处理函数。


  • 所有事件都是委托在id = root的DOM元素中(网上很多说是在document中,17版本不是了);
  • 在应用中所有节点的事件监听其实都是在id = root的DOM元素中触发;
  • React自身实现了一套事件冒泡捕获机制;
  • React实现了合成事件SyntheticEvent;
  • React在17版本不再使用事件池了(网上很多说使用了对象池来管理合成事件对象的创建销毁,那是16版本及之前);
  • 事件一旦在id = root的DOM元素中委托,其实是一直在触发的,只是没有绑定对应的回调函数;


51.React事件和原生事件的执行顺序


import React, { useRef, useEffect } from "react";
import "./styles.css";
const logFunc = (target, isSynthesizer, isCapture = false) => {
    const info = `${isSynthesizer ? "合成" : "原生"}事件,${
        isCapture ? "捕获" : "冒泡"}阶段,${target}元素执行了`;
    console.log(info);
};
const batchManageEvent = (targets, funcs, isRemove = false) => {
    targets.forEach((target, targetIndex) => {
        funcs[targetIndex].forEach((func, funcIndex) => {
            target[isRemove ? "removeEventListener" : "addEventListener"](
                "click",
                func,
                !funcIndex
            );
        });
    });
};
export default function App() {
    const divDom = useRef();
    const h1Dom = useRef();
    useEffect(() => {
        const docClickCapFunc = () => logFunc("document", false, true);
        const divClickCapFunc = () => logFunc("div", false, true);
        const h1ClickCapFunc = () => logFunc("h1", false, true);
        const docClickFunc = () => logFunc("document", false);
        const divClickFunc = () => logFunc("div", false);
        const h1ClickFunc = () => logFunc("h1", false);
        batchManageEvent(
            [document, divDom.current, h1Dom.current],
            [
                [docClickCapFunc, docClickFunc],
                [divClickCapFunc, divClickFunc],
                [h1ClickCapFunc, h1ClickFunc]
            ]
        );
        return () => {
            batchManageEvent(
                   [document, divDom.current, h1Dom.current],
                [
                    [docClickCapFunc, docClickFunc],
                    [divClickCapFunc, divClickFunc],
                    [h1ClickCapFunc, h1ClickFunc]
                ],
                true
            );
        };
    }, []);
    return (
        <div
          ref={divDom}
          className="App1"
          onClickCapture={() => logFunc("div", true, true)}
          onClick={() => logFunc("div", true)}
        >
          <h1
            ref={h1Dom}
            onClickCapture={() => logFunc("h1", true, true)}
            onClick={() => logFunc("h1", true)}
          >
            Hello CodeSandbox
          </h1>
        </div>
    );
}


React16


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0te4nKyx-1686967105054)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\1685930695759.png)]


React17


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-39vZwaap-1686967105055)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\1685930673755.png)]


总结


  • 16版本先执行原生事件,当冒泡到document时,统一执行合成事件,
  • 17版本在原生事件执行前先执行合成事件捕获阶段,原生事件执行完毕执行冒泡阶段的合成事件,通过根节点来管理所有的事件


52.jsx转换真实dom流程


  • 使用React.createElement()或JSX编写React组件,实际上所有的JSX都会转换成React.createElement(),Babel完成转换过程
  • createElement函数对key和ref等特殊的props进行处理,生成vDom
  • ReactDOM.render将生成好的dom渲染到容器上,进行处理转化成真实dom


53.什么是JSX?


JSX即JavaScript XML。一种在React组件内部构建标签的类XML语法。JSX为react.js开发的一套语法糖,也是react.js的使用基础。React在不使用JSX的情况下一样可以工作,然而使用JSX可以提高组件的可读性,因此推荐使用JSX。


优点:


  • 允许使用熟悉的语法来定义 HTML 元素树;
  • 提供更加语义化且移动的标签;
  • 程序结构更容易被直观化;
  • 抽象了 React Element 的创建过程;
  • 可以随时掌控 HTML 标签以及生成这些标签的代码;
  • 是原生的 JavaScript。


八、微信小程序篇


1. 请谈谈WXML与标准的html的异同?


(1) WXML是小程序框架设计的一套标签语言,用来构建小程序页面的结构,其作用类似于网页开发中的html


① 都是用来描述页面的结构


② 都由标签,属性等构成


③ 标签名字不一样,且小程序标签更少,单一标签更多


④ 小程序多了一些wx:if这样的属性以及{{}}这样的表达式


⑤ WXML仅能在微信小程序开发者工具中预览,而HTML可以在浏览器内预览


⑥ 组件封装不同,WXML对组件进行了重新封装


⑦ 小程序运行在JS Core内,没有DOM树和window对象,小程序中无法使用window对象和document对象


2. 请谈谈WXSS和CSS的异同?


(1) 新增了rpx尺寸单位,css中需要手动进行像素单位换算,例如rem


(2) WXSS支持新的尺寸rpx,在不同大小的屏幕上小程序会自动进行换算


(3) 提供了全局样式和局部样式,项目根目录中的app.wxss会作用于所有小程序页面,局部页面的.wxss样式仅对当前页面生效


(4) WXSS仅支持部分css选择器:


① 类选择器,id选择器


② 元素选择器


③ 并集选择器,后代选择器


④ ::after和::before等伪类选择器


3.请谈谈微信小程序主要目录和文件的作用


(1) project.config.json: 项目配置文件,用得最多的就是配置是否开启https校验;


(2) App.js :设置一些全局的基础数据等,页面的脚本文件,存放页面的数据、事件处理函数等;


(3) App.json :当前页面的配置文件,配置窗口的外观 、表现等,页面中的配置项会覆盖 app.json的;


(4) App.wxss :公共样式,引入iconfont等;


(5) pages: 里面包含一个个具体的页面;


(6) index.json: (配置当前页面标题和引入组件等);


(7) index.wxml:页面的模板结构文件;


(8) .wxss文件:当前页面的样式表文件;


(9) index.js :(页面的逻辑,请求和数据处理等)


4.请谈谈小程序的双向绑定和vue的异同


(1) 小程序双向绑定:首先通过 bindinput 绑定文本框的输入事件


① 在 data 中声明一个变量 content ,将其动态绑定成文本框的 value 值


② 在 bindinput 事件中通过事件参数 e.detail.value 可以获取到文本框中最新的 value 值


③ 通过 this.setData 将文本框最新的 value 值 赋值给 动态绑定的value值 content 即可实现数据的双向绑定


(2) Vue双向绑定:首先为文本框绑定 @input 监听文本框的输入事件


① 为文本框动态绑定 value 属性,其值是在data中定义的变量


② 在 @input绑定的事件中 通过事件参数 event.target.value 可以获取到 input 框中最新的value值


③ 将其重新获取到的 value 赋值给 value值动态绑定的那个变量


(3) 大体上区别不大,绑定事件不同,以及获取value值的具体方式不同,以及在小程序中设置data中的数据,

需要调用 this.setData方法进行设置


5.简单描述下微信小程序的相关文件类型


(1) WXML(weixin Markup Language) 是框架设计的一套标签语言,结合基础组件,事件系统,可以构建出页面的结构。内容主要事微信自己定义的一套组件。


(2) WXSS(WeiXin Style Sheets) 是一套样式语言,主要用于描述 WXML 的组件样式。


(3) JS 逻辑处理,网络请求


(4) json 小程序设置,静态配置


(5) app.json 必须要有这个文件,此文件用来对微信小程序进行全局配置,决定页面文件的路径、窗口表现、设置网络超时时间、设置tabBar 最少两个


6.微信小程序有哪些传值(传递数据)方法


(1) 使用全局变量传递数据


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pu3nDBnO-1686967105056)(file:///C:\Users\lenovo\AppData\Local\Temp\ksohtml14876\wps1.jpg)]


(2) 使用本地存储数据传递


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H6cLbqBk-1686967105057)(file:///C:\Users\lenovo\AppData\Local\Temp\ksohtml14876\wps2.jpg)]


(3) 使用路由传递数据


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p4zSUW0R-1686967105058)(file:///C:\Users\lenovo\AppData\Local\Temp\ksohtml14876\wps3.jpg)]


7.Bindtap和catchtap区别?


(1) Bindtap和catchtap都属于点击事件函数,将事件绑定到组件上,点击组件后可以触发函数


bindtap :子元素使用bindtap绑定事件后,执行的时候,会冒泡到父元素(触发父元素上绑定的bingtap事件)


catchtap :不会冒泡到父元素上,阻止事件冒泡


8.wx.navigateTo(),wx.redirectTo(),wx.switchTab(),wx.navigateBack(),wx.reLaunch()的区别?


(1) wx.navigateTo(Object):保留当前页面,跳转到应用内的某个页面。但是不能跳到 tabbar 页面。使用 wx.navigateBack 可以返回到原页面。小程序中页面栈最多十层


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


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


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


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


9.微信小程序和h5区别?


(1) 从开发的角度:H5和小程序的开发工具就非常不同,小程序都是依赖于微信客户端的,所以相对来说开发工具没有H5那么多;与标准的H5语言是不一样的;而且还独立了很多的原生app的组件,所以它在组件封装上与H5也都是有所不同


(2) 从运行环境:网页开发者需要面对的环境是各式各样的浏览器,PC 端需要面对 IE、Chrome、QQ浏览器等,在移动端需要面对Safari、Chrome以及 iOS、Android 系统中的各式 WebView 。而小程序开发过程中需要面对的是两大操作系统 iOS 和 Android 的微信客户端,以及用于辅助开发的小程序开发者工具,小程序中三大运行环境也是有所区别的。


(3) 服务器配置:因为小程序的性能会比H5 高一些,所以服务器的配置要求上来说,小程序的要求要比H5更高一些系统权限:因为微信能获取到更多的系统权限,如网络通信状态、数据缓存能力等,这些系统级权限都能与微信小程序无缝衔接,这也就是官方宣称的微信小程序能够拥有Native App(原生APP)的流畅性能。而H5 web应用对系统本身的权限的获取则相对少了很多,这一点恰巧是H5 web应用经常被诟病的地方。也因此,H5的大多数应用被圈定在业务逻辑简单、功能单一的范围上。


(4) 标签名字:写 HTML 的时候,经常会用到的标签是 div, p, span,小程序的 WXML 用的标签是 view, button, text 等等,这些标签就是小程序给开发者包装好的基本能力。小程序多了一些 wx:if 这样的属性以及 {{ }} 这样的表达式在网页的一般开发流程中,我们通常会通过 JS 操作 DOM (对应 HTML 的描述产生的树),以引起界面的一些变化响应用户的行为。


10.小程序和vue写法区别?


(1) 事件定义区别:vue通过@绑定事件,小程序通过bind


(2) 事件函数传值:vue传值直接写在函数括号中,微信小程序传值需要用data-自定义名字={{需要传递的值}}


(3) 关键字引用:vue中属性名前面加 “:” 或者 v-bind,微信小程序中只需要属性值加“{{}}”包起来就行


(4) 指令使用方式:vue中通过v-使用,小程序中通过wx:使用


11.Rpx的理解:


(1) Rpx是微信小程序中响应单位


(2) rpx是微信小程序独有的、解决屏幕自适应的尺寸单位


(3) 可以根据屏幕宽度进行自适应,不论大小屏幕,规定屏幕宽为750rpx,再页面宽度750px时,1rpx = 1px


12.微信小程序可以做dom操作吗?


(1) 微信小程序不支持document.querySelect获取元素,因为微信小程序的渲染层和逻辑层是独立的,但是它内置了获取元素的两种方法


① wx.createSelectorQuery()获取dom元素


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oEW3uzli-1686967105059)(file:///C:\Users\lenovo\AppData\Local\Temp\ksohtml14876\wps4.jpg)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XH7ohB3C-1686967105059)(file:///C:\Users\lenovo\AppData\Local\Temp\ksohtml14876\wps5.jpg)]


13.小程序和vue区别?


(1) 生命周期不一样,微信小程序生命周期比较简单


(2) 数据绑定也不同,微信小程序数据绑定需要使用{{}},vue 直接:就可以


(3) 显示与隐藏元素,vue中,使用 v-if 和 v-show 控制元素的显示和隐藏,小程序中,使用wx-if 和hidden 控制元素的显示和隐藏


(4) 事件处理不同,小程序中,全用 bindtap(bind+event),或者 catchtap(catch+event) 绑定事件,vue:使用 v-on:event 绑定事件,或者使用@event 绑定事件


(5) 数据双向绑定也不也不一样在 vue中,只需要再表单元素上加上 v-model,然后再绑定 data中对应的一个值,当表单元素内容发生变化时,data中对应的值也会相应改变,这是 vue非常 nice 的一点。微信小程序必须获取到表单元素,改变的值,然后再把值赋给一个 data中声明的变量。


14.小程序自定义tabbar理解?


(1) 首先在挨批评。App.json中配置tabbar,开启custom 自定义tabbar,配置完之后创建custom-tab-bar文件,这时候会在小程序底部生成文件内容,给index.wxml添加tabBar的结构代码, 给index.js 添加数据配置 和 事件方法


15.微信小程序如何设置缓存?


(1) wx.getStorage/wx.getStorageSync 读取本地缓存,通过wx.setStorage/wx.setStorageSync 写数据到缓存,其中Sync后缀的接口表示是同步接口


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BOmNe545-1686967105060)(file:///C:\Users\lenovo\AppData\Local\Temp\ksohtml14876\wps6.jpg)]


16. 微信小程序如何进行网络请求?


(1) wx.request(Object object) 用于发送网络请求


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r5VZUL7r-1686967105062)(file:///C:\Users\lenovo\AppData\Local\Temp\ksohtml14876\wps7.jpg)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-so2LhJfi-1686967105063)(file:///C:\Users\lenovo\AppData\Local\Temp\ksohtml14876\wps8.jpg)]


17. 小程序生命周期分为三个部分


(1) 应用级:


① onLaunch:小程序初始化完成时触发,全局只触发一次


② onShow:小程序启动或切前台显示时触发


③ onHide:小程序从前台进入后台时触发(如 切换到其他的App


④ onError:小程序发生脚本错误或 API 调用报错时触发


(2) 页面级:


① onLoad:页面加载时执行,只执行一次


② onShow:页面展示时执行,执行多次


③ onReady:页面初次渲染时执行,只执行一次


④ onHide:页面从前台进入后台时执行


⑤ onUnload:页面卸载时执行


(3) 组件级:


① created(重要):组件实例刚刚被创建好时触发


② attached(重要):在组件完全初始化完毕、进入页面节点树后被触发


③ ready:在组件在视图层布局完成后执行


④ moved:在组件实例被移动到节点树另一个位置时执行


⑤ detached(重要):在组件离开页面节点树后被触发


⑥ Error:每当组件方法抛出错误时执行


18. 微信小程序如何做模块化?


(1) 可以将一些公共的代码抽离成为一个单独的 js 文件,作为一个模块。模块只有通过 module.exports 或者 exports 才能对外暴露接口


(2) 需要使用这些模块的文件中,使用 require 将公共代码引入


19. 微信所有api都放在哪里,简单介绍几个


(1) 小程序全局对象是: wx,所有的 API 都保存在 wx 对象中


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IzoRS18A-1686967105064)(file:///C:\Users\lenovo\AppData\Local\Temp\ksohtml14876\wps9.jpg)]


20. 微信小程序应用和页面生命周期触发顺序?


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZlYZbvim-1686967105064)(file:///C:\Users\lenovo\AppData\Local\Temp\ksohtml14876\wps10.jpg)]


21. 微信小程序自定义组件使用


(1) 创建components文件夹


(2) 创建组件文件(例tabs),新建为component形式


(3) 在tabs的json文件中设置component:true


(4) 在需要引入的组件页面的json文件中,在usingComponent里面写键值对,组件名:路径


(5) 在需要页面写入标签


注意:WXML 节点标签名只能是小写字母、中划线和下划线的组合


22. 微信小程序事件通道使用


微信小程序事件通道(事件总线)是用于在小程序多个页面或组件之间通过触发事件进行通信的机制。通过wx.navigateTo或wx.redirectTo方法跳转到目标页面或组件时,传入events参数,并将它赋值为事件通道对象


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z9pQwqTy-1686967105065)(file:///C:\Users\lenovo\AppData\Local\Temp\ksohtml14876\wps11.jpg)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6a1HlQJq-1686967105066)(file:///C:\Users\lenovo\AppData\Local\Temp\ksohtml14876\wps12.jpg)]


23. 小程序如何使用vant组件库


如果是以js为web页面脚本语言创建的小程序,本身不存在package.json文件,就不可以使用npm命令


先通过npm init初始化npm,产生package.json文件


通过vantweapp官网 npm下载组件库


在微信小程序中通过工具构建npm


移除app.json中“style”:“v2”,避免小程序样式高于vant导致样式混乱


在app.json中usingComponents注册组件


24. 微信小程序父子传递


父传子:通过父组件标签绑定属性,子组件在properties中接收属性


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-olTg2Lfx-1686967105067)(file:///C:\Users\lenovo\AppData\Local\Temp\ksohtml14876\wps13.jpg)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NMW1E5Xq-1686967105068)(file:///C:\Users\lenovo\AppData\Local\Temp\ksohtml14876\wps14.jpg)]


子传父:通过父组件bind自定义事件,绑定自身函数,子组件在触发函数中通过this.triggerEvent(‘父组件bind事件名’,传递参数)传递给父组件绑定函数中


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oNSSzdfi-1686967105068)(file:///C:\Users\lenovo\AppData\Local\Temp\ksohtml14876\wps15.jpg)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JOYkl8El-1686967105069)(file:///C:\Users\lenovo\AppData\Local\Temp\ksohtml14876\wps16.jpg)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s8Gidbyv-1686967105070)(file:///C:\Users\lenovo\AppData\Local\Temp\ksohtml14876\wps17.jpg)]


25. 小程序授权登录


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J7YYIVIy-1686967105071)(file:///C:\Users\lenovo\AppData\Local\Temp\ksohtml14876\wps18.jpg)]


26. web-view


web-view 是一个 web 浏览器组件,可以用来承载网页的容器,会自动铺满整个页面.


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vj1a9jN1-1686967105072)(file:///C:\Users\lenovo\AppData\Local\Temp\ksohtml14876\wps19.jpg)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mQueHEZG-1686967105073)(file:///C:\Users\lenovo\AppData\Local\Temp\ksohtml14876\wps20.jpg)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1MvzbHfJ-1686967105074)(file:///C:\Users\lenovo\AppData\Local\Temp\ksohtml14876\wps21.jpg)]


六、Webpack 篇


1.webpack五大核心


  • Entry - 入口

Entry 指示 Webpack 从哪个模块开始构建内部依赖图,以及如何将所有依赖项连接在一起。


  • Output - 输出

Output 定义了 Webpack 构建的输出文件的位置和文件名。


  • Loader - 加载器

Loader 用于对模块的源代码进行转换,以便于 Webpack 处理。例如,当 Webpack 遇到一个 “.css” 文件时,使用 “css-loader” 和 “style-loader” 将其转换为 JavaScript 代码以便于在浏览器中呈现样式。


  • Plugin - 插件

Plugin 在 Webpack 的构建过程中扮演一个关键的角色,它可以完成各种各样的任务,例如压缩代码、拷贝文件到输出目录、创建全局变量等等。


  • Mode - 模式

Mode 是 Webpack 4 引入的一个新特性,它提供了三种不同的构建模式,即 “development”、“production” 和 “none”,分别对应着开发模式、生产模式和不设置模式。


1. 谈谈你对Webpack的理解(Webpack是什么?)


Webpack 是一个 静态模块打包器,可以分析各个模块的依赖关系,项目中的所有资源皆为模块,通过分析模块间的依赖关系,在其内部递归构建出一个依赖关系图,其中包含应用程序需要的每个模块,然后将这些模块打包成一个或多个 bundle。最终编绎输出模块为 HTML、JavaScript、CSS 以及各种静态文件(图片、字体等)。


webpack 就像一条生产线,要经过一系列处理流程(loader)后才能将源文件转换成输出结果。 这条生产线上的每个处理流程的职责都是单一的,多个流程之间有存在依赖关系,只有完成当前处理后才能交给下一个流程去处理。

插件就像是一个插入到生产线中的一个功能,在特定的时机对生产线上的资源做处理。 webpack 在运行过程中会广播事件,插件只需要监听它所关心的事件,就能加入到这条生产线中,去改变生产线的运作。


webpack的主要作用如下:

  • 模块打包 可以将不同模块的文件打包整合在一起,并且保证它们之间的引用正确,执行有序。利用打包我们就可以在开发的时候根据我们自己的业务自由划分文件模块,保证项目结构的清晰和可读性。
  • 编译兼容 在前端的“上古时期”,手写一堆浏览器兼容代码一直是令前端工程师头皮发麻的事情,而在今天这个问题被大大的弱化了,通过webpack的Loader机制,不仅仅可以帮助我们对代码做polyfill,还可以编译转换诸如.less,.vue,.jsx这类在浏览器无法识别的格式文件,让我们在开发的时候可以使用新特性和新语法做开发,提高开发效率。
  • 能力扩展 通过webpack的Plugin机制,我们在实现模块化打包和编译兼容的基础上,可以进一步实现诸如按需加载,代码压缩等一系列功能,帮助我们进一步提高自动化程度,工程效率以及打包输出的质量。


2. Webpack的打包过程/打包原理/构建流程?


  • 初始化:启动构建,读取与合并配置参数,加载plugin,实例化Compiler
  • 编译:从Entry出发,针对每个Module串行调用对应的Loader去翻译文件中的内容,再找到该Module依赖的Module,递归的进行编译处理
  • 输出:将编译后的Module组合成Chunk,将Chunk转换成文件,输出到文件系统中

细节:


Webpack CLI 通过 yargs模块解析 CLI 参数,并转化为配置对象option(单入口:Object,多入口:Array),调用 webpack(option) 创建 compiler 对象。


如果有 option.plugin,则遍历调用plugin.apply()来注册 plugin,


判断是否开启了 watch,如果开启则调用 compiler.watch,否则调用 compiler.run,开始构建。


创建 Compilation 对象来收集全部资源和信息,然后触发 make 钩子。


make阶段从入口开始递归所有依赖,


每次遍历时调用对应Loader翻译文件中内容,然后生成AST,遍历AST找到下个依赖继续递归,


根据入口和模块之间关系组装chunk,输出到dist中的一个文件内。


在以上过程中,webpack会在特定的时间点(使用tapable模块)广播特定的事件,插件监听事件并执行相应的逻辑,并且插件可以调用webpack提供的api改变webpack的运行结果


3. loader的作用


webpack中的loader是一个函数,主要为了实现源码的转换,所以loader函数会以源码作为参数,比如,将ES6转换为ES5,将less转换为css,然后再将css转换为js,以便能嵌入到html文件中。


默认情况下,webpack只支持对js和json文件进行打包,但是像css、html、png等其他类型的文件,webpack则无能为力。因此,就需要配置相应的loader进行文件内容的解析转换。


4. 有哪些常见的Loader?他们是解决什么问题的?


常用的loader如下:


  • image-loader:加载并且压缩图片文件。
  • less-loader:加载并编译 LESS 文件。
  • sass-loader:加载并编译 SASS/SCSS 文件。
  • css-loader:加载 CSS,支持模块化、压缩、文件导入等特性,使用css-loader必须要配合使用style-loader。
  • style-loader:用于将 CSS 编译完成的样式,挂载到页面的 style 标签上。需要注意 loader 执行顺序,style-loader 要放在第一位,loader 都是从后往前执行。
  • babel-loader:把 ES6 转换成 ES5
  • postcss-loader:扩展 CSS 语法,使用下一代 CSS,可以配合 autoprefixer 插件自动补齐 CSS3 前缀。
  • eslint-loader:通过 ESLint 检查 JavaScript 代码。
  • vue-loader:加载并编译 Vue 组件。
  • file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件 (处理图片和字体)
  • url-loader:与 file-loader 类似,区别是用户可以设置一个阈值,大于阈值会交给 file-loader 处理,小于阈值时返回文件 base64 形式编码 (处理图片和字体)。
  • source-map-loader:加载额外的 Source Map 文件,以方便断点调试。


5. plugin的作用


plugin是一个类,类中有一个apply()方法,主要用于Plugin的安装,可以在其中监听一些来自编译器发出的事件,在合适的时机做一些事情。


webpack中的plugin赋予其各种灵活的功能,例如打包优化、资源管理、环境变量注入等,它们会运行在webpack的不同阶段(钩子 / 生命周期),贯穿了webpack整个编译周期。目的在于「解决 loader 无法实现的其他事」。


6. 有哪些常见的Plugin?他们是解决什么问题的?


  • html-webpack-plugin:可以复制一个有结构的html文件,并自动引入打包输出的所有资源(JS/CSS)
  • clean-webpack-plugin:重新打包自动清空 dist 目录
  • mini-css-extract-plugin:提取 js 中的 css 成单独文件
  • optimize-css-assets-webpack-plugin:压缩css
  • uglifyjs-webpack-plugin:压缩js
  • commons-chunk-plugin:提取公共代码
  • define-plugin:定义环境变量


7. Webpack中Loader和Plugin的区别


运行时机

  • 1.loader运行在编译阶段
  • 2.plugins 在整个周期都起作用


使用方式

Loader:1.下载 2.使用

Plugin:1.下载 2.引用 3.使用


loader是文件加载器,能够加载资源文件,并对这些文件进行一些处理,诸如编译、压缩等,最终一起打包到指定的文件中;plugin赋予了webpack各种灵活的功能,例如打包优化、资源管理、环境变量注入等,目的是解决 loader无法实现的其他事。


在运行时机上,loader 运行在打包文件之前;plugin则是在整个编译周期都起作用。


在配置上,loader在module.rules中配置,作为模块的解析规则,类型为数组。每一项都是一个 Object,内部包含了 test(类型文件)、loader、options (参数)等属性;plugin在 plugins中单独配置,类型为数组,每一项是一个 plugin 的实例,参数都通过构造函数传入。


8. webpack的热更新是如何做到的?说明其原理?


热更新的核心就是客户端从服务端拉去更新后的文件,准确的说是 chunk diff (chunk 需要更新的部分),实际上webpack-dev-server与浏览器之间维护了一个websocket,当本地资源发生变化时,webpack-dev-server会向浏览器推送更新,并带上构建时的hash,让客户端与上一次资源进行对比。客户端对比出差异后会向webpack-dev-server发起 Ajax 请求来获取更改内容(文件列表、hash),这样客户端就可以再借助这些信息继续向webpack-dev-server发起 jsonp 请求获取该chunk的增量更新。


后续的部分(拿到增量更新之后如何处理?哪些状态该保留?哪些又需要更新?)由HotModulePlugin 来完成,提供了相关 API 以供开发者针对自身场景进行处理,像react-hot-loader和vue-loader都是借助这些 API 实现热更新。


详细:

1、在 webpack 的 watch 模式下,文件系统中某一个文件发生修改,webpack 监听到文件变化,根据配置文件对模块重新编译打包,并将打包后的代码通过简单的 JavaScript 对象保存在内存中。

2、webpack-dev-server 和 webpack 之间的接口交互,而在这一步,主要是 dev-server 的中间件webpack-dev-middleware 和 webpack 之间的交互,webpack-dev-middleware 调用 webpack 暴露的 API对代码变化进行监控,并且告诉 webpack,将代码打包到内存中。

3、webpack-dev-server 对文件变化的一个监控,这一步不同于第一步,并不是监控代码变化重新打包。当我们在配置文件中配置了devServer.watchContentBase 为 true 的时候,Server 会监听这些配置文件夹中静态文件的变化,变化后会通知浏览器端对应用进行 live reload。注意,这儿是浏览器刷新,和 HMR 是两个概念

4、webpack-dev-server 代码的工作,该步骤主要是通过 sockjs(webpack-dev-server 的依赖)在浏览器端和服务端之间建立一个 websocket 长连接,将 webpack 编译打包的各个阶段的状态信息告知浏览器端,

同时也包括第三步中 Server 监听静态文件变化的信息。浏览器端根据这些 socket 消息进行不同的操作。当然服务端传递的最主要信息还是新模块的 hash 值,后面的步骤根据这一 hash 值来进行模块热替换。

webpack-dev-server/client 端并不能够请求更新的代码,也不会执行热更模块操作,而把这些工作又交回给了 webpack,webpack/hot/dev-server 的工作就是根据 webpack-dev-server/client 传给它的信息以及 dev-server 的配置决定是刷新浏览器呢还是进行模块热更新。当然如果仅仅是刷新浏览器,也就没有后面那些步骤了。HotModuleReplacement.runtime 是客户端 HMR 的中枢,它接收到上一步传递给他的新模块的 hash 值,它通过 JsonpMainTemplate.runtime 向 server 端发送 Ajax 请求,服务端返回一个 json,该 json 包含了所有要更新的模块的 hash 值,获取到更新列表后,该模块再次通过 jsonp 请求,获取到最新的模块代码。

5、决定 HMR 成功与否的关键步骤,在该步骤中,HotModulePlugin 将会对新旧模块进行对比,决定是否更新模块,在决定更新模块后,检查模块之间的依赖关系,更新模块的同时更新模块间的依赖引用。最后一步,当 HMR 失败后,回退到 live reload 操作,也就是进行浏览器刷新来获取最新打包代码。


9. 如何解决循环依赖问题


Webpack 中将 require 替换为 *webpack_require*,会根据 moduleId 到 installedModules 找是否加载过,加载过则直接返回之前的 export,不会重复加载。


10. 如何提高Webpack构建速度


1. 代码压缩

  • JS 压缩

webpack 4.0默认在生产环境的时候是支持代码压缩的,即mode=production模式下。实际上webpack 4.0默认是使用terser-webpack-plugin这个压缩插件,在此之前是使用 uglifyjs-webpack-plugin,两者的区别是后者对 ES6 的压缩不是很好,同时我们可以开启 parallel参数,使用多进程压缩,加快压缩。

  • CSS 压缩

CSS 压缩通常是去除无用的空格等,因为很难去修改选择器、属性的名称、值等。可以使用另外一个插件:css-minimizer-webpack-plugin。

  • HTML 压缩

使用HtmlWebpackPlugin插件来生成 HTML 的模板时候,通过配置属性minify进行 html 优化。

module.exports = {
plugin:[
  new HtmlwebpackPlugin({
    minify:{
      minifyCSS: false, // 是否压缩css
      collapseWhitespace: false, // 是否折叠空格
      removeComments: true // 是否移除注释
    }
  })
  ]
}


2. 图片压缩

配置image-webpack-loader


3. Tree Shaking

Tree Shaking是一个术语,在计算机中表示消除死代码,依赖于 ES Module 的静态语法分析(不执行任何的代码,可以明确知道模块的依赖关系)。在webpack实现Tree shaking有两种方案:


usedExports:通过标记某些函数是否被使用,之后通过 Terser 来进行优化的


module.exports = {
    ...
    optimization:{
        usedExports
    }
  }


使用之后,没被用上的代码在webpack打包中会加入unused harmony export mul注释,用来告知Terser在优化时,可以删除掉这段代码。


sideEffects:跳过整个模块/文件,直接查看该文件是否有副作用


sideEffects用于告知webpack compiler哪些模块时有副作用,配置方法是在package.json中设置sideEffects属性。如果sideEffects设置为false,就是告知webpack可以安全的删除未用到的exports。如果有些文件需要保留,可以设置为数组的形式,如:


"sideEffecis":[
    "./src/util/format.js",
    "*.css" // 所有的css文件
]


4. 缩小打包域

排除webpack不需要解析的模块,即在使用loader的时候,在尽量少的模块中去使用。可以借助 include和exclude这两个参数,规定loader只在那些模块应用和在哪些模块不应用。


5. 减少 ES6 转为 ES5 的冗余代码

使用、bable-plugin-transform-runtime插件


6. 提取公共代码

通过配置CommonsChunkPlugin插件,将多个页面的公共代码抽离成单独的文件


7. 其他

组件懒加载、路由懒加载、开启gzip、公共的第三方包上cdn、配置cache缓存Loader对文件的编译副本、配置resolve提高文件的搜索速度(@: src)


七、性能优化篇


1. 浏览器缓存优化


为了让浏览器缓存发挥最大作用,该策略尽量遵循以下五点就能发挥浏览器缓存最大作用。


  • 「考虑拒绝一切缓存策略」:Cache-Control:no-store
  • 「考虑资源是否每次向服务器请求」:Cache-Control:no-cache
  • 「考虑资源是否被代理服务器缓存」:Cache-Control:public/private
  • 「考虑资源过期时间」:Expires:t/Cache-Control:max-age=t,s-maxage=t
  • 「考虑协商缓存」:Last-Modified/Etag

缓存策略通过设置HTTP报文实现,在形式上分为**「强缓存/强制缓存」和「协商缓存/对比缓存」**。为了方便对比,笔者将某些细节使用图例展示,相信你有更好的理解。


image.png

image.png

整个缓存策略机制很明了,先走强缓存,若命中失败才走协商缓存。若命中强缓存,直接使用强缓存;若未命中强缓存,发送请求到服务器检查是否命中协商缓存;若命中协商缓存,服务器返回304通知浏览器使用本地缓存,否则返回最新资源。


有两种较常用的应用场景值得使用缓存策略一试,当然更多应用场景都可根据项目需求制定。


  • 「频繁变动资源」:设置Cache-Control:no-cache,使浏览器每次都发送请求到服务器,配合Last-Modified/ETag验证资源是否有效
  • 「不常变化资源」:设置Cache-Control:max-age=31536000,对文件名哈希处理,当代码修改后生成新的文件名,当HTML文件引入文件名发生改变才会下载最新文件


2. 渲染层面性能优化


**「渲染层面」**的性能优化,无疑是如何让代码解析更好执行更快。因此笔者从以下五方面做出建议。


  • 「CSS策略」:基于CSS规则
  • 「DOM策略」:基于DOM操作
  • 「阻塞策略」:基于脚本加载
  • 「回流重绘策略」:基于回流重绘
  • 「异步更新策略」:基于异步更新

上述五方面都是编写代码时完成,充满在整个项目流程的开发阶段里。因此在开发阶段需时刻注意以下涉及到的每一点,养成良好的开发习惯,性能优化也自然而然被使用上了。


渲染层面的性能优化更多表现在编码细节上,而并非实体代码。简单来说就是遵循某些编码规则,才能将渲染层面的性能优化发挥到最大作用。


**「回流重绘策略」**在渲染层面的性能优化里占比较重,也是最常规的性能优化之一。上年笔者发布的掘金小册《玩转CSS的艺术之美》使用一整章讲解回流重绘,本章已开通试读,更多细节请戳这里。


CSS策略

  • 避免出现超过三层的嵌套规则
  • 避免为ID选择器添加多余选择器
  • 避免使用标签选择器代替类选择器
  • 避免使用通配选择器,只对目标节点声明规则
  • 避免重复匹配重复定义,关注可继承属性

DOM策略

  • 缓存DOM计算属性
  • 避免过多DOM操作
  • 使用DOMFragment缓存批量化DOM操作

阻塞策略

  • 脚本与DOM/其它脚本的依赖关系很强:对``设置defer
  • 脚本与DOM/其它脚本的依赖关系不强:对``设置async

回流重绘策略

  • 缓存DOM计算属性
  • 使用类合并样式,避免逐条改变样式
  • 使用display控制DOM显隐,将DOM离线化

异步更新策略

  • 在异步任务中修改DOM时把其包装成微任务


3. 性能优化六大指标


六大指标基本囊括大部分性能优化细节,可作为九大策略的补充。笔者根据每条性能优化建议的特征将指标划分为以下六方面。


  • 「加载优化」:资源在加载时可做的性能优化
  • 「执行优化」:资源在执行时可做的性能优化
  • 「渲染优化」:资源在渲染时可做的性能优化
  • 「样式优化」:样式在编码时可做的性能优化
  • 「脚本优化」:脚本在编码时可做的性能优化
  • 「V8引擎优化」:针对V8引擎特征可做的性能优化


八、其他杂项篇


1. 常见的浏览器内核有哪些?


  • 主要分成两部分:渲染引擎(layout engineer或Rendering Engine)和JS引擎。
  • 渲染引擎:负责取得网页的内容(HTML、XML、图像等等)、整理讯息(例如加入CSS等),以及计算网页的显示方式,然后会输出至显示器或打印机。浏览器的内核的不同对于网页的语法解释会有不同,所以渲染的效果也不相同。所有网页浏览器、电子邮件客户端以及其它需要编辑、显示网络内容的应用程序都需要内核。
  • JS引擎则:解析和执行javascript来实现网页的动态效果。
  • 最开始渲染引擎和JS引擎并没有区分的很明确,后来JS引擎越来越独立,内核就倾向于只指渲染引擎。
  • 常见内核
  • Trident 内核:IE, MaxThon, TT, The World, 360, 搜狗浏览器等。[又称 MSHTML]
  • Gecko 内核:Netscape6 及以上版本,FF, MozillaSuite / SeaMonkey 等
  • Presto 内核:Opera7 及以上。 [Opera内核原为:Presto,现为:Blink;]
  • Webkit 内核:Safari, Chrome等。 [ Chrome的:Blink(WebKit 的分支)]

2. 网页前端性能优化的方式有哪些?


1.压缩 css, js, 图片

2.减少 http 请求次数, 合并 css、js 、合并图片(雪碧图)

3.使用 CDN

4.减少 dom 元素数量

5.图片懒加载

6.静态资源另外用无 cookie 的域名

7.减少 dom 的访问(缓存 dom)

8.巧用事件委托

9.样式表置顶、脚本置低


3. 网页从输入网址到渲染完成经历了哪些过程?


大致可以分为如下7步:


输入网址;

  • 发送到DNS服务器,并获取域名对应的web服务器对应的ip地址;
  • 与web服务器建立TCP连接;
  • 浏览器向web服务器发送http请求;
  • web服务器响应请求,并返回指定url的数据(或错误信息,或重定向的新的url地址);
  • 浏览器下载web服务器返回的数据及解析html源文件;
  • 生成DOM树,解析css和js,渲染页面,直至显示完成;


4. 线程与进程的区别?


  • 一个程序至少有一个进程,一个进程至少有一个线程.
  • 线程的划分尺度小于进程,使得多线程程序的并发性高。
  • 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
  • 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
  • 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。


5. HTTP常见的状态码?


100 Continue 继续,一般在发送post请求时,已发送了http header之后服务端将返回此信息,表示确认,之后发送具体参数信息

200 OK 正常返回信息

201 Created 请求成功并且服务器创建了新的资源

202 Accepted 服务器已接受请求,但尚未处理

301 Moved Permanently 请求的网页已永久移动到新位置。

302 Found 临时性重定向。

303 See Other 临时性重定向,且总是使用 GET 请求新的 URI。

304 Not Modified 自从上次请求后,请求的网页未修改过。

400 Bad Request 服务器无法理解请求的格式,客户端不应当尝试再次使用相同的内容发起请求。

401 Unauthorized 请求未授权。

403 Forbidden 禁止访问。

404 Not Found 找不到如何与 URI 相匹配的资源。

500 Internal Server Error 最常见的服务器端错误。

503 Service Unavailable 服务器端暂时无法处理请求(可能是过载或维护)。


6. 图片懒加载?


当页面滚动的时间被触发 -> 执行加载图片操作 -> 判断图片是否在可视区域内 -> 在,则动态将data-src的值赋予该图片


7. 移动端性能优化?


  • 尽量使用css3动画,开启硬件加速
  • 适当使用touch时间代替click时间
  • 避免使用css3渐变阴影效果
  • 可以用transform: translateZ(0) 来开启硬件加速
  • 不滥用float。float在渲染时计算量比较大,尽量减少使用
  • 不滥用web字体。web字体需要下载,解析,重绘当前页面
  • 合理使用requestAnimationFrame动画代替setTimeout
  • css中的属性(css3 transitions、css3 3D transforms、opacity、webGL、video)会触发GUP渲染,耗电


8. TCP 传输的三次握手、四次挥手策略


  • 三次握手:

为了准确无误地吧数据送达目标处,TCP协议采用了三次握手策略。用TCP协议把数据包送出去后,TCP不会对传送后的情况置之不理,他一定会向对方确认是否送达,握手过程中使用TCP的标志:SYN和ACK

  • 发送端首先发送一个带SYN的标志的数据包给对方
  • 接收端收到后,回传一个带有SYN/ACK标志的数据包以示传达确认信息
  • 最后,发送端再回传一个带ACK的标志的数据包,代表“握手”结束
  • 如在握手过程中某个阶段莫明中断,TCP协议会再次以相同的顺序发送相同的数据包


  • 断开一个TCP连接需要“四次挥手”
  • 第一次挥手:主动关闭方发送一个FIN,用来关注主动方到被动关闭方的数据传送,也即是主动关闭方告诫被动关闭方:我已经不会再给你发数据了(在FIN包之前发送的数据,如果没有收到对应的ACK确认报文,主动关闭方依然会重发这些数据)。但是,此时主动关闭方还可以接受数据
  • 第二次挥手:被动关闭方收到FIN包后,发送一个ACK给对方,确认序号收到序号 +1(与SYN相同,一个 FIN占用一个序号)
  • 第三次挥手:被动关闭方发送一个 FIN。用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,我的数据也发送完了,不会给你发送数据了
  • 第四次挥手:主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,至此,完成四次挥手


9. HTTP 和 HTTPS,为什么HTTPS安全?


  • HTTP协议通常承载与 TCP协议之上,在HTTP和TCP之间添加一个安全协议层(SSL或TSL),这个时候,就成了我们常说的HTTPS
  • 默认HTTP的端口号为80,HTTPS的端口号为443
  • 因为网络请求需要中间有很多的服务器路由的转发,中间的节点都可能篡改信息,而如果使用HTTPS,密钥在你和终点站才有,https之所有说比http安全,是因为他利用ssl/tls协议传输。包含证书,流量转发,负载均衡,页面适配,浏览器适配,refer传递等,保障了传输过程的安全性


10. axios和fetch区别对比


axios 是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端,本质上也是对原生XHR的封装,只不过它是Promise的实现版本,符合最新的ES规范,它本身具有以下特征


  • 从浏览器中创建 XMLHttpRequest
  • 支持 Promise API
  • 客户端支持防止CSRF
  • 提供了一些并发请求的接口(重要,方便了很多的操作)
  • 从 node.js 创建 http 请求
  • 拦截请求和响应
  • 转换请求和响应数据
  • 取消请求
  • 自动转换JSON数据


fetch优势:


  • 语法简洁,更加语义化
  • 基于标准 Promise 实现,支持 async/await
  • 同构方便,使用 isomorphic-fetch
  • 更加底层,提供的API丰富(request, response)
  • 脱离了XHR,是ES规范里新的实现方式


fetch存在问题


  • fetch是一个低层次的API,你可以把它考虑成原生的XHR,所以使用起来并不是那么舒服,需要进行封装。
  • fetch只对网络请求报错,对400,500都当做成功的请求,服务器返回 400,500 错误码时并不会 reject,只有网络错误这些导致请求不能完成时,fetch 才会被 reject。
  • fetch默认不会带cookie,需要添加配置项: fetch(url, {credentials: ‘include’})
  • fetch不支持abort,不支持超时控制,使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了流量的浪费
  • fetch没有办法原生监测请求的进度,而XHR可以

九、主观题篇


1. 你都做过什么项目呢?具体聊某一个项目中运用的技术.


注意:用心找自己做的项目中自己感觉最拿出来手的(复杂度最高,用的技术最多的项目),描述的时候尽可能往里面添加一些技术名词

布局我们用html5+css3

我们会用reset.css重置浏览器的默认样式

JS框架的话我们选用的是jQuery(也可能是Zepto)

我们用版本控制工具git来协同开发

我们会基于gulp搭建的前端自动化工程来开发(里面包含有我们的项目结构、我们需要引用的第三方库等一些信息,我们还实现了sass编译、CSS3加前缀等的自动化)

我们的项目中还用到了表单验证validate插件、图片懒加载Lazyload插件


2. 你遇到过比较难的技术问题是?你是如何解决的?


3. 常使用的库有哪些?常用的前端开发工具?开发过什么应用或组件?


4. 除了前端以外还了解什么其它技术么?你最最厉害的技能是什么?


5. 对前端开发工程师这个职位是怎么样理解的?它的前景会怎么样?


前端是最贴近用户的程序员,比后端、数据库、产品经理、运营、安全都近。

1、实现界面交互

2、提升用户体验

3、有了Node.js,前端可以实现服务端的一些事情

前端是最贴近用户的程序员,前端的能力就是能让产品从 90分进化到 100 分,甚至更好,

参与项目,快速高质量完成实现效果图,精确到1px;

与团队成员,UI设计,产品经理的沟通;

做好的页面结构,页面重构和用户体验;

处理hack,兼容、写出优美的代码格式;

针对服务器的优化、拥抱最新前端技术。



  • 「阻塞策略」:基于脚本加载
  • 「回流重绘策略」:基于回流重绘
  • 「异步更新策略」:基于异步更新

上述五方面都是编写代码时完成,充满在整个项目流程的开发阶段里。因此在开发阶段需时刻注意以下涉及到的每一点,养成良好的开发习惯,性能优化也自然而然被使用上了。


渲染层面的性能优化更多表现在编码细节上,而并非实体代码。简单来说就是遵循某些编码规则,才能将渲染层面的性能优化发挥到最大作用。


**「回流重绘策略」**在渲染层面的性能优化里占比较重,也是最常规的性能优化之一。上年笔者发布的掘金小册《玩转CSS的艺术之美》使用一整章讲解回流重绘,本章已开通试读,更多细节请戳这里。


CSS策略

  • 避免出现超过三层的嵌套规则
  • 避免为ID选择器添加多余选择器
  • 避免使用标签选择器代替类选择器
  • 避免使用通配选择器,只对目标节点声明规则
  • 避免重复匹配重复定义,关注可继承属性

DOM策略

  • 缓存DOM计算属性
  • 避免过多DOM操作
  • 使用DOMFragment缓存批量化DOM操作

阻塞策略

  • 脚本与DOM/其它脚本的依赖关系很强:对``设置defer
  • 脚本与DOM/其它脚本的依赖关系不强:对``设置async

回流重绘策略

  • 缓存DOM计算属性
  • 使用类合并样式,避免逐条改变样式
  • 使用display控制DOM显隐,将DOM离线化

异步更新策略

  • 在异步任务中修改DOM时把其包装成微任务


3. 性能优化六大指标


六大指标基本囊括大部分性能优化细节,可作为九大策略的补充。笔者根据每条性能优化建议的特征将指标划分为以下六方面。


  • 「加载优化」:资源在加载时可做的性能优化
  • 「执行优化」:资源在执行时可做的性能优化
  • 「渲染优化」:资源在渲染时可做的性能优化
  • 「样式优化」:样式在编码时可做的性能优化
  • 「脚本优化」:脚本在编码时可做的性能优化
  • 「V8引擎优化」:针对V8引擎特征可做的性能优化


八、其他杂项篇


1. 常见的浏览器内核有哪些?


  • 主要分成两部分:渲染引擎(layout engineer或Rendering Engine)和JS引擎。
  • 渲染引擎:负责取得网页的内容(HTML、XML、图像等等)、整理讯息(例如加入CSS等),以及计算网页的显示方式,然后会输出至显示器或打印机。浏览器的内核的不同对于网页的语法解释会有不同,所以渲染的效果也不相同。所有网页浏览器、电子邮件客户端以及其它需要编辑、显示网络内容的应用程序都需要内核。
  • JS引擎则:解析和执行javascript来实现网页的动态效果。
  • 最开始渲染引擎和JS引擎并没有区分的很明确,后来JS引擎越来越独立,内核就倾向于只指渲染引擎。
  • 常见内核
  • Trident 内核:IE, MaxThon, TT, The World, 360, 搜狗浏览器等。[又称 MSHTML]
  • Gecko 内核:Netscape6 及以上版本,FF, MozillaSuite / SeaMonkey 等
  • Presto 内核:Opera7 及以上。 [Opera内核原为:Presto,现为:Blink;]
  • Webkit 内核:Safari, Chrome等。 [ Chrome的:Blink(WebKit 的分支)]



2. 网页前端性能优化的方式有哪些?


1.压缩 css, js, 图片

2.减少 http 请求次数, 合并 css、js 、合并图片(雪碧图)

3.使用 CDN

4.减少 dom 元素数量

5.图片懒加载

6.静态资源另外用无 cookie 的域名

7.减少 dom 的访问(缓存 dom)

8.巧用事件委托

9.样式表置顶、脚本置低


3. 网页从输入网址到渲染完成经历了哪些过程?


大致可以分为如下7步:


输入网址;

  • 发送到DNS服务器,并获取域名对应的web服务器对应的ip地址;
  • 与web服务器建立TCP连接;
  • 浏览器向web服务器发送http请求;
  • web服务器响应请求,并返回指定url的数据(或错误信息,或重定向的新的url地址);
  • 浏览器下载web服务器返回的数据及解析html源文件;
  • 生成DOM树,解析css和js,渲染页面,直至显示完成;


4. 线程与进程的区别?


  • 一个程序至少有一个进程,一个进程至少有一个线程.
  • 线程的划分尺度小于进程,使得多线程程序的并发性高。
  • 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
  • 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
  • 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。


5. HTTP常见的状态码?


100 Continue 继续,一般在发送post请求时,已发送了http header之后服务端将返回此信息,表示确认,之后发送具体参数信息

200 OK 正常返回信息

201 Created 请求成功并且服务器创建了新的资源

202 Accepted 服务器已接受请求,但尚未处理

301 Moved Permanently 请求的网页已永久移动到新位置。

302 Found 临时性重定向。

303 See Other 临时性重定向,且总是使用 GET 请求新的 URI。

304 Not Modified 自从上次请求后,请求的网页未修改过。

400 Bad Request 服务器无法理解请求的格式,客户端不应当尝试再次使用相同的内容发起请求。

401 Unauthorized 请求未授权。

403 Forbidden 禁止访问。

404 Not Found 找不到如何与 URI 相匹配的资源。

500 Internal Server Error 最常见的服务器端错误。

503 Service Unavailable 服务器端暂时无法处理请求(可能是过载或维护)。


6. 图片懒加载?


当页面滚动的时间被触发 -> 执行加载图片操作 -> 判断图片是否在可视区域内 -> 在,则动态将data-src的值赋予该图片


7. 移动端性能优化?


  • 尽量使用css3动画,开启硬件加速
  • 适当使用touch时间代替click时间
  • 避免使用css3渐变阴影效果
  • 可以用transform: translateZ(0) 来开启硬件加速
  • 不滥用float。float在渲染时计算量比较大,尽量减少使用
  • 不滥用web字体。web字体需要下载,解析,重绘当前页面
  • 合理使用requestAnimationFrame动画代替setTimeout
  • css中的属性(css3 transitions、css3 3D transforms、opacity、webGL、video)会触发GUP渲染,耗电


8. TCP 传输的三次握手、四次挥手策略


  • 三次握手:

为了准确无误地吧数据送达目标处,TCP协议采用了三次握手策略。用TCP协议把数据包送出去后,TCP不会对传送后的情况置之不理,他一定会向对方确认是否送达,握手过程中使用TCP的标志:SYN和ACK

  • 发送端首先发送一个带SYN的标志的数据包给对方
  • 接收端收到后,回传一个带有SYN/ACK标志的数据包以示传达确认信息
  • 最后,发送端再回传一个带ACK的标志的数据包,代表“握手”结束
  • 如在握手过程中某个阶段莫明中断,TCP协议会再次以相同的顺序发送相同的数据包


  • 断开一个TCP连接需要“四次挥手”
  • 第一次挥手:主动关闭方发送一个FIN,用来关注主动方到被动关闭方的数据传送,也即是主动关闭方告诫被动关闭方:我已经不会再给你发数据了(在FIN包之前发送的数据,如果没有收到对应的ACK确认报文,主动关闭方依然会重发这些数据)。但是,此时主动关闭方还可以接受数据
  • 第二次挥手:被动关闭方收到FIN包后,发送一个ACK给对方,确认序号收到序号 +1(与SYN相同,一个 FIN占用一个序号)
  • 第三次挥手:被动关闭方发送一个 FIN。用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,我的数据也发送完了,不会给你发送数据了
  • 第四次挥手:主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,至此,完成四次挥手


9. HTTP 和 HTTPS,为什么HTTPS安全?


  • HTTP协议通常承载与 TCP协议之上,在HTTP和TCP之间添加一个安全协议层(SSL或TSL),这个时候,就成了我们常说的HTTPS
  • 默认HTTP的端口号为80,HTTPS的端口号为443
  • 因为网络请求需要中间有很多的服务器路由的转发,中间的节点都可能篡改信息,而如果使用HTTPS,密钥在你和终点站才有,https之所有说比http安全,是因为他利用ssl/tls协议传输。包含证书,流量转发,负载均衡,页面适配,浏览器适配,refer传递等,保障了传输过程的安全性


10. axios和fetch区别对比


axios 是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端,本质上也是对原生XHR的封装,只不过它是Promise的实现版本,符合最新的ES规范,它本身具有以下特征


  • 从浏览器中创建 XMLHttpRequest
  • 支持 Promise API
  • 客户端支持防止CSRF
  • 提供了一些并发请求的接口(重要,方便了很多的操作)
  • 从 node.js 创建 http 请求
  • 拦截请求和响应
  • 转换请求和响应数据
  • 取消请求
  • 自动转换JSON数据


fetch优势:


  • 语法简洁,更加语义化
  • 基于标准 Promise 实现,支持 async/await
  • 同构方便,使用 isomorphic-fetch
  • 更加底层,提供的API丰富(request, response)
  • 脱离了XHR,是ES规范里新的实现方式


fetch存在问题


  • fetch是一个低层次的API,你可以把它考虑成原生的XHR,所以使用起来并不是那么舒服,需要进行封装。
  • fetch只对网络请求报错,对400,500都当做成功的请求,服务器返回 400,500 错误码时并不会 reject,只有网络错误这些导致请求不能完成时,fetch 才会被 reject。
  • fetch默认不会带cookie,需要添加配置项: fetch(url, {credentials: ‘include’})
  • fetch不支持abort,不支持超时控制,使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了流量的浪费
  • fetch没有办法原生监测请求的进度,而XHR可以


九、主观题篇


1. 你都做过什么项目呢?具体聊某一个项目中运用的技术.


注意:用心找自己做的项目中自己感觉最拿出来手的(复杂度最高,用的技术最多的项目),描述的时候尽可能往里面添加一些技术名词

布局我们用html5+css3

我们会用reset.css重置浏览器的默认样式

JS框架的话我们选用的是jQuery(也可能是Zepto)

我们用版本控制工具git来协同开发

我们会基于gulp搭建的前端自动化工程来开发(里面包含有我们的项目结构、我们需要引用的第三方库等一些信息,我们还实现了sass编译、CSS3加前缀等的自动化)

我们的项目中还用到了表单验证validate插件、图片懒加载Lazyload插件


2. 你遇到过比较难的技术问题是?你是如何解决的?


3. 常使用的库有哪些?常用的前端开发工具?开发过什么应用或组件?


常用的库:


  • React.js:用于构建用户界面的JavaScript库。
  • Vue.js:另一个流行的JavaScript框架,用于构建可交互的用户界面。
  • Angular:一个支持大型应用程序开发的完整的JavaScript框架。
  • jQuery:一个简化DOM操作和处理事件的JavaScript库。
  • Lodash:提供了很多实用的功能和工具函数,用于简化JavaScript开发任务。


常用的前端开发工具:

  • Visual Studio Code:一款流行的轻量级代码编辑器。
  • Sublime Text:Sublime Text是一款强大而轻量级的代码编辑器。它具有快速、稳定和高度可定制的特点,支持丰富的插件生态系统,可以满足各种前端开发需求。Sublime Text具有直观的界面、强大的搜索和替换功能、多行选择、语法高亮和自动完成等功能,让前端开发更加高效。
  • WebStorm:WebStorm是一款由JetBrains开发的集成开发环境(IDE)。它专注于提供全面的JavaScript开发体验,适用于前端和后端开发。WebStorm具有强大的代码编辑功能、智能代码完成、错误检查和修复、调试器、Git集成、自动重载和实时预览等功能,能够提供更高效和便捷的前端开发环境。

组件例如:


  • 单页应用(SPA):构建响应式、高度交互的单页Web应用。
  • 表单验证组件:用于验证和处理用户输入的表单组件。
  • 图片轮播组件:用于展示多张图片的轮播效果。
  • 响应式导航菜单:根据不同设备和屏幕大小自适应的导航菜单。
  • 数据可视化组件:用于呈现和展示数据的图表、图形等组件。


4. 除了前端以外还了解什么其它技术么?你最最厉害的技能是什么?


5. 对前端开发工程师这个职位是怎么样理解的?它的前景会怎么样?


前端是最贴近用户的程序员,比后端、数据库、产品经理、运营、安全都近。

1、实现界面交互

2、提升用户体验

3、有了Node.js,前端可以实现服务端的一些事情

前端是最贴近用户的程序员,前端的能力就是能让产品从 90分进化到 100 分,甚至更好,

参与项目,快速高质量完成实现效果图,精确到1px;

与团队成员,UI设计,产品经理的沟通;

做好的页面结构,页面重构和用户体验;

处理hack,兼容、写出优美的代码格式;

针对服务器的优化、拥抱最新前端技术。


相关实践学习
容器服务Serverless版ACK Serverless 快速入门:在线魔方应用部署和监控
通过本实验,您将了解到容器服务Serverless版ACK Serverless 的基本产品能力,即可以实现快速部署一个在线魔方应用,并借助阿里云容器服务成熟的产品生态,实现在线应用的企业级监控,提升应用稳定性。
云原生实践公开课
课程大纲 开篇:如何学习并实践云原生技术 基础篇: 5 步上手 Kubernetes 进阶篇:生产环境下的 K8s 实践 相关的阿里云产品:容器服务&nbsp;ACK 容器服务&nbsp;Kubernetes&nbsp;版(简称&nbsp;ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情:&nbsp;https://www.aliyun.com/product/kubernetes
目录
相关文章
|
1月前
|
前端开发 JavaScript 网络协议
前端最常见的JS面试题大全
【4月更文挑战第3天】前端最常见的JS面试题大全
53 5
|
4月前
|
设计模式 前端开发 算法
No210.精选前端面试题,享受每天的挑战和学习
No210.精选前端面试题,享受每天的挑战和学习
No210.精选前端面试题,享受每天的挑战和学习
|
4月前
|
消息中间件 缓存 前端开发
No209.精选前端面试题,享受每天的挑战和学习
No209.精选前端面试题,享受每天的挑战和学习
No209.精选前端面试题,享受每天的挑战和学习
|
4月前
|
前端开发 JavaScript Java
No208.精选前端面试题,享受每天的挑战和学习
No208.精选前端面试题,享受每天的挑战和学习
No208.精选前端面试题,享受每天的挑战和学习
|
4月前
|
存储 JSON 前端开发
No206.精选前端面试题,享受每天的挑战和学习
No206.精选前端面试题,享受每天的挑战和学习
No206.精选前端面试题,享受每天的挑战和学习
|
2月前
|
存储 缓存 监控
2024年春招小红书前端实习面试题分享
春招已经拉开帷幕啦! 春招的拉开,意味着新一轮的求职大战已经打响,希望每位求职者都能充分准备,以最佳的状态迎接挑战,找到心仪的工作,开启职业生涯的新篇章。祝愿每位求职者都能收获满满,前程似锦!
80 3
|
2月前
|
前端开发 数据可视化 安全
2024金三银四必看前端面试题!简答版精品!
2024金三银四必看前端面试题!2w字精品!简答版 金三银四黄金期来了 想要跳槽的小伙伴快来看啊
97 3
|
3月前
|
存储 前端开发 JavaScript
前端面试:如何实现并发请求数量控制?
前端面试:如何实现并发请求数量控制?
97 0
|
4月前
|
前端开发 JavaScript 数据处理
No207.精选前端面试题,享受每天的挑战和学习
No207.精选前端面试题,享受每天的挑战和学习
No207.精选前端面试题,享受每天的挑战和学习
|
4月前
|
存储 缓存 前端开发
No205.精选前端面试题,享受每天的挑战和学习
No205.精选前端面试题,享受每天的挑战和学习