Maosheng

Maosheng 查看完整档案

广州编辑广东轻工职业技术学院  |  计算机多媒体技术 编辑中山市威盛网络科技  |  前端开发 编辑 segmentfault.com/u/maosheng_lms 编辑
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 个人简介什么都没有

个人动态

Maosheng 发布了文章 · 2019-08-24

关于React中的setState

在react中,setState是用以改变class组件状态的函数,它有两种用法:
一 传入一个updater函数,该函数有两个参数,一个是当前的state,还有一个是当前的props。该函数的返回值需要是一个更改的state值的对象,它将于state进行浅合并,其用法如下:

    this.setState((state, props) => {
        return { count: state.count + props.number };
    });

二 直接传入一个对象:

    this.setState({ count: this.state.count + this.props.number });

setState函数还可以接受第二个参数,该参数为一个函数,将在更改的State生效之后调用:

    console.log(this.state.count); // 1
    this.setState({ count: 0 }, () => {
        console.log(this.state.count); // 0
    });
    console.log(this.state.count); // ? 此处即可能是1,也可能是0

从上面代码可以看到,最后一行输出的count是不固定的,这是为什么呢?
因为在react中,class内的事件处理程序会默认进行批处理,即如果你在componentDidMount里面调用三次setState函数,那么它最终会在componentDidMount执行完毕后,将三个State的更改合并为一次调用。所以这时候setState就是异步的。
而在其他场景下,setState将会是同步的,例如setTimeout内, Promise的then里面。
一个简单的例子:

class SetStateExample extends Component {
  constructor() {
    super();
    this.state = {
      count: 0
    };

    this.onClick = this.onClick.bind(this);
  }

  componentDidMount() {
    console.log('componentDidMount before', this.state.count);
    this.setState({ count: this.state.count + 1 });
    console.log('componentDidMount after', this.state.count);
  }

  onClick() {
    console.log('onClick before', this.state.count)
    this.setState({ count: this.state.count + 1 }, () => {
      console.log('setState callback', this.state.count);
    });
    console.log('onClick after', this.state.count);
    Promise.resolve().then(() => {
      console.log('promise.then before', this.state.count);
      this.setState({ count: this.state.count + 1 });
      console.log('promise.then after', this.state.count);
      this.onClassEvent();
    });
  }

  onClassEvent() {
    console.log('onClassEvent before', this.state.count);
    this.setState({ count: this.state.count + 1 });
    console.log('onClassEvent after', this.state.count);
  }

  render() {
    return <div className="test">
      <div>count: {this.state.count}</div>
      <button onClick={this.onClick}>点击改变count</button>
    </div>;
  }
}

让我们运行结果:

clipboard.png

首先第一第二行输出是在componentDidMount里面,我们在函数内调用了setState,并在前后分别输出了改变的值,结果表明,函数调用前与函数调用后该值并没有立即改变,则表明在这里setState是一个异步调用。那么初步判定在生命周期函数内部,setState是异步的调用。

然后第三第四行输出是在onClick函数的回调里面,该函数定义在class中,通过用户点击触发。在setState调用前后我们的输出结果是一致的,这也表明其是一个异步调用。而在setState第二个参数中我们输出了改变后的count, 即第五行输出,表明我们的更改生效了。

然后第六行以后的输出是我们在onClick函数内调用了promise.resolve().then()输出的,它是一个异步调用,react是无法知道它什么时候执行,什么时候完成执行的,所以这时候react默认setState是同步的。从输出我们可以看到每次更改之后,state的值都是立即变化生效的。

而在promise的回调内,我们还调用了一个定义于class内的事件函数,但是该事件函数内的setState也是同步的形式。这说明了setState的同步或者异步与其定义位置并没有直接的关系,而应该取决于是否由React直接进行调用,因为只有是React直接调用的情况下,它才知道该函数什么时候执行完毕,才能进行批处理的优化。否则则默认是同步的调用。(具体内部实现就不展开了,因为我也不是特别懂HHHH,反正意思就大概是这么个意思)

