Πώς να ρυθμίσετε την απόδοση και την παρακολούθηση χρηστών στο React με το Google Analytics

Η παρακολούθηση των χρηστών και της απόδοσης της εφαρμογής σας είναι ένα πολύ σημαντικό μέρος της σύγχρονης ανάπτυξης ιστού. Μπορεί να έχετε δει αναφορές εταιρειών να αυξάνουν τα έσοδα μειώνοντας απλώς το χρόνο φόρτωσης της εφαρμογής τους κατά μερικές εκατοντάδες χιλιοστά του δευτερολέπτου.

Η παρακολούθηση της συμπεριφοράς των χρηστών σας είναι επίσης σημαντική. Αυτό θα σας επιτρέψει να τροποποιήσετε και να δημιουργήσετε την εφαρμογή σας σύμφωνα με τον προτιμώμενο τρόπο αλληλεπίδρασης των χρηστών σας με την εφαρμογή σας, με αποτέλεσμα πιο ευτυχισμένους χρήστες και περισσότερη επισκεψιμότητα στον ιστότοπό σας.  

Εδώ είναι το ολοκληρωμένο έργο:

//github.com/iqbal125/react-hooks-google-analytics

Σε αυτόν τον οδηγό θα σας δώσω έναν πλήρη θεμελιώδη οδηγό για την παρακολούθηση τόσο της απόδοσης όσο και της συμπεριφοράς των χρηστών. Μέχρι το τέλος του σεμιναρίου θα έχετε όλα όσα χρειάζεστε για να δημιουργήσετε σύνθετες ρυθμίσεις παρακολούθησης χρηστών και απόδοσης.

Ακολουθήστε με στο Twitter για περισσότερα μαθήματα στο μέλλον: //twitter.com/iqbal125sf

Πίνακας περιεχομένων

  1. Εισαγωγή
  2. Google Analytics
  3. Προβολές σελίδων παρακολούθησης
  4. Απόδοση φορτίου παρακολούθησης
  5. Παρακολούθηση απόδοσης απόδοσης
  6. Παρακολούθηση βαφής
  7. Κύλιση παρακολούθησης
  8. Παρακολούθηση συμβάντων
  9. Γρήγορες συμβουλές απόδοσης και ευρετική

Εισαγωγή

Θα σας δείξω τις μετρήσεις απόδοσης για την ανάπτυξη της αντίδρασης για να διατηρήσετε αυτό το σεμινάριο συνοπτικό. Αλλά σε μια πραγματική κατάσταση, μην δοκιμάσετε την έκδοση dev, επειδή περιέχει πολλούς κωδικούς χειρισμού σφαλμάτων και έλλειψη βελτιστοποιήσεων που θα σας δώσουν εξαιρετικά στρεβλωμένα αποτελέσματα.

Για αυτόν τον λόγο, είναι καλύτερο να δοκιμάσετε την έκδοση παραγωγής κατασκευής για να καθορίσετε ορισμένες μετρήσεις βασικής γραμμής.

Επίσης, για λόγους συντομίας, θα αναφέρω απλώς το Google Analytics ως GA.

Το GA επίσης δεν λειτουργεί με τοπικό κεντρικό υπολογιστή. Για να λάβετε μια προσομοίωση του τι ενδέχεται να στέλνετε στο GA χωρίς να το στείλετε πραγματικά και να βλάψετε τα analytics σας, μπορείτε απλώς να αντικαταστήσετε μια κονσόλα.

ReactGA  είναι η καθολική συνάρτηση και είναι γνωστή ως η ουρά εντολών επειδή δεν εκτελεί εντολές αμέσως αλλά προσθέτει εντολές σε μια ουρά και στη συνέχεια τις στέλνει ασύγχρονα. Αυτό δεν συνδέει το κύριο νήμα και δεν οδηγεί στον κώδικα παρακολούθησης να βλάψει την απόδοση της εφαρμογής σας.

Ένα χτύπημα είναι όταν ένας ιχνηλάτης GA στέλνει δεδομένα στο Google Analytics.

