面试集锦(二)

面试集锦(二)

1.React相关知识

1.1 react生命周期

react生命周期分为三个阶段:装载过程(Mount)、更新过程(update)、卸载过程(unmount)。

参考:https://blog.csdn.net/mafan121/article/details/77965379

1.2 react父子组件传参

父组件向子组件传参:props。

在父组件中向子组件添加属性,子组件通过this.props获取。

子组件向父组件传参:回调函数。

在父组件向子组件添加事件,子组件调用父组件事件并将参数带回。

1.3 react高阶组件HOC

HOC是一个函数,其接收一个组件作为参数,返回一个增强的组件。

高阶组件主要被用于:代码模块化、代码复用、修改props、劫持渲染(操作state)。

常用的实现方式有:

  • 属性代理(Props Proxy):高阶组件操控传递给 WrappedComponent 的 props。
  • 反向继承(Inheritance Inversion)高阶组件继承(extends)WrappedComponent。

示例:

import React, { Component } from 'react'

const withStorage = WrappedComponent => {
  return class extends Component{
    componentWillMount() {
      let data = localStorage.getItem('data')
      this.setState({ data })
    }

    render() {
      return <WrappedComponent data={this.state.data} {...this.props} /> 
    }
  }
}

export default withStorage

这里采用属性代理的方式定义了一个高阶组件,其每个传入组件加载前都将去拿取localStoragedata数据,避免了在每个组件中写componentWillMount,提高了代码复用率。

1.4 react的children嵌套

react中任何组件都有一个children属性。他表示当前组件下的子元素,有点类似于innerHTML属性

this.props.children的值有3种类型:undefined(无子节点)、object(一个子节点)、Array(多个子节点)。

常用的方法:

  • React.Children.count(object children) :返回子节点个数
  • React.Children.map(object children, function fn [, object context]):遍历children子元素,执行fn,并返回结果集。
  • React.Children.forEach(object children, function fn [, object context]):仅仅遍历子元素执行fn,无结果返回。
  • React.Children.only(object children):返回children中仅有的子级,否则跑出异常。

1.5 父子组件如何事件互调

父组件调用子组件事件:refs。

为子组件绑定ref属性,在需要用到的时候通过this.props.refs属性获取到子组件,然后子组件就可以调用自己的事件了。

子组件调用父组件事件:props。

在父组件为子组件绑定事件,在子组件中通过this.props.事件名调用。

1.6 react diff算法原理

react diff算法分为3大策略,简化了比较的复杂度。

  1. Tree diff :通过updateDepthVirtual DOM树进行层级控制,两棵树只进行同层节点比较,这样只需一次遍历即可完成整颗树的比较

  2. component diff:依旧根据层级比较,同类型的组件可通过shouldComponentUpdate判断是否更新,不同类型组件直接替换。

  3. element diff:对同层的节点,可根据key值进行删除、插入、移动。

    通过这三大策略,所有的比较可操作均在同一层级完成,大大节省了比较重绘时间。

1.7 react router

window.location.hash:获取url中的hash值,即#之后的部分。

hash值的改变并不会引起浏览器发送请求,所以可以借鉴这一原理,来实现页面的前端切换

但是含有#的url不太符合我们的要求,我们期望url中的路由都是以/拼接的。这就相当于url整体变更了,那么页面也将重新渲染。在Html5中history API提出了window.history.pushStatewindow.history.replaceState方法,其可以让我们的url达到#类似的效果,url改变也不刷新页面,仅仅从历史记录中查找更新组件。

react router利用封装了window.history的第三方库history,可以用来兼容不同的浏览器和环境。主要包括以下3类history:

  • createHashHistory:老版浏览器,主要使用hash来实现。
  • createBrowserHistory:支持H5的浏览器,主要使用h5中的history来实现。
  • createMemoryHistory:node环境下,使用memeory存储。

执行url前进

  • createBrowserHistory: pushState()、replaceState()
  • createHashHistory: location.hash= window.location.replace()
  • createMemoryHistory: 在内存中进行历史记录的存储

