tcx面试整理(1)

198 阅读14分钟

面试题整理

2. BFC

块级格式化上下文,是一个独立的渲染区域,让处于 BFC 内部的元素与外部的元素相互隔离,使内外元素的定位不会相互影响。

IE下为 Layout,可通过 zoom:1 触发

  • 触发条件:

    • 根元素

    • position: absolute/fixed

    • display: inline-block / table

    • float 元素

    • ovevflow !== visible

  • 规则:

    • 属于同一个 BFC 的两个相邻 Box 垂直排列

    • 属于同一个 BFC 的两个相邻 Box 的 margin 会发生重叠

    • BFC 中子元素的 margin box 的左边, 与包含块 (BFC) border box的左边相接触 (子元素 absolute 除外)

    • BFC 的区域不会与 float 的元素区域重叠

    • 计算 BFC 的高度时,浮动子元素也参与计算

    • 文字层不会被浮动层覆盖,环绕于周围

  • 应用:

    • 阻止margin重叠

    • 可以包含浮动元素 —— 清除内部浮动(清除浮动的原理是两个div都位于同一个 BFC 区域之中)

    • 自适应两栏布局

    • 可以阻止元素被浮动元素覆盖

4. 居中布局

  • 水平居中

    • 行内元素: text-align: center

    • 块级元素: margin: 0 auto

    • absolute + transform

    • flex + justify-content: center

  • 垂直居中

    • line-height: height

    • absolute + transform

    • flex + align-items: center

    • table

  • 水平垂直居中

    • absolute + transform

    • flex + justify-content + align-items

5. 选择器优先级

  • !important > 行内样式 > #id > .class > tag > * > 继承 > 默认

  • 选择器 从右往左 解析

6.去除浮动影响,防止父级高度塌陷

  • 通过增加尾元素清除浮动

    • :after / <br> : clear: both
  • 创建父级 BFC

  • 父级设置高度


7. 对象的拷贝

  • 浅拷贝: 以赋值的形式拷贝引用对象,仍指向同一个地址,修改时原对象也会受到影响

    • Object.assign

    • 展开运算符(...)

  • 深拷贝: 完全拷贝一个新对象,修改时原对象不再受到任何影响

    • JSON.parse(JSON.stringify(obj)): 性能最快

      • 具有循环引用的对象时,报错

      • 当值为函数、undefined、或symbol时,无法拷贝

    • 递归进行逐一赋值

13. 类型判断

判断 Target 的类型,单单用 typeof 并无法完全满足,这其实并不是 bug,本质原因是 JS 的万物皆对象的理论。因此要真正完美判断时,我们需要区分对待:

  • 基本类型(null): 使用 String(null)

  • 基本类型(string / number / boolean / undefined) + function: 直接使用 typeof即可

  • 其余引用类型(Array / Date / RegExp Error): 调用toString后根据[object XXX]进行判断

17. ES6/ES7

由于 Babel 的强大和普及,现在 ES6/ES7 基本上已经是现代化开发的必备了。通过新的语法糖,能让代码整体更为简洁和易读。

  • 声明

    • let / const: 块级作用域、不存在变量提升、暂时性死区、不允许重复声明

    • const: 声明常量,无法修改

  • 解构赋值

  • class / extend: 类声明与继承

  • Set / Map: 新的数据结构


21. 数组(array)

  • map: 遍历数组,返回回调返回值组成的新数组

  • forEach: 无法break,可以用try/catch中throw new Error来停止

  • filter: 过滤

  • some: 有一项返回true,则整体为true

  • every: 有一项返回false,则整体为false

  • join: 通过指定连接符生成字符串

  • push / pop: 末尾推入和弹出,改变原数组, 返回推入/弹出项

  • unshift / shift: 头部推入和弹出,改变原数组,返回操作项

  • sort(fn) / reverse: 排序与反转,改变原数组

  • concat: 连接数组,不影响原数组, 浅拷贝

  • slice(start, end): 返回截断后的新数组,不改变原数组

  • splice(start, number, value...): 返回删除元素组成的数组,value 为插入项,改变原数组

  • indexOf / lastIndexOf(value, fromIndex): 查找数组项,返回对应的下标

  • reduce / reduceRight(fn(prev, cur), defaultPrev): 两两执行,prev 为上次化简函数的return值,cur 为当前值(从第二项开始)

