从零搭建基于webpack的Electron-Vue3项目(1)——基于webpack的Vue3项目搭建
阅读原文时间:2023年07月09日阅读:1

从零搭建基于webpack的Electron-Vue3项目(1)——基于webpack的Vue3项目搭建

本篇文章内容,主要是基于webpack的Vue3项目开发环境进行搭建,暂时还不涉及到Electron的整合。可以独立的当作一个内容来进行阅读。

创建目录electron-vue3-webpack并进入执行npm init命令。设置了基础的项目信息后,我们开始本次的环境搭建之旅。

前置条件

基本熟悉webpack是什么以及它打包的运行处理过程。

环境准备

前端编写

项目根目录创建src\renderer目录,用于存放前端代码。向其中编写一个简单的前端页面以及JS:

  1. src\renderer\index.html


    It's Title

  2. src\renderer\main.js

    console.log(document.title); // 控制台输出html的title

webpack安装

命令行执行npm install -D webpack

HtmlWebpackPlugin插件安装

接下来安装webpack插件HtmlWebpackPlugin。该插件主要生成html,这里我们配置该插件template为上述index.html,配置方式见下文webpack插件配置部分。执行命令npm install --save-dev html-webpack-plugin

配置webpack

在项目根目录下创建webpack.config.js,内容如下:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: './src/renderer/main.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/renderer/index.html' // 配置html模板文件
    })
  ]
};

至此,一个webpack的基础环境配置完成:

打包运行

  1. 命令行执行npx webpack
  2. 完成编译检查dist目录;
  3. 使用浏览器打开dist/index.html,并检查控制台。

实际上,当你查看dist中的html的时候你会发现,webpack打包为我们引入了生成的js:

目前为止,我们完成了webpack的基础环境搭建以及试运行。接下来我们将引入Vue3,并以webpack打包的方式来进行Vue3的项目搭建。

实际上,无论是Vue2还是Vue3,引入Vue并使用webpack来进行打包的原理都是一样的。

引入Vue3、Vue-Router以及Vuex组件

安装Vue3

# 最新稳定版
$ npm install vue@next -S

创建App.vue根组件

renderer目录下创建App.vue文件,内容如下:

<template>
  <router-view/> <!-- router-view必须要引入Vue-Router组件,下文进行配置 -->
</template>
<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

安装Vue-Router

$ npm install vue-router@next -S

创建Router实例

renderer目录下创建router目录,并增加index.js文件,其中内容如下:

import {createRouter, createWebHashHistory} from 'vue-router';

const routes = [ // 路由内容暂不配置
  // {
  //   path: '/',
  //   name: 'Home',
  //   component: Home
  // },
];

const router = createRouter({
  history: createWebHashHistory(),
  routes
});

export default router;

安装Vuex

$ npm install vuex@next -S

创建Vuex实例

renderer目录下创建store目录,并增加index.js文件,其中内容如下:

import {createStore} from 'vuex';

export default createStore({
  state: {},
  mutations: {},
  actions: {},
  modules: {}
});

编写main.js以及模板html

  1. src\renderer\index.html中的<body>标签中,添加<div id="app"></div>



    It's Title


  2. 修改src\renderer\main.js代码:

    import { createApp } from 'vue'
    import App from './App.vue' // 引入上述
    import router from './router' // 引入上述编写的router实例
    import store from './store' // 引入上述vuex实例

    createApp(App).use(store).use(router).mount('#app') // 链式加载,"#app"指代模板中

至此,目前的项目结构如下:

然而,当我们进行了上述一系列的配置以后,再次运行webpack却发现,报错了:

从报错信息可以看出,当webpack想要解析App.vue文件时候,报错提示我们:You may need an appropriate loader to handle this file type(你或许需要一个对应的loader来处理这种文件类型)。

了解到webpack打包机制的读者很容易知道,我们需要安装Vue对应的loader

安装Vue-Loader

较为熟悉的同学可能立马去官方的指引文件看文档了,其实官方文档也已经相当详细的说明了Vue-Loader的配置方式,详情见链接。(注意:截至2020/11/22文档还是Vue2版本下的配置,也许你在看的时候,已经正式替换为了Vue3的正确配置了)

  1. 安装vue-loader

  2. 修改webpack.config.js配置。

我也天真的以为Vue3的webpack也是这样的,然而最后吃了闭门羹。实际上,Vue3大不一样,接下来将有一系列的组件进行安装,在此之前我们先记录下当前的依赖:

  "devDependencies": {
    "html-webpack-plugin": "^4.5.0",
    "webpack": "^5.6.0",
    "webpack-cli": "^4.2.0"
  },
  "dependencies": {
    "vue": "^3.0.2",
    "vue-router": "^4.0.0-rc.5",
    "vuex": "^4.0.0-rc.1"
  }