检查url回退

  • createBrowserHistory: popstate
  • createHashHistory: hashchange
  • createMemoryHistory:不涉及UI,直接在历史记录中查找回退

createBrowserHistory/createHashHistory将state存储在sessionStorage中,方便state传递获取。

1.8 setState原理

setState(newState,fn)

或者

setState((prevState,prevProps)=>{
	return newState
},fn)

setState的第一个参数,也可以接受一个函数,该函数,接收上一次的状态和props作为参数

  • setState只在合成时间和钩子函数中是“异步”的,在原生事件和setTimeout中是同步的。
  • setState的“异步”并不是说内部由异步代码实现,其实本身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,形式了所谓的“异步”,当然可以通过第二个参数 setState(partialState, callback) 中的callback拿到更新后的结果。
  • setState 的批量更新优化也是建立在“异步”(合成事件、钩子函数)之上的,在原生事件和setTimeout 中不会批量更新,在“异步”中如果对同一个值进行多次 setState , setState 的批量更新策略会对其进行覆盖,取最后一次的执行,如果是同时 setState 多个不同的值,在更新时会对其进行合并批量更新。

在这里插入图片描述
setState工作流程:

  • 将传入的newState合并到当前state,并放入状态队列中
  • 检查是否需要批量更新,如果需要,则将当前组件实例放入dirtyComponent
  • 当所有需要变更的组件收集完成后,遍历dirtyComponent,对每个组件进行状态更新。
  • updateComponent时会比较前后状态是否一致,不一致则重新渲染,否则不重绘。
class Example extends React.Component {
  constructor() {
    super();
    this.state = {
      val: 0
    };
  }
  
  componentDidMount() {
    this.setState({val: this.state.val + 1});
    console.log(this.state.val);    // 第 1 次 log

    this.setState({val: this.state.val + 1});
    console.log(this.state.val);    // 第 2 次 log

    setTimeout(() => {
      this.setState({val: this.state.val + 1});
      console.log(this.state.val);  // 第 3 次 log

      this.setState({val: this.state.val + 1});
      console.log(this.state.val);  // 第 4 次 log
    }, 0);
  }
  render() {
    return null;
  }
};

输出0 0 2 3

  • 因为setState在同一进程中会合并状态,且本身是异步操作,所以1,2次赋值会被合并,主线程打印1、2次时,赋值未完成。
  • 当执行setTimeout时,1、2次赋值已执行完成,此时val=1。因为setTimeout进入了新的异步队列,和1、2次赋值并不在同一进程所以不会与1、2次赋值合并。
  • 因为在setTimeout中setState是同步的,所以依次打印2,3

1.9 react强制渲染

默认情况下,当组件的state或props改变时,组件将重新渲染。如果你的render()方法依赖于一些其他的数据,你可以告诉React组件需要通过调用forceUpdate()重新渲染。

forceUpdate会跳过shouldComponentUpdate直接执行render()

2.redux相关知识

2.1 redux原理

redux:component --> dispatch(action) --> reducer --> subscribe --> getState --> component

参考:https://blog.csdn.net/mafan121/article/details/72673815

react-redux:component --> actionCreator(data) --> reducer --> component

connect(state => state, action)(Component);

参考:https://blog.csdn.net/mafan121/article/details/72830491

dva: dva=react-router+redux+redux-saga

2.2 redux 优缺点

  • 一个组件的所有数据都必须从父组件传递过来,不能像flux,从store中直接获取。

  • 当一个组件相关的数据更新时,即使父组件不需要用到这个组件,父组件依旧需要重新render,这将导致渲染效率降低,需要通过shouldComponentUpdate来优化。

  • redux将数据流规范了,所有数据均在store中管理,避免了父子组件不易传参的问题。

  • redux将流程规范了,减少了手动编码量。

2.3 redux-thunk和redux-saga

