如何解决使用 react 作为内容脚本时多次重新渲染
我目前正在学习如何使用 React 开发 chrome 扩展。但是,我遇到了一个问题,即使使用非常基本的组件,react 也会重新渲染 3 次。
因此,内容脚本创建了 3 次嵌套在彼此之上的 iframe —— 我注意到的一件事是查询 iframe 在每次重新渲染时总是返回 null。
这是将作为内容脚本注入 iframe 的组件。
import { render } from 'react-dom';
interface Props {}
const Sidebar: FC<Props> = () => {
return <div>some sidebar</div>;
};
// render first on the entry point
const app = document.getElementById('app');
if (app) {
render(
<StrictMode>
<Sidebar />
</StrictMode>,document.getElementById('app')
);
}
console.log('check');
const id = 'extensionWrapper';
const iframe = document.getElementById(id);
if (!iframe) {
const iframe = document.createElement('iframe');
// get the index.html from chrome extension directory
const content = chrome.extension.getURL('index.html');
console.log({ content });
iframe.src = content;
iframe.id = id;
document.body.appendChild(iframe);
}
这是webpack.config.js
const { resolve } = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');
const copyWebpackPlugin = require('copy-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const tsRule = {
test: /\.tsx?$/,exclude: /node_modules/,use: 'ts-loader',};
const plugins = [
new HTMLWebpackPlugin({
template: 'src/index.html',filename: 'index.html',chunks: ['contentScript'],inject: 'body',}),new copyWebpackPlugin({
patterns: [
{
from: 'public',// to is relative to output folder set in output.path
to: '.',},],new CleanWebpackPlugin(),];
module.exports = {
mode: 'development',devtool: 'cheap-module-source-map',entry: {
// popup: './src/popup-page/popup.tsx',contentScript: './src/content-scripts/content-script.tsx',output: {
filename: '[name].js',path: resolve(__dirname,'dist'),module: {
rules: [tsRule],plugins,};
manifest.json
{
"short_name": "react chrome","name": "react chrome extension","version": "1.0","manifest_version": 2,"content_scripts": [
{
"matches": ["<all_urls>"],"js": ["contentScript.js"]
}
],"web_accessible_resources": ["*.html"]
}
解决方法
我还没有找到关于多重渲染的答案,但万一有人在这篇文章中偶然发现 - 而不是在 manifest.json
中指定内容脚本,我决定只使用动态注入内容脚本下面的片段:
在 background.ts 中
const isWebProtocol = (url: string | undefined): boolean => {
return !!url && url.startsWith('http');
};
chrome.tabs.onUpdated.addListener((tabId,changeInfo,tab) => {
if (changeInfo.status === 'complete' && tab.active === true && isWebProtocol(tab.url)) {
chrome.tabs.executeScript(tabId,{
file: 'injectScript.js',runAt: 'document_idle',});
}
});
将在每个选项卡上注入的内容脚本
injectScript.js
const id = 'extensionWrapper';
const iframe = document.getElementById(id);
if (!iframe) {
const iframe = document.createElement('iframe');
// get the index.html from chrome extension directory
const content = chrome.extension.getURL('index.html');
iframe.src = content;
iframe.id = id;
document.body.appendChild(iframe);
}
初始化主要反应组件的主要内容脚本
contentScript.tsx
import React,{ FC,StrictMode } from 'react';
import { render } from 'react-dom';
// import Sidebar from "./content-script.module.css";
import "../style.css";
import "./content-script.css";
interface Props {}
const Sidebar: FC<Props> = () => {
return <div className="sidebar">some sidebar</div>;
};
// render first on the entry point
const app = document.getElementById('app');
if (app) {
render(
<StrictMode>
<Sidebar />
</StrictMode>,document.getElementById('app')
);
}
最后,包含主要反应组件将被注入到的元素(即 index.html
)的 #app
文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。