十五分钟带你学会 Electron

2023-10-29

什么是 Electron

Electron 是一种基于 Node.js 和 Chromium 开发的开源框架,用于构建跨平台桌面应用程序。

Electron 提供了许多跨平台的功能,例如窗口管理、菜单、对话框和托盘图标等。它还可以轻松地与 Node.js 模块进行集成,以便开发人员可以使用已有的 Node.js 生态系统中的库和工具。

这使得 Electron 非常适合开发具有丰富用户界面和本地操作系统集成的桌面应用程序。

由于 Electron 使用 Chromium 作为其呈现引擎,因此应用程序可以获得高性能和现代 Web 技术的支持。

同时,由于其跨平台特性,开发人员可以使用一致的开发工具和技术来构建适用于 Windows、macOS 和 Linux 等多个操作系统的应用程序。

简单点说,就是 Electron 提供一套方法,将 nodejs 和 Chromium 打包成一套跨平台桌面端应用。


为什么要选择 Electron

我们来看一下有哪些技术可以开发桌面应用:

编程语言 / 技术框架 可提供的能力 代表作
C#/WPF 开发 Windows 应用 Visual Studio
Swift 开发 macOS 应用 CodeEdit
C++/QT 跨平台应用 YY 语音
C++/CEF 跨平台应用 钉钉
Java/Swing 跨平台应用 JetBrains系列软件
JavaScript/Electron 跨平台应用 Visual Studio Code
Rust/Tauri 跨平台应用 xplorer
Dart/Flutter 跨平台应用 rustdesk

作为一名开发者,学习某项技术需要看投资回报率高不高,像 Windows 平台上的 C# 和 macOS 平台上的 Swift,个人觉得技术路线会比较窄,未来更多的场景是采用跨平台的技术来实现。

所以如果有 C++ 的功底,QT 和 CEF 是不错的选择,而对于前端来说,最好的选择无外乎下面三个:

  • Electron:纯 JavaScript 技术栈,生态非常成熟,前端同学快速上手,无学习成本
  • Tauri:打出来的包非常小,需要一定的 Rust 基础
  • Flutter:一套代码通吃 web、iOS、Android、macOS、Windows、Linux 六大平台

可以看到,上述三个选择的优势都非常明显,但是其他两个对于前端来说都有一定的学习成本,需要学习 rust 和 dart 语言,而 Electron 对于前端来说简直就是零成本,所以从学习成本来说 Electron 是最适合的。

从流行度上来说,相当多的桌面应用使用的就是 Electron 开发。这其中不乏大名鼎鼎、如雷贯耳的应用,例如 Postman、Notion、Skype 等,而且我敢打赌,各位的电脑上一定安装过用 Electron 开发的应用,如果你用的是 Mac 电脑,请在命令行运行下面的命令来检测本地采用 Electron 技术开发的桌面软件:

for app in /Applications/*; do;[ -d  $app/Contents/Frameworks/Electron\ Framework.framework ] && echo $app; done

我本地检测出来的应用有:

$ for app in /Applications/*; do;[ -d  $app/Contents/Frameworks/Electron\  Framework.framework ] && echo $app; done
/Applications/BaiduNetdisk_mac.app
/Applications/QQ.app
/Applications/Scratch 3.app
/Applications/Visual Studio Code.app
/Applications/Xmind.app
/Applications/aDrive.app
/Applications/lx-music-desktop.app

最后我们来看一下 Electron 比 Web 的优势在哪里:

  • 资源本地化和离线持久化:对于传统 Web 应用来说,性能卡点往往不在于解析和渲染,而在于网络请求,把资源本地化之后,可以做到应用启动后页面秒开,极大程度上减少了白屏和用户等待的时间。另外,传统 Web 的数据都是通过调用接口实时保存到服务端,无法做到离线持久化,而 Electron 可以将用户配置、缓存数据全部写入本地文件,在离线状态下依然能够正常使用软件,当恢复在线时同步到云端,例如笔记类应用。
  • 系统底层 API 调用:Web 环境本质上是浏览器提供的一个沙箱,出于安全考虑限制了很多底层能力,例如文件操作等,而在 Electron 里面,你可以调用任何操作系统提供的 API,包括 node.js 帮开发者封装好的 fs、child_process、os 等。
  • 不受限的网络通信:每一个前端开发都会遇到跨域问题,这其实是 Web 浏览器为了保证不同域下的数据安全,人为制造出来的限制,而在 Electron 环境下,你不用考虑是否跨域,所有 http 和 https 请求都畅通无阻,而且你还能对这些请求进行拦截和重定向,甚至修改任意 header 字段和响应结果。不仅如此,更底层的网络能力,例如 tcp 和 udp 也是支持的,你完全可以做一个类似于 QQ 一样的聊天软件。
  • 可定制化的窗口:传统 Web 只能给用户提供一个 tab,单调乏味,Electron 可以创建各式各样的无边框窗口,甚至可以设置成圆形或三角形的形状,例如你可能用过苹果自带的 Spotlight 或者 Alfred,只提供了一个搜索入口,快捷键唤起全局置顶的输入框,用完即走,从视觉和交互体验上都完爆 Web,除此之外,桌面应用还可以设置托盘菜单,做出类似于 QQ 消息一闪一闪的效果。

所以各位前端们还等什么,快跟着我一起学起来!


安装 Electron

为什么要特地讲一下安装呢,因为这里有一个坑

当执行 npm install electron -D 会一直卡在一个地方无法正常安装

解决办法 使用 cnpm:

npm install -g cnpm --registry=https://registry.npmmirror.com
cnpm install electron -D

桌面CSDN实战

纸上得来终觉浅,我们接下来先通过手写一个 Electron Demo 带大家入门 Electron。

我们首先来快速创建一个 Electron 项目:

$ mkdir my-electron
$ cd my-electron
$ npm init -y
$ npm install electron electron-packager --dev

接下来我们创建一个 src 目录,里面再分别创建两个子目录 main 和 renderer。

在 Electron 中有主进程渲染进程两个重要的概念,我们在主进程里面用 Node.js 代码调用 Electron 封装好的 API 来创建窗口,管理应用整个生命周期,而在渲染进程里面加载传统的 Web 界面。

因此 main 目录用于存放跟主进程相关的代码,renderer 目录用于存放跟渲染进程相关的代码。

整个桌面应用的入口在主进程里面,接下来在 main 目录中创建 index.js 作为入口。

为了让 Electron 知晓该入口,我们需要在 package.json 中做以下指定:

"main": "src/main/index.js",
"scripts": {
  "start": "electron ."
},

至此一个基础的 Electron 项目就搭建完毕,接下来我们可以来写我们的页面了。

在 src/main/index.js 入口文件中,我们来加载一个 CSDN :

//引入两个模块:app 和 BrowserWindow
//app 模块,控制整个应用程序的事件生命周期。
//BrowserWindow 模块,它创建和管理程序的窗口。
const { app, BrowserWindow } = require('electron')

//在 Electron 中,只有在 app 模块的 ready 事件被激发后才能创建浏览器窗口
app.whenReady().then(() => {
  //创建一个窗口
  createWindow()
})

function createWindow() {
  const mainWindow = new BrowserWindow({ width: 800, height: 600 })
  //窗口加载 URL
  mainWindow.loadURL('https://www.csdn.net/')
}

然后在终端输入 npm start 启动项目,你会看到一个加载 CSDN 官网的窗口弹出来了
在这里插入图片描述

一般的企业桌面应用都会定义自己的专属协议,我们可能都遇到过在网页的百度网盘点击下载,会自动打开我们电脑里的百度网盘软件。这是怎么做到的呢?就是通过自定义协议。

所谓自定义协议,其实就是给应用起个独一无二的名称,然后注册到操作系统里面,凡是通过这个协议名就能唤起这个软件了,在 Electron 中注册协议只需要一行代码:

app.setAsDefaultProtocolClient('electron-desktop')

注册之后,当在浏览器中输入 electron-desktop:// 之后,会发现弹出跳转提示,点击同意就能启动并跳转到桌面应用了,通过这种协议唤起应用被称为 scheme 唤起,而且在唤起的时候还可以带上一些参数,例如:

electron-desktop://width=800&height=600

自定义协议之后,可以用 scheme 唤起桌面应用,这是非常重要的能力

这里面最关键的是需要拿到协议唤起参数,否则唤起 QQ 之后不知道要跟谁聊天,唤起百度网盘之后不知道要下载哪款资料。

scheme 唤起的行为是操作系统默认支持的,操作系统也提供了 API 来监听唤起事件并拿到唤起参数。

在 Mac 和 Windows 上获取协议唤起参数是不一样的,这是由于平台策略不同导致的,这里讲解一下 Mac。

在 Mac 上面通过监听 open-url 事件,可以拿到唤起的 scheme 参数:

app.on('open-url', (event, url) => {
  console.log(url) // 打印 electron-desktop://width=800&height=600
})

url 里面就是 scheme 唤起的完整地址字符串,除了开头的 electron-desktop:// 前缀之外,后面的内容是完全交给用户自定义的,例如:

  • electron-desktop://hello-CSDN
  • electron-desktop://1+1=2

这些都可以唤起,上面之所以用 width=800&height=600 完全是因为模仿 http 地址栏的 query 参数的格式,有现成的 API 方便解析参数而已。

下面我们把 open-url 的回调获取到的 scheme 参数解析出来放到全局变量 urlParams 里面:

const { app, BrowserWindow } = require('electron')

const protocol = 'electron-desktop'
app.setAsDefaultProtocolClient(protocol)

let urlParams = {}

app.on('open-url', (event, url) => {
  const scheme = `${protocol}://`
  const urlParams = new URLSearchParams(url.slice(scheme.length))
  urlParams = Object.fromEntries(urlParams.entries())
})

app.whenReady().then(() => {
  createWindow()
})

function createWindow() {
  const mainWindow = new BrowserWindow({ width: 800, height: 600 })
  mainWindow.loadURL('https://www.csdn.net/')
}

协议唤起在 Mac 平台上有两点需要注意:

  • open-url 要在 ready 事件之前注册,因为有些场景是需要拿到参数之后再决定如何创建窗口的,如果放在 ready 回调里面,createWindow 可能会拿不到该参数了。
  • 在应用支持多实例场景下,如果程序未启动,会立即启动应用,在 open-url 中获取到唤起参数。如果存在正在运行的实例(可能有多个),会激活(其中一个)已经运行的程序,而不会开启新的实例,被激活的实例可以通过 open-url 回调获取唤起参数。

接下来我们来完成这个桌面 CSDN demo:

  • 打开桌面应用后立即进入 CSDN 首页
  • 支持用 csdn:// 这个 scheme 唤起应用
  • 支持用 csdn://width=500&height=300 这个 scheme 指定窗口大小
const { app, BrowserWindow } = require('electron')

let mainWindow

const protocol = 'csdn'
const scheme = `${protocol}://`
app.setAsDefaultProtocolClient(protocol)

let urlParams = {}

handleSchemeWakeup(process.argv)

app.on('open-url', (event, url) => handleSchemeWakeup(url))

app.whenReady().then(() => {
  createWindow()
})

// 创建 electron 新窗口
function createWindow() {
  const width = parseInt(urlParams.width) || 800
  const height = parseInt(urlParams.height) || 600
  if (mainWindow) {
    mainWindow.setSize(width, height)
  } else {
    mainWindow = new BrowserWindow({ width, height })
    mainWindow.loadURL('https://www.csdn.net/')
  }
}

// 处理自定义协议 scheme 唤起
function handleSchemeWakeup(argv) {
  const url = [].concat(argv).find((v) => v.startsWith(scheme))
  if (!url) return
  // url 之间的 search 转换
  const searchParams = new URLSearchParams(url.slice(scheme.length))
  urlParams = Object.fromEntries(searchParams.entries())
  if (app.isReady()) createWindow()
}

到此为止,我们的桌面 CSDN 实战就完成了。

我们把传统的 Web 页面通过用 Electron 加载出来的方式叫做「套壳桌面应用」,这也是将网站做成桌面软件最快速的方式。

在开发完成后,需要将应用程序打包成可执行文件,可以使用 electron-builder 进行打包。

在命令行中运行以下命令进行安装:

npm install electron-builder --save-dev

安装完成后,可以在项目的 package.json 文件中添加以下脚本:

"scripts": {   "start": "electron .",   "build": "electron-builder" }

然后,在命令行中运行 npm run build 命令即可打包 Electron 应用程序。

相信你通过上面这个的应用已经了解了 Electron 的简单开发和打包流程,

接下来我们来学习一些 Electron 的基础理论知识和进阶用法。


Electron 基础配置

const { app, BrowserWindow } = require('electron')
let win
// 监听electron 加载完毕的时候的创建窗口等等
app.on('ready', function () {
    // 创建一个窗口 设置属性
    win = new BrowserWindow({
    //fullscreen: true   //全屏
    //frame: false,   	//让桌面应用没有边框,这样菜单栏也会消失
    resizable: false,   //不允许用户改变窗口大小
    width: 800,         //设置窗口宽高
    height: 600,
    icon: iconPath,     //应用运行时的标题栏图标
    minWidth: 300,     // 最小宽度
    minHeight: 500,    // 最小高度
    maxWidth: 300,    // 最大宽度
    maxHeight: 600,    // 最大高度
    // 进行对首选项的设置
    webPreferences:{    
      backgroundThrottling: false,   //设置应用在后台正常运行
      nodeIntegration:true,     //设置能在页面使用nodejs的API
      contextIsolation: false,  //关闭警告信息
      //preload: path.join(__dirname, './preload.js')
    }
  })
  // 这里让主进程加载一个index.html
  win.loadFile('index.html')
  // 设置为最顶层
  //win.setAlwaysOnTop(true)
  //win.loadURL(`www.baidu.com`) 可以让主进程打开文件或者一个链接
  // 监听窗口关闭事件
  win.on('closed',()=>{
      //释放win
      win = null
  })
})

// 监听所有的窗口都关闭了
app.on('window-all-closed', () => {
    console.log('窗口全部都关闭了')
})

Electron 进程

Electron 应用程序有两种类型的进程:主进程和渲染进程。

主进程负责管理应用程序的生命周期和所有窗口,而渲染进程负责显示窗口内容。

通常,主进程和渲染进程是通过 ipcMainipcRenderer 模块进行通信。

使用主进程和渲染进程,你可以更好地管理应用程序和窗口,并且可以在不同的进程中处理不同的任务。

主进程

Electron 运行 package.json 的 main 脚本的进程被称为主进程 (只有一个)

主进程特点:

  • 主进程连接着操作系统和渲染进程,可以把它看做页面和计算机沟通的桥梁
  • 进程间通信、窗口管理
  • 全局通用服务
  • 一些只能或适合在主进程做的事情,例如浏览器下载、全局快捷键处理、托盘、session
  • 维护一些必要的全局状态

渲染进程

渲染进程就是我们所熟悉前端环境了,只是载体改变了,从浏览器变成了 window.

注:出于安全考虑,渲染进程是不能直接访问本地资源的,因此都需要在主进程完成。

渲染进程特点:

  • Electron 使用了 Chromium 来展示 web 页面,所以 Chromium 的多进程架构也被使用到
  • 每个 web 页面运行在它自己的渲染进程中。每个渲染进程都是相互独立的,并且只关心他们自己的网页
  • 使用 BrowserWindow 类开启一个渲染进程并将这个实例运行在该进程中,当一个 BrowserWindow 实例被销毁后,相应的渲染进程也会被终止
  • 渲染进程中不能调用原生资源,但是渲染进程中同样包含 Node.js 环境,所以可以引入 Node.js

主进程与渲染进程的区别

  • 主进程使用 BrowserWindow 实例创建网页
  • 每个 BrowserWindow 实例都在自己的渲染进程里运行着一个网页。当一个 BrowserWindow 实例被销毁后,相应的渲染进程也会被终止
  • 主进程管理所有页面和与之对应的渲染进程
  • 由于在网页里管理原生 GUI 资源是非常危险而且容易造成资源泄露,所以在网页面调用 GUI 相关的 API 是不被允许的。如果你想在网页里使用 GUI 操作,其对应的渲染进程必须与主进程进行通讯,请求主进程进行相关的 GUI 操作

把它们想象成这样 Chrome 的每个标签页及其页面,就好比 Electron 中的一个单独渲染进程。

即使关闭所有标签页,Chrome 依然存在。这好比 Electron 的主进程,能打开新的窗口或关闭这个应用。

主进程与渲染进程的通信

主线程渲染线程 通过 webContents.send 来发送 —>ipcRenderer.on 来监听

渲染线程主线程 需要通过 ipcRenderer.send 发送 —> ipcMain.on 来监听


Electron 跨平台问题

跨平台问题是在开发 Electron 应用程序时需要考虑的一个重要问题。

由于不同平台有不同的 UI 和 API,因此需要使用不同的代码来处理跨平台问题。

以下是一些常见的跨平台问题和解决方法:

  • 文件路径:在不同的操作系统上,文件路径的格式可能不同。为了解决这个问题,可以使用 Node.js 提供的 path 模块来处理文件路径。
  • 窗口大小和位置:在不同的操作系统上,窗口的大小和位置可能有所不同。为了解决这个问题,可以使用 Electron 提供的 screen 模块来获取屏幕大小和分辨率,并使用相对位置来设置窗口的大小和位置。
  • 快捷键:在不同的操作系统上,快捷键的组合键可能不同。为了解决这个问题,可以使用 Electron 提供的 globalShortcut 模块来注册快捷键,该模块可以自动适应不同的操作系统。

可以使用快捷键 Ctrl+Shift+I(Windows 和 Linux)或 Cmd+Shift+I(macOS)打开开发者工具。


Electron 部署

打包应用程序

在将应用程序部署到生产环境之前,需要将其打包为可执行文件。

以下是一些常用的工具,可以帮助我们将 Electron 应用程序打包为可执行文件:

  • electron-builder:一个基于 Electron 的打包器,支持将应用程序打包为各种格式,如 Windows、macOS 和 Linux。
  • electron-packager:另一个流行的打包器,也支持多种格式。

以下介绍一下 Electron Builder 的使用步骤和注意事项:

首先需要使用 npm 安装 Electron Builder:

npm install electron-builder --save-dev

然后我们要配置 package.json 文件,我们需要添加一些字段来配置应用程序的打包和发布。

  • build 字段:用于配置应用程序的构建选项。
  • directories 字段:用于配置应用程序的源代码和构建输出目录。
  • repository 字段:用于配置应用程序的源代码仓库地址。

以下是一个 package.json 文件的示例:

{
    "name":"my-electron-app",
    "version":"1.0.0",
    "description":"my Electron App",
    "main":"main.js",
    "scripts":{
        "start":"electron .",
        "build":"electron-builder"
    },
    "repository":{
        "type":"git",
        "url":"https://github.com/username/my-electron-app.git"
    },
    "build":{
        "appId":"com.example.my-electron-app",
        "productName":"my Electron App",
        "directories":{
            "output":"dist"
        }
    }
}

build 字段中,我们需要添加一些构建选项来指定应用程序的行为,以下是一些常用的构建选项:

  • appId:应用程序的 ID。
  • productName:应用程序的名称。
  • files:要打包的文件和文件夹。
  • directories:源代码和构建输出目录。
  • asar:是否将应用程序打包成 ASAR 文件。
  • macwinlinux:用于配置每个平台的构建选项。
  • dmgnsisdeb:用于配置每个平台的安装包选项。

以下是一个常见的 build 字段的示例:

{
    "build":{
        "appId":"com.example.my-electron-app",
        "productName":"My Electron App",
        "directories":{
			"buildResources": "build",   //指定打包需要的静态资源,默认是build
      		"output": "dist",  //打包生成的目录,默认是dist
        },
        "files":[
            "main.js",
            "package.json",
            "index.html",
            "assets/**/*"
        ],
        "asar":true,
        "mac":{
            "target":"dmg",
            "icon":"assets/icon.icns"
            "category": "public.app-category.utilities"  //应用程序安装到哪个分类下
        },
        "win":{
            "target":"nsis",
            "icon":"assets/icon.ico"
        },
        "linux":{
            "target":"deb",
            "icon":"assets/icon.png"
        },
        "dmg": {
             "background": "build/background.jfif",   //安装窗口背景图
             "icon": "build/icons/icon.icns",         //安装图标
             "iconSize": 100,                         //图标的尺寸
             "contents": [                            //安装图标在安装窗口中的坐标信息
                {
                  "x": 380,
                  "y": 180,
                  "type": "link",
                  "path": "/Applications"
                },
                {
                  "x": 130,
                  "y": 180,
                  "type": "file"
                }
             ],
             "window": {                             //安装窗口的大小
                "width": 540,
                "height": 380
             }
   		}
    }
}

