Incomplete Manual for Front End Performance Optimization

  css, html5, javascript, node.js, typescript

Performance optimization is a university question. This article only elaborates on personal accumulated knowledge. Please add the following.

Throw a question from the inputurlWhat did you do when everything from the address bar to the interface was displayed?

  • 1. Browser toDNSThe server requests to resolve the domain name corresponding to the URLIPAddress;
  • 2. EstablishmentTCPConnection (three-way handshake);
  • 3. The browser issues a read file (URLIn the domain name after the corresponding part of the file)HTTPRequest, the request message asTCPThe data of the third message of the three-way handshake is sent to the server;
  • 4. The server responds to the browser request and sends the corresponding html text to the browser;
  • 5. The browser will be thehtmlText and display content;
  • 6. ReleaseTCPConnection (four waves);

The above question is a question that the interviewer likes to ask very much. Let’s break down these 6 steps and discuss the optimization step by step.

I.DNSanalysis

  • DNS’ Resolution: Resolve domain name to ip address, match from top to bottom, stop as long as hit

    • Go cache
    • Browser DNS cache
    • Local DNS cache
    • Router DNS cache
    • Network operator server DNS cache (80% of DNS resolution is completed here)
    • Recursive query

Optimization strategy: Allow browser cache as much as possible, which can save us a lot of time, as followsdns-prefetchThe introduction of, every timednsParsing probably requires20-120 seconds

Second,TCPThree-way handshake

  • SYN (Synchronization Sequence Number) ACK (Acknowledgement Character)

    • First handshake: the Client sets the flag bit SYN to 1, randomly generates a value seq=J, and sends the data packet to the Server. the Client enters the SYN_SENT state until the Server confirms.
    • Second handshake: after receiving the data pACKet, the Server knows from the flag bit SYN=1 that the Client requests to establish a connection. the Server sets the flag bits SYN and ack to 1, ack=J+1, randomly generates a value seq=K, and sends the data packet to the Client to confirm the connection request. the Server enters the SYN_RCVD state.
    • Three-way handshake No.1: After receiving the acknowledgement, the Client checks whether the ack is J+1 and ack is 1. If it is correct, it sets the flag ACK to 1, ack=K+1, and sends the data packet to the Server. The Server checks whether the ACK is K+1 and ACK is 1. If it is correct, the connection is ESTABLISHED successfully, the Client and the Server enter the established state, complete the three-way handshake, and then the data transmission between the Client and the Server can begin.

Third, the browser sends the request

