模块打包工具-Vite

372 阅读1分钟

打包工具出现要解决的问题:

1、ES Module的浏览器兼容性问题;
2、模块文件过多导致频繁发送网络请求问题;
3、资源文件模块化的问题;

Vite为什么加载那么快?

1、不需要打包,只需要加载需要的模块;
2、vue文件预先被Vite预编译为js。
3、第三方依赖或者裸模块的加载,会被预打包。

使用Vite时如何配置路径别名

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import {resolve} from 'path'

// https://vitejs.dev/config/
export default defineConfig({
  resolve: {
    alias: {
      '@': resolve('./src'),
      'comp': resolve('./src/components')
    }
  },
  plugins: [vue()]
})

样式文件如何使用module

// 1、
<style module>
code{
  background-color: #eee;
  padding: 2px 4px;
  border-radius: 4px;
  color: red;
}
</style>

<p>
  Edit
  <code :class="$style.code">components/HelloWorld.vue</code> to test hot module replacement.
</p>
// 2、
// Index.module.css
.code {
    background-color: #eee;
    padding: 2px 4px;
    border-radius: 4px;
    color: red;
}

使用时首先需要导入到Vue文件中

import classes from './Index.module.css';
...
setup: () => {
  const count = ref(0)
  return { count, classes }
}


<p>
  Edit
  <code :class="classes.code">components/HelloWorld.vue</code> to test hot module replacement.
</p>

针对不同的浏览器做css hack

我们会优先使用autoprefixer来帮助我们解决问题;

安装依赖:npm i autoprefixer -D; 创建一个名为postcss.config.js的文件同时添加内容:

module.exports = {
  plugins: [
    require('autoprefixer')
  ]
}

配置代理

在 vite.config.js文件中写入配置项,配置方法和之前的一致

server: {
  proxy: {
    '/api': {
      target: '',
      changeOrigin: true,
      rewrite: path => path.replace(/^/api/, '')
    }
  }
},

数据mock

npm i mockjs -S
npm i vite-plugin-mock -D

在 vite.config.js中配置plugin

import { viteMockServe } from 'vite-plugin-mock';
export default defineConfig({
  plugins: [ viteMockServe({}) ]
})

eslint格式化配置

在package.json文件中配置devDependencies属性,添加以下依赖

"@typescript-eslint/eslint-plugin": "^4.15.2",
"@typescript-eslint/parser": "^4.15.2",
"@vue/eslint-config-prettier": "^6.0.0",
"@vue/eslint-config-typescript": "^7.0.0",
"@vuedx/typescript-plugin-vue": "^0.6.3",
"eslint": "^7.20.0",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-vue": "^7.6.0",
"prettier": "^2.2.1"

创建 .eslintrc.js 同时添加配置

module.exports = {
    root: true,
    env: {
        browser: true,
        es6: true,
        node: true
    },
    extends: [
        'plugin:vue/vue3-recommended',
        'eslint:recommended',
        '@vue/typescript/recommended',
        '@vue/prettier',
        '@vue/prettier/@typescript-eslint'
    ],
    parserOptions: {
        ecmaVersion: 2021
    },
    plugins: [],
    rules: {
        "no-unused-vars":"off",
        "@typescript-eslint/no-unused-vars":"off"
    }
}

同时在package.json文件中添加命令

"lint": "eslint --ext .ts,vue src/** --no-error-on-unmatched-pattern --quiet"
"lint:fix": "eslint --ext .ts,vue src/** --no-error-on-unmatched-pattern --fix"

Jest

首先需要在package.json文件中配置依赖:

"jest": "^26.6.3",
"@types/jest": "^26.0.20",
"vue-jest": "^5.0.0-alpha.7",
"babel-jest": "^26.6.3",
"@babel/preset-env": "^7.12.17",
"@vue/test-utils": "^2.0.0-beta.9",
"ts-jest": "^26.5.1",
"@babel/preset-typescript": "^7.12.17"

添加一个新的文件jest.config.js

