Thank you very much for all the problems you pointed out in the comments. Due to this year, there have been a lot of changes, so it has been delayed, has been fixed the problems and suggestions pointed out by the leaders. Please rest assured to eat! Thanks ~ 🥳

Did not expect the last article can be so popular, excited. 🤩. But it’s also scary, and that means responsibility. In the next part, many knowledge points need more in-depth research and understanding. The blogger is also limited in level and worried that he can’t bear everyone’s expectations. But after all, we still need to straighten out our mentality, put aside our emotions, focus on every word, not only to ourselves, but also to the community. Learn from each other and grow together with each other.

Recently, I have been busy with business and my energy is limited. Although I try to be rigorous and revise repeatedly, there must be some omissions in the article. In the last article, many friends pointed out many problems, for which I am deeply sorry, I will humbly accept and correct mistakes. I am also very grateful to so many friends who have discussed with me through wechat or public accounts. Thank you for your support and encouragement.

The introduction

As you know, React is now dominating front-end development. Excellent performance, strong ecology, make it unstoppable. The five companies on the blog are all React Technology stack. As far as I know, React is also the main technology stack in large factories. React has become an essential part of the interview process.

This part mainly elaborates React from the following aspects:

  • Fiber
  • The life cycle
  • SetState
  • HOC(Advanced component)
  • Redux
  • React Hooks
  • SSR
  • Functional programming

Originally, the plan was to only have two parts, but I wrote more and more. Due to the limited space, I had to split the middle part in order to have a better reading experience. I hope you don’t mind. 🙃, and Hybrid App/Webpack/performance optimization/Nginx will be included in the next section.

It is recommended to start with the basics from the first part.

  • Interview part one. 🤑
  • Interview next;

Small dish chicken blog for praise 🙂 blog

Advanced knowledge

Framework: the React

React is also the most popular front-end framework and a must for many large companies to interview. Although React and Vue are different, they are also a UI framework. Although the implementations may be different, they are similar in some concepts, such as data-driven, componentization, virtual DOM, etc. Here are a few concepts that are unique to React.

1. Fiber

React’s core process can be divided into two parts:

  • reconciliation (Scheduling algorithm, also called render):
    • Update state and props;
    • Calling lifecycle hooks;
    • Generate a Virtual DOM.
      • It’s better to call it a Fiber Tree;
    • Diff algorithm is performed through the old and new VDOM to obtain vDOM change;
    • Determine if you need to rerender
  • commit:
    • Perform DOM node updates if needed;

To understand Fiber, let’s first look at why we need it.

  • The problem: As the application gets larger, the entire process of updating the render becomes cumbersome, with a large number of component renderings causing the main process to be occupied for long periods of time, causing some animations or high-frequency operations to lag and drop frames. The key is synchronous blocking. In the previous scheduling algorithm, React needed to instantiate each class component, generate a component tree, and use synchronous recursion to traverse the rendering process. The biggest problem with this process was that it could not be paused and restored.

  • Solution: There are two methods to solve synchronous blocking: asynchronous and task splitting. React Fiber is designed to split tasks.

  • Description:

    • In React V16, the scheduling algorithm is reconstructed, and the previous Stack Reconciler is reconstituted into a new version of Fiber Reconciler, which becomes a single linked list tree traversal algorithm with linked lists and Pointers. Through pointer mapping, each cell records the previous and next steps in the traversal, making traversal possible to pause and restart.
    • Here I understand it as a task segmentation scheduling algorithm, which mainly divides the original synchronous update rendering tasks into independent small task units, and distributes the small tasks to the idle time of the browser according to different priorities, making full use of the event loop mechanism of the main process.
  • Core:

    • Fiber can be represented here as a data structure:
    class Fiber {
    	constructor(instance) {
    		this.instance = instance
    		// Point to the first child node
    		this.child = child
    		// Point to the parent node
    		this.return = parent
    		// Point to the first sibling node
    		this.sibling = previous
    	}	
    }
    Copy the code
    • List tree traversal algorithm: through node preservation and mapping, it can be stopped and restarted at any time, so as to achieve the basic premise of task segmentation;

      • 1. First, by continuously traversing the child nodes, to the end of the tree;
      • Start traversing sibling nodes through sibling nodes;
      • 3. Return the parent node and go to 2.
      • 4, until the root node, jump out the traversal;
    • Task splitting, rendering updates in React can be divided into two phases:

      • Reconciliation phase: VDOM data comparison, which is a phase suitable for splitting. For example, after comparing a part of the tree, pause the animation call, and then come back to continue the comparison.
      • Commit: Update the change list to the DOM, not suitable for splitting, to keep the data in sync with the UI. Otherwise, you may block UI updates, resulting in data updates and UI inconsistencies.
    • Scatter execution: Once tasks are split, small task units can be spread out to queue execution during idle periods in the browser. The key to this implementation are two new apis: requestIdleCallback and requestAnimationFrame

      • Low priority tasks torequestIdleCallbackProcessing, which is a browser-provided callback for the idle period of the event loop, requires pollyfill and has a deadline parameter that restricts the execution of the event to continue to shred tasks;
      • High priority tasks torequestAnimationFrameProcessing;
    // something like this
    requestIdleCallback((deadline) = > {
        // When there is free time, we perform a component render;
        // Put tasks into pieces of time;
        while ((deadline.timeRemaining() > 0|| deadline.didTimeout) && nextComponent) { nextComponent = performWork(nextComponent); }});Copy the code
    • Priority policy: Text box input > Tasks to be completed at the end of this schedule > Animation Transition > Interactive Feedback > Data Update > Tasks that will not be displayed but will be displayed in the future

