From 33058f2b292b3a581333bdfb21b8f671898c5060 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:40:17 -0500 Subject: initial commit --- .../index.html | 91 ++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 files/zh-cn/mozilla/add-ons/performance_best_practices_in_extensions/index.html (limited to 'files/zh-cn/mozilla/add-ons/performance_best_practices_in_extensions') diff --git a/files/zh-cn/mozilla/add-ons/performance_best_practices_in_extensions/index.html b/files/zh-cn/mozilla/add-ons/performance_best_practices_in_extensions/index.html new file mode 100644 index 0000000000..d6a6f7515b --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/performance_best_practices_in_extensions/index.html @@ -0,0 +1,91 @@ +--- +title: 扩展中的性能最佳实践 +slug: Mozilla/Add-ons/Performance_best_practices_in_extensions +translation_of: Archive/Add-ons/Performance_best_practices_in_extensions +--- +

Firefox 的一个巨大优势就是可扩展性非常强。扩展组件几乎可以做任何事情。但这也带来了一个劣势: 扩展组件如果写的不好,将会大大的影响浏览器性能,包括 firefox 的整体体验。 本文则提供了一些最佳实践方式,他们不仅能够提升你的组件的性能和速度,也会对 firefox 带来同样的影响。

+

提升启动性能

+

Extensions are loaded and run whenever a new browser window opens. That means every time a window opens, your extension can have an impact on how long it takes the user to see the content they're trying to view. There are several things you can do to reduce the amount of time your extension delays the appearance of the user's desired content.

+

只在需要的时候装载需要的东西

+

Don't load things during startup that are only needed if the user clicks a button, or if a given preference is enabled when it's not. If your extension has features that only work when the user has logged into a service, don't load the resources for those features until the user actually logs in.

+

使用  JavaScript code modules

+

You can create your own JavaScript code modules incorporating sets of features that are only needed under specific circumstances. This makes it easy to load chunks of your extension on the fly as needed, instead of loading everything all at once.

+

This has an advantage over XPCOM modules, which are always loaded when your extension starts up.

+

Of course, for extremely simple extensions it may not make sense to modularize your code.

+

Defer everything that you can

+

Most extensions have a load event listener in the main overlay that runs their startup functions. Do as little as possible here. The browser window is blocked while your add-on's load handler runs, so the more it does, the slower Firefox will appear to the user.

+

If there is anything that can be done even a fraction of a second later, you can use an {{ interface("nsITimer") }} or the {{ domxref("window.setTimeout()") }} method to schedule that work for later.  Even a short delay can have a big impact.

+

通用性能提示

+

避免产生内存泄漏

+

Memory leaks require the garbage collector and the cycle collector to work harder, which can significantly degrade performance.

+

Zombie compartments are a particular kind of memory leak that you can detect with minimal effort.  See the Zombie compartments page, especially the Proactive checking of add-ons section.

+

See Common causes of memory leaks in extensions for ways to avoid zombie compartments and other kinds of leaks.

+

As well as looking for these specific kinds of leaks, it's worth exercising your extension's functionality and checking the contents of about:memory for any excessive memory usage.  For example, bug 719601 featured a "System Principal" JavaScript compartment containing 100s of MBs of memory, which is much larger than usual.

+

Use JavaScript Modules

+

JavaScript modules are just like any other JavaScript, with the exception that they are singletons and Firefox can cache the compiled code for faster use the next time the browser is started. Any time your add-on loads JavaScript from an {{ HTMLElement("script") }} element you should consider using a JavaScript Module instead. For more on how JavaScript modules work, see the Using JavaScript Code Modules page.

+

Avoid Writing Slow CSS

+ +

Avoid DOM mutation event listeners

+

Adding DOM mutation listeners to a document disables most DOM modification optimizations and profoundly degrades the performance of further DOM modifications to that document. Moreover, removing the listeners does not reverse the damage. For these reasons, the following events should be avoided wherever possible: DOMAttrModified, DOMAttributeNameChanged, DOMCharacterDataModified, DOMElementNameChanged, DOMNodeInserted, DOMNodeInsertedIntoDocument, DOMNodeRemoved, DOMNodeRemovedFromDocument, DOMSubtreeModified

+

For more on these events and their deprecation, see Mutation events. Use Mutation Observers instead if possible.

+

延迟加载服务 services

+

