Electron + Vue3 开发桌面应用

目录

  • 一、搭建项目
    • 1、创建 Vue 项目
    • 2、安装 Electron
    • 3、运行 Electron
    • 4、Vue 和 Electron 结合
    • 5、其它配置
    • 6、其它问题
      • (1)热更新失效、控制台报错
  • 二、打包
    • 1、安装 electron-builder
    • 2、打包配置
      • (1)修改公共基础路径
      • (2)修改 Electron 配置
      • (3)打包配置
      • (3)打包
    • 3、其它问题
      • (1)web和客户端同时部署
      • (2)刷新页面后报错问题
      • (3)不带协议的 url 被解析为 file 协议问题
      • (4)存取 `cookie` 问题
      • (5)限制只能打开一个窗口
      • (6)关闭窗口时提示
  • 三、代码签名
      • (1)windows
      • (2)mac
  • 四、自动更新


相关文档

Vite: https://vitejs.cn
Vue : https://cn.vuejs.org
Electron: https://www.electronjs.org/
electron-builder: https://github.com/electron-userland/electron-builder
concurrently: https://github.com/open-cli-tools/concurrently

示例环境

电脑: MacBook Pro (13-inch, M1, 2020)
系统: macOS 11.6
Node 版本: v16.14.0
npm 版本: v7.1.2


一、搭建项目

1、创建 Vue 项目

参考:https://cn.vuejs.org/guide/quick-start.html

确保你安装了最新版本的 Node.js,然后在命令行中运行以下命令:

npm init vue@latest 

这一指令将会安装并执行 create-vue,它是 Vue 官方的项目脚手架工具。你将会看到一些诸如 TypeScript 和测试支持之类的可选功能提示,本项目只做示例,所以只选了 Vue Router

✔ Project name: … electron_vue3_test
✔ Add TypeScript? … No
✔ Add JSX Support? … No
✔ Add Vue Router for Single Page Application development? … Yes
✔ Add Pinia for state management? … No
✔ Add Vitest for Unit Testing? … No
✔ Add Cypress for both Unit and End-to-End testing? … No
✔ Add ESLint for code quality? … No
✔ Add Prettier for code formatting? … No

在项目被创建后,通过以下步骤安装依赖并启动开发服务器:

cd 
npm install
npm run dev

执行后终端显示如下:

  VITE v3.1.2  ready in 333 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose

访问 http://localhost:5173/ 显示页面如下所示,Vue 项目创建成功

Electron + Vue3 开发桌面应用_第1张图片

如果想要修改端口号,修改 vite.config.js 文件的 server.port 选项为你想要的端口号,这里我改成了 3004

export default defineConfig({
  // ...
  server: {
    port: 3004
  }
});

2、安装 Electron

参考:https://www.electronjs.org/zh/docs/latest/tutorial/installation

执行以下命令安装 Electron

npm install electron --save-dev

如果安装过程中卡住,可尝试设置以下环境变量

ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/"

mac 修改环境变量的方法:
终端 中输入 vim ~/.zshrc 打开文件
i 进入编辑模式
添加一行 export ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/"
esc 退出编辑模式
输入 :wq 保存并退出
终端 输入 source ~/.zshrc 使刚才的修改生效

3、运行 Electron

参考:https://www.electronjs.org/zh/docs/latest/tutorial/quick-start

在根目录创建 electron 文件夹,新建 index.html 文件,添加如下代码:

DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <meta
      http-equiv="Content-Security-Policy"
      content="default-src 'self'; script-src 'self'"
    />
    <meta
      http-equiv="X-Content-Security-Policy"
      content="default-src 'self'; script-src 'self'"
    />
    <title>Hello from Electron renderer!title>
  head>
  <body>
    <h1>Hello from Electron renderer!h1>
    <p>p>
  body>
html>

electron 目录下新建文件 main.js,并添加如下代码:

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

const createWindow = () => {
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
  })
  
  // 使用 loadFile 加载 electron/index.html 文件
  mainWindow.loadFile(path.join(__dirname, "./index.html"));
}

// 在应用准备就绪时调用函数
app.whenReady().then(() => {
  createWindow()
})

修改 package.json 文件,指定 electron/main.jsElectron 的入口文件,并添加 electron:dev 命令以开发模式运行 Electron

{
  "main": "electron/main.js",
  "scripts": {
    "electron:dev": "electron ."
  }
}

然后打开终端,执行 npm run electron:dev ,显示窗口如下所示,Electron 运行成功

Electron + Vue3 开发桌面应用_第2张图片

4、Vue 和 Electron 结合

删除 electron/index.html 文件,修改 electron/main.js 文件代码如下:

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

const createWindow = () => {
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
  })

  // 主要改了这里
  // mainWindow.loadFile(path.join(__dirname, "./index.html"));
  // 使用 loadURL 加载 http://localhost:3004 ,也就是我们刚才创建的 Vue 项目地址
  // 3004 改为你 Vue 项目的端口号
  mainWindow.loadURL("http://localhost:3004/");
}

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

有时开发需要,会使用 nginx 代理为线上地址,则将 loadURL 的参数改为代理的地址,并在 electron/main.js 中添加如下代码:

参考:https://stackoverflow.com/questions/49637026/mainwindow-loadurlhttps-localhost3000-show-white-screen-on-electron-app/49673272

// 证书的链接验证失败时,触发该事件
app.on(
  "certificate-error",
  function (event, webContents, url, error, certificate, callback) {
    event.preventDefault();
    callback(true);
  }
);

打开终端,执行 npm run dev 启动 Vue 项目,再新建一个终端,执行 npm run electron:dev 启动 Electron ,即可成功启动,显示如下:

Electron + Vue3 开发桌面应用_第3张图片

这样每次启动项目需要分别执行两个命令,有些麻烦,我们使用 concurrently把它们合并成一个命令。

参考:https://github.com/open-cli-tools/concurrently

执行以下命令,安装 concurrently

 npm install concurrently --save-dev

修改 package.json 文件中的 electron:dev 命令,同时执行 viteelectron . 两个命令,用引号将单独的命令括起来,并使用 \ 转义引号,代码如下:

// package.json
{
  "scripts": {
    "electron:dev": "concurrently vite \"electron .\""
  }
}

重新执行 npm run electron:dev ,即可成功显示。

5、其它配置

以下是开发环境下,我的一些其它配置,可供参考:

// electron/main.js
const { app, BrowserWindow, Menu, screen } = require("electron");
const path = require("path");

// 是否是生产环境
const isPackaged = app.isPackaged;

// 禁止显示默认菜单
Menu.setApplicationMenu(null);

// 主窗口
let mainWindow;

