Vue 项目之 Webpack 打包 Vue 代码(4)

532 阅读5分钟

「这是我参与11月更文挑战的第23天,活动详情查看:2021最后一次更文挑战

1. 编写 App.vue 文件

前面说到,我们要把 template 选项中的内容、data 选项中的内容以及样式代码都放在一个文件中,就可以使用 .vue 文件。具体怎么编写呢?我们可以这样来做:比如在 src 目录下新建 vue 文件夹,在 vue 文件夹下新建 App.vue 文件,文件内容如下:

<template>
  <h2>我是 Vue 渲染出来的</h2>
  <h2>{{ title }}</h2>
</template>

<script>
// 在当前文件中导出对象,之后在其它文件中就可以导入使用了
export default {
  data() {
    return {
      title: '你好'
    }
  },
  // 还可以有其它选项
  methods: {

  }
};
</script>

<style>
h2 {
  color: #f66;
}
</style>

把模板、脚本和样式代码都编写到这个 App.vue 文件中后,之前的有关代码就可以不要了,我们来对 src/main.js 文件做修改:

// 第三方库的导入可以放最上面(个人习惯)
import { createApp } from 'vue/dist/vue.esm-bundler';

import { sum } from './js/math';
const { formatPrice } = require('./js/format');
// 导入 App.vue 文件中导出的对象(你可能会问,App.vue 导出的对象中没有 template 啊?不用担心,我们后面会使用 loader 对这个 .vue 文件做编译,编译之后,会自动把 template 中的内容放进 script 中的),注意:这里的 .vue 文件扩展名不能省略哦
import App from './vue/App.vue';

import './js/element';

console.log(sum(10, 20));
console.log(formatPrice(100));

// 编写 Vue 相关的代码
// const app = createApp({
//   template: '#my-app',
//   data() {
//     return {
//       title: '你好'
//     }
//   }
// });
const app = createApp(App);
app.mount('#app');

修改完成后,我们来运行 npm run build 命令对项目代码编译打包:

image-20211122192841383

你会发现打包报错了,很显然,这是因为我们没有给 webpack 配置相应的 loader 来处理 .vue 类型的文件。那么我们需要哪个 loader 来帮助我们处理 .vue 文件呢?没错,正是 vue-loader,我们先来安装它:

npm install vue-loader@next -D

注意:

vue-loader 是用来处理 Vue2 的代码的,而我们现在写的是 Vue3 的代码,所以需要使用 vue-loader@next

因为处理 .vue 文件的过程只存在于开发时,所以这里使用 -D

安装好之后,我们再去 webpack.config.js 中添加配置:

...

module.exports = {
  ...
  module: {
    rules: [
      ...
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      }
    ]
  },
  ...
}

再来进行打包之前,为了便于讲解,我们先重新安装一下 Vue,这次指定安装的版本为 3.2.12

npm install vue@3.2.12

重新安装完成后,我们来运行 npm run build 命令进行打包:

image-20211122222912952

你会发现,仍然有一个错误需要解决,这次错误的大意是:我们当前使用的 Vue 需要是 3.2.13 以上的版本(包括 3.2.13 版本),或者需要在依赖树中有 @vue/compiler-sfc。也就是说,要解决这个错误,我们有两种方案:

  1. 重新安装 >=3.2.13 版本的 Vue
  2. 仍然用当前 3.2.12 版本的 Vue,但需要再手动安装 @vue/compiler-sfc 这个包;

1.1 <=3.2.12 版本的 Vue

我们先采用第 2 种方案,安装 @vue/compiler-sfc

npm install @vue/compiler-sfc -D

@vue/compiler-sfc 这个包其实就是用来解析整个 SFC 文件的(包括 SFC 文件中的 template,其实都是通过这个包来解析的)。

而在 Vue2 时,对 template 做解析则是通过 vue-template-compiler 这个包来完成的。

安装完成后,我们再来运行 npm run build 命令进行打包:

image-20211122225746546

你会发现,又出现了新的错误,从其中最后一个错误中我们可以看到,vue-loader 在使用时没有使用相应的插件,我们需要在 webpack 配置中配置 VueLoaderPlugin 插件。

也就是说,对于 .vue 文件的解析,除了配置 vue-loader,还需要使用 vue-loader 中的 VueLoaderPlugin(因为 plugin 是贯穿 webpack 的整个生命周期的,所以它可以在某个时刻帮助 vue-loader 做一些事情)。所以我们来到 webpack.config.js 文件中添加插件的配置:

...
// 从 vue-loader 中引入 VueLoaderPlugin
const { VueLoaderPlugin } = require('vue-loader/dist/index');

module.exports = {
  ...
  module: {
    rules: [
      ...
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      }
    ]
  },
  plugins: [
    ...
    new VueLoaderPlugin() // 使用 VueLoaderPlugin
  ]
}