package.json 文件中,可以使用以下命令来打包应用程序:

npm run build

执行此命令后,Electron Builder 将使用我们在 build 字段中配置的选项来构建应用程序,并将构建输出文件保存到 directories.output 中指定的目录。

在打包生成的文件夹中,会有一个 app.asar,它是 Electron 应用程序的主业务文件压缩包,要知道项目中哪些文件被 pack 到安装包,可以通过解压 app.asar 进行查看。

解压完 app.asar 后,里面除了项目的所有文件,还有一个 node_modules

对于 node_modules并不是所有 node_modules 中的内容都会被打包进安装包,只有 package.jsondependencies 字段中的依赖会被打包,devDependencies 字段中的依赖则不会。

这是唯一规则,跟项目实际是否使用依赖没有关系。

所以,为了减小安装包体积,建议在渲染进程中使用的外部包,都安装在 devDependencies 中,然后使用 webpack 将外部包的代码和业务代码打包到一起。

发布应用程序

将应用程序打包为可执行文件之后,我们就可以将其发布到各个平台的应用商店或者自己的网站上。

electron-updater:用于自动化发布应用程序的工具

更新应用程序的安装包应该存放在互联网的某台服务器上,每次打开应用的时候,进行自动检测,根据当前应用程序的 version 和线上版本进行匹配,当发现有新的 version 的时候,就自动下载,下载完成后,询问用户是否安装新版本。

