【面试题2】2

简介: 【面试题2】

说说React jsx转换成真实DOM的过程

使用React.createElement或JSX编写React组件,实际上所有的 JSX 代码最后都会转换成React.createElement(...) ,Babel帮助我们完成了这个转换的过程。

createElement函数对key和ref等特殊的props进行处理,并获取defaultProps对默认props进行赋值,并且对传入的孩子节点进行处理,最终构造成一个虚拟DOM对象

ReactDOM.render将生成好的虚拟DOM渲染到指定容器上,其中采用了批处理、事务等机制并且对特定浏览器进行了性能优化,最终转换为真实DOM

参考文献:

https://bbs.huaweicloud.com/blogs/265503)

https://huang-qing.github.io/react/2019/05/29/React-VirDom/

https://segmentfault.com/a/1190000018891454

说说你对@reduxjs/toolkit的理解?和react-redux有什么区别

一,Redux

 redux只是一种架构模式,它可以应用到任意需要使用它的框架,react,vue等等。它是为了解决相对复杂的应用中不同组件之间共享状态而产生的,比如react中两个组件要访问同一个状态,可以把它提到最近的父组件,然后向下传递,但应用一旦复杂了,这样就会变得繁琐。redux这种模式就解决了类似这样的问题。

redux就是提供了一个叫store的容器里面的state存放了全局的数据状态,对外提供了三个方法getState(), dispatch(), subscribe()

getState(): 用来获取state的值,dispatch(action)用来发起一个action告诉一个叫reducer的函数怎么去更新state,同时把上一次的state作为参数也传给reducer, reducer拿到参数后,返回更新后的state,  得到新的state后就需要渲染组件,可以手动去调用render方法,但这样恒麻烦,通过subscribe接受一个调用render的函数放在一个数组中,每次dispatch的时候除了会通过reducer改变state,还会遍历还数组中的函数去调用,这样每次数据发生变化就可以重新去渲染组件。

二,react-redux

     react-redux跟redux不同的是,它是专门为react服务的,它将redux中store的概念和React中context的概念结合起来,解决了相对复杂的react应用中不同组件共享状态传值的问题,它提供一个Provider的容器组件,该组件接收外界通过props将store传给它,并将store放在context中,子组件可以在connect的时候取到store,Connect接收mapStateToProps(该诉高阶组件需要什么样的数据,是一个接收state值作为参数返回所需state对象的函数), mapDispatchToProps(该诉高阶组件怎么样去触发dispatch,是一个函数,接收dispatch作为参数返回包含触发dispatch的函数的对象)作为参数返回一个以当前业务组件作为参数的高阶组件,明白了redux的实现原理,熟悉React,react-redux的实现就比较容易一些了。

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

原理:

类组件中render函数就是render方法

class Foo extends React.Component {
    render() { //类组件中
        return <h1> Foo </h1>;
    }
}

函数组件就是整个函数组件

function Foo() {  //函数组件中
    return <h1> Foo </h1>;
}

在render函数中的jsx语句会编译成我们熟悉的js代码

在render过程中,React将新调用的render函数返回的树与旧版本的树进行比较·,这一步是决定如何更新dom的必要步骤,然后进行diff比较,更新dom树

触发时机

render的执行时机主要分成两部分:

类组件调用setState修改状态:

class Foo extends React.Component {
  state = { count: 0 };
  increment = () => {
    const { count } = this.state;
    const newCount = count < 10 ? count + 1 : count;
    this.setState({ count: newCount });
  };
  render() {
    const { count } = this.state;
    console.log("Foo render");
    return (
      <div>
        <h1> {count} </h1>
        <button onClick={this.increment}>Increment</button>
      </div>
    );
  }
}

函数组件通过useState修改状态

function Foo() {
  const [count, setCount] = useState(0);
  function increment() {
    const newCount = count < 10 ? count + 1 : count;
    setCount(newCount);
  }
  console.log("Foo render");
  return (
    <div>
      <h1> {count} </h1>
      <button onClick={increment}>Increment</button>
    </div>
  ); 
}