Θα επικεντρωθούμε κυρίως στον κώδικα React σε αυτό το σεμινάριο, υπάρχουν πολύ καλύτερα άλλα σεμινάρια για να μάθετε πώς να χρησιμοποιείτε το GA.  

Υπάρχουν 3 κύριες λειτουργίες που θα χρησιμοποιήσουμε κατά την αποστολή επισκέψεων στο GA. Πρέπει να γνωρίζετε ότι υπάρχουν περισσότερα, αλλά θα επικεντρωθούμε μόνο σε αυτά τα 3 για τους σκοπούς αυτού του σεμιναρίου.

GAReact.pageview(): θα περάσει σε μια συμβολοσειρά που περιέχει τη διαδρομή.

GAReact.timing(): Θα πάρει ένα αντικείμενο ως παράμετρο. Θα περιέχει πληροφορίες σχετικά με τις μετρήσεις απόδοσης που θα δούμε παρακάτω. Τα πεδία θα είναι category, variable, valueκαι label. Παρατηρήστε ότι μόνο η ιδιότητα αξίας θα προέρχεται από την εφαρμογή μας οι υπόλοιπες ιδιότητες καθορίζονται από τον χρήστη.

GAReact.event(): Θα πάρει ένα αντικείμενο ως παράμετρο. Θα περιέχει δεδομένα σχετικά με τα γεγονότα που λαμβάνουν χώρα στην εφαρμογή (έντυπο υποβάλει, κουμπί κλικ, κ.λπ.) θα πρέπει πεδία category, action, labelκαι value. Λάβετε υπόψη ότι μόνο η τιμή θα προέλθει από την εφαρμογή, οι υπόλοιπες ιδιότητες καθορίζονται από τον χρήστη.

Συνθετικές δοκιμές έναντι RUM

Ίσως αναρωτιέστε γιατί αντιμετωπίζετε όλο αυτό το πρόβλημα της ρύθμισης μετρήσεων Performance Observer εάν μπορείτε απλά να χρησιμοποιήσετε ένα εργαλείο όπως το Lighthouse ή το Webpage speed test και να λάβετε αυτές τις μετρήσεις εισάγοντας απλώς τη διεύθυνση url.

Εργαλεία όπως αυτά είναι σημαντικά, αλλά είναι αυτά που είναι γνωστά ως συνθετικές δοκιμές, καθώς η δοκιμή θα γίνει στη συσκευή σας και η δοκιμή δεν θα αντικατοπτρίζει πραγματικά αυτό που βιώνουν οι χρήστες σας. Μπορείτε να πετάξετε το CPU ή το δίκτυο όταν κάνετε αυτές τις δοκιμές, αλλά εξακολουθούν να είναι προσομοιώσεις.

Η χρήση του GA με μετρήσεις Performance Observer θα μας δώσει πραγματικούς αριθμούς από τους πραγματικούς τελικούς χρήστες, τις συσκευές και τα δίκτυά μας. Αυτό είναι γνωστό ως παρακολούθηση RUM ή πραγματικός χρήστης.

Συνθετικά εργαλεία δοκιμών. Απλώς εισαγάγετε τη διεύθυνση URL της εφαρμογής σας για να εκτελέσετε τις δοκιμές συνθετικών.

  • //www.webpagetest.org/
  • //www.thinkwithgoogle.com/feature/testmysite
  • //developers.google.com/speed/pagespeed/insights/
  • //www.uptrends.com/tools/website-speed-test
  • //tools.pingdom.com/

Google Analytics

Ρύθμιση

Εάν έχετε ήδη ρυθμίσει το Google Analytics στην εφαρμογή σας, μη διστάσετε να παραλείψετε αυτήν την ενότητα, δεν υπάρχει τίποτα νέο εδώ.

Για να ρυθμίσετε το GA, μεταβείτε στον πίνακα ελέγχου και κάντε κλικ στην καρτέλα διαχειριστή στο πλευρικό συρτάρι. Στη συνέχεια, κάντε κλικ στην επιλογή προσθήκης ιδιότητας. Συμπληρώστε τις απαιτούμενες πληροφορίες.