还有一种情况就是 web 资源和 “app 壳子” 分离,web 资源放在服务器,每次都通过网络动态加载,像我们上面的桌面 CSDN 实战一样:

mainWindow.loadURL('https://www.csdn.net/')

在业务需要频繁更新的场景中,可以使用这种方式,快速无障碍地实现更新。

在这种情况下,我们可以按照上述方式打包和更新 “壳子”,也就是主进程相关;

而页面资源的打包和普通的前端项目打包无异,这里不再赘述。


Electron 跨端原理

Electron 的跨端原理并不难理解,我们在这里简单介绍一下,相信大家能很容易理解。

它通过集成浏览器内核,使用前端技术来实现不同平台下的渲染,并结合了 ChromiumNode.js 和用于调用系统本地功能的 API 三大板块。
在这里插入图片描述

  • ChromiumElectron 提供强大的 UI 渲染能力,由于 Chromium 本身跨平台,因此无需考虑代码的兼容性。最重要的是,可以使用前端三板斧进行 Electron 开发。
  • Chromium 并不具备原生 GUI 的操作能力,因此 Electron 内部集成 Node.js,编写 UI 的同时也能够调用操作系统的底层 API,例如 path、fs、crypto 等模块。
  • Native APIElectron 提供原生系统的 GUI 支持,借此 Electron 可以调用原生应用程序接口。