Tips:

Fiber is actually a programming idea and has many applications in other languages (Ruby Fiber). The core idea is to split and coordinate tasks, and actively give the execution authority to the main thread, so that the main thread has time gaps to deal with other high-priority tasks.

Task splitting, asynchronous calls, and caching strategies are three obvious ways to solve the problem of process blocking.

Thank you @Pengyuan for pointing out some of the core ideas in the comments. Thank you!!

2. Life cycle

In the new version, React officials have proposed new changes to the life cycle:

  • usegetDerivedStateFromPropsreplacecomponentWillMountcomponentWillReceiveProps;
  • usegetSnapshotBeforeUpdatereplacecomponentWillUpdate;
  • Avoid the use ofcomponentWillReceiveProps;

In fact, the reason for this change is the aforementioned Fiber. First, we know from the above that React can be divided into two phases: Reconciliation and Commit, and the corresponding life cycle is as follows:

  • reconciliation:

    • componentWillMount
    • componentWillReceiveProps
    • shouldComponentUpdate
    • componentWillUpdate
  • commit:

    • componentDidMount
    • componentDidUpdate
    • componentWillUnmount

In Fiber, tasks are split in the Reconciliation phase, involving pauses and restarts, which can cause unexpected errors when the lifecycle functions in Reconciliation are called multiple times in a single update render loop.

The recommended lifecycle for the new version is as follows:

class Component extends React.Component {
  / / replace ` componentWillReceiveProps `,
  // Called during initialization and update
  // Static function, cannot use this
  static getDerivedStateFromProps(nextProps, prevState) {}
  
  // Determine if the component needs to be updated
  // Can be used for component performance tuning
  shouldComponentUpdate(nextProps, nextState) {}
  
  // Triggers when the component is mounted
  componentDidMount() {}
  
  / / replace componentWillUpdate
  // Get the latest DOM data before the update
  getSnapshotBeforeUpdate() {}
  
  // Called after the component is updated
  componentDidUpdate() {}
  
  // The component is about to be destroyed
  componentWillUnmount() {}
  
  // The component was destroyed
  componentDidUnmount() {}
}
Copy the code
  • Suggestions:

    • inconstructorInitialize state;
    • incomponentDidMountEvent listening is performed in thecomponentWillUnmountIn unbinding event;
    • incomponentDidMountData is made in the request, not incomponentWillMount;
    • Used when you need to update state according to propsgetDerivedStateFromProps(nextProps, prevState);
      • Old props need to be stored for comparison;
    public static getDerivedStateFromProps(nextProps, prevState) {
    	// When data changes in the new props, update to state synchronously
    	if(nextProps.data ! == prevState.data) {return {
    			data: nextProps.data
    		}
    	} else {
    		return null1
    	}
    }
    Copy the code
    • Can be found incomponentDidUpdateListen for changes in props or state, for example:
    componentDidUpdate(prevProps) {
    	// Retrieve the data when the id changes
    	if (this.props.id ! == prevProps.id) {this.fetchData(this.props.id); }}Copy the code
    • incomponentDidUpdateusesetState, conditions must be added, otherwise it will enter an infinite loop;
    • getSnapshotBeforeUpdate(prevProps, prevState)You can get the latest render data before the update, it is called after render, before update;
    • shouldComponentUpdate: Calls each time by defaultsetState“, will eventually go to the DIFF stage, but can passshouldComponentUpdateThe life hook returnsfalseTo directly block the logic behind the execution, is usually used to do conditional rendering, optimize rendering performance.

3. setState

