2.0发文系统前端版块难点

128 阅读5分钟

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打包及自动更新文章链接

参考文章:blog.csdn.net/weixin_6715…

blog.csdn.net/weixin_6715…