Pages were originally rendered on the server side, in PHP, JSP and other technologies, where the server filled in the data through a template engine and returned the generated HTML for the browser to render. At that point, the form is submitted synchronously, and the server returns the HTML of the resulting page.
Later, with Ajax technology, browsers could asynchronously request that the server return XML or JSON. Ajax was originally based on XML, which is how it got its name. Json became popular later because XML had a lot of unnecessary tags and a lot of content.
The data interaction between the web page and the server becomes asynchronous, allowing the server to return JSON data, concatenate HTML in the browser, and render (dom generation in the browser is equivalent to rendering). There was little need to refresh the page, so SPA (Single Page Application) evolved.
Early page development is based on the browser DOM API operation DOM to do rendering and interaction, but THE DOM API is relatively wordy, and browser compatibility problems at that time is also more troublesome, different browsers have different writing methods. Jquery was in its heyday when it came along and quickly became popular in order to simplify DOM manipulation and make it easier to work with browsers.
I’ve always been used to dividing web pages into the physical layer and the logical layer. Even if the DOM is the physical layer, jquery is a set of utility functions that manipulate the DOM and also work on the physical layer.
Basically, what a web page does is take the data and render the DOM, and update the DOM after the data changes. This process is universal. Later, the MVVM framework emerged to automatically map the data changes to the DOM, eliminating the need to manually manipulate the DOM. Vue, React and other modern front-end frameworks. I call this layer the logical layer.
In addition to providing the function of data-driven view changes, the front-end framework also supports the logical division of DOM. Part of DOM can be encapsulated into components, and components can be combined with each other to form the entire interface. The physical layer is still dom, but with automatic mapping of data to DOM, we only need to write components in the logical layer.
Now the front-end will not learn the physical layer of DOM manipulation jquery, but directly from the logical layer of vue, React front-end framework.
This is not to say that jquery is not needed at all. The front-end framework mainly deals with the binding of data to the DOM, and the DOM can be updated automatically after changes. If you don’t need to update the DOM, you can just manipulate the DOM directly, such as various active pages, with no data update, and jquery is still very convenient to manipulate the DOM.
The front-end framework is based on the declarative idea of UI = F (State). You only need to declare component views, component state data, and dependencies between components, and then state changes will automatically update the DOM. Jquery’s library of tools that manipulate the DOM directly is imperative.
React and Vue use different methods to describe views. React extends the JSX syntax to JS, implemented by Babel, and allows you to write logic directly in JS when describing views. Vue is a TEMPLATE DSL that implements a set of template syntax, including interpolation, instructions, filters, etc. Compared with JSX, it is more concise. The compiler of Template is implemented by VUE.
The Vue template is limited and can only access data, prop and Method, which can be used for static analysis and optimization. However, the React JSX cannot be used for static analysis and optimization because it is directly a JS syntax and has more dynamic logic.
However, vue templates are not all good because they are separated from the JS context and make it difficult to do type derivation in typescript, requiring a separate declaration of all prop, Method, and data types. React JSX is already in the same context as JS, and typescript is a natural fit.
So both Vue Template and React JSX have their advantages and disadvantages.
Front-end frameworks are data-driven view changes, and this data is scattered across each component. How do you update the DOM after the data changes?
There are basically only three ways to detect data changes: watch, dirty check and no check.
Vue is a date-based watch. The component level listens for changes in Object properties through Object.defineProperty, and overrides the array API to listen for changes in array elements, followed by DOM updates.
Angular updates the DOM based on dirty checking, comparing the data after every possible change in logic.
React renders the entire DOM every time. This is because we don’t render directly into the DOM. Instead, we add a layer of virtual DOM in the middle, render into the virtual DOM each time, and then update the corresponding DOM if the virtual DOM rendered under diff changes.
These are the three ways in which data-driven views of the front-end framework change.
Vue is a component-level data watch. When there are a lot of changes in the monitoring data inside the component, an update may require a large amount of calculation, which may lead to frame loss, namely rendering lag. So vue optimizes by breaking up large components into small pieces so that there aren’t too many Watchers per data.
React doesn’t listen for data changes. Instead, it renders the entire virtual DOM and diff. The optimization based on this scheme is to skip rendering with shouldComponentUpdate for components that do not need to regenerate the VDOM.
ShouldComponentUpdate skips part of the component rendering and can still be extremely computative when the application component tree is extremely large. A large amount of calculation may also lead to render lag, how to do?
Tree traversal can be done depth-first or breadth-first. Component tree rendering is depth-first. It is usually done recursively, but if the path can be recorded through a linked list, it can become a loop. It becomes a loop, so it can be segmented by time slice, so that the generation of the VDOM does not block page rendering, just as the operating system schedules multiple processes.
React Fiber is a function that changes the generation of the VDOM from recursion to loop by changing the component tree to a linked list.
Compared with the previous component nodes, fiber node has no parent and children attributes, but more child, Sibling and return attributes.
Optimized rendering performance with fiber linked list tree.
As you can see, vUE performance tuning is not the same as React performance tuning:
Vue is a solution for component-level data monitoring. The problem can occur when there are too many watchers for one property, so the optimization idea is to break the large component into small pieces, ensuring that there are not too many Watchers for each property.
React does not listen to and check data changes, render vDOM every time, and then compare vDOM. ShouldComponentUpdate should skip the render of some components. React also has a component tree in it called Fiber to change the recursion into interruptible rendering, gradually generating the entire VDOM in time slices.
React and Vue have different solutions:
The component of VUE is the way of option object, so it is natural to think of the logical reuse way of mixin through object attribute. The logical reuse scheme of vue2 component is mixin, but mixin is difficult to distinguish the source of mixed attribute and method, which is messy and has poor code maintenance. But there is no better plan.
React also initially supported mixins, but later abandoned them.
React components have both class and function forms, so it is natural for high-order components like high-order functions to perform part of the logic in the parent component and then render the child component.
There is no logical way to pass that PART of JSX as props to another component to reuse, other than to add a layer of component HOC.
HOC and Render props are two logical reuse schemes supported by the React class component.
The original Function component is stateless and exists only as an aid to class component rendering.
However, the logic reuse of HOC eventually leads to too deep nesting of components, and the internal life cycle of the class is too much, and all the logic put together leads to large components.
How to solve the problem of deep nested class components and large components? And don’t introduce disruptive updates, which could end badly.
The React team then looked at the function component and wondered if it would be possible to support state in the function component by extending the API.
The function component needs to support state. Where does state exist?
The class component node has state, and the fiber node still has state. The function component does not have state, so the fiber node does not have state.
Add state to the fiber node of the function component.
React adds the memorizedState property to the Fiber node of the Function component to store data, and then uses that data in the Function component via the HOOKS API.
Since we use data from fiber nodes, we named the API useXxx.
Each hooks API has its own place to store data. How do you organize it? There are two solutions, map and array.
With Map, the hooks API specifies keys that access data in the Fiber node.
With arrays the order cannot be changed, so the hooks API cannot appear in logic blocks like if, only at the top level.
To simplify usage, hooks end up using arrays. Of course, this is implemented using linked lists.
Each hooks API takes data from the corresponding Fibre.memoriedState to use.
The Hooks API falls into three categories:
The first type is data:
- UseState: Store data in fiber. MemoriedState
- UseMemo: Stores data in the corresponding element in Fiber. MemoriedState. Values are computed by cached functions and recalculated after state changes
- UseCallback: Storing data in the corresponding element of Fiber. MemoriedState, where the value is a function, and re-executing the function after the state changes, is a simplified API for useMemo in the scenario where the value is a function. For example, useCallback(fn, [a,b]) equals useMemo(() => fn, [a,b]).
- UseReducer: Store data in the corresponding element of fiber. MemoriedState. The value is the result returned by the reducer
- UseRef: store data in the corresponding element in fiber. MemoriedState in the form of {current: specific value}.
UseState is the simplest way to store values. UseMemo executes functions based on state and caches the results, which is the equivalent of a getter for a Vue. UseCallback is a simplification of the case where values are functions. UseReducer uses actions to trigger value changes. UseRef packages a layer of objects that are the same every time you compare them, so you can put some data that doesn’t change.
Either way, these hooks apis return values.
The second type is logical:
- UseEffect: This function is executed asynchronously, again when the dependency state changes, and the returned cleanup function is called when the component is destroyed
- UseLayoutEffect: Execute the function synchronously after rendering to get the DOM
Both of these hooks apis are used to execute logic, logic that does not need to be rendered can be put in useEffect.
The third class is dedicated to ref forwarding:
Data can be shared through various schemes, but dom elements have to be forwarded by ref. Ref forwarding is when a ref is created in the parent component and the child component passes the element. You can use useImperativeHandle to make changes you want to make before uploading.
Using these three hooks apis, and more hooks to be added later, function components can also store state and perform logic in phases, providing an alternative to class components.
And, more importantly, the hooks API is a form of function calls that pass parameters, which further encapsulates a more powerful function: custom hooks. This way you can reuse logic across components.
Let’s go back to where we started with class components that were too deeply nested and components that were too large.
- Logical extensions don’t need nested hoc anymore, just call a custom hooks
- Component logic is no longer written in the class; it can be isolated into separate hooks
React addresses the logic reuse of the class component through the hooks API of the Function component. (Fiber is for performance, hooks are for logic reuse)
Vue2 uses logic in the way of mixins, and the problem of too large components can also be solved in vuE3 through similar thinking.
In order to feel more like the native experience, it is now mostly a single-page application without refreshing the page, a browser rendering (CSR) scheme that takes data from the server and drives the DOM changes. However, for some low-end machines, server-side rendering (SSR) is still required. Instead of going back to the template engine server-side rendering of the JSP and PHP era, render it as a string based on the same component tree. Server rendering and browser rendering use the same component code, which is an isomorphic solution.
Technology from the emergence to the improvement of the surrounding ecosystem is a cycle, from the beginning of the server side rendering, to the later client side rendering, and then the logical layer of the component scheme, and finally to re-implement the server side rendering based on the component scheme. In fact, the physical layer has not changed, but the logical layer is constantly added one layer after another, in order to improve production efficiency, reduce development costs, and ensure quality, which is also the trend of technological development.