盘点工作中遇到的bug(持续更新)

199 阅读2分钟

服务端渲染有样式,但客户端渲染却没有?

首先看这样一个demo

import React from 'react';

function App() {
  return (
    <p style={{ background:'cyan; xxx' }}>
        App
    </p>
  );
}

export default App;

这个demo在服务端渲染时,刷新页面会发现p元素的背景色设置成功了。

image.png

但当此demo在客户端渲染时(多数SSR框架比如Next.js,在路由跳转时都采用客户端渲染)背景色却没设置成功。

image.png

到底是什么的不同造成了这样的差异呢?

在服务端渲染时,node端将调用renderToString()renderToNodeStream方法生成HTML字符串。在浏览器访问时服务端直出HTML字符串,通过React.hydrate比较双端渲染结果是否一致,一致则直接使用服务端渲染的结果,并在已有标记上绑定事件监听器。

可以看到相比于客户端渲染,整个服务端渲染的流程中最大的不同是少了真实DOM的渲染。

接下来分析虚拟DOM映射到真实DOM的过程,也就是completeWork这个方法的调用栈,重点查看style属性设置的方法。

image.png

这里从方法名就能看出设置style属性的是哪个方法。

image.png

可以看到React中设置style属性的方式,就是设置Element.style属性。此属性会返回一个CSSStyleDeclaration对象,如果设置的css属性值有语法错误就会失败。

Raised if the specified CSS string value has a syntax error and is unparsable.

有趣的是我试了试通过Element.setAttribute()设置属性值,是可以成功的。

ele.setAttribute('style', 'background:cyan; xxx')

自定义hooks先触发useEffect还是子组件先触发useEffect?

const useTest = () => {

  useEffect(() => {
    console.log('child hooks mount')
  }, [])

  return 'test'
}

const Child = () => {
  useEffect(() => {
    console.log('child component mount')
  }, [])

  return <div>child</div>
}

function App() {
  useTest()

  useEffect(() => {
    console.log('parent mount')
  })

  return (
    <div className="App">
      <Child />
    </div>
  );
}

执行结果为

child component mount
child hooks mount
parent mount

源代码部分

var firstEffect;

  if (finishedWork.effectTag > PerformedWork) {
    // A fiber's effect list consists only of its children, not itself. So if
    // the root has an effect, we need to add it to the end of the list. The
    // resulting list is the set that would belong to the root's parent, if it
    // had one; that is, all the effects in the tree including the root.
    if (finishedWork.lastEffect !== null) {
      finishedWork.lastEffect.nextEffect = finishedWork;
      firstEffect = finishedWork.firstEffect;
    } else {
      firstEffect = finishedWork;
    }
  } else {
    // There is no effect on the root.
    firstEffect = finishedWork.firstEffect;
  }

参考链接

CSS-CSSStyleDeclaration