Εάν διαβάζετε αυτό το σεμινάριο, θα υποθέσω ότι είστε αρκετά έξυπνοι για να ρυθμίσετε το Google Analytics με αυτές τις 3 γραμμές διδασκαλίας. Εάν όχι, εδώ είναι ένας εύχρηστος οδηγός από την Google.

//support.google.com/analytics/answer/1042508?hl=el

Αφού ολοκληρώσετε τη ρύθμιση, θα λάβετε ένα αναγνωριστικό παρακολούθησης στην κορυφή, το οποίο θα χρησιμοποιήσουμε στην εφαρμογή React.

Ρύθμιση σε αντίδραση

Δεν χρειάζεται να εφεύρουμε ξανά τον τροχό εδώ, μπορούμε να χρησιμοποιήσουμε μια βοηθητική βιβλιοθήκη που δημιουργήθηκε από το Ίδρυμα Mozilla για να μας βοηθήσει με τη ρύθμιση του React.

Για να εγκαταστήσετε τη βιβλιοθήκη απλώς εκτελέστε

npm install react-ga

Στη συνέχεια, απλώς εισαγάγετε το αντικείμενο ReactGA όπου θέλετε να το χρησιμοποιήσετε

import ReactGA from 'react-ga';

Θα πρέπει επίσης να το αρχικοποιήσετε στο ριζικό στοιχείο με το αναγνωριστικό παρακολούθησης από το google analytics.  

... ReactGA.initialize('UA-00000-1'); ...

Παρατηρητές

ReactGA cant do anything besides send data to the Google Analytics website. To actually get performance metrics to send, we will use the Performance Observer browser API. This Performance Observer API has nothing to do with GA or even React, it is a browser API that is available in most modern browsers

How the PerformanceObserver and related APIs work exactly is a fairly big topic and is far out of the scope of this tutorial. See the Further Reading section for more info and links to tutorials.

The basic idea of how they work is they "observe" something and then send that data asynchronously at the time of the browsers choosing. This keeps the main thread free for critical app functionality and related tasks, therefore tracking events does not affect your app performance.

Before observers you would have to add an event listener and fire it synchronously every time something happened, this resulted  in noticeable performance issues if too many events were being fired at once. So this is the problem observers are looking to solve.

Track Page Views

Tracking page views in a single page app like react may seem like it would be complicated but its relatively simple thanks to react-router and history libraries.

history.listen((location) => { ReactGA.set({ page: location.pathname }); ReactGA.pageview(location.pathname) } );

history.listen() allows you to fire a callback on route changes which in our case happens to be the GA hit. The route is contained in the pathname property of location.  But there are a couple of things to note such as:

Handling the intial load

History is listening for page changes but it doesn’t cause a hit on the initial page load.

There are a few ways to handle the initial load. I found a simple way to do it that I think requires the least amount of code and complexity and it is just to have an initial load variable and save it to the global state. In the home page just use a conditional to check if it is false then set it to true after the hit.

Context variable in the parent component

... const [initialLoad, setInitialLoad] = useState(false) ...  setInitialLoad(true), >

Then the useEffect() in the home child component

... useEffect(() => { if(!context.initialLoadProp) { ReactGA.pageview(props.location.pathname); context.setInitialLoadProp(true) } }, []) ... 

There are other implementations and discussions you can find here:

//github.com/react-ga/react-ga/wiki/React-Router-v4-withTracker

Tracking pages with User ids

Another thing you might want to know is how many users are visiting their private profile pages. Just using the pageviews would give you a unique url for every hit and will not work.

Say you have the following URLs with user ids:

user/4543456/messages

user/4543456/account

user/3543564/messages

user/3543564/replytomessage

user/3543564/account

These will all give you a unique page hit. A simple fix is to just check with a conditional and then remove the unique id. You can also use a conditional to make sure you dont send these pages to google analytics.