Optimization strategy:

    • 1.HTTPThe most time-consuming part of protocol communication is establishmentTCPThe process of connection, then we can useHTTP Keep-AliveInHTTPIn the early days, eachHTTPAll requests require one to be opened.TCP socketConnect and disconnect this after one use.TCPConnection. Usekeep-aliveThis state can be improved, that is, multiple copies of data can be continuously sent in one TCP connection without disconnecting the connection. By usingkeep-aliveMechanisms can be reducedTCPThe number of connection establishment also means that it can be reduced.TIME_WAITState connection to improve performance and increasehttpServer Throughput (LesstcpConnections mean fewer system kernel calls
    • 2. However,keep-aliveIt’s not a free lunch, it’s a long time.TCPConnection easily leads to invalid occupation of system resources. Misconfiguredkeep-aliveSometimes the loss is even greater than that caused by reusing the connection. So, set it up correctlykeep-alive timeoutTime is very important. (Thiskeep-alive_timoutTime value means: onehttpprocreanttcpThe connection needs toholdLivekeepalive_timeoutSeconds before closing this connection), if you want to learn more, you can read this article.Keep-alve performance optimization test results
    • 3. UsewebScoketCommunication protocol, once onlyTCPHandshaking keeps the connection all the time, and he has better support for the transmission of binary data, which can be applied to instant messaging, mass and high concurrency scenarios.The principle and detailed explanation of webSocket
    • 4. ReductionHTTPNumber of requests, each timeHTTPEvery request has a request header, and every return response has a response header. Multiple requests not only waste time but also cause the network to transmit many invalid resources, using front-end modularization technologyAMD CMD commonJS ES6 and other modular schemesCompress and pack multiple files into one file, of course, not all of them can be put into one file, because it may be slow to transfer, weighing a middle value.
    • 5. Configuration uses lazy loading. For some files that users do not use immediately, they will request again after triggering specific events. Perhaps users only want to see the contents of the first half of your home page, but you have requested all the pictures of the entire page. If the number of users is large, this is a great waste.
    • 6. The deployment of server resources should use the same source policy as far as possible.
    • 7. In need of more than onecookieTo identify various conditions of users, use thesessionInstead, the data is stored in a server-side or server-side database so that only one is required.cookieTransmission saves a large amount of invalid transmission, and the stored data can be permanently large wirelessly.
    • 8. UsepreloadAnddns-prefetchprefetch, pre-request resources, this request method will not block browser parsing, and can cache the pre-requested resources, and can be setcrossorginCaching cross-domain resources will not delay the rendering time of the first screen, but also speed up the later loading time, because the resources needed by the latter itself will be directly read from the cache instead of going through network requests.
    • 9. UsedeferAndasyncThe script of the property, the asynchronous loading method, will send the request first, and thenJSThe engine continues to parse the following.asyncThe attribute scripts of the will be loaded out of order. whoever requests to come back first will be loaded immediately. when the request comes back, no matter inDOMParsing is still script parsing, and this will be parsed first.asncyScript, it will blockDOMAn analysis of.deferThe of the property will pressHTMLThe structure is loaded in sequence in theDOMContentLoadBefore loading, but before loading allDOMThe analysis must have been completed.deferThe script for the property does not blockDOMIt is also called deferred script.Because it is not sure whether it is in factDOMContentLoadedBefore loading, so usually only onedeferThe script, refer to the mobile jingdong web page.Async and defer
    • For details, please refer toPreload and prefetch

    4. The server returns a response and the browser receives the response data

    I have never thought of any optimization method used here. I thought of it tonight and used it.NginxReverse proxy server is mainly to optimize the server side.

    • NginxIs a lightweightWebServer/reverse proxy server and email (IMAP/POP3) proxy server, and in aBSD-likeIssued under the agreement. It is characterized by less memory and strong concurrency. In fact, nginx’s concurrency does perform well in the same type of web servers and is used in mainland China.nginxThe website users include Baidu, Jingdong, Sina, Netease, Tencent, Taobao, etc.
    • NginxIs a very simple installation, configuration files are very concise (can also supportperlGrammar),BugVery few services.NginxIt is especially easy to start, and it can almost run 7*24 without interruption, even if it runs for several months, it does not need to restart. You can also upgrade the software version without interruption of service.
    • It can solve cross-domain, request filtering, gzip configuration, load balancing, static resource server, etc …
    • Think of the service window as our back-end server, with countless clients initiating requests at the back end. Load balancing is used to help us reasonably distribute numerous client requests to vario us servers so as to achieve full utilization of server resources and less request time.
    • NginxHow to Realize Load Balancing

      • How does nginx Realize Load Balancing
    Upstream specifies a list of backend server addresses
     upstream balanceServer {
     server 10.1.22.33:12345;
     server 10.1.22.34:12345;
     server 10.1.22.35:12345;
     }
     The replication code intercepts the response request in the server and forwards the request to the list of servers configured in Upstream.
     server {
     server_name   fe.server.com ;
     listen 80;
     location /api {
     proxy_pass http://balanceServer;
     }
     }
    • The above configuration only specifies the list of servers that nginx needs to forward, and does not specify the allocation policy.
    • The policy adopted by default allocates all client request polls to the server. This strategy can work normally, but if one of the servers is under too much pressure and delays occur, all users assigned to this server will be affected.
    • Minimum Connection Number Policy

    Prioritizing requests to less stressed servers balances the length of each queue and avoids adding more requests to more stressed servers.

    upstream balanceServer {
     least_conn;  //Configure servers with less pressure
     server 10.1.22.33:12345;
     server 10.1.22.34:12345;
     server 10.1.22.35:12345;
     }
    • Relying on NGINX Plus, it is preferentially allocated to the server with the shortest response time.
    upstream balanceServer {
     fair;  //Configure the server with the shortest response time
     server 10.1.22.33:12345;
     server 10.1.22.34:12345;
     server 10.1.22.35:12345;
     }
    • Client ip binding
    Requests from the same ip are always assigned to only one server, effectively solving the session sharing problem of dynamic web pages.
    upstream balanceServer {
    ip_hash;  //Configure an IP to always assign only one server
    server 10.1.22.33:12345;
    server 10.1.22.34:12345;
    server 10.1.22.35:12345;
    }
    • Configure static resource servers
    location ~* \.(png|gif|jpg|jpeg)$ {
     root    /root/static/;
     autoindex on;
     access_log  off;
     expires     10h;  # Set Expiration Time to 10 Hours
     }
     The replication code matches requests that end with png|gif|jpg|jpeg.
     And forward the request to the local path, the path specified in root is nginx
     Local path.  At the same time, some cache settings can also be made.
    • NginxSolve cross-domain problems
    Nginx's Principle of Solving Cross-Domain Problems
     For example:
     
     The domain name of the front-end server is: fe.server.com
     The domain name of the back-end service is: dev.server.com
     
     Now my request to dev.server.com in fe.server.com will definitely cross-domain.
     Now we just need to start a nginx server and set server_name to fe.server.com.
     Then set the corresponding location to intercept the cross-domain request from the front end, and finally proxy the request back to dev.server.com.
     Such as the following configuration:
     server {
     listen       80;
     server_name   fe.server.com ;
     location / {
     proxy_pass  dev.server.com ;
     }
     }
     Copying the code perfectly bypasses the browser's cognate strategy: fe.server.com visits fe.server.com in nginx
     It belongs to homologous access, while nginx's request forwarded to the server will not trigger the browser's homologous policy.
    • The most important point has come, nowBATJMost use this configuration:

      • Configure GZIP

        • GZIPIs the prescribed three standardsHTTPOne of the compression formats. At present, the vast majority of websites are in use.GZIPTransmissionHTML、CSS、JavaScriptSuch as resource files.
        • For text files, GZip has a very obvious effect, and the traffic required for transmission will drop to about 1/4 ~ 1/3 after opening.
        • EnableGZipRequiredHTTPThe default value for the minimum version isHTTP/1.1
        • EnablegzipAt the same time need the support of the client and the server, if the client supportgzipAs long as the server can returngzipThe file of can be enabledgzipYes, we can passnginxTo enable the server to support gzip. BelowresponeIncontent-encoding:gzip, refers to the server openedgzipThe compression method of.
      • See this article for details.Nginx configuration GZIP

    For text files, GZip has a very obvious effect, and the traffic required for transmission will drop to about 1/4 ~ 1/3 after opening.

    NginxThe function is very powerful and the configuration is very convenient. Those who are interested can read this article more.Nginx analysis

    Fifth, the browser analyzes the data and draws the rendering page

    • Pre-parsing (sending out the request of the tag that needs to send the request)
    • Analysis from Top to BottomhtmlFile
    • When encountering an html tag, call the HTML parser to parse itDOMTree
    • encountercssMark, call css parser to parse itCSSOMTree
    • linkBlocking-To resolve the splash screen, all styles of resolving the splash screen
    • styleNon-blocking, unrelated to splash screen style
    • willDOMTrees andCSSOMTrees combine to formrenderTree
    • Layout layout render rendering
    • encounterscriptTag, blocking, callingjsParser parsingjsCode, may be modifiedDOMTrees may also be modifiedCSSOMTree
    • willDOMTrees andCSSOMTrees combine to formrenderTree
    • layoutLayoutrenderRendering (Rescheduling and Redrawing)
    • scriptAttribute of labelasnyc defer

    Performance optimization strategy:

    • Use the style that requires blockinglinkIntroduction, unnecessary usestyleTags (specific need to block viewing of business scenarios)
    • When there are many pictures, you must use lazy loading. Pictures need to be optimized most.webpack4We also need to configure picture compression in, which can greatly compress the picture size, and can be used for the new version of browser.Webp format pictureWebP details, picture optimization has the greatest performance improvement.
    • webpack4Configure code division and extract common codes into separate modules. Convenient cache
    /*
    When runtimeChunk is set to true, true, webpack will save all chunk file names into a separate chunk.
    Updating a file in this way will only affect the chunk and runtimechunk where it is located, avoiding changes to the file that references the Chunk.
    */
    runtimeChunk: true,
    splitChunks: {
    Chunks: 'all' // the default entry's chunk will not be split and configured as all
    }
    }
    //Because it is a single entry file configuration, the situation of multiple entries is not considered. Multiple entries should be handled separately.
    • For those that require event-drivenwebpack4For those who are lazy to load, please read this article.Webpack4 optimization tutorial, write very comprehensive
    • Some primitivejavaScriptTheDOMOptimization of operations and the like will be summarized below

    Six,TCPFour wave of his hand, disconnect


    Conclusion: Is the performance only a matter of load time or DOMContentLoaded time?

    • RAIL

      • ResponceResponse, research shows that responding to the user’s input within 100ms is usually considered as an immediate response by human beings. If the time goes on, the connection between operation and reaction will be interrupted, and people will feel that its operation is delayed. For example, when a user clicks a button and gives a response within 100ms, the user will feel that the response is timely and will not notice any delay.
      • AnimatonAt present, the screen refresh frequency of most devices is 60Hz, that is, 60 times per second. Therefore, as long as the running speed of web animation reaches 60FPS, we will feel the animation is very smooth.
      • IdleRAIL stipulates that tasks running in idle periods must not exceed 50ms. Of course, it is not only RAIL’s stipulation. The Longtasks standard of W3C Performance Working Group also stipulates that tasks exceeding 50ms are long tasks. How did the 50ms figure come about? The browser is single-threaded, which means that the main thread can only process one task at a time. If one task takes too long to execute, the browser cannot perform other tasks. The user will feel that the browser is stuck because his input cannot receive any response. In order to give a response within 100ms, limiting the tasks executed in the idle period to 50ms means that even if the user’s input behavior occurs at the beginning of the idle task execution, the browser still has the remaining 50ms to respond to the user’s input without causing noticeable delay to the user.
      • LoadIf the webpage cannot be loaded and the user can see the content within 1 second, the user’s attention will be distracted. Users will feel that what they want to do is interrupted. If they cannot open the web page for 10 seconds, users will feel disappointed and give up what they want to do. They may not come back later.

      How to make web pages more silky?

      • Use requestAnimationFrame

        • Even if you can guarantee that the total time of each frame is less than 16ms, there is no guarantee that no frame will be lost, depending on the way JS is triggered to execute. Assume that setTimeout or setInterval is used to trigger JS execution and modify the style to cause visual changes; Then there will be such a situation, because setTimeout or setInterval has no way to guarantee when the callback function will be executed. It may be executed in the middle of each fram e or at the end of each frame. Therefore, even if we can guarantee that the total time of each frame is less than 16ms, if the timing of execution is in the middle or at the end of each frame, the final result is still that there is no way to change the screen every 16ms, that is to say, even if we can guarantee that the total time of each frame is less than 16ms, if the timer is used to trigger the animation, the animation will still lose frames due to the uncertain timing of the trigger of the timer. At present, there is only one API in the whole Web that can solve this problem, that is, requestAnimationFrame, which can ensure the callback function to trigger stably at the beginning of each frame.
      • AvoidFSL

        • Execute firstJS, and then inJSThe style is modified in, resulting in the calculation of the style, and then the change of the style triggers the layout, drawing and composition. But ..JavaScriptYou can force the browser to execute the layout ahead of time, which is called forced synchronization of the layout.FSL.

          //Reading the value of offsetWidth will result in redrawing
           const newWidth = container.offsetWidth;
           
           //Setting the value of width causes rearrangement, but inside the for loop
           Code execution speed is extremely fast, when the above query operation results in redrawing
           Not finished yet, the following code will cause rearrangement again, and this is heavy
           The row will force the redraw above to be finished and rearranged directly, thus affecting the performance.
           Very large.  So we usually define a variable outside the loop, here
           Faces use variables instead of container.offsetWidth;
           boxes[i].style.width = newWidth + 'px';
           }
      • UsetransformProperty to manipulate animation, this property is handled by the synthesizer alone, so using this property can avoid layout and drawing.
      • UsetranslateZ(0)Turn on the layer to reduce redrawing rearrangement. Especially at the mobile end, try to use ittransformReplaceabsolute. The best way to create a layer is to use will-change, but some browsers that do not support this attribute can use 3D transform: translateZ(0)) to force the creation of a new layer.
      • Those who are interested can read this text.Front-end page optimization
      • The switching of styles is best defined in advance.classThroughclassTo change the style in batches, avoid redrawing and rearranging many times.
      • You can switch firstdisplay:noneModify the style again
      • Multiple timesappendOperations can be inserted into a newly generated element before being inserted into the page at one time.
      • Code reuse, function coritization, encapsulation of high-order functions, encapsulation of multiple reuse codes into common functions (commonly known as methods),ReactWhich are packaged into high-order components,ES6Inheritance can be used in,TypeScriptInterface inheritance, class inheritance, interface merging, class merging.
      • In storing data inLocalstorage and sessionstorageIn the middle school, you can define a module by yourself and store a copy of the data in the memory, so as long as you can read directly from the memory, the speed is faster and the performance is better.
      • It is better to use local variables instead of global variables without defining global variables. The search speed is twice as fast.
      • Highly recommended reading:Ruan Yifeng ES6 Tutorial
      • as well asWhat is TypeScript and Getting Started


    Add the followingReactThe performance optimization scheme of:

    • In the life cycle functionshouldComponentUpdateRightthis.stateAndprev stateFor shallow comparison, usefor-inLoop through both,

    As long as they get each value, as long as there is a difference, they will return.true, update components.

    • Com pont is not applicable when defining components, use React.component instead, soReactThe mechanism will automaticallyshouldComponentUpdateIn order to decide whether to update.
    • The above two optimization schemes only carry out shallow comparison and only compare the values of direct attributes. Of course, you can also add them to the abovethis.propsAndprevpropsBecauseshouldComponentUpdateThe life cycle function of has these two parameters. IfProps and stateThe value of is more complex, then the following method can be used to make a deep comparison.
    • Resolve:

      • Make sure it is a new value every time.
      • Using the immutable-js library, this library ensures that the generated values are unique.

        var map1 = Immutable.Map({ a: 1, b: 2, c: 3 });
        var map2 = map1.set('b', 50);
        map1.get('b');  // 2
        map2.get('b');  // 50
    • Summary: Using the above methods can reduce unnecessary repeated rendering.
    • ReactTheJSXSyntax requires that a root label must be wrapped to reduce unnecessaryDOMLevel, we useFragmentLabel instead, so that rendering will not render redundantDOMNode, letDIFFThe algorithm traverses faster.
    • UseReduxManage the reuse status of multiple global components.
    • ReactWhat is built isSPAApplication, yesSEONot friendly enough, can choose partSSRTechnical progressSEOOptimization.
    • YesAnt-designThis kind of thingUIComponent library, on-demand load configuration, fromThe introduction method of importbutton from 'anted' changed to import {Button} from antd.In the past few years. (SimilarThe difference between runtime and polifill in Babel7).
    • InReactSome of the data in the need to update, but not in a hurry to use, or each update of this data does not need to update components to re-render, you can periodically into classes on the instance of the attribute, which can reduce repeated meaninglessDIFFAnd rendering.
    • ReduxThe use of depends on the situation, if it is only a local state (only a component or parent-child component is used, do not use itRedux)。 For a parent-child and parent-child-child multi-layer componentState data, can also be usedContext contextTo deliver.ContextBut used by multiple different-level components of a complex projectstate, must be onRedux.
    • All native listening events, timers, etc. must be incomponentWillUnmountIf not, memory leaks will occur in large projects, which will greatly affect performance. ! !
    • What is React Hooks?
      Pure function components used to define stateful and lifecycle functions (in the past pure function components did not have stateful and lifecycle functions ~)
      Hooks is a new feature added to React v16.7.0-alpha and is backward compatible.

      • What is a hook (Hook) is essentially a function that allows you to use the state and life cycle functions of React components.
      • To make code more reusable, not in the definition of complexHOC(high order components) andClass component.
      • Use:

        useState(initValue)
         - const [ state, setState ] = React.useState(initValue);
         -Method for defining status data and operating status data
         useEffect(function)
         - useEffect(() => { do something })
         -Side effect functions (sending requests for data, subscribing to events, modifying DOM, etc.)
         -is essentially a life cycle function, equivalent to componentDidMount, componentDidUpdate, and componentWillUnmount
         useContext(Context)
         -context refers to the return value of React.createContext
         
         -The following Hooks are only used in special scenes and when necessary-
         useReducer
         - const [state, dispatch] = useReducer(reducer, initialState);
         -a useState alternative, equivalent to redux
         useCallback
         - useCallback(fn, inputs)
         -equivalent to shouldComponentUpdate, fn will only be called if the value of inputs changes
         useMemo(create, inputs)
         -equivalent to useCallback

    • For more details, please refer to the official documents:HOOKS document
      pay attention to

      • Hooks can only be called at the top level. Do not call hooks in loops, control flows, and nested functions.
      • Hooks can only be called from functional components of React. Do not call hooks in regular JavaScript functions. -(In addition, you can also call hooks in your custom hooks. )

    NativeJavaScriptImplementation of lazy loading:

    • Lazy loading, literally, can be simply understood as not loading when it is not in use. For the elements in the page, we can understand as follows: only when scrolling the page content makes this element enter the browser window (or slightly ahead of time, a given amount of advance is required), can we start loading pictures;
    • Don’t giveimgelementarysrcAttribute assignment, will not issue a request [cannot makesrc=""Even if only tosrcAssigned a null value will also issue a request], and once givensrcAttribute is assigned to the resource address value, then the request is issued to display the picture; So here we use this controlimgThe timing of element loading. At the beginning of the resourceurlPlace in custom attributedata-srcAmong them, and then when you need to load, get the attribute and assign it to the element’ssrcAttribute
    • From the above analysis, we can see that the main problem to be solved is how to detect whether the element is in the window. Here we need to use dom to operate the apiel.getBoundingClientRect()To obtain its position and determine whether it is in the window, which is briefly described here.
    • Element.getBoundingClientRect()The method returns the size of the element and its position relative to the viewport. The return value is aDOMRectObject, which is defined by the element’sgetClientRects()The method returns a set of rectangles, that is, the CSS border set associated with the element.DOMRectThe object contains a set of read-only attributes that describe the border-Left, top, right and bottom, in pixels. Except ..Width and heightAll external attributes are relative to the position of the upper left corner of the viewport.

      • Therefore, we can use the following logic to determine whether an element enters the window:
    function isInSight(el){
     var eldom = typeof el == 'object'?  el:document.querySelector(el);
     var bound = eldom.getBoundingClientRect();
     //bound here includes el's distance from the window.
     // bound.left is the distance value of the element from the left side of the window;
     // bound.top is the distance value of Yuan Shu from the top of the window;
     
     //Judging whether the element enters the window by the above two values;
     var clientHeigt = window.innerHeight;
     var clientWidth = window.innerWidth;
     // return (bound.top>=0&&bound.left>=0)&&(bound.top<=window.innerHeight+20)&&(bound.left<=window.innerWidth+20);
     return !  ((bound.top>clientHeigt)||(bound.bottom<0)||(bound.left>clientWidth)||(bound.right<0))
     }

    • among themWindow.innerHeight and window.innerWidthIt is the height and width of the window respectively. The reason why 20 is added is to make lazy loading a little earlier and make the user experience better.
    • Add scroll event listening:

      • So when will you check whether the element is in the window and judge whether it is loaded? here, because the position of the element relative to the window will change due to the scrolling of the page, that is to say, scrolling will change the result of isInSight, so here we add scroll event monitoring to the window:
    //When loading is completed, detect and load the pictures within the visual range.
    window.onload= checkAllImgs;
    //Add scrolling monitoring to detect whether the pictures in the current range can be loaded when the range changes.
    window.addEventListener("scroll",function(){
    checkAllImgs();
    })
    
    //Detect all pictures and assign the src attribute of the pictures in the window, namely start loading;
    function checkAllImgs(){
    var imgs = document.querySelectorAll("img");
    Array.prototype.forEach.call(imgs,function(el){
    if(isInSight(el)){
    loadImg(el);
    }
    })
    }
    //Start loading resources for the specified el
    function loadImg(el){
    var eldom = typeof el == 'object'?  el:document.querySelector(el);
    if(!  eldom.src){
    //lazy loading img definition is as follows: < divclass = "img" > < imghat = "loading" data-index = 7data-src = "http://az 608707.vo.msecnd.net/files/Martapuramarket _ en-us 950204987 _ 1366x768.jpg" > </div >
    var source = eldom.getAttribute("data-src");
    var index = eldom.getAttribute("data-index");
    eldom.src = source;
    The console.log ("the" +index+ "picture enters the window and starts loading.  .  .  。"  )
    }
    
    }
    • In this way, the lazy loading of pictures can be realized simply, and of course, scrolling can be optimized.

    Now the latest version of Google’s browser should also be supported.<img>Inside the labelloadingProperty, believe that future development will be more and more convenient.
    All of the above are based on my knowledge points, and there will be more performance optimization schemes in the later period. I’ll pass by and have a nice collection ~, welcome to ask questions and add ~