1.打包项目为桌面应用
2.实现桌面应用自动更新
3.内嵌微信公众平台网页注入cookie实现免登陆
1.
踩坑记录:
1.项目cookie实现,首页一直显示加载中,无法正常跳转,经过多方打听,原因就是electron不支持原始cookie的使用,导致页面跳转时没有权限访问其他路由,解决方案,将封装的存改cookie的函数,全部改为本地存储
2.打包运行起来后,所有接口全部失效,查看日志,原因:所有接口请求前缀全部为app:file//开头,请求全部变成了本地请求,此时我们的接口是http请求协议.解决方案,配置vue.config文件下静态资源路径
完成上面两部重要步骤后,此时项目打包安装后应该是正常运行了.
electron项目有两个进程,background.js为项目主进程
实现自动更新需要引入官方自带的api
2.写入更新代码
if (!process.env.WEBPACK_DEV_SERVER_URL) {
autoUpdater.autoDownload = false
autoUpdater.signals.updateDownloaded(() => {})
autoUpdater.on('error', (error) => {
log.warn('检查更新失败: ' + error == null ? 'unknown' : (error.stack || error).toString())
// dialog.showErrorBox('Error: ', error == null ? 'unknown' : (error.stack || error).toString())
})
autoUpdater.on('update-available', (info) => {
// var appInfo = {
// info: info.version,
// files: info.files,
// path: info.path,
// sha512: info.sha512,
// releaseDate: info.releaseDate
// }
dialog.showMessageBox({
type: 'info',
title: '更新提示',
message: '软件需要更新,您是否立即更新?',
buttons: ['推迟', '立即更新']
}).then((res) => {
log.warn('index:' + res.response)
if (res.response === 1) {
log.warn('选择升级')
autoUpdater.downloadUpdate()
} else {
log.warn('选择不升级:')
}
})
})
// 检查更新时触发
autoUpdater.on('update-available', (res) => {
log.warn('检查更新时触发')
// log.warn(res)
// dialog.showMessageBox({
// title: '检查更新',
// message: '正在检查更新'
// })
})
// 没有可用更新
autoUpdater.on('update-not-available', () => {
log.warn('没有可用更新')
// dialog.showMessageBox({
// title: '已是最新版',
// message: '当前版本是最新版本。'
// })
})
// 安装更新
autoUpdater.on('update-downloaded', (res) => {
// log.warn(res)
log.warn('下载完毕!提示安装更新')
dialog.showMessageBox({
title: '升级提示!',
message: '已自动升级为最新版,请等待程序安装完成并重启应用!'
}, () => {
log.warn('确认安装')
setImmediate(() => autoUpdater.quitAndInstall(true, true))
})
})
3.启动应用调用检测更新函数
if (process.env.WEBPACK_DEV_SERVER_URL) {
// Load the url of the dev server if in development mode
win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
if (!process.env.IS_TEST) win.webContents.openDevTools()
} else {
createProtocol('app')
// Load the index.html when not in development
win.loadURL('app://./index.html')
autoUpdater.checkForUpdates() //检测有无新版本
}
4..在vue.config中配置更新地址
module.exports = {
pluginOptions: {
electronBuilder: {
builderOptions: {
productName: "打包后的exe名称",
appId: "xxx",
//注意, 只有在配置了publish路径时, build之后才会生成latest.yml文件
publish: [
{
"provider": "generic",
"url": "你的文件服务器地址"
}
]
}
}
.......
},
5.在package.json中配置第一个版本号
"version": "0.0.1" //package.json 这是你的版本号
6.打包你的项目生成dist_electron文件
此时桌面应用自动升级完成,后续接到新需求,升级时显示升级进度条.ok,上面代码全部推翻
第一步,封装更新js文件
import { autoUpdater } from 'electron-updater'
import { ipcMain } from 'electron'
let mainWindow = null;
export function updateHandle(window, feedUrl) {
mainWindow = window;
let message = {
error: '检查更新出错',
checking: '正在检查更新',
updateAva: '检测到新版本,正在下载',
updateNotAva: '您已经更新到最新版本了',
};
//设置更新包的地址
autoUpdater.setFeedURL(feedUrl);
//监听升级失败事件
autoUpdater.on('error', function (error) {
sendUpdateMessage({
cmd: 'error',
message: error
})
});
//监听开始检测更新事件
autoUpdater.on('checking-for-update', function (message) {
sendUpdateMessage({
cmd: 'checking-for-update',
message: message
})
});
//监听发现可用更新事件
autoUpdater.on('update-available', function (message) {
sendUpdateMessage({
cmd: 'update-available',
message: message
})
});
//监听没有可用更新事件
autoUpdater.on('update-not-available', function (message) {
sendUpdateMessage({
cmd: 'update-not-available',
message: message
})
});
// 更新下载进度事件
autoUpdater.on('download-progress', function (progressObj) {
sendUpdateMessage({
cmd: 'download-progress',
message: progressObj
})
});
//监听下载完成事件
autoUpdater.on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateUrl) {
sendUpdateMessage({
cmd: 'update-downloaded',
message: {
releaseNotes,
releaseName,
releaseDate,
updateUrl
}
})
//退出并安装更新包
autoUpdater.quitAndInstall();
});
//接收渲染进程消息,开始检查更新
ipcMain.on("checkForUpdate", (e, arg) => {
//执行自动更新检查
// sendUpdateMessage({cmd:'checkForUpdate',message:arg})
autoUpdater.checkForUpdates();
})
}
//给渲染进程发送消息
function sendUpdateMessage(text) {
mainWindow.webContents.send('message', text)
}
主进程引入更新js文件
import { updateHandle } from './update'
桌面应用窗口初始化监听事件
// Create the browser window.
let win = new BrowserWindow({
width: 1400,
height: 800,
webPreferences: {
// nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
// // preload: path.join(__dirname, '../aaa.js'),
// webviewTag:true,
// webSecurity:false
nodeIntegration: true,
contextIsolation: false,
webviewTag:true,
webSecurity:false,
preload: path.join(__dirname, './preload.js')
}
})
if (process.env.WEBPACK_DEV_SERVER_URL) { 这里是判断此时环境的
// Load the url of the dev server if in development mode
win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
if (!process.env.IS_TEST) win.webContents.openDevTools()
} else {
createProtocol('app')
// Load the index.html when not in development
win.loadURL('app://./index.html')
// autoUpdater.checkForUpdates()
}
win.on('closed', () => {
win = null
})
let feedUrl = "https://beijingbizhi.beijingzhuangxiu01.cn/documentLssuingSystem/";
updateHandle(win,feedUrl); 这一步是运行更新代码的,
}
运行代码后,向渲染进程发送是否有可用更新的消息
let _this = this;
//接收主进程版本更新消息
ipcRenderer.on("message", (event, arg) => {
// for (var i = 0; i < arg.length; i++) {
console.log(arg);
if ("update-available" == arg.cmd) {
//显示升级对话框
_this.dialogVisible = true;
} else if ("download-progress" == arg.cmd) {
//更新升级进度
/**
*
* message{bytesPerSecond: 47673
delta: 48960
percent: 0.11438799862426002
total: 42801693
transferred: 48960
}
*/
console.log(arg.message.percent);
let percent = Math.round(parseFloat(arg.message.percent));
_this.percentage = percent;
} else if ("error" == arg.cmd) {
_this.dialogVisible = false;
_this.$message("暂无新版本");
}
// }
});
ipcRenderer.send("checkForUpdate");
//20秒后开始检测新版本
// let timeOut = window.setTimeout(() => {
// ipcRenderer.send("checkForUpdate");
// }, 20000);
// clearTimeout;
// 间隔1小时检测一次
// let interval = window.setInterval(() => {
// ipcRenderer.send("checkForUpdate");
// }, 3600000);
此时更新代码写完,运行起来 报错
问题
ipcRenderer is undefined
原因:渲染进程中使用了主进程的方法,所以导致报错,此时业务逻辑必须在渲染进程中使用这个api
此时得新建一个js预加载文件.preload.js
window.ipcRenderer = require('electron').ipcRenderer;
将此api挂载到window全局对象下
主进程运行,初始化创建窗口时,运行perload.js文件
3.新建页面,webview嵌入微信公众平台网页
let loadingInstance = Loading.service({
target:document.querySelector('#div'),
text:'玩命加载中,不要催...',
background:'#fff'
});
let foo = document.querySelector("#foo");
this.abc = localStorage.getItem('cookie');
var token=localStorage.getItem('wxtoken');
this.abc=this.abc+"\n"+'window.document.cookie="wxuin="; ';
console.log("服务器返回的cookie1");
console.log(this.abc);
var clear_all_cookies="function clearAllCookie() { var keys = document.cookie.match(/[^ =;]+(?==)/g); if(keys) { for(var i = keys.length; i--;) document.cookie = keys[i] + '=0;expires=' + new Date(0).toUTCString() }}; clearAllCookie();";
// foo.openDevTools()
let cookie = await foo.executeJavaScript('document.cookie')
console.log("浏览器没有清空之前的cookie");
console.log(cookie);
console.log("浏览器清空cookie");
foo.executeJavaScript(clear_all_cookies);
cookie = await foo.executeJavaScript('document.cookie')
console.log("浏览器清空之后的cookie");
console.log(cookie);
await foo.executeJavaScript(this.abc);
console.log('注入完毕');
cookie = await foo.executeJavaScript('document.cookie')
console.log("浏览器注入完毕之后的cookie");
console.log(cookie);
foo.loadURL('https://mp.weixin.qq.com/cgi-bin/home?t=home/index&token='+token+'&lang=zh_CN');
}
<template>
<editor v-model="value" :init="init" id="tinymce"></editor>
</template>
<script>
//导入富文本插件
import tinymce from "tinymce";
import Editor from "@tinymce/tinymce-vue";
import "tinymce/themes/silver/theme";
//导入语言包
import langs from "./zh_CN";
//导入皮肤包
import toolbar_skin from "@/components/tinyMce/skins/ui/oxide-dark/skin.css";
import skin from "tinymce/skins/ui/oxide-dark/skin.css";
import metaInfo from "vue-meta";
import "tinymce/plugins/paste";
import "tinymce/icons/default/icons.min.js";
export default {
name: "tinyMce",
components: {
Editor,
},
props: {
myValue: {
type: String,
require: true,
},
},
metaInfo: {
meta: [{ name: "referrer", content: "never" }],
},
data() {
return {
baseUrl: process.env.VUE_APP_BASE_API,
value: this.myValue,
originalValue: "",
init: {
selector: "#tinymce",
language_url: langs,
language: "zh_CN",
height: 750,
menubar: true,
branding: false,
convert_urls: false, //关闭url自动检测
skin_url: toolbar_skin,
toolbar: [
'code undo redo restoredraft | cut copy paste pastetext | forecolor backcolor bold italic underline strikethrough link anchor | alignleft aligncenter alignright alignjustify outdent indent | \
styleselect formatselect fontselect fontsizeselect | bullist numlist | blockquote subscript superscript removeformat | \
table image media charmap emoticons hr pagebreak insertdatetime print preview | fullscreen | bdmap indent2em lineheight formatpainter axupimgs'],
auto_focus: true,
content_style: "img {max-width:100%;}",
plugins: " print preview searchreplace autolink directionality visualblocks visualchars fullscreen image link media template code codesample charmap hr pagebreak nonbreaking anchor wordcount imagetools textpattern emoticons bdmap formatpainter axupimgs ",
convert_urls: true,
images_upload_url: this.baseUrl + "/zjyauto/uploadimg",
images_upload_handler: (blobInfo, success, failure, progress) => {
//发送Ajax请求
const xhr = new XMLHttpRequest();
xhr.open("post", this.baseUrl + "/zjyauto/uploadimg");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (JSON.parse(xhr.response).location) {
this.$message.success("上传成功");
success(JSON.parse(xhr.response).location);
} else {
this.$message.error("上传失败");
failure("");
}
}
};
const formData = new FormData();
formData.append("file", blobInfo.blob());
xhr.send(formData);
},
automatic_uploads: true,
file_picker_types: "image paste",
paste_data_images: true,
},
};
},
watch: {
ReviseFlag: {
deep: true,
handler(val) {
console.log(val);
},
},
},
mounted() {
this.originalValue = this.myValue;
tinymce.init({
font_formats:
"微软雅黑='微软雅黑';宋体='宋体';黑体='黑体';仿宋='仿宋';楷体='楷体';隶书='隶书';幼圆='幼圆';Andale Mono=andale mono,times;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Trebuchet MS=trebuchet ms,geneva;Verdana=verdana,geneva;Webdings=webdings;Wingdings=wingdings",
custom_elements: "mpprofile,~mpprofile",
schema: "html5",
});
},
beforeDestroy() {
this.$store.state.tinyMce.ReviseFlag
? this.$emit("getNewVal", this.value)
: this.$emit("getNewVal", this.originalValue);
},
};
</script>
<style lang="scss" scoped>
</style>
JavaScript中执行上下文和执行栈
简单的来说,执行上下文是一种对Javascript代码执行环境的抽象概念,也就是说只要有Javascript代码运行,那么它就一定是运行在执行上下文中
执行上下文的类型分为三种:
- 全局执行上下文:只有一个,浏览器中的全局对象就是 window对象,this 指向这个全局对象
- 函数执行上下文:存在无数个,只有在函数被调用的时候才会被创建,每次调用函数都会创建一个新的执行上下文
- Eval 函数执行上下文: 指的是运行在 eval 函数中的代码,很少用而且不建议使用
下面给出全局上下文和函数上下文的例子:
紫色框住的部分为全局上下文,蓝色和橘色框起来的是不同的函数上下文。只有全局上下文(的变量)能被其他任何上下文访问
可以有任意多个函数上下文,每次调用函数创建一个新的上下文,会创建一个私有作用域,函数内部声明的任何变量都不能在当前函数作用域外部直接访问
electron打包及自动更新文章链接