Transaction: Transaction: Transaction: Transaction: Transaction: Transaction: Transaction: Transaction: Transaction

  • The transaction (Transaction):
    • React is a call structure in React that wraps a method with the struct initialize – perform(method) -close. Through transactions, the beginning and end of a method can be managed uniformly; In a transaction flow, the process is performing some operation;

  • SetState: Used in React to modify state and update views. It has the following characteristics:

  • Asynchronous vs. Synchronous: setState is not purely asynchronous or synchronous, it actually depends on the context of the call:

    • inSynthetic eventsLifecycle hooks (except componentDidUpdate),setStateIt’s “asynchronous”;
      • why: because thesetStateIn the implementation of, there is a judgment that the component update is pushed in while the update policy is being executed in the transaction flowdirtyComponentsWaiting in a queue for execution; Otherwise, the execution startsbatchedUpdatesQueue update;
        • In a lifecycle hook call, the update policy is before the update, the component is still in the transaction flow, andcomponentDidUpdateIs after the update, at which point the component is no longer in the transaction flow and therefore executes synchronously;
        • In synthetic events, React is an event delegate mechanism based on transaction flow completion, which is also in the transaction flow.
      • The problem: can’t insetStateImmediately after thethis.stateGets the updated value on the
      • To solve: If you need to synchronize immediately to get the new value,setStateYou can actually pass in a second argument.setState(updater, callback), to get the latest value in the callback;
    • inNative eventssetTimeout,setStateIt is synchronous and can get the updated value immediately;
      • The reason: Native events are an implementation of the browser itself, independent of transaction flow and naturally synchronous; whilesetTimeoutIs placed in the timer thread to delay execution, at which point the transaction flow has ended and is therefore synchronized.
  • Batch updates: In synthetic event and lifecycle hooks, setState stores the merge state (object.assign) when updating the queue. Therefore, the previous key value will be overwritten, and only one update will be performed.

  • Functional: It is officially recommended that you pass in the form of a function due to problems with Fiber and merging. SetState (fn), return the newState object in fn, for example this.setstate ((state, props) => newState);

    • Using functional, can be used to avoidsetStateWith the batch update logic, the passed function will beOrder calls;
  • Notes:

    • SetState merges, in which multiple successive calls in synthetic event and lifecycle hooks are optimized to be one;
    • When the component has been destroyed, if called againsetStateThere are usually two solutions to this problem:
      • Mount the data externally, passing it through props, such as in a Redux or parent;
      • Maintains a state quantity (isUnmounted) within the component,componentWillUnmountIs marked true insetStateTo judge before;

4. HOC(Advanced components)

HOC(Higher Order Componennt) is a component mode formed by the React mechanism community, which is powerful in many third-party open source libraries.

  • Description:

    • A higher-order component is not a component, but an enhancer. You can enter a meta-component and return a new enhancer.
    • The main functions of higher-order components are code reuse, operational status and parameters;
  • Usage:

    • Props Proxy: Returns a component that is enhanced based on the wrapped component.

      • Default parameters: You can wrap a layer of default parameters for a component.
      function proxyHoc(Comp) {
      	return class extends React.Component {
      		render() {
      			const newProps = {
      				name: 'tayde'.age: 1,}return <Comp {. this.props} {. newProps} / >}}}Copy the code
      • Extracting state: You can use props to outer the state dependency of a wrapped component, for example, to transform a controlled component:
      function withOnChange(Comp) {
      	return class extends React.Component {
      		constructor(props) {
      			super(props)
      			this.state = {
      				name: ' ',
      			}
      		}
      		onChangeName = (a)= > {
      			this.setState({
      				name: 'dongdong',
      			})
      		}
      		render() {
      			const newProps = {
      				value: this.state.name,
      				onChange: this.onChangeName,
      			}
      			return <Comp {. this.props} {. newProps} / >}}}Copy the code

      Using the following gestures, you can quickly convert an Input component into a controlled component.

      const NameInput = props= > (<input name="name" {. props} / >)
      export default withOnChange(NameInput)
      Copy the code
      • Package component: Can be a layer of package elements,
      function withMask(Comp) {
        return class extends React.Component {
            render() {
      		  return( <div> <Comp {... this.props} /> <div style={{ width: '100%', height: '100%', backgroundColor: 'rgba(0, 0, 0, .6)', }} </div> ) } } }Copy the code
    • Inheritance Inversion: Returns a component that inherits from the wrapped component. This is often used for:

      function IIHoc(Comp) {
          return class extends Comp {
              render() {
                  return super.render(); }}; }Copy the code
      • Render Highjacking

        • Conditional rendering: Renders different components based on the condition
        function withLoading(Comp) {
            return class extends Comp {
                render() {
                    if(this.props.isLoading) {
                        return <Loading />
                    } else {
                        return super.render()
                    }
                }
            };
        }
        Copy the code
        • You can directly modify the React element tree rendered by the wrapped component
      • Operate State: You can use this. State to obtain and Operate the status of the wrapped component. But such operations tend to make state hard to track, hard to maintain, and careful to use.

  • Application scenario:

    • Permission control: Through abstract logic, the permission judgment of the page is unified, and the page is rendered according to different conditions:
    function withAdminAuth(WrappedComponent) {
        return class extends React.Component {
    		constructor(props){
    			super(props)
    			this.state = {
    		    	isAdmin: false,}}async componentWillMount() {
    		    const currentRole = await getCurrentUserRole();
    		    this.setState({
    		        isAdmin: currentRole === 'Admin'}); } render() {if (this.state.isAdmin) {
    		        return <Comp {. this.props} / >;
    		    } else {
    		        return (<div>You do not have permission to view this page, please contact your administrator!</div>); }}}; }Copy the code
    • Performance monitoring, parcel component life cycle, unified burial point:
    function withTiming(Comp) {
        return class extends Comp {
            constructor(props) {
                super(props);
                this.start = Date.now();
                this.end = 0;
            }
            componentDidMount() {
                super.componentDidMount && super.componentDidMount();
                this.end = Date.now();
                console.log(`${WrappedComponent.name}Component rendering time isThe ${this.end - this.start} ms`);
            }
            render() {
                return super.render(); }}; }Copy the code
    • Code reuse, which abstracts repetitive logic.
  • Use note:

      1. Pure functions: Enhancement functions should be pure functions to avoid intruding into modification components;
      1. Avoid usage pollution: Ideally, irrelevant parameters and events of element components should be transparently transmitted to ensure that usage is unchanged;
      1. Namespaces: Add specific component names to HOC to facilitate development debugging and problem finding;
      1. reference: If you need to pass a meta component’s refs reference, useReact.forwardRef;
      1. Static methods: The static methods on the meta component cannot be automatically transmitted, resulting in the business layer cannot be called; Solution:
      • function
      • Static method assignment
      1. Rerender: Since the enhancement function returns a new component each time it is called, using the enhancement function in Render causes the entire HOC to be rerendered each time and the previous state to be lost;

