服务端渲染有样式,但客户端渲染却没有?
首先看这样一个demo
import React from 'react';
function App() {
return (
<p style={{ background:'cyan; xxx' }}>
App
</p>
);
}
export default App;
这个demo在服务端渲染时,刷新页面会发现p元素的背景色设置成功了。
但当此demo在客户端渲染时(多数SSR框架比如Next.js,在路由跳转时都采用客户端渲染)背景色却没设置成功。
到底是什么的不同造成了这样的差异呢?
在服务端渲染时,node端将调用renderToString()或renderToNodeStream方法生成HTML字符串。在浏览器访问时服务端直出HTML字符串,通过React.hydrate比较双端渲染结果是否一致,一致则直接使用服务端渲染的结果,并在已有标记上绑定事件监听器。
可以看到相比于客户端渲染,整个服务端渲染的流程中最大的不同是少了真实DOM的渲染。
接下来分析虚拟DOM映射到真实DOM的过程,也就是completeWork这个方法的调用栈,重点查看style属性设置的方法。
这里从方法名就能看出设置style属性的是哪个方法。
可以看到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;
}