2. 生命周期

  • _init_

    • initLifecycle/Event,往vm上挂载各种属性

    • callHook: beforeCreated: 实例刚创建

    • initInjection/initState: 初始化注入和 data 响应性

    • created: 创建完成,属性已经绑定, 但还未生成真实dom

    • 进行元素的挂载: $el / vm.$mount()

    • 是否有template: 解析成render function

      • *.vue文件: vue-loader会将<template>编译成render function
    • beforeMount: 模板编译/挂载之前

    • 执行render function,生成真实的dom,并替换到dom tree中

    • mounted: 组件已挂载

  • update:

    • 执行diff算法,比对改变是否需要触发UI更新

    • flushScheduleQueue

      • watcher.before: 触发beforeUpdate钩子 - watcher.run(): 执行watcher中的 notify,通知所有依赖项更新UI
    • 触发updated钩子: 组件已更新

  • actived / deactivated(keep-alive): 不销毁,缓存,组件激活与失活

  • destroy:

    • beforeDestroy: 销毁开始

    • 销毁自身且递归销毁子组件以及事件监听

      • remove(): 删除节点

      • watcher.teardown(): 清空依赖

      • vm.$off(): 解绑监听

    • destroyed: 完成后触发钩子

6. vue-router

  • mode

    • hash

    • history

  • 跳转

    • this.$router.push()

    • <router-link to=""></router-link>

  • 占位

    • <router-view></router-view>

7. vuex

  • state: 状态中心

  • mutations: 更改状态

  • actions: 异步更改状态

  • getters: 获取状态

  • modules: 将state分成多个modules,便于管理


vue生命周期面试题

什么是vue生命周期?

Vue 实例从创建到销毁的过程,就是生命周期。也就是从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。

vue生命周期的作用是什么?

它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑。

vue生命周期总共有几个阶段?

它可以总共分为8个阶段:创建前/后, 载入前/后,更新前/后,销毁前/销毁后

第一次页面加载会触发哪几个钩子?

第一次页面加载时会触发 beforeCreate, created, beforeMount, mounted 这几个钩子

DOM 渲染在 哪个周期中就已经完成?

DOM 渲染在 mounted 中就已经完成了

简单描述每个周期具体适合哪些场景?

生命周期钩子的一些使用方法: beforecreate : 可以在这加个loading事件,在加载实例时触发 created : 初始化完成时的事件写在这里,如在这结束loading事件,异步请求也适宜在这里调用 mounted : 挂载元素,获取到DOM节点 updated : 如果对数据统一处理,在这里写上相应函数 beforeDestroy : 可以做一个确认停止事件的确认框 nextTick : 更新数据后立即操作dom

arguments是一个伪数组,没有遍历接口,不能遍历

Vue 开发必须知道的 36 个技巧

2.watch

2.1 常用用法

1.场景:表格初始进来需要调查询接口 getList(),然后input 改变会重新查询

created(){

this.getList()

},

watch: {

inpVal(){

this.getList()

}

}

复制代码

2.2 立即执行

2.可以直接利用 watch 的immediate和handler属性简写

watch: {

inpVal:{

handler: 'getList',

immediate: true

}

}

复制代码

2.3 深度监听

3.watch 的 deep 属性,深度监听,也就是监听复杂数据类型

watch:{

inpValObj:{

handler(newVal,oldVal){

console.log(newVal)

console.log(oldVal)

},

deep:true

}

}


3. 14种组件通讯

3.1 props

这个应该非常属性,就是父传子的属性; props 值可以是一个数组或对象;

// 数组:不建议使用

props:[]

// 对象

