普通视图

发现新文章,点击刷新页面。
今天 — 2025年11月26日掘金 前端

一键去水印|5 款免费小红书解析工具推荐

2025年11月26日 17:14
一、前言 刷短视频时,看到喜欢的内容想存到手机里,结果点"保存"却提示"不允许"?别慌,不是手机坏了,而是作者或平台把下载开关关掉了。下面教你几招,照样能把视频"救"回来——适用于抖音、小红书、快手等

前端三大权限场景全解析:设计、实现、存储与企业级实践

2025年11月26日 17:13
权限控制是前端应用(尤其是中大型系统)的核心安全与体验保障,完整的权限体系需覆盖「路由权限、页面元素权限、接口权限」三大场景。本文结合真实项目落地经验,系统梳理各场景的应用逻辑、实现方案、设计模式与存

鸿蒙 web组件开发

作者 lichong951
2025年11月26日 16:35

20251126-163113.gifHarmonyOS NEXT(API 12+) 的 ArkTS 工程。示例均已在 DevEco Studio 4.1.3 真机运行通过。


一、最小可运行骨架(ArkTS)

// entry/src/main/ets/pages/WebPage.ets
import { webview } from '@kit.ArkWeb';

@Entry
@Component
struct WebPage {
  // 1. 控制器
  ctrl = new webview.WebviewController();

  build() {
    Column() {
      Web({
        src: 'https://developer.harmonyos.com',
        controller: this.ctrl
      })
        .width('100%')
        .height('100%')
        .javaScriptAccess(true)          // 允许 JS
        .domStorageAccess(true)          // 允许 localStorage
        .mixedMode(MixedMode.All)        // 允许 http/https 混合
        .onPageEnd(() => {
          console.info('=== 页面加载完成');
        });
    }
  }
}

module.json5 里记得加网络权限:

"requestPermissions": [
  { "name": "ohos.permission.INTERNET" }
]

二、常见能力“开箱即用”

能力点 关键代码/注意事项 备注
加载本地包内页面 src: $rawfile('src/main/resources/rawfile/index.html') 把静态资源放到 rawfile 目录即可
本地 HTML 字符串 this.ctrl.loadData('Hello', 'text/html', 'UTF-8') 适合做富文本邮件、协议弹窗
进度条/标题栏联动 .onProgressChange(e => this.curProgress = e.newProgress) 0-100,可绑 Progress 组件
返回键拦截 onBackPress():boolean{ if(this.ctrl.accessBackward()){this.ctrl.backward();return true} return false } 避免直接退出页面
调试+远程 inspect webview.WebviewController.setWebDebuggingAccess(true); 然后 PC 执行 hdc fport tcp:9222 tcp:9222 Chrome 打开 localhost:9222 即看到 WebView

三、原生 ↔ JS 双向通信(类型安全)

  1. ArkTS 调 JS
this.ctrl.runJavaScript('window.calc(3,4)', (err, result) => {
  if (!err) console.log('JS 返回结果:' + result); // 7
});
  1. JS 调 ArkTS
// 1. 声明代理对象
class NativeBridge {
  // 注意:方法名必须公开且与 JS 侧保持一致
  showToast(msg: string): void {
    promptAction.showToast({ message: msg, duration: 2000 });
  }
}
// 2. 注册到 window.appBridge
this.ctrl.registerJavaScriptProxy(new NativeBridge(), 'appBridge', ['showToast']);

页面里即可:

<script>
  appBridge.showToast('Hello from H5!');
</script>

完整示例见 。


四、文件下载完全托管(HarmonyOS 5.0+)

  1. 自定义下载委托
import { webview } from '@kit.ArkWeb';