副作用*:调用函数时,除了返回值,还对主函数产生了附加的影响。

除了返回值外,还做了其他的影响。
凡是跟外部环境存在的交互都属于副作用。

redux-saga
redux-saga是基于sagas模式,通过Generator函数来实现异步协调操作的。它在项目启动的时候会监听action,当特定的action被dispatch时会唤醒saga。

saga是由多个effect组成的,一个effect就是一个js链式对象,其包含了一些可以被saga middleware执行的指令。

call、put、take都是saga提供的effect创建器,他们都会生成一个effect对象,然后将其交由saga middleware处理。

使用redux-saga:

  1. 注册saga
// 创建redux-saga中间件
const sagaMiddleware = createSagaMiddleware()
// 生成store
const store = createStore(rootReducer, applyMiddleware(sagaMiddleware))
// 执行redux-saga中间件
sagaMiddleware.run(rootSaga)
  1. 创建saga
export function* rootSaga() {
    while(true) {
        yield take('FETCH_USER');
        const { data } =  yield call(axios.post, 'http://rest.learncode.academy/api/wetern/users');
        yield put({ type: 'UPDATE_USER', payload: data })
    }
}

saga必定是一个*函数,每个effect指令都应yield处理。

优点:

  1. 声明式 Effects:所有的操作以JavaScript对象的方式被 yield,并被 middleware 执行。使得在 saga 内部测试变得更加容易,可以通过简单地遍历 Generator 并在 yield 后的成功值上面做一个 deepEqual 测试。
  2. 高级的异步控制流以及并发管理:可以使用简单的同步方式描述异步流,并通过 fork 实现并发任务。
  3. 架构上的优势:将所有的异步流程控制都移入到了 sagas,UI 组件不用执行业务逻辑,只需 dispatch action 就行,增强组件复用性。

redux-thunk
redux-thunk也是对action层做了副作用处理,它允许我们接受一个函数作为action,但是这个函数action必须接收dispatch作为参数,我们可以在函数action中处理异步操作。

当action为函数时会执行函数内部逻辑,从而达到异步效果

使用redux-thunk
1.注册中间件

const middleware = applyMiddleware(thunk);

2.定义action函数

export default ()=>(dispatch)=>{
		//异步逻辑处理
        //发送同步action更新状态
        dispatch({type:'init',data:data});
};

缺点:

  1. action 虽然扩展了,但因此变得复杂,后期可维护性降低;
  2. thunks 内部测试逻辑比较困难,需要mock所有的触发函数;
  3. 协调并发任务比较困难,当自己的 action 调用了别人的 action,别人的 action 发生改动,则需要自己主动修改;
  4. 业务逻辑会散布在不同的地方:启动的模块,组件以及thunks内部。

3.css布局

3.1 水平居中

内联元素:text-align:center;

块级元素:margin:0 auto; 或者绝对定位。

flex居中:display: flex; justify-content: center;

参考:https://blog.csdn.net/mafan121/article/details/53925555

3.2 垂直居中

单行元素:line-height:行高;

多行元素:绝对定位

flex居中:display: flex; align-items: center;

参考:https://blog.csdn.net/mafan121/article/details/53925555

3.3 左右布局

  • flex居中:flex:1;

  • calc计算:calc(100%-200px);

  • 浮动布局:float:left;

  • 定位布局:position:absolute;

参考:https://blog.csdn.net/mafan121/article/details/100057888

3.4 属性冲突

animation与display不兼容等。

参考:https://blog.csdn.net/mafan121/article/details/87795065

当然还有很多兼容性的属性,这里并没有列举,例如透明度:opacity等

3.5 盒子模型

盒子=外边距+边框+内边距+组件大小

W3C盒模型:padding、border所占的空间不算入width、height,相当于:box-sizing: content-box;

width=content

IE盒模型:padding、border所占的空间算入width、height,相当于: box-sizing: border-box;

width=padding+border+content

box-sizing可以改变盒子模型。

