Movatterモバイル変換


[0]ホーム

URL:


Framework
Life Cycle

Life Cycle of Plasmo CSUI

Plasmo's CSUI orchestrates a lifecycle dedicated to mounting and unmounting your React, Vue, or Svelte components in a content script. Although each UI library/framework has a slightly different mounting API, the top-level lifecycle is largely the same:

  1. Get anAnchor
  2. Create or locate aRoot Container
  3. Render the component onto theRoot Container

Terminologies

TermDescription
AnchorTell CSUI how and where to mount your component
Anchor-getterTell CSUI how to find your anchor(s)
OverlayMount your component on a top-level (max z-index) overlay element
InlineMount your component into the webpage's DOM, next to a target element
Root ContainerA ShadowDOM element created by CSUI to isolate your component
RendererThe top-level life-cycle runner (it does everything)

Anchor

CSUI Life Cycle

A Plasmo CSUI anchor is defined by the following type:

exporttypePlasmoCSUIAnchor= {  type:"overlay"|"inline"  element:Element}

By default, the CSUI lifecycle creates an overlay anchor using thedocument.body element:

{  type:"overlay",  element:document.body}

If any anchor-getter function is defined and exported, the CSUI lifecycle will use the returned element and the relevant anchor type instead. Since the anchor-getter functions can be async, you also have the power to controlwhen Plasmo mounts your component. For example, you can wait for a specific element to appear on the page before mounting your component.

The anchor is passed down to the CSUI via the anchor props. You can access it as follow:

importtype { PlasmoCSUIProps }from"plasmo"constAnchorTypePrinter:FC<PlasmoCSUIProps>= ({ anchor })=> {return <span>{anchor.type}</span>}exportdefault AnchorTypePrinter

Overlay

Overlay anchors spawnCSUI Overlay Containers which are batch-mounted onto a singleRoot Container element per CSUI. TheOverlay Containers are absolutely positioned relative to each anchor's element with maxed out z-index. Then, your exportedCSUI Component is mounted onto eachOverlay Container:

Overlay Anchor Mounting

To specify a single overlay anchor, export agetOverlayAnchor function:

importtype { PlasmoGetOverlayAnchor }from"plasmo"exportconstgetOverlayAnchor:PlasmoGetOverlayAnchor=async ()=>document.querySelector("#pricing")

To specify a list of overlay anchors, export agetOverlayAnchorList function:

importtype { PlasmoGetOverlayAnchorList }from"plasmo"exportconstgetOverlayAnchorList:PlasmoGetOverlayAnchorList=async ()=>document.querySelectorAll("a")
⚠️

getOverlayAnchorList does not cover dynamic case at the moment. For example,if new anchors are added to the web page after the initial rendering, the CSUIlifecycle will not be able to detect it. PR is welcome to improve thisfeature!

Update Position

The defaultOverlay Container listens to the window scroll event to align itself with the anchor element. You can customize how theOverlay Container refreshes its absolute positioning by exporting awatchOverlayAnchor function. The example below refreshes the position every 8472ms:

importtype { PlasmoWatchOverlayAnchor }from"plasmo"exportconstwatchOverlayAnchor:PlasmoWatchOverlayAnchor= (  updatePosition)=> {constinterval=setInterval(()=> {updatePosition()  },8472)// Clear the interval when unmountedreturn ()=> {clearInterval(interval)  }}

Checkwith-content-scripts-ui/contents/plasmo-overlay-watch.tsx (opens in a new tab) for an example.

Inline

Inline anchor embeds yourCSUI Component directly into the web page. Each anchor spawns aRoot Container appended next to its target element. Within eachRoot Container, anInline Container is created which is then used to mount the exportedCSUI Component:

CSUI Inline Anchor

To specify a single inline anchor, export agetInlineAnchor function:

importtype { PlasmoGetInlineAnchor }from"plasmo"exportconstgetInlineAnchor:PlasmoGetInlineAnchor=async ()=>document.querySelector("#pricing")

To specify single inline anchor with insert position:

importtype { PlasmoGetInlineAnchor }from"plasmo"exportconstgetInlineAnchor:PlasmoGetInlineAnchor=async ()=> ({      element:document.querySelector("#pricing"),      insertPosition:"afterend"})

To specify a list of inline anchors, export agetInlineAnchorList function:

importtype { PlasmoGetInlineAnchorList }from"plasmo"exportconstgetInlineAnchorList:PlasmoGetInlineAnchorList=async ()=>document.querySelectorAll("a")

To specify a list of inline anchors with insert position:

importtype { PlasmoGetInlineAnchorList }from"plasmo"exportconstgetInlineAnchorList:PlasmoGetInlineAnchorList=async ()=> {constanchors=document.querySelectorAll("a")returnArray.from(anchors).map((element)=> ({    element,    insertPosition:"afterend"  }))}

Checkwith-content-scripts-ui/contents/plasmo-inline.tsx (opens in a new tab) for an example.

Root Container

Root container creation

TheRoot Container is where yourCSUI Component is mounted. The built-inRoot Container is a ShadowDOM element with theplasmo-csui custom tag. This allows you to style theRoot Container and their exported components without being impacted by the web page's styles.

Custom DOM Mounting

TheRoot Container creates ashadowHost which gets injected into the web page's DOM tree. By default, Plasmo injects theshadowHost after the element for an inline anchor, and before thedocument.body for an overlay anchor. To customize this behavior, export amountShadowHost function:

importtype { PlasmoMountShadowHost }from"plasmo"exportconstmountShadowHost:PlasmoMountShadowHost= ({  shadowHost,  anchor,  mountState})=> {anchor.element.appendChild(shadowHost)mountState.observer.disconnect()// OPTIONAL DEMO: stop the observer as needed}

Closed Shadow Root

By default, the shadow root is "open," allowing anyone (developer and extension user) to inspect the hierarchy of the ShadowDOM. To override this behavior, export acreateShadowRoot function:

importtype { PlasmoCreateShadowRoot }from"plasmo"exportconstcreateShadowRoot:PlasmoCreateShadowRoot= (shadowHost)=>shadowHost.attachShadow({ mode:"closed" })

Custom Styles

The built-in ShadowDOM provides a convenient mechanism for extension developers to safely style their components by exporting agetStyle function that returns anHTML style element (opens in a new tab).

For further guidance on styling CSUI, please readStyling Plasmo CSUI.

Custom Root Container

Sometimes, you'll want to completely replace Plasmo's Shadow DOM container implementation to fit your needs. For example, you might want to piggyback on an element within the web page itself instead of creating a new DOM element. To do so, export agetRootContainer function:

importtype { PlasmoGetRootContainer }from"plasmo"exportconstgetRootContainer= ()=>document.getElementById("itero")

Some reasons you'd want to do this:

⚠️

If you export agetRootContainer function, any function that extends thebuilt-in ShadowDOM such asgetStyle orgetShadowHostId will be ignored.Call those functions within your customgetRootContainer logic as needed!

Checkwith-content-scripts-ui (opens in a new tab) for an example.

Renderer

Renderer mounting Component

TheRenderer is in charge of observing the website's DOM to detect the presence of eachRoot Container and tracking the linking between each anchor's element and itsRoot Container. Once a stableRoot Container is determined, theRenderer mounts the exportedCSUI Component into theRoot Container using either anInline Container or anOverlay Container, depending on the type of theAnchor.

Detecting and Optimizing Root Container Removal

When a webpage changes its DOM structure, theRoot Container might be removed. For example, given an email client filled with inbox items, and a CSUI injected inline next to each item. When an item is deleted, the root container will be removed as well.

To detectRoot Container removal, theCSUI Renderer compares each mounted container's root against thewindow.document object. This check can be optimized to O(1) by exporting agetShadowHostId function:

importtype { PlasmoGetShadowHostId }from"plasmo"exportconstgetShadowHostId:PlasmoGetShadowHostId= ()=>`adonais`

The function also allows developers to customize the id for each anchor found:

importtype { PlasmoGetShadowHostId }from"plasmo"exportconstgetShadowHostId:PlasmoGetShadowHostId= ({ element })=>element.getAttribute("data-custom-id")+`-pollax-iv`

Custom Renderer

Developers may export arender function to override the default renderer. You might need this ability to:

  • Provide a customInline container orOverlay container
  • Customize the mounting logic
  • Provide a customMutationObserver

For example, to use an existing element as a custom container:

importtype { PlasmoRender }from"plasmo"import { CustomContainer }from"~components/custom-container"constEngageOverlay= ()=> <span>ENGAGE</span>// This function overrides the default `createRootContainer`exportconstgetRootContainer= ()=>newPromise((resolve)=> {constcheckInterval=setInterval(()=> {constrootContainer=document.getElementById("itero")if (rootContainer) {clearInterval(checkInterval)resolve(rootContainer)      }    },137)  })exportconstrender:PlasmoRender=async ({  anchor,// the observed anchor, OR document.body.  createRootContainer// This creates the default root container})=> {constrootContainer=awaitcreateRootContainer()constroot=createRoot(rootContainer)// Any rootroot.render(    <CustomContainer>      <EngageOverlay />    </CustomContainer>  )}

How to dynamically create a custom container:

importtype { PlasmoRender }from"plasmo"import { CustomContainer }from"~components/custom-container"constEngageOverlay= ()=> <span>ENGAGE</span>// This function overrides the default `createRootContainer`exportconstgetRootContainer= ({ anchor, mountState })=>newPromise((resolve)=> {constcheckInterval=setInterval(()=> {let { element, insertPosition }= anchorif (element) {constrootContainer=document.createElement("div")mountState.hostSet.add(rootContainer)mountState.hostMap.set(rootContainer, anchor)element.insertAdjacentElement(insertPosition, rootContainer)clearInterval(checkInterval)resolve(rootContainer)      }    },137)  })exportconstrender:PlasmoRender=async ({  anchor,// the observed anchor, OR document.body.  createRootContainer// This creates the default root container})=> {constrootContainer=awaitcreateRootContainer(anchor)constroot=createRoot(rootContainer)// Any rootroot.render(    <CustomContainer>      <EngageOverlay />    </CustomContainer>  )}

To utilize the built-inInline Container orOverlay Container:

importtype { PlasmoRender }from"plasmo"constAnchorOverlay= ({ anchor })=> <span>{anchor.innerText}</span>exportconstrender:PlasmoRender=async (  {    anchor,// the observed anchor, OR document.body.    createRootContainer// This creates the default root container  },  _,  OverlayCSUIContainer)=> {constrootContainer=awaitcreateRootContainer()constroot=createRoot(rootContainer)// Any rootroot.render(// You must pass down an anchor to mount the default container. Here we pass the default one    <OverlayCSUIContaineranchor={anchor}>      <AnchorOverlayanchor={anchor} />    </OverlayCSUIContainer>  )}

If you need to customize the MutationObserver, do not export an anchor-getter function. Otherwise, the built-in MutationObserver will still be spawned.

Content Scripts UIStyling

[8]ページ先頭

©2009-2025 Movatter.jp