【前端】-近期面试题

setup

setup 中的代码与普通的 <script> 只在组件被首次引入的时候执行一次不同,<script setup> 中的代码会在每次组件实例被创建的时候执行。 所以,任何在 <script setup> 声明的顶层的绑定 (包括变量,函数声明,以及 import 导入的内容) 都能在模板中直接使用 。有方法通过 import 导入,放在 setup 中,就无需通过 methods 暴露,可以直接用

 

HOC
  1. 初期(黑命贵之前)的 react 通过 React.createdClass 方法创建组件,可以加 mixins 属性,和 vue 一样,后来不用了。后来还有 Component.prototype[key] = mixin[key]这种继承的方式。
  2. 换成后来这种 hoc(com)的方式后,需要注意包装顺序,内层的 hoc 距离原组件最近,然后逐层向外。
  3. hoc 如果不用传参,就像 withRouter,只需要写一层 return 就行。如果参数多,类似 mapStateToProps 等,就可能会 return 多次,一层 return 套着一层 return,其实没事区别。
  4. 写 hoc 时,也有两种写法。一个是正向属性代理:
class Index extends React.Component{
  render(){
    return <div> hello,world </div>
    }
  }
Index.say = function(){
  console.log('my name is alien')
}
function HOC(Component) {
  return class wrapComponent extends React.Component{
    render(){
      return <Component { ...this.props } { ...this.state } />
    }
  }
} 
const newIndex = HOC(Index) console.log(newIndex.say)

这个比较常见,还有一个是反向的组件继承:

class Index extends React.Component{
  render(){
    return <div> hello,world </div>
    }
  }
Index.say = function(){
  console.log('my name is alien')
}
function HOC(Component) {
  return class wrapComponent extends Component{
  }
}
const newIndex = HOC(Index) console.log(newIndex.say)

第一种是想组件里加东西,第二种直接继承。

 

 

vue 新版路由主要变化

new Router 改为 createRouter。mode 的变化 。base 属性改到了 createWebHistory 的第一个参数。通配符路由没了,改为正则,404 的判断是 path: '/:path(.*)*'

 

新版 vue 父组件调用子组件
  1. 首先用 ref 引用子组件<Son ref="sonRef"></Son>,通过 const sonRef = ref(null)引用
  2. 子组件可以用之前的选项式 api 的方式,默认暴露整个组件的内容。在 setup 方式的组件中,默认是关闭的,引用组件后不会暴露 setup 中的内容,所以需要 defineExpose 方法,defineExpose({ prop1, prop2, fn1, fn2 }),指定那些属性和方法需要暴露。

 

pinia 的使用
  1. 与 vuex 的不同,通过 defineStore 来创建 Store,第一个参数是 id,这是唯一的,用来将 store 连接到 devtools。store 中有 state、getter、action。
  2. 在项目的入口文件 main.js 或者 main.ts 中通过 createPinia 方法创建一个 Pinia 实例,然后 app.use()
  3. 在 store 目录下可以创建多个文件,对应多个 store。Pinia 中没有 module 的概念,是一个拍平的 store 结构。Pinia 推荐按照功能去划分一个个的 store,这样更方便管理和使用。 一个文件一个 store,通过 defineStore 方法创建,对应的函数名的风格是 useXXXStore,第一个参数是 id,后面是 state、getter、action
  4. pinia的持久化存储,除了自己写也可以用插件,比如pinia-plugin-persistedstate。先安装,然后pinia.use(piniaPluginPersistedstate),在defineStore时配置persist选项,直接写true或者写对象来控制,其实还是存在本地了。
const useUserStore = defineStore('user', {
  // 也可以直接写 persist: true
  persist: { 
    key: "USER",
    storage: sessionStorage, 
    paths: ["token"]
  }
})

  1. 总结一下发展历程:
    1. vuex 的 commit(mutation),在 mutation 中直接修改 state
    2. redux 中 dispatch(action),在 reducer 中 return 新的 state,“只换不修”
    3. pinia 和 rtk 中的直接修改 state 中的属性,通过代理对象拦截赋值操作,看起来是可修改的,其实不可修改。

 

RTK-redux toolkit

这是 redux 的工具包,简化了 redux,或者说标准化了 redux。核心概念仍然是 store、state、reducer、action。

rtk 有两个关键方法,configureStore 和 createSlice。

import { createSlice, configureStore } from '@reduxjs/toolkit'
const initialState = { name: "coderlzw", age: 20 }
const userReducer = createSlice({
  name: "user",
  initialState: initialState,
  reducer: {
    setName (state, action) {
      state.name = action.payload
    }
  }
})
const store = configureStore({
  reducer: { user: userReducer.reducer },
  devTools: true
})
export const { setName } = userReducer.actions
export default store

createSlice 的作用是创建一个片段,里面的 reducer 是通过 immmer 库生成的,可以保证数据“不变”,所以在它的 reducer 中可以使用“可变”的语法,比如 state.value = xxx 这种类似 vuex 风格的语法更简单,也能达到 redux 那种效果,这依赖于 createSlice。另外 createSlice 还会自动生成 reducer 对应的 action 以及 type,使代码更少。immmer 库是基于 promise 的,直接修改属性时会拦截并返回新对象。

 

通过ts封装axios
import axios from "axios";
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'


class MyHttp {
    // 这个instance只定义了类型,在构造函数里给的值
    instance: AxiosInstance
    baseConfig: AxiosRequestConfig = {baseURL: '/api', timeout: 5000}