props:{

inpVal:{

type:Number, //传入值限定类型

// type 值可为String,Number,Boolean,Array,Object,Date,Function,Symbol

// type 还可以是一个自定义的构造函数,并且通过 instanceof 来进行检查确认

required: true, //是否必传

default:200, //默认值,对象或数组默认值必须从一个工厂函数获取如 default:()=>[]

validator:(value) {

// 这个值必须匹配下列字符串中的一个

return ['success', 'warning', 'danger'].indexOf(value) !== -1

}

}

}

复制代码

3.2 $emit

这个也应该非常常见,触发子组件触发父组件给自己绑定的事件,其实就是子传父的方法

// 父组件

<home @title="title">

// 子组件

this.$emit('title',[{title:'这是title'}])

复制代码

3.3 vuex

1.这个也是很常用的,vuex 是一个状态管理器 2.是一个独立的插件,适合数据共享多的项目里面,因为如果只是简单的通讯,使用起来会比较重 3.API

state:定义存贮数据的仓库 ,可通过this.$store.state 或mapState访问

getter:获取 store 值,可认为是 store 的计算属性,可通过this.$store.getter 或

mapGetters访问

mutation:同步改变 store 值,为什么会设计成同步,因为mutation是直接改变 store 值,

vue 对操作进行了记录,如果是异步无法追踪改变.可通过mapMutations调用

action:异步调用函数执行mutation,进而改变 store 值,可通过 this.$dispatch或mapActions

访问

modules:模块,如果状态过多,可以拆分成模块,最后在入口通过...解构引入


3.13 路由传参

1.方案一

// 路由定义

{

path: '/describe/:id',

name: 'Describe',

component: Describe

}

// 页面传参

this.$router.push({

path: `/describe/${id}`,

})

// 页面获取

this.$route.params.id

复制代码

2.方案二

// 路由定义

{

path: '/describe',

name: 'Describe',

component: Describe

}

// 页面传参

this.$router.push({

name: 'Describe',

params: {

id: id

}

})

// 页面获取

this.$route.params.id

复制代码

3.方案三

// 路由定义

{

path: '/describe',

name: 'Describe',

component: Describe

}

// 页面传参