The XPCOMUtils JavaScript module provides two methods for lazily loading things:

+ +

As of Firefox 4.0, many common services are already cached for you in Services.jsm.

+

Reduce file I/O

+

TODO: Give examples below, link to code, bugs, docs.

+ +

Use the right compression level for JAR and XPI files

+

Reading data from compressed archives costs time. The higher the compression level of the archive, the higher also the performance cost of reading the data from it. So any JAR files in your extension should always be packed with compression level 0 (no compression) for better performance. It may seem counter-intuitive, but doing this will increase the JAR file size and actually decrease the XPI file size as it allows for compression between files inside the JAR to be done when compressing the XPI (essentially a poor-man's solid archive effect).

+

If your extension doesn't specify em:unpack then its XPI file will not be unpacked in Firefox 4 and used directly instead. This makes choosing a low compression level preferable; we recommend using compression level 1. It will increase the download size only a small amount, even compared to maximum compression.

+

使用异步 I/O

+

This cannot be stressed enough: never do synchronous I/O on the GUI thread.

+ +

Unnecessary onreadystatechange in XHR

+

addEventListener(load/error) and/or xhr.onload/.onerror are usually sufficient for most uses and will only be called once, contrary to onreadystatechange. When using XHR in websites people tend to use onreadystatechange (for compatiblity reasons). Often it is enough to just load the resource or handle errors. load/error event listener are far less often called than onreadystatechange, i.e. only once, and you don't need to check readyState or figure out if it is an error or not. Only use onreadystatechange if you want to process the response while it is still arriving.

+

Removing Event Listeners

+

Remove event listener if they are not needed any more. It is better to actually remove event listener instead of just having some flag to check if the listener is active which is checked every time when an event is propagated. Abandon schemes like: function onMouseOver(evt) { if (is_active) { /* doSomeThing */ } } Also, remove "fire-once" listeners again:

+
 function init() {
+   var largeArray;
+   addEventListener('load', function onLoad() {
+        removeEventListener('load', onLoad, true);
+        largeArray.forEach();
+ }, true);
+
+

Else a lot of closure stuff might be still referenced (largeArray in this example). And the listener will sit idle in some internal table.

+

Populate menus as needed

+

Populate "context" menus (page, tabs, tools) as needed and keep computation to a minimum (UI responsiveness). There is no need to populate the context menu every time something changes. It is enough to populate it once the user actually needs it. Add a listener to the "popupshowing" event and compute there.

+

Avoid mouse movement events

+

Avoid mouse movement events (enter/over/exit) or at least keep computation to a minimum. Mouse movement events, especially the mouseover event, usually happen at high frequency. Best would be to only store the new information and compute "stuff" once the user actually requests it (e.g. in a popupshowing event). Also don't forget to remove the event listeners when no longer needed (see above).

+

Avoid polling

+

Use {{ interface("nsIObserverService") }} functionality instead. Everybody is free to post "custom" notifications via {{ interface("nsIObserverService") }}, but few extensions actually use this. However, a lot of other services also provide observer functionality, such as nsIPrefBranch2.

+

aPNG/aGIF inappropriate in a lot of cases

+

Animations require a lot of time to set up, as a lot of images are decoded (the frames). Animated images may have their cached representations evicted quite often, causing the frames of your animated images to be reloaded lots of times, not just once. {{ interface("nsITree") }} / {{ XULElem("tree") }} seems to be extra special in this regard, as it doesn't seem to cache animations at all under certain circumstances.

+

base64/md5/sha1 implementations

+

Do not ship your own base64/md5/sha1 implementations. Regarding base64 there are the built-in atob/btoa functions that do the job just well and are available in overlay script as well as in in JavaScript modules and components. Hashes can be computed using {{ interface("nsICryptoHash") }}, which accepts either a string or an {{ interface("nsIInputStream") }}.

+

Image sprites

+

You may combine multiple images into one (sprites). See {{ cssxref("-moz-image-region") }}. Most XUL widgets that are used to display some image (incl. {{ XULElem("button") }} and {{ XULElem("toolbarbutton") }}) allow to use {{ cssxref("list-style-image") }}. Avoid the imagesrc/src attributes to define images where possible.

+

Consider using Chrome Workers

+

You can use a {{ domxref("ChromeWorker") }} to execute long running tasks or do data processing.

+

参考

+ -- cgit v1.2.3-54-g00ecf