添加完配置后,我们再来打包:

image-20211122231232902

可以看到,这次打包成功了,这就意味着 SFC 文件中的代码已经成功编译了。再来通过 Live Server 插件在浏览器中打开 build/index.html 文件,页面效果如下:

image-20211122231757314

可以看到,App.vue 文件中的模板内容、数据、样式都成功进行了显示。

而且,我们可以回顾一下之前我们讲过的组件化,在真实开发中,我们会按照组件化的方式进行开发。当我们想要创建一个组件时,就可以通过新建一个 .vue 文件来实现,比如我们这里在 src/vue/ 目录下新建 HelloWorld.vue 文件:

<template>
  <h2>HelloWorld 组件</h2>
</template>

<script>
export default {
  data() {
    return {}
  }
}
</script>

<style>
</style>

然后通过最常用的局部注册组件的方式(当然,你也可以全局注册(app.component(...)))对这个 HelloWorld 组件进行注册,我们在 App.vue 中进行局部注册:

<template>
  <h2>我是 Vue 渲染出来的</h2>
  <h2>{{ title }}</h2>
  <!-- 使用 HelloWorld 组件(如果是以 Pascal 格式命名的组件,使用时可以用短横线分隔的形式使用) -->
  <hello-world></hello-world>
</template>

<script>
// 在 App.vue 中引入 HelloWorld 组件
import HelloWorld from './HelloWorld.vue';

// 在当前文件中导出对象,之后在其它文件中就可以导入使用了
export default {
  // 局部注册组件
  components: {
    // 局部注册 HelloWorld 组件
    // HelloWorld: HelloWorld
    // ES6 中增强了对象语法,变量名和属性名一样时,属性值可以简写:
    HelloWorld
  },
  data() {
    return {
      title: '你好'
    }
  },
  // 还可以有其它选项
  methods: {

  }
};
</script>

<style>
h2 {
  color: #f66;
}
</style>

然后再来运行 npm run build 命令打包,打包成功后来看浏览器中的页面效果:

image-20211123185914929

可以看到,HelloWorld 组件的内容成功显示出来了。但是,你会发现 HelloWorld 组件中的字体颜色也变成了 #f66 色,如果希望这个在 App.vue 中设置的样式只作用于 App.vue(而不会穿透进子组件中),我们可以这样做:

  1. App.vue 中的 style 添加 scoped 属性;

    ...
    
    <style scoped>
    h2 {
      color: #f66;
    }
    </style>
    
  2. HelloWorld.vuetemplate 中的元素包一层根元素,比如用一个 <div> 元素作为根元素:

    <template>
      <div>
        <h2>HelloWorld 组件</h2>
      </div>
    </template>
    
    ...
    

修改完这两个文件后,我们再来打包看下效果:

image-20211123190103985

这次,子组件 HelloWorld.vue 中的 <h2> 元素中的文字颜色就没有再被设置为 #f66 了。

1.2 >=3.2.13 版本的 Vue

我们再来采用第 1 种方案,即重新安装 >=3.2.13 版本的 Vue,我们这里直接安装最新版本的 Vue3

npm install vue@next

然后,我们可以把之前手动安装的 @vue/compiler-sfc 卸载掉:

npm uninstall @vue/compiler-sfc

再来运行 npm run build 命令进行打包,你会发现,使用了 vue 最新版本后,即使没有手动安装 @vue/compiler-sfc,也可以成功打包,而且页面效果也没问题:

image-20211123190531607

那为什么使用了 vue 最新版本后就可以不用自己来安装 @vue/compiler-sfc 了呢?这是因为 Vue 3.2.13+ 版本之后已经把 SFC 文件的编译器(即 @vue/compiler-sfc 这个包)直接放到了 vue 的包下。我们可以从 Vue 3.2.13+ 之后源码的依赖中找到依据:

vue@3.2.13 版本中的依赖:

image-20211122214229428

当前(2021-11-22)最新版本的 Vuevue@3.2.22)中的依赖:

image-20211122215509009

再来看 vue@3.2.12 版本中的依赖:

image-20211122214706530

可以看到,vue3.2.12 版本时,还没有在依赖中添加 @vue/compiler-sfc,在 3.2.13 版本之后就做了添加。

此外,我们也可以在 vue-loader 的源码中找到有关说明:

image-20211123191626628

总之,现在我们就可以编写很多组件,然后如果在另外一个组件中需要使用某个组件,就可以导入它,接着进行局部注册,之后就可以使用它了。

以上,就是 Webpack 中对 .vue 文件的支持方式,需要做的主要有三点:

  1. 安装 vue-loader@next
  2. 安装 @vue/compiler-sfcvue 的版本 >=3.2.13 之后,则不需要再进行手动安装);
  3. 配置 VueLoaderPlugin 插件;