函数组件通过useState这种形式更新数据,当数组的值不发生变化啦,就不会触发render

小结:

render函数里面可以编写JSX,转化成createElement这种形式,用于生成虚拟DOM,最终转化成真实DOM

在React 中,类组件只要执行了 setState 方法,就一定会触发 render 函数执行,函数组件使用useState更改状态不一定导致重新render

组件的props 改变了,不一定触发 render 函数的执行,但是如果 props 的值来自于父组件或者祖先组件的 state

在这种情况下,父组件或者祖先组件的 state 发生了改变,就会导致子组件的重新渲染

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

React性能优化的方法

  • 避免使用内联函数

【每次render渲染时,都会创建一个新的函数实例,应该在组件内部创建一个函数,讲事件绑定到函数,这样每次调用render时,就不会创建单独的函数实例】

  • 使用react fragement避免额外标记

【用户创建新组件时,每个组件应具有单个父标签,父级不能有两个标签。所以顶部要有一个公共标签,所以经常在组件顶部添加额外标签div,这个div标签充当父标签意外,没有其他作用,这个时候可以使用fragement,它不会向组件引入任何的额外标记,但是可以作为父级标签。】

  • 使用immutable

【在react中使用immutablr能够带来性能优化,主要体现在减少渲染的次数,为了避免重复渲染,会在shouldComponentUpdate()中做对比,当返回true,执行render方法。immutable通过is方法完成对比。】

  • 懒加载组件

【从工程方面考虑,webpack存在代码拆分的能力,可以为应用创建多个包,并在运行时动态加载,减少初始包的大小, 在react中使用Suspense,lazy组件】

  • 事件绑定方式

【从性能考虑,在render方法中使用bind和箭头函数,都会生成新的方法实例,在constructer中欧给使用bind和箭头函数,性能提高】

  • 服务端渲染

【可以使用户更快的看到显然成功的页面,服务端渲染可以起一个node服务,可以使用express。koa等,调用react的renderToString方法,将跟组件渲染成字符串,再输出到相应中】

  • 组件拆分

【合理使用hooks】

React.Memo来缓存组件

使用useMemo缓存大量的计算

使用shouldComponentUpdate或者React.PureComponent

避免使用内联对象

避免使用匿名函数

延迟加载不是立即需要的组件

调整css而不是强制组件加载和卸载

使用React.Fragment避免添加额外的DOM

说说你对栈、队列的理解?应用场景

栈:

栈(stack)又名堆栈,它是一种运算受限的线性表,限定仅在表尾进行插入和删除操作的线性表

表尾这一端被称为栈顶,相反地另一端被称为栈底,向栈顶插入元素被称为进栈、入栈、压栈,从栈顶删除元素又称作出栈

所以其按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据,具有记忆作用

队列:

跟栈十分相似,队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作

进行插入操作的端称为队尾,进行删除操作的端称为队头,当队列中没有元素时,称为空队列

在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出

应用场景:

借助栈的先进后出的特性,可以简单实现一个逆序数处的功能,首先把所有元素依次入栈,然后把所有元素出栈并输出

包括编译器的在对输入的语法进行分析的时候,例如"()"、"{}"、"[]"这些成对出现的符号,借助栈的特性,凡是遇到括号的前半部分,即把这个元素入栈,凡是遇到括号的后半部分就比对栈顶元素是否该元素相匹配,如果匹配,则前半部分出栈,否则就是匹配出错

包括函数调用和递归的时候,每调用一个函数,底层都会进行入栈操作,出栈则返回函数的返回值

生活中的例子,可以把乒乓球盒比喻成一个堆栈,球一个一个放进去(入栈),最先放进去的要等其后面的全部拿出来后才能出来(出栈),这种就是典型的先进后出模型

队列

当我们需要按照一定的顺序来处理数据,而该数据的数据量在不断地变化的时候,则需要队列来帮助解题

