一、vue中render函数与template对比。
介绍:
render --- js的方式做渲染。
tempalte --- html方式做渲染。
render里有一个函数h,这个h的作用是将单文件组件进行虚拟DOM的创建,然后再通过render进行解析。h就是createElement()方法:createElement( 标签名称, 属性配置, children )。
template是一种编译方式,但是template最终还是要通过render的方式再次进行编译。
区别:
1、render渲染方式可以让我们将js发挥到极致,因为render的方式其实是通过createElement()进行虚拟DOM的创建。逻辑性比较强,适合复杂的组件封装;
2、template是类似于html一样的模板来进行组件的封装,适合简单的组件封装;
3、render的性能比template的性能好很多;
4、render函数优先级大于template。
二、redux中createStore方法内部大概做了些什么?(也就是说store是怎么生成的?)
首先,执行createStore方法时,传入reducer,createStore方法返回一个对象,包含三个方法:getState,dispatch,subscribe。
subscribe方法相当于一个监听器,用来接收监听函数,并将监听函数push到一个数组中,重复的函数将不再被添加,当执行dispatch方法时,在dispatch方法内部,调用reducer,返回新的state,并执行数组中的所有监听函数,getState方法就是返回最新的state。
三、'true'==true的返回结果是什么?为什么?(相等运算符在计算的时候遵循的规则是什么?)
答案:
返回结果是false。
分析:
首先,相等运算符在比较不同的数据类型之前会强制转换。
在转换不同的数据类型时,相等运算符遵循下列基本规则:
1、如果有一边是布尔,会在比较之前将其转换为数值,false --> 0,true --> 1;
2、如果有一边是字符串,另一边是数值,在比较之前先将字符串转换为数值;
3、如果有一边是对象,另一边不是,则调用对象的valueOf()方法,用得到的基本类型按照前两条规则进行比较。
那么,'true'==true,根据规则1,就转换为'true'==1,再根据规则2,将字符串'true'转换为数值,转换为数值会调用Number()函数,Number()函数转换规则如下:
1、如果是Boolean值,true和false将分别被转换为1和0;
2、如果是数字值,只是简单的传入和返回;
3、如果是null值,返回0;
4、如果是undefined,返回NaN;
5、如果是字符串,遵循下列规则:
(1)如果字符串只包含数字(包括前面带加号或负号的情况),则将其转换为十进制数值,即'1'会变成1,'111'会变成111,'01234'会变成1234,这里的前面的0会被省略;
(2)如果字符串中包含有效的浮点格式,如"1.1",则将其转换为对应的浮点数值(同样,如果前面有0,如02.1,也会忽略前面的0);
(3)如果字符串中包含有效的十六进制格式,例如"0xf",则将其转换为相同大小的十进制整数值;
(4)如果字符串是空的(不包含任何字符),则将其转换为0;
(5)如果字符串中包含除上述格式之外的字符,则将其转换为NaN。
那么,按照规则5(5),'true'会被转换为NaN,最后就变为对NaN==1进行判断求值,又有NaN与任何值都不相等,包括NaN本身,所以最终的结果为false。
整个判断流程为:'true' == true --> 'true' == 1 --> NaN == 1 --> false。
四、Promise为什么可以实现链式调用,以及链式调用的执行顺序?
Promise为什么可以实现链式调用?
promise 的 then/catch 方法执行后会也返回一个 promise。
链式调用的执行顺序?
关于执行顺序,这里有两个结论:
1、当执行 then 方法时,如果前面的 promise 已经是 resolved 状态,则直接将回调放入微任务队列中;
2、当一个 promise 被 resolve 时,会遍历之前通过 then 给这个 promise 注册的所有回调,将它们依次放入微任务队列中。
关于结论1:
执行then方法是同步的,而then中的回调是异步的,同步代码执行时,会先将then中的回调先放入微任务队列,等同步任务执行完毕后,再依次取出执行,同时,在同步执行then方法时,会进行判断:
1、如果前面的 promise 已经是 resolved 状态,则会立即将回调推入微任务队列(但是执行回调还是要等到所有同步任务都结束后);
2、如果前面的 promise 是 pending 状态则会将回调存储在 promise 的内部(promse内部的一个数组中),一直等到 promise 被 resolve 才将回调推入微任务队列。
关于结论2:
对于会遍历之前通过 then 给这个 promise 注册的所有回调这句话的理解,来考虑一种情景,假设实例化一个promise,该promise被延迟resolve(比如将resolve的调用放入setTimeout函数中,设置延迟时间为3秒),但因为then方法是同步的,那么,在这个promise被resolve之前,可能已经通过then方法注册了几个回调函数,那么这几个回调函数不会被执行,也不会被放入到微任务队列中,它们会被这个promise内部储存起来,等到这个promise被resolve后,才会对之前储存的这几个回调函数进行遍历依次推入微任务队列(这里先存入的回调函数就先推入微任务队列),此时如果没有同步任务就会逐个取出再执行。
这里需要注意的点:
1、对于普通的 promise 来说,当执行完 resolve 函数时,promise 状态就为 resolved;
2、对于 then 方法返回的 promise 它是没有 resolve 函数的,取而代之只要 then 中回调函数代码执行完毕,这个 then 返回的 promise 就算被 resolve。
(原文参考链接:https://mp.weixin.qq.com/s/VfP9_8kUx6hyrII7LVae6g)
五、简单说一下es6装饰器,在react中,装饰器可以用来装饰函数组件吗?
介绍:
装饰器(Decorator)是一种与类(class)相关的语法,用来注释或修改类和类方法。装饰器是一种函数,写成@ + 函数名,它可以放在类和类方法的定义前面。装饰器函数的参数就是被修饰的类和类方法本身。如果觉得一个参数不够用,可以在装饰器外面在封装一层函数,该函数再返回一个函数,用来接收类和类方法。
在react中,装饰器可以用来装饰函数组件吗?
答案:
不能。
分析:
装饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。这意味着,装饰器能在编译阶段运行代码。也就是说,装饰器本质就是编译时执行的函数。
react函数组件本身是个函数,函数是存在函数提升的。在代码编译时,函数已经被定义,但是没有被赋值,在代码编译时,装饰器也已经执行完毕,等到代码运行的时候,装饰器已不再起作用了。
react函数组件本身是个函数,函数是存在函数提升的。在代码编译阶段,函数被定义,装饰器开始执行,但由于函数提升,函数体还没有任何内容,所以装饰器也就起不到他应有的装饰作用(也就是增强或者扩展函数功能)。(感觉这段之前的描述不太准确,故作修改。--- 20.09.02改)
六、简单说一下,如何在react中实现瀑布流加载?(左右两列的一个商品长列表)
分析:
首先,实现瀑布流加载,需要把长列表左右两列分开进行渲染,如果不这样做,按照正常思路合并为一个列表进行渲染的话,尽管每一个商品的高度不一样,但是在渲染的时候,每渲染完一行,左右两列中,短的一列会自动补白,用来填补高度,保持和长的一列一样高,这样最终的渲染结果就是每一行的两个商品是一样高的,最终左右两列也是一样高的,所以必须得左右两列分开渲染。
思路:
1、在componentDidMount中,可以直接对window进行滚动事件的监听,但是会出现因为被滚动父级盒子不是window,导致滚动事件不被触发的情况,这里只需要把addEventListener第三个参数设为true,即在捕获阶段就执行window滚动事件的回调函数;(这一步多写了,属于触底后加载数据的部分,故删除,那么在TheWaterfallFlow.jsx的componentDidMount中的给window添加滚动事件的监听这一步也作删除。--- 20.08.28改)
2、拿到总数据后,存入this.arr中,接着创建两个ref,假设分别为this.leftDiv和this.rightDiv,分别绑定到左右列表的单个商品的最外层容器盒子上(即进行循环的部分的最外层div),再设置两个响应属性,这里假设为leftData和rightData,定义为两个空数组,分别用来存储左右两列的数据;
3、在componentDidmount中,调用this.setState方法,先往两个空数组中分别存入一个数据(用的是数组的shift()方法),等到dom渲染完毕,在this.setState方法的第二个参数回调函数中,就可以通过this.leftDiv.current和this.rightDiv.current拿到两个dom元素,通过this.leftDiv.current.offsetHeight和this.rightDiv.current.offsetHeight可以获取到这两个dom元素的高度,然后再设置this.leftContainerHeight和this.rightContainerHeight把这两个高度分别存起来。这时候调用另外定义的一个函数,这里假设这个函数为this.handleLoadNextElement,这个函数内部通过判断this.leftContainerHeight和this.rightContainerHeight的大小关系,调用this.setState方法,往高度小的一边存入一个新的数据,和旧数据进行合并(展开运算符,数组的shift()方法),大的一边本次不存入新数据,如果相等,就往leftData里存入一个新数据,和旧数据进行合并,接下来的步骤就和上面所说的步骤差不多了,等到dom渲染完毕,在this.setState方法的第二个参数回调函数中,就可以通过this.leftDiv.current和this.rightDiv.current分别拿到两个列表的最后一个dom元素,通过this.leftDiv.current.offsetHeight和this.rightDiv.current.offsetHeight可以获取到这两个dom元素的高度,再分别将这两个高度累加到this.leftContainerHeight和this.rightContainerHeight中;
4、累加完高度,紧接着就是递归调用this.handleLoadNextElement了,进行高度的比较,接着存数据,再进行渲染,每次渲染完,在this.setState的第二个参数回调函数中,拿到左右两列列表的最后一个dom元素的高度,再进行高度的累加,如此反复,直到数据渲染完毕。
大概代码实现如下:
// TheWaterfallFlow.jsx
import React, { Component, createRef } from 'react'
import TheWaterfallFlowUI from './TheWaterfallFlowUI'
import axios from 'axios'
const get = ({url, params}) => {
return axios({
url,
method: 'get',
params
})
.then((result) => {
return result.data;
})
}
const post = ({url, data}) => {
return axios({
url,
method: 'post',
data
})
.then((result) => {
return result.data;
})
}
export default class TheWaterfallFlow extends Component {
constructor() {
super();
this.leftDiv = createRef();
this.rightDiv = createRef();
}
state = {
leftData: [],
rightData: []
}
handleLoadNextElement() {
if (!this.arr.length) return
if (this.leftContainerHeight <= this.rightContainerHeight) {
this.setState(
(preState) => {
return {
leftData: [
...preState.leftData,
this.arr.shift()
],
rightData: preState.rightData
}
},
() => {
if (this.leftDiv.current) {
this.leftContainerHeight += this.leftDiv.current.offsetHeight;
this.handleLoadNextElement();
}
}
);
} else {
this.setState(
(preState) => {
return {
leftData: preState.leftData,
rightData: [
...preState.rightData,
this.arr.shift()
]
}
},
() => {
if (this.rightDiv.current) {
this.rightContainerHeight += this.rightDiv.current.offsetHeight;
this.handleLoadNextElement();
}
}
);
}
}
async componentDidMount() {
// window.addEventListener('scroll', (e) => this.handleLazyLoading(e), true);
this.arr = [];
this.leftContainerHeight = 0;
this.rightContainerHeight = 0;
let result = await get({
url: 'url'
})
this.arr = result;
this.setState(
{
leftData: [this.arr.shift()],
rightData: [this.arr.shift()]
},
() => {
this.leftContainerHeight += this.leftDiv.current.offsetHeight;
this.rightContainerHeight += this.rightDiv.current.offsetHeight;
this.handleLoadNextElement();
}
);
}
render() {
return (
<TheWaterfallFlowUI
leftData={this.state.leftData}
rightData={this.state.rightData}
leftDiv={this.leftDiv}
rightDiv={this.rightDiv}
/>
)
}
}
// TheWaterfallFlowUI.jsx
import React from 'react'
export default function TheWaterfallFlowUI(props) {
return (
props.leftData.length > 0 && props.rightData.length > 0 &&
<div className="content">
<div>
{
props.leftData.map((item) =>
(
<div
key={item.id}
ref={props.leftDiv}
>
......
</div>
)
)
}
</div>
<div>
{
props.rightData.map((item) =>
(
<div
key={item.id}
ref={props.rightDiv}
>
......
</div>
)
)
}
</div>
</div>
)
}
如果发现有什么不足之处或者不正确需要修改补充的地方,欢迎指正,共同进步~~~