【原创】从零开始搭建Electron+Vue+Webpack项目框架(三)Electron+vue+webpack联合调试
导航:
(一)Electron跑起来
(二)从零搭建Vue全家桶+webpack项目框架
(三)Electron+Vue+Webpack,联合调试整个项目
(四)Electron配置润色
(五)预加载及自动更新
(六)构建、发布整个项目(包括client和web)
摘要:前面两篇介绍了如何启动Electron和Vue项目,但到目前为止,虽然把Electron和Vue放到了一个工程里,但貌似它们之间并没有什么关系,通过不同的命令各自运行,Vue写出的页面也无法在Electron中展示,这跟我们的标题显然是不符合的。先喊个口号,从我做起,拒绝标题党!这篇就说一下,如何把Electron和Vue通过Webpack结合起来。项目完整代码:https://github.com/luohao8023/electron-vue-template
一、新建webpack.main.config.js,webpack打包electron的常规配置。
const path=require('path'); const webpack = require('webpack'); const { dependencies } = require('../package.json'); module.exports = { mode: process.env.NODE_ENV, entry: { main: ['./src/main/main.js'], }, output: { path: path.join(process.cwd(), 'app'), libraryTarget: 'commonjs2', filename: './[name].js' }, node: { fs: 'empty', __dirname: false }, optimization: { runtimeChunk: false, minimize: true }, module: { rules: [ { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ } ] }, externals: [ ...Object.keys(dependencies || {}) ], resolve: { extensions: ['.js'] }, plugins:[ new webpack.NoEmitOnErrorsPlugin(), new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"production"' }) ], target: 'electron-main' };
二、改造dev.js,主体思路是打包主进程、打包渲染进程,启动electron应用。
process.env.NODE_ENV = 'development';//开发模式 const webpack = require('webpack'); const WebpackDevServer = require('webpack-dev-server'); const webpackHotMiddleware = require('webpack-hot-middleware'); const chalk = require('chalk'); const http = require('http'); const { spawn } = require('child_process'); const electron = require('electron'); const path = require('path'); // 构建主进程 function buildMain() { const mainWebpackConfig = require('./webpack.main.config.js'); return new Promise((resolve, reject) => { console.log('打包APP主进程......'); let log = ''; // 删除历史打包数据 require('del')(['./app/main.js']); const mainCompiler = webpack(mainWebpackConfig); mainCompiler.run((err, stats) => { let errorInfo = ''; if (err) { console.log('打包主进程遇到Error!'); reject(chalk.red(err)); } else { Object.keys(stats.compilation.assets).forEach(key => { log += chalk.blue(key) + '\n'; }) stats.compilation.warnings.forEach(key => { log += chalk.yellow(key) + '\n'; }) stats.compilation.errors.forEach(key => { errorInfo += chalk.red(`${key}:${stats.compilation.errors[key]}`) + '\n'; }) log += errorInfo+ chalk.green(`time:${(stats.endTime-stats.startTime)/1000} s\n`) + "\n"; if(errorInfo){ reject(errorInfo) }else{ resolve(log); } console.log('打包主进程完毕!', log); } }); }); } // 构建渲染进程 function devRender() { console.log('启动渲染进程调试......'); const webpackDevConfig = require('./webpack.render.config.js'); const compiler = webpack(webpackDevConfig); new WebpackDevServer( compiler, { contentBase: webpackDevConfig.output.path, publicPath: webpackDevConfig.output.publicPath, open: true,//打开默认浏览器 inline: true,//刷新模式 hot: true,//热更新 quiet: true,//除第一次编译外,其余不显示编译信息 progress: true,//显示打包进度 setup(app) { app.use(webpackHotMiddleware(compiler)); app.use('*', (req, res, next) => { if (String(req.originalUrl).indexOf('.html') > 0) { console.log(req.originalUrl) getHtml(res); } else { next(); } }); } } ).listen(8099, function(err) { if (err) return console.log(err); console.log(`Listening at http://localhost:8099`); }); compiler.hooks.done.tap('doneCallback', (stats) => { const compilation = stats.compilation; Object.keys(compilation.assets).forEach(key => console.log(chalk.blue(key))); compilation.warnings.forEach(key => console.log(chalk.yellow(key))); compilation.errors.forEach(key => console.log(chalk.red(`${key}:${stats.compilation.errors[key]}`))); console.log(chalk.green(`${chalk.white('渲染进程调试完毕\n')}time:${(stats.endTime-stats.startTime)/1000} s`)); }); } // 启动Electron function startElectron() { let electronProcess = spawn(electron, [path.join(process.cwd(), 'app/main.js')]); electronProcess.stdout.on('data', data => { // 正常输出为蓝色 electronLog(data, 'blue'); }); electronProcess.stderr.on('data', data => { // 错误信息为红色 electronLog(data, 'red'); }); } // 美化输出 function electronLog(data, color) { let log = ''; data.toString().split(/\r?\n/).forEach(line => { log += `\n${line}`; }); if (/[0-9A-z]+/.test(log)) { console.log( chalk[color].bold('┏ Electron -------------------') + log + chalk[color].bold('┗ ----------------------------') ); } } function getHtml(res) { http.get(`http://localhost:8099`, (response) => { response.pipe(res); }).on('error', (err) => { console.log(err); }); } // 构建 function build() { Promise.all([buildMain(), devRender()]).then(() => { startElectron(); }).catch(err => { console.log(err); process.exit(); }); } build();
运行npm run dev,迎接即将出现的各种错误吧!以下不是运行一次时出现的错误,而是每次解决完报错之后默认运行一次。
1、找不到babel-loader,这个简单,装一个就行了,npm i babel-loader -D。
2、看样子是babel-loader还需要其他依赖,按提示安装,npm i @babel/core -D。
3、直接打开了浏览器有木有?!!在dev.js中62行,把打开默认浏览器的选项设为false,即open:false。
4、再运行,electron可以正常打开了,但显示但内容却不对,无论怎么修改vue文件重新运行,都不会起作用。找到src/main/main.js,把
win.loadURL(encodeURI(indexPath));
这一行改为
win.loadURL('http://localhost:8099');
发现之前代码的一个错误,win变量是在createWindow函数中声明但,但在函数外部却有调用,修改一下。
再次运行,可以正常启动,而且显示的是vue文件中的内容,尝试修改一下页面内容,热刷新也是正常的。
至此,我们已经可以正常的进行本地开发调试了,再说一下大致的思路:
第一篇和第二篇主要是把完整的Electron项目和Vue项目融合到一个工程里,本篇主要是增加了主进程的webpack配置,并改造了dev.js,在这个脚本中主要做了下面的事:启动渲染进程的打包(这里是devServer),打包主进程,以main.js为入口,输出位置为app文件夹,之后用node的spawn模块,以electron命令执行app文件夹下的main.js,启动electron,并把electron的页面地址只想devServer地址,这就是本地调试的大致逻辑了。文字不太多,主要是理解webpack配置及调试脚本。后面会介绍到构建整个项目,并打包exe安装文件,以及其他的一些配置优化、项目结构优化。