Web 组件性能问题

问题描述 投票:0回答:1

在过去的三周里,我将我的 Web 应用程序从经典的 HTML+CSS+JS 切换到 Web 组件。一开始都是玫瑰。很好用:1. 封装,2. 重用,3. 编写的 JavaScript 代码更少,4. 重构一个,重构全部,5. UI 干净漂亮,6. 易于定位和分组。等等

当我对它们感到兴奋时,我开始将所有内容更改为 Web 组件,并将一个组件嵌套在另一个组件中...然后突然变成了一个真正的地狱...为了使组件彼此交互,还有很多工作要做其他,在 ShadowRoot 中使用我的 API,等等......最后我设法让它们一起工作。

这是我的弹出窗口之一的示例:

  <s-popup id="userPanelPOP" placeholder="Painel do Usuário">
    <div class="flexCenter" loading="lazy">
      <div class="popupPageExt pdg10">
        <!-- User Panel Header -->
        <div class="w100 flex" style="height: 65px;">
          <!-- ProfilePicture Container -->
          <div class="h100 w100 flexCenter" id="profilePictureUserPanelBOX">
            <div class="rel">
              <svg-hexed-image class="profilePic"></svg-hexed-image>
              <div class="abs changePhotoBox">
                <div class="rel profilePhotoIconBanner">
                  <i class="bi bi-camera-fill abs profilePhotoIcon"></i>
                  <input class="inputFile" type="file" onchange="uploadProfilePicture(this)">
                </div>
              </div>
            </div>
          </div>
          <!-- Status Container -->
          <div class="h100 w100 flexCenter" id="statusUserPanelBOX">
            <div class="balloon" id="userPanelAuthBox">
              <i class="bi bi-exclamation-octagon-fill userPanelMenssager"></i>
            </div>
          </div>
          <!-- Logout Container -->
          <div class="h100 w100 rel">
            <s-button class="abs posTR" id="logoutUserPanelBTN" model="danger" height="30px">
              <div class="centerItemsH">
                <i class="bi bi-box-arrow-left"></i>
                <div style="min-width: 4px;"></div>
                Sair
                <div style="min-width: 1px;"></div>
              </div>
            </s-button>
          </div>
        </div>
        <!-- Welcome Menssage -->
        <s-text class="mgnTB10" id="welcomeUserPanelTXT"></s-text>
        <!-- Selling / Buying -->
        <div class="w100 flex mediaW380InlineBOX">
          <!-- My Store -->
          <div class="flexCenter flexRight w100">
            <s-button class="w100" id="myStoreUserPanelBTN" model="disable">
              <div class="centerItemsH">
                <i class="bi bi-shop fs24"></i>
                <div class="w10"></div>
                Minha Loja
              </div>
            </s-button>
          </div>
          <!-- Divisor -->
          <div class="mediaW380InlineDIV" style="min-width:10px;"></div>
          <!-- My Purchases Historic -->
          <div class="flexCenter flexLeft w100">
            <s-button class="w100" id="myPurchasesUserPanelBTN">
              <div class="centerItemsH">
                <i class="bi bi-clock-history fs24"></i>
                <div class="w10"></div>
                Minhas Compras
              </div>
            </s-button>
          </div>
        </div>
        <!-- User Informations -->
        <s-topic class="mgnT10" id="userInformationsTPC" icon="bi-arrow-down-right-square" placeholder="Informações do Usuário" display="none" style="min-width:212px">
          <div class="popupPage">
            <s-input
              class="mgnT5"
              id="nameUserPanelINP"
              placeholder="Nome Completo:"
              maxlength="80"
              charCounter="true"
              model="edit"
              regexAllow="FULL_NAME"
            >Nome Completo</s-input>
            <s-input
              id="emailUserPanelINP"
              placeholder="Email:"
              inputmode="email"
              maxlength="80"
              charCounter="true"
              model="edit"
              regexAllow="EMAIL"
            >Email</s-input>
            <div class="flex">
              <div class="w100">
                <s-input
                  id="birthUserPanelINP"
                  placeholder="Data de Nascimento:"
                  inputmode="numeric"
                  maxlength="10"
                  model="edit"
                  regexAllow="BIRTH"
                >Data de Nascimento</s-input>
              </div>
              <div class="mw10"></div>
              <div class="w100">
                <s-input
                  id="telefonUserPanelINP"
                  placeholder="Telefone:"
                  inputmode="numeric"
                  maxlength="14"
                  model="edit"
                  regexAllow="TELEFON"
                >Telefone</s-input>
              </div>
            </div>
            <!-- Gender Row -->
            <div class="flex">
              <div class="w100">
                <s-input
                  id="cpfUserPanelINP"
                  placeholder="CPF:"
                  model="edit"
                  maxlength="14"
                  inputmode="numeric"
                  regexAllow="CPF"
                >CPF</s-input>
              </div>
              <div class="mw10"></div>
              <div class="w100">
                <s-input
                  id="passwordUserPanelINP"
                  placeholder="Nova Senha:"
                  model="edit"
                  maxlength="60"
                >Senha</s-input>
              </div>
            </div>
            <s-input
              class="disNone"
              id="firstPasswordUserPanelINP"
              placeholder="Primeira Senha:"
              maxlength="60"
              type="password"
            >Primeira Senha</s-input>
            <s-input
              class="disNone"
              id="newPassword1UserPanelINP"
              placeholder="Nova Senha:"
              maxlength="60"
              type="password"
            >Nova Senha</s-input>
            <s-input
              class="disNone"
              id="newPassword2UserPanelINP"
              placeholder="Nova Senha:"
              maxlength="60"
              type="password"
            >Nova Senha</s-input>
            <div class="flex">
              <div class="w100 flexCenter">
                <s-checkbox class="mgn10" id="womanUserPanelCB" onclick="tagGenderUserPanel(this)" group="true">Mulher</s-checkbox>
              </div>
              <div class="w100 flexCenter">
                <s-checkbox class="mgn10" id="manUserPanelCB" onclick="tagGenderUserPanel(this)" group="true">Homem</s-checkbox>
              </div>
              <div class="w100 flexCenter">
                <s-checkbox class="mgn10" id="otherUserPanelCB" onclick="tagGenderUserPanel(this)" group="true">Outro</s-checkbox>
              </div>
            </div>
            <!-- User Addresses -->
            <s-topic id="userAddressesTPC" icon="bi-arrow-down-right-square" placeholder="Endereços" display="none">
              <div class="popupPage">
                <s-box placeholder="Novo Endereço" id="newAddressBOX">
                  <s-topic class="mgnT5" id="closeNewAddressUserPanelTPC" icon="bi-dash-square-dotted" placeholder="Excluir Novo Endereço">
                  <s-input
                    id="nameNewAddressUserPanelINP"
                    placeholder="Nome do Endereço (Ex.:Casa):"
                    maxlength="80"
                    charCounter="true"
                    regexAllow="FULL_NAME"
                  >Email</s-input>
                  <div class="flex">
                    <div class="w100">
                      <s-input
                        id="cepNewAddressUserPanelINP"
                        placeholder="CEP:"
                        inputmode="numeric"
                        maxlength="10"
                        regexAllow="CPF"
                      >CEP</s-input>
                    </div>
                    <div class="mw10"></div>
                    <div class="w100">
                      <s-input
                        id="stNumberNewAddressUserPanelINP"
                        placeholder="Nº do Endereço:"
                        inputmode="numeric"
                        maxlength="7"
                        regexAllow="NUMBER"
                      >Nº do Endereço</s-input>
                    </div>
                  </div>
                  <s-input
                    id="streetNewAddressUserPanelINP"
                    placeholder="Rua:"
                    maxlength="80"
                    charCounter="true"
                    regexAllow="FULL_NAME"
                  >Rua</s-input>
                  <s-input
                    id="districtNewAddressUserPanelINP"
                    placeholder="Bairro:"
                    maxlength="80"
                    charCounter="true"
                    regexAllow="FULL_NAME"
                  >Bairro</s-input>
                  <div class="flex w100">
                    <div class="w100">
                      <s-input
                        id="cityNewAddressUserPanelINP"
                        placeholder="Cidade:"
                        maxlength="40"
                        regexAllow="FULL_NAME"
                      >CEP</s-input>
                    </div>
                    <div class="mw10"></div>
                    <div style="max-width:60px;min-width:60px;">
                      <s-input
                        id="stateNewAddressUserPanelINP"
                        maxlength="2"
                        regexAllow="FULL_NAME"
                      >Estado</s-input>
                    </div>
                  </div>
                  <s-input
                    id="complementNewAddressUserPanelINP"
                    placeholder="Complemento (opcional):"
                    maxlength="80"
                    charCounter="true"
                    regexAllow="FULL_NAME"
                  >Complemento</s-input>
                  <s-button class="w100 mgnT10" id="submitNewAddressUserPanelBTN" model="disable">Cadastrar Novo Endereço</s-button>
                </s-box>
                <s-topic class="mgnT10" id="addNewAddressUserPanelTPC" icon="bi-plus-square-dotted" placeholder="Adicionar Endereço"></s-topic>
              </div>
            </s-topic>   
          </div>
        </s-topic>
        <s-input
          class="disNone"
          id="actualPasswordUserPanelINP"
          placeholder="Senha Atual:"
          maxlength="60"
          type="password"
        >Senha Atual</s-input>
        <s-button class="disNone mgnTB10" id="updateUserInfoUserPanelBTN">Atualizar Informações</s-button>
      </div>
    </div>
  </s-popup>