5. Redux

Redux is a data manager, which can be thought of as a global Data Store instance. It ensures the robustness, traceability and predictability of data through certain use rules and restrictions. It is independent of React and can run independently in any JavaScript environment, which also provides a better data synchronization channel for isomorphic applications.

  • Core concept:

    • Single data source: The entire application has a unique state tree, that is, all states are ultimately maintained in a root Store;
    • State the read-only: The best way to ensure state control is to monitor state changes. So here are the two requirements:
      • The data in the Redux Store cannot be directly modified.
      • Strictly control the implementation of modification;
    • Pure function: specifies that only a pure function (Reducer) can be used to describe modifications.
  • The approximate data structure is as follows:

  • Concept realization:

    • Store: Global Store singleton, only one Store per Redux application, which has the following methods for use:
      • getState: Obtain the state;
      • dispatch: Triggers action, updates state;
      • subscribe: Subscribe to data changes, register listeners;
    / / create
    const store = createStore(Reducer, initStore)
    Copy the code
    • Action: Acts as a behavior carrier and maps corresponding Reducer. It can also act as a data carrier and transfer data from applications to stores. It is the only data source of stores.
    // A normal Action
    const action = {
    	type: 'ADD_LIST'.item: 'list-item-1',}/ / use:
    store.dispatch(action)
    
    // There is usually an Action creater for easy invocation.
    funtion addList(item) {
    	return const action = {
    		type: 'ADD_LIST',
    		item,
    	}
    }
    
    // The call will become:
    dispatch(addList('list-item-1'))
    Copy the code
    • Reducer: a pure function that describes how to modify data. The Action is the name of the Action, while the Reducer is the actual Action.
    // A regular Reducer
    // @param {state}: old data
    // @param {action}: Action object
    // @returns {any}: indicates new data
    const initList = []
    function ListReducer(state = initList, action) {
    	switch (action.type) {
    		case 'ADD_LIST':
    			return state.concat([action.item])
    			break
    		defalut:
    			return state
    	}
    }
    Copy the code

    Note:

    1. Keep the data immutable, do not modify the state directly, but return oneA new object, can be usedAssign/copy/extend/deconstructTo create a new object;
    2. By default, the original data needs to be returned to avoid data clearing.
    3. It is best to set the initial value to facilitate the initialization of the application and data stability;
  • Advanced:

    • React-Redux: Used in combination with React;
      • <Provider>: Passes the Store to the component through the context.
      • connectRedux: a high-order component that makes it easy to use Redux in the React component
          1. willstorethroughmapStateToPropsUse after filteringpropsInjection components
          1. According to themapDispatchToPropsCreates methods that are used when invoked by the componentdispatchTrigger the correspondingaction
    • Splitting and refactoring the Reducer:
      • As the project gets larger, if the reducer of all the state is written in one function, it will be difficult to maintain.
      • The reducer can be split, that isThe function decompositionAnd finally reusecombineReducers()Reconstruct merge;
    • Asynchronous Action: Since the Reducer is a strictly pure function, data cannot be requested in the Reducer. Data must be obtained first and thendispatch(Action)Ok, here are three different asynchronous implementations:
      • redex-thunk
      • redux-saga
      • redux-observable