    constructor(config: AxiosRequestConfig) {
        this.instance = axios.create(Object.assign(this.baseConfig, config))


        this.instance.interceptors.request.use((config: any) => {
            // const token = localStorage.getItem("token") as string
            if(token) {
              config.headers!.Authorization = token;
            }
            return config
        }, (err:any) => {
            return Promise.reject(err)
        })
    }


    public request(config: AxiosRequestConfig): Promise<AxiosResponse> {
        return this.instance.request(config)
    }
    public get<T=any> (url: string, config?: AxiosRequestConfig): Promise<AxiosResponse> {
        return this.instance.get(url, config)
    }
    public post<T=any>(url: string, data?: object, config?: AxiosRequestConfig): Promise<AxiosResponse> {
        return this.instance.post(url, data, config)
    }
}


const http = new MyHttp({});
export default http;

 

 

全屏相关
  1. js包的名字:be-full,方法有beFull、isFull、exitFull
  2. 全屏监听事件:resize、blur+focus(监听tab切屏)、visibilitychange(监听浏览器标签页的切换)、contextmenu。

 

 

什么是宏任务和微任务
  • 宏任务包括:setTimeout setInterval Ajax DOM事件
  • 微任务:Promise async/await
  • 微任务比宏任务的执行时间要早
同步和异步

JS是单线程执行的语言,在同一个时间只能做一件事情。这就导致后面的任务需要等到前面的任务完成才能执行,如果前面的任务很耗时就会造成后面的任务一直等待。为了解决这个问题JS中出现了同步任务和异步任务。

同步任务:

在主线程上排队执行的任务只有前一个任务执行完毕,才能执行后一个任务,形成一个执行栈。

异步任务:

不进入主线程,而是进入任务队列,当主线程中的任务执行完毕,就从任务队列中取出任务放进主线程中来进行执行。由于主线程不断重复的获得任务、执行任务、再获取再执行,所以者种机制被叫做事件循环(Event Loop)

我们都知道 Js 是单线程的,但是一些高耗时操作带来了进程阻塞的问题。为了解决这个问题,Js 有两种任务的执行模式:同步模式(Synchronous)和异步模式(Asynchronous)

在异步模式下,创建异步任务主要分为宏任务与微任务两种。ES6 规范中,宏任务(Macrotask) 称为 Task, 微任务(Microtask) 称为 Jobs。

 

宏任务是由宿主(浏览器、Node)发起的,而微任务由 JS 自身发起。

宏任务(Macrotask)大概如下:setTimeout setInterval MessageChannel I/O

setImmediate(Node环境) script(整体代码块)

微任务(Microtask)大概如下:MutationObserver(浏览器环境) promise.[ then/catch/finally ]

事件队列 process.nextTick(Node环境)

 

 

组件挂载时

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

  • constructor()
  • static getDerivedStateFromProps()
  • render()
  • componentDidMount()
组件更新时

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

  • static getDerivedStateFromProps()
  • shouldComponentUpdate()
  • render()
  • getSnapshotBeforeUpdate()
  • componentDidUpdate()
组件卸载时

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

  • componentWillUnmount()
错误处理

当渲染过程,生命周期或子组件的构造函数中抛出错误时,会调用如下方法:

  • static getDerivedStateFromProps()
  • componentDidCatch()

 

 

new的过程发生了什么?

1、new先创建了一个空对象

var news = new Object()

 

2、我们将这个空对象的__proto__属性指向whatnew构造函数的prototype属性,使两者都有共同的原型对象whatnew.prototype,因此空对象可以访问whatnew函数

new.__proto__ = whatnew.prototype

 

3、修改whatnew的this到object上面,call/apply,并把参数传入

4、最后把object返回给mynew就可以了

 

微前端的实现方案

什么是微服务?将一个单体应用,按照一定的规则拆分为一组服务。这些服务,各自拥有自己的仓库,可以独立开发、独立部署,有独立的边界,可以由不同的团队来管理,甚至可以使用不同的编程语言来编写。但对前端来说,仍然是一个完整的服务。以前是按照组件拆分,现在是按服务拆分。

 

1. 路由分发式微前端, 比如最常用的方案是通过 HTTP 服务的反向代理来实现。nginx--反向代理,不同页面的请求就可以分发到不同的服务器上。

2. iframe

3. single-spa 在 single-spa 方案中,应用被分为两类:基座应用和子应用。single-spa 会在基座应用中维护一个路由注册表,每个路由对应一个子应用。基座应用启动以后,当我们切换路由时,如果是一个新的子应用,会动态获取子应用的 js 脚本,然后执行脚本并渲染出相应的页面;如果是一个已经访问过的子应用,那么就会从缓存中获取已经缓存的子应用,激活子应用并渲染出对应的页面。

4. qiankun 是在 single-spa 的基础上做了二次开发,在框架层面解决了使用 single-spa 时需要开发人员自己编写子应用加载、通信、隔离等逻辑的问题,是一种比 single-spa 更优秀的微前端方案。

5. webpack5 以及 Web Component

 

 

vue中父组件如何监听到子组件的生命周期

1. // Child.vue

mounted() {

this.$emit("mounted");

}

 

2. // Parent.vue

<Child @hook:mounted="doSomething" ></Child>

 

 

 

 

posted @ 2023-05-10 10:19  心砚  阅读(93)  评论(0编辑  收藏  举报