接着说Vue3的改变。事实上,Vue3的loader已经有了如下的变更:

  1. vue-loadernpm包为截至2020/11/22,版本为:v16.0.0-rc.2,该版本安装时需要指定版本

    npm install -D vue-loader@v16.0.0-rc.2

  2. VueLoaderPlugin 的导入方式已经发生改变:

    // 原先方式:
    const VueLoaderPlugin = require('vue-loader/lib/plugin')
    // 全新方式:
    const { VueLoaderPlugin } = require('vue-loader')

  3. 移除vue-template-compiler,新增了@vue/compiler-sfc

vue-template-compiler在Vue2中是配合vue-loader组件,在Vue3中被移除了,取而代之的事@vue/compiler-sfc

npm install -D @vue/compiler-sfc

当然,为了让webpack处理.vue文件中的<style>块,以及各种css、图片等资源,我们添加一些常规的loader

npm install -D css-loader style-loader file-loader

至此,我们的package.json的依赖部分如下,大家自行对比:

  "devDependencies": {
    "@vue/compiler-sfc": "^3.0.2",
    "css-loader": "^5.0.1",
    "file-loader": "^6.2.0",
    "html-webpack-plugin": "^4.5.0",
    "style-loader": "^2.0.0",
    "vue-loader": "^16.0.0-rc.2",
    "webpack": "^5.6.0",
    "webpack-cli": "^4.2.0"
  },
  "dependencies": {
    "vue": "^3.0.2",
    "vue-router": "^4.0.0-rc.5",
    "vuex": "^4.0.0-rc.1"
  }

配置webpack

在上述步骤的基础上,我们终于可以进行webpack的配置编写,直接上配置:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {VueLoaderPlugin} = require('vue-loader');
module.exports = {
  entry: './src/renderer/main.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader'
        ]
      },
      {
        test: /\.(png|jpg|gif|svg)$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              esModule: false, // esModule主要为了处理require图片报错的问题
            }
          }
        ],
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/renderer/index.html'
    }),

    // 请确保引入这个插件来施展魔法
    new VueLoaderPlugin()
  ]
};

这个配置的每一项的功能,不再赘述。不清楚的可以复习一下webpack配置,链接在此

webpack打包Vue3

迎来最后一步打包操作:npx webpack,输出对应成功结果,然后在dist目录下检查一下打包结果即可。

$ npx webpack

后续

当然,目前的demo没有任何实质的代码,还不能确保我们的工程有效。接下来的内容,我们继续编写Vue3的组件,完成一个简单的涵盖路由跳转的Example。

编写Example

编写一个Home页面、Plus页面的example,涵盖Vue3的一些新特性,API具体功能见官方网站。Example具体内容:

  • Home页面包含一个add按钮,以及可点击的卡片GO Plus Page
  • 当点击add按钮时,页面上数字+1,同时会调用VuexAPI来完成向store.number的提交+1操作;
  • 点击GO Plus Page卡片,调用Vue RouterAPI跳转到Plus页面;
  • Plus界面会显示store.number当前值,以及完成加载后会以50%概率随机加载2张(edge和chrome图标)图片的其中1张。

整体结构

Home、Plus页面

<!-- Home.vue -->
<template>
  <div class="home">
    <p>{{ 'number = ' + number }}</p>
    <button @click="add">add</button>
    <div class="card" @click="cardClick">
      <p>GO Plug Page</p>
    </div>
  </div>
</template>

<script>
// 从 vue 中引入 ref 函数
import {ref} from 'vue';
import {useRouter} from "vue-router";
import {useStore} from "vuex";

export default {
  name: "Home",
  setup() {
    // 用 ref 函数包装一个响应式变量 number
    let number = ref(0);

    let store = useStore();

    let router = useRouter();

    // 设定一个方法
    function add() {
      // number是被ref函数包装过了的,其值保存在.value中
      number.value++;
      // Vuex提交
      store.commit('plus', {num: 1});
    }

    // 定义卡片点击,进行路由跳转
    function cardClick() {
      router.push('/plus');
    }

    // 将 成员 返回出去,供template中使用
    return {number, add, cardClick};
  }
};
</script>
<style scoped>
</style>

<!-- Plus.vue -->
<template>
  <div style="text-align: center">
    <p>Refresh this page and you will see the icon change</p>
    <div class="img-display">
      <img :src="imgSrc" alt="">
    </div>
    <div class="number-display">
      <p>{{ 'state.number = ' + state.number }}</p>
    </div>
  </div>