6. React Hooks

React usually uses class or function definitions to create components:

We can use many React features in class definitions, such as state, various component lifecycle Hooks, etc., but we can’t use them in function definitions. Therefore, React Hooks are introduced in React 16.8. Better use of the React feature in function definition components.

  • Benefits:

    • 1. Cross-component reuse: In fact, render props/HOC is also intended for reuse. Compared to them, Hooks are the official low-level API, which are the lightest, and cost less to remodel, so they don’t affect the original component hierarchy and the legendary nesting inferno.
    • 2,Class definitions are more complex:
      • Different life cycles can make logic fragmented and chaotic, which is difficult to maintain and manage;
      • Always need attentionthisPoint to the problem;
      • The cost of code reuse is high and the use of higher-order components often makes the whole component tree become bloated.
    • 3. State isolation from THE UI: Thanks to the Hooks feature, state logic becomes much smaller in granularity and can easily be abstracted into a single custom Hooks, making state and UI in components much clearer and more isolated.
  • Note:

    • Avoid calling hooks in looping/conditional/nested functions to ensure that the order of calls is stable.
    • Hooks can only be called from function defined components and hooks. Do not call them from class components or normal functions.
    • Not in theuseEffectThe use ofuseState, React will report an error;
    • Class components will not be replaced or discarded, there is no need to forcibly transform class components, two ways can coexist;
  • Important hooks *:

    • State of the hook (useState): Used to define the component’s State, which is added to the class definitionthis.stateThe function;
    // useState accepts only one parameter: the initial state
    // Return the name of the component and the function that changed the component
    const [flag, setFlag] = useState(true);
    // Modify the status
    setFlag(false)
    	
    // The above code maps to the class definition:
    this.state = {
    	flag: true	
    }
    const flag = this.state.flag
    const setFlag = (bool) = > {
        this.setState({
            flag: bool,
        })
    }
    Copy the code
    • Lifecycle hook (useEffect) :

    There are many lifecycle functions in the class definition, and a corresponding function (useEffect) is provided in the React Hooks. Think of this as a combination of componentDidMount, componentDidUpdate, and componentWillUnmount.

    • useEffect(callback, [source])Accept two parameters
      • callback: hook callback function;
      • source: Sets triggering conditions, and triggers only when the source changes;
      • useEffectThe hook is not passed in[source]By default, the function returned in the last saved callback will be called first each time when render is called, and then the callback will be called again.
    useEffect((a)= > {
    	// Event binding is performed after the component is mounted
    	console.log('on')
    	addEventListener()
    	
    	// Event unbinding is performed when the component updates
    	return (a)= > {
    		console.log('off')
    		removeEventListener()
    	}
    }, [source]);
    
    
    // Each time the source changes, the result is executed (in a class-defined lifecycle for easy understanding):
    // --- DidMount ---
    // 'on'
    // --- DidUpdate ---
    // 'off'
    // 'on'
    // --- DidUpdate ---
    // 'off'
    // 'on'
    // --- WillUnmount --- 
    // 'off'
    Copy the code
    • With the second parameter, we can simulate several common life cycles:

      • componentDidMountIntroduced:[]Is called only once during initialization;
      const useMount = (fn) = > useEffect(fn, [])
      Copy the code
      • componentWillUnmountIntroduced:[], the function returned by the callback is executed only once;
      const useUnmount = (fn) = > useEffect((a)= > fn, [])
      Copy the code
      • mountedMounted: can be encapsulated into a highly reusable mounted state using useState.
      const useMounted = (a)= > {
          const [mounted, setMounted] = useState(false);
          useEffect((a)= > {
              !mounted && setMounted(true);
              return (a)= > setMounted(false); } []);return mounted;
      }
      Copy the code
      • componentDidUpdate: useEffectIt’s going to be done every time, but it just excludes DidMount;
      const mounted = useMounted() 
      useEffect((a)= > {
          mounted && fn()
      })
      Copy the code
  • Other built-in hooks:

    • UseContext: Obtains the context object

    • UseReducer: Similar to the implementation of Redux ideas, but not sufficient to replace Redux and can be understood as a Redux inside a component:

      • It is not persistent and will be destroyed when the component is destroyed;
      • Belong to components, each component is isolated from each other, simple use it and can not share data;
      • Cooperate withuseContextGlobal, can be completed with a lightweight Redux; (easy-peasy)
    • UseCallback: cache callback functions, avoid passing in a new function instance each time caused by dependent components to re-render, has the effect of performance optimization;

    • UseMemo: Props for caching incoming props to prevent dependent components from being rerendered each time

    • UseRef: gets the real node of the component;

    • useLayoutEffect:

      • DOM update synchronous hook. Usage anduseEffectSimilar, but different from the point in time of execution.
      • useEffectIt is asynchronous execution and does not wait for the DOM to actually renderuseLayoutEffectWill be triggered after the actual rendering;
      • You can get the updated state;
  • Custom Hooks (useXxxxx): Based on the fact that Hooks can reference other Hooks, we can write custom Hooks, as shown in useMounted above. For another example, we need a custom title for each page:

function useTitle(title) {
  useEffect(
    (a)= > {
      document.title = title;
    });
}

/ / use:
function Home() {
	const title = 'This is the home page'
	useTitle(title)
	
	return (
		<div>{title}</div>)}Copy the code

7. SSR

SSR, commonly known as Server Side Render (Server Side Render), speaking people is: directly in the Server layer to obtain data, rendering the completed HTML file, directly returned to the user’s browser access.

  • Front and back separation: The front end is isolated from the server, and the front end dynamically obtains data and renders the page.

  • Pain points:

    • First-screen rendering performance bottlenecks:

      • White space delay: HTML download time + JS download/execution time + request time + render time. During this time, the page is blank.
    • SEO problem: Because the initial state of the page is empty, the crawler cannot obtain any valid data in the page, so it is not friendly to the search engine.

      • Although there has been a dynamic rendering crawler technology, but as far as I know, most of the domestic search engines are still not implemented.

The original server-side rendering did not have these problems. However, we could not return to the original nature. We needed to ensure the existing front-end independent development mode and rendering by the server, so we used React SSR.

  • Principle:

    • Node services: Makes it possible to run the same set of code on the front and back ends.
    • Virtual Dom: Allows front-end code to run outside the browser.
  • Conditions: Node mid-tier, React/Vue and other frameworks. The structure is roughly as follows:

  • Development process: (Take React + Router + Redux + Koa as an example)

    • 1. In the same project, build the front and rear end parts, and the conventional structure:

      • build
      • public
      • src
        • client
        • server
    • 2. Use Koa route to listen for page access in server:

    import * as Router from 'koa-router'
    
    const router = new Router()
    // If the Api layer is also provided in the middle
    router.use('/api/home'.async() = > {// Return the data
    })
    
    router.get(The '*'.async (ctx) => {
    	/ / returns the HTML
    })
    Copy the code
    • 3. Match the front-end page route by accessing the URL:
    // Front page routing
    import { pages } from '.. /.. /client/app'
    import { matchPath } from 'react-router-dom'
    
    // Use one of the matching methods provided by the React-Router library
    const matchPage = matchPath(ctx.req.url, page)
    Copy the code
    • 4. Obtain data by configuring the page route. In general, ssR-related static configurations can be added to the page routing for abstract logic to ensure the universality of server-side logic, such as:

      class HomePage extends React.Component{
      	public static ssrConfig = {
      		  cache: true,
               fetch() {
              	  // Request for data}}}Copy the code

      There are usually two ways to get data:

      • The middle tier also uses HTTP to obtain data, so the FETCH method can be shared back and forth.
      const data = await matchPage.ssrConfig.fetch()
      Copy the code
      • The middle tier does not use HTTP, but uses some internal calls, such as Rpc or direct database reading. In this case, the server can also directly call the corresponding method to obtain data. Typically, you need to configure specific information in ssrConfig to match the corresponding data fetch method.
      // Page routing
      class HomePage extends React.Component{
      	public static ssrConfig = {
              fetch: {
              	 url: '/api/home',}}}// Match the corresponding data acquisition method according to the rule
      // The rules are free, as long as the correct method is matched
      const controller = matchController(ssrConfig.fetch.url)
      
      // Get the data
      const data = await controller(ctx)
      Copy the code
    • Create a Redux Store and dispatch data into it:

    import { createStore } from 'redux'
    // Obtain the Clinet layer reducer
    // The logic of the front layer must be reused to ensure consistency;
    import { reducers } from '.. /.. /client/store'
    
    / / create a store
    const store = createStore(reducers)
     
    // Get the configured Action
    const action = ssrConfig.action
    
    // Store data
    store.dispatch(createAction(action)(data))
    Copy the code
    • 6, inject Store, callrenderToStringRender the React Virtual Dom asstring:
    import * as ReactDOMServer from 'react-dom/server'
    import { Provider } from 'react-redux'
    
    // Obtain the Clinet layer root component
    import { App } from '.. /.. /client/app'
    
    const AppString = ReactDOMServer.renderToString(
    	<Provider store={store}>
    		<StaticRouter
    			location={ctx.req.url}
    			context={{}}>
    			<App />
    		</StaticRouter>
    	</Provider>
    )
    Copy the code
    • Wrap AppString as a full HTML file.

    • 8. At this point, the complete HTML file can be generated. But it’s a purely static page, with no style and no interaction. The next step is to insert JS and CSS. We can access the asset-manifest.json file generated by the front end packaging to get the corresponding file path and inject the same reference into the Html.

    const html = ` <! DOCTYPE html> <html lang="zh"> <head></head> <link href="${cssPath}" rel="stylesheet" />
    		<body>
    			<div id="App">${AppString}</div>
    			<script src="${scriptPath}"></script>
    		</body>
    	</html>
    `
    Copy the code
    • 9. Data dehydration: In order to synchronize the data obtained by the server to the front end. Basically, the data is serialized, inserted into HTML, and returned to the front end.
    import serialize from 'serialize-javascript'
    // Get the data
    const initState = store.getState()
    const html = ` <! DOCTYPE html> <html lang="zh"> <head></head> <body> <div id="App"></div> <script type="application/json" id="SSR_HYDRATED_DATA">${serialize(initState)}</script>
    		</body>
    	</html>
    `
    
    ctx.status = 200
    ctx.body = html
    Copy the code

    Tips:

    There are two special points here:

    1. Serialize-javascript serialization store is used instead of json. stringify to ensure data security and avoid code injection and XSS attacks.

    2. Using JSON for transmission, you can get faster loading speed;

    • 10. Data water absorption at the Client layer: During store initialization, dehydrated data is used as initialization data and stores are created simultaneously.
    const hydratedEl = document.getElementById('SSR_HYDRATED_DATA')
    const hydrateData = JSON.parse(hydratedEl.textContent)
    
    // Create the Redux store with the initial state
    const store = createStore(reducer, hydrateData)
    Copy the code

8. Functional programming

Functional programming is a programming paradigm that you can think of as a software architecture mindset. It has an independent set of theoretical basis and boundary laws, the pursuit of more concise, predictable, high reuse, easy to test. In fact, there are rich functional programming ideas in many well-known libraries, such as React/Redux, etc.

  • Common programming paradigm:

    • Imperative programming (procedural programming): is more concerned with the steps to solve the problem, step by step in the form of language to tell the computer what to do;
    • Event-driven programming: event subscription and triggering, widely used in GUI programming design;
    • Object-oriented programming is a design pattern based on classes, objects and methods. It has three basic concepts: encapsulation, inheritance and polymorphism.
    • Functional programming
      • To put it in a more advanced way, mathematically oriented programming. Afraid of ~ 🥴
  • The concept of functional programming:

    • Pure functions (deterministic functions): the foundation of functional programming, making programs flexible, highly extensible, and maintainable;

      • Advantage:

        • Completely independent and decoupled from the outside;
        • Highly reusable, executable in any context, on any timeline and guaranteed stable results;
        • Strong testability;
      • Condition:

        • Do not modify parameters;
        • Do not rely on or modify any data outside the function.
        • Completely controllable, the parameters are the same, the return value must be the same: for example, the function cannot containnew Date()orMath.rando()Such uncontrollable factors;
        • Reference transparency;
      • Many of the apis or utility functions we often use are pure functions, such as split/join/map;

    • Function composition: multiple functions are combined and then called, which can realize the combination of a function unit to achieve the final goal;

      • Flat nesting: First of all, we must think that the simplest operation for composing functions is wrapping, because in JS, functions can also be used as arguments:

        • f(g(k(x))): nested hell, low readability, when the function is complex, easy to get confused;
        • What to do ideally:xxx(f, g, k)(x)
      • Result transfer: if you want to achieve the above method, that is the XXX function to achieve: the execution of the result of the execution of the transfer between functions;

        • At this point we can think of a natively provided array method:reduce, it can be executed in the order of the array, passing the result;
        • So we were able to implement a methodpipe, for function combinations:
        / /... Fs: combine functions into arrays;
        // array.prototype.reduce;
        // p: initial parameter;
        const pipe = (. fs) = > p => fs.reduce((v, f) = > f(v), p)
        Copy the code
      • Use: to achieve a hump name to the hyphen naming function:

      // 'Guo DongDong' --> 'guo-dongdong'
      // Function combination
      const toLowerCase = str= > str.toLowerCase()
      const join = curry((str, arr) = > arr.join(str))
      const split = curry((splitOn, str) = > str.split(splitOn));
      
      const toSlug = pipe(
      	toLowerCase,	
      	split(' '),
      	join('_'),
      	encodeURIComponent,);console.log(toSlug('Guo DongDong'))
      Copy the code
      • Benefits:

        • Hide intermediate parameters, do not need temporary variables, to avoid the error rate of this link;
        • You only need to care about the stability of each pure function unit, no longer need to care about naming, passing, calling, etc.
        • Reusability is strong, any function unit can be arbitrary reuse and combination;
        • Strong scalability, low cost, for example, now add a requirement, to check the output of each link:
        const log = curry((label, x) = > {
        	console.log(`${ label }: ${ x }`);
        	return x;
        });
        
        const toSlug = pipe(
        	toLowerCase,	
        	log('toLowerCase output'),
        	split(' '),
        	log('split output'),
        	join('_'),
        	log('join output'),
        	encodeURIComponent,);Copy the code

      Tips:

      Some tool pure functions can refer to Lodash /fp directly, such as curry/map/split, etc., without having to implement them as we did above.

    • 3. The idea of data being immutable, which is one of the core concepts in functional programming:

      • Advocacy: Once an object is created, it will not be modified. When the value needs to be changed, it returns an entirely new object, rather than modifying the original object directly.
      • Objective: To ensure the stability of data. It can effectively improve controllability and stability by avoiding the unknown modification of dependent data, which leads to its own execution exception.
      • Is not the same asconst. useconstAfter an object is created, its properties can still be modified;
      • More similar toObject.freeze: Freezes the object, butfreezeThere is still no guarantee that deep properties will not be string changed;
      • immutable.js: js data immutable library, which ensures the immutable data, is widely used in React ecology, greatly improving performance and stability;
        • trieData structure:
          • A data structure that can effectively deep freeze an object to ensure its immutability;
          • Structure sharing: memory reference addresses of immutable objects can be shared to reduce memory usage and improve data operation performance.
    • Avoid state sharing between different functions, transfer of data using copy or new objects, abide by the principle of immutable data;

    • Avoid changing the external state from inside a function, such as changing the value of a variable in the global or parent scope, which may cause errors in other units.

    • To avoid performing side effects inside unit functions, separate these operations into more independent tool units.

      • Log output
      • Read and write files
      • Network request
      • Calling an external process
      • Call a function that has side effects
  • Higher-order function: a class of functions that take a function as an argument and return a new enhancement. It is usually used in:

    • The logic behavior is isolated and abstracted to facilitate rapid reuse, such as data processing, compatibility, etc.
    • Function combination, combining a list of unit functions into a more powerful function;
    • Function enhancement, rapid expansion of function functions,
  • Benefits of functional programming:

    • Function side effects are small, all functions exist independently, without any coupling, and reuse is very high;
    • Do not pay attention to execution time, execution order, parameters, naming, etc., can focus on data flow and processing, can effectively improve the stability and robustness;
    • The pursuit of unitalization, granularity, so that its reconstruction and transformation costs are reduced, maintenance, scalability is good;
    • Easier to unit test.
  • Conclusion:

    • Functional programming is actually a kind of programming idea, which is more granular, it breaks down the application into a set of minimal unit functions, combined call operation data flow;
    • It advocates pure functions/function composition/immutable data, and is careful to share/rely on external/side effects within functions;

Tips:

In fact, it is difficult and not necessary to perfectly elaborate the whole idea in the interview process, but here is just a taste of it, some personal understanding. The blogger is also a junior small rookie, stay on the surface, just for everyone can help, light spray 🤣;

In my opinion, there is no contradiction between these programming paradigms, and each has its own advantages and disadvantages.

Understanding and learning their ideas and advantages, reasonable design fusion, excellent software programming ideas to improve our application;

The ultimate goal of all design ideas must be to make our applications more decoupled and granular, easy to expand, easy to test, high reuse, and more efficient and safe development;

Underscore: Underscore/Underscore/Underscore/Underscore/Underscore/Underscore

conclusion

At this point, you will find that you have begun to go into some theory and principle level, not as easy to understand as the last part. But it’s a rite of passage, and it can’t be mastered in five minutes forever. Stop at the surface of the language, go deeper into the principles, patterns, architecture, causality, and you’ll suddenly find yourself a senior software engineer. 😁.

I hope you can settle down. Although some theories and concepts are boring, you can have your own understanding after repeatedly thinking about them and then try them in practice.

In the interview for senior engineers, the interviewer will not focus on whether you can “stopPropagation” or “center propagation”, but more on your thinking and research ability. Showing that you have a deep understanding of the research will impress your interviewer.

Tips:

Contact me directly via [email protected] or QQ/wechat: 159042708.

Blogger really write very hard, do not star, really want to cry. – making. 🤑