最后,需要相当长的时间才能完全迁移,并且当我切换到 Web 组件时,我注意到页面加载变得非常慢。 Google Lighthouse 性能得分从 90 下降到 40...

即使我尝试了延迟加载、延迟渲染,我也将 JS 分割成包并按要求交付它们,但我不认为这是使用渐进式 Web 应用程序的好方法...

现在我不知道这是否值得...我觉得我浪费了太多时间“原地打转”,结果页面速度慢了很多...

我应该继续使用 Web 组件还是继续使用旧的快速方法来开发 UI?

web-component
1个回答
2
投票

我找到了解决方案!通过做更多的研究,我现在明白,通过像这样使用innerHTML来嵌套Web组件并不是一个好主意:

<s-popup>

class Popup extends HTMLElement {
  constructor(){
    super();
    // Init ShadowRoot
    const shadowRoot = this.attachShadow({ mode: 'open' });
    // Add class to all popups
    this.classList.add('popup');
    this.classList.add('disNone');
    // Get tag attributes
    const id = this.getAttribute('id')??'';
    const placeholder = this.getAttribute('placeholder')??'';
    const content = this.innerHTML??'';
    // Design HTML component
    shadowRoot.innerHTML = `
      <style>
        @import url('./assets/style.min.css');
        @import url('./assets/~bootstrap-icons/font/bootstrap-icons.css');
        :host{ display: flex; }
      </style>
      <div class="popupWrap1">
        <div class="popupWrap2">
          <div class="popupWrap3 popupCloser">
            <div class="popupWrap4 popupCloser">
              <div class="popupDialog popupFilm" onclick="event.stopPropagation();">
                <div class="popupTitle flexCenter">
                  ${placeholder}
                  <i class="bi bi-square-fill bgSquareClose"></i>
                  <i class="bi bi-x-square-fill popupCloseIcon" onclick="closePopup(${id});"></i>
                  <div class="popupReturn flexCenter"></div>
                  <div class="titleBottomBorder"></div>
                </div>
                ${content}
              </div>
            </div>
          </div>
        </div>
      </div>`;
      // Popup closer
      this.shadowRoot.querySelector('.popupCloser').onclick = e =>{ closePopup(this); }
  }
  hideNavBars(){
    this.shadowRoot.querySelector('.popupWrap1').style.marginTop = '0px';
    this.shadowRoot.querySelector('.popupWrap1').style.marginBottom = '0px';
  }
  showNavBars(){
    this.shadowRoot.querySelector('.popupWrap1').style.marginTop = nbHeaderHeight + 'px';
    this.shadowRoot.querySelector('.popupWrap1').style.marginBottom = nbFooterHeight + 'px';
  }
}
customElements.define('s-popup', Popup);