总结起来,Chromium 负责页面 UI 渲染,Node.js 负责业务逻辑,Native API 则提供原生能力和跨平台。


总结

Electron 确实是构建跨平台桌面应用程序的利器,建议大家学习的时候多看看官方的文档,因为Electron版本迭代太快了,不及时看官方文档真的还是有不少坑的。

简单做个小总结,本文介绍了一些 Electron 的基础知识,包括 Electron 的定义、优势、基础配置、进程、部署、跨端原理,并完成了一个桌面 CSDN 应用实战案例。

通过掌握上述这些知识,你已经可以试着开发属于你自己的 Electron 应用程序啦,快去试试吧。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

十五分钟带你学会 Electron 的相关文章

  • JW Player javaScript API 不工作

    我使用 jwplayer version 5 10 2295 和浏览器 chrome 25 My code jwplayer container setup file path width 300px height 100px autost
  • axios 请求中未发送正文数据

    我试图通过 axios 请求将数据发送到我的后端脚本 但正文看起来是空的 这是前端发送的请求 axios request method GET url http localhost 4444 next api headers Authori
  • MailTo 从 Javascript

    我有一个链接按钮 用于从页面内容构建邮件 从 javascript 启动它而不打开空白窗口或干扰调用它的窗口的最佳方法是什么 function Email var sMailTo mailto var sBody var alSelecte
  • 如何在 DOM 中的每个元素中调用函数,即使它们是动态创建的

    我想对 DOM 上的特定元素调用函数 例如 red css backgroundColor pink 它适用于 DOM 中已经存在的任何元素 但我也希望在动态添加到 DOM 的元素中调用此方法 我尝试过类似的事情 red on functi
  • 使用 jQuery Select2 清除下拉菜单

    我正在尝试使用奇妙的方式以编程方式清除下拉菜单Select2 http ivaynberg github com select2 图书馆 使用 Select2 远程 ajax 调用动态填充下拉列表query option HTML
  • 如何在不阻止触摸启动的情况下防止“过度滚动历史导航”?

    我正在实现基于滑动的导航 但我在使用 Chrome 时遇到了麻烦 当页面向右拖动时 会触发新实现的功能 过度滚动历史导航 从而导致跳回 到 历史 1 为了防止这种情况 我必须打电话 preventDefault on touchstart
  • 如何立即启动setInterval循环? [复制]

    这个问题在这里已经有答案了 在一个简单的setInterval setInterval function Do something every 9 seconds 9000 第一个动作将在 9 秒后发生 t 9s 如何强制循环立即执行第一个
  • 设置双指缩放时精确的滚动位置

    我正在创建一个地图应用程序 它将标记图像放置在画布上并滚动到它 我正在使用浏览器的捏缩放和滚动来放大 缩小地图 然而 我注意到有一些奇怪的行为 我想知道如何解决它 这有点难以解释 但我们开始吧 假设您处于网页的标准缩放级别 无法进一步缩小
  • JS 保留以零结尾的小数[重复]

    这个问题在这里已经有答案了 在JavaScript中 是否可以 锁定 十进制数 以保留以零结尾的 浮点数 例如 我有 2 个不同的数字 如下所示 伪代码 let a 1 0 let b 1 00 a b true should be fal
  • JavaScript 将键添加到数组中的每个值

    我下面有这个数组 它由一个简单的数组组成 我想要完成的是放一把钥匙id在每个数组值前面以实现类似的效果 id a id b id c id d 有没有一种简单的方法可以做到这一点 任何帮助将不胜感激 谢谢 var test a b c d
  • 全局传递 xhr onload 函数的值

    在我正在创建的应用程序中 我有以下 XMLHttpRequest 并且我正在尝试传递结果data在 的里面xhr onload 到在同一父函数中创建的数组中 var url http api soundcloud com resolve j
  • 如何在 Web 服务器上设置 gzip 压缩?

    我有一个嵌入式网络服务器 总共有 2 兆空间 通常 您使用 gzip 文件对客户端有利 但这会节省我们在服务器上的空间 我读到你可以只 gzip js 文件并将其保存在服务器上 我在 IIS 上测试过 但没有任何运气 为了使这项工作成功 我
  • 使用 JQuery 禁用和启用所有超链接

    我有以下禁用所有超链接的内容 但在事件发生后我想再次启用它们 我该如何执行此操作 a click function return false 我认为这不仅仅是将其设置为 true 那么简单 谢谢大家 不要以这种方式绑定 点击 处理程序 而是
  • 为什么我收到“在嵌套函数中通过 this 对类字段进行潜在无效的引用访问”错误

    在普通 JS 中 我的代码可以正常工作 对于这种情况 我想组件化我的Wall类应该在浏览器中显示用户上传的图像 同样 这在 vanilla JS 中正常工作 但在 JSX 中不起作用 我得到了一个potentially invalid re
  • 如何使用新的analytics.js跟踪多个帐户?

    我需要使用 Google 的新的analytics js 跟踪一个页面上两个帐户的综合浏览量 有大量教程和示例如何使用较旧的 ga js 进行操作 但我发现的只是这个分析文档页面 https developers google com an
  • 呃!尝试将包发布到 npm 时出现 403

    我正在尝试将包发布到 npm 您可以在此处查看存储库 https github com biowaffeln mdx state https github com biowaffeln mdx state 我登录到 npmnpm login
  • 如何为我的整个 Node.js 应用程序使用相同的 MySQL 连接?

    我有一个app js 我从那里运行我的整个应用程序 在 app js 内部 我require许多文件中都有代码 对于每个文件 我都这样做 var mysql require mysql var mclient mysql createCon
  • 语法错误:意外的标记“?”在 repl.it 上用 JavaScript 制作不和谐机器人时 [重复]

    这个问题在这里已经有答案了 我收到错误 const token this client token this client accessToken SyntaxError Unexpected token Discord 机器人代码 con
  • 如何在画布上所有其他内容后面绘制图像? [复制]

    这个问题在这里已经有答案了 我有一块画布 我想用drawImage在画布上当前内容后面绘制图像 由于画布上已经有内容 我正在使用字面上的画布来创建包含图像的画布 因此我无法真正先绘制图像 所以我无法使用drawImage在我呈现其余内容之前
  • ChartJs:如何按时间值(而不是像素)以编程方式平移

    我使用的是chartJs 3 6 1 和 Chartjs plugin zoom 1 2 1 但我认为这并不重要 我有 2 个时间序列折线图 当我通过拖放平移一个图表时 我也想移动 平移另一个图表 为此 我为 graphA 启用了缩放 平移