... history.listen((location) => { if(location.pathname.includes('/user')) { let rootURL = location.pathname.split('/')[1] let userPage = location.pathname.split('/')[3] let pageHit = `/${rootURL}/${userPage}` ReactGA.pageview(pageHit) } else { ReactGA.set({ page: location.pathname }); ReactGA.pageview(location.pathname) } }); ...

We are simply just parsing out the user id and then concatenating the route again before sending the hit.

This would probably not be true for forum posts since having a unique URL for each post would be correct, since you would like to know how many people visited each post.

Tracking Load Performance

Getting the load performance is relatively easy. All the load performance data is under the navigation entry which is part of the navigation timing API.

The Performance Observer Can be setup like so in the root parent component.

const callback = list => { list.getEntries().forEach(entry => { ReactGA.timing({ category: "Load Performace", variable: "Some metric", value: "Value of Metric" }) }) } var observer = new PerformanceObserver(callback); observer.observe({entryTypes: ['navigation'] })

First we define a function to be called for each entry. Then we pass this callback into our PerformanceObserver and finally we call the .observe() method on our observer and pass in navigation as an entryType.  

This will give you the following entry:

This is a very large amount of information but really we need only 3 properties to track the main load performance:

requestStart: when the browser issues a request to get the webpage from the server

responsesStart: when the first byte of the website arrives

responseEnd: When the last byte of the website arrives

There are a few things that happen before the requestStart such as DNS lookup and the TLS handshake. Use this data and see if you can combine it with other properties to make new metrics.

With the above three properties we can create 3 important metrics. Simply substitute in the variable and value properties for the respective metric.

const callback = list => { list.getEntries().forEach(entry => { ReactGA.timing({ category: "Load Performace", variable: 'Server Latency', value: entry.responseStart - entry.requestStart }) }) }

Server latency:

entry.responseStart - entry.requestStart

Download time:

entry.responseEnd - entry.responseStart

Total app load time:

entry.responseEnd - entry.requestStart

Time to Interactive

This metric is essentially how long it takes for a user to be able to interact with your site.

Until a native TTI metric is available through the browser api we can use this handy polyfill npm module for now. This module is created by Google.

npm install tti-polyfill

Then we can use the polyfill like so.

... import ttiPolyfill from 'tti-polyfill'; ttiPolyfill.getFirstConsistentlyInteractive().then((tti) => { ReactGA.timing({ category: "Load Performace", variable: 'Time to Interactive', value: tti }) }); ...

We are simply sending a hit inside of our function with a chained .then() statement since we will retrieve this metric asynchronously.

Tracking Rendering Performance

Now we can discuss Rendering Performance, which is how long it takes for React to construct the tree of DOM nodes. We can track rendering performance with the mark and measure entries.

mark is used to "mark" a point in time you want to keep track of. Just pass in a string as the name of the mark on the exact line you want mark for tracking.

performance.mark("name of mark")

measure is the difference between the 2 marks. Just set the name of the measure and pass in start and end marks, which will give you the difference between the marks in milliseconds.

performance.measure.("name of mark", startingMark, EndingMark)

Thankfully React comes pre-bundled with these marks and measures, which saves us from having to open up the React Source and having to write them ourselves. Simply pass in mark and measure for the entry types and you're done.

... var observer = new PerformanceObserver(callback); observer.observe({entryTypes: ['mark', 'measure'] }) ...

This will give you the time it takes for root parent component and all child components to render in milliseconds. You will get entries that look something like this:

You will also get the time it takes for the lifecycle methods to execute. There is a wealth of information here, simply pick what you want to track and send it to GA by checking for the name in a conditional statement.

... const callback = list => { list.getEntries().forEach(entry => { if(entry.name.includes('App') ) { ReactGA.timing({ category: "App Render Performace", variable: entry.name, value: entry.duration }) } }) } ...

Tracking Paint Performance

Now we can track paint which is when the pixels are drawn (or "painted") to the screen after the DOM tree is rendered.  

Tracking paint performance comprises 3 metrics: First Paint, First Contentful Paint and First Meaningful paint.

First Paint: First time anything besides a blank white page is present.

First Contentful Paint: When the first DOM element is present. Text, image etc.

First Paint and First Contentful Paint will be given automatically by the API. Simply do the following:

... const callback = list => { list.getEntries().forEach(entry => { ReactGA.timing({ category: "Paint", variable: entry.name, value: entry.startTime }) }) } var observer = new PerformanceObserver(callback); observer.observe({entryTypes: ['paint'] })

The entries will look like this

It is entirely possible that both these metrics are exactly the same, down to the millisecond. Even if they are not the same there can be an irrelevant difference between them. For this reason another more flexible metric is used, called:

First Meaningful Paint: When something “meaningful” is painted to the screen. “meaningful” is kept intentionally vague, allowing the developer to decide themselves what they want to test for.  

According to Google, the First Paint and First Contentful paint answer the question of “Is it happening” and First Meaningful Paint answers “Is it useful”. “Is it useable” is answered by Time to Interactive.

A common way to use First Meaningful paint is to test for the hero element. Which is the main element on the page.

An example for youtube would be the video player. Suggested videos and comments would all then be non hero secondary elements. Tracking when the video player has finished painting would be the First Meaningful Paint in this case.

A common hero element on the homepage is a background image near the header that gives the value proposition or theme of the website. Knowing this, we can use the resource timing api to measure when our image has finished loading and make this our First Meaningful paint metric.

If your hero element is an image then you can simply look at the resource timing API and then look at responseEnd property and use that as your FMP.

... const callback = list => { list.getEntries().forEach(entry => { ReactGA.timing({ category: "First Meaningful Paint", variable: entry.name, value: entry.responseEnd }) }) } var observer = new PerformanceObserver(callback); observer.observe({entryTypes: ['resource'] }) ...

Entire resource timing response.

Since the loaded image doesn't technically mean its painted you can also try to set the marks manually.

 //jsx {performance.mark('start')  {performance.mark('end') {performance.measure('diff', 'start', 'end') 

Again there is a lot of flexibility in this metric, really consider your use case and what your trying to measure.

Track Scroll

Tracking the scroll position of your user is a fairly important part of analytics. You can for example see how many users scrolled past a certain image or section of text. Having this information you can then tweak your content and increase your conversion.

You would have seen older implementations use getBoundingClientRect() but this would tie up the main thread and therefore tracking the scroll would actually decrease performance. You can execute the scroll events asynchronously with IntersectionObserver.  

The IntersectionObserver is different than the PerformanceObserver we have been working with in the last sections.

The IntersectionObserver takes 2 arguments a callback and a options object. The options object will have 3 properties

root: The element you are trying to test the intersection against. In most cases this will be the viewport which will be the value of null.

rootMargin: The margins around the root element. ex: "10px"

threshold: How much of the element is visible before isIntersecting is true. ex: 0.5 means isIntersecting  is true when 50% of the element is visible. 0 means the very top of the element and 1.0 means the entire element.

The entry will return an object like so:

isIntersecting:  Essentially used to determine if the element is visible which would be true when the threshold is reached.

And now the code:

//Get the element you want to track with useRef const intersectTarget = useRef(null) //Use the observer inside the component you want to track scroll useEffect(() => { const opts = { root: null, rootMargin: '0px', threshold: 0 } const callback = list => { list.forEach(entry => { if(entry.isIntersecting) { ReactGA.event({ category: 'Scroll', action: 'Scrolled to heading 2', value: entry.intersectionRatio }) } }) } const observerScroll = new IntersectionObserver(callback, opts) observerScroll.observe(intersectTarget.current) }, []) //jsx 

Second Heading

The main idea here is to first initialize the useRef hook. Then use the ref on the element you want to track for scroll. The callback and observer will be called in the useEffect hook and the element can be passed to the .observe() method with the name of the ref and the .current property.

Note: The intersection observer will fire on the initial page load even if the element is not visible. This is normal and you will see that the isIntersecting property is false.

Note also: At the time of this writing there is also an isVisible property on the entry, but it does not function as its name suggests. It stays false even when the element visible. It is not documented anywhere so I cant comment on its purpose, but I would suggest using isIntersecting   property instead.  

Track Events

