Vue 3源码极客之:`Vue`的`@vue/cli`:其插件系统和配置管理。

各位观众老爷,大家好! 今天咱不开车,来聊聊Vue 3 的一个重要伙伴: @vue/cli,尤其是它的插件系统和配置管理。 这玩意儿就像Vue项目的“变形金刚”,能根据你的需求变出各种形态。

一、 插件系统:让Vue项目“百变星君”

@vue/cli 的插件系统是它最核心的功能之一。 简单来说,插件就是一些预先写好的代码,可以自动完成项目配置、安装依赖、添加功能等任务。 想象一下,你想要一个支持 TypeScript 的 Vue 项目,不用自己吭哧吭哧地配置,直接装个 @vue/cli-plugin-typescript 插件,它就能帮你搞定一切,是不是很爽?

  1. 插件的本质

插件本质上就是一个 Node.js 模块,通常导出一个函数。 这个函数接收两个参数:

  • api:一个包含各种方法的对象,用于操作项目配置、注册命令、安装依赖等。
  • options:插件的配置选项,可以在 vue.config.js 中设置。

咱们来看一个简单的插件例子:

// my-plugin.js
module.exports = (api, options) => {
  // 注册一个命令
  api.registerCommand('hello', {
    description: '打印一句问候语',
    usage: 'vue-cli-service hello',
    fn: () => {
      console.log('Hello, Vue!');
    }
  });

  // 修改 webpack 配置
  api.chainWebpack(config => {
    config.module
      .rule('vue')
      .use('vue-loader')
        .loader('vue-loader')
        .tap(options => {
          // 修改 vue-loader 的配置
          options.compilerOptions = {
            ...options.compilerOptions,
            whitespace: 'condense' // 去除多余空格
          }
          return options
        })
  });

  // 注入 webpack-dev-server 配置
  api.configureDevServer(config => {
    config.proxy = {
      '/api': {
        target: 'http://localhost:3000',
        changeOrigin: true
      }
    }
  });

  // 添加一个 webpack 插件
  api.configureWebpack(config => {
    config.plugins.push(
      new require('webpack').BannerPlugin('Generated by my-plugin')
    );
  });

  // 安装依赖
  api.extendPackage({
    dependencies: {
      'lodash': '^4.17.21'
    }
  });
};

这个插件做了几件事:

  • 注册了一个 hello 命令,运行 vue-cli-service hello 就可以在控制台看到 "Hello, Vue!"。
  • 修改了 webpack 配置,优化 Vue 组件的编译。
  • 配置了 webpack-dev-server,添加了代理。
  • 添加了一个 webpack 插件,在打包后的文件中添加 banner。
  • 安装了 lodash 依赖。
  1. 插件的使用

要使用插件,有几种方式:

  • 通过 CLI 安装:

    vue add my-plugin

    这会自动安装插件并将其添加到 package.jsondevDependencies 中。

  • 手动安装:

    npm install my-plugin --save-dev

    然后,在 vue.config.js 中配置插件:

    // vue.config.js
    module.exports = {
      configureWebpack: config => {
        config.resolve.alias['@'] = path.resolve(__dirname, 'src');
      },
      pluginOptions: {
        'my-plugin': {
          // 插件的配置选项
        }
      }
    }
  1. api 对象详解

api 对象是插件的核心,它提供了很多方法来操作项目。 咱们来详细看看一些常用的方法:

方法名 描述
registerCommand 注册一个命令,可以通过 vue-cli-service <command> 运行。
chainWebpack 修改 webpack 配置。 这是一个链式 API,可以方便地修改 webpack 配置的各个方面。
configureWebpack 直接修改 webpack 配置。 这种方式更灵活,但需要更了解 webpack 配置的结构。
configureDevServer 修改 webpack-dev-server 配置。
extendPackage 修改 package.json 文件。 可以添加依赖、修改 scripts 等。
resolve 解析项目中的路径。 例如,api.resolve('src/components/MyComponent.vue') 会返回 src/components/MyComponent.vue 的绝对路径。
hasPlugin 检查是否安装了某个插件。
injectOptions 将插件的配置选项注入到 vue.config.js 中。
onRootDisk 检查给定的路径是否在根磁盘上。
setCwd 设置当前工作目录。