this.$router.push({

path: '/describe',

query: {

id: id

`}

)

// 页面获取

this.$route.query.id

复制代码

4.三种方案对比 方案二参数不会拼接在路由后面,页面刷新参数会丢失 方案一和三参数拼接在后面,丑,而且暴露了信息


30.4 路由模式

设置 mode 属性:hash或 history

30.5 Vue.$router

this.$router.push():跳转到不同的url,但这个方法回向history栈添加一个记录,点击后退会返回到上一个页面

this.$router.replace():不会有记录

this.$router.go(n):n可为正数可为负数。正数返回上一个页面,类似 window.history.go(n)

复制代码

30.6 Vue.$route

表示当前跳转的路由对象,属性有: name:路由名称 path:路径 query:传参接收值 params:传参接收值 fullPath:完成解析后的 URL,包含查询参数和 hash 的完整路径 matched:路由记录副本 redirectedFrom:如果存在重定向,即为重定向来源的路由的名字

this.$route.params.id:获取通过 params 或/:id传参的参数

this.$route.query.id:获取通过 query 传参的参数


框架: React

React 的核心流程可以分为两个部分:

  • reconciliation (调度算法,也可称为 render):

    • 更新 state 与 props;

    • 调用生命周期钩子;

    • 生成 virtual dom;

      • 这里应该称为 Fiber Tree 更为符合;
    • 通过新旧 vdom 进行 diff 算法,获取 vdom change;

    • 确定是否需要重新渲染

  • commit:

    • 如需要,则操作 dom 节点更新;


2. 生命周期

在新版本中,React 官方对生命周期有了新的 变动建议:

  • 使用getDerivedStateFromProps 替换componentWillMount;

  • 使用getSnapshotBeforeUpdate替换componentWillUpdate;

  • 避免使用componentWillReceiveProps;

其实该变动的原因,正是由于上述提到的 Fiber。首先,从上面我们知道 React 可以分成 reconciliation 与 commit 两个阶段,对应的生命周期如下:

  • reconciliation:

    • componentWillMount

    • componentWillReceiveProps

    • shouldComponentUpdate

    • componentWillUpdate

  • commit:

    • componentDidMount

    • componentDidUpdate

    • componentWillUnmount

新版的建议生命周期如下:

class Component extends React.Component {

// 替换 `componentWillReceiveProps` ,

// 初始化和 update 时被调用

// 静态函数,无法使用 this

static getDerivedStateFromProps(nextProps, prevState) {}

// 判断是否需要更新组件

// 可以用于组件性能优化

shouldComponentUpdate(nextProps, nextState) {}

// 组件被挂载后触发

componentDidMount() {}

// 替换 componentWillUpdate

// 可以在更新之前获取最新 dom 数据

getSnapshotBeforeUpdate() {}

// 组件更新后调用

componentDidUpdate() {}

// 组件即将销毁

componentWillUnmount() {}

// 组件已销毁

componentDidUnMount() {}

}

  • 使用建议:

    • 在constructor初始化 state;

    • 在componentDidMount中进行事件监听,并在componentWillUnmount中解绑事件;

    • 在componentDidMount中进行数据的请求,而不是在componentWillMount;

    • 需要根据 props 更新 state 时,使用getDerivedStateFromProps(nextProps, prevState);

      • 旧 props 需要自己存储,以便比较;
  • public static getDerivedStateFromProps(nextProps, prevState) {

  • // 当新 props 中的 data 发生变化时,同步更新到 state 上

  • if (nextProps.data !== prevState.data) {

  • return {

  • data: nextProps.data

  • }

  • } else {

  • return null1

  • }

  • }

复制代码

  • 可以在componentDidUpdate监听 props 或者 state 的变化,例如:

componentDidUpdate(prevProps) {

// 当 id 发生变化时,重新获取数据

if (this.props.id !== prevProps.id) {

this.fetchData(this.props.id);

}

}

复制代码

  • 在componentDidUpdate使用setState时,必须加条件,否则将进入死循环;

  • getSnapshotBeforeUpdate(prevProps, prevState)可以在更新之前获取最新的渲染数据,它的调用是在 render 之后, update 之前;

  • shouldComponentUpdate: 默认每次调用setState,一定会最终走到 diff 阶段,但可以通过shouldComponentUpdate的生命钩子返回false来直接阻止后面的逻辑执行,通常是用于做条件渲染,优化渲染的性能。

3. setState

 setState: React 中用于修改状态,更新视图。它具有以下特点:

异步与同步: setState并不是单纯的异步或同步,这其实与调用时的环境相关:

  • 合成事件生命周期钩子(除 componentDidUpdate) 中,setState是"异步"的;

    • 原因: 因为在setState的实现中,有一个判断: 当更新策略正在事务流的执行中时,该组件更新会被推入dirtyComponents队列中等待执行;否则,开始执行batchedUpdates队列更新;

      • 在生命周期钩子调用中,更新策略都处于更新之前,组件仍处于事务流中,而componentDidUpdate是在更新之后,此时组件已经不在事务流中了,因此则会同步执行;

      • 在合成事件中,React 是基于 事务流完成的事件委托机制 实现,也是处于事务流中;

    • 问题: 无法在setState后马上从this.state上获取更新后的值。

    • 解决: 如果需要马上同步去获取新值,setState其实是可以传入第二个参数的。setState(updater, callback),在回调中即可获取最新值;

  • 原生事件setTimeout 中,setState是同步的,可以马上获取更新后的值;

    • 原因: 原生事件是浏览器本身的实现,与事务流无关,自然是同步;而setTimeout是放置于定时器线程中延后执行,此时事务流已结束,因此也是同步;

批量更新: 在 合成事件生命周期钩子 中,setState更新队列时,存储的是 合并状态(Object.assign)。因此前面设置的 key 值会被后面所覆盖,最终只会执行一次更新;

函数式: 由于 Fiber 及 合并 的问题,官方推荐可以传入 函数 的形式。setState(fn),在fn中返回新的state对象即可,例如this.setState((state, props) => newState);

  • 使用函数式,可以用于避免setState的批量更新的逻辑,传入的函数将会被 顺序调用

注意事项:

  • setState 合并,在 合成事件 和 生命周期钩子 中多次连续调用会被优化为一次;

  • 当组件已被销毁,如果再次调用setState,React 会报错警告,通常有两种解决办法:

    • 将数据挂载到外部,通过 props 传入,如放到 Redux 或 父级中;

    • 在组件内部维护一个状态量 (isUnmounted),componentWillUnmount中标记为 true,在setState前进行判断;


5. Redux

Redux 是一个 数据管理中心,可以把它理解为一个全局的 data store 实例。它通过一定的使用规则和限制,保证着数据的健壮性、可追溯和可预测性。它与 React 无关,可以独立运行于任何 JavaScript 环境中,从而也为同构应用提供了更好的数据同步通道。

  • 核心理念:

    • 单一数据源: 整个应用只有唯一的状态树,也就是所有 state 最终维护在一个根级 Store 中;

    • 状态只读: 为了保证状态的可控性,最好的方式就是监控状态的变化。那这里就两个必要条件:

      • Redux Store 中的数据无法被直接修改;

      • 严格控制修改的执行;

    • 纯函数: 规定只能通过一个纯函数 (Reducer) 来描述修改;

  • 大致的数据结构如下所示:

  • 理念实现:

    • Store: 全局 Store 单例, 每个 Redux 应用下只有一个 store, 它具有以下方法供使用:

      • getState: 获取 state;

      • dispatch: 触发 action, 更新 state;

      • subscribe: 订阅数据变更,注册监听器;

  • // 创建

  • const store = createStore(Reducer, initStore)

复制代码

  • Action: 它作为一个行为载体,用于映射相应的 Reducer,并且它可以成为数据的载体,将数据从应用传递至 store 中,是 store 唯一的数据源

// 一个普通的 Action

const action = {

type: 'ADD_LIST',

item: 'list-item-1',

}

// 使用:

store.dispatch(action)

// 通常为了便于调用,会有一个 Action 创建函数 (action creater)

funtion addList(item) {

return const action = {

type: 'ADD_LIST',

item,

}

}

// 调用就会变成:

dispatch(addList('list-item-1'))

复制代码

  • Reducer: 用于描述如何修改数据的纯函数,Action 属于行为名称,而 Reducer 便是修改行为的实质;

// 一个常规的 Reducer

// @param {state}: 旧数据

// @param {action}: Action 对象

// @returns {any}: 新数据

const initList = []

function ListReducer(state = initList, action) {

switch (action.type) {

case 'ADD_LIST':

return state.concat([action.item])

break

defalut:

return state

}

}

复制代码

注意:

  • 遵守数据不可变,不要去直接修改 state,而是返回出一个 新对象,可以使用 assign / copy / extend / 解构 等方式创建新对象;

  • 默认情况下需要 返回原数据,避免数据被清空;

  • 最好设置 初始值,便于应用的初始化及数据稳定;

  • 进阶:

    • React-Redux: 结合 React 使用;

      • <Provider>: 将 store 通过 context 传入组件中;

      • connect: 一个高阶组件,可以方便在 React 组件中使用 Redux;

        • 将store通过mapStateToProps进行筛选后使用props注入组件

        • 根据mapDispatchToProps创建方法,当组件调用时使用dispatch触发对应的action

    • Reducer 的拆分与重构:

      • 随着项目越大,如果将所有状态的 reducer 全部写在一个函数中,将会 难以维护

      • 可以将 reducer 进行拆分,也就是 函数分解,最终再使用combineReducers()进行重构合并;

    • 异步 Action: 由于 Reducer 是一个严格的纯函数,因此无法在 Reducer 中进行数据的请求,需要先获取数据,再dispatch(Action)即可,下面是三种不同的异步实现:

作者:汤晨旭
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。