The basic idea to tracking events is to send GA hits inside of the function call attached to an event handler. There really isn’t much more to it than that.

One thing to note is if your user is submitting a form you can specify the     transport: beacon property in your event hit, which will let you reliably send the hit even if the page is reloaded to another page. This isn't so much of an issue in a single page app like React, but if you did want to do this, just know this option is available.

See the navigator beacon for more info

I will show you how to track a form submit, but this pattern will work with basically any event. Again you are simply sending the hit in a function attached to the event handler.

 ... const handleSubmit = (event) => { event.preventDefault(); ReactGA.event({ category: 'Form', action: 'Form Submit', transport: 'beacon' }); apiCall('/apicall', event.target.value) }; ...  ...  ...

Quick Performance Tips and Heuristics

Biggest area for improvement I see for a lot of developers is to code things from scratch instead of using a library. Tree shaking reduces bloat a little bit, but there is still considerable bloat compared to coding something yourself. Which will allow you to only write the code you need.  Try to use as little libraries as possible. Instead of using a library for a few functions, try to just look at the source code of the library and try to implement those functions yourself. Also keep in mind that most libraries have to prioritize ease of use and personalization over performance.

Some other ones:

  • For event firing like scroll you will definitely need debouncing/throttling. You don't need to do this for observers.  
  • Only import functions and variables you need and let tree shaking discard unused code.
  • Do not pass in an anonymous function to events.
  • Turn your React app into a PWA allowing users to download and install your webapp locally on their device.
  • Reduce payloads with code splitting and lazy loading.
  • Decrease file size by using gzip or similar compression
  • Serve images from a CDN
  • Enable caching through your http headers on server responses.
  • Optimize images. See the google fundamentals guide for a full guide on how to do so.
  • Use the new CSS Flexbox for layout. Avoid Layout Thrashing
  • Only Use transforms and opacity for css changes. So instead of changing height and width use scale X and scaleY

Luckily, A lot of these optimizations, like minification and gzip, are done for automatically when you run the npm build command on a React Project.

Conclusion

Ευχαριστώ για την ανάγνωση! Αν βρήκατε άλλες δημιουργικές μετρήσεις ή έξυπνους τρόπους παρακολούθησης χρηστών, ενημερώστε με στα σχόλια.

Ακολουθήστε με στο twitter για περισσότερα μαθήματα στο μέλλον: //twitter.com/iqbal125sf

Αναρτήσεις ιστολογίου:

//www.searchenginejournal.com/a-technical-seo-guide-to-lighthouse-performance-metrics/292703/#κλείσιμο

//infrequently.org/2017/10/can-you-afford-it-real-world-web-performance-budgets/

//speedcurve.com/blog/user-timing-and-custom-metrics/

//designsystem.digital.gov/performance/

//hackernoon.com/react-performance-primer-64fe623c4821

//reactjs.org/docs/optimizing-performance.html

Παρατηρητές:

//css-tricks.com/paint-timing-api/

//css-tricks.com/breaking-performance-api/

//hackernoon.com/tracking-element-visibility-with-react-and-the-intersection-observer-api-7dfaf3a47218

//www.smashingmagazine.com/2018/01/deferring-lazy-loading-intersection-observer-api/

//www.sitepen.com/blog/improving-performance-with-the-paint-timing-api/

Βασισμένο στο Google:

//developers.google.com/web/fundamentals/performance/user-centric-performance-metrics

//developers.google.com/web/fundamentals/performance/navigation-and-resource-timing/

//developers.google.com/web/tools/chrome-devtools/speed/get-started

//marketingplatform.google.com/about/optimize/

//developers.google.com/analytics/devguides/collection/analyticsjs/user-timings

//support.google.com/analytics/answer/1033068#Ανατομία

//developers.google.com/analytics/devguides/collection/analyticsjs/single-page-applications

//support.google.com/analytics/answer/1033068

//docs.google.com/document/d/1GGiI9-7KeY3TPqS3YT271upUVimo-XiL5mwWorDUD4c/preview#

//www.doubleclickbygoogle.com/articles/mobile-speed-matters/