CSS
[Q1] 已知如下代码,如何修改才能让图片宽度为 300px ?注意下面代码不可修改。
1 | <img src="1.jpg" style="width:480px!important;”> |
[A1]
没有顺序,置前置后都生效
1、max-width: 300px;
2、transform: scale(0.625,0.625);
3、zoom: 0.5;
(zoom的缩放是相对于左上角的;而scale默认是居中缩放。)
4、padding: 0 90px; box-sizing: border-box;
5、用js document.getElementsByTagName("img")[0].setAttribute("style","width:300px!important;");
6、给图片设置动画
1 | img { |
原理是CSS动画的样式优先级高于!important的特性
[Q2] 介绍下 BFC 及其应用
[A2]
块级格式化上下文 (Block Formatting Context)
怎样才能形成BFC:
1、根元素
2、float的值不能为none
3、overflow的值不能为visible
4、display的值为table-cell, table-caption, inline-block,flex中的任何一个
5、position的值不为relative和static
[BFC] https://juejin.im/post/5a4dbe026fb9a0452207ebe6
[BFC] https://zhuanlan.zhihu.com/p/25321647
[清除浮动] https://www.cnblogs.com/dolphinX/p/3508869.html
[清除浮动详解] https://www.cnblogs.com/iyangyuan/archive/2013/03/27/2983813.html
[Q3] vw vh 是什么意思
[A3]
vw:相对于视口的宽度,视口被均分为100单位的vw
vh:相当于视口的宽度
[Q4] 用css画出三角形
1 |
|
https://blog.csdn.net/weixin_36270908/article/details/98947183
[Q5] 三栏布局
当中间内容超出时 https://jiafei2333.github.io/interview/layout_overflow.html 中float中间部分超出 若希望中间内容不到左边去 可以给中间设为bfc 因为bfc不与float重叠 例如 overflow:hidden | overflow:auto;
box-sizing: content-box;
默认
React/Vue
[Q1] 写 React / Vue 项目时为什么要在列表组件中写 key,其作用是什么?
https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/1
在交叉对比中,当新节点跟旧节点头尾交叉对比没有结果时,会根据新节点的key去对比旧节点数组中的key,从而找到相应旧节点(这里对应的是一个key => index 的map映射)。如果没找到就认为是一个新增节点。而如果没有key,那么就会采用遍历查找的方式去找到对应的旧节点。一种一个map映射,另一种是遍历查找。相比而言。map映射的速度更快。
key的作用是为了在diff算法执行时更快的找到对应的节点,提高diff速度。
【拓展】
React/Vue Diff算法
[Q2] React 中 setState 什么时候是同步的,什么时候是异步的?
在React中,如果是由React引发的事件处理(比如通过onClick引发的事件处理),调用setState不会同步更新this.state,除此之外的setState调用会同步执行this.state。所谓“除此之外”,指的是绕过React通过addEventListener直接添加的事件处理函数,还有通过setTimeout/setInterval产生的异步调用。
原因:在React的setState函数实现中,会根据一个变量isBatchingUpdates判断是直接更新this.state还是放到队列中回头再说,而isBatchingUpdates默认是false,也就表示setState会同步更新this.state,但是,有一个函数batchedUpdates,这个函数会把isBatchingUpdates修改为true,而当React在调用事件处理函数之前就会调用这个batchedUpdates,造成的后果,就是由React控制的事件处理过程setState不会同步更新this.state。
[Q] 聊聊 Redux 和 Vuex 的设计思想
[Q] 聊聊 Vue 的双向数据绑定,Model 如何改变 View,View 又是如何改变 Model 的?
[Q] Virtual DOM 真的比操作原生 DOM 快吗?谈谈你的想法。
[Q] react的constructor中为什么要写super(prop
【解答】
1、我们为什么要调用super?能不能不调用它?
在 JavaScript 中,super 指代父类的构造函数。
在你调用父类构造函数之前,你无法在构造函数中使用 this。JavaScript 不会允许你这么做。
1 | class Checkbox extends React.Component { |
JavaScript 强制你在使用 this 前运行父类构造函数有一个很好的理由。考虑这样一个类结构:
1 | class Person { |
想象一下如果在调用 super
前使用 this
是被允许的。一个月之后。我们或许会改变 greetColleagues
把 person
的 name
加到消息中。
1 | greetColleagues() { |
但我们忘了 this.greetColleagues() 是在 super() 设置 this.name 之前被调用的。this.name 甚至还没被定义!如你所见,像这样的代码理解起来会很困难。
为了避免这样的陷阱,JavaScript 强制规定,如果你想在构造函数中用this,就必须先调用 super。
1 | constructor(props) { |
2、另一个问题:为什么要传 props?
事实上即便在调用 super() 时没有传入 props 参数,你依然能够在 render 和其它方法中访问 this.props
但在 super() 调用一直到构造函数结束之前,this.props 依然是未定义的
1 | // React 内部 |
这也是推荐总是使用 super(props) 的写法,即便这是非必要的。
1、调用super的原因:在ES6中,在子类的constructor中必须先调用super才能引用this;根本原因是constructor会覆盖父类的constructor,导致你父类构造函数没执行,所以手动执行下。
2、super(props)的目的:在constructor中可以使用this.props。
参看文章:
https://juejin.im/post/5c04fea5f265da6133565696
JS
[Q] [‘1’, ‘2’, ‘3’].map(parseInt) what & why ?
parseInt() 参数:
string 必需。要被解析的字符串。
radix 可选。表示要解析的数字的基数。该值介于 2 ~ 36 之间。如果省略该参数或其值为 0,则数字将以 10 为基础来解析。如果它以 “0x” 或 “0X” 开头,将以 16 为基数。如果该参数小于 2 或者大于 36,则 parseInt() 将返回 NaN。
解析:https://github.com/sisterAn/blog/issues/19
【拓展】
1 | 1、 |
【拓展】
1 | let unary = fn => val => fn(val) |
[Q] 什么是防抖和节流?有什么区别?如何实现?
【时间起因】https://github.com/mqyqingfeng/Blog/issues/22 (只看原因,不看这里的原理解析)
【简洁归纳】防抖是虽然事件持续触发,但只有等事件停止触发后 n 秒才执行函数,节流是持续触发的时候,每 n 秒执行一次函数。
https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/5
防抖
1 | <input type="text" id="inp" /> |
1 | function debounce(fn) { |
[疑问] fn.apply(this, arguments);而不是这样 fn()
[解答] 为了确保上下文环境为当前的this,所以不能直接用fn。
如果单单为了打印那句console.log(‘防抖成功’);确实可以直接fn(),但我们得考虑实际情况,让sayHi的this指向input是必要的,例如我们需要在输入完改变字体颜色,如下:
1 | function sayHi() { console.log('防抖成功'); this.style.color = 'red'; } |
这个时候 fn.apply(this, arguments);
的作用就显而易见了
【应用场景】
- 表单的连续点击,防止重复提交。比如重复发送一篇文章,提交按钮一直点,但是在一定的时间内只会触发一次。
- 类百度的搜索,连续输入等输入停止后再搜索。
- 一直拖动浏览器窗口,只想触发一次事件等。
节流
1 | function throttle(fn) { |
【应用场景】
- 节流可以是scroll,用滚动条计算高度的时候原先会实时计算,用节流,可以每隔例如300ms计算一次。
- 自动保存草稿功能,当用户在输入的时候(一直触发事件),单位时间内只保存一次草稿。
- 游戏中的刷新率
[Q] 介绍下 Set、Map、WeakSet 和 WeakMap 的区别?
[Q] 介绍下深度优先遍历和广度优先遍历,如何实现?
[A]
[Q] 有以下 3 个判断数组的方法,请分别介绍它们之间的区别和优劣Object.prototype.toString.call() 、 instanceof 以及 Array.isArray() ?
[A]
- Object.prototype.toString.call()
每一个继承 Object 的对象都有 toString 方法,如果 toString 方法没有重写的话,会返回 [Object type],其中 type 为对象的类型。但当除了 Object 类型的对象外,其他类型直接使用 toString 方法时,会直接返回都是内容的字符串,所以我们需要使用call或者apply方法来改变toString方法的执行上下文。
1 | const an = ['Hello','An']; |
这种方法对于所有基本的数据类型都能进行判断,即使是 null 和 undefined 。
1 | Object.prototype.toString.call('An') // "[object String]" |
- instanceof
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
1 | [] instanceof Array; // true |
- Array.isArray()
Array.isArray() 用于确定传递的值是否是一个 Array。
- 当检测Array实例时, Array.isArray 优于 instanceof,因为Array.isArray能检测iframes.
1 | var iframe = document.createElement('iframe'); |
- Array.isArray() 与 Object.prototype.toString.call()
Array.isArray()是ES5新增的方法,当不存在 Array.isArray() ,可以用 Object.prototype.toString.call() 实现。
1 | if (!Array.isArray) { |
[Q] 全局作用域中,用 const 和 let 声明的变量不在 window 上,那到底在哪里?如何去获取?
ES6规定,var 命令和 function 命令声明的全局变量,依旧是顶层对象的属性,但 let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。
1 | var a = 12; |
通过上图也可以看到,在全局作用域中,用 let 和 const 声明的全局变量并没有在全局对象中,只是一个块级作用域(Script)中
[Q] cookie 和 token 都存放在 header 中,为什么不会劫持 token?
【XSS攻击】
恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。
【XSS危害】
- 窃取网页浏览中的cookie值
登录完毕之后服务端会返回一个cookie值。这个cookie值相当于一个令牌,拿着这张令牌就等同于证明了你是某个用户。如果你的cookie值被窃取,那么攻击者很可能能够直接利用你的这张令牌不用密码就登录你的账户。如果想要通过script脚本获得当前页面的cookie值,通常会用到document.cookie。(不过某些厂商的cookie有其他验证措施如:Http-Only保证同一cookie不能被滥用) - 劫持流量实现恶意跳转
这个很简单,就是在网页中想办法插入一句像这样的语句:1
<script>window.location.href="http://www.baidu.com";</script>
【CSRF攻击】
跨站请求伪造:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
一个典型的CSRF攻击有着如下的流程:
- 受害者登录a.com,并保留了登录凭证(Cookie)。
- 攻击者引诱受害者访问了b.com。
- b.com 向 a.com 发送了一个请求:a.com/act=xx。浏览器会…
- a.com接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是受害者自己发送的请求。
- a.com以受害者的名义执行了act=xx。
- 攻击完成,攻击者在受害者不知情的情况下,冒充受害者,让a.com执行了自己定义的操作。
浏览器发送请求的时候不会自动带上token,而cookie在浏览器发送请求的时候会被自动带上。csrf就是利用的这一特性,所以token可以防范csrf,而cookie不能。
1、首先token不是防止XSS的,而是为了防止CSRF的;
2、CSRF攻击的原因是浏览器会自动带上cookie,而浏览器不会自动带上token
[Q] 下面的代码打印什么内容,为什么?
1 | var b = 10; |
【拓展】 自执行的具名函数A内部修改A的值无效
1 | (function A() { |
【拓展】
1 | var b = 10; |
所以严格模式下能看到错误:Uncaught TypeError: Assignment to constant variable
1 | var b = 10; |
其他情况例子:
有window:
1 | var b = 10; |
有var
1 | var b = 10; |
[Q] 简单改造下面的代码,使之分别打印 10 和 20。
1 | var b = 10; |
【解答】
1 | var b = 10; |
[Q] 下面代码输出什么?
1 | var a = 10; |
【解答】
undefined
10
20
[Q] 实现一个 sleep 函数,比如 sleep(1000) 意味着等待1000毫秒,可从 Promise、Generator、Async/Await 等角度
[Q] Object.assign() 和 … 的区别
- 当一个 Object 使用了 Object.defineProperty 修改了 set 方法,因为调用 Object.assign 会触发 setter 方法,会触发意想不到的错误
- 如果将空对象作为第一个参数传递给Object.assign(),看起来 Object spread 会更快。
[Q] 为什么要用闭包 全部放在全局不好吗?
【解答】
垃圾回收:会去获取变量、方法被调用的次数,被调用的次数为0就会销毁;
把变量、方法等全部定义在全局是可以的,随着代码的执行早晚会被销毁;变量都在全局太多会重名 出现污染的情况所以要用闭包(避免全局变量污染);闭包的缺点:内存泄漏 add = 一个方法(闭包方法);add和这个方法形成一个调用栈,add不会释放,闭包里面的变量也不会释放,只能手动释放 add = null
其他
[Q] 谈谈你对 TCP 三次握手和四次挥手的理解
【三次握手】
1 | 三次握手之所以是三次是保证client和server均让对方知道自己的接收和发送能力没问题而保证的最小次数。 |
或者
1 | 1、客户端发送位码为syn=1,随机产生seq number=1234567的数据包到服务器,服务器由SYN=1知道客户端要求建立联机(客户端:我要连接你) |
【拓展】
提问:为什么http建立连接需要三次握手,不是两次或四次?
回答: 三次是最少的安全次数,两次不安全,四次浪费资源
【四次挥手】
1 | 为什么要四次挥手?TCP是全双工,何为全双工就是客户端与服务端建立两条通道,通道1:客户端的输出连接服务端的输入;通道2:客户端的输入连接服务端的输出。两个通道可以同时工作:客户端向服务端发送信号的同时服务端也可以向客户端发送信号。所以关闭双通道的时候就是这样: |
[Q] 介绍下重绘和回流(Repaint & Reflow),以及如何进行优化 ?
浏览器的渲染过程
1、解析HTML,生成DOM树,解析CSS,生成CSSOM树
2、将DOM树和CSSOM树结合,生成渲染树(Render Tree)
3、根据RenderTree,我们就知道了所有节点的样式,然后计算他们在页面上的大小和位置,最后把节点绘制到页面上。
需要明白,这几个步骤并不一定一次性顺序完成。如果 DOM 或 CSSOM 被修改,以上过程需要重复执行,这样才能计算出哪些像素需要在屏幕上进行重新渲染。实际页面中,CSS 与 JavaScript 往往会多次修改 DOM 和 CSSOM。
https://github.com/chenjigeng/blog/issues/4
https://juejin.im/post/5a8e242c5188257a6b060000
[Q] 浏览器输入URL后发生了什么?
https://www.xuecaijie.com/it/157.html#1Q64p5DeC8dKFF
[Q] 请求时浏览器缓存 from memory cache 和 from disk cache 的依据是什么,哪些数据什么时候存放在 Memory Cache 和 Disk Cache中
了解下: https://juejin.im/post/5c22ee806fb9a049fb43b2c5?utm_source=gold_browser_extension
Node
[Q] 介绍下 npm 模块安装机制,为什么输入 npm install 就可以自动安装对应的模块?
- 发出npm install命令
- 查询node_modules目录之中是否已经存在指定模块
- 若存在,不再重新安装
- 若不存在
- npm 向 registry 查询模块压缩包的网址 (https://registry.npmjs.org/react 跟模块名, 就会看到 react 模块所有版本的信息。)
- 下载压缩包,存放在根目录下的.npm目录里
- 解压压缩包到当前项目的node_modules目录
【拓展】
注意,一个模块安装以后,本地其实保存了两份。一份是/.npm目录下的压缩包,另一份是node_modules目录下解压后的代码。/.npm目录。也就是说,如果一个模块在~/.npm下有压缩包,但是没有安装在node_modules目录中,npm 依然会从远程仓库下载一次新的压缩包。
但是,运行npm install的时候,只会检查node_modules目录,而不会检查
[Q] 浏览器和Node 事件循环的区别
https://jiafei2333.github.io/2019/09/16/EventLoop/
算法
[Q] 两个数组合并成一个数组
请把两个数组 [‘A1’, ‘A2’, ‘B1’, ‘B2’, ‘C1’, ‘C2’, ‘D1’, ‘D2’] 和 [‘A’, ‘B’, ‘C’, ‘D’],合并为 [‘A1’, ‘A2’, ‘A’, ‘B1’, ‘B2’, ‘B’, ‘C1’, ‘C2’, ‘C’, ‘D1’, ‘D2’, ‘D’]。
https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/39
[Q] 改造下面的代码,使之输出0 - 9,写出你能想到的所有解法。
1 | for (var i = 0; i< 10; i++){ |
解法:
1 | // 1 错 10个10 |
【拓展】
bind(null, …)
1 | function multiply (x, y, z) { |
例如这里第一次就传了x的值,那么yz的值就后续调用里面传入的。
bind() 的另一个最简单的用法是使一个函数拥有预设的初始参数。只要将这些参数(如果有的话)作为 bind() 的参数写在 this 后面。当绑定函数被调用时,这些参数会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们后面。
[Q] 使用迭代的方式实现 flatten 函数
1 | var arr=[1,2,3,[4,5],[6,[7,[8]]]] |
【解答】
1 | var arr=[1,2,3,[4,5],[6,[7,[8]]]] |
1 | var arr=[1,2,3,[4,5],[6,[7,[8]]]] |
1 | var arr=[1,2,3,[4,5],[6,[7,[8]]]] |
[Q] 下面代码中 a 在什么情况下会打印 1?
1 | var a = ?; |
【解答】
== 会进行隐式类型转换 所以我们重写toString方法就可以了
这题考察的是类型的隐式转换,考引用类型在比较运算符时候,隐式转换会调用本类型toString或valueOf方法.
1 | var a = { |
1 | var a = { |
valueOf优先级大于toString, 同时存在,会调用valueOf
[Q] 筛选id相同和不相同的值
1 | let arr1 = [1,2,3,4,5]; |
[Q] 用最短的代码实现去重
1 | [...new Set([1,'1',2,1,1,3]) |
网站优化
1、css、js文件压缩
2、dns预解析 加载静态资源快 优先加载
3、双核浏览器 例如 360、搜狗 都是ie和chrome内核 表示渲染页面优先使用weikit
语音面试题
[Q] 什么是高阶组件,你在工作是如何应用的?
高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。高阶组件是参数为组件,返回值为新组件的函数(高阶组件时一个函数,并不是组件)。组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件。
请注意,HOC 不会修改传入的组件,也不会使用继承来复制其行为。相反,HOC 通过将组件包装在容器组件中来组成新组件。HOC 是纯函数,没有副作用。
不要在 render 方法中使用 HOC(render中的高阶组件会在每次render时重新mount,重新挂载组件会导致该组件及其所有子组件的状态丢失。)
重要:https://zh-hans.reactjs.org/docs/higher-order-components.html
应用场景: react-redux 的connect,react-router-dom withRouter,全局loading封装,Form.create()对表单form域中的数据进行统一的管理和处理,数据埋点?logger?,
代码复用: 将组件重复部分抽出来,在通过高阶组件拓展,增删改props,达到组件可复用的目的。
条件渲染:控制组件的渲染逻辑,常见:鉴权, 判断页面权限
生命周期捕获/劫持:借助父组件子组件生命周期规则捕获子组件的生命周期,常见case:打点。
高阶组件其实是装饰者模式在react中的一种实现,它类似于高阶函数,接收react组件 作为参数,返回一个新的react组件,高阶函数的实现方法有两种,分别是属性代理和反向代理,在工作中属性代理使用的比较多,例如用户权限页面的控制,我们基本上用的属性代理,这个原理其实是跟react-redux中的connect很相似,另一种就是组件的复用。
通常都是有一个函数传入一个组件,然后返回一个被增强的组件,目标就是把通用的业务逻辑抽象到高阶组件上,然后高阶组件通过属性传递给子组件,解决通用代码逻辑复用的问题,但是他会有两个问题,就是如果嵌套层级过深的话,会造成传值的数据流难以维护,重复的属性会被覆盖,还有就是这样子组件的使用权权在高阶组件本身,不够灵活。
反向继承、劫持、渲染劫持、切面编程 有点aop面向编程,有点装饰者设计模式的感觉
useState、useEffects可以代替高阶组件
分为代理方式的高阶组件;继承方式的高阶组件
[Q] 展示组件(Presentational component)和容器组件(Container component)之间有何不同?
1、有状态【Stateful】和 无状态【Stateless】:
容器组件倾向于有状态,展示组件倾向于无状态,这不是硬性规定,因为容器组件和展示组件都可以是有状态的。
2、类【Classes】 和 函数【Functions】
3、纯粹【Pure】 和 不纯粹 【Impure】:
纯粹:输入什么就输出什么,不会再其中做相应的变动。
不管是展示组件还是容器组件都会有上面的二分特性。在我看来,展示组件往往是没有状态的纯函数,而容器组件往往是有状态的纯类。
这里把素材库的例子聚一下,中间card这块就是纯函数组件,可以视频、音频、图片多处复用解耦了逻辑和页面展示,取值、属性、方法回调什么的。
[Q] React中Refs的作用是什么?
http://react.html.cn/docs/refs-and-the-dom.html
三种创建方式:
1、直接指定ref = ‘xxx’ ref 是一个字符串,react不推荐使用了
2、通过React.createRef()创建,创建的ref 有个 current属性,函数组件如果需要使用,需要使用React.forwardRef 转发一下ref。hoc组件传递ref也存在同样的问题,需要使用React.forwardRef 转发
3、通过函数 ref={input => this.userName = input} ,获取值需要使用 this.userName.value
- 当 ref 属性被用于一个普通的 HTML 元素时,React.createRef() 将接收底层 DOM 元素作为它的 current 属性以创建 ref 。
- 当 ref 属性被用于一个自定义类组件时,ref 对象将接收该组件已挂载的实例作为它的 current 。
- 你不能在函数式组件上使用 ref 属性,因为它们没有实例。如果要用就用React.forwardRef转发一下ref(…这里放函数组件)
在非受控组件中使用,获取真实dom元素的值,或者获取焦点、控制video、audio的播放状态、或者是一些动画
useRef()?
[Q] 组件的状态(state)和属性(props)之间有何不同?
共同点: props 和 state 都属于 react 数据接口,他们都可以决定组件的行为和状态
不同点:
props 属于对外接口,state 属于对内接口,props 主要是父组件传递参数配置该组件,组件内部无法控制也无法修改。想要修改父组件的数据只能通过回调函数的形式(即在子组件中调用父组件的方法)。
state 主要用于保存,控制,修改组件自身状态,你可以理解state是一个局部的,只能被组件自身控制的数据源。不能被外界获取和修改。我们可以通过this.setState更新数据,setState导致组件重新渲染。
总之props是让外部对组件进行配置,state是让组件控制自己的状态
工作中:没有state的组件叫无状态组件,反之有状态组件,工作中尽量多无状态组件,尽量少写有状态组件,增加代码的可维护性和复用性。
[Q] React context 是什么?有什么用?
1、React Context优点:能够让数据在组件树中传递,基于树形结构共享数据的方式,在某个节点组件开启提供context后,所有后代节点组件都可以获取到共享的数据。
而props或者state进行多级数据传递,则数据需要自顶下流经过每一级组件,无法跨级。
2、React Context缺点:
(1)context相当于全局变量, 难以追溯数据源
(2)耦合度高,即不利于组件复用也不利于测试
(3)当 props 改变或者 setState 被调用,生成新的 context,但是 shouldComponentUpdate 返回的 false 会 block 住 context,导致没有更新。
3、React Context使用:可以通过Provider组件的value来传递数据,也可以通过调用react.createContext()来产生context,然后在Consumer组件获得context中的数据。
[Q] 了解Redux么,说一下redux吧
Redux 本身是个状态管理框架,核心或者说目的一句话就能概括, 清晰的描述应用的状态 。
Redux 核心和原则
1.这个应用的状态是一个唯一的状态树
2.状态是只读的, 只能通过 action 来触发修改, 其实实际修改状态的是 reducer
3修改状态只能通过纯函数
Redux 中的概念
1.reducer
reducer 就是实际改变 state 的函数, 在 redux 中, 也只有 reducer 能够改变 state.
根据 redux 的原则, 整个应用只有一个唯一的状态树, 这样, 理论上只要一个 reducer 就够了. 但是, 实际编码时, 如果应用稍具规模, 只有一个 reducer 文件, 显然不利于分模块合作开发, 也不利于代码维护.
所以, reducer 一般是按模块, 或者根据你所使用的框架来组织, 会分散在多个文件夹中. 这时, 可以通过 redux 提供的 API combineReducers 来合并多个 reducer, 形成一个唯一的状态树.
reducer 的使用只要注意 2 点:1、必须是纯函数2、多个 reducer 文件时, 确保每个 reducer 处理不同的 action, 否则可能会出现后面的 reducer 被覆盖的情况
2.state
state 或者说是 store, 其实就是整个应用的状态.
3.action
redux 中的 action 其实就是一个 包含 type 字段的plain object. type 字段决定了要执行哪个 reducer, 其他字段作为 reducer 的参数.
4.action creator
action creator 本质是一个函数, 返回值是一个满足 action 的定义的 plain object. 使用 action creator 的目的就是简化 action 的定义
5.dispatch
view层通过action来改变store从而改变当前的state,但是action只是一个对象而已,store.dispatch() 就是 view 发出 Action对象的唯一方法。
dispatch的中文意思就是派遣、发送的意思。 即将action发送到store.
6.subscribe
store允许使用 store.subscribe 方法设置监听函数,一旦 state 发生变化, 就自动执行这个函数。
7.middleware
redux 的 middleware 发生在 dispatching an action 和 reaches the reducer 之间. 在这个时间点, 除了可以实现异步操作, 还可以实现 logging等等.
[Q] 什么是受控组件和非受控组件?
由React控制值的表单元素称为受控组件。受控组件的特点:
1.由React通过JSX渲染出来
2.由React控制值的改变,也就是说想要改变元素的值,只能通过React提供的方法来修改,并且只能通过setState来设置受控组件的值。
非受控组件则是将真实数据储存在DOM节点中,由表单元素本身维持自身状态,并根据用户输入进行更新。非受控组件的特点:
1.由元素本身维持自身状态,不需要为状态更新编写数据处理;
2.在React中设置默认值需要设置defaultValue,获取值需要使用ref。
在大多数情况下,建议使用受控组件来实现表单,因为非受控组对比受控组件,有以下一些缺点:
1.无法即时校验(提前校验用户输入);
2.无法根据输入值的变化禁用提交按钮(通过禁用提交来约束用户输入);
3.无法强制输入格式;
4.无法响应值的输入(如即时获得用户的输入来改变其他状态显示);
5.无法动态输入。
[Q] 怎么实现React组件的国际化?
【解答】
一般像多语言国际化这种全局需求,我们可以使用 React 的 context 来全局共享一份相关数据,包含:“当前语言” 和一些通用词汇的语言包。
同时,React 组件本身也可以维护一套自己的语言包,比如时间选择器等,通过获取全局的 “当前语言”,然后通过映射获取指定位置的字符,进行拼接或展示。
在应用过程中,注意几点:
- 切换语言是一个低频需求,但语言包可能会比较大,可以按需加载
- 限制词语或句子的长度,在语言切换时,长度可能会变化,比如英文单词可能比中文单词长,会影响布局
- 注意颜色在不同语言、文化中的差异
- 注意日期和货币格式在不同国家和地区的差异显示
[Q] React为什么要搞Hooks,React Hooks帮我们解决了哪些问题?
【解答】
- class 组件需要编译成函数,增加了代码体积,hook 用在函数组件不会产生冗余代码
- class 复用逻辑需要借助高阶组件,高阶组件嵌套会导致不能直接获取真实 ref,嵌套多个也会比较复杂,hook 可以使用自定义 hook 来复用逻辑
- 函数组件中没有 this,可以避免之前事件的绑定 this 问题
- hook 的 useEffect 可以将订阅与取消订阅逻辑写在一处,减少重复代码
- useEffect useCallback useMemo 第二个参数可以优化性能,只在依赖项改变时才会更新,不用像之前写在 shouldComponentUpdate 中再比较 props 去确认是否更新
上面这部分可以参考,然后自己总结一版
[Q] 创建React动画有哪些方式?
【解答】
创建React动画有以下几种:1.基于定时器或requestAnimationFrame的间隔动画;使用定时器可能会有掉帧问题,而使用requestAnimationFrame则性能较好,完全使用js,不依赖css,帧数跟屏幕刷新率一致,页面运行到后台会自动暂停提高性能。2.基于css3中的animation和transition简单动画;有较高的性能,代码量少,但是只能依赖于css效果,对于复杂动画比较难实现跟控制。3.React动画插件CssTransitionGroup;性能比较好,但限定于入场跟出场场景。4.其他第三方动画库。
[Q] 你对immutable有了解吗?它有什么作用?
1、Immutable实现的原理
Immutable实现的原理是利用结构共享形成持久化数据结构,也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变。同时为了避免 deepCopy 把所有节点都复制一遍带来的性能损耗,Immutable使用了结构共享,即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。
2、Immutable的优点
(1)节约内存
JavaScript 中的对象一般是可变的(Mutable),因为使用了引用赋值,新的对象简单的引用了原始对象,改变新的对象将影响到原始对象。为了解决这个问题,一般的做法是使用shallowCopy(浅拷贝)或 deepCopy(深拷贝)来避免被修改,但这样做造成了CPU和内存的浪费。Immutable 可以很好地解决这些问题。
(2)Undo/Redo,Copy/Paste,时间轴等功能容易实现
因为每次数据都是不一样的,只要把这些数据放到一个数组里储存起来,想回退到哪里就拿出对应数据即可,很容易开发出撤销重做这种功能。
(3)并发安全
Immutable Data一旦创建,就不能再被更改,也就不需要并发锁。
3、Immutable使用
(1)与React搭配使用,Immutable简洁高效的判断数据是否变化,提高渲染速度及性能
(2)与Redux/flux搭配使用
【或者】
immutable是实现Immutable data的库,这个库有大量的api可以产生不可被修改的数据,对Immutable对象的任何修改或添加删除操作都会返回一个新的Immutable对象。其实现原理是持久化数据结构,相对于深拷贝而言,通过旧数据来创建新数据的时候,只修改发生变化的节点及其父节点,他们节点保持共享,性能更好,处理速度更快。
跟React配合使用能提高性能:一方面如果在state中保存了一份有深层结构的引用类型的数据,如果没有Immutable.js,则需要深拷贝一份再做修改(Object.assign及react中的setState都是属于浅拷贝)。而用Immutable.js将state中的数据包装一下,不需深拷贝就可以直接修改。另一方面由于修改后返回的是新对象,React.js只需要在oldState.obj === newState.obj这一层就能判断出obj产生了变化,不需要深入obj的深层结构。
[Q] React性能优化有哪些方法?【未完】
【解答】
1.Code Splitting。利用webpack打包分离去重实现动态导入,减少重复性代码块。
React.memo包裹函数组件,进行组件记忆
如果使用Component那就需要shouldComponent进行优化,也可以使用使用pureComponent组件代替Component,前提是组件的状态是值类型,如果是引用类型则会出现异常。
使用react中的lazy,Suspense懒加载组件
使用React Fragments避免额外渲染
不要使用内联函数,不要再render方法中操作状态,render函数应该保证纯净
使用immutable数据,避免很多浑然不知的bug
组件尽可能的拆分解耦,可以让部分组件避免不必要的domdiff;给列表类组件提供key,让domdiff可以实现最少的更新操作;
在constructor中使用bind来绑定this,而不在使用时绑定或减少使用箭头函数,因为constructor只在组件初始化时执行一次,而使用时绑定是每次render都会执行,箭头函数也是如此;按需引入props,避免多余更新;使用css隐藏节点而不是强制加载和卸载;使用React.Fragment减少不必要DOM。
使用事件节流和防抖
使用CND托管文件和资源
使用Reselect避免重复计算样式:
组件少写内联样式
使用css动画代替js动画
transform: rotate(0deg) 开启css加速,屏蔽非标准webkit带来的bug
6.使用SSR,可以在服务端生成html后返回到客户端,使客服端能快速看到完整渲染的页面。
7.使用react性能查看工具了解组件加载到卸载的情况,方便优化代码。
[Q] 描述事件在React中的处理方式?
收集面试题
1、哪些html标签会阻塞浏览器渲染,除了script还有哪些?长轮询的iframe会阻塞渲染