Performance#
Key Points#
- Regularly run Lighthouse audits to ensure the site is performant.
- Strive for the best possible Lighthouse scores.
- Ideally in the 80-100 range, but definitely above 50.
- Learn & apply the most performant methods available:
- Ensure Miva’s TTFBs are below or near 500ms
- Sites will periodically be evaluated & ranked against the performance KPI metrics below. Monitor your clients and recommend improvements as you see them:
KPIs & Standards#
Name | Code | Definition | Good | Average | Poor |
---|---|---|---|---|---|
Time to First Byte | TTFB | Measures the duration from the user or client making an HTTP request to the first byte of the page being received by the client’s browser. It is a measurement used as an indication of the responsiveness of a webserver or other network resources. | < 800 ms. | 800 ms. to 1800 ms. | >= 1800 ms. |
First Contentful Paint | FCP | The time when the browser first rendered any text, image (including background images){target=_blank}, non-white canvas or SVG. This includes text with pending webfonts. This is the first time users could start consuming page content | < 1800 ms. | 1800 ms. to 3000 ms. | >= 3000 ms. |
Largest Contentful Paint | LCP | A user-centric metric for measuring perceived load speed because it marks the point in the page load timeline when the page’s main content has likely loaded—a fast LCP helps reassure the user that the page is useful. | < 2500 ms. | 2500 ms. to 4000 ms. | >= 4000 ms. |
First Input Delay | FID | A user-centric metric for measuring load responsiveness because it quantifies the experience users feel when trying to interact with unresponsive pages—a low FID helps ensure that the page is usable. | < 100 ms. | 100 ms. to 300 ms. | >= 300 ms. |
Cumulative Layout Shift | CLS | A user-centric metric for measuring visual stability because it helps quantify how often users experience unexpected layout shifts—a low CLS helps ensure that the page is delightful. | < 0.1 | 0.1 to 0.25 | >= 0.25 |
Best Practices#
Use Efficient Processes#
The code should be efficient, and should not affect TTFB in a way where the site would go down.
Incorrect
<mvt:assign name="l.settings:has_forbidden_items" value="0" />
<mvt:foreach iterator="group" array="basket:groups">
<mvt:item name="customfields" param="Read_Product_ID( l.settings:group:product_id, 'forbidden', l.settings:group:customfield_values:customfields:forbidden )" />
<mvt:if expr="l.settings:group:customfield_values:customfields:forbidden">
<mvt:assign name="l.settings:has_forbidden_items" value="1" />
</mvt:if>
</mvt:foreach>
Reasoning: Continues to loop when it’s not necessary.
Correct
<mvt:assign name="l.settings:has_forbidden_items" value="0" />
<mvt:foreach iterator="group" array="basket:groups">
<mvt:item name="customfields" param="Read_Product_ID( l.settings:group:product_id, 'forbidden', l.settings:group:customfield_values:customfields:forbidden )" />
<mvt:if expr="l.settings:group:customfield_values:customfields:forbidden">
<mvt:assign name="l.settings:has_forbidden_items" value="1" />
<mvt:foreachstop />
</mvt:if>
</mvt:foreach>
Reasoning: Once the field has been found, it stops looping.
Use Efficient Functions#
Incorrect
<mvt:do file="g.Module_Library_DB" name="l.product_loaded" value="Product_Load_Code( l.product_code, l.product )" />
Reasoning: Not utilizing the “Cached” function when applicable, to possibly reduce a DB lookup.
Correct
<mvt:do file="g.Module_Library_DB" name="l.product_loaded" value="Product_Load_Code_Cached( l.product_code, l.product )" />
Reasoning: Utilizes the “Cached” function when applicable, to possibly reduce a DB lookup.
Avoid Infinite Loops#
There should not be a way to create an infinite loop.
Incorrect
<mvt:if expr="g.Empty_Cart">
<mvt:do file="g.Module_Library_DB" name="l.success" value="Runtime_Basket_Empty( g.Basket:basket_id )" />
<mvt:do file="g.Module_Feature_PGR_DB" name="l.success" value="BasketDiscountTotal_Delete_All_Basket( g.Basket:basket_id )" />
<mvt:assign name="l.result" value="miva_output_header( 'Status', '302 Moved Temporarily' )" />
<mvt:eval expr="miva_output_header( 'Location', 'https://' $ encodeentities(g.domain:name) $ s.request_uri )" />
<mvt:exit />
</mvt:if>
<a href="&mvte:urls:BASK:auto_sep;Empty_Cart=1">Empty Cart</a>
Reasoning: The code will infinitely loop because the query-string ?Empty_Cart=1
will continue to get passed in the s.request_uri
of the following Location
headers.
Correct
<mvt:if expr="g.Empty_Cart">
<mvt:do file="g.Module_Library_DB" name="l.success" value="Runtime_Basket_Empty( g.Basket:basket_id )" />
<mvt:do file="g.Module_Feature_PGR_DB" name="l.success" value="BasketDiscountTotal_Delete_All_Basket( g.Basket:basket_id )" />
<mvt:assign name="l.result" value="miva_output_header( 'Status', '302 Moved Temporarily' )" />
<mvt:assign name="l.result" value="miva_output_header( 'Location', encodeentities( l.settings:urls:BASK:auto ) )" />
<mvt:exit />
</mvt:if>
<a href="&mvte:urls:BASK:auto_sep;Empty_Cart=1">Empty Cart</a>
Reasoning: The redirect to the new location is a fix value that cannot create an infinite loop.