所以当在一些回调内部调用setState时应该注意将多个setState合并,因为它是同步的,多次更新状态会很影响性能。
以及需要注意进行异步调用的时候,如果需要使用变化后的值,请确保在异步调用完成后,一般是在setState的回调内,或者在componentDidUpdate钩子内,但是请注意小心使用,因为很容易一不小心导致循环调用而崩溃。

如果你想在本应同步调用的回调内,对setState进行异步调用,即让它进行批处理,React也提供了一个API:

promise.then(() => {
  // Forces batching
  ReactDOM.unstable_batchedUpdates(() => {
    this.setState({a: true}); // Doesn't re-render yet
    this.setState({b: true}); // Doesn't re-render yet
    this.props.setParentState(); // Doesn't re-render yet
  });
  // When we exit unstable_batchedUpdates, re-renders once
});

在unstable_batchedUpdates内部进行的setState会是异步调用,但是该API是不稳定的,因为后续的React版本更新中将会默认进行批处理即异步调用,届时该API将被删除。而这个后续的版本,很可能就是React 17

记录与分享,欢迎斧正,虚心求教

参考连接:
https://stackoverflow.com/que...
https://react.docschina.org/d...
https://github.com/Advanced-F...
https://github.com/sisterAn/b...

查看原文

赞 0 收藏 0 评论 0

Maosheng 赞了回答 · 2019-07-18

解决求助js正则匹配style并添加内容

我也不明白为啥不用css...

