依晨

依晨 查看完整档案

北京编辑河北科技师范学院  |  计算机科学与技术 编辑泰康保险集团  |  前端开发工程师 编辑填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 该用户太懒什么也没留下

个人动态

依晨 收藏了文章 · 2月24日

electron打包更新到集成sqlite

本文主要讲以下几点

  • 简单介绍electron
  • electron的进程与渲染层通讯
  • electron的打包配置
  • electron的自动更新
  • electron如何集成sqlite3
  • electron的资料分享
  • electron在内网开发的一点点建议
  • electron主进程的生命周期

快速开始一个electron与简介

electron是什么

打开官网第一眼就是使用 JavaScript, HTML 和 CSS 构建跨平台的桌面应用,更加通俗一点就是一个桌面浏览器,实际上这样理解也是对的。因为electron就是通过javaScript使chromium来展示web页面

我为啥用它

  • 前端可用制作跨平台应用
  • 生态圈好
  • 由github开发维护
  • electron(70+k)比nw.js (35+k)Star多两倍
  • electron产品我使用过表现优秀

    • Vscode
    • Atom
    • gitHub桌面端
    • ...

如何快速上手electron

我认为以下是快速开始的最好办法

  • 一定要多读官方文档,可以减少大部分时间
  • 官方提供的快速开始模板
  • 官方提供快速熟悉的API的模板

直接使用electron官方提供的例子

// 看这个的源码,很简单可以快速上手
# 克隆这仓库
$ git clone https://github.com/electron/electron-quick-start
# 进入仓库
$ cd electron-quick-start
# 安装依赖库
$ npm install
# 运行应用
$ npm start

如果想了解这个electron的API可以克隆这个库并运行

git clone https://github.com/electron/electron-api-demos

cd electron-api-demos

npm install

npm start

如何调试electron

渲染进程调试(web页面)
渲染器进程这chrome浏览器一样,BrowserWindow创建窗口后添加窗口实例.webContents.open

主进程Electron调试
使用vsCode中的debug,注意调试路径,如果需要增加变量环境依照配置增加即可

electron中主进程的生命周期以及常用事件和渲染层

主要聊electron中主进程的生命周期,渲染器的部分生命周期,一般只介绍通用都存在的,如果只有某个系统有的api就不写在这里了,大家可以自行查阅

主进程中的生命周期

生命周期图
生命周期图

正常流程会触发的生命周期

  • will-finish-launching:当应用程序完成基础的启动的时候被触发
  • web-contents-created:webContents被创建完成
  • browser-window-created:BrowserWindow被创建完成
  • ready:当 Electron 完成初始化时被触发
  • remote-require: 引入remote时被调用
  • before-quit: 在应用程序开始关闭窗口之前触发
  • will-quit:当所有窗口都已关闭并且应用程序将退出时发出
  • quit: 在应用程序退出时发出
  • window-all-closed:当所有的窗口都被关闭时触发
这里要注意如果是进程杀死退出的所有都不触发,如果是cmd+Q或者开发者使用app.quit()退出的window-all-closed是不会被触发的,基本操作一般在ready中处理

进程相关

  • gpu-process-crashed: 当 gpu 进程崩溃或被杀时触发。

其他

  • browser-window-focus: 在 browserWindow 获得焦点时发出
  • browser-window-blur:在 browserWindow 失去焦点时发出

渲染进程--(浏览器)-BrowserWindow

  • ready-to-show:当页面已经渲染完成(但是还没有显示) 并且窗口可以被显示时触发
  • move: 窗口移动
  • resize: 调整窗口大小后触发
  • close: 在窗口要关闭的时候触发。 它在DOM 的beforeunload 和 unload 事件之前触发.
  • blur: 失去焦点,同app
  • focus: 获得焦点,同app
  • maximize:窗口最大化时触发
  • unmaximize: 当窗口最大化退出状态触发
  • minimize: 窗口最小化时触发
  • restore: 当窗口从最小化还原触发
  • ...

渲染进程 BrowserWindow实例中的webContents

  • did-finish-load:导航完成时触发,即选项卡的旋转器将停止旋转,并指派onload事件后
  • did-finish-load: 这个事件类似于 did-finish-load, 不过是在加载失败或取消后触发
  • dom-ready: 一个框架中的文本加载完成后触发该事件
  • crashed: 当渲染进程崩溃或被结束时触发
  • unresponsive: 页面未响应触发
  • devtools-opened: 当开发者工具被打开时,触发该事件。
  • devtools-closed: 当开发者工具被关闭时,触发该事件。
  • ...

electron的进程与渲染层通讯

  • 主进程和渲染器进程
  • 主进程和渲染器进程的区别

electron主进程与渲染进程

chrome浏览器由于每个标签页都是一个进程,而electron所运行的进程称为主进程并且只有一个,主进程要操控浏览器的每个标签的网页称为渲染器进程,如何通讯呢?

主进程和渲染器进程

进程

进程是正在运行的程序的实例(狭义定义)

electron中的主进程

Electron 运行 package.json 的 main 脚本的进程被称为主进程。 在主进程中运行的脚本通过创建web页面来展示用户界面。 一个 Electron 应用总是有且只有一个主进程。

electron中的渲染器进程

由于 Electron 使用了 Chromium 来展示 web 页面,所以 Chromium 的多进程架构也被使用到。 每个 Electron 中的 web 页面运行在它自己的渲染进程中。

渲染器进程与主进程之间的区别

  • 主进程是从开始运行一直存在,渲染器进程通过BrowserWindow来创建实例,实例销毁则渲染进程销毁
主进程使用 BrowserWindow 实例创建页面。 每个 BrowserWindow 实例都在自己的渲染进程里运行页面。 当一个 BrowserWindow 实例被销毁后,相应的渲染进程也会被终止。
  • 主进程管理所有渲染进程,渲染进程是独立且自我管理(web页面)
主进程管理所有的web页面和它们对应的渲染进程。 每个渲染进程都是独立的,它只关心它所运行的 web 页面。
  • 主进程中可以调用底层所有的GUI的API,渲染进程则因为安全问题不能随意调用。如果要调用则需要通讯让主进程来调用。
在页面中调用与 GUI 相关的原生 API 是不被允许的,因为在 web 页面里操作原生的 GUI 资源是非常危险的,而且容易造成资源泄露。 如果你想在 web 页面里使用 GUI 操作,其对应的渲染进程必须与主进程进行通讯,请求主进程进行相关的 GUI 操作。

主进程与渲染器进程如何通讯

主进程与渲染器进程通过ipcMain与ipcRenderer来通讯

ipcMain与ipcRenderer通讯

主进程向渲染器进程通讯

这个方式主要是主进程中使用ipcMain使用on监听,监听获取后通过event.sender(相当于webContent)send来发送一个事件,渲染进程中使用ipcRenderer通过on来接收,如果是同步可以通过evnet.returnValue来返回主进程的结果代码如下:

渲染器进程向主进程通讯

渲染器进程主要通过ipcRenderer这个模块中的send来发送,该方法中可以同步与异步发送消息,接收消息使用on来接收

通讯简化

electron的打包

常用方式:

  1. electron-builder(本人主要用这个)
  2. electron-pakager
  3. electron-forge

electron-builder

electron-builder 是一个完整的解决方案,并且自带自动更新策略

electron-builder打包在package的script配置好

打包常用参数:

  "build": {
    "appId": "your.id", // appid
    "productName": "程序名称", // 程序名称
    "files": [    // 打包需要的不过滤的文件
      "build/**/*",
      "main.js",
      "node_modules/**/*"
    ],
    "directories": { 
      "output": "./dist-out", // 打包输出的目录
      "app": "./",  // package所在路径
      "buildResources": "assets"   
    },
    "nsis": {
      "oneClick": false,  // 是否需要点击安装,自动更新需要关掉
      "allowToChangeInstallationDirectory": true, //是否能够选择安装路径
      "perMachine": true // 是否需要辅助安装页面
    },
    "win": {
      "target": [
        {
          "target": "nsis",  // 输出目录的方式
          "arch": [ // 输出的配置ia32或者x64/x86
            "x64"
          ]
        }
      ],
      "publish": [ // 自动更新的配置
        {
          "provider": "generic", // 自己配置更新的服务器要选generic
          "url": "http://127.0.0.1:8080/updata/" //更新配置的路径
        }
      ]
    }
  }