module.exports = {
    transform: {
        "^.+.vue$": "vue-jest",
        "^.+.jsx?$": "babel-jest",
        "^.+.tsx?$": "ts-jest",
    },
    moduleNameMapper: {
        "^@/components(.*)$": "<rootDir>/src/components$1"
    },
    testMatch: ["**/tests/unit/**/*.[jt]s?(x)"]
}

Vite插件的形式

export default {
  name: "my-vite-plugin",
  resolveId(id) {},
  load(id) {},
  transform(code){}
}
export default function (options) {
  return {
    name: "my-vite-plugin",
    resolveId(id) {},
    load(id) {},
    transform(code){}
  }
}

插件钩子

开发时,Vite创建一个插件容器按照顺序调用各个钩子
  • config: 修改Vite配置
  • configResolved: Vite配置确认
  • configureServer: 用于配置dev server
  • transformIndexHtml: 用于转换宿主页
  • resolved 创建自定义确认函数,常用语定位第三方依赖
  • load 创建自定义加载函数,可用于返回自定义的内容
  • transform 可用于转换已加载的模块内容
  • handleHotUpdate: 自定义HMR更新时调用

定义我的第一个插件

// vite-plugin-example.js
export default function (options) {
  return {
    name: "plugin-example", // 随便定义
    resolveId(source) {
      // 是否处理当前的请求
      if (source === "virtual-module") {
        return source; // 表示接管
      }
      return null;
    },
    load(id) {
      if (id === "virtual-module") {
        // 返回加载模块代码
        return 'export default "this is virtual-module"';
      }
      return null;
    },
  };
}

使用时在vite.config.js中

import virtualModule from "./plugins/vite-plugin-example";

```
plugins: [vue(), virtualModule()],
```

自定义差价实例

// vite-plugin-i18n.js
export default {
  // 将load进来的代码块进一步的加工处理
  /**
   *
   * @param code 代码块的内容
   * @param id 请求的URL
   * @returns {string|null}
   */
  transform(code, id) {
    // 将i18n信息写入组件配置
    if (/vue&type=i18n/.test(id)) {
      return `export default Comp => {
                Comp.i18n = ${code}
            }`;
    }
    return null;
  },
};
// vite-config.js
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import { resolve } from "path";
import virtualModule from "./plugins/vite-plugin-example";
import i18n from "./plugins/vite-plugin-i18n";

// https://vitejs.dev/config/
export default defineConfig({
  server: {
    proxy: {
      "/api": {
        target: "",
        changeOrigin: true,
        rewrite: (path) => path.replace(/^/api/, ""),
      },
    },
  },
  resolve: {
    alias: {
      "@": resolve("./src"),
      comp: resolve("./src/components"),
    },
  },
  plugins: [vue(), virtualModule(), i18n],
});
// App.vue
<template>
  <img alt="Vue logo" src="./assets/logo.png" />
  <HelloWorld msg="Hello Vue 3 + TypeScript + Vite" />
  <span>{{ t("language") }}</span>
  <select v-model="locale">
    <option value="zh">zh</option>
    <option value="en">en</option>
  </select>
  <span>{{ t("hello") }}</span>
</template>

<i18n>
{
  "en": {
    "language": "Language",
    "hello": "hello, world!"
  },
  "zh": {
    "language": "语言",
    "hello": "你好,世界!"
  }
}
</i18n>

<script lang="ts">
import { defineComponent, getCurrentInstance, ref, computed } from "vue";
import HelloWorld from "./components/HelloWorld.vue";

export default defineComponent({
  name: "App",
  components: {
    HelloWorld,
  },
  setup: () => {
    const ins = getCurrentInstance();
    function useI18n() {
      const locale = ref("zh");
      const i18n = ins!.type.i18n;
      const t = (msg) => {
        return computed(() => i18n[locale.value][msg]).value;
      };
      return { locale, t };
    }
    const { locale, t } = useI18n();
    return {
      locale,
      t,
    };
  },
});
</script>

<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>