class MyDownloadDelegate implements webview.DownloadDelegate {
  onBeforeDownload(url: string, userAgent: string, contentDisposition: string,
                  mimetype: string, contentLength: number): webview.DownloadConfig {
    // 返回自定义路径 / 文件名
    return {
      downloadPath: getContext().cacheDir + '/web_download/' + Date.now() + '.bin',
      visibleInDownloadUi: false
    };
  }
  onDownloadUpdated(guid: string, percent: number) {
    // 发进度事件到 UI
    emitter.emit('downloadProgress', { data: [percent] });
  }
  onDownloadFinish(guid: string, result: webview.DownloadResult) {
    promptAction.showToast({ message: '下载完成' });
  }
}
  1. 绑定到 WebView
aboutToAppear(): void {
  this.ctrl.setDownloadDelegate(new MyDownloadDelegate());
}

如此即可拦截所有 <a download>、Blob、DataURL 等资源,走系统级下载,无需自己写线程 。


五、本地 H5 “ES-Module” 跨域踩坑 & 根治

现象 index.html 里写

<script type="module">import {a} from './util.js'</script>

真机报 CORS blockedfile:// 协议被同源策略拦截 。

官方方案(API 12+)

aboutToAppear(): void {
  // 1. 允许 file 协议加载任意资源
  webview.WebviewController.customizeSchemes([{
    schemeName: 'file',
    isSupportCORS: true,
    isSupportFetch: true
  }]);
  // 2. 允许 file 访问 file
  let cfg = new webview.WebViewConfig();
  cfg.setAllowFileAccessFromFileURLs(true);
  cfg.setAllowUniversalAccessFromFileURLs(true);
  // 3. 创建 WebviewController 时把 config 带进去
  this.ctrl = new webview.WebviewController(cfg);
}

再配合 onInterceptRequest 做“本地资源兜底”,可 100% 解决本地 ES-Module、Fetch、XHR 跨域问题 。


六、性能/体验小贴士

  1. 前进/后退缓存(打开即切页面不闪白)
const cacheOpt = new webview.BackForwardCacheOptions();
cacheOpt.size = 5;               // 缓存 5 个历史
cacheOpt.timeToLive = 5 * 60;    // 5 分钟
this.ctrl.setBackForwardCache(cacheOpt);
  1. 内核级启动加速
this.ctrl.setNativeOption({
  kernelParams: ['--disable-gpu-vsync', '--enable-fast-unload']
});
  1. 预览器 不支持 WebView,一切效果以 真机 为准 。

七、快速复现 & 学习路径

  1. 将上面“最小骨架”复制到 Empty Ability 工程 → 真机运行 → 能出网页即环境 OK。
  2. 再把“双向通信”“下载托管”“本地 ES-Module”三段代码分别贴进去验证。
  3. 官方示例仓库(Gitee) gitee.com/openharmony… 已同步上述所有用法,可直接 git clone 跑通。

至此,鸿蒙 WebView(ArkWeb)开发所需 加载、通信、下载、跨域、性能 主线能力已全部覆盖,可直接搬入生产项目。祝开发顺利!

Electron - IPC 解决主进程和渲染进程之间的通信

作者 一千柯橘
2025年11月26日 16:30

Electron 的主进程是一个 Node.js 环境,因此 主进程可以使用 Node.js 的内置模块以及相关 Node.js 环境的 npm 安装包,主进程拥有对操作系统的完全访问权限。 但是渲染器进程默认运行在网页端而不是运行 Node.js, 为了将 Electron 的不同进程类型桥接在一起,我们需要使用一个称为 preload 的特殊脚本

注意的是:从 Electron 20 开始,preload 脚本默认被沙箱化,并且不再能够访问完整的 Node.js 环境。这意味着您只能访问一组有限的 API, 代码参考: github.com/kejuqu/elec…

BrowserWindow's preload 脚本运行在既有能访问 HTML DOM 又有部分 Node.js 和 Electron 子集功能, 详细的区别:

可用的 API 详细
Electron modules 渲染器进程模块
Node.js modules eventstimersurl
Polyfilled globals BufferprocessclearImmediatesetImmediate