在package.json中增加的快速启动项

"scripts": {
  "pack": "electron-builder --dir",
  "dist": "electron-builder"
}

启动打包

通常需要注意的点

  • 注意路径,由于打包后的路径会有问题最好使用path.join()来处理一下。
  • 碰到 The process cannot access the file because it is being used by another process.这个问题多数是vscode占用了关掉重开就好了

如果配合create-react-app创建出来的应用

  • 可以先让react的程序进行打包成静态文件,在把静态文件打包到electron应用中
  • 如果electron-builder总是报类似electron.js找不到的警告,并且在上面提示让你去看网页中的方法,可以通过electron-builder提示的网址来修改,这实际是一个教程需要科学上网。

electron-builder的自动更新

如果在API中看到autoUpdater这个API,希望在看到这个API之前先参考官方的自动更新这样会让你少点坑,因为electron-builder的自动更新机制和electron提供有些不一样,electron-builder官网也有说明,否则就会一直报error了

这个更新实际上是对比例两个版本之间的版本号,如果当前版本小于,服务器版本的话就会进行下载更新

更新机制

在资料里有一篇专门讲更新机制的,很好。这里简单总结。
更新步骤.png

autoUpader生命周期

  • error: 如果任意一个环节有问题就会走到这步
  • checking-for-update :当开始检查更新的时候触发。
  • update-available: 发现更新
  • update-not-available:当没有可用更新的时候触发.
  • update-downloaded: 在更新下载完成的时候触发。
  • before-quit-for-update : 此事件是在用户调用quitAndInstall()之后发出的。

更新要注意的点:

  • electron-builder中的autoUplader需要使用 electron-updater这个模块,非原生模块
  • package.json中的publish中的url与更新的地址要一致(注意端口),否则会报net::ERR_CONNECTION_REFUSED
  • 如果是未打包也想跑一边autoUpdater的流程需要一个dev-app-update.yml的文件放在与main.js同一层级的地方,类似win-ia32-unpacked/resource/app-update.yml这样的文件,直接复制改名就能使用
  • 如果更新完成后在package中配置了自动安装的选项,在关闭应用就能直接安装,如果想自己控制马上安装需要加autoUpdater.quitAndInstall()
  • 如果初次启动应用在终端输出了你想要的结果,在渲染选只输出来了一部分,或者没有输出,如果不是报错的话,不妨监听一下该渲染进程的did-finish-load这个声明周期,在这里面做处理,根据场景可以使用once来监听
  • 需要一个静态网站服务器在你需要的文件夹使用http-server -o,如果没有就全局安装一个npm i http-server -g,在package中需要配置publish中的 "provider": "generic"
  • 碰到 spawn **/*.exe ENOENT这类的可能是你在开发环境运行了一个未安装的应用,并且使用了autoUpdater.quitAndInstall()就会报错,如果是安装好了就不会报这个错
//electron-quick-start 
//main.js 主进程中

// 可以没有这个
const { autoUpdater } = require('electron-updater')

// ================= start 非必须 ===============
const log = require('electron-log');
autoUpdater.logger = log;
autoUpdater.logger.transports.file.level = 'info';
// ================ end 非必须 ==================

// 封装
function sendStatusToWindow(text) {
  log.info(text);
  mainWindow.webContents.send('message', text);
}

/**
 * 自动更新
 */
