面试题整理2022.6.19
vue的双向绑定原理
-
Vue中有2种数据绑定的方式:
- 单向绑定(v-bind):数据只能从data流向页面
- 双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data
-
使用场景:
1.双向绑定一般都应用在表单类元素上(如:input、select等)
2.v-model:value 可以简写为 v-model,因为v-model默认收集的就是value值 -
vue数据的双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的。其核心就是通过Object.defineProperty()方法设置set和get函数来实现数据的劫持,在数据变化时发布消息给订阅者,触发相应的监听回调。也就是说数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变
react为什么是单向数据流
-
React遵循从上到下的数据流向,即单向数据流。React是单向数据流,数据主要从父节点传递到子节点(通过props)。如果顶层(父级)的某个props改变了,React会重渲染所有的子节点。
-
单向数据流是react规范的数据流向, 它的作用是极大的降低了我们组件间通信的代码耦合,让组件间的通信更为清晰。
-
简单的单向数据流(unidirectional data flow)是指用户访问View,View发出用户交互的Action,在Action里对state进行相应更新。state更新后会触发View更新页面的过程。这样数据总是清晰的单向进行流动,便于维护并且可以预测。
vue中key值的作用
提高元素节点渲染的销量
key 的特殊 attribute 主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识VNodes。如果不使用key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。而使用 key 时,它会基于 key的变化重新排列元素顺序,并且会移除 key 不存在的元素。有相同父元素的子元素必须有独特的 key。重复的 key 会造成渲染错误。
vue中data为什么是一个函数?
一句话总结: 函数有自己独立的作用域
1.vue中组件是用来复用的,为了防止data复用,将其定义为函数。
2.vue组件中的data数据都应该是相互隔离,互不影响的,组件每复用一次,data数据就应该被复制一次,之后,当某一处复用的地方组件内data数据被改变时,其他复用地方组件的data数据不受影响,就需要通过data函数返回一个对象作为组件的状态。
3.当我们将组件中的data写成一个函数,数据以函数返回值形式定义,这样每复用一次组件,就会返回一份新的data,拥有自己的作用域,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。
4.当我们组件的date单纯的写成对象形式,这些实例用的是同一个构造函数,由于JavaScript的特性所导致,所有的组件实例共用了一个data,就会造成一个变了全都会变的结果。
vue2和vue3的区别
1. 双向数据绑定原理发生了改变
vue2的双向数据绑定是利用了es5 的一个API Object.definepropert() 对数据进行劫持 结合发布订阅模式来实现的。vue3中使用了es6的proxyAPI对数据进行处理。
相比与vue2,使用proxy API 优势有:defineProperty只能监听某个属性,不能对全对象进行监听;可以省去for in 、闭包等内容来提升效率(直接绑定整个对象即可);可以监听数组,不用再去单独的对数组做特异性操作,vue3可以检测到数组内部数据的变化。
vue3支持碎片Fragment
可以有多个根结点
Composition组合式api
Vue2 与vue3 最大的区别是vue2使用选项类型api,对比vue3合成型api。旧得选项型api在代码里分割了不同得属性:data,computed,methods等;新得合成型api能让我们使用方法来分割,相比于旧的API使用属性来分组,这样代码会更加简便和整洁
生命周期
vue2 ---------------------------------- vue3
beforeCreate -> setup()
Created -> setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroyed -> onBeforeUnmount
destroyed -> onUnmounted
activated -> onActivated
deactivated -> onDeactivated
setup()函数的特性
- setup()函数接收两个参数:props、context(包含attrs、slots、emit)
- setup函数是处于生命周期beforeCreated和created俩个钩子函数之前, 执行setup时,组件实例尚未被创建(在setup()内部将this修改成了undefined,不再指向实例)
- 因为setup函数中,props是响应式的,当传入新的prop时,它将会被更新,所以不能使用es6解构,因为它会消除prop得响应性,如需解构prop,可以通过使用setup函数中得toRefs来完成此操作
- 父传子,用props,子传父用事件 Emitting Events。在vue2中,会调用this$emit然后传入事件名和对象;在vue3中得setup(props,context)中得第二个参数content对象中就有emit,那么我们只要在setup()接收第二个参数中使用分解对象法取出emit就可以在setup方法中随意使用了
- 在setup()内使用响应式数据时,需要通过 .value 获取
响应式数据的声明
- ref定义的是基本数据类型, ref通过Object.defineProperty()的get和set实现数据劫持, ref操作数据.value,读取时不需要.value
- reactive一般定义对象或数组数据类型, reactive通过Proxy实现数据劫持, reactive操作和读取数据不需要.value
v-for和v-if
-
vue2中v-for的优先级大于v-if, vue3中v-if的优先级大于v-for
-
在vue2中, v-if 和 v-for 是可以一起使用作用在某个元素上,用于对数据的筛选和显示, 但是并不推荐这种做法, 会影响性能, 增加渲染时间, 建议在渲染之前先将数据处理好再放到页面中渲染
react中函数式组件和类组件的区别
-
概念:
-
函数组件: 函数组件也称无状态组件,就是以函数形态存在的 React 组件, 在
hooks出现之前,react 中的函数组件通常只考虑负责UI的渲染,没有自身的状态,没有业务逻辑代码,是一个纯函数。下面这个函数组件就是一个纯函数,它的输出只由参数props决定,不受其他任何因素影响, 函数组件没有实例,没有生命周期, 所以我们说在hook之前的函数组件和类组件最大的区别又是状态的有无。 -
Hook: 是从 React 16.8 版本推出的新特性,目的是解决 React 的状态共享以及组件生命周期管理混乱的问题。React Hooks 的出现标志着,React 不会再存在无状态组件的情况,React 将只有类组件和函数组件的概念。
-
类组件: 类组件就是基于 ES6 语法,通过继承
React.component得到的组件。
-
-
差异: 对比与类组件, 函数组件没有生命周期, 不需要继承Class, 没有this, 无state, 因此更加灵活便于逻辑的拆分
route和router的区别
可以理解为,一个是用来获取路由信息的,一个是用来操作路由的
- route是路由信息对象,每一个路由都会有一个route对象,是一个局部的对象,里面主要包含路由的一些基本信息,包括name、meta、path、hash、query、params、fullPath、matched、redirectedFrom等等
- router是VueRouter的实例,通过Vue.use(VueRouter)和VueRouter构造函数得到一个router的实例对象,这个对象中是一个全局的对象,他包含了所有的路由包含了许多关键的对象和属性
hooks中常用的api有哪些
useState()
- const [state, setState] = useState(initialState);
- 返回一个 state,以及更新 state 的函数。
- 页面中使用: 直接
{state} - 数据的修改: 只能通过setState
setState(newState);
useEffect()
- 它可以用来更好的处理副作用,如异步请求等;可以把 useEffect Hook 看做
componentDidMount,componentDidUpdate和componentWillUnmount这三个函数的组合。 - 可以给
useEffect传递第二个参数,它是 effect 所依赖的值数组, 如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组([])作为第二个参数。这就告诉 React 你的 effect 不依赖于 props 或 state 中的任何值,所以它永远都不需要重复执行 (如果没有第二个参数, 只要页面中有任何数据的变化都会重新请求数据, 以至于可能无限次疯狂的请求)
useContext()
const value = useContext(MyContext);( useContext 的参数必须是 context 对象本身)- 接收一个 context 对象(
React.createContext的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的<MyContext.Provider>的valueprop 决定 - 实现跨层级的数据传递, 提供了一种在组件之间共享 props 的方式
实现步骤:
(1) 存在一个组件跨层级的关系
(2) 新建一个context目录专门用于存储需要跨层级传递的数据
/* src/context/numContext.js */
import React from "react";
export default React.createContext(100)
(3) 在父组件中引入context并传递数据
import React, { useContext } from 'react'
import Child1 from '../component/Child1'
import NumContext from '../context/numContext'
export default function UseContext() {
const context = useContext(NumContext)
return (
<div>父组件
<NumContext.Provider value={1000}>
{context} {/* 100 */}
<Child1></Child1>
</NumContext.Provider>
</div>
)
}
(4) 孙组件中通过useContext获取
import React, { useContext } from 'react'
import NumContext from '../context/numContext'
export default function Child2() {
const context = useContext(NumContext)
return (
<div>孙组件
{context} {/* 1000 */}
</div>
)
}
useReducer()
const [state, dispatch] = useReducer(reducer, initialArg, init);- 它接受一个reducer (形如
(state, action) => newState), 并返回当前的state以及与其配套的dispatch方法
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter({initialState}) {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<View>
Count: {state.count}
<Button onClick={() => dispatch({type: 'increment'})}>+</Button>
<Button onClick={() => dispatch({type: 'decrement'})}>-</Button>
</View>
);
}
- useReducer 这个 Hooks 在使用上几乎跟 Redux/React-Redux 一模一样,唯一缺少的就是无法使用 redux 提供的中间件。如果遇到多个组件依赖于reducer中的同一个状态, 某一个组件中更改此状态时不会改变另一个组件中的状态值, 这时就需要结合useContext来实现数据的同步更新
useCallback()
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
- 返回一个memorized回调函数, 该回调函数仅在某个依赖项改变时才会更新
- 普通用法和useEffect很类似,
useCallback(fn, deps)相当于useMemo(() => fn, deps) - 但是当你把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染(例如
shouldComponentUpdate)的子组件时,它将非常有用。可以提高性能, 避免组件的重复渲染(用在父组件中)
React.memo()
- 使用React.memo()包裹子组件
- 这是react的高阶组件写法 -> 组件作为函数(memo)的参数,函数的返回值是一个新的组件
————————————————————————子组件————————————————————————————————
import React, { memo } from 'react'
function Child3(props) {
//如果没有memo那么父组件中任意一个数据的改变都会触发子组件的重新渲染
//如果希望它变成一个纯组件(只是单纯显示数据)的,父组件中任意变动不会影响到子组件,可以使用memo包裹
//使用memo包裹后子组件只会在第一次初始化的时候渲染一次,之后不会受到父组件的影响
//但是如果希望指定某个数据变化后子组件重新渲染
//子组件的props中可以接收到父组件定义的fn方法,只要父组件中num发生变化,子组件就会重新加载
console.log("子组件渲染");
return (
<div>Child3</div>
)
}
export default memo(Child3)
————————————————————————————父组件————————————————————————————
import React, { useState, useCallback } from 'react'
import Child3 from '../component/Child3'
export default function UseCallback() {
let [num, setNum] = useState(10)
const [n, setN] = useState(100)
//使用usecallback来实现某一个数据对子组件的影响
let fn = useCallback(
() => {
console.log(num);
}, [num]
)
return (
<div>useCallback
父组件中num的值为: num:{num}
<button onClick={() => { setNum(num + 1) }}>num+1</button>
<br></br>
父组件中n的值为: n:{n}
<button onClick={() => { setN(n + 1) }}>n+1</button>
<Child3 fn={fn}></Child3>
</div>
)
}
useMemo()
-
也可以用于提升性能, 但是区别于useCallback(避免组件的重复渲染), useMemo主要是在当前页面中缓存数据, 避免不必要的重复执行
-
import React, { useCallback, useState, useMemo } from 'react' export default function UseMemo() { let [num, setNum] = useState(10) let [price, setPrice] = useState(1000) const [n, setN] = useState(0) let total2 = () => { console.log("total2正在计算"); //n每一次变化都会触发函数的重新执行 return num * price } let total3 = useCallback( () => { //利用useCallback也同样n变化会触发每一次重新执行,说明行不通 console.log("total3正在计算"); return num * price }, [num, price] ) let total4 = useMemo(() => { //只会当num和price触发时才会执行里面的函数 //这里total4不是一个函数,里面的函数直接执行得到一个返回值给total4 console.log("total4正在计算"); return num * price }, [num, price]) return ( <div>useMemo <div>变量n {n} <button onClick={() => { setN(n + 1) }}>n+1</button> </div> <div>数量num {num} <button onClick={() => { setNum(num + 1) }}>num+1</button> </div> <div>价格price {price} <button onClick={() => { setPrice(price + 1) }}>price+1</button> </div> <div>小计1: {num * price} </div> <div>小计2: {total2()} </div> <div>小计3: {total3()} </div> <div>小计4: {total4} </div> </div> ) }
useRef()
const refContainer = useRef(initialValue);- 声明一个唯一的不能重复的变量, 他可以方便的保存任何可变的值
useRef返回一个可变的 ref 对象,其.current属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变- 当ref对象内容发生变化时, useRef并不会通知你, 变更 .current 属性不会引发组件重新渲染
import React, { useRef } from 'react'
export default function UseRef() {
let usernameInput = useRef()
return (
<div>
<div>name
<input type="text" ref={usernameInput}></input>
<button onClick={() => {
console.log("name:", usernameInput.current.value);
}}>取到input中的值</button>
</div>
</div>
)
}
useImperativeHandle
useImperativeHandle(ref, createHandle, [deps])- 可以让你在使用
ref时自定义暴露给父组件的实例值 - useImperativeHandle需要和
forwardRef一起使用 - 不能在函数式组件上直接使用ref属性,因为他们没有实例。
——————————————————————————————————子组件———————————————————————
import React, { useRef, forwardRef, useImperativeHandle } from 'react'
function Child4(props, ref) {
let userageInput = useRef()
//useImperativeHandle将此方法绑定到current属性上
useImperativeHandle(ref, () => ({
//这里返回的是父组件中current值
getUserage: () => {
return userageInput.current.value
}
}))
return (
<div>age:
<input type="text" ref={userageInput}></input>
</div>
)
}
export default forwardRef(Child4) // 高阶组件
//如果不使用forwardRef: Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
————————————————————————————父组件————————————————————————————
import React, { useRef } from 'react'
import Child4 from '../component/Child4'
export default function UseRef() {
let usernameInput = useRef()
let child4 = useRef() //给组件Child4声明一个唯一的名字
return (
<div>UseRef
<div>name
<input type="text" ref={usernameInput}></input>
<button onClick={() => {
console.log("name:", usernameInput.current.value);
}}>取到input中的值</button>
</div>
<Child4 ref={child4}></Child4>
<button onClick={() => {
console.log("age:", child4.current.getUserage());
}}>取到子组件中input中的值</button>
</div>
)
}
useLayoutEffect()
其函数签名与 useEffect 相同,但它会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。
尽可能使用标准的 useEffect 以避免阻塞视觉更新。
解释一下useEffect和useLayoutEffect的区别
- 举个简单的例子, 初始值为 hello world 的一个变量, 在useEffect和useLayoutEffect中分别被更改为了world hello. 会有什么变化?
- useEffect 是渲染完之后异步执行的,所以会导致 hello world 先被渲染到了屏幕上,再变成 world hello,就会出现闪烁现象。而 useLayoutEffect 是渲染之前同步执行的,所以会等它执行完再渲染上去,就避免了闪烁现象。也就是说我们最好把操作 dom 的相关操作放到 useLayouteEffect中去,避免导致闪烁。
vue中如何使用ref绑定组件
在JavaScript中需要通过document.querySelector("#demo")来获取dom节点,然后再获取这个节点的值。在Vue中,我们不用获取dom节点,元素绑定ref之后,直接通过this.$refs即可调用,这样可以减少获取dom节点的消耗
ref 有三种用法:
-
1、ref 加在普通的元素上,用this.$refs.name 获取到的是dom元素
-
2、ref 加在子组件上,用this.$refs.name 获取到的是组件实例,可以使用组件的所有方法。
-
3、利用 v-for 和 ref 获取一组数组或者dom 节点, 包含循环数组源
this.$refs 是一个对象,持有当前组件中注册过 ref特性的所有 DOM 元素和子组件实例(如有重名那么会被覆盖)
子组件:
<template>
<div>{{msg}}</div>
</template>
<script>
export default {
data() {
return {
msg: '我是子组件'
}
},
methods: {
changeMsg() {
this.msg = '变身'
}
}
}
</script>
父组件:
<template>
<div @click="parentMethod">
<children ref="children"></children>
</div>
</template>
<script>
import children from 'components/children.vue'
export default {
components: {
children
},
data() {
return {}
},
methods: {
parentMethod() {
this.$refs.children //返回一个对象
this.$refs.children.changeMsg() // 调用children的changeMsg方法
}
}
}
</script>
react中通过ref绑定组件
- 组件内的标签可以定义
ref属性来标识自己(相当于是从原生的id获取元素封装出来的)
3.1 字符串形式的ref (过时API)
-
所有在这个实例化对象的标签里定义了的
ref属性,都会收集到这个实例化对象的refs属性 -
定义:
<input ref="input1"/> -
使用:
this.refs.input1(获取到的是真实dom元素)
3.2 回调形式的ref
-
定义:
<input ref={ c => this.input1 = c } />(这个回调函数中的参数c就是当前ref所在的dom元素, 然后将获取到的dom元素直接放到组件实例自身上,所以可以用this.直接取值)
-
使用:
this.input1 -
回调执行次数: 内联的回调,渲染时调用一次,每次更新都会执行两次(第一次传入null,第二次传入参数DOM元素,这是因为每次渲染都会创建一个新的函数实例); 类绑定的回调<将ref回调定义成class的绑定函数>,就在初始渲染时调用一次. 但是不需要纠结,大多是情况下无关紧要
3.3 reateRef
-
定义:
myRef = React.createRef()(React.createRef调用后可以返回一个容器)标签中:
<input ref={this.myRef}/>(通过ref属性将容器附加到 React 元素) -
使用:
this.myRef.current -
注意: 该容器可以存储被ref所标识的节点,该容器是“专人专用”的, 也就是说如果第二个节点需要用的话只能重新创建一个ref容器
3.4 createRef和useRef
- useRef声明一个唯一的不能重复的变量, 他可以方便的保存任何可变的值,
useRef返回一个可变的 ref 对象,其.current属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变, 当ref对象内容发生变化时, useRef并不会通知你, 变更 .current 属性不会引发组件重新渲染
import React, { useRef } from 'react'
export default function UseRef() {
let usernameInput = useRef()
return (
<div>
<div>name
<input type="text" ref={usernameInput}></input>
<button onClick={() => {
console.log("name:", usernameInput.current.value);
}}>取到input中的值</button>
</div>
</div>
)
}
useRef仅能用在 FunctionComponent,createRef仅能用在 ClassComponent。
function App() {
// 错误用法,永远也拿不到 ref
const valueRef = React.createRef();
return <div ref={valueRef} />;
}
// 上述 valueRef 会随着 App 函数的 Render 而重复初始化,这也是 Hooks 的独特之处,虽然用在普通函数中,但在 React 引擎中会得到超出普通函数的表现,比如初始化仅执行一次,或者引用不变。
// 为什么 createRef 可以在 ClassComponent 正常运行呢?这是因为 ClassComponent 分离了生命周期,使例如 componentDidMount 等初始化时机仅执行一次。
说一下react的生命周期
| 生命周期 | 说明 |
|---|---|
| 组件挂载时 | |
| constructor (常用) | 构造函数,最先被执行,我们通常在构造函数里 (1)初始化state对象 (2)声明方法给自定义方法绑定this (3)接收props |
| getDerivedStateFromProps(静态方法) | static getDerivedStateFromProps(nextProps, prevState),这是个静态方法,当我们接收到新的属性想去修改state,可以使用 |
| render (常用) | 初始化渲染或更新渲染调用, 是纯函数,只返回需要渲染的东西,不应该包含其它的业务逻辑,可以返回原生的DOM、React组件、Fragment、Portals、字符串和数字、Boolean和null等内容 |
| componentDidMount (常用) | 组件挂载完毕的钩子,会在组件已经被渲染到 DOM 中后运行 (开启监听,发起ajax请求,设置计时器) |
| 数据更新时: | |
| getDerivedStateFromProps(静态方法) | 此方法在更新和挂载阶段都可能会调用 |
| shouldComponentUpdate | 控制组件更新的“阀门”,shouldComponentUpdate(nextProps, nextState),有两个参数nextProps和nextState,表示新的属性和变化之后的state,返回一个布尔值,true表示会触发重新渲染,false表示不会触发重新渲染,默认返回true,我们通常利用此生命周期来优化React程序性能(第一次页面挂载时不会使用,之后用于可以阻止更新) |
| render (常用) | 更新渲染时调用(new props-props属性更新时; setState状态更新时; forceUpdate强制更新数据时) |
| getSnapshotBeforeUpdate | getSnapshotBeforeUpdate(prevProps, prevState),这个方法在render之后,componentDidUpdate之前调用,有两个参数prevProps和prevState,表示之前的属性和之前的state,这个函数有一个返回值,会作为第三个参数传给componentDidUpdate,如果你不想要返回值,可以返回null,此生命周期必须与componentDidUpdate搭配使用 |
| componentDidUpdate(常用) | componentDidUpdate(prevProps, prevState, snapshot),该方法在getSnapshotBeforeUpdate方法之后被调用,有三个参数prevProps,prevState,snapshot,表示之前的props,之前的state,和snapshot。第三个参数是getSnapshotBeforeUpdate返回的,如果触发某些回调函数时需要用到 DOM 元素的状态,则将对比或计算的过程迁移至getSnapshotBeforeUpdate,然后在 componentDidUpdate中统一触发回调或更新状态。 |
| 组件卸载时 | |
| componentWillUnmount (常用) | 当组件被卸载或者销毁了就会调用,做一些收尾工作 (如清理定时器,取消网络请求,清理无效的DOM元素等垃圾清理工作) |
| 捕获父子组件异常时 | |
| componentDidCatch | 两个参数:error和info, 捕获到后代组件的错误 |
快速排序的原理
思路:
(1)在数据集之中,选择一个元素作为"基准"(pivot)。
(2)所有小于"基准"的元素,都移到"基准"的左边;所有大于"基准"的元素,都移到"基准"的右边。
(3)对"基准"左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止。
var list = [12,42,63,13,90,87,27];
function quickSort(arr){
if(arr.length <= 1){
return arr;
}//检查数组的元素个数,如果小于等于1,就返回
arr = arr.slice();//给arr重新复制一片空间,可以不影响原数组
var midIndex = parseInt(arr.length / 2);
var mid = arr.splice(midIndex,1)[0];//将得到的数组转化为数字
// 定义两个空数组,用来存放一左一右的两个子集
var leftArr = [];
var rightArr = [];
//开始遍历数组,大于mid的放到左边,小于的放到右边
for(var i=0;i<arr.length;i++){
if(mid <= arr[i]){
leftArr.push(arr[i]);
}else{
rightArr.push(arr[i]);
}
}
//利用递归每次得到中间值后都分别进行比较,直到左右元素为空或者只有一个值
return quickSort(leftArr).concat(mid,quickSort(rightArr));
}
console.log(list);//(7) [12, 42, 63, 13, 90, 87, 27]
console.log(quickSort(list));//(7) [90, 87, 63, 42, 27, 13, 12]
事件轮询
一个JavaScript代码的具体流程:
- 执行全局Script同步代码,这些同步代码有一些是同步语句,有一些是异步语句(比如setTimeout等);
- 全局Script代码执行完毕后,调用栈Stack会清空;
- 从微队列microtask queue中取出位于队首的回调任务,放入调用栈Stack中执行,执行完后microtask queue长度减1;
- 继续取出位于队首的任务,放入调用栈Stack中执行,以此类推,直到直到把microtask queue中的所有任务都执行完毕。注意,如果在执行microtask的过程中,又产生了microtask,那么会加入到队列的末尾,也会在这个周期被调用执行,直到microtask queue为空停止;
- microtask queue中的所有任务都执行完毕,此时microtask queue为空队列,调用栈Stack也为空;
- 取出宏队列macrotask queue中位于队首的任务 (时间相同先放先执行,时间不同谁快谁先执行),放入Stack中执行;
- 执行完毕后,调用栈Stack为空;
- 重复第3-7个步骤;(即每执行一个宏任务, 都要清空一次微任务队列, 以此类推至代码全部执行)
- 重复第3-7个步骤;
......可以看到,这就是浏览器的事件循环Event Loop
主线程
js的任务队列分为同步任务和异步任务,所有的同步任务都是在主线程里执行的,异步任务可能会在macrotask或者microtask里面。
宏任务
-
DOM渲染后触发 (ES 语法没有,JS 引擎不处理,浏览器或 nodejs 干预处理)
-
script(整体代码) setTimeout setInterval I/O UI交互事件 postMessage MessageChannel setImmediate(Node.js 环境)
微任务
-
DOM渲染前触发 (原因: ES 语法标准之内,JS 引擎来统一处理。即不用浏览器有任何干预,可一次性处理完,更快更及时。)
-
Promise.then Object.observe MutationObserver process.nextTick(Node.js 环境)
vue-router中有哪些组件
-
<router-link :to='' class='active-class'> //路由声明式跳转 ,active-class是标签被点击时的样式 <router-view> //渲染路由的容器 <keep-alive> //缓存组件
react中hook的优缺点
- 优点
- 1、更容易实现代码的复用: 在类组件中如果想实现代码的复用, 通常要使用到高阶组件, 但是高阶组件使用起来也有一定的问题, 比如HOC需要在原组件上进行包裹或嵌套, 大量时候会产生很多嵌套, 还有可能劫持props造成一定冲突; 而在react hooks中, 自定义hooks可以将组件中类似的状态逻辑抽取出来, 做成一个useHook. 这样在每次调用时就会有一份独立的存储空间. 而且hooks实现起来代码量比较少, 维护更容易
- 2、副作用统一数据, 没有生命周期: 在react hooks下, 没有生命周期函数, 几乎所有的操作都在
useEffect中进行处理. 而且这个useEffect的执行时机是在更新渲染更新之后异步执行的, 做到了数据处理逻辑和渲染分离 - 3、代码的可读性更强: 会让函数组件更简洁, 可读性更强
- 缺点:
- 1、useEffect滥使用, 导致代码混乱
- 2、react hooks会有闭包问题
vue3中组合式api的优势
- vue2是Options选项式API, 新增或者修改一个需求,就需要分别在data,methods,computed里修改
- 总结选项式API的坏处就是:代码碎片化、逻辑不复用
- vue3中使用Composition组合式API
- 代码集中化: 组合式API的组合就在于它把变量、函数集中在一起,减少分离掩盖的潜在的逻辑问题,不在“跳转”相关代码的选项块
- 逻辑高复用: 可以把一个功能所有的 methods、data 封装在一个独立的函数里,复用代码非常容易

浙公网安备 33010602011771号