队列的使用广泛应用在广度优先搜索中,例如层次遍历一个二叉树的节点值(后续的篇章比讲到)

生活中的例子,排队买票,排在队头的永远先处理,后面的必须等到前面的全部处理完毕再进行处理,这也是典型的先进先出模型

说说你对git rebase 和git merge的理解?区别?

merge和rebasea都是合并历史记录,但是各自特性不同:

merge

通过merge合并分支会新增一个merge commit,然后将两个分支的历史联系起来

其实是一种非破坏性的操作,对现有分支不会以任何方式被更改,但是会导致历史记录相对复杂

rebase

rebase会将整个分支移动到另一个分支上,有效地整合了所有分支上的提交

主要的好处是历史记录更加清晰,是在原有提交的基础上将差异内容反映进去,消除了 git merge所需的不必要的合并提交

说说git常用的命令有哪些

git init // 初始化 在工作路径上创建主分支

git clone 地址 // 克隆远程仓库

git clone -b 分支名 地址 // 克隆分支的代码到本地

git status // 查看状态

git add 文件名 // 将某个文件存入暂存区

git checkout -- file // 撤销工作区的修改 例如git checkout -- readMe.txt 将本次readMe.txt在工作区的修改撤销掉

git add b c //把b和c存入暂存区

git add . // 将所有文件提交到暂存区

git add -p 文件名 // 一个文件分多次提交

git stash -u -k // 提交部分文件内容 到仓库 例如本地有3个文件 a b c 只想提交a b到远程仓库 git add a b 然后 git stash -u -k 再然后git commit -m "备注信息" 然后再push push之后 git stash pop 把之前放入堆栈的c拿出来 继续下一波操作

git commit -m "提交的备注信息"  // 提交到仓库

若已经有若干文件放入仓库,再次提交可以不用git add和git commit -m "备注信息" 这2步, 直接用

git commit -am "备注信息" // 将内容放至仓库 也可用git commit -a -m "备注信息"

* git commit中的备注信息尽量完善 养成良好提交习惯 例如 git commit -m "变更(范围):变更的内容"

我是一个小趴菜,希望可以对和我一样的新手小白有帮助🤭

相关文章
|
Java 关系型数据库 MySQL
面试题30天打卡-day03
面试题30天打卡-day03
26 0
|
缓存 JavaScript 前端开发
【面试题总结】
【面试题总结】
|
5月前
|
存储 算法 编译器
C++面试题其一
C++文件编译与执行的四个阶段 预处理:处理#include、#define等预处理指令。 编译:将源码翻译为目标代码。 汇编:将目标代码转换为机器指令。 链接:将目标文件和库文件合并生成可执行文件。 STL中的vector的实现,是怎么扩容的? vector通过动态数组实现,当容量不足时,分配更大的内存(通常是原来的两倍),复制旧数据到新内存,并释放旧内存。
77 2
|
5月前
|
存储 程序员 编译器
C++面试题其二
extern "C" 用于告诉编译器按照C语言的链接方式处理代码,通常用于C++代码与C代码混合编程,以防止因名字修饰(name mangling)引起的链接错误。例如: extern "C" { void c_function(); } 通过这些问题的深入理解和解答,能够更好地掌握C++编程的核心概念和实际应用,为面试做好充分的准备。
70 1
|
3月前
|
JavaScript 前端开发 API
面试题总结
面试题总结
103 58
|
缓存 JavaScript 前端开发
【面试题1】
【面试题1】
|
6月前
|
缓存 小程序 Java
【面试题】1、总结面试题1
【面试题】1、总结面试题1
57 0
|
NoSQL 关系型数据库 应用服务中间件
面试题30天打卡-day15
面试题30天打卡-day15
43 0
|
设计模式 负载均衡 Java
面试题30天打卡-day25
面试题30天打卡-day25
27 0
|
存储 算法 安全
【C++】面试题
【C++】面试题
141 0