二、 配置管理:让Vue项目井井有条

@vue/cli 提供了一套强大的配置管理系统,让你能够灵活地配置项目的各个方面。 主要的配置文件是 vue.config.js,它允许你自定义 webpack 配置、 devServer 配置、插件选项等。

  1. vue.config.js 的结构

vue.config.js 是一个 Node.js 模块,导出一个对象。 这个对象包含各种配置选项,例如:

// vue.config.js
module.exports = {
  // 项目部署的基础路径
  publicPath: '/',

  // 输出文件目录
  outputDir: 'dist',

  // 静态资源目录 (相对于 outputDir 的)
  assetsDir: 'static',

  // 是否在开发环境下每次保存代码时都启用 eslint 验证
  lintOnSave: process.env.NODE_ENV !== 'production',

  // 生产环境是否生成 sourceMap 文件
  productionSourceMap: false,

  // webpack 配置
  configureWebpack: config => {
    // ...
  },

  // webpack 链式操作
  chainWebpack: config => {
    // ...
  },

  // css 相关配置
  css: {
    // 是否使用 css 分离插件 ExtractTextPlugin
    extract: true,

    // 开启 CSS source maps?
    sourceMap: false,

    // css 预设器配置项
    loaderOptions: {
      sass: {
        // 引入全局样式
        prependData: `@import "@/assets/styles/global.scss";`
      }
    }
  },

  // webpack-dev-server 相关配置
  devServer: {
    open: true,
    host: 'localhost',
    port: 8080,
    https: false,
    hotOnly: false,
    proxy: {
      '/api': {
        target: 'http://localhost:3000',
        changeOrigin: true
      }
    },
    before: app => {}
  },

  // 第三方插件配置
  pluginOptions: {
    // ...
  }
}
  1. 常用的配置选项

咱们来详细看看一些常用的配置选项:

  • publicPath: 项目部署的基础路径。 如果你将项目部署到服务器的子目录,例如 /my-app/,则需要将 publicPath 设置为 /my-app/
  • outputDir: 输出文件目录。 默认情况下,构建后的文件会输出到 dist 目录。
  • assetsDir: 静态资源目录。 静态资源,例如图片、字体等,会输出到 assetsDir 指定的目录。
  • lintOnSave: 是否在开发环境下每次保存代码时都启用 eslint 验证。
  • productionSourceMap: 生产环境是否生成 sourceMap 文件。 建议在生产环境禁用 sourceMap,以减小文件体积。
  • configureWebpack: 直接修改 webpack 配置。
  • chainWebpack: 修改 webpack 配置。 这是一个链式 API,可以方便地修改 webpack 配置的各个方面。
  • css: css 相关配置。 可以配置是否使用 css 分离插件、开启 CSS source maps、css 预设器配置项等。
  • devServer: webpack-dev-server 相关配置。 可以配置 host、port、proxy 等。
  • pluginOptions: 第三方插件配置。
  1. chainWebpack 的妙用

chainWebpack 是一个非常强大的配置选项,它允许你通过链式 API 修改 webpack 配置。 这种方式比直接修改 configureWebpack 更安全、更灵活。

咱们来看一个例子:

// vue.config.js
module.exports = {
  chainWebpack: config => {
    // 修改 svg loader
    config.module
      .rule('svg')
        .exclude.add(path.resolve(__dirname, 'src/icons')) // 排除 src/icons 目录
        .end()

    // 添加 svg-sprite-loader
    config.module
      .rule('icons')
        .test(/.svg$/)
        .include.add(path.resolve(__dirname, 'src/icons')) // 只包含 src/icons 目录
        .end()
      .use('svg-sprite-loader')
        .loader('svg-sprite-loader')
        .options({
          symbolId: 'icon-[name]'
        })
        .end()
  }
}

