The cause of the incident is as follows:
I have a need to add a dark mode for my blog. I need to save the user's choices in loadstorage, but to publish the content to github Pages, I need to compile it in the node environment. There will be objects such as window and localstorage that cannot be found. If I want to solve this problem, I need to write the logic into useEffect.
However, the problem follows. After subsequent tests, it is found that the callback function of useEffect is after the onload event of the document, so the problem of style flicker will occur, because the browser has started drawing before the onload event. If you change the theme color at this time, there will be an obvious style change. For example, if the user defaults to dark mode, he can only load the white background first and then switch to black background after onload.
But after subsequent tests
- The componentDidMount of a class component occurs before the onLoad event,
- The function component has useEffect to simulate, and the componentDidMount occurs after onLoad, so we can't believe in function components. Ha ha 😂.
But at that time, it was like practicing with function components, so it couldn't be solved all the time, so we had to write the logic into the external js, but in Gatsby js is the index Html is hidden. If you want to add js scripts globally, you need to create additional HTML js for injection. However, in the traditional react, it is actually more convenient, which can be changed directly under the public folder.
Although I didn't try to replace it with a class component, I still think there will be flickering. It's best to mount the relevant logic into the native HTML at the top line of the body tag.
text
Thank you for the main station B Mountain people Help.
As the saying goes, practice is the only criterion for testing truth. There are many articles on the Internet about the life cycle of Vue and React, or the loading order of HTML documents, but I really haven't seen anyone discuss these two issues together. First, it is known that onLoad will occur after all script scripts are executed, even if the script is defer or async.
Directly to the test results, I also introduced the relationship between DOMContentLoaded event and browser rendering mechanism based on the previous discussion. See the mountain people's blog for the specific code and demonstration: https://www.idev365.com/frontend/zsxq/12.load-a-page/
Relationship with Vue
<html> <head> <script> console.log("script1 [start]") window.onload=function(){ console.log("onload [complete]") } document.addEventListener("DOMContentLoaded", () => console.log("DOMContentLoaded finish!")) console.log("script1 [end]") </script> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> <script> console.log("vue [start]") Vue.createApp({ template: '<div>Hello Vue 3.0</div>', beforeCreate(){ console.log("[app] before create") }, created(){ console.log("[app] created") }, beforeMount(){ console.log("[app] before mount") }, mounted(){ console.log("[app] mounted") }, beforeUnmount(){ console.log("[app] before unmount") }, unmounted(){ console.log("[app] unmounted") } }).mount("#root") console.log("vue [end]") </script> </body> </html>
The results are as follows:
script1 [start] script1 [end] vue [start] [app] before create [app] created [app] before mount [app] mounted vue [end] DOMContentLoaded finish! onload [complete]
Relationship with React class components
<html> <head> <script> console.log("script1 [start]") window.onload=function(){ console.log("onload [complete]") } document.addEventListener("DOMContentLoaded", () => console.log("DOMContentLoaded finish!")) console.log("script1 [end]") </script> <script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> </head> <body> <div id="root"></div> <script type="text/babel"> console.log("react [start]") class Hello extends React.Component { constructor(props) { super(props); console.log("[hello] constructor") } componentDidMount() { console.log("[hello] componentDidMount") } componentDidUpdate(prevProps, prevState, snapshot){ console.log("[hello] componentDidUpdate") } componentWillUnmount(){ console.log("[hello] componentWillUnmount") } render() { console.log("[hello] render") return <h1>Hello React</h1>; } } ReactDOM.render( <Hello />, document.getElementById('root') ); console.log("react [end]") </script> </body> </html>
script1 [start] script1 [end] DOMContentLoaded finish! react [start] [hello] constructor [hello] render [hello] componentDidMount react [end] onload [complete]
Relationship with React function components
<html> <head> <script> console.log("script1 [start]") window.onload=function(){ console.log("onload [complete]") } document.addEventListener("DOMContentLoaded", () => console.log("DOMContentLoaded finish!")) console.log("script1 [end]") </script> <script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> </head> <body> <div id="root"></div> <script type="text/babel"> console.log("react [start]") const { useEffect } = React function Hello(){ console.log("[hello] [start]") useEffect(function(){ console.log("[hello] [in useEffect]") }) console.log("[hello] [render]") return <h1>Hello React</h1>; } ReactDOM.render( <Hello />, document.getElementById('root') ); console.log("react [end]") </script> </body> </html>
script1 [start] script1 [end] DOMContentLoaded finish! react [start] [hello] [start] [hello] [render] react [end] onload [complete] [hello] [in useEffect]
My understanding
Let's start with the observed facts:
- It can be seen that in addition to the function components of React, each method is triggered by onLoad event at the end, waiting for the execution of other scripts.
- A strange phenomenon is that for Vue, the DOMContentLoaded event is triggered so late? In other words, is the execution priority of Vue script so high? This is something I can't figure out at present.
Combined with browser rendering mechanism
The above tests didn't solve the practical problems, because what we really want to implement is to solve the problem of style change (I think it's not the same problem as style flicker, which is a little different), so I studied some performance tools.
I found that the real browser rendering rules are very different from those in the eight part essay. For example, the real rendering of Chrome is actually divided into several stages.
For example, the figure above tells us some information. First:
- DCL indicates DOMContentLoaded event
- L stands for onLoad event
- FP represents the first paint
- FCP stands for first contentful paint
- LCP stands for large Contentful paint
Therefore, I believe that in DCL, the theme color and overall framework of the page have been rendered. If you wait for subsequent theme changes at this time, it will inevitably cause the problem of style flicker. As a result, it may be found that whether the style transformation is placed in the onLoad event or the DOMContentLoaded event, it will cause some flicker problems. And the browser will automatically select different rendering order according to the complexity of HTML. You can see that this DCL happened just before L.
So the best way is to put it on the top line of the body tag. This will not flicker.