function checkUpdata() {
  autoUpdater.setFeedURL('http://127.0.0.1:8080/updata/')
  // autoUpdater.on('error', (error) => {
  //   sendStatusToWindow( `[error]:${error}`)
  // })
  autoUpdater.on('checking-for-update', () => {
    sendStatusToWindow('Checking for update...');
    // mainWindow.webContents.send('downlaod')

  })
  autoUpdater.on('update-available', (info) => {
    sendStatusToWindow('Update available.');
  })
  autoUpdater.on('update-not-available', (info) => {
    sendStatusToWindow('Update not available.');
  })
  autoUpdater.on('error', (err) => {
    sendStatusToWindow('Error in auto-updater. ' + err);
  })
  autoUpdater.on('download-progress', (progressObj) => {
    let log_message = "Download speed: " + progressObj.bytesPerSecond;
    log_message = log_message + ' - Downloaded ' + progressObj.percent + '%';
    log_message = log_message + ' (' + progressObj.transferred + "/" + progressObj.total + ')';
    sendStatusToWindow(log_message);
    mainWindow.webContents.send('downloadExe', progressObj.percent)
  })
  autoUpdater.on('update-downloaded', (info) => {
    mainWindow.webContents.send('downloaded')


    sendStatusToWindow('Update downloaded');
    // autoUpdater.quitAndInstall();
  });

electron中添加sqlite3

需要的环境

如果是windows环境需要准备 vs2015 与python2.7的环境

  1. python2.7.x 网上下载安装即可
  2. vs2015 以下方式可选其一

    1. 工具包 npm install --vs2015 -g windows-build-tools(推荐)
    2. 安装visual studio 中安装vs2015工具包(超久)

程序中可添加的本地数据库,最多使用的两款

  • sqlite3(关系型)
  • neDB(非关系型)

为自己的数据加上本地数据库sqlite3

sqlite3【gitHub】
为程序添加sqlite3npm i sqlite3 或者yarn add sqlite3

如何使用?

// 官网例子
var sqlite3 = require('sqlite3').verbose();
var db = new sqlite3.Database(':memory:'); // 这里是把数据存入内存
db.serialize(function() {
var db = new sqlite3.Database()
  db.run("CREATE TABLE lorem (info TEXT)");
  var stmt = db.prepare("INSERT INTO lorem VALUES (?)");
  for (var i = 0; i < 10; i++) {
      stmt.run("Ipsum " + i);
  }
  stmt.finalize();

  db.each("SELECT rowid AS id, info FROM lorem", function(err, row) {
      console.log(row.id + ": " + row.info);
  });
});

db.close();

sqilte3在安装的时候自动生成sqlite所需要的文件包,这个二进制文件需要使用node-pre-gyp或者是node-gyp这也就是为什么需要安装环境的原因,在这一步鬼知道当时报了多少东西,尤其是还要把这玩意弄进内网。
比如说:

  • 开始运行的时候就报Error: Cannot find module 'E:\electronjs\electron-quick-start\node_modules\sqlite3\lib\binding\electron-v4.0-win32-x64\node_sqlite3.node'类似这样的东西,实际上确实没有这个东西,怎么解决呢?

    • 使用node-gyp这个模块直接重新编译一个版本正确的
node-gyp rebuild 
--target=4.0.4 --arch=x64
--target_platform=win32 --dist-url=https://atom.io/download/electron/
--module_name=node_sqlite3 --module_path=../lib/binding/electron-v4.0-win32-x64

// 使用到的模块解析
// target                => electron的版本号一定要一直
// target_platform       => 需要打包的平台ia32/x64等
// dist-url              => 这个是需要下载相关内容的地址
// module_name           => 需要打包的模块名称
// module_path           => 打包输出的地方

如果觉得麻烦可以试试npm i electron-rebuild -D然后在使用./node_modules/.bin/electron-rebuild重新编译一遍,有时候可能是缓存问题,把缓存清除npm cache clear -f清除一下,还有就是npm i重新安装一下,因为环境的问题会跳出各种问题,可以多多尝试这些方法

was compiled against a different Node.js version using
NODE_MODULE_VERSION 64. This version of Node.js requires
NODE_MODULE_VERSION 69. Please try re-compiling or re-installing

如让你选择node编译版本的github【issues】====>也有可能是编译问题,利用electron-rebuild 重新编译一遍或者把sqlite3降级

需要注意的

  • 打包的时候如果报error MSB4019: 未找到导入的项目Microsoft.WebApplication.targets重新安装一下vs2015工具包,或者以前安装的有问题重新安装(我就碰到了)
  • 用node-gyp编译sqlite3打包需要binding.gyp这个python文件以及其他配置所以直接进入node_modules\sqlite3这个文件运行 node-gyp的操作
  • 打包的时候要注意在渲染进程中使用sqlite3在打包的时候会出现打包问题,解决可以选在在主进程操作本地数据库
  • 打包之后使用DB会出现一个问题,在当前目录怎么都生成不了DB,在网上查了要么就通过手动授权为管理员打开,要么手动管理员打开,这根本就不能行,后来参考electron-vue发现可以使用app.getPath('userData')这个方法进行获取app可缓存地址,然后使用path.join合并一下路径就解决了无法生成DB文件的问题,事实上indexDB,locaStore这个两个的存储文件也在这个目录下

其他模块

  • globalShortcut: 注册全局快捷键
  • Tray: 托盘
  • Menu:菜单事件

内网开发需要注意

内网开发真的很烦。。
首先需要环境,然后就是需要下载的包,其次就是需要编译后的各种文件如sqlite3编译后的文件等如果在内网开发electron就需要把这些需要的包全都复制到对应位置

大部分需要配置的文件都在C:\Users\xxxxxx代表你所在的文件夹,
部分下载缓存文件在C:\Users\xxx\AppData\Local目录下的electron 和 electron-builderer里

内网01.png

参考的学习资料

以下文章大部分阅读过,觉得不错推荐给大家

electron

electron构建相关

electron打包相关

electron更新

electron

electron编译

electron tips

查看原文

依晨 回答了问题 · 2019-08-08

解决opencv.js图像增强

参照这个写出来了https://stackoverflow.com/que...

关注 1 回答 1

依晨 收藏了文章 · 2019-07-07

electron-builder打包见解

Electron-builder打包详解

开发electron客户端程序,打包是绕不开的问题。下面就我在工作中的经验以及目前对electron-builder的了解来分享一些心得。

基本概念

官网的定义

A complete solution to package and build a ready for distribution Electron app for macOS, Windows and Linux with “auto update” support out of the box.

关于electronelectron-builder的基础部分这篇文章就跳过了,有兴趣的话可以看这篇文章

如何使用

builder的使用和配置都是很简单的
builder配置有两种方式

  • package.json中直接配置使用(比较常用,我们下面着重来讲这个)
  • 指定electron-builder.yml文件

demo地址会在文章末尾给出(demo项目中electron使用得是V2.0.7版本,目前更高得是2.0.8版本)。

下面是一个简单的package.js中带注释的配置

  1. 基础配置
"build": {  // 这里是electron-builder的配置
    "productName":"xxxx",//项目名 这也是生成的exe文件的前缀名
    "appId": "com.xxx.xxxxx",//包名  
    "copyright":"xxxx",//版权  信息
    "directories": { // 输出文件夹
      "output": "build"
    }, 
    // windows相关的配置
    "win": {  
      "icon": "xxx/icon.ico"//图标路径 
    }  
  }

在配置文件中加入以上的文件之后就可以打包出来简单的<font clolor="red">文件夹</font>,文件夹肯定不是我们想要的东西。下一步我们来继续讲别的配置。

  1. 打包目标配置

要打包成安装程序的话我们有两种方式,

  1. 使用NSIS工具对我们的文件夹再进行一次打包,打包成exe
  2. 通过electron-builder的nsis直接打包成exe,配置如下
"win": {  // 更改build下选项
    "icon": "build/icons/aims.ico",
    "target": [
      {
        "target": "nsis" // 我们要的目标安装包
      }
    ]
  },
  1. 其他平台配置
  "dmg": { // macOSdmg
    "contents": [
      ...
    ]
    },
    "mac": {  // mac
      "icon": "build/icons/icon.icns"
    },
    "linux": { // linux
      "icon": "build/icons"
    }
  1. nsis配置

这个要详细的讲一下,这个nsis的配置指的是安装过程的配置,其实还是很重要的,如果不配置nsis那么应用程序就会自动的安装在C盘。没有用户选择的余地,这样肯定是不行的

关于nsis的配置是在build中nsis这个选项中进行配置,下面是部分nsis配置

"nsis": {
  "oneClick": false, // 是否一键安装
  "allowElevation": true, // 允许请求提升。 如果为false,则用户必须使用提升的权限重新启动安装程序。
  "allowToChangeInstallationDirectory": true, // 允许修改安装目录
  "installerIcon": "./build/icons/aaa.ico",// 安装图标
  "uninstallerIcon": "./build/icons/bbb.ico",//卸载图标
  "installerHeaderIcon": "./build/icons/aaa.ico", // 安装时头部图标
  "createDesktopShortcut": true, // 创建桌面图标
  "createStartMenuShortcut": true,// 创建开始菜单图标
  "shortcutName": "xxxx", // 图标名称
  "include": "build/script/installer.nsh", // 包含的自定义nsis脚本 这个对于构建需求严格得安装过程相当有用。
  "script" : "build/script/installer.nsh" // NSIS脚本的路径,用于自定义安装程序。 默认为build / installer.nsi  
},

关于includescript 到底选择哪一个 ?

在对个性化安装过程需求并不复杂,只是需要修改一下安装位置,卸载提示等等的简单操作建议使用include配置,如果你需要炫酷的安装过程,建议使用script进行完全自定义。

NSIS对于处理安装包这种东西,功能非常的强大。但是学习起来并不比一门高级语言要容易。其中的奥秘还要各位大佬自行探索

这里上一些学习资源

  1. 关于操作系统的配置

主要是windows中64和32位的配置

CLI参数

electron-builder --ia32 // 32位
electron-builder        // 64位(默认)

nsis中配置

"win": {
  "icon": "build/icons/aims.ico",
  "target": [
    {
      "target": "nsis",
      "arch": [ // 这个意思是打出来32 bit + 64 bit的包,但是要注意:这样打包出来的安装包体积比较大,所以建议直接打32的安装包。
        "x64", 
        "ia32"
      ]
    }
  ]
}
  1. 更新配置

下面这个是给更新用的配置,主要是为了生成lastest.yaml配置文件

"publish": [
  {
    "provider": "generic", // 服务器提供商 也可以是GitHub等等
    "url": "http://xxxxx/" // 服务器地址
  }
],

完整配置

基本上可用的完整的配置

"build": {
    "productName":"xxxx",//项目名 这也是生成的exe文件的前缀名
    "appId": "com.leon.xxxxx",//包名  
    "copyright":"xxxx",//版权  信息
    "directories": { // 输出文件夹
      "output": "build"
    }, 
    "nsis": {
      "oneClick": false, // 是否一键安装
      "allowElevation": true, // 允许请求提升。 如果为false,则用户必须使用提升的权限重新启动安装程序。
      "allowToChangeInstallationDirectory": true, // 允许修改安装目录
      "installerIcon": "./build/icons/aaa.ico",// 安装图标
      "uninstallerIcon": "./build/icons/bbb.ico",//卸载图标
      "installerHeaderIcon": "./build/icons/aaa.ico", // 安装时头部图标
      "createDesktopShortcut": true, // 创建桌面图标
      "createStartMenuShortcut": true,// 创建开始菜单图标
      "shortcutName": "xxxx", // 图标名称
      "include": "build/script/installer.nsh", // 包含的自定义nsis脚本
    },
    "publish": [
      {
        "provider": "generic", // 服务器提供商 也可以是GitHub等等
        "url": "http://xxxxx/" // 服务器地址
      }
    ],
    "files": [
      "dist/electron/**/*"
    ],
    "dmg": {
      "contents": [
        {
          "x": 410,
          "y": 150,
          "type": "link",
          "path": "/Applications"
        },
        {
          "x": 130,
          "y": 150,
          "type": "file"
        }
      ]
    },
    "mac": {
      "icon": "build/icons/icon.icns"
    },
    "win": {
      "icon": "build/icons/aims.ico",
      "target": [
        {
          "target": "nsis",
          "arch": [
            "ia32"
          ]
        }
      ]
    },
    "linux": {
      "icon": "build/icons"
    }
  }

命令行参数(CLI)

Commands(命令):

  electron-builder build                    构建命名                      [default]
  electron-builder install-app-deps         下载app依赖
  electron-builder node-gyp-rebuild         重建自己的本机代码
  electron-builder create-self-signed-cert  为Windows应用程序创建自签名代码签名证书
  electron-builder start                    使用electronic-webpack在开发模式下运行应用程序(须臾要electron-webpack模块支持)

Building(构建参数):

  --mac, -m, -o, --macos   Build for macOS,                              [array]
  --linux, -l              Build for Linux                               [array]
  --win, -w, --windows     Build for Windows                             [array]
  --x64                    Build for x64 (64位安装包)                     [boolean]
  --ia32                   Build for ia32(32位安装包)                     [boolean]
  --armv7l                 Build for armv7l                              [boolean]
  --arm64                  Build for arm64                               [boolean]
  --dir                    Build unpacked dir. Useful to test.           [boolean]
  --prepackaged, --pd      预打包应用程序的路径(以可分发的格式打包)
  --projectDir, --project  项目目录的路径。 默认为当前工作目录。
  --config, -c             配置文件路径。 默认为`electron-builder.yml`(或`js`,或`js5`)

Publishing(发布):

  --publish, -p  发布到GitHub Releases [choices: "onTag", "onTagOrDraft", "always", "never", undefined]

<font color="red">Deprecated(废弃):</font>

  --draft       请改为在GitHub发布选项中设置releaseType                 [boolean]
  --prerelease  请改为在GitHub发布选项中设置releaseType                 [boolean]
  --platform    目标平台 (请更改为选项 --mac, --win or --linux)
           [choices: "mac", "win", "linux", "darwin", "win32", "all", undefined]
  --arch        目标arch (请更改为选项 --x64 or --ia32)
                   [choices: "ia32", "x64", "armv7l", "arm64", "all", undefined]

Other(其他):

  --help     Show help                                                 [boolean]
  --version  Show version number                                       [boolean]

Examples(例子):

  electron-builder -mwl                        为macOS,Windows和Linux构建(同时构建)
  electron-builder --linux deb tar.xz          为Linux构建deb和tar.xz
  electron-builder -c.extraMetadata.foo=bar    将package.js属性`foo`设置为`bar`
  electron-builder --config.nsis.unicode=false 为NSIS配置unicode选项
    

TargetConfiguration(构建目标配置):

target:  String - 目标名称,例如snap.
arch “x64” | “ia32” | “armv7l” | “arm64”> | “x64” | “ia32” | “armv7l” | “arm64”  -arch支持列表

常见的错误

  • NPM下载的问题

    因为NPM在国内比较慢。导致electron-V.xxxx.zip下载失败。这些东西如果是第一次打包的话是需要下载对应electron版本的支持文件。解决办法有两个

    1. 设置镜像:在C盘User中找到.npmrc文件。然后加入下面这句代码,但是这个有时候也不是很好用
    ELECTRON_MIRROR=http://npm.taobao.org/mirrors/electron/
    1. 直接去淘宝镜像文件库找到对应的文件并下载,放到指定的目录下,electron的淘宝镜像地址。下载完之后放到指定的文件。一般文件得地址在C:\Users\Administrator\AppData\Local\electron\Cache。例如我要下载1.8.4版本的electron,那么找到镜像下得文件然后放到指定文件夹中。

图片描述
图片描述

这就解决了这个问题,简单又暴力。

  • NSIS下载问题

    如果你要打NSIS得包还需要西再下载nsis-resources-xxx等等包。经过上面得经验这下我们知道缺什么就填什么呗,通过错误日志我们可以得到我们要下载得版本,一般错误中通常会展示github下载地址,直接点开连接去下载。但是位置这次不一样了。因为这是electron-builder的支持环境所以我们要放在C:\Users\Administrator\AppData\Local\electron-builder\cache\nsis\下了。
    图片描述

一般情况下解决这些问题的思路就是,缺什么拿什么😄。

总结

electron-builder是一个简单又强大的库。反正我是很服

Demo地址

原文地址 如果觉得有用得话给个⭐吧

查看原文

依晨 赞了文章 · 2019-07-07

electron-builder打包见解

Electron-builder打包详解

开发electron客户端程序,打包是绕不开的问题。下面就我在工作中的经验以及目前对electron-builder的了解来分享一些心得。

基本概念

官网的定义

A complete solution to package and build a ready for distribution Electron app for macOS, Windows and Linux with “auto update” support out of the box.

关于electronelectron-builder的基础部分这篇文章就跳过了,有兴趣的话可以看这篇文章

如何使用

builder的使用和配置都是很简单的
builder配置有两种方式

  • package.json中直接配置使用(比较常用,我们下面着重来讲这个)
  • 指定electron-builder.yml文件

demo地址会在文章末尾给出(demo项目中electron使用得是V2.0.7版本,目前更高得是2.0.8版本)。

下面是一个简单的package.js中带注释的配置

  1. 基础配置
"build": {  // 这里是electron-builder的配置
    "productName":"xxxx",//项目名 这也是生成的exe文件的前缀名
    "appId": "com.xxx.xxxxx",//包名  
    "copyright":"xxxx",//版权  信息
    "directories": { // 输出文件夹
      "output": "build"
    }, 
    // windows相关的配置
    "win": {  
      "icon": "xxx/icon.ico"//图标路径 
    }  
  }

在配置文件中加入以上的文件之后就可以打包出来简单的<font clolor="red">文件夹</font>,文件夹肯定不是我们想要的东西。下一步我们来继续讲别的配置。

  1. 打包目标配置

要打包成安装程序的话我们有两种方式,

  1. 使用NSIS工具对我们的文件夹再进行一次打包,打包成exe
  2. 通过electron-builder的nsis直接打包成exe,配置如下
"win": {  // 更改build下选项
    "icon": "build/icons/aims.ico",
    "target": [
      {
        "target": "nsis" // 我们要的目标安装包
      }
    ]
  },
  1. 其他平台配置
  "dmg": { // macOSdmg
    "contents": [
      ...
    ]
    },
    "mac": {  // mac
      "icon": "build/icons/icon.icns"
    },
    "linux": { // linux
      "icon": "build/icons"
    }
  1. nsis配置

这个要详细的讲一下,这个nsis的配置指的是安装过程的配置,其实还是很重要的,如果不配置nsis那么应用程序就会自动的安装在C盘。没有用户选择的余地,这样肯定是不行的

关于nsis的配置是在build中nsis这个选项中进行配置,下面是部分nsis配置

"nsis": {
  "oneClick": false, // 是否一键安装
  "allowElevation": true, // 允许请求提升。 如果为false,则用户必须使用提升的权限重新启动安装程序。
  "allowToChangeInstallationDirectory": true, // 允许修改安装目录
  "installerIcon": "./build/icons/aaa.ico",// 安装图标
  "uninstallerIcon": "./build/icons/bbb.ico",//卸载图标
  "installerHeaderIcon": "./build/icons/aaa.ico", // 安装时头部图标
  "createDesktopShortcut": true, // 创建桌面图标
  "createStartMenuShortcut": true,// 创建开始菜单图标
  "shortcutName": "xxxx", // 图标名称
  "include": "build/script/installer.nsh", // 包含的自定义nsis脚本 这个对于构建需求严格得安装过程相当有用。
  "script" : "build/script/installer.nsh" // NSIS脚本的路径,用于自定义安装程序。 默认为build / installer.nsi  
},

关于includescript 到底选择哪一个 ?

在对个性化安装过程需求并不复杂,只是需要修改一下安装位置,卸载提示等等的简单操作建议使用include配置,如果你需要炫酷的安装过程,建议使用script进行完全自定义。

NSIS对于处理安装包这种东西,功能非常的强大。但是学习起来并不比一门高级语言要容易。其中的奥秘还要各位大佬自行探索

这里上一些学习资源

  1. 关于操作系统的配置

主要是windows中64和32位的配置

CLI参数

electron-builder --ia32 // 32位
electron-builder        // 64位(默认)

nsis中配置

"win": {
  "icon": "build/icons/aims.ico",
  "target": [
    {
      "target": "nsis",
      "arch": [ // 这个意思是打出来32 bit + 64 bit的包,但是要注意:这样打包出来的安装包体积比较大,所以建议直接打32的安装包。
        "x64", 
        "ia32"
      ]
    }
  ]
}
  1. 更新配置

下面这个是给更新用的配置,主要是为了生成lastest.yaml配置文件

"publish": [
  {
    "provider": "generic", // 服务器提供商 也可以是GitHub等等
    "url": "http://xxxxx/" // 服务器地址
  }
],

完整配置

基本上可用的完整的配置

"build": {
    "productName":"xxxx",//项目名 这也是生成的exe文件的前缀名
    "appId": "com.leon.xxxxx",//包名  
    "copyright":"xxxx",//版权  信息
    "directories": { // 输出文件夹
      "output": "build"
    }, 
    "nsis": {
      "oneClick": false, // 是否一键安装
      "allowElevation": true, // 允许请求提升。 如果为false,则用户必须使用提升的权限重新启动安装程序。
      "allowToChangeInstallationDirectory": true, // 允许修改安装目录
      "installerIcon": "./build/icons/aaa.ico",// 安装图标
      "uninstallerIcon": "./build/icons/bbb.ico",//卸载图标
      "installerHeaderIcon": "./build/icons/aaa.ico", // 安装时头部图标
      "createDesktopShortcut": true, // 创建桌面图标
      "createStartMenuShortcut": true,// 创建开始菜单图标
      "shortcutName": "xxxx", // 图标名称
      "include": "build/script/installer.nsh", // 包含的自定义nsis脚本
    },
    "publish": [
      {
        "provider": "generic", // 服务器提供商 也可以是GitHub等等
        "url": "http://xxxxx/" // 服务器地址
      }
    ],
    "files": [
      "dist/electron/**/*"
    ],
    "dmg": {
      "contents": [
        {
          "x": 410,
          "y": 150,
          "type": "link",
          "path": "/Applications"
        },
        {
          "x": 130,
          "y": 150,
          "type": "file"
        }
      ]
    },
    "mac": {
      "icon": "build/icons/icon.icns"
    },
    "win": {
      "icon": "build/icons/aims.ico",
      "target": [
        {
          "target": "nsis",
          "arch": [
            "ia32"
          ]
        }
      ]
    },
    "linux": {
      "icon": "build/icons"
    }
  }

命令行参数(CLI)

Commands(命令):

  electron-builder build                    构建命名                      [default]
  electron-builder install-app-deps         下载app依赖
  electron-builder node-gyp-rebuild         重建自己的本机代码
  electron-builder create-self-signed-cert  为Windows应用程序创建自签名代码签名证书
  electron-builder start                    使用electronic-webpack在开发模式下运行应用程序(须臾要electron-webpack模块支持)

Building(构建参数):

  --mac, -m, -o, --macos   Build for macOS,                              [array]
  --linux, -l              Build for Linux                               [array]
  --win, -w, --windows     Build for Windows                             [array]
  --x64                    Build for x64 (64位安装包)                     [boolean]
  --ia32                   Build for ia32(32位安装包)                     [boolean]
  --armv7l                 Build for armv7l                              [boolean]
  --arm64                  Build for arm64                               [boolean]
  --dir                    Build unpacked dir. Useful to test.           [boolean]
  --prepackaged, --pd      预打包应用程序的路径(以可分发的格式打包)
  --projectDir, --project  项目目录的路径。 默认为当前工作目录。
  --config, -c             配置文件路径。 默认为`electron-builder.yml`(或`js`,或`js5`)

Publishing(发布):

  --publish, -p  发布到GitHub Releases [choices: "onTag", "onTagOrDraft", "always", "never", undefined]

<font color="red">Deprecated(废弃):</font>

  --draft       请改为在GitHub发布选项中设置releaseType                 [boolean]
  --prerelease  请改为在GitHub发布选项中设置releaseType                 [boolean]
  --platform    目标平台 (请更改为选项 --mac, --win or --linux)
           [choices: "mac", "win", "linux", "darwin", "win32", "all", undefined]
  --arch        目标arch (请更改为选项 --x64 or --ia32)
                   [choices: "ia32", "x64", "armv7l", "arm64", "all", undefined]

Other(其他):

  --help     Show help                                                 [boolean]
  --version  Show version number                                       [boolean]

Examples(例子):

  electron-builder -mwl                        为macOS,Windows和Linux构建(同时构建)
  electron-builder --linux deb tar.xz          为Linux构建deb和tar.xz
  electron-builder -c.extraMetadata.foo=bar    将package.js属性`foo`设置为`bar`
  electron-builder --config.nsis.unicode=false 为NSIS配置unicode选项
    

TargetConfiguration(构建目标配置):

target:  String - 目标名称,例如snap.
arch “x64” | “ia32” | “armv7l” | “arm64”> | “x64” | “ia32” | “armv7l” | “arm64”  -arch支持列表

常见的错误

  • NPM下载的问题

    因为NPM在国内比较慢。导致electron-V.xxxx.zip下载失败。这些东西如果是第一次打包的话是需要下载对应electron版本的支持文件。解决办法有两个

    1. 设置镜像:在C盘User中找到.npmrc文件。然后加入下面这句代码,但是这个有时候也不是很好用
    ELECTRON_MIRROR=http://npm.taobao.org/mirrors/electron/
    1. 直接去淘宝镜像文件库找到对应的文件并下载,放到指定的目录下,electron的淘宝镜像地址。下载完之后放到指定的文件。一般文件得地址在C:\Users\Administrator\AppData\Local\electron\Cache。例如我要下载1.8.4版本的electron,那么找到镜像下得文件然后放到指定文件夹中。

图片描述
图片描述

这就解决了这个问题,简单又暴力。

  • NSIS下载问题

    如果你要打NSIS得包还需要西再下载nsis-resources-xxx等等包。经过上面得经验这下我们知道缺什么就填什么呗,通过错误日志我们可以得到我们要下载得版本,一般错误中通常会展示github下载地址,直接点开连接去下载。但是位置这次不一样了。因为这是electron-builder的支持环境所以我们要放在C:\Users\Administrator\AppData\Local\electron-builder\cache\nsis\下了。
    图片描述

一般情况下解决这些问题的思路就是,缺什么拿什么😄。

总结

electron-builder是一个简单又强大的库。反正我是很服

Demo地址

原文地址 如果觉得有用得话给个⭐吧

查看原文

赞 45 收藏 32 评论 6

依晨 提出了问题 · 2019-06-16

crypto-js解密图片

我在electron-vue中有个加密并解密图片的逻辑,逻辑是这样:读取从服务器下载到本地的一个加密后的图片,然后使用crypto-js的AES进行解密。目前一直解密不成功,我在本地写了个demo,前端自己加密后的图片也解密不成功,但如果只是将加密后形成的字符串保存在内存中,然后对其解密,却可以解密成功。
求解密图片哪里出问题了?

代码如下:

async encrypt() {
    const eKey = '123';
    const filedir = '/Users/test/Documents/data/old.jpg';
    const c = fs.readFileSync(filedir);
    const data = new Buffer(c).toString('base64');
    let encrypted = CryptoJS.AES.encrypt(data, eKey).toString();
    this.mydata = encrypted;
    fs.writeFile('hao.jpg', encrypted, function (err) {
        if (err) {
            console.log(err);
        }
        else {
            console.log('保存成功!');
        }
    });
},
decrypt() {
    const eKey = '123';
    // 读取加密后的图片
    const filedir = '/Users/test/Documents/data/hao.jpg';
    const c = fs.readFileSync(filedir);
    const data = new Buffer(c).toString('base64');

    // let base64 = CryptoJS.AES.decrypt(this.mydata, eKey).toString(CryptoJS.enc.Utf8);
    // let base64 = CryptoJS.AES.decrypt(data, eKey).toString(CryptoJS.enc.Utf8);
    let decrypted = CryptoJS.AES.decrypt(data, eKey);
    let base64 = new Buffer(decrypted.words).toString('base64');
    
    let img = new Image();
    img.src = 'data:image/jpeg;base64,' + base64;
    img.style.height = '600px';
    img.style.width = '600px';
    document.body.append(img);
}

关注 1 回答 0

依晨 提出了问题 · 2019-05-24

解决opencv.js图像增强

有个需求:点击某按钮后,让图片有增强的效果。之前的代码是别人用python+opencv写的,如下:

self.img_cv2 = cv2.imread('test.png')
r, g, b = cv2.split(self.img_cv2)
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
b_ahe = clahe.apply(b)
g_ahe = clahe.apply(g)
r_ahe = clahe.apply(r)

self.enhence_img = cv2.merge([r_ahe, g_ahe, b_ahe])

现在我要把它改为JS,如下:

let src = this.$cv.imread(this.$refs.srcimg);
console.log(src.channels());
let claheDst = new this.$cv.Mat();
let rgbaPlanes = new this.$cv.MatVector();
this.$cv.split(src, rgbaPlanes);
let R = rgbaPlanes.get(0);
let G = rgbaPlanes.get(1);
let B = rgbaPlanes.get(2);
let tileGridSize = new this.$cv.Size(8, 8);
let clahe = new this.$cv.CLAHE(2.0, tileGridSize);
let rAhe = clahe.apply(R, claheDst);
let gAhe = clahe.apply(G, claheDst);
let bAhe = clahe.apply(B, claheDst);
let oneImg = this.$cv.merge(rAhe, claheDst);
let twoImg = this.$cv.merge(gAhe, oneImg);
let threeImg = this.$cv.merge(bAhe, twoImg);
this.$cv.imshow(this.$refs.dstimg, threeImg);
src.delete();
rgbaPlanes.delete();

但是总是运行到merge时报错『TypeError: Cannot read property '$$' of undefined』。opencv.js中的merge方法必须接收2个参数。求解决报错方案

关注 1 回答 1

依晨 提出了问题 · 2019-05-15

图片上画矩形、圆、不规则图形

有个需求:在PC端的图片上可以画矩形、圆、不规则图形,需要将画的图形的坐标传给后端,然后还要根据后端传来的图片与坐标点,再在图片上绘出之前画的图形。求大神给指个方向,是不是只能用canvas了?有现成的框架支持么?谢谢!

关注 2 回答 1

依晨 赞了文章 · 2019-03-14

webpack踩坑记——DllPlugin和DllReferencePlugin

场景描述:

使用vue-cli官方提供的webpack项目,进行钉钉移动端的开发,项目中期遇到文件限制大小的问题,钉钉要求一个js不能超过5MB,而npm run dev的时候,并不会把app.js进行拆分,所以所有的依赖库都在app.js里,太大了。
于是就有了这个需求————在dev环境里,也把依赖库拆出来。

方案一:

npm run build里的方法拷贝过来,其实就是CommonsChunkPlugin

原配置里的写法是把node_modules里的库都打包到vendor.js里,这个方法里可以自己配置,返回个布尔值就可以了。
拷贝过来以后,发现app.js是变小了,但是vendor.js巨大,5.2mb,还是超了,而且,理论上这些库(vue全家桶、mint、g2)加起来也不应该这么大呀。

方案二:

把依赖的库,先整体都不作处理地打包出来,剩余自己的app.js还是该怎么样怎么样,webpack给我们提供了这么个插件DllPlugin

新建一个配置文件,比如build/webpack.dll.conf.js

const path = require('path')
const webpack = require('webpack')
module.exports = {
  entry: {
    vendor: ['vue','vue-router']
  },
  output: {
    path: path.join(__dirname, '../static'),
    filename: 'dll.[name].js',
    library: '[name]'
  },
  plugins: [
    new webpack.DllPlugin({
      path: path.join(__dirname, '../', '[name]-manifest.json'),
      name: '[name]'
    })
  ]
}

entry里就是先打包出来的库,output就是输出地址和名字,输出到static,因为原配置已经会把static里的内容直接复制到dist里,就不去折腾改其他地方了。
Dllplugin里的path,会输出一个vendor-manifest.json,这是用来做关联id的,打包的时候不会打包进去,所以不用放到static里
然后运行一下webpack -p --progress --config build/webpack.dll.conf.js
成功以后,static下会有dll.vendor.js,根目录下会有vendor.manifest.json
各自打开看一下,就会看到依赖库的源码和匹配id

ok,到这里,抽离依赖库的事情就完成了,那么接下来问题就是怎么引用呢,怎么在dev和build跑呢?

这里补了一点dll和commonsChunk概念上的区别,commonsChunk之所以慢和大,是因为每次run的时候,都会去做一次打包,而实际上我们不会一直去更新我们引用的依赖库,所以dll的做法就等于是,事先先打包好依赖库,然后只对每次都修改的js做打包。

继续刚才的步骤

修改build/webpack.base.conf.js,添加DllReferencePlugin的配置

const manifest = require('../vendor-manifest.json')
。。。。
plugins: [
    new webpack.DllReferencePlugin({
      manifest
    })
  ]

然后我们直接打开index.html,在底部加上<script data-original="./static/dll.vendor.js"></script>
是的,就是这么简单粗暴。
运行一下npm run dev,打开f12看看网络监控,一切顺利的话,这样就ok了

接下来是打包,只需要把原来的commonsChunkPlugin的东西删掉就可以了。
npm run build --report
可以感受到速度比原来快了不是一点点

继续优化

  1. 依赖从package.json读取,而不是手写;
  2. 带上hash,以免线上缓存问题;
  3. 带上hash后,自动修改index.html里的引用;
  4. 命令太长了,最好yarn run一下就好了
const path = require('path')
const webpack = require('webpack')
const package = require('../package.json')
const AssetsPlugin = require('assets-webpack-plugin')
//读取package.json里的依赖,normalize.css除外,打包会报错
const package = require('../package.json')
let dependencies = Object.keys(package.dependencies) || []
//如果使用了chrome的vue-devtool,那打包的时候把vue也排除掉,因为压缩过的vue是不能使用vue-devtool的
dependencies = dependencies.length > 0 ? dependencies.filter(item => item !== 'vue') : []

module.exports = {
  entry: {
    vendor: dependencies
  },
  output: {
    path: path.join(__dirname, '../static'),
    filename: 'dll.[name]_[hash:6].js',
    library: '[name]_[hash:6]'
  },
  plugins: [
    new webpack.DllPlugin({
      path: path.join(__dirname, '../', '[name]-manifest.json'),
      name: '[name]_[hash:6]'
    }),
    new AssetsPlugin({
      filename: 'bundle-config.json',
      path: './'
    })
  ]
}

在build文件夹新建一个dll.js,读一下配置,跑一下webpack,输出点日志。

var ora = require('ora')
var chalk = require('chalk')
var webpack = require('webpack')
var webpackConfig = require('./webpack.dll.conf')

var spinner = ora('building for dependencies...')
spinner.start()

webpack(webpackConfig, function (err, stats) {
  spinner.stop()
  if (err) throw err
  process.stdout.write(stats.toString({
    colors: true,
    modules: false,
    children: false,
    chunks: false,
    chunkModules: false
  }) + '\n\n')

  console.log(chalk.cyan('  Build complete.\n'))
})

保存以后,到package.json里添加命令,"dll": "node build/dll.js"
运行 yarn dll 完成~

代码不在这里赘述,可以看我的 github

总结

没有最好的配置,只有最适合的,遇到问题,思考问题,解决问题

参考

  1. https://doc.webpack-china.org...
  2. http://engineering.invisionap...
  3. https://segmentfault.com/a/11...
查看原文

赞 37 收藏 27 评论 20

依晨 收藏了文章 · 2019-02-26

vue + typescript 进阶篇

vue + typescript 进阶篇

本文是继 Vue + TypeScript 新项目起手式 之后的进阶+踩坑配置,所以推荐先行阅读前文

完整阅读完之后,基本可以顺利在新项目中使用vue + typescript

另外特别注意!!!

不推荐在已有项目上强加 typescript, 因ts写法的组件跟之前的组件不兼容,若上的话需要修改之前写的组件

配置完整版可参考 vue-typescript-starter,若没配置出来,也可以对照修改配置

直接进入正题:

概览

  • ts 支持 render jsx 写法

  • ts 支持 es6 / es67

  • 配置 vuex

  • vue 识别全局方法/变量

  • 支持 mixin

  • 支持 ProvidePlugin 的全局变量,比如 lodash_

支持 render jsx 写法

这里一共分两步

  1. 首先得先让 vue 支持 jsx 写法

  2. 再让 vue 中的 ts 支持 jsx 写法

让 vue 支持 jsx

按照官方做法,安装Babel 插件

图片描述

安装依赖

npm install\
  babel-plugin-syntax-jsx\
  babel-plugin-transform-vue-jsx\
  babel-helper-vue-jsx-merge-props\
  babel-preset-es2015\
  --save-dev

.babelrc中添加:

{
  "plugins": ["transform-vue-jsx"]
}

之后就可以这些写render,如下图:

图片描述

让 ts 支持 jsx

首先配置 webpack
找到./build/webpack.base.conf.js

  • 找到resolve.extensions 里面加上.tsx 后缀

  resolve: {
    extensions: ['.js', '.vue', '.json', '.ts', '.tsx']
  }
  • 找到module.rules 修改webpack对.tsx.ts 的解析

  module: {
    rules: [
      {
        test: /\.(js|vue)$/,
        loader: 'eslint-loader',
        enforce: 'pre',
        include: [resolve('src'), resolve('test')],
        options: {
          formatter: require('eslint-friendly-formatter')
        }
      },
        // 从这里复制下面的代码就可以了
        // 如果之前按照起手式配置的同学,请替换配置
      {
        test: /\.tsx?$/,
        exclude: /node_modules/,
        enforce: 'pre',
        loader: 'tslint-loader'
      },
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: Object.assign(vueLoaderConfig, {
          loaders: {
            ts: "ts-loader",
            tsx: "babel-loader!ts-loader"
          }
        })
      },
      {
        test: /\.tsx?$/,
        exclude: /node_modules/,
        use: [
          "babel-loader",
          {
            loader: "ts-loader",
            options: { appendTsxSuffixTo: [/\.vue$/] }
          }
        ]
      },
      // 复制截止
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: [resolve('src'), resolve('test')]
      },

上面的配置,主要意思是 vue 文件识别ts/tsx代码的时候,先过一遍ts-loader,在过一遍babel-loader,我知道这听起来有点蠢,但是jsx不能不要对吧?

然后在 tsconfig.json中,添加对jsx的支持

  "compilerOptions": {
    "jsx": "preserve"
    }

之后就可以顺利在.vue单文件中的tsjsx代码了,如下图所示:

图片描述

敲黑板,这里又有重点,使用 jsx 写法的话, 一定要使用 .tsx,不要用.ts了,切记!!!

支持es6 / es7

tsconfig.json中,添加对es6 / es7的支持,更多的配置请见tsconfig - 编译选项

    "lib": [
      "dom",
      "es5",
      "es6",
      "es7",
      "es2015.promise"
    ]

图片描述

不然的话,连Object.assign 这种最基本的函数也会在ts中报错,真的令人难过

配置 vuex

这里就比较简单了

# 安装依赖
npm i vuex vuex-class --save
  • vuex:在 vue 中集中管理应用状态

  • vuex-class :在 vue-class-component 写法中 绑定 vuex

Store的配置跟原来一模一样,引用的时候有一点区别,下面的例子介绍了用法,应该一看便知,这里我不做赘述

import Vue from 'vue'
import Component from 'vue-class-component'
import {
  State,
  Getter,
  Action,
  Mutation,
  namespace
} from 'vuex-class'

const ModuleGetter = namespace('path/to/module', Getter)

@Component
export class MyComp extends Vue {
  @State('foo') stateFoo
  @State(state => state.bar) stateBar
  @Getter('foo') getterFoo
  @Action('foo') actionFoo
  @Mutation('foo') mutationFoo
  @ModuleGetter('foo') moduleGetterFoo

  // If the argument is omitted, use the property name
  // for each state/getter/action/mutation type
  @State foo
  @Getter bar
  @Action baz
  @Mutation qux

  created () {
    this.stateFoo // -> store.state.foo
    this.stateBar // -> store.state.bar
    this.getterFoo // -> store.getters.foo
    this.actionFoo({ value: true }) // -> store.dispatch('foo', { value: true })
    this.mutationFoo({ value: true }) // -> store.commit('foo', { value: true })
    this.moduleGetterFoo // -> store.getters['path/to/module/foo']
  }
}

让 vue 识别全局方法/变量

在项目中使用 ui 组件是很正常的操作

比如使用 Element-uImeesage,用法如下图:

  this.$message({
    message: '恭喜你,这是一条成功消息',
    type: 'success'
  })

但是在配置了 typescript之后

图片描述

那是因为 $message属性,并没有在 vue实例中声明

解决办法也非常简单,那我们就声明一下呗

在之前文章中创建的 src/vue-shim.d.ts文件中,增加如下代码:

// 声明全局方法
declare module 'vue/types/vue' {
  interface Vue {
    $Message: any,
    $Modal: any
  }
}

这样,之后再使用this.$message()的话就不会报错了

支持 mixin

我在vue-property-decorator里里外外找了好几圈,缺没有找到mixin这个修饰器

 // 如果全局mixin,那也太蠢了
 Vue.mixin( mixin )

找非常多的 ts + vue 项目,但是没有找到我理想的mixin的方式,
那么就自己进行探索咯,下图是我自己使用的目前最佳mixin方式:

声明了一个mixin组件,如下图:

图片描述

其实就是我在mixin中声明了声明属性 / 方法,那么我就在vue实例中声明这个属性 / 方法

使用方式如下图:

图片描述

支持 ProvidePlugin 的全局变量,比如 lodash 的 _

如果我们在项目中有使用 jquery,lodash 这样的工具库的时候,肯定不希望在所有用到的地方都import _ from ‘lodash’
@types/lodash

那我们就来配置一下:

首先还是在webpack.base.conf.js 中添加一个插件、并把这个 vendor拉出来


  entry: {
    app: './src/main.ts',
    vendor: [
      "lodash"
    ]
  }

  plugins: [
    new webpack.ProvidePlugin({
      _: 'lodash'
    })
  ]

上面的意思是,当模块使用这些变量的时候wepback会自动加载

然后,你需要告诉eslint这个 _ 是全局的

.eslintrc.js中添加

  globals: {
    _: true
  },

接下来,你还需要告诉ts这个 _ 是全局的

vue-shim.d.ts

declare global {
  const _: typeof lodash
}

如果没有上面这段声明,但是在 ts 中使用的话,会报如下的错误:

图片描述

这个问题Consider allowing access to UMD globals from modules · Issue #10178 · Microsoft/TypeScript · GitHub

有一个很简单的解释,就是害怕你全局声明的_import _ from 'lodash' 的行为不一致,这样的话,之后会留下隐患

到这里,本文的配置就到此结束

最后

本文的这些配置都是在新项目开发中,一步步用血汗踩出来的

目测已经涵盖了大部分的使用问题,如果有其他的意见或建议的话,欢迎在本文下面评论~~

再发一次,配置完整版可参考 vue-typescript-starter,若没配置出来,也可以对照修改配置

在刚上typescript的时候,我是拒绝的,嫌弃每个地方都要声明类型,不然就走不下去,但是如果让你们做以下一个选择题:

  • 在编译时发现问题

  • 还是运行时发现问题

我会毫不犹豫选择前者,这是ts强类型带给我最大的亮点

参考链接/推荐阅读

查看原文

依晨 赞了文章 · 2019-02-26

vue + typescript 进阶篇

vue + typescript 进阶篇

本文是继 Vue + TypeScript 新项目起手式 之后的进阶+踩坑配置,所以推荐先行阅读前文

完整阅读完之后,基本可以顺利在新项目中使用vue + typescript

另外特别注意!!!

不推荐在已有项目上强加 typescript, 因ts写法的组件跟之前的组件不兼容,若上的话需要修改之前写的组件

配置完整版可参考 vue-typescript-starter,若没配置出来,也可以对照修改配置

直接进入正题:

概览

  • ts 支持 render jsx 写法

  • ts 支持 es6 / es67

  • 配置 vuex

  • vue 识别全局方法/变量

  • 支持 mixin

  • 支持 ProvidePlugin 的全局变量,比如 lodash_

支持 render jsx 写法

这里一共分两步

  1. 首先得先让 vue 支持 jsx 写法

  2. 再让 vue 中的 ts 支持 jsx 写法

让 vue 支持 jsx

按照官方做法,安装Babel 插件

图片描述

安装依赖

npm install\
  babel-plugin-syntax-jsx\
  babel-plugin-transform-vue-jsx\
  babel-helper-vue-jsx-merge-props\
  babel-preset-es2015\
  --save-dev

.babelrc中添加:

{
  "plugins": ["transform-vue-jsx"]
}

之后就可以这些写render,如下图:

图片描述

让 ts 支持 jsx

首先配置 webpack
找到./build/webpack.base.conf.js

  • 找到resolve.extensions 里面加上.tsx 后缀

  resolve: {
    extensions: ['.js', '.vue', '.json', '.ts', '.tsx']
  }
  • 找到module.rules 修改webpack对.tsx.ts 的解析

  module: {
    rules: [
      {
        test: /\.(js|vue)$/,
        loader: 'eslint-loader',
        enforce: 'pre',
        include: [resolve('src'), resolve('test')],
        options: {
          formatter: require('eslint-friendly-formatter')
        }
      },
        // 从这里复制下面的代码就可以了
        // 如果之前按照起手式配置的同学,请替换配置
      {
        test: /\.tsx?$/,
        exclude: /node_modules/,
        enforce: 'pre',
        loader: 'tslint-loader'
      },
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: Object.assign(vueLoaderConfig, {
          loaders: {
            ts: "ts-loader",
            tsx: "babel-loader!ts-loader"
          }
        })
      },
      {
        test: /\.tsx?$/,
        exclude: /node_modules/,
        use: [
          "babel-loader",
          {
            loader: "ts-loader",
            options: { appendTsxSuffixTo: [/\.vue$/] }
          }
        ]
      },
      // 复制截止
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: [resolve('src'), resolve('test')]
      },

上面的配置,主要意思是 vue 文件识别ts/tsx代码的时候,先过一遍ts-loader,在过一遍babel-loader,我知道这听起来有点蠢,但是jsx不能不要对吧?

然后在 tsconfig.json中,添加对jsx的支持

  "compilerOptions": {
    "jsx": "preserve"
    }

之后就可以顺利在.vue单文件中的tsjsx代码了,如下图所示:

图片描述

敲黑板,这里又有重点,使用 jsx 写法的话, 一定要使用 .tsx,不要用.ts了,切记!!!

支持es6 / es7

tsconfig.json中,添加对es6 / es7的支持,更多的配置请见tsconfig - 编译选项

    "lib": [
      "dom",
      "es5",
      "es6",
      "es7",
      "es2015.promise"
    ]

图片描述

不然的话,连Object.assign 这种最基本的函数也会在ts中报错,真的令人难过

配置 vuex

这里就比较简单了

# 安装依赖
npm i vuex vuex-class --save
  • vuex:在 vue 中集中管理应用状态

  • vuex-class :在 vue-class-component 写法中 绑定 vuex

Store的配置跟原来一模一样,引用的时候有一点区别,下面的例子介绍了用法,应该一看便知,这里我不做赘述

import Vue from 'vue'
import Component from 'vue-class-component'
import {
  State,
  Getter,
  Action,
  Mutation,
  namespace
} from 'vuex-class'

const ModuleGetter = namespace('path/to/module', Getter)

@Component
export class MyComp extends Vue {
  @State('foo') stateFoo
  @State(state => state.bar) stateBar
  @Getter('foo') getterFoo
  @Action('foo') actionFoo
  @Mutation('foo') mutationFoo
  @ModuleGetter('foo') moduleGetterFoo

  // If the argument is omitted, use the property name
  // for each state/getter/action/mutation type
  @State foo
  @Getter bar
  @Action baz
  @Mutation qux

  created () {
    this.stateFoo // -> store.state.foo
    this.stateBar // -> store.state.bar
    this.getterFoo // -> store.getters.foo
    this.actionFoo({ value: true }) // -> store.dispatch('foo', { value: true })
    this.mutationFoo({ value: true }) // -> store.commit('foo', { value: true })
    this.moduleGetterFoo // -> store.getters['path/to/module/foo']
  }
}

让 vue 识别全局方法/变量

在项目中使用 ui 组件是很正常的操作

比如使用 Element-uImeesage,用法如下图:

  this.$message({
    message: '恭喜你,这是一条成功消息',
    type: 'success'
  })

但是在配置了 typescript之后

图片描述

那是因为 $message属性,并没有在 vue实例中声明

解决办法也非常简单,那我们就声明一下呗

在之前文章中创建的 src/vue-shim.d.ts文件中,增加如下代码:

// 声明全局方法
declare module 'vue/types/vue' {
  interface Vue {
    $Message: any,
    $Modal: any
  }
}

这样,之后再使用this.$message()的话就不会报错了

支持 mixin

我在vue-property-decorator里里外外找了好几圈,缺没有找到mixin这个修饰器

 // 如果全局mixin,那也太蠢了
 Vue.mixin( mixin )

找非常多的 ts + vue 项目,但是没有找到我理想的mixin的方式,
那么就自己进行探索咯,下图是我自己使用的目前最佳mixin方式:

声明了一个mixin组件,如下图:

图片描述

其实就是我在mixin中声明了声明属性 / 方法,那么我就在vue实例中声明这个属性 / 方法

使用方式如下图:

图片描述

支持 ProvidePlugin 的全局变量,比如 lodash 的 _

如果我们在项目中有使用 jquery,lodash 这样的工具库的时候,肯定不希望在所有用到的地方都import _ from ‘lodash’
@types/lodash

那我们就来配置一下:

首先还是在webpack.base.conf.js 中添加一个插件、并把这个 vendor拉出来


  entry: {
    app: './src/main.ts',
    vendor: [
      "lodash"
    ]
  }

  plugins: [
    new webpack.ProvidePlugin({
      _: 'lodash'
    })
  ]

上面的意思是,当模块使用这些变量的时候wepback会自动加载

然后,你需要告诉eslint这个 _ 是全局的

.eslintrc.js中添加

  globals: {
    _: true
  },

接下来,你还需要告诉ts这个 _ 是全局的

vue-shim.d.ts

declare global {
  const _: typeof lodash
}

如果没有上面这段声明,但是在 ts 中使用的话,会报如下的错误:

图片描述

这个问题Consider allowing access to UMD globals from modules · Issue #10178 · Microsoft/TypeScript · GitHub

有一个很简单的解释,就是害怕你全局声明的_import _ from 'lodash' 的行为不一致,这样的话,之后会留下隐患

到这里,本文的配置就到此结束

最后

本文的这些配置都是在新项目开发中,一步步用血汗踩出来的

目测已经涵盖了大部分的使用问题,如果有其他的意见或建议的话,欢迎在本文下面评论~~

再发一次,配置完整版可参考 vue-typescript-starter,若没配置出来,也可以对照修改配置

在刚上typescript的时候,我是拒绝的,嫌弃每个地方都要声明类型,不然就走不下去,但是如果让你们做以下一个选择题:

  • 在编译时发现问题

  • 还是运行时发现问题

我会毫不犹豫选择前者,这是ts强类型带给我最大的亮点

参考链接/推荐阅读

查看原文

赞 158 收藏 153 评论 97

认证与成就

  • 获得 12 次点赞
  • 获得 15 枚徽章 获得 1 枚金徽章, 获得 4 枚银徽章, 获得 10 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2016-09-19
个人主页被 305 人浏览