参考:https://blog.csdn.net/mafan121/article/details/51548222

3.6 样式优先级

!important > 行内样式 > ID选择器 > 类选择器 > 元素 > 通配符 > 继承 > 浏览器默认属性

参考:https://blog.csdn.net/mafan121/article/details/48158001

3.7 CSS中link 和@import的区别

linkhtml标签,只能存放于html源码中,既可以加载css文件,也能设置rss、rel等连接属性。在页面加载的时候同时被加载。可以通过dom操作插入。

@import是CSS2.1中的语法,只有导入样式的作用,在页面加载完成后才开始加载。无法通过dom操作导入。

4.js相关知识

4.1 js基础类型

String、number、boolean、null、undefined、symbol

4.2 js原型和原型链

  • 每个构造函数都有一个prototype属性,其指向原型对象。
  • 每个对象都有一个constructor属性,其指向构造函数。
  • 每个对象还有一个__proto__属性,其指向构造函数的原型(原型对象),即__proto__ === constructor.prototype

实例对象相似于原型对象,但是原型对象是为了实现继承而存在的,并不相等。

参考:https://blog.csdn.net/mafan121/article/details/48970993

4.3 事件委托

事件委托又称事件代理,是利用事件冒泡原理,将子元素的事件绑定到父元素上,让父元素担当事件的监听者。然后通过特殊的子元素标识来区分目前触发的是哪个子元素事件。例如常见的点击浏览器任意位置关闭弹窗,就是将点击事件委托给了document对象。

优点是:

  • 可以节省大量的内存,减少子元素相同事件的注册。
  • 当新增子元素时无需再次对其进行事件绑定。

示例:

<!DOCTYPE html>
<html> 
<head>
  <meta charset="utf-8">
  <script src="http://lib.sinaapp.com/js/jquery/2.0.2/jquery-2.0.2.min.js"></script>