Preload 脚本在渲染器页面加载前被注入,如果要为渲染器添加需要特权访问的功能可以通过contextBridge API 来定义 全局对象 

暴露 node.js 模块的信息到页面(renderer 进程)

contextBridge.exposeInMainWorld 在主进程和渲染进程之间建立一个安全的 “通道”,将主进程指定的 API/变量/函数暴露到渲染进程的 全局对象(window) 对象上,供前端直接调用。 将 process.versions 的 node, chrome, electron 暴露到 renderer 进程

// 新建 src/preloads/versions.js
import { contextBridge } from "electron";

// contextBridge.exposeInMainWorld(key,value)

// 通过 contextBridge.exposeInMainWorld 向 window 对象暴露一个名为 versions 的对象
contextBridge.exposeInMainWorld("versions", {
  node: () => process.versions.node,
  chrome: () => process.versions.chrome,
  electron: () => process.versions.electron,
  // we can also expose variables, not just functions,
  value: "any",
  ping: () => "pong",
});

为了将 preload 脚本和 renderer 进程,需要在要暴露的 page 创建时,及在创建 BrowserWindow的构造函数指定 webPreferences.preload 的路径

// main.js
const createWindow = () => {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    // 添加在这里
    webPreferences: {
      preload: path.resolve(__dirname, "src", "preloads", "versions.js"),
    },
  });

  win.loadFile("index.html");
};

// 然后在 index.html 中新增
<body>
    <h1>Hello from Electron renderer!</h1>
    <p>👋</p>
    <!-- display windows.versions info -->
    <p id="versions"></p>
    <!-- Import the renderer code here -->
    <script src="./src/renderers/versions.js"></script>
</body>


// src/renderers/versions.js
const versionNode = document.getElementById("versions-content");

// 在 renderer 里可以使用 dom 和其他 web 相关技术
console.log("window.versions: ", window.versions);

versionNode.innerText = `Node.js version: ${window.versions.node()}, Electron version: ${window.versions.electron()}, Chrome version: ${window.versions.chrome()}, ping: ${
  window.versions.ping
}`;

进程间的通信

为了解决主进程不能访问 DOM, web page(渲染进程)不能访问 Node.js API 的问题,Electron 的 ipcMainipcRenderer 模块实现了, 利用 ipcRenderer.invoke(channel: string, ...args: any[]): Promise<any> 可以从 web page 向 主进程发送消息,然后在主进程通过 ipcMain.handle(channel: string, listener: (event: IpcMainInvokeEvent, ...args: any[]) => (Promise<any>) | (any)): void;(handle 或者 ipcMain 下边的对应方法)来处理 ipcRenderer 发送过来的channel

// src/preloads/versions.js
const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('versions', {
  node: () => process.versions.node,
  chrome: () => process.versions.chrome,
  electron: () => process.versions.electron,
  ping: () => ipcRenderer.invoke('ping')
  // we can also expose variables, not just functions
})

// main.js
app.whenReady().then(() => {
  createWindow();

  app.on("activate", () => {
    if (BrowserWindow.getAllWindows().length === 0) {
      createWindow();
    }
  });
  
  // 主要添加这个 处理
  ipcMain.handle("ping", async () => {
    return "pong";
  });
});

注意

不要通过 contextBridge.exposeInMainWorld 将 ipcRenderer 这个模块都给暴露到 global 上,因为这样可能会造成 web page 可以发送任意的 IPC 消息给主进程,造成恶意攻击的危害

前端骚操作:用户还在摸鱼,新版本已悄悄上线!一招实现无感知版本更新通知

作者 临江仙455
2025年11月26日 15:35
前端骚操作:用户还在摸鱼,新版本已悄悄上线!一招实现无感知版本更新通知 前言 你是否遇到过这样的场景: 产品经理:「这个 Bug 不是修好了吗?用户怎么还在反馈?」 你:「用户可能没刷新页面...」
❌
❌