前言
随着前端技术的迅速发展,我们见证了从最初的 HTML + JS + CSS 到如今的各种前端框架的崛起。在这篇文章中,我们将探讨如何在 HTML 中添加全局脚本,特别是在 Electron 应用程序中的需求。由于很多网站使用 CDN 脚本,但在 Electron 的本地化和安全需求下,将 CDN 脚本下载到本地并在 HTML 中引用成了一个挑战。许多初学者可能不清楚如何配置它,我们将以 Electron-React-Boilerplate 为例,简要介绍打包逻辑以及如何向公共路径添加脚本。
Electron-React-Boilerplate 简介
Electron-React-Boilerplate 是一个为 Electron 应用程序提供了基础结构的项目模板。它结合了 Electron 和 React,为开发者提供了一个快速启动 Electron 项目的方式。在这个项目中,我们将重点关注如何处理全局脚本的加载和配置。
打包逻辑分析
Electron-React-Boilerplate 的打包逻辑主要基于 webpack。Webpack 是一个模块打包器,它将 JavaScript 文件及其依赖项打包成一个或多个 bundle。在 Electron-React-Boilerplate 中,webpack 负责将应用程序的源代码和相关资源打包成可在 Electron 中运行的格式。
在 webpack 的配置中,我们需要确保将全局脚本正确地引入到 HTML 中。一种常见的方法是通过 webpack 的 HtmlWebpackPlugin 插件来自动向生成的 HTML 文件中添加脚本标签。但是,对于 Electron 应用程序,我们可能需要手动控制这个过程,以便更好地管理本地资源和安全性。
向公共路径添加脚本
为了设置能在 Electron 中使用cdn脚本我打算将cdn脚本下载到本地并放置在公共路径,此时我遇到了第一问题,electron-react-boilerplate中没有public文件夹。
1. 从 electron 到 webpack 逆推
参考 https://www.electronjs.org/zh/docs/latest/tutorial/quick-start
electron 本身并没有公共路径的概念,它只是忠实的执行mian.js的指令,关键问题在于HTML文件所处的位置,即脚本从HTML开始,HTML所处的位置即是公共路径。
参考: https://webpack.js.org/guides/getting-started/
也就是说控制HTML位置的并不是 electron 而是 webpack 打包工具的配置。
为了向 Electron 应用程序的 HTML 文件中添加全局脚本,我们可以通过 webpack 的配置文件来实现。在electron-react-boilerplate中配置文件路径如下
2. 寻找公共路径
从webpack.config.renderer.dev.ts 下面的配置项中可以看到打包的输出路径在dist/renderer文件夹(release/app/dist/renderer)
output: {
path: webpackPaths.distRendererPath,
publicPath: '/',
filename: 'renderer.dev.js',
library: {
type: 'umd',
},
},
已经找到了HTML所在的公共路径,但是由于在package.json中 start:renderer中使用的是 webpack serve,无法在对应路径下看到打包的文件。
"scripts": {
"build": "concurrently \"npm run build:main\" \"npm run build:renderer\"",
"build:dll": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.renderer.dev.dll.ts",
"build:main": "cross-env NODE_ENV=production TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.main.prod.ts",
"build:renderer": "cross-env NODE_ENV=production TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.renderer.prod.ts",
"postinstall": "ts-node .erb/scripts/check-native-dep.js && electron-builder install-app-deps && npm run build:dll",
"lint": "cross-env NODE_ENV=development eslint . --ext .js,.jsx,.ts,.tsx",
"package": "ts-node ./.erb/scripts/clean.js dist && npm run build && electron-builder build --publish never && npm run build:dll",
"rebuild": "electron-rebuild --parallel --types prod,dev,optional --module-dir release/app",
"start": "ts-node ./.erb/scripts/check-port-in-use.js && npm run start:renderer",
"start:main": "cross-env NODE_ENV=development electronmon -r ts-node/register/transpile-only .",
"start:preload": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.preload.dev.ts",
"start:renderer": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack serve --config ./.erb/configs/webpack.config.renderer.dev.ts",
"test": "jest"
},
我们可以创建一个新的命令 start:dist
将其中的webpack serve
改成webpack
效果如下
"scripts": {
"build": "concurrently \"npm run build:main\" \"npm run build:renderer\"",
"build:dll": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.renderer.dev.dll.ts",
"build:main": "cross-env NODE_ENV=production TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.main.prod.ts",
"build:renderer": "cross-env NODE_ENV=production TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.renderer.prod.ts",
"postinstall": "ts-node .erb/scripts/check-native-dep.js && electron-builder install-app-deps && npm run build:dll",
"lint": "cross-env NODE_ENV=development eslint . --ext .js,.jsx,.ts,.tsx",
"package": "ts-node ./.erb/scripts/clean.js dist && npm run build && electron-builder build --publish never && npm run build:dll",
"rebuild": "electron-rebuild --parallel --types prod,dev,optional --module-dir release/app",
"start": "ts-node ./.erb/scripts/check-port-in-use.js && npm run start:renderer",
"start:main": "cross-env NODE_ENV=development electronmon -r ts-node/register/transpile-only .",
"start:preload": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.preload.dev.ts",
"start:renderer": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack serve --config ./.erb/configs/webpack.config.renderer.dev.ts",
"start:dist": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.renderer.dev.ts",
"test": "jest"
},
此时运行 npm run start:dist
在对应路径下就可以看到打包的文件
3. 修改配置文件
此时距离解决问题已经只有一步之遥,在考虑如何将cdn脚本复制到release/app/dist/renderer之前,我先介绍一下electron-react-boilerplate webpack 配置文件,你需要关注开发和生产环境配置,基础配置为公共的基础配置,将cdn复制到公共路径的需求是一个公共的配置,因此需要在基础配置中添加相关配置。
我们可以使用 CopyWebpackPlugin 来将cdn或者其他你需要的脚本复制到打包后的目标路径中。这样,我们就可以确保全局脚本在应用程序运行时可用。
运行下面的命令安装webpack插件
npm install -D copy-webpack-plugin
webpack.config.base.ts修改如下
/**
* Base webpack config used across other specific configs
*/
import path from 'path';
import webpack from 'webpack';
import TsconfigPathsPlugins from 'tsconfig-paths-webpack-plugin';
import CopyPlugin from 'copy-webpack-plugin';
import webpackPaths from './webpack.paths';
import { dependencies as externals } from '../../release/app/package.json';
const configuration: webpack.Configuration = {
externals: [...Object.keys(externals || {})],
stats: 'errors-only',
module: {
rules: [
{
test: /\.[jt]sx?$/,
exclude: /node_modules/,
use: {
loader: 'ts-loader',
options: {
// Remove this line to enable type checking in webpack builds
transpileOnly: true,
compilerOptions: {
module: 'esnext',
},
},
},
},
],
},
output: {
path: webpackPaths.srcPath,
// https://github.com/webpack/webpack/issues/1114
library: {
type: 'commonjs2',
},
},
/**
* Determine the array of extensions that should be used to resolve modules.
*/
resolve: {
extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'],
modules: [webpackPaths.srcPath, 'node_modules'],
// There is no need to add aliases here, the paths in tsconfig get mirrored
plugins: [new TsconfigPathsPlugins()],
},
plugins: [
new CopyPlugin({
patterns: [
{
from: path.join(
webpackPaths.rootPath,
'assets',
'live2dcubismcore.min.js',
),
to: path.join(
webpackPaths.distRendererPath,
'live2dcubismcore.min.js',
),
},
],
}),
new webpack.EnvironmentPlugin({
NODE_ENV: 'production',
}),
],
};
export default configuration;
参考上面的配置将live2dcubismcore.min.js路径替换为你的脚本路径即可,最终运行npm run start:dist
效果如下
最后修改 src/renderer/index.ejs如下即可(将对应部分替换为你的脚本即可),index.ejs为HTML模板文件,对index.ejs的修改会同步到HTML中
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta
http-equiv="Content-Security-Policy"
content="script-src 'self' 'unsafe-inline'"
/>
<title>Hello Electron React!</title>
<script type="text/javascript" src="./live2dcubismcore.min.js"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>
在上面的示例中,我们通过 <script>
标签将全局脚本 live2dcubismcore.min.js
引入到了 HTML 文件中。这样一来,在 Electron 应用程序运行时,全局脚本就会被加载并执行。
总结
通过以上的分析,我们了解了如何在 Electron-React-Boilerplate 中处理全局脚本的加载和配置。通过将全局脚本放置在公共路径下,并在 HTML 文件中添加相应的引用,我们可以确保 Electron 应用程序能够正常地加载并执行这些脚本。这为开发者提供了一种便捷的方式来管理 Electron 应用程序中的全局资源,同时也提高了应用程序的安全性和可维护性。
希望本文能够帮助读者更好地理解 Electron-React-Boilerplate 的打包逻辑,并在实际项目中正确地处理全局脚本的加载和配置。