const createWindow = () => {
  // 创建浏览器窗口
  mainWindow = new BrowserWindow({
    // 默认窗口标题,如果由loadURL()加载的HTML文件中含有标签,此属性将被忽略。</span>
    <span class="token literal-property property">title</span><span class="token operator">:</span> <span class="token string">"Electron + Vue3"</span><span class="token punctuation">,</span>
    <span class="token comment">// width: 800,</span>
    <span class="token comment">// height: 600,</span>
    <span class="token comment">// 设置窗口尺寸为屏幕工作区尺寸</span>
    <span class="token literal-property property">width</span><span class="token operator">:</span> screen<span class="token punctuation">.</span><span class="token function">getPrimaryDisplay</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>workAreaSize<span class="token punctuation">.</span>width<span class="token punctuation">,</span>
    <span class="token literal-property property">height</span><span class="token operator">:</span> screen<span class="token punctuation">.</span><span class="token function">getPrimaryDisplay</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>workAreaSize<span class="token punctuation">.</span>height<span class="token punctuation">,</span>
    <span class="token comment">// 设置最小尺寸</span>
    <span class="token literal-property property">minWidth</span><span class="token operator">:</span> <span class="token number">800</span><span class="token punctuation">,</span>
    <span class="token literal-property property">minHeight</span><span class="token operator">:</span> <span class="token number">600</span><span class="token punctuation">,</span>
    <span class="token comment">// 窗口图标。 在 Windows 上推荐使用 ICO 图标来获得最佳的视觉效果, 默认使用可执行文件的图标.</span>
    <span class="token comment">// 在根目录中新建 build 文件夹存放图标等文件</span>
    <span class="token literal-property property">icon</span><span class="token operator">:</span> path<span class="token punctuation">.</span><span class="token function">resolve</span><span class="token punctuation">(</span>__dirname<span class="token punctuation">,</span> <span class="token string">"../build/icon.ico"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token comment">// 开发环境下,打开开发者工具。</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>isPackaged<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    mainWindow<span class="token punctuation">.</span>webContents<span class="token punctuation">.</span><span class="token function">openDevTools</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token comment">// 使用 loadURL 加载 http://localhost:3004 ,也就是我们刚才创建的 Vue 项目地址</span>
  <span class="token comment">// 3004 改为你 Vue 项目的端口号</span>
  <span class="token comment">// mainWindow.loadURL("http://localhost:3004/");</span>
  <span class="token comment">// 如果使用了 nginx 代理,url 改为代理地址</span>
  mainWindow<span class="token punctuation">.</span><span class="token function">loadURL</span><span class="token punctuation">(</span><span class="token string">"https://example.com/"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token comment">// 在应用准备就绪时调用函数</span>
app<span class="token punctuation">.</span><span class="token function">whenReady</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
  <span class="token function">createWindow</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  app<span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">"activate"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
    <span class="token comment">// 通常在 macOS 上,当点击 dock 中的应用程序图标时,如果没有其他</span>
    <span class="token comment">// 打开的窗口,那么程序会重新创建一个窗口。</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>BrowserWindow<span class="token punctuation">.</span><span class="token function">getAllWindows</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>length <span class="token operator">===</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token function">createWindow</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此,通常对程序和它们在任务栏上的图标来说,应当保持活跃状态,直到用户使用 Cmd + Q 退出。</span>
app<span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">"window-all-closed"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>process<span class="token punctuation">.</span>platform <span class="token operator">!==</span> <span class="token string">"darwin"</span><span class="token punctuation">)</span> app<span class="token punctuation">.</span><span class="token function">quit</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 如果开发环境使用了 nginx 代理,禁止证书报错</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>isPackaged<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// 证书的链接验证失败时,触发该事件</span>
  app<span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span>
    <span class="token string">"certificate-error"</span><span class="token punctuation">,</span>
    <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">event<span class="token punctuation">,</span> webContents<span class="token punctuation">,</span> url<span class="token punctuation">,</span> error<span class="token punctuation">,</span> certificate<span class="token punctuation">,</span> callback</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      event<span class="token punctuation">.</span><span class="token function">preventDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token function">callback</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> 
  <h2>6、其它问题</h2> 
  <h3>(1)热更新失效、控制台报错</h3> 
  <p>我使用了 <code>nginx</code> 代理,开发时出现了热更新失效的问题,并且控制台报了如下错误</p> 
  <pre><code>WebSocket connection to 'wss://example.com/' failed: 
WebSocket connection to 'wss://localhost:3004/' failed: 
[vite] failed to connect to websocket.
your current setup:
  (browser) example.com/ <--[HTTP]--> localhost:3004/ (server)
  (browser) example.com:/ <--[WebSocket (failing)]--> localhost:3004/ (server)
Check out your Vite / network configuration and https://vitejs.dev/config/server-options.html#server-hmr 
</code></pre> 
  <p>已经通过在 <code>nginx</code> 的配置文件中添加如下代码解决:</p> 
  <blockquote> 
   <p>参考:https://blog.csdn.net/weixin_39621860/article/details/111490058</p> 
  </blockquote> 
  <pre><code>server {
    # ...
    location / {
        # ...
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }
}
</code></pre> 
  <hr> 
  <h1>二、打包</h1> 
  <h2>1、安装 electron-builder</h2> 
  <blockquote> 
   <p>参考:https://www.electron.build/#installation</p> 
  </blockquote> 
  <p>打开终端,执行一下命令安装:<br> 使用 <code>yarn</code> (官方推荐):</p> 
  <pre><code>yarn add electron-builder --dev
</code></pre> 
  <p>或是 <code>npm</code> :</p> 
  <pre><code>npm install electron-builder --save-dev
</code></pre> 
  <h2>2、打包配置</h2> 
  <h3>(1)修改公共基础路径</h3> 
  <p>修改 <code>vite.config.js</code> 中的 <code>base</code> 选项为 “<code>./</code>”,如下所示:</p> 
  <pre><code>export default defineConfig({
  base: "./"
  // ...
});
</code></pre> 
  <h3>(2)修改 Electron 配置</h3> 
  <p>修改 <code>electron/main.js</code> 文件中 <code>mainWindow.loadURL</code> 的参数为 <code>vue</code> 打包后的文件路径</p> 
  <pre><code>// electron/main.js
const path = require("path");
const createWindow = () => {
  //...
  mainWindow.loadURL(`file://${path.join(__dirname, "../dist/index.html")}`);
};
</code></pre> 
  <h3>(3)打包配置</h3> 
  <p>在 <code>package.json</code> 中添加如下配置,可根据需求调整:</p> 
  <blockquote> 
   <p>参考:<br> https://www.electron.build/#quick-setup-guide<br> https://docs.npmjs.com/cli/v9/configuring-npm/package-json?v=true</p> 
  </blockquote> 
  <pre><code>{
  "name": "electron_vue3_test",
  "description": "Electron + Vue3 开发桌面应用",
  "author": "喵喵喵",
  "version": "1.0.0",
  "scripts": {
    // ...
    "electron:build": "npm run build && electron-builder",
    "postinstall": "electron-builder install-app-deps"
  },
  // ...
  "build":{
    "appId": "your.id",
    "productName": "Electron + Vue3 开发桌面应用",
    "copyright": "Copyright © year ${author}",
    "directories": {
      "output": "app_client"
    },
    "mac": {
      "category": "public.app-category.music",
      "icon": "build/icon.icns",
      "target": [
        {
          "target": "dmg",
          "arch": [
            "x64"
          ]
        },
        {
          "target": "zip",
          "arch": [
            "x64"
          ]
        }
      ]
    },
    "win": {
      "icon": "build/icon.ico",
      "target": [
        {
          "target": "nsis",
          "arch": [
            "x64",
            "ia32"
          ]
        }
      ]
    },
    "nsis": {
      "oneClick": false,
      "allowToChangeInstallationDirectory": true,
      "installerIcon": "build/icon.ico",
      "uninstallerIcon": "build/icon.ico"
    }
  }
}
</code></pre> 
  <h3>(3)打包</h3> 
  <p>打开终端,执行 <code>npm run electron:build</code> 打包,打包后的应用程序在 <code>app_client</code> 目录下,<code>.dmg</code> 文件为 mac 的安装包,如下所示:<br> <a href="http://img.e-com-net.com/image/info8/62a486b7d99b43c9a3da71bdff337342.jpg" target="_blank"><img src="http://img.e-com-net.com/image/info8/62a486b7d99b43c9a3da71bdff337342.jpg" alt="Electron + Vue3 开发桌面应用_第4张图片" width="650" height="376" style="border:1px solid black;"></a></p> 
  <h2>3、其它问题</h2> 
  <h3>(1)web和客户端同时部署</h3> 
  <p>如果要一套代码部署在web端和客户端,还需要再做些处理。</p> 
  <p>在根目录下新增以下环境变量文件:<br> <code>.env.development</code> 文件,web 端开发环境配置,文件内容如下:</p> 
  <pre><code>VITE_PLATFORM = 'web'
</code></pre> 
  <p><code>.env.production</code> 文件,web 端生产环境配置,文件内容如下:</p> 
  <pre><code>VITE_PLATFORM = 'web'
</code></pre> 
  <p><code>.env.electron_production</code> 文件,客户端生产环境配置,文件内容如下:</p> 
  <pre><code>VITE_PLATFORM = 'electron'
</code></pre> 
  <p>修改 <code>package.json</code> 文件 <code>scripts</code> 下的 <code>electron:build</code> 命令为 <code>vite build --mode electron_production && electron-builder</code>。</p> 
  <p>修改 <code>vite.config.js</code> 文件如下:</p> 
  <pre><code>import { defineConfig, loadEnv } from "vite";

export default ({ mode }) => {
  const isWeb = loadEnv(mode, process.cwd()).VITE_PLATFORM === "web";

  const config = {
    base: isWeb ? "/" : "./",
    // ...
  };
  return defineConfig(config);
};
</code></pre> 
  <p>如果图片、css等资源文件需上传cdn,<code>vite.config.js</code> 文件还要增加如下代码:</p> 
  <pre><code>// ...
export default ({ mode }) => {
  const isWeb = loadEnv(mode, process.cwd()).VITE_PLATFORM === "web";
  const config = {
  // ...
  }
  if (isWeb) {
    config.experimental = {
      renderBuiltUrl(filename: string, { type }: { type: "public" | "asset" }) {
        if (type === "asset") {
          return "https://example.com/" + filename;
        }
      },
    };
  }
  return defineConfig(config);
};
</code></pre> 
  <h3>(2)刷新页面后报错问题</h3> 
  <p>我的方法是监听加载失败和导航事件,使用 <code>loadURL</code> 重新加载页面,并禁止刷新页面的快捷键。</p> 
  <p>在 <code>electron/main.js</code> 中判断如果是生产环境则做处理:</p> 
  <pre><code>// electron/main.js
const { app, BrowserWindow } = require("electron");

const isPackaged = app.isPackaged;
let mainWindow;

const createWindow = () => {
  mainWindow = new BrowserWindow({
  // ...
  });

  // 加载文件
  function load() {
    mainWindow.loadURL(
      isPackaged
        ? `file://${path.join(__dirname, "../dist/index.html")}`
        : "https://example.com/"
    );
  }

  if (isPackaged) {
    // 生产环境下,load 的是 html 文件,要做特殊处理。
    // 加载失败之后触发
    mainWindow.webContents.on("did-fail-load", () => {
      load();
    });

    // 当用户或页面想要导航时触发。
    // 它可能发生在 window.location 对象改变或用户点击页面上的链接时,可能会发生这种情况。
    // 当使用如 webContents.loadURL 和 webContents.back APIs 以编程方式导航时,将不会触发此事件。
    // 页面内导航也不会触发,例如点击锚点或更新 window.location.hash。 可使用 did-navigate-in-page 事件。
    mainWindow.webContents.on("will-navigate", (event, url) => {
      event.preventDefault();
      load();
    });
    
    // 禁止使用快捷键刷新
    mainWindow.webContents.on("before-input-event", (event, input) => {
      mainWindow.webContents.setIgnoreMenuShortcuts(
        input.key.toLowerCase() === "f5" ||
        (input.control && input.key.toLowerCase() === "r") ||
        (input.meta && input.key.toLowerCase() === "r")
      );
    });
  }

  load();
};

</code></pre> 
  <h3>(3)不带协议的 url 被解析为 file 协议问题</h3> 
  <p>项目接口中有很多不带协议名的 url 链接,应解析为 https 协议,但打包后被解析为 file 协议,在 <code>onBeforeRequest</code> 方法中对 url 做处理:</p> 
  <pre><code>// electron/main.js
const { app, BrowserWindow } = require("electron");

const isPackaged = app.isPackaged;
let mainWindow;

const createWindow = () => {
  mainWindow = new BrowserWindow({
  // ...
  });

  if (isPackaged) {
    // 将 file://example.com/xxx 的 文件改为 https 协议
    session.defaultSession.webRequest.onBeforeRequest((details, callback) => {
      if (/^file:\/\/example\.com\//.test(details.url)) {
        callback({ redirectURL: details.url.replace(/^file/, "https") });
      } else {
        callback(details);
      }
    });
  }
};
</code></pre> 
  <h3>(4)存取 <code>cookie</code> 问题</h3> 
  <p>网站有些功能用到了 <code>cookie</code> ,但打包后的应用程序读取的 <code>html</code> 文件为 <code>file</code> 协议,存取 <code>cookie</code> 会有问题。</p> 
  <p>第一问题是请求接口时获取 cookie 和接收服务端响应时设置 cookie 的问题,我解决的办法是在 <code>onBeforeSendHeaders</code> 和 <code>onHeadersReceived</code> 方法中对 <code>cookie</code> 做处理,在网站域名下存取 <code>cookie</code>。</p> 
  <p>在 <code>electron</code> 目录下新建 <code>utils.js</code> 文件,内容如下:</p> 
  <pre><code>const { session } = require("electron");

// 将响应头中的 set-cookie 转成 set() 方法需要的 details 对象
function getCookieDetails(cookie) {
  const details = {
    url: "https://example.com/",
  };
  cookie = cookie.split("; ");
  cookie.forEach((item, index) => {
    item = item.split("=");
    if (index === 0) {
      details.name = item[0];
      details.value = item[1];
    } else if (["domain", "path"].includes(item[0])) {
      details[item[0]] = item[1];
    } else if (item[0] == "expires") {
      details.expirationDate = new Date(item[1]).getTime() / 1000;
    } else if (item[0] == "HttpOnly") {
      details.httpOnly = true;
    }
    // HttpOnly 没有 item[1]  value 设为 true
  });
  return details;
}

module.exports = {
  // 是否是本站接口
  isWeapi: /^https:\/\/example\.com\/api\//,
  // 设置 cookies
  setCookies: function (cookies) {
    const setCookiePromise = [];

    cookies.forEach((cookie) => {
      const details = getCookieDetails(cookie);
      setCookiePromise.push(session.defaultSession.cookies.set(details));
    });

    return Promise.all(setCookiePromise);
  },

  // 获取 cookies,传给后端
  getCookies: async function () {
    const cookies = await session.defaultSession.cookies.get({
      domain: ".example.com"
    });
    const cookiesArr = [];
    cookies.forEach((cookie) => {
      cookiesArr.push(`${cookie.name}=${cookie.value}`);
    });
    return cookiesArr.join("; ");
  },
};
</code></pre> 
  <p>在 <code>electron/main.js</code> 中添加如下代码:</p> 
  <pre><code>const { app, BrowserWindow, session } = require("electron");
const { isWeapi, setCookies, getCookies } = require("./utils.js");

const isPackaged = app.isPackaged;
let mainWindow;

const createWindow = () => {
  mainWindow = new BrowserWindow({
  // ...
  });

  if (isPackaged) {
    // 请求接口时,如果是本站接口,则获取 .example.com 中的 cookie,添加到请求头中
    session.defaultSession.webRequest.onBeforeSendHeaders(
      async (details, callback) => {
        if (isWeapi.test(details.url)) {
          const cookies = await getCookies();
          if (cookies) details.requestHeaders.cookie = cookies;
        }
        callback({ requestHeaders: details.requestHeaders });
      }
    );

    // 接受到服务端响应时,如果是本站接口,则获取响应头中的 set-cookie,添加到 .example.com 的 cookie 中
    session.defaultSession.webRequest.onHeadersReceived(
      async (details, callback) => {
        if (
          isWeapi.test(details.url) &&
          "responseHeaders" in details &&
          "set-cookie" in details.responseHeaders
        ) {
          await setCookies(details.responseHeaders["set-cookie"]);
        }
        callback({ responseHeaders: details.responseHeaders });
      }
    );
  }
};
</code></pre> 
  <p>第二个问题是 <code>document.cookie</code> 获取不到,我在项目中使用的是 js-cookie 这个插件,我的解决方案是封装一个 Cookie对象,根据当前的协议类型判断使用哪种方法,如果是 <code>file</code> 协议,具体代码如下:</p> 
  <pre><code>// TODO: 待更新
</code></pre> 
  <h3>(5)限制只能打开一个窗口</h3> 
  <p>使用 <code>requestSingleInstanceLock</code> 方法判断是否已有实例在运行,并监听 <code>second-instance</code> ,当运行第二个实例时,聚焦到主窗口,在 <code>electron/main.js</code> 中添加如下代码:</p> 
  <pre><code>// 限制只能打开一个窗口
const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) {
  app.quit();
} else {
  app.on("second-instance", () => {
    // 当运行第二个实例时,将会聚焦到 mainWindow 这个窗口
    if (mainWindow) {
      if (mainWindow.isMinimized()) mainWindow.restore();
      mainWindow.focus();
      // mainWindow.show();
    }
  });
  
  app.whenReady().then(() => {
  // ...
  });
}
</code></pre> 
  <h3>(6)关闭窗口时提示</h3> 
  <p>监听 <code>close</code> 事件,使用 <code>dialog</code> 弹出提示,在 <code>electron/main.js</code> 中添加如下代码:</p> 
  <pre><code>// electron/main.js
const { app, BrowserWindow, dialog } = require("electron");

const isPackaged = app.isPackaged;
let mainWindow;

const createWindow = () => {
  mainWindow = new BrowserWindow({
  // ...
  });
  
  // 在窗口要关闭的时候触发
  mainWindow.on("close", (e) => {
    e.preventDefault();
    dialog
      .showMessageBox(mainWindow, {
        type: "info",
        title: "退出提示",
        defaultId: 0,
        cancelId: 1,
        message: "确定要退出吗?",
        buttons: ["退出", "取消"],
      })
      .then((res) => {
        if (res.response === 0) {
          // e.preventDefault();
          // mainWindow.destroy();
          app.exit(0);
        }
      });
  });
};
</code></pre> 
  <hr> 
  <h1>三、代码签名</h1> 
  <blockquote> 
   <p>参考:https://www.electron.build/code-signing</p> 
  </blockquote> 
  <h3>(1)windows</h3> 
  <p>windows 的证书需要找有资质的商家购买,在此不做说明,只说一下签名配置。</p> 
  <p>在 <code>package.json</code> 的 <code>build</code> 中添加如下代码,设置使用的签名算法,我用的是 <code>sha256</code>:</p> 
  <pre><code>{
  "build": {
    "win": {
      "signingHashAlgorithms": [
        "sha256"
      ]
    }
  }
}
</code></pre> 
  <p>设置环境变量,<code>CSC_LINK</code>(证书地址) 和 <code>CSC_KEY_PASSWORD</code>(证书密码)。运行 <code>npm run electron:build</code> 即可打包签名的客户端。</p> 
  <h3>(2)mac</h3> 
  <blockquote> 
   <p>参考:Notarizing your Electron application</p> 
  </blockquote> 
  <p>mac 代码签名需要申请 Apple Developer Program ,具体流程可参考苹果开发者公司账号申请全流程以及出现的问题,有问题就在客服页面打电话咨询客服。</p> 
  <p>Apple Developer Program 申请完成后,要生成签名证书,参考Mac Electron 应用的签名(signature)和公证(notarization)。</p> 
  <p>证书生成后,需要修改一下配置,使用 Electron Notarize 公证应用程序,打开终端,执行以下命令安装 Electron Notarize:</p> 
  <pre><code> npm install @electron/notarize --save-dev
</code></pre> 
  <p>浏览器打开 https://appleid.apple.com/account/manage,生成 Apple 专用密码。</p> 
  <p>打开终端,输入如下命令,其中 <code><USERNAME></code> 替换为你的 Apple Id,<code><PASSWORD></code> 替换为刚才生成的专用密码:</p> 
  <pre><code>security add-generic-password -a "<USERNAME>" -w "<PASSWORD>" -s "AC_PASSWORD"
</code></pre> 
  <p>在 <code>build</code> 目录下新建 <code>notarize.js</code> 文件,并添加如下代码:</p> 
  <pre><code>const { notarize } = require("@electron/notarize");

exports.default = async function packageTask(context) {
  const { electronPlatformName, appOutDir } = context;
  if (electronPlatformName !== "darwin") {
    return;
  }

  const appName = context.packager.appInfo.productFilename;

  return await notarize({
    appBundleId: "your.id",
    appPath: `${appOutDir}/${appName}.app`,
    appleId: "email@example.com",
    appleIdPassword: `@keychain:AC_PASSWORD`,
  });
};
</code></pre> 
  <p>在 <code>build</code> 目录下新建 <code>entitlements.mac.plist</code> 文件,并添加如下代码:</p> 
  <pre><code><?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>com.apple.security.cs.allow-jit</key>
    <true/>
    <key>com.apple.security.cs.debugger</key>
    <true/>
  </dict>
</plist>
</code></pre> 
  <p>在 <code>package.json</code> 的 <code>build</code> 中添加如下代码:</p> 
  <pre><code>{
  "build": {
    "afterSign": "build/notarize.js",
    "mac": {
      "hardenedRuntime": true,
      "gatekeeperAssess": false,
      "entitlements": "build/entitlements.mac.plist",
      "entitlementsInherit": "build/entitlements.mac.plist",
    }
  }
}
</code></pre> 
  <p>设置环境变量,<code>CSC_LINK</code>(证书地址) 和 <code>CSC_KEY_PASSWORD</code>(证书密码)。运行 <code>npm run electron:build</code> 即可打包签名的客户端。</p> 
  <hr> 
  <h1>四、自动更新</h1> 
  <blockquote> 
   <p>参考:Auto Update</p> 
  </blockquote> 
  <p>打开终端,执行以下命令安装 electron-updater:</p> 
  <pre><code>npm install electron-updater
</code></pre> 
  <p>在 <code>package.json</code> 的 <code>build</code> 中添加如下代码,其中 url 为客户端更新地址:</p> 
  <pre><code>{
  "build": {
    "publish": {
      "provider": "generic",
      "url": "https://example.com/app_client/",
      "channel": "latest"
    },
  }
}
</code></pre> 
  <p>注:mac 必须进行代码签名、target 同时启用 dmg 和 zip,才能自动更新</p> 
  <p>在 <code>electron/main.js</code> 文件中添加如下代码:</p> 
  <pre><code>const { app, BrowserWindow, dialog } = require("electron");
const { autoUpdater } = require("electron-updater");

const isPackaged = app.isPackaged;
let mainWindow;

const createWindow = () => {
  mainWindow = new BrowserWindow({
  // ...
  });

  // 自动更新
  function handleUpdate() {
    // 更新地址
    const updateURL = "https://js.houzi8.com/app_client/";
    // 设置是否自动下载,默认是true,当点击检测到新版本时,会自动下载安装包,所以设置为false
    autoUpdater.autoDownload = false;
    // 如果安装包下载好了,那么当应用退出后是否自动安装更新
    autoUpdater.autoInstallOnAppQuit = true;
    // 是否接受开发版,测试版之类的版本号
    autoUpdater.allowPrerelease = false;
    // 是否可以回退版本,比如从开发版降到旧的稳定版
    autoUpdater.allowDowngrade = false;
    autoUpdater.setFeedURL(updateURL);
    autoUpdater.on("error", function (error) {
      // 检查更新出错
    });
    autoUpdater.on("checking-for-update", function () {
      // 检查中
    });
    autoUpdater.on("update-not-available", function (info) {
      // 已经是最新版
    });
    autoUpdater.on("update-available", function (info) {
      // 检测到新版本
      dialog
        .showMessageBox(mainWindow, {
          type: "info",
          title: "更新提示",
          defaultId: 0,
          cancelId: 1,
          message: "检测到新版本,是否立即更新?",
          buttons: ["确定", "取消"],
        })
        .then((res) => {
          if (res.response === 0) {
            // 执行下载安装包
            autoUpdater.downloadUpdate();
          }
        });
    });
    autoUpdater.on("download-progress", function (progress) {
      // 下载进度
    });
    autoUpdater.on("update-downloaded", function (info) {
      // 新安装包下载完成
      dialog
        .showMessageBox(mainWindow, {
          type: "info",
          title: "更新提示",
          defaultId: 0,
          cancelId: 1,
          message: "新版本下载完成,是否立即安装?",
          buttons: ["确定", "取消"],
        })
        .then((res) => {
          if (res.response === 0) {
            // 退出应用并安装更新
            autoUpdater.quitAndInstall();
            mainWindow.destroy();
          }
        });
    });

    // 执行自动更新检查
    autoUpdater.checkForUpdates();
  }

  if (isPackaged) {
    handleUpdate();
  }
};
</code></pre> 
  <p>配置完成后,执行 <code>npm run electron:build</code> 打包,将打包生成的文件上传到 https://example.com/app_client/ 即可。<br> mac 下需上传的文件有:latest-mac.yml、Electron + Vue3 开发桌面应用-1.0.0-mac.zip、Electron + Vue3 开发桌面应用-1.0.0.dmg<br> windows 下需上传的文件有:latest.yml、Electron + Vue3 开发桌面应用 Setup 1.0.0.exe</p> 
  <hr> 
  <p><strong>参考文献:</strong></p> 
  <blockquote> 
   <p>Vite<br> Vue<br> Electron<br> electron-builder<br> concurrently<br> electron-vite-vue3-ts开发环境搭建<br> mainWindow.loadURL(“https://localhost:3000/”) show white screen on Electron app<br> vue热更新失效_vue-cli3热更新失效<br> package.json 文档<br> Notarizing your Electron application<br> Apple Developer Program<br> 苹果开发者公司账号申请全流程以及出现的问题<br> Mac Electron 应用的签名(signature)和公证(notarization)<br> Electron Notarize</p> 
  </blockquote> 
 </div> 
</div>����
                            </div>
                        </div>
                    </div>
                    <!--PC和WAP自适应版-->
                    <div id="SOHUCS" sid="1697922750951272448"></div>
                    <script type="text/javascript" src="/views/front/js/chanyan.js"></script>
                    <!-- 文章页-底部 动态广告位 -->
                    <div class="youdao-fixed-ad" id="detail_ad_bottom"></div>
                </div>
                <div class="col-md-3">
                    <div class="row" id="ad">
                        <!-- 文章页-右侧1 动态广告位 -->
                        <div id="right-1" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_1"> </div>
                        </div>
                        <!-- 文章页-右侧2 动态广告位 -->
                        <div id="right-2" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_2"></div>
                        </div>
                        <!-- 文章页-右侧3 动态广告位 -->
                        <div id="right-3" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_3"></div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="container">
        <h4 class="pt20 mb15 mt0 border-top">你可能感兴趣的:(Electron,electron,javascript,vue.js)</h4>
        <div id="paradigm-article-related">
            <div class="recommend-post mb30">
                <ul class="widget-links">
                    <li><a href="/article/1835512920797179904.htm"
                           title="element实现动态路由+面包屑" target="_blank">element实现动态路由+面包屑</a>
                        <span class="text-muted">软件技术NINI</span>
<a class="tag" taget="_blank" href="/search/vue%E6%A1%88%E4%BE%8B/1.htm">vue案例</a><a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a>
                        <div>el-breadcrumb是ElementUI组件库中的一个面包屑导航组件,它用于显示当前页面的路径,帮助用户快速理解和导航到应用的各个部分。在Vue.js项目中,如果你已经安装了ElementUI,就可以很方便地使用el-breadcrumb组件。以下是一个基本的使用示例:安装ElementUI(如果你还没有安装的话):你可以通过npm或yarn来安装ElementUI。bash复制代码npmi</div>
                    </li>
                    <li><a href="/article/1835509897106649088.htm"
                           title="Long类型前后端数据不一致" target="_blank">Long类型前后端数据不一致</a>
                        <span class="text-muted">igotyback</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a>
                        <div>响应给前端的数据浏览器控制台中response中看到的Long类型的数据是正常的到前端数据不一致前后端数据类型不匹配是一个常见问题,尤其是当后端使用Java的Long类型(64位)与前端JavaScript的Number类型(最大安全整数为2^53-1,即16位)进行数据交互时,很容易出现精度丢失的问题。这是因为JavaScript中的Number类型无法安全地表示超过16位的整数。为了解决这个问</div>
                    </li>
                    <li><a href="/article/1835498925755297792.htm"
                           title="DIV+CSS+JavaScript技术制作网页(旅游主题网页设计与制作)云南大理" target="_blank">DIV+CSS+JavaScript技术制作网页(旅游主题网页设计与制作)云南大理</a>
                        <span class="text-muted">STU学生网页设计</span>
<a class="tag" taget="_blank" href="/search/%E7%BD%91%E9%A1%B5%E8%AE%BE%E8%AE%A1/1.htm">网页设计</a><a class="tag" taget="_blank" href="/search/%E6%9C%9F%E6%9C%AB%E7%BD%91%E9%A1%B5%E4%BD%9C%E4%B8%9A/1.htm">期末网页作业</a><a class="tag" taget="_blank" href="/search/html%E9%9D%99%E6%80%81%E7%BD%91%E9%A1%B5/1.htm">html静态网页</a><a class="tag" taget="_blank" href="/search/html5%E6%9C%9F%E6%9C%AB%E5%A4%A7%E4%BD%9C%E4%B8%9A/1.htm">html5期末大作业</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E9%A1%B5%E8%AE%BE%E8%AE%A1/1.htm">网页设计</a><a class="tag" taget="_blank" href="/search/web%E5%A4%A7%E4%BD%9C%E4%B8%9A/1.htm">web大作业</a>
                        <div>️精彩专栏推荐作者主页:【进入主页—获取更多源码】web前端期末大作业:【HTML5网页期末作业(1000套)】程序员有趣的告白方式:【HTML七夕情人节表白网页制作(110套)】文章目录二、网站介绍三、网站效果▶️1.视频演示2.图片演示四、网站代码HTML结构代码CSS样式代码五、更多源码二、网站介绍网站布局方面:计划采用目前主流的、能兼容各大主流浏览器、显示效果稳定的浮动网页布局结构。网站程</div>
                    </li>
                    <li><a href="/article/1835496149843275776.htm"
                           title="关于城市旅游的HTML网页设计——(旅游风景云南 5页)HTML+CSS+JavaScript" target="_blank">关于城市旅游的HTML网页设计——(旅游风景云南 5页)HTML+CSS+JavaScript</a>
                        <span class="text-muted">二挡起步</span>
<a class="tag" taget="_blank" href="/search/web%E5%89%8D%E7%AB%AF%E6%9C%9F%E6%9C%AB%E5%A4%A7%E4%BD%9C%E4%B8%9A/1.htm">web前端期末大作业</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/html/1.htm">html</a><a class="tag" taget="_blank" href="/search/css/1.htm">css</a><a class="tag" taget="_blank" href="/search/%E6%97%85%E6%B8%B8/1.htm">旅游</a><a class="tag" taget="_blank" href="/search/%E9%A3%8E%E6%99%AF/1.htm">风景</a>
                        <div>⛵源码获取文末联系✈Web前端开发技术描述网页设计题材,DIV+CSS布局制作,HTML+CSS网页设计期末课程大作业|游景点介绍|旅游风景区|家乡介绍|等网站的设计与制作|HTML期末大学生网页设计作业,Web大学生网页HTML:结构CSS:样式在操作方面上运用了html5和css3,采用了div+css结构、表单、超链接、浮动、绝对定位、相对定位、字体样式、引用视频等基础知识JavaScrip</div>
                    </li>
                    <li><a href="/article/1835496148601761792.htm"
                           title="HTML网页设计制作大作业(div+css) 云南我的家乡旅游景点 带文字滚动" target="_blank">HTML网页设计制作大作业(div+css) 云南我的家乡旅游景点 带文字滚动</a>
                        <span class="text-muted">二挡起步</span>
<a class="tag" taget="_blank" href="/search/web%E5%89%8D%E7%AB%AF%E6%9C%9F%E6%9C%AB%E5%A4%A7%E4%BD%9C%E4%B8%9A/1.htm">web前端期末大作业</a><a class="tag" taget="_blank" href="/search/web%E8%AE%BE%E8%AE%A1%E7%BD%91%E9%A1%B5%E8%A7%84%E5%88%92%E4%B8%8E%E8%AE%BE%E8%AE%A1/1.htm">web设计网页规划与设计</a><a class="tag" taget="_blank" href="/search/html/1.htm">html</a><a class="tag" taget="_blank" href="/search/css/1.htm">css</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/dreamweaver/1.htm">dreamweaver</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a>
                        <div>Web前端开发技术描述网页设计题材,DIV+CSS布局制作,HTML+CSS网页设计期末课程大作业游景点介绍|旅游风景区|家乡介绍|等网站的设计与制作HTML期末大学生网页设计作业HTML:结构CSS:样式在操作方面上运用了html5和css3,采用了div+css结构、表单、超链接、浮动、绝对定位、相对定位、字体样式、引用视频等基础知识JavaScript:做与用户的交互行为文章目录前端学习路线</div>
                    </li>
                    <li><a href="/article/1835492740536823808.htm"
                           title="node.js学习" target="_blank">node.js学习</a>
                        <span class="text-muted">小猿L</span>
<a class="tag" taget="_blank" href="/search/node.js/1.htm">node.js</a><a class="tag" taget="_blank" href="/search/node.js/1.htm">node.js</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/vim/1.htm">vim</a>
                        <div>node.js学习实操及笔记温故node.js,node.js学习实操过程及笔记~node.js学习视频node.js官网node.js中文网实操笔记githubcsdn笔记为什么学node.js可以让别人访问我们编写的网页为后续的框架学习打下基础,三大框架vuereactangular离不开node.jsnode.js是什么官网:node.js是一个开源的、跨平台的运行JavaScript的运行</div>
                    </li>
                    <li><a href="/article/1835448239864770560.htm"
                           title="JavaScript 中,深拷贝(Deep Copy)和浅拷贝(Shallow Copy)" target="_blank">JavaScript 中,深拷贝(Deep Copy)和浅拷贝(Shallow Copy)</a>
                        <span class="text-muted">跳房子的前端</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/1.htm">前端面试</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/ecmascript/1.htm">ecmascript</a>
                        <div>在JavaScript中,深拷贝(DeepCopy)和浅拷贝(ShallowCopy)是用于复制对象或数组的两种不同方法。了解它们的区别和应用场景对于避免潜在的bugs和高效地处理数据非常重要。以下是对深拷贝和浅拷贝的详细解释,包括它们的概念、用途、优缺点以及实现方式。1.浅拷贝(ShallowCopy)概念定义:浅拷贝是指创建一个新的对象或数组,其中包含了原对象或数组的基本数据类型的值和对引用数</div>
                    </li>
                    <li><a href="/article/1835437775344726016.htm"
                           title="博客网站制作教程" target="_blank">博客网站制作教程</a>
                        <span class="text-muted">2401_85194651</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/maven/1.htm">maven</a>
                        <div>首先就是技术框架:后端:Java+SpringBoot数据库:MySQL前端:Vue.js数据库连接:JPA(JavaPersistenceAPI)1.项目结构blog-app/├──backend/│├──src/main/java/com/example/blogapp/││├──BlogApplication.java││├──config/│││└──DatabaseConfig.java</div>
                    </li>
                    <li><a href="/article/1835428948339683328.htm"
                           title="JavaScript `Map` 和 `WeakMap`详细解释" target="_blank">JavaScript `Map` 和 `WeakMap`详细解释</a>
                        <span class="text-muted">跳房子的前端</span>
<a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/%E5%8E%9F%E7%94%9F%E6%96%B9%E6%B3%95/1.htm">原生方法</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>在JavaScript中,Map和WeakMap都是用于存储键值对的数据结构,但它们有一些关键的不同之处。MapMap是一种可以存储任意类型的键值对的集合。它保持了键值对的插入顺序,并且可以通过键快速查找对应的值。Map提供了一些非常有用的方法和属性来操作这些数据对:set(key,value):将一个键值对添加到Map中。如果键已经存在,则更新其对应的值。get(key):获取指定键的值。如果键</div>
                    </li>
                    <li><a href="/article/1835419870070665216.htm"
                           title="切换淘宝最新npm镜像源是" target="_blank">切换淘宝最新npm镜像源是</a>
                        <span class="text-muted">hai40587</span>
<a class="tag" taget="_blank" href="/search/npm/1.htm">npm</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/node.js/1.htm">node.js</a>
                        <div>切换淘宝最新npm镜像源是一个相对简单的过程,但首先需要明确当前淘宝npm镜像源的状态和最新的镜像地址。由于网络环境和服务更新,镜像源的具体地址可能会发生变化,因此,我将基于当前可获取的信息,提供一个通用的切换步骤,并附上最新的镜像地址(截至回答时)。一、了解npm镜像源npm(NodePackageManager)是JavaScript的包管理器,用于安装、更新和管理项目依赖。由于npm官方仓库</div>
                    </li>
                    <li><a href="/article/1835383919906746368.htm"
                           title="高性能javascript--算法和流程控制" target="_blank">高性能javascript--算法和流程控制</a>
                        <span class="text-muted">海淀萌狗</span>

                        <div>-for,while和do-while性能相当-避免使用for-in循环,==除非遍历一个属性量未知的对象==es5:for-in遍历的对象便不局限于数组,还可以遍历对象。原因:for-in每次迭代操作会同时搜索实例或者原型属性,for-in循环的每次迭代都会产生更多开销,因此要比其他循环类型慢,一般速度为其他类型循环的1/7。因此,除非明确需要迭代一个属性数量未知的对象,否则应避免使用for-i</div>
                    </li>
                    <li><a href="/article/1835373236217540608.htm"
                           title="360前端星计划-动画可以这么玩" target="_blank">360前端星计划-动画可以这么玩</a>
                        <span class="text-muted">马小蜗</span>

                        <div>动画的基本原理定时器改变对象的属性根据新的属性重新渲染动画functionupdate(context){//更新属性}constticker=newTicker();ticker.tick(update,context);动画的种类1、JavaScript动画操作DOMCanvas2、CSS动画transitionanimation3、SVG动画SMILJS动画的优缺点优点:灵活度、可控性、性能</div>
                    </li>
                    <li><a href="/article/1835360244646113280.htm"
                           title="JavaScript中秋快乐!" target="_blank">JavaScript中秋快乐!</a>
                        <span class="text-muted">Q_w7742</span>
<a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/ecmascript/1.htm">ecmascript</a>
                        <div>我们来实现一个简单的祝福网页~主要的难度在于使用canvas绘图当点击canvas时候,跳出“中秋节快乐”字样,需要注册鼠标单击事件和计时器。首先定义主要函数:初始化当点击canvas之后转到onCanvasClick函数,绘图生成灯笼。functiononCanvasClick(){//事件处理函数context.clearRect(0,0,canvas1.width,canvas1.heigh</div>
                    </li>
                    <li><a href="/article/1835359727924637696.htm"
                           title="Nginx从入门到实践(三)" target="_blank">Nginx从入门到实践(三)</a>
                        <span class="text-muted">听你讲故事啊</span>

                        <div>动静分离动静分离是将网站静态资源(JavaScript,CSS,img等文件)与后台应用分开部署,提高用户访问静态代码的速度,降低对后台应用访问。动静分离的一种做法是将静态资源部署在nginx上,后台项目部署到应用服务器上,根据一定规则静态资源的请求全部请求nginx服务器,达到动静分离的目标。rewrite规则Rewrite规则常见正则表达式Rewrite主要的功能就是实现URL的重写,Ngin</div>
                    </li>
                    <li><a href="/article/1835354700392787968.htm"
                           title="Nginx的使用场景:构建高效、可扩展的Web架构" target="_blank">Nginx的使用场景:构建高效、可扩展的Web架构</a>
                        <span class="text-muted">张某布响丸辣</span>
<a class="tag" taget="_blank" href="/search/nginx/1.htm">nginx</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/%E6%9E%B6%E6%9E%84/1.htm">架构</a>
                        <div>Nginx,作为当今最流行的Web服务器和反向代理软件之一,凭借其高性能、稳定性和灵活性,在众多Web项目中扮演着核心角色。无论是个人博客、中小型网站,还是大型企业级应用,Nginx都能提供强大的支持。本文将探讨Nginx的几个主要使用场景,帮助读者理解如何在实际项目中充分利用Nginx的优势。1.静态文件服务对于包含大量静态文件(如HTML、CSS、JavaScript、图片等)的网站,Ngin</div>
                    </li>
                    <li><a href="/article/1835354447627251712.htm"
                           title="前端知识点" target="_blank">前端知识点</a>
                        <span class="text-muted">ZhangTao_zata</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/css/1.htm">css</a>
                        <div>下面是一个最基本的html代码body{font-family:Arial,sans-serif;margin:20px;}//JavaScriptfunctionthatdisplaysanalertwhencalledfunctionshowMessage(){alert("Hello!Youclickedthebutton.");}MyFirstHTMLPageWelcometoMyPage</div>
                    </li>
                    <li><a href="/article/1835332133149831168.htm"
                           title="2024年最全Flutter如何和Native通信-Android视角,Electron开发Android界面" target="_blank">2024年最全Flutter如何和Native通信-Android视角,Electron开发Android界面</a>
                        <span class="text-muted">2401_84544531</span>
<a class="tag" taget="_blank" href="/search/%E7%A8%8B%E5%BA%8F%E5%91%98/1.htm">程序员</a><a class="tag" taget="_blank" href="/search/android/1.htm">android</a><a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95/1.htm">面试</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a>
                        <div>总结【Android详细知识点思维脑图(技能树)】其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。虽然Android没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明Android中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪</div>
                    </li>
                    <li><a href="/article/1835331376895848448.htm"
                           title="【JS】前端文件读取FileReader操作总结" target="_blank">【JS】前端文件读取FileReader操作总结</a>
                        <span class="text-muted">程序员-张师傅</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>前端文件读取FileReader操作总结FileReader是JavaScript中的一个WebAPI,它允许web应用程序异步读取用户计算机上的文件(或原始数据缓冲区)的内容,例如读取文件以获取其内容,并在不将文件发送到服务器的情况下在客户端使用它。这对于处理图片、文本文件等非常有用,尤其是当你想要在用户界面中即时显示文件内容或进行文件预览时。创建FileReader对象首先,你需要创建一个Fi</div>
                    </li>
                    <li><a href="/article/1835331375377510400.htm"
                           title="【前端】vue 报错:The template root requires exactly one element" target="_blank">【前端】vue 报错:The template root requires exactly one element</a>
                        <span class="text-muted">程序员-张师傅</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a>
                        <div>【前端】vue报错:Thetemplaterootrequiresexactlyoneelement在Vue.js中,当你遇到错误“Thetemplaterootrequiresexactlyoneelement”时,这通常意味着你的Vue组件的模板(template)根节点不是单一的元素。Vue要求每个组件的模板必须有一个根元素来包裹所有的子元素。这个错误通常出现在以下几种情况:模板中有多个并行</div>
                    </li>
                    <li><a href="/article/1835298981362626560.htm"
                           title="webstorm报错TypeError: this.cliEngine is not a constructor" target="_blank">webstorm报错TypeError: this.cliEngine is not a constructor</a>
                        <span class="text-muted">Blue_Color</span>

                        <div>点击Details在控制台会显示报错的位置TypeError:this.cliEngineisnotaconstructoratESLintPlugin.invokeESLint(/Applications/RubyMine.app/Contents/plugins/JavaScriptLanguage/languageService/eslint/bin/eslint-plugin.js:97:</div>
                    </li>
                    <li><a href="/article/1835296397365178368.htm"
                           title="创建一个完整的购物商城系统是一个复杂的项目,涉及前端(用户界面)、后端(服务器逻辑)、数据库等多个部分。由于篇幅限制,我无法在这里提供一个完整的系统代码,但我可以分别给出一些关键部分的示例代码,涵盖几" target="_blank">创建一个完整的购物商城系统是一个复杂的项目,涉及前端(用户界面)、后端(服务器逻辑)、数据库等多个部分。由于篇幅限制,我无法在这里提供一个完整的系统代码,但我可以分别给出一些关键部分的示例代码,涵盖几</a>
                        <span class="text-muted">uthRaman</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/ui/1.htm">ui</a><a class="tag" taget="_blank" href="/search/%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">服务器</a>
                        <div>前端(HTML/CSS/JavaScript)grsyzp.cnHTML页面结构(index.html)html购物商城欢迎来到购物商城JavaScript(Ajax请求商品数据,app.js)javascriptdocument.addEventListener('DOMContentLoaded',function(){fetch('/api/products').then(response=</div>
                    </li>
                    <li><a href="/article/1835293121953492992.htm"
                           title="了解 UNPKG:前端开发者的包管理利器" target="_blank">了解 UNPKG:前端开发者的包管理利器</a>
                        <span class="text-muted">小于负无穷</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/typescript/1.htm">typescript</a><a class="tag" taget="_blank" href="/search/css/1.htm">css</a><a class="tag" taget="_blank" href="/search/html5/1.htm">html5</a><a class="tag" taget="_blank" href="/search/node.js/1.htm">node.js</a>
                        <div>在现代前端开发中,JavaScript包管理和模块化是至关重要的,而npm则是最流行的JavaScript包管理器之一。不过,随着前端项目复杂性的增加,有时候我们希望快速引入外部依赖,而无需本地安装和构建。此时,CDN(内容分发网络)成为了一种方便快捷的解决方案,而UNPKG就是这种方式中的佼佼者。什么是UNPKG?UNPKG是一个基于npm的内容分发网络(CDN),它允许开发者直接通过URL从n</div>
                    </li>
                    <li><a href="/article/1835249513875468288.htm"
                           title="说说在 Vue.js 中如何实现组件间通信" target="_blank">说说在 Vue.js 中如何实现组件间通信</a>
                        <span class="text-muted">deniro</span>

                        <div>1用法假设父组件的模板包含子组件,我们可以通过props来正向地把数据从父组件传递给子组件。props可以是字符串数组,也可以是对象。html:js:Vue.component('deniro-component',{props:['message'],template:'{{message}}'});varapp=newVue({el:'#app',data:{}});渲染结果:"嫦娥四号"成功</div>
                    </li>
                    <li><a href="/article/1835247344719851520.htm"
                           title="2019-05-29 vue-router的两种模式的区别" target="_blank">2019-05-29 vue-router的两种模式的区别</a>
                        <span class="text-muted">Kason晨</span>

                        <div>1、大家都知道vue是一种单页应用,单页应用就是仅在页面初始化的时候加载相应的html/css/js一单页面加载完成,不会因为用户的操作而进行页面的重新加载或者跳转,用javascript动态的变化html的内容优点:良好的交互体验,用户不需要刷新页面,页面显示流畅,良好的前后端工作分离模式,减轻服务器压力,缺点:不利于SEO,初次加载耗时比较多2、hash模式vue-router默认的是hash</div>
                    </li>
                    <li><a href="/article/1835237282664247296.htm"
                           title="【EI会议征稿通知】2024电子电路与电子技术国际学术研讨会(ECET 2024)" target="_blank">【EI会议征稿通知】2024电子电路与电子技术国际学术研讨会(ECET 2024)</a>
                        <span class="text-muted">小鹿学姐</span>
<a class="tag" taget="_blank" href="/search/%E4%BF%A1%E6%81%AF%E4%B8%8E%E9%80%9A%E4%BF%A1/1.htm">信息与通信</a><a class="tag" taget="_blank" href="/search/%E9%87%8F%E5%AD%90%E8%AE%A1%E7%AE%97/1.htm">量子计算</a><a class="tag" taget="_blank" href="/search/5G/1.htm">5G</a>
                        <div>2024电子电路与电子技术国际学术研讨会(ECET2024)2024InternationalSymposiumonElectronicCircuitandElectronicTechnology2024电子电路与电子技术国际学术研讨会(ECET2024)将于2024年11月8-10日在中国·南京隆重召开。本次会议将集中讨论电子电路与电子技术的最新研究成果,旨在建立一个高水平的学术交流平台,以便领</div>
                    </li>
                    <li><a href="/article/1835216109297561600.htm"
                           title="electron多标签页模式更像客户端" target="_blank">electron多标签页模式更像客户端</a>
                        <span class="text-muted">diygwcom</span>
<a class="tag" taget="_blank" href="/search/electron/1.htm">electron</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a>
                        <div>Electron多标签页模式是指在Electron框架中实现的类似Web浏览器的多标签页功能。Electron是一个使用Web技术(HTML、CSS和JavaScript)来创建跨平台桌面应用程序的框架。在Electron中实现多标签页模式,通常需要借助一些特定的库或组件,如BrowserView或electron-tabs,或者通过自定义实现。实现方式1.使用BrowserViewBrowser</div>
                    </li>
                    <li><a href="/article/1835194426499100672.htm"
                           title="外卖霸王餐返利外卖会员卡小程序开发" target="_blank">外卖霸王餐返利外卖会员卡小程序开发</a>
                        <span class="text-muted">闹小艾</span>
<a class="tag" taget="_blank" href="/search/good506070/1.htm">good506070</a><a class="tag" taget="_blank" href="/search/%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F/1.htm">微信小程序</a><a class="tag" taget="_blank" href="/search/%E5%B0%8F%E7%A8%8B%E5%BA%8F/1.htm">小程序</a>
                        <div>外卖霸王餐返利外卖会员卡小程序开发"社交电商赋能下的外卖返利小程序"是专为商家与用户双赢而设计的创新平台。以下是其开发方案的详细步骤:一、需求梳理:首先,我们需要明确小程序的核心功能和特色。包括设定活动类型、返利策略,以及用户体验友好的界面设计。二、技术决策:技术选型是关键。我们采用小程序的开发框架,利用JavaScript作为前端开发语言,并结合微信提供的API进行后端接口调用与数据处理。三、账</div>
                    </li>
                    <li><a href="/article/1835192531881652224.htm"
                           title="Axure设计之全屏与退出全屏交互实现" target="_blank">Axure设计之全屏与退出全屏交互实现</a>
                        <span class="text-muted">招风的黑耳</span>
<a class="tag" taget="_blank" href="/search/Axure/1.htm">Axure</a><a class="tag" taget="_blank" href="/search/axure/1.htm">axure</a><a class="tag" taget="_blank" href="/search/%E4%BA%A4%E4%BA%92/1.htm">交互</a>
                        <div>在AxureRP中,设计全屏与退出全屏的交互功能可以极大地提升用户体验,尤其是在展示产品原型或进行演示时。本文将详细介绍如何在AxureRP中通过结合JavaScript代码实现全屏与退出全屏的交互效果。Axure原型设计web端交互元件库:https://1zvcwx.axshare.com一、设计思路全屏与退出全屏的交互设计主要依赖于JavaScript代码来控制浏览器的全屏模式。在Axure</div>
                    </li>
                    <li><a href="/article/1835162742533746688.htm"
                           title="全面指南:用户行为从前端数据采集到实时处理的最佳实践" target="_blank">全面指南:用户行为从前端数据采集到实时处理的最佳实践</a>
                        <span class="text-muted">数字沉思</span>
<a class="tag" taget="_blank" href="/search/%E8%90%A5%E9%94%80/1.htm">营销</a><a class="tag" taget="_blank" href="/search/%E6%B5%81%E9%87%8F%E8%BF%90%E8%90%A5/1.htm">流量运营</a><a class="tag" taget="_blank" href="/search/%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84/1.htm">系统架构</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/%E5%86%85%E5%AE%B9%E8%BF%90%E8%90%A5/1.htm">内容运营</a><a class="tag" taget="_blank" href="/search/%E5%A4%A7%E6%95%B0%E6%8D%AE/1.htm">大数据</a>
                        <div>引言在当今的数据驱动世界,实时数据采集和处理已经成为企业做出及时决策的重要手段。本文将详细介绍如何通过前端JavaScript代码采集用户行为数据、利用API和Kafka进行数据传输、通过Flink实时处理数据的完整流程。无论你是想提升产品体验还是做用户行为分析,这篇文章都将为你提供全面的解决方案。设计一个通用的ClickHouse表来存储用户事件时,需要考虑多种因素,包括事件类型、时间戳、用户信</div>
                    </li>
                    <li><a href="/article/1835154546289111040.htm"
                           title="EcmaScript和JavaScript的区别" target="_blank">EcmaScript和JavaScript的区别</a>
                        <span class="text-muted">每天吃八顿</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/ecmascript/1.htm">ecmascript</a>
                        <div>ECMAScript和JavaScript是经常被混淆的两个术语,但实际上它们之间存在一些区别:ECMAScript:ECMAScript(通常缩写为ES,并且有版本号如ES5,ES6和ES7等)是由ECMA国际(EuropeanComputerManufacturersAssociation)制定的一种脚本语言的规范。这个规范定义了语法、命令、数据类型等基本元素。ECMAScript是一种规范,</div>
                    </li>
                                <li><a href="/article/15.htm"
                                       title="Spring4.1新特性——Spring MVC增强" target="_blank">Spring4.1新特性——Spring MVC增强</a>
                                    <span class="text-muted">jinnianshilongnian</span>
<a class="tag" taget="_blank" href="/search/spring+4.1/1.htm">spring 4.1</a>
                                    <div>目录 
Spring4.1新特性——综述 
Spring4.1新特性——Spring核心部分及其他 
Spring4.1新特性——Spring缓存框架增强 
Spring4.1新特性——异步调用和事件机制的异常处理 
Spring4.1新特性——数据库集成测试脚本初始化 
Spring4.1新特性——Spring MVC增强 
Spring4.1新特性——页面自动化测试框架Spring MVC T</div>
                                </li>
                                <li><a href="/article/142.htm"
                                       title="mysql 性能查询优化" target="_blank">mysql 性能查询优化</a>
                                    <span class="text-muted">annan211</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/sql/1.htm">sql</a><a class="tag" taget="_blank" href="/search/%E4%BC%98%E5%8C%96/1.htm">优化</a><a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a><a class="tag" taget="_blank" href="/search/%E5%BA%94%E7%94%A8%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">应用服务器</a>
                                    <div>

1 时间到底花在哪了?
  mysql在执行查询的时候需要执行一系列的子任务,这些子任务包含了整个查询周期最重要的阶段,这其中包含了大量为了
  检索数据列到存储引擎的调用以及调用后的数据处理,包括排序、分组等。在完成这些任务的时候,查询需要在不同的地方
  花费时间,包括网络、cpu计算、生成统计信息和执行计划、锁等待等。尤其是向底层存储引擎检索数据的调用操作。这些调用需要在内存操</div>
                                </li>
                                <li><a href="/article/269.htm"
                                       title="windows系统配置" target="_blank">windows系统配置</a>
                                    <span class="text-muted">cherishLC</span>
<a class="tag" taget="_blank" href="/search/windows/1.htm">windows</a>
                                    <div>删除Hiberfil.sys :使用命令powercfg -h off 关闭休眠功能即可: 
http://jingyan.baidu.com/article/f3ad7d0fc0992e09c2345b51.html 
类似的还有pagefile.sys 
 
msconfig 配置启动项 
shutdown 定时关机 
 
ipconfig 查看网络配置 
ipconfig /flushdns</div>
                                </li>
                                <li><a href="/article/396.htm"
                                       title="人体的排毒时间" target="_blank">人体的排毒时间</a>
                                    <span class="text-muted">Array_06</span>
<a class="tag" taget="_blank" href="/search/%E5%B7%A5%E4%BD%9C/1.htm">工作</a>
                                    <div>======================== 
||  人体的排毒时间是什么时候?|| 
========================  
 
转载于: 
http://zhidao.baidu.com/link?url=ibaGlicVslAQhVdWWVevU4TMjhiKaNBWCpZ1NS6igCQ78EkNJZFsEjCjl3T5EdXU9SaPg04bh8MbY1bR</div>
                                </li>
                                <li><a href="/article/523.htm"
                                       title="ZooKeeper" target="_blank">ZooKeeper</a>
                                    <span class="text-muted">cugfy</span>
<a class="tag" taget="_blank" href="/search/zookeeper/1.htm">zookeeper</a>
                                    <div>Zookeeper是一个高性能,分布式的,开源分布式应用协调服务。它提供了简单原始的功能,分布式应用可以基于它实现更高级的服务,比如同步, 配置管理,集群管理,名空间。它被设计为易于编程,使用文件系统目录树作为数据模型。服务端跑在java上,提供java和C的客户端API。 Zookeeper是Google的Chubby一个开源的实现,是高有效和可靠的协同工作系统,Zookeeper能够用来lea</div>
                                </li>
                                <li><a href="/article/650.htm"
                                       title="网络爬虫的乱码处理" target="_blank">网络爬虫的乱码处理</a>
                                    <span class="text-muted">随意而生</span>
<a class="tag" taget="_blank" href="/search/%E7%88%AC%E8%99%AB/1.htm">爬虫</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C/1.htm">网络</a>
                                    <div>下边简单总结下关于网络爬虫的乱码处理。注意,这里不仅是中文乱码,还包括一些如日文、韩文 、俄文、藏文之类的乱码处理,因为他们的解决方式 是一致的,故在此统一说明。     网络爬虫,有两种选择,一是选择nutch、hetriex,二是自写爬虫,两者在处理乱码时,原理是一致的,但前者处理乱码时,要看懂源码后进行修改才可以,所以要废劲一些;而后者更自由方便,可以在编码处理</div>
                                </li>
                                <li><a href="/article/777.htm"
                                       title="Xcode常用快捷键" target="_blank">Xcode常用快捷键</a>
                                    <span class="text-muted">张亚雄</span>
<a class="tag" taget="_blank" href="/search/xcode/1.htm">xcode</a>
                                    <div>一、总结的常用命令: 
 
    隐藏xcode command+h 
 
    退出xcode command+q 
 
    关闭窗口 command+w 
 
    关闭所有窗口 command+option+w 
 
    关闭当前</div>
                                </li>
                                <li><a href="/article/904.htm"
                                       title="mongoDB索引操作" target="_blank">mongoDB索引操作</a>
                                    <span class="text-muted">adminjun</span>
<a class="tag" taget="_blank" href="/search/mongodb/1.htm">mongodb</a><a class="tag" taget="_blank" href="/search/%E7%B4%A2%E5%BC%95/1.htm">索引</a>
                                    <div>一、索引基础:    MongoDB的索引几乎与传统的关系型数据库一模一样,这其中也包括一些基本的优化技巧。下面是创建索引的命令:    > db.test.ensureIndex({"username":1})    可以通过下面的名称查看索引是否已经成功建立: &nbs</div>
                                </li>
                                <li><a href="/article/1031.htm"
                                       title="成都软件园实习那些话" target="_blank">成都软件园实习那些话</a>
                                    <span class="text-muted">aijuans</span>
<a class="tag" taget="_blank" href="/search/%E6%88%90%E9%83%BD+%E8%BD%AF%E4%BB%B6%E5%9B%AD+%E5%AE%9E%E4%B9%A0/1.htm">成都 软件园 实习</a>
                                    <div>无聊之中,翻了一下日志,发现上一篇经历是很久以前的事了,悔过~~ 
  断断续续离开了学校快一年了,习惯了那里一天天的幼稚、成长的环境,到这里有点与世隔绝的感觉。不过还好,那是刚到这里时的想法,现在感觉在这挺好,不管怎么样,最要感谢的还是老师能给这么好的一次催化成长的机会,在这里确实看到了好多好多能想到或想不到的东西。 
  都说在外面和学校相比最明显的差距就是与人相处比较困难,因为在外面每个人都</div>
                                </li>
                                <li><a href="/article/1158.htm"
                                       title="Linux下FTP服务器安装及配置" target="_blank">Linux下FTP服务器安装及配置</a>
                                    <span class="text-muted">ayaoxinchao</span>
<a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/FTP%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">FTP服务器</a><a class="tag" taget="_blank" href="/search/vsftp/1.htm">vsftp</a>
                                    <div>检测是否安装了FTP 
[root@localhost ~]# rpm -q vsftpd 
如果未安装:package vsftpd is not installed  安装了则显示:vsftpd-2.0.5-28.el5累死的版本信息 
  
安装FTP 
运行yum install vsftpd命令,如[root@localhost ~]# yum install vsf</div>
                                </li>
                                <li><a href="/article/1285.htm"
                                       title="使用mongo-java-driver获取文档id和查找文档" target="_blank">使用mongo-java-driver获取文档id和查找文档</a>
                                    <span class="text-muted">BigBird2012</span>
<a class="tag" taget="_blank" href="/search/driver/1.htm">driver</a>
                                    <div>注:本文所有代码都使用的mongo-java-driver实现。 
  
在MongoDB中,一个集合(collection)在概念上就类似我们SQL数据库中的表(Table),这个集合包含了一系列文档(document)。一个DBObject对象表示我们想添加到集合(collection)中的一个文档(document),MongoDB会自动为我们创建的每个文档添加一个id,这个id在</div>
                                </li>
                                <li><a href="/article/1412.htm"
                                       title="JSONObject以及json串" target="_blank">JSONObject以及json串</a>
                                    <span class="text-muted">bijian1013</span>
<a class="tag" taget="_blank" href="/search/json/1.htm">json</a><a class="tag" taget="_blank" href="/search/JSONObject/1.htm">JSONObject</a>
                                    <div>一.JAR包简介 
    要使程序可以运行必须引入JSON-lib包,JSON-lib包同时依赖于以下的JAR包: 
    1.commons-lang-2.0.jar 
    2.commons-beanutils-1.7.0.jar 
    3.commons-collections-3.1.jar 
&n</div>
                                </li>
                                <li><a href="/article/1539.htm"
                                       title="[Zookeeper学习笔记之三]Zookeeper实例创建和会话建立的异步特性" target="_blank">[Zookeeper学习笔记之三]Zookeeper实例创建和会话建立的异步特性</a>
                                    <span class="text-muted">bit1129</span>
<a class="tag" taget="_blank" href="/search/zookeeper/1.htm">zookeeper</a>
                                    <div>为了说明问题,看个简单的代码, 
  
    import org.apache.zookeeper.*;  
      
    import java.io.IOException;  
    import java.util.concurrent.CountDownLatch;  
    import java.util.concurrent.ThreadLocal</div>
                                </li>
                                <li><a href="/article/1666.htm"
                                       title="【Scala十二】Scala核心六:Trait" target="_blank">【Scala十二】Scala核心六:Trait</a>
                                    <span class="text-muted">bit1129</span>
<a class="tag" taget="_blank" href="/search/scala/1.htm">scala</a>
                                    <div>Traits are a fundamental unit of code reuse in Scala. A trait encapsulates method and field definitions, which can then be reused by mixing them into classes. Unlike class inheritance, in which each c</div>
                                </li>
                                <li><a href="/article/1793.htm"
                                       title="weblogic version 10.3破解" target="_blank">weblogic version 10.3破解</a>
                                    <span class="text-muted">ronin47</span>
<a class="tag" taget="_blank" href="/search/weblogic/1.htm">weblogic</a>
                                    <div>版本:WebLogic Server 10.3 
 
说明:%DOMAIN_HOME%:指WebLogic Server 域(Domain)目录 
例如我的做测试的域的根目录 DOMAIN_HOME=D:/Weblogic/Middleware/user_projects/domains/base_domain 
 
1.为了保证操作安全,备份%DOMAIN_HOME%/security/Defa</div>
                                </li>
                                <li><a href="/article/1920.htm"
                                       title="求第n个斐波那契数" target="_blank">求第n个斐波那契数</a>
                                    <span class="text-muted">BrokenDreams</span>

                                    <div>        今天看到群友发的一个问题:写一个小程序打印第n个斐波那契数。 
        自己试了下,搞了好久。。。基础要加强了。 
        
  &nbs</div>
                                </li>
                                <li><a href="/article/2047.htm"
                                       title="读《研磨设计模式》-代码笔记-访问者模式-Visitor" target="_blank">读《研磨设计模式》-代码笔记-访问者模式-Visitor</a>
                                    <span class="text-muted">bylijinnan</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/1.htm">设计模式</a>
                                    <div>声明: 本文只为方便我个人查阅和理解,详细的分析以及源代码请移步 原作者的博客http://chjavach.iteye.com/ 
 
 


import java.util.ArrayList;
import java.util.List;

interface IVisitor {
	
	//第二次分派,Visitor调用Element
	void visitConcret</div>
                                </li>
                                <li><a href="/article/2174.htm"
                                       title="MatConvNet的excise 3改为网络配置文件形式" target="_blank">MatConvNet的excise 3改为网络配置文件形式</a>
                                    <span class="text-muted">cherishLC</span>
<a class="tag" taget="_blank" href="/search/matlab/1.htm">matlab</a>
                                    <div>MatConvNet为vlFeat作者写的matlab下的卷积神经网络工具包,可以使用GPU。 
主页:
http://www.vlfeat.org/matconvnet/ 
教程:
http://www.robots.ox.ac.uk/~vgg/practicals/cnn/index.html 
 
注意:需要下载新版的MatConvNet替换掉教程中工具包中的matconvnet:
http</div>
                                </li>
                                <li><a href="/article/2301.htm"
                                       title="ZK Timeout再讨论" target="_blank">ZK Timeout再讨论</a>
                                    <span class="text-muted">chenchao051</span>
<a class="tag" taget="_blank" href="/search/zookeeper/1.htm">zookeeper</a><a class="tag" taget="_blank" href="/search/timeout/1.htm">timeout</a><a class="tag" taget="_blank" href="/search/hbase/1.htm">hbase</a>
                                    <div>http://crazyjvm.iteye.com/blog/1693757 文中提到相关超时问题,但是又出现了一个问题,我把min和max都设置成了180000,但是仍然出现了以下的异常信息: 
Client session timed out, have not heard from server in 154339ms for sessionid 0x13a3f7732340003</div>
                                </li>
                                <li><a href="/article/2428.htm"
                                       title="CASE WHEN 用法介绍" target="_blank">CASE WHEN 用法介绍</a>
                                    <span class="text-muted">daizj</span>
<a class="tag" taget="_blank" href="/search/sql/1.htm">sql</a><a class="tag" taget="_blank" href="/search/group+by/1.htm">group by</a><a class="tag" taget="_blank" href="/search/case+when/1.htm">case when</a>
                                    <div>CASE WHEN 用法介绍 
 
1. CASE WHEN 表达式有两种形式 
 
 
--简单Case函数  
 
CASE sex  
WHEN '1' THEN '男'  
WHEN '2' THEN '女'  
ELSE '其他' END  
 
--Case搜索函数  
 
CASE 
WHEN sex = '1' THEN </div>
                                </li>
                                <li><a href="/article/2555.htm"
                                       title="PHP技巧汇总:提高PHP性能的53个技巧" target="_blank">PHP技巧汇总:提高PHP性能的53个技巧</a>
                                    <span class="text-muted">dcj3sjt126com</span>
<a class="tag" taget="_blank" href="/search/PHP/1.htm">PHP</a>
                                    <div>PHP技巧汇总:提高PHP性能的53个技巧  用单引号代替双引号来包含字符串,这样做会更快一些。因为PHP会在双引号包围的字符串中搜寻变量,  单引号则不会,注意:只有echo能这么做,它是一种可以把多个字符串当作参数的函数译注:  PHP手册中说echo是语言结构,不是真正的函数,故把函数加上了双引号)。  1、如果能将类的方法定义成static,就尽量定义成static,它的速度会提升将近4倍</div>
                                </li>
                                <li><a href="/article/2682.htm"
                                       title="Yii框架中CGridView的使用方法以及详细示例" target="_blank">Yii框架中CGridView的使用方法以及详细示例</a>
                                    <span class="text-muted">dcj3sjt126com</span>
<a class="tag" taget="_blank" href="/search/yii/1.htm">yii</a>
                                    <div>CGridView显示一个数据项的列表中的一个表。 
表中的每一行代表一个数据项的数据,和一个列通常代表一个属性的物品(一些列可能对应于复杂的表达式的属性或静态文本)。  CGridView既支持排序和分页的数据项。排序和分页可以在AJAX模式或正常的页面请求。使用CGridView的一个好处是,当用户浏览器禁用JavaScript,排序和分页自动退化普通页面请求和仍然正常运行。 
实例代码如下:</div>
                                </li>
                                <li><a href="/article/2809.htm"
                                       title="Maven项目打包成可执行Jar文件" target="_blank">Maven项目打包成可执行Jar文件</a>
                                    <span class="text-muted">dyy_gusi</span>
<a class="tag" taget="_blank" href="/search/assembly/1.htm">assembly</a>
                                    <div>Maven项目打包成可执行Jar文件 
在使用Maven完成项目以后,如果是需要打包成可执行的Jar文件,我们通过eclipse的导出很麻烦,还得指定入口文件的位置,还得说明依赖的jar包,既然都使用Maven了,很重要的一个目的就是让这些繁琐的操作简单。我们可以通过插件完成这项工作,使用assembly插件。具体使用方式如下: 
1、在项目中加入插件的依赖: 
<plugin>
	</div>
                                </li>
                                <li><a href="/article/2936.htm"
                                       title="php常见错误" target="_blank">php常见错误</a>
                                    <span class="text-muted">geeksun</span>
<a class="tag" taget="_blank" href="/search/PHP/1.htm">PHP</a>
                                    <div>1.  kevent() reported that connect() failed (61: Connection refused) while connecting to upstream, client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.1", upstream: "fastc</div>
                                </li>
                                <li><a href="/article/3063.htm"
                                       title="修改linux的用户名" target="_blank">修改linux的用户名</a>
                                    <span class="text-muted">hongtoushizi</span>
<a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/change+password/1.htm">change password</a>
                                    <div>Change Linux Username 
更改Linux用户名,需要修改4个系统的文件: 
/etc/passwd 
/etc/shadow 
/etc/group 
/etc/gshadow 
古老/传统的方法是使用vi去直接修改,但是这有安全隐患(具体可自己搜一下),所以后来改成使用这些命令去代替: 
vipw 
vipw -s 
vigr 
vigr -s 
  
具体的操作顺</div>
                                </li>
                                <li><a href="/article/3190.htm"
                                       title="第五章 常用Lua开发库1-redis、mysql、http客户端" target="_blank">第五章 常用Lua开发库1-redis、mysql、http客户端</a>
                                    <span class="text-muted">jinnianshilongnian</span>
<a class="tag" taget="_blank" href="/search/nginx/1.htm">nginx</a><a class="tag" taget="_blank" href="/search/lua/1.htm">lua</a>
                                    <div>对于开发来说需要有好的生态开发库来辅助我们快速开发,而Lua中也有大多数我们需要的第三方开发库如Redis、Memcached、Mysql、Http客户端、JSON、模板引擎等。 
一些常见的Lua库可以在github上搜索,https://github.com/search?utf8=%E2%9C%93&q=lua+resty。 
  Redis客户端 
lua-resty-r</div>
                                </li>
                                <li><a href="/article/3317.htm"
                                       title="zkClient 监控机制实现" target="_blank">zkClient 监控机制实现</a>
                                    <span class="text-muted">liyonghui160com</span>
<a class="tag" taget="_blank" href="/search/zkClient+%E7%9B%91%E6%8E%A7%E6%9C%BA%E5%88%B6%E5%AE%9E%E7%8E%B0/1.htm">zkClient 监控机制实现</a>
                                    <div>  
       直接使用zk的api实现业务功能比较繁琐。因为要处理session loss,session expire等异常,在发生这些异常后进行重连。又因为ZK的watcher是一次性的,如果要基于wather实现发布/订阅模式,还要自己包装一下,将一次性订阅包装成持久订阅。另外如果要使用抽象级别更高的功能,比如分布式锁,leader选举</div>
                                </li>
                                <li><a href="/article/3444.htm"
                                       title="在Mysql 众多表中查找一个表名或者字段名的 SQL 语句" target="_blank">在Mysql 众多表中查找一个表名或者字段名的 SQL 语句</a>
                                    <span class="text-muted">pda158</span>
<a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a>
                                    <div>在Mysql 众多表中查找一个表名或者字段名的 SQL 语句:   
  方法一:SELECT table_name, column_name from information_schema.columns WHERE column_name LIKE 'Name';
  方法二:SELECT column_name from information_schema.colum</div>
                                </li>
                                <li><a href="/article/3571.htm"
                                       title="程序员对英语的依赖" target="_blank">程序员对英语的依赖</a>
                                    <span class="text-muted">Smile.zeng</span>
<a class="tag" taget="_blank" href="/search/%E8%8B%B1%E8%AF%AD/1.htm">英语</a><a class="tag" taget="_blank" href="/search/%E7%A8%8B%E5%BA%8F%E7%8C%BF/1.htm">程序猿</a>
                                    <div>1、程序员最基本的技能,至少要能写得出代码,当我们还在为建立类的时候思考用什么单词发牢骚的时候,英语与别人的差距就直接表现出来咯。 
2、程序员最起码能认识开发工具里的英语单词,不然怎么知道使用这些开发工具。 
3、进阶一点,就是能读懂别人的代码,有利于我们学习人家的思路和技术。 
4、写的程序至少能有一定的可读性,至少要人别人能懂吧... 
 
以上一些问题,充分说明了英语对程序猿的重要性。骚年</div>
                                </li>
                                <li><a href="/article/3698.htm"
                                       title="Oracle学习笔记(8) 使用PLSQL编写触发器" target="_blank">Oracle学习笔记(8) 使用PLSQL编写触发器</a>
                                    <span class="text-muted">vipbooks</span>
<a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a><a class="tag" taget="_blank" href="/search/sql/1.htm">sql</a><a class="tag" taget="_blank" href="/search/%E7%BC%96%E7%A8%8B/1.htm">编程</a><a class="tag" taget="_blank" href="/search/%E6%B4%BB%E5%8A%A8/1.htm">活动</a><a class="tag" taget="_blank" href="/search/Access/1.htm">Access</a>
                                    <div>    时间过得真快啊,转眼就到了Oracle学习笔记的最后个章节了,通过前面七章的学习大家应该对Oracle编程有了一定了了解了吧,这东东如果一段时间不用很快就会忘记了,所以我会把自己学习过的东西做好详细的笔记,用到的时候可以随时查找,马上上手!希望这些笔记能对大家有些帮助! 
    这是第八章的学习笔记,学习完第七章的子程序和包之后</div>
                                </li>
                </ul>
            </div>
        </div>
    </div>

<div>
    <div class="container">
        <div class="indexes">
            <strong>按字母分类:</strong>
            <a href="/tags/A/1.htm" target="_blank">A</a><a href="/tags/B/1.htm" target="_blank">B</a><a href="/tags/C/1.htm" target="_blank">C</a><a
                href="/tags/D/1.htm" target="_blank">D</a><a href="/tags/E/1.htm" target="_blank">E</a><a href="/tags/F/1.htm" target="_blank">F</a><a
                href="/tags/G/1.htm" target="_blank">G</a><a href="/tags/H/1.htm" target="_blank">H</a><a href="/tags/I/1.htm" target="_blank">I</a><a
                href="/tags/J/1.htm" target="_blank">J</a><a href="/tags/K/1.htm" target="_blank">K</a><a href="/tags/L/1.htm" target="_blank">L</a><a
                href="/tags/M/1.htm" target="_blank">M</a><a href="/tags/N/1.htm" target="_blank">N</a><a href="/tags/O/1.htm" target="_blank">O</a><a
                href="/tags/P/1.htm" target="_blank">P</a><a href="/tags/Q/1.htm" target="_blank">Q</a><a href="/tags/R/1.htm" target="_blank">R</a><a
                href="/tags/S/1.htm" target="_blank">S</a><a href="/tags/T/1.htm" target="_blank">T</a><a href="/tags/U/1.htm" target="_blank">U</a><a
                href="/tags/V/1.htm" target="_blank">V</a><a href="/tags/W/1.htm" target="_blank">W</a><a href="/tags/X/1.htm" target="_blank">X</a><a
                href="/tags/Y/1.htm" target="_blank">Y</a><a href="/tags/Z/1.htm" target="_blank">Z</a><a href="/tags/0/1.htm" target="_blank">其他</a>
        </div>
    </div>
</div>
<footer id="footer" class="mb30 mt30">
    <div class="container">
        <div class="footBglm">
            <a target="_blank" href="/">首页</a> -
            <a target="_blank" href="/custom/about.htm">关于我们</a> -
            <a target="_blank" href="/search/Java/1.htm">站内搜索</a> -
            <a target="_blank" href="/sitemap.txt">Sitemap</a> -
            <a target="_blank" href="/custom/delete.htm">侵权投诉</a>
        </div>
        <div class="copyright">版权所有 IT知识库 CopyRight © 2000-2050 E-COM-NET.COM , All Rights Reserved.
<!--            <a href="https://beian.miit.gov.cn/" rel="nofollow" target="_blank">京ICP备09083238号</a><br>-->
        </div>
    </div>
</footer>
<!-- 代码高亮 -->
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shCore.js"></script>
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shLegacy.js"></script>
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shAutoloader.js"></script>
<link type="text/css" rel="stylesheet" href="/static/syntaxhighlighter/styles/shCoreDefault.css"/>
<script type="text/javascript" src="/static/syntaxhighlighter/src/my_start_1.js"></script>





</body>

</html>