</template>

<script>
import {useStore} from "vuex";
import {computed} from "vue";

export default {
  name: "Plus",
  setup() {
    const state = useStore().state;
    const imgSrc = computed(() => {
      let randomBool = (Math.ceil(Math.random() * 10)) % 2 === 0;
      if (randomBool) {
        return require('../assets/img/edge.png');
      } else {
        return require('../assets/img/chrome.png');
      }
    }); // 计算属性
    return {
      state,
      imgSrc
    };
  }
};
</script>
<style scoped>
</style>

Vuex、Router

// ---------------------------
// src/renderer/store/index.js
// ---------------------------
import {createStore} from 'vuex';

export default createStore({
  state: {
    number: 0,
  },
  mutations: {
    plus(state, {num}) {
      state.number += num;
    }
  },
  actions: {},
  modules: {}
});

// ----------------------------
// src/renderer/router/index.js
// ----------------------------
import {createRouter, createWebHashHistory} from 'vue-router';
// 定义路由组件, 注意,这里一定要使用 文件的全名(包含文件后缀名)
import Home from "../views/Home.vue";
import Plus from "../views/Plus.vue";

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/plus',
    name: 'Plus',
    component: Plus
  }
];

const router = createRouter({
  history: createWebHashHistory(),
  routes
});

export default router;

效果

PS:Plug页面刷新后,会清空state.number的内容所以归零。

上述进行开发的过程中,我们的流程始终的开发过程为:

  1. webpack配置;
  2. 项目编码;
  3. npx webpack
  4. 通过IDE查看页面结果,若需要修改,则回到步骤2。

实际开发中,我们会发现如下的一个问题:文件改动后,需要重新执行npx webpack命令完成编译,耗时麻烦。

watch参数

这个过程解决起来特别的简单,webpack实际上提供了运行参数watch,该参数简单来说就是在第一次调用以后并不会直接退出webpack,而会有一个单独的进程来监听webpack配置中指定入口代码执行以来整个依赖文件的变动,一旦发生了改变,则会立刻进行重新编译。关于watch参数详情,见此链接:Watch and WatchOptions | webpack

使用方式简单来说:

  • 启动 webpack 命令时,带上--watch参数
  • 配置 webpack.config.js 中设置 watch:true

这里我们使用前者

// package.json
...
"scripts": {
    "build-vue3-with-webpack": "npx webpack --watch" // 添加watch参数
},
...

运行脚本后,你会看到控制台并不会在完成编译后退出,而是hold住的,且命令行最下方显示:

webpack 5.6.0 compiled successfully in 4621 ms
[webpack-cli] watching files for updates... // 监听文件更新

每当你修改了文件,你会发现命令行会有编译更新:

其实到了这一步后,开发效率已经有了很大的提升。然而有想法的开发人员会发现,倘若每次进行更新了文件,就要重新等待webpack进行编译,随着项目文件的越来越多,时间也会越来越就久,甚至到了后期,编译一次就要几分钟。其实webpack还有一个超级有用的开发者工具:webpack-dev-server

webpack-dev-server

使用webpack-dev-server的好处是修改了后不需要刷新浏览器,同时,该模块不会将文件编译输出为最终文件,而是放在内存中,转换起来很快。

安装

$ npm install webpack-dev-server -D // 请注意 —D 开发依赖

配置以及使用

webpack.config.js

需要在webpack配置文件中增加devServer节点,告知 dev server,从什么位置查找文件(但并不意味着会生成文件到该指定位置):

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {VueLoaderPlugin} = require('vue-loader');
module.exports = {
  entry: './src/renderer/main.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  devServer: { // 新增devServer节点
    contentBase: './dist', // 告知dev server从什么位置查找
  },
  ...
}
package.json/scripts

添加一个可以直接运行 dev server 的 script:

{
  "name": "electron-vue3-webpack",
  ...
  "scripts": {
    "build-vue3-with-webpack": "npx webpack --watch",
    "start": "webpack serve --open" // 新增脚本,启动webpack-dev-server
  },
  ...
}

完成配置后,我们关闭之前的"npx webpack --watch",使用新的start脚本运行程序。然后进行开发:

这里和上面watch的区别在于:

  1. 没有实际的dist目录以及编译后的前端文件生成;
  2. 浏览器页面无需手动刷新页面,会自动进行;

关于webpack-dev-server的相关信息,可以进一步参考官方文档:开发环境 | webpack (docschina.org)