这个例子修改了 svg loader,使其能够使用 svg-sprite-loader 来处理 src/icons 目录下的 svg 文件。

  1. 环境变量的使用

vue.config.js 中,你可以使用环境变量来动态地配置项目。 例如,你可以根据 NODE_ENV 环境变量来设置不同的配置选项。

// vue.config.js
module.exports = {
  publicPath: process.env.NODE_ENV === 'production'
    ? '/my-app/'
    : '/',

  devServer: {
    proxy: {
      '/api': {
        target: process.env.API_URL || 'http://localhost:3000',
        changeOrigin: true
      }
    }
  }
}

在这个例子中,publicPath 会根据 NODE_ENV 环境变量的值来设置不同的值。 如果 NODE_ENVproduction,则 publicPath 设置为 /my-app/; 否则,设置为 /devServer.proxy 也使用了环境变量 API_URL 来设置代理的目标地址。

三、 插件开发实战:一个简单的主题切换插件

为了更好地理解插件系统,咱们来开发一个简单的主题切换插件。 这个插件允许用户通过命令行来切换项目的主题。

  1. 插件的结构

创建一个名为 vue-cli-plugin-theme 的目录,并在其中创建以下文件:

vue-cli-plugin-theme/
├── index.js
├── generator.js
└── template/
    └── theme.scss
  • index.js: 插件的主入口文件。
  • generator.js: 用于生成项目文件的生成器。
  • template/theme.scss: 主题的样式文件模板。
  1. index.js 的内容
// index.js
module.exports = (api, options) => {
  // 注册一个命令
  api.registerCommand('theme', {
    description: '切换项目主题',
    usage: 'vue-cli-service theme <theme-name>',
    args: {
      '<theme-name>': '主题名称'
    },
    fn: async (args) => {
      const themeName = args._[0];
      if (!themeName) {
        console.error('请指定主题名称');
        return;
      }

      // 修改 vue.config.js
      api.extendPackage({
        vue: {
          pluginOptions: {
            theme: {
              currentTheme: themeName
            }
          }
        }
      });

      console.log(`已切换到主题:${themeName}`);
    }
  });

  // 修改 webpack 配置
  api.chainWebpack(config => {
    config.module
      .rule('scss')
        .oneOf('vue')
          .use('sass-loader')
            .tap(options => {
              options.additionalData = `@import "@/template/theme.scss";`;
              return options;
            })
  });
};

这个插件注册了一个 theme 命令,允许用户通过 vue-cli-service theme <theme-name> 来切换主题。 它还修改了 webpack 配置,将 template/theme.scss 文件引入到所有的 scss 文件中。

  1. generator.js 的内容
// generator.js
module.exports = (api, options, rootOptions) => {
  api.render('./template');
};

这个生成器将 template/theme.scss 文件复制到项目的 src 目录下。

  1. template/theme.scss 的内容
// template/theme.scss
$theme-name: "default"; // 默认主题

@if $theme-name == "default" {
  body {
    background-color: #fff;
    color: #333;
  }
} @else if $theme-name == "dark" {
  body {
    background-color: #333;
    color: #fff;
  }
}

这个文件定义了两个主题: defaultdark

  1. 插件的使用

首先,将插件安装到项目中:

vue add ./vue-cli-plugin-theme

然后,运行 vue-cli-service theme dark 来切换到 dark 主题。 再次运行 vue-cli-service theme default 来切换回 default 主题。

四、 总结

@vue/cli 的插件系统和配置管理是 Vue 项目开发的重要工具。 插件系统可以让你轻松地扩展项目的功能,而配置管理可以让你灵活地配置项目的各个方面。 掌握这两个工具,可以大大提高你的开发效率。

好了,今天的讲座就到这里。 各位观众老爷,咱们下回再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注