You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Angular开发无iframe的Chrome扩展侧边栏(非官方Sidepanel)

无需iframe的Angular Chrome扩展侧边栏实现方案

核心思路

将Angular应用打包为Web自定义元素(Custom Element),直接注入到目标页面的DOM中,替代iframe方案。这种方式和MaxAI的实现逻辑一致,通过自定义标签嵌入应用,避免iframe的刷新空白问题,同时保留Angular的完整功能。


步骤1:配置Angular支持自定义元素

修改main.ts,将根组件转为自定义元素

替换原有的Angular启动逻辑,使用createCustomElement将App组件注册为Web自定义元素:

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { createCustomElement } from '@angular/elements';
import { AppModule } from './app/app.module';
import { AppComponent } from './app/app.component';

platformBrowserDynamic().bootstrapModule(AppModule)
  .then(moduleRef => {
    // 将App组件转为自定义元素
    const sidebarElement = createCustomElement(AppComponent, { 
      injector: moduleRef.injector 
    });
    // 注册自定义标签(必须包含连字符,符合Web组件规范)
    customElements.define('angular-sidebar-widget', sidebarElement);
  })
  .catch(err => console.error(err));

配置哈希路由避免刷新问题

在app-routing.module.ts中启用哈希路由,确保页面刷新时路由正常:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HashLocationStrategy, LocationStrategy } from '@angular/common';

const routes: Routes = [
  // 你的业务路由配置
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
  providers: [{ provide: LocationStrategy, useClass: HashLocationStrategy }]
})
export class AppRoutingModule { }

步骤2:调整Angular打包配置

修改angular.json的build选项,确保打包输出的资源可被Chrome扩展访问:

"build": {
  "builder": "@angular-devkit/build-angular:browser",
  "options": {
    "outputPath": "dist/angular-chrome-extension",
    "index": "src/index.html",
    "main": "src/main.ts",
    "polyfills": "src/polyfills.ts",
    "tsConfig": "tsconfig.app.json",
    "assets": ["src/favicon.ico", "src/assets"],
    "styles": ["src/styles.scss"],
    "scripts": []
  },
  "configurations": {
    "production": {
      "outputHashing": "all",
      "buildOptimizer": true,
      "optimization": true
    }
  },
  "defaultConfiguration": "production"
}

执行ng build --prod打包项目,将dist目录下的所有文件复制到Chrome扩展的根目录。


步骤3:更新Manifest.json配置

调整web_accessible_resources,允许目标页面访问Angular打包后的资源:

{
  "name": "angularchrome",
  "description": "AngularChrome Extension.",
  "version": "1.0.1",
  "manifest_version": 3,
  "permissions": ["activeTab"],
  "action": {
    "default_icon": "/assets/icons/Icon.png",
    "default_title": "Toggle Sidebar"
  },
  "icons": {
    "16": "/assets/icons/Icon.png",
    "32": "/assets/icons/Icon.png",
    "48": "/assets/icons/Icon.png",
    "128": "/assets/icons/Icon.png"
  },
  "background": {
    "service_worker": "background.js"
  },
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content.js"],
      "css": ["content.css"]
    }
  ],
  "content_security_policy": {
    "extension_pages": "script-src 'self'; object-src 'self'"
  },
  "web_accessible_resources": [
    {
      "resources": [
        "runtime.js",
        "polyfills.js",
        "main.js",
        "styles.css",
        "/assets/*"
      ],
      "matches": ["<all_urls>"]
    }
  ]
}

注意:将permissions改为activeTab更贴合需求,避免不必要的权限申请。


步骤4:编写内容脚本与后台脚本

content.js:注入自定义元素并控制显示/隐藏

// 注入Angular资源并创建侧边栏
function injectSidebar() {
  // 按顺序加载Angular核心脚本
  const scriptUrls = [
    chrome.runtime.getURL('runtime.js'),
    chrome.runtime.getURL('polyfills.js'),
    chrome.runtime.getURL('main.js')
  ];

  // 全部脚本加载完成后创建自定义元素
  scriptUrls.forEach((url, index) => {
    const script = document.createElement('script');
    script.src = url;
    script.onload = () => {
      if (index === scriptUrls.length - 1) {
        const sidebar = document.createElement('angular-sidebar-widget');
        sidebar.id = 'angular-sidebar';
        document.body.appendChild(sidebar);
      }
    };
    document.head.appendChild(script);
  });
}

// 监听后台的切换指令
chrome.runtime.onMessage.addListener((message) => {
  if (message.action === 'toggleSidebar') {
    const existingSidebar = document.getElementById('angular-sidebar');
    if (existingSidebar) {
      // 切换显示状态
      existingSidebar.style.display = existingSidebar.style.display === 'none' ? 'block' : 'none';
    } else {
      // 首次注入侧边栏
      injectSidebar();
    }
  }
});

background.js:监听扩展图标点击事件

chrome.action.onClicked.addListener((tab) => {
  // 向当前标签页的content script发送切换指令
  chrome.tabs.sendMessage(tab.id, { action: 'toggleSidebar' });
});

步骤5:添加侧边栏样式(content.css)

固定侧边栏位置,确保层级高于页面内容:

#angular-sidebar {
  position: fixed;
  top: 0;
  right: 0;
  width: 420px;
  height: 100vh;
  border-left: 1px solid #e5e7eb;
  background-color: #ffffff;
  z-index: 999999;
  box-shadow: -2px 0 12px rgba(0, 0, 0, 0.08);
  overflow-y: auto;
}

关键注意事项

  1. 自定义标签规范:必须使用包含连字符的标签名(如angular-sidebar-widget),符合Web组件标准。
  2. 样式隔离:可通过Angular的ViewEncapsulation.ShadowDom进一步隔离组件样式,避免与目标页面样式冲突。
  3. 资源加载顺序:必须按runtime.js→polyfills.js→main.js的顺序加载,否则Angular无法正常初始化。
  4. 测试流程:打包后将扩展目录加载到Chrome开发者模式下,测试不同页面的注入效果与刷新稳定性。

内容的提问来源于stack exchange,提问作者Nidhin Kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 14:58:12