随机推荐

  • [计算机毕业设计]深度学习的图标型验证码识别系统

    前言 大四是整个大学期间最忙碌的时光 一边要忙着准备考研 考公 考教资或者实习为毕业后面临的就业升学做准备 一边要为毕业设计耗费大量精力 近几年各个学校要求的毕设项目越来越难 有不少课题是研究生级别难度的 对本科同学来说是充满挑战 为帮助大
  • OpenGL ES 3.0 开发(一)

    什么是 OpenGLES OpenGLES 全称 OpenGL for Embedded Systems 是三维图形应用程序接口 OpenGL 的子集 本质上是一个跨编程语言 跨平台的编程接口规范 主要应用于嵌入式设备 如手机 平板等 由科
  • SpringBoot websocket + java swing 实现大文件上传与下载

    使用场景 无法直接登录服务器上传文件 使用web端上传超大文件出现超时 实现原理 上传 server端与client端建立websocket连接 client将待传文件进行分块 然后将文件的相关信息 文件名 md5值 分块大小 总块数 当前
  • 角谱衍射计算

    角谱衍射 角谱衍射计算 D F F T D FFT D FFT 即采用两次傅里叶变换 原始公式和 S
  • IP协议相关技术

    前言 其实我们在上网的时候并不是直接使用IP地址 同样IP地址还不方便记忆 这样我们就需要IP相关的技术来帮助我们通信 DNS 一开始人们使用TCP IP世界中的主机识别码来转换成具体的IP地址 这样人们就可以直接使用主机名称 但是随着网络
  • Chatglm2-6b模型相关问题

    Chatglm2 6b模型相关问题 1 Chatglm2 6b模型p tuning后推理答非所问 2 ChatGLM2 6b ptuning 3 ChatGLM2 6b部署 1 Chatglm2 6b模型p tuning后推理答非所问 据C
  • python词云 小说《庆余年》

    一 概述 使用jieba分词和wordcloud生产 小说的词云库 源码地址 https github com jw star pythonDemo tree master E5 BA 86 E4 BD 99 E5 B9 B4wordclo
  • HTML第一次作业

    什么是web前端 web前端就是由多种技术制作的 用来给用户展示的网页 也叫网站的前台部分 包括的技术有html css javascript jQueery bt等 什么是HTML hyper text maekuo language 骨
  • DVWA靶场存储型XSS漏洞实验

    文章目录 文章目录 前言 一 XSS基础 1 什么是XSS 2 XSS漏洞原理 3 XSS漏洞成因 4 XSS漏洞危害 5 存储型XSS漏洞原理 6 存储型XSS与反射型XSS的区别 二 DVWA靶场实战 1 存储型XSS漏洞利用 LOW
  • 求逆矩阵的常用三种方法

    1 待定系数法 矩阵A 1 2 1 3 假设所求的逆矩阵为 a b c d 则 这里写图片描述 从而可以得出方程组 a 2c 1 b 2d 0 a 3c 0 b 3d 1 解得 a 3 b 2 c 1 d 1 2 伴随矩阵求逆矩阵 伴随矩阵
  • node之Buffer(缓冲区)

    Node js Buffer 缓冲区 JavaScript 语言自身只有字符串数据类型 没有二进制数据类型 但在处理像TCP流或文件流时 必须使用到二进制数据 因此在 Node js中 定义了一个 Buffer 类 该类用来创建一个专门存放
  • 矢量绘图UI设计Sketch

    Sketch是一款Mac操作系统上常用的矢量图形编辑软件 旨在帮助用户设计和创建高质量的UI和UX界面 软件安装 Sketch 中文 以下是Sketch软件的一些主要特点 矢量工具和对象 Sketch提供了多种画线 填充 阴影 文本和形状等
  • UE4 部分命令知识点梳理

    UE4 部分命令知识点梳理 1 r SSGI Enable 0 1 屏幕空间 全局光照关闭 开启 2 DFO 距离场AO 指数指的是AO强度 遮挡最大距离 指的是距离场AO影响的最大距离 3 r forcelod 1 0 1 等 设置场景模
  • CSS中clear:both的作用

    clear both意思就是清除浮动 例如我们设置了三个div如下
  • es6中let var const 的特点及区别

    首先 var是定义一个变量常用的方法 与其相似的还有let和const 以下介绍他们三个的特点及不同 一 var var的用法很多 没有什么局限 可以对变量进行声明 例如 注意 var let const 是js的关键词 需要写在scrip
  • CSDN笔记

    拉普拉斯变换的收敛域 ROC 与逆变换 ILT 1 是否可积即是否收敛 如果可收敛 面积 拉氏值即为收敛域 1 收敛的条件 e jwt 积分为振荡函数 2 常系数线性微分方程对应线性时不变系统 其分析步骤有三 3 拉氏逆变换 ILT 的方法
  • Linux僵尸进程怎么处理,Linux 僵尸进程如何处理

    Linux 允许进程查询内核以获得其父进程的 PID 或者其任何子进程的执行状态 例如 进程可以创建一个子进程来执行特定的任务 然后调用诸如 wait 这样的一些库函数检查子进程是否终止 如果子进程已经终止 那么 它的终止代号将告诉父进程这
  • js求时间差

    js求时间差 var date1 new Date 开始时间 alert aa var date2 new Date 结束时间 var date3 date2 getTime date1 getTime 时间差的毫秒数 计算出相差天数 va
  • 基于SpringBoot的购票系统的设计与实现

    博主介绍 在职Java研发工程师 专注于程序设计 源码分享 技术交流 专注于Java技术领域和毕业设计 温馨提示 文末有 CSDN 平台官方提供的老师 Wechat QQ 名片 项目名称 基于SpringBoot的购票系统的设计与实现 演示
  • 十五分钟带你学会 Electron

    文章目录 什么是 Electron 为什么要选择 Electron 安装 Electron 桌面CSDN实战 Electron 基础配置 Electron 进程 主进程 渲染进程 主进程与渲染进程的区别 主进程与渲染进程的通信 Electr