</head>
<body>
  <ul id="myLinks">
    <li id="goSomewhere">Go somewhere</li>
    <li id="doSomething">Do something</li>
    <li id="sayHi">Say hi</li>
  </ul>
 
  <script>
     var ulEle = document.getElementById("myLinks");
      ulEle.addEventListener("click", function (event) {
      var target = event.target;
      switch (target.id) {
        case "doSomething":
          document.title = "事件委托";
          break;
        case "goSomewhere":
          location.href = "http://www.baidu.com";
          break;
        case "sayHi": 
          alert("hi");
          break;
      }
  </script>
</body>
</html>

4.4 防抖和节流

防抖:事件的执行必须间隔一定的时间,如果在指定时间内再次出发,则重新计时。

节流:在一段时间内,事件只能触发一次,时间段内的触发不生效。

参考:https://blog.csdn.net/mafan121/article/details/82115933

4.5 闭包的使用

闭包是函数内部的函数,作用是防止变量污染,模仿块级作用域,使函数内部的变量可以被外部访问。

参考:https://blog.csdn.net/mafan121/article/details/48243895

4.6 cookie、localStorage、sessionStorage的区别和优缺点

三者都是将数据缓存在浏览器端,但cookie数据存储量大约在4k左右,而storage存储量为5M左右。

cookie将会随http传递给后端,而storage仅仅是保存在本地,除非用户手动设置,否则不会传递给服务器。

storage:https://blog.csdn.net/mafan121/article/details/60133107

cookie:https://blog.csdn.net/mafan121/article/details/100062220

4.7 CommonJS、AMD、CMD的区别

CommonJS:是服务器端js模块化的规范,一个单独的文件就是一个模块,加载模块使用require方法,该方法读取一个文件并执行,最后返回文件内部的exports对象。NodeJs采用该规范。

AMD:异步模块定义,客户端js模块化的规范,只有一个接口:define(id?,dependencies?,factory);需要在声明模块的时候制定所有的依赖(dep),并且还要当做形参传到factory中。通过define定义,通过require方法加载。RequireJS采用这种规范。

CMD:通用模块定义,客户端js模块化的规范,通过define定义,通过require方法加载。SeaJs采用这种规范。

AMD和CMD的区别

  • 对于依赖的模块,AMD提前执行,而CMD则采用懒加载的方式延迟执行。
  • 对于requireAMD分全局require和局部requireCMD中没有全局require,而是按需加载。
  • CMD推崇依赖就近,AMD推崇依赖前置。

4.8 apply和 call的区别

apply和 call都是以一个对象代替当前对象,调用当前对象的方法,实际上是变更了当前方法的this指向。

区别在于:出第一个参数外,call接收任何类型数据作为参数,且参数可以是多个,而apply只能接收数组作为参数,且只能有一个数组。

参考: https://blog.csdn.net/mafan121/article/details/52922149

5.跨域问题

浏览器同源策略导致了跨域问题,同时也减少了浏览器遭受的XSS、CSFR等攻击。

常见的解决方案:

  • 通过jsonp跨域
  • postMessage跨域
  • nginx跨域
  • 设置document.domain相同
  • 服务端设置Access-Control-Allow-Origin

参考:https://segmentfault.com/a/1190000011145364

6.webpack打包优化

1.代码抽离,提取公共代码,分离css代码。

2.Externals第三方库外部引入。

3.减少loader筛选范围,精确resolve匹配路径,降低打包速度。

4.缓存loader的执行结果(cacheDirectory)

loader:它是一个转换器,主要用于编译文件进行文件转换的。
plugin:它是一个扩展容器,是针对编译完成后,监听完善整个打包过程的。

6.1webpack插件书写

plugin本质上就是一个类,其包含一个apply(compiler)方法。我们可以在compiler对象的构造上挂载一些监听函数,当钩子被触发时,执行这些监听函数。

compiler:包含了webpack环境中所有的配置信息(包含options、loaders、plugins等)。在webpack启动时被实例化,全局唯一。

相当一一个webpack实例

compilation:包含当前模块的资源、编译生成的资源、文件的变化等信息。当在开发模式下运行时,每次文件的改变都将生成一个新的compilation对象,对应一组新的编译资源。

webpack通过Tapable来组织插件执行顺序。其利用观察者模式,广播所有事件,插件只需关注它监听的事件,并执行其相关操作即可。

// 监听编译完毕事件
class DonePlugin {
  constructor(options) {
    this.options = options;
  }
  apply(compiler) {
    // 每当编译[done写完了]完成后,都会call done这个事件
    compiler.hooks.done.tap('DonePlugin', () => {
      console.log(this.options.message || arguments);
    });
  }
}

module.exports = DonePlugin;

常见的监听事件包含done(编译完成)、emit(chunk输出到结果文件)、compilation(文件改变)、run(开始编译),具体请参照webpack事件。

7 git或svn 常用命令

git 常用的命令:

git fetch <远程主机名> <分支名>    //拉取远程分支到本地
git pull  <远程主机名> <远程分支名> //拉取远程分支代码,并将远程分支代码和本地分支代码合并。
git checkout <分支名>						//切换分支
git merge <分支名>								//合并分支到当前分支

git reset --hard 目标版本号				// 回滚到指定版本
git revert -n 提交号							// 撤销某次提交

git add .												//获取所有修改的文件
git commit -m "描述"						 //提交修改
git push origin <分支名>					//推送代码到远端服务器

git commit --no-verify -m "提交描述"(跳过eslint或tslint校验)

svn常用命令:

svn checkout <path> 								//切换到服务器指定目录
svn add <file>											//获取需要提交的文件
svn commit -m "描述" 									//提交文件
svn update -r m <path> 							//更新版本
svn lock -m “描述” [--force] <path> //加/解锁
svn delete <path> -m “描述” 				//删除文件
svn log <path>  										//显示该文件的修改日志
svn svn merge -r m:n <path> 				//合并2个版本中的指定文件
svn diff <path> 										//比较当前文件和基础版本文件的差异			

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值