s.replace(/<(img).*?(\/>|<\/img>)/g, function(a){
    if(a.indexOf('style') < 0 ){
        return a.replace(/<\s*img/, '<img style="max-width:100%;"');
    }else{
        return a.replace(/style=("|')/, 'style=$1max-width:100%;')
    }
});

关注 2 回答 2

Maosheng 提出了问题 · 2019-07-17

解决求助js正则匹配style并添加内容

我有一段富文本,内容如下:

var s = '<img class="img1" data-original="aaa.jpg" /><p class="p">asde</p><img class="img2" data-original="bbb.png" style="width:900px;" ></img><img style="height:80px;" class="img3" data-original="ccc.gif" />';

请问如何使用正则将上述字符串的img全都加上max-width:100%;的属性.
由于某些img中已经存在style属性,如果有的话需要将max-width:100%;加到原来的style里面不影响本来就在其中的属性。

正则学的不好,脑壳疼
十分感谢

关注 2 回答 2

Maosheng 发布了文章 · 2019-06-30

js的类型转化三两事儿

在js中,类型转换是一个被非常多人诟病的地方。新手看了会发矇,老手看了会头疼。

类型转换,又成为强制类型转换,主要区分为显式强制类型转换隐式强制类型转换

按我理解,类型转换的意思就很明显,就是当程序运行时需要此刻的变量的类型与变量的实际类型不符时,就会进行强制转换,在一些静态语言中,这个过程发生在编译阶段,或者干脆就抛出错误。而在js中,这个转换过程发生在运行时,所以你写代码的时候并不会意识到自己已经掉进坑里了。

显式强制类型转换,简单的说,就是你觉得你可以明面上一眼看出来的,比如‘’ + number,+string,Boolean(value); 而隐式强制类型转换就是反过来的意思。

举个栗子:

var a = 'value';
if (a == true) {
  console.log('a is true');
} else if (a == false) {
  console.log('a is false');
}

按照正常的脑回路,一般人不会这么写。但是确实是有人会写if (a == true) {...}这种语句,当然,后面的else if (a == false)是为了节目效果加的。即便是这样,a == true这种写法也是不可取的。

最后的输出是,两个都不输出。即在这个隐式强制类型转换中,a即不会等于true, 也不会等于false。

首先解释一下为什么a既不等于true, 也不等于false。按照正常人类的脑回路,应该是将a先转换为布尔值,然后再将两个布尔值对比。这种情况下,a转成布尔值只能转换成true或者false。要是这么想,那你就是too young, too simple了

当使用相等操作符==进行判断时,将遵循以下规则(来自红宝书):

· 如果又一个操作数是布尔值,则在比较相等性之前先将其转换为数值(false转换为0,而true转换为1
· 如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值
· 如果一个操作数是对象,另一个操作数不是,则调用对象的valueOf()方法,用得到的基本类型值按照之前的规则进行比较
· null和undefined是相等的
· 要比较相等性之前,不能将null和undefined转换成其他任何值
· 如果一个操作数是NaN,则相等操作符返回false。
· 如果两个操作数都是对象,则比较它们是不是同一个对象,是则返回true,否则返回false

所以就可以知道上例中,转换是如何进行的。首先两个相等操作的操作数中都包含布尔值,先将布尔值转换为数值所以这里true转换为1,false转换为0。接下来再次进行比较,此时两对操作数中都包含一个字符串操作数,则将字符串转换为数值, a转换为数值是NaN,此时再次进行比较,由于有一个操作数是NaN,按照规则,返回false。所以a既不等于true也不等于false。

由上可以看出,使用==进行条件判断的时候是非常具有危险性的,尤其是当其中一个操作数为true的时候,如果另一个操作数不为布尔值,则很有可能就掉进坑里了。

那么一般在判断的时候怎么去进行判断比较好呢。我认为尽量不使用==操作符。使用===进行全等判断,或者直接将你需要判断的值丢进if判断里,比如if (a == true)可以改成if (a)或者if (!!a)这样就能避开==操作符的坑了。如果一定要使用==进行比较,不用不舒服,则最好先将操作数进行显式类型转换为同一类型的数据,再进行比较,

知其然就要知其所以然。
为什么if (a)if (!!a)就可以避开==操作符的坑呢?它们进行强制类型转换时的转换规则又是怎样的。

其实在if的判断语句块内,如果判断值不是布尔值的话,会自动调用Boolean()函数进行布尔值的转换。而!!则相当于一次Boolean()的值类型转换了。所以在if的判断语句里直接放入判断条件和显式的将判断条件进行转换,其作用是一样的。区别也只是在于是否对后续看代码的人友好了,如果后面代码维护可能是接触代码不深的新手,则建议进行显式的转换。

由上面的解释可以知道if (a)if (!!a)if (Boolean(a))的作用是一样的,所以这里只要了解一下Boolean()函数的转换规则就可以了:

任何非空字符串为true,空字符串('')为false
任何非零数字值(包括无穷大)为true,0和NaN为false
任何对象为true,null为false
undefined为false

以上规则来自红宝书,部分内容省略

看以上的Boolean()函数的转换规则,与我们日常工作中所需要的类型转换是非常契合的。
比如我们普遍会认为并要求if可以拦截空字符串,false,null,undefined,0与NaN

需要注意的是,虽然红宝书上并没明确指出,但三元运算符( ? : )使用的转换规则也是Boolean()函数的转换规则.

而除了条件判断,在其他一些操作里也会出现类型转换,比如关系操作符(<, >, <=, >=):

var a = '23';
var b = '3';
console.log(a < b); // true

上面这个转换可能很多的前端开发工程师都掉进过坑里并摸不着头脑。
可能你也看出来了出现‘23’小于‘3’的原因或许是在于它们都是字符串。如果关系操作符(<,>,<=,>=)两边都是字符串,则比较两个字符串对应的字符编码。'2'的字符编码是50,而3的字符编码是51,所以这里出现了‘23’小于‘3’的情况。

到这里很多人都会惊叹一声,并表示记住了此知识点。但是这样往往是不够的,你需要深挖下去,需要确切的了解关系操作符的转换规则,才能确保不会再次掉进它的坑里。

如果两个操作数都是数值,则执行数值比较 如果两个操作数都是字符串,则比较两个字符串对应的字符编码值
如果一个操作数是数值,则将另一个操作数转换为一个数值,然后执行数值比较
如果一个操作数是对象,则调用这个对象的valueOf()方法,并用得到的结果根据前面的规则进行比较
如果一个操作数是布尔值,则先将其转换为数值,然后再执行比较
如果一个操作数是NaN,undefined,则返回false
如果操作数为null, '', 则转换为0进行比较

多看看红宝书就会发现js中的类型转换其实坑还是很多的,还有一些加减乘除的操作符本身也会产生类型转换的问题。但是基本问题不是太大,所以就不单独拎出来了。

最后,一道思考题:

// do something ...
console.log(typeof a); // 'object'
console.log(a == false); // true

上面的对象为什么会等于false,这个题是之前刷到的一道面试题,可不是我瞎编的。。

最后,欢迎斧正,没有仔细的准备,所以写的少且杂,也是作为自己的一个总结回顾吧。

查看原文

赞 0 收藏 0 评论 0

Maosheng 收藏了文章 · 2019-05-30

前端常见问题——安卓文本无法垂直居中

安卓文本无法垂直居中的问题

问题

安卓 webview 的字体大小在小于 12 像素的时候无法使用行高来垂直居中

ps:目前在微信等应用都存在该问题,而在最新的移动端 Chrome 浏览器上无该问题(截止本文编写时间,微信客户端的 chrome 版本为 57,chrome 版本为 70)。

尝试方案

  1. table 布局:文本偏上

    <div class="solution" style="display: table; height: 16px;">
      <span style=" display: table-cell; font-size: 10px; vertical-align: middle;">hot 热门</span>
    </div>
  2. flex 布局:文本偏上

    <div class="solution" style="display: inline-flex; align-items: center; height: 16px; line-height: 1; font-size: 10px;">
      <span>hot 热门</span>
    </div>
  3. transform 缩放:文本居中了,但是 transform 不能还原元素在 dom 上的占用区域大小

    <div class="solution" style="height: 32px; line-height: 32px; font-size: 20px; transform: scale(0.5, 0.5); transform-origin: left top;">
      <span>hot 热门</span>
    </div>
  4. zoom 缩放:文本偏上

    <div class="solution" style="height: 32px; line-height: 32px; font-size: 20px; zoom: 0.5;">
      <span>hot 热门</span>
    </div>
  5. 固定高度+内边距+行高设定为字体大小:文本偏上

    <div class="solution" style="box-sizing: border-box; height: 16px; padding: 3px 0; line-height: 10px; font-size: 10px;">
      <span>hot 热门</span>
    </div>
  6. 固定高度+内边距+行高设为 normal:文本偏上

    <div class="solution" style="box-sizing: border-box; height: 16px; padding: 3px; line-height: normal; font-size: 10px;">
      <span>hot 热门</span>
    </div>
  7. 内边距+行高设为 normal:文本居中,但在部分客户端上不居中

    <div class="solution" style="box-sizing: border-box; padding: 2px; line-height: normal; font-size: 10px;">
      <span>hot 热门</span>
    </div>
  8. 行高+字体大小设为 initial:文本居中,在最新的 Chrome 浏览器上不居中

    <div class="solution" style="line-height: 16px; font-size: initial;">
      <span style="font-size: 10px;">hot 热门</span>
    </div>

Edit 安卓文本垂直居中

安卓文本垂直居中

微信渲染效果

解决方案

在不同的安卓客户端上测试上述方法发现以下三个方法或许可以帮助解决居中问题,我们可以根据实际客户端的支持情况来选择其中一种方式来解决无法居中问题。

  1. transform 缩放

    <div class="solution" style="height: 32px; line-height: 32px; font-size: 20px; transform: scale(0.5, 0.5); transform-origin: left top;">
      <span>hot 热门</span>
    </div>
  2. 内边距+行高设为 normal

    <div class="solution" style="box-sizing: border-box; padding: 2px; line-height: normal; font-size: 10px;">
      <span>hot 热门</span>
    </div>
  3. 行高+字体大小设为 initial

    <div class="solution" style="line-height: 16px; font-size: initial;">
      <span style="font-size: 10px;">hot 热门</span>
    </div>

参考文献

查看原文

Maosheng 提出了问题 · 2019-04-10

微信支付安全域名配置需要加上hash值吗?

使用react开发微信公众号H5,涉及到支付的问题,有点脑壳疼
安全域名的配置需要加上hash值吗?

比如实际连接为 http://www.xxx.com/#/pay
那安全域名的配置需要配置完整的(http://www.xxx.com/#/pay)?
还是只需要http://www.xxx.com就行

还有个页面是将商品id放在目录里面了, 这样搞就要改,
url大概是这样 http://www.xx.com/#/{商品id}/pay

这样是不是改成http://www.xx.com/#/pay?id={商品id} 好一点?

关注 2 回答 1

Maosheng 提出了问题 · 2019-04-08

mongodb表设计咨询

自己做的一个练手项目,有个功能是需要展示商家的菜单,但是不清楚怎么设计表的结构。(因为没玩过这东西,本人本职是前端)

构思的界面大概是这样的

clipboard.png

clipboard.png

本来是打算用一个商家表搞定全部的,但是想了想,好像这样整的话,菜单那部分不好弄。
有没有大佬弄过类似的,给个思路,衷心谢谢了
不一定就要mongodb数据库, 给个差不多的思路

目前我做的表是这样的,但是在菜单这里的数据自我感觉不太好

clipboard.png

关注 2 回答 1

Maosheng 赞了文章 · 2019-02-14

附实例!图解React的生命周期及执行顺序

本文由云+社区发表

作者:前端林子

1.七个可选的生命周期

可以结合下图来看:

img

(1) componentWillMount() 仅在render()方法前被调用一次,如果在该方法中调用了setState方法去改变组件的状态值,那么调用render()后,将会直接看到改变过了的状态值,并且不论状态值怎么改变,componentWillMount()都不会再被调用。

(2) componentDidMount() 仅在render()方法后被立即调用一次(客户端),相对于父组件而言,该方法在子组件中会先被调用。如果需要使用一些JaveScript框架或者类似于setInterval()这样的方法,建议在该方法内使用。

(3) ShouldComponentUpdate(object nextProps, object nextState) 在初始渲染调用render()方法时不会被调用,后面在接受到新的state或者props时,在render()方法前被调用。为防止一些潜在的bug,该方法默认总是返回true。如果你确定state及props改变后不需要渲染组件,那么也可以指定返回false,需要注意的是,这样的结果会导致后面的render()、componentWillUpdate()、componentDidUpdate()都不会被调用。

一般的,我们可以通过该函数来优化性能:

一个React项目需要更新一个小组件时,很可能需要父组件更新自己的状态。而一个父组件的重新更新会造成它旗下所有的子组件重新执行render()方法,形成新的虚拟DOM,再用diff算法对新旧虚拟DOM进行结构和属性的比较,决定组件是否需要重新渲染

无疑这样的操作会造成很多的性能浪费,所以我们开发者可以根据项目的业务逻辑,在shouldComponentUpdate()中加入条件判断,从而优化性能

例如React中的就提供了一个PureComponent的类,当我们的组件继承于它时,组件更新时就会默认先比较新旧属性和状态,从而决定组件是否更新。值得注意的是,PureComponent进行的是浅比较,所以组件状态或属性改变时,都需要返回一个新的对象或数组

(4) componentWillReceiveProps(object nextProps) 在初始渲染调用render()方法时不会被调用,当接收到一个新的props时,该方法被调用。我们都知道,如果改变一个状态的值,则会触发render()方法,所以可以在这个方法里调用setState()方法去改变一个状态的值,当该方法接收到新的props时,setState()就可以避免一次额外的render()了。 在这个方法里,尤其需要注意一点,就是接收到新的props一定会触发render()方法,但是render()方法被触发不一定是因为接收到了新的props

(5) componentWillUpdate(object nextProps, object nextState) 在初始渲染调用render()方法时不会被调用,当接收到新的props及state时,在render()方法之前被调用。

不要在此方法再去更新props 或者 state

(6) componentDidUpdate(object prevProps, object prevState) 在初始渲染调用render()方法时不会被调用,当组件更新被刷新到DOM之后被立即调用。

可以在这里访问,并修改 DOM

(7) componentWillUnmount() 在组件从DOM上卸载前被调用,在这个方法里面,我们主要是完成一些清除操作,比如说清除掉一些过时了的定时器等。

2.执行顺序及次数

(1) getDefaultProps(),调用1次

(2) getInitialState(),调用1次

(3) componentWillMount(),调用1次

(4) render(),调用>=1次

(5) componentDidMount():仅客户端,调用1次

(6) componentWillReceiveProps(object nextProps),调用>=0次

(7) ShouldComponentUpdate(object nextProps, object nextState),调用>=0次

(8) componentWillUpdate(object nextProps, object nextState),调用>=0次

(9) render(),调用>=1次

(10) componentDidUpdate(object prevProps, object prevState),调用>=0次

(11) componentWillUnmount(),调用1次

3.实例

我写了一个小demo可直接在浏览器里运行,大家可以通过控制台查看父组件、子组件中的各生命周期调用的顺序:

<!DOCTYPE html>

<html>

    <head>

        <script data-original="https://fb.me/react-15.2.0.js"></script>

        <script data-original="https://fb.me/react-dom-15.2.0.js"></script>

        <script data-original="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>

    </head>

<body>

    <div id="app-container"></div>

    <script type="text/babel">
        var SubCounter = React.createClass({
            componentWillReceiveProps:function() {
                console.log('9、子组件将要接收到新属性');
            },

            shouldComponentUpdate:function(newProps, newState) {
                console.log('10、子组件是否需要更新');
                if (newProps.number < 5) return true;
                return false
            },

            componentWillUpdate:function() {
                console.log('11、子组件将要更新');
            },

            componentDidUpdate:function() {
                console.log('13、子组件更新完成');
            },

            componentWillUnmount:function() {
                console.log('14、子组件将卸载');
            },

            render:function() {
                console.log('12、子组件挂载中');
                return (
                        <p>{this.props.number}</p>
                )
            }
        });

        var Counter = React.createClass({
           
            getInitialState:function(){
                return(
                    this.state={
                        number:0
                    }
                )
            },

            componentWillMount:function(){
                console.log('3、父组件挂载之前');
            },

            componentDidMount:function(){
                console.log('5、父组件挂载完成');
            },

            shouldComponentUpdate:function(newProps, newState) {
                console.log('6、父组件是否需要更新');
                if (newState.number<15) return true;
                return false
            },

            componentWillUpdate:function() {
                console.log('7、父组件将要更新');
            },

            componentDidUpdate:function() {
                console.log('8、父组件更新完成');
            },

            handleClick : function(){
                this.setState({
                    number: this.state.number + 1
                })
            },
            render:function() {
                console.log('4、render(父组件挂载)');
                return (
                    <div>
                        <p>{this.state.number}</p>
                        <button onClick={this.handleClick}>+</button>
                        {this.state.number<10?<SubCounter number={this.state.number}/>:null}
                    </div>
                )
            }
        });        

        ReactDOM.render(<Counter />, document.getElementById('app-container'));

    </script>

</body>

</html>

点击一次按钮,通过控制台可以看到:

img

4.小结

本文主要是图文结合地介绍了react的生命周期及执行顺序,同时附上了一个实例,可以清楚地看到父组件、子组件的调用顺序。如存在问题,欢迎指正~~~

此文已由腾讯云+社区在各渠道发布

获取更多新鲜技术干货,可以关注我们腾讯云技术社区-云加社区官方号及知乎机构号

查看原文

赞 31 收藏 25 评论 3

Maosheng 赞了回答 · 2019-01-21

解决在react中是否有类似小程序的block,vue的template元素一样的,只做包裹,不渲染的

react 可以渲染数组,可以实现你所说的需求。
你可以把你要渲染的多个组件放入数组中。

{[<组件1/>,<组件2/>,...]}

关注 3 回答 2

Maosheng 提出了问题 · 2019-01-19

解决在react中是否有类似小程序的block,vue的template元素一样的,只做包裹,不渲染的

因为react中的组件只能有一个根元素,导致必须要渲染多个元素的时候都要建一个元素包裹住,显得很多余很不爽

但是vue是有只包裹不渲染的元素的,类似的小程序也有,而我看react的文档似乎没提到有这东西

那么请问一下react中有包裹元素吗

关注 3 回答 2

认证与成就

  • 获得 32 次点赞
  • 获得 36 枚徽章 获得 1 枚金徽章, 获得 7 枚银徽章, 获得 28 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2017-02-21
个人主页被 734 人浏览