但只需使用

<slot>
标签而不是innerHTML 就可以了,现在页面加载速度非常快!

<s-popup>

class Popup extends HTMLElement {
  constructor(){
    super();
    // Init ShadowRoot
    const shadowRoot = this.attachShadow({ mode: 'open' });
    // Add class to all popups
    this.classList.add('popup');
    this.classList.add('disNone');
    // Get tag attributes
    const id = this.getAttribute('id')??'';
    const placeholder = this.getAttribute('placeholder')??'';
    // Design HTML component
    shadowRoot.innerHTML = `
      <style>
        @import url('./assets/style.min.css');
        @import url('./assets/~bootstrap-icons/font/bootstrap-icons.css');
        :host{ display: flex; }
      </style>
      <div class="popupWrap1">
        <div class="popupWrap2">
          <div class="popupWrap3 popupCloser">
            <div class="popupWrap4 popupCloser">
              <div class="popupDialog popupFilm" onclick="event.stopPropagation();">
                <div class="popupTitle flexCenter">
                  ${placeholder}
                  <i class="bi bi-square-fill bgSquareClose"></i>
                  <i class="bi bi-x-square-fill popupCloseIcon" onclick="closePopup(${id});"></i>
                  <div class="popupReturn flexCenter"></div>
                  <div class="titleBottomBorder"></div>
                </div>
                <slot></slot>
              </div>
            </div>
          </div>
        </div>
      </div>`;
      // Popup closer
      this.shadowRoot.querySelector('.popupCloser').onclick = e =>{ closePopup(this); }
  }
  hideNavBars(){
    this.shadowRoot.querySelector('.popupWrap1').style.marginTop = '0px';
    this.shadowRoot.querySelector('.popupWrap1').style.marginBottom = '0px';
  }
  showNavBars(){
    this.shadowRoot.querySelector('.popupWrap1').style.marginTop = nbHeaderHeight + 'px';
    this.shadowRoot.querySelector('.popupWrap1').style.marginBottom = nbFooterHeight + 'px';
  }
}
customElements.define('s-popup', Popup);

就这么简单!

原因是当使用innerHTML嵌套Web组件时,每次我们渲染每个内部嵌套组件时,整个DOM都需要更新,因为innerHTML直接操作DOM。但通过使用

<slot>
来代替,浏览器会创建一个 Virtual DOM,然后使用某种快速内存分配来渲染其中的页面,最后,它只对 DOM 进行简单的单次更新。现在它就像魅力一样发挥作用!

© www.soinside.com 2019 - 2024. All rights reserved.