从零搭建Vite2+Vue+Antd项目

2,792 阅读3分钟

Vite是什么

Vite(法语意思是 “快”,发音为 /vit/,类似 veet)是一种全新的前端构建工具。你可以把它理解为一个开箱即用的开发服务器 + 打包工具的组合,但是更轻更快。Vite 利用浏览器原生的 ES 模块支持和用编译到原生的语言开发的工具(如 esbuild)来提供一个快速且现代的开发体验。

Vite2.0 的发布带来了对低版本浏览器的构建支持,使其可以正式投入项目生产。

初始化项目

# npm 6.x
npm init @vitejs/app my-vue-app --template vue-ts

# npm 7+, 需要额外的双横线:
npm init @vitejs/app my-vue-app -- --template vue-ts

本人选择了 Vue3 + TypeScript 的模板初始化(更多模板),模板载入完成后,按提示进入项目目录、载入依赖并尝试运行项目。 init.png

less-CSS预处理器

npm i less -D

vue-router

  1. 载入vue-router4.x
npm i vue-router@4
  1. 在 src 目录下新建 router/index.ts ,写入路由配置。
import {createRouter, createWebHashHistory, RouteRecordRaw} from 'vue-router';
import Home from '../views/Home.vue';

const routes: Array<RouteRecordRaw> = [ // 加入类型声明可以约束配置项,提升书写体验
    {
        path: '/',
        name: 'Home',
        component: Home,
    }
];

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

export default router;
  1. 在 main.ts 中引入路由配置
import {createApp} from 'vue';
import router from './router'; // <---
import App from './App.vue';

createApp(App)
    .use(router) // <---
    .mount('#app');
  1. 在 src 目录下新建 views/Home.vue ,写入首页想要呈现的内容。
<template>
  <img alt="Vue logo" src="../assets/logo.png" />
  <p>Home: Hello Vue 3 + TypeScript + Vite</p>
</template>

<script lang="ts">
import {defineComponent} from 'vue'export default defineComponent({
  name: 'Home'
})
</script>
  1. App.vue 此时可以作为 reouter-view 的载体,例如修改为以下内容。
<template>
    <div id="nav">
        <router-link to="/">Home</router-link>
    </div>
    <router-view/>
</template>

<style lang="less">
#app {
  text-align: center;
  color: #2c3e50;
}

#nav {
  padding: 30px;

  a {
    font-weight: bold;
    color: #2c3e50;

    &.router-link-exact-active {
      color: #42b983;
    }
  }
}
</style>
  1. 运行项目,Home 页已经呈现出来。

image.png

vuex

  1. 载入vuex4.x
npm i vuex@4
  1. 在 src 目录下新建 store/index.ts ,写入状态管理器配置。
import {InjectionKey} from 'vue';
import {createStore, Store} from 'vuex';
import user, {State as UserState} from './user';

// 将各模块的State类型汇总
export interface State {
    user: UserState;
}

export const key: InjectionKey<Store<State>> = Symbol();

const debug = process.env.NODE_ENV !== 'production';

export const store = createStore<State>({
    modules: {user},
    strict: debug
});
// 向组件注入$store的类型声明
declare module '@vue/runtime-core' {
    interface ComponentCustomProperties {
        $store: Store<State>;
    }
}
  1. 模块化,新建store/user.ts实现user模块。
import {Module} from 'vuex';
import {State as RootState} from './index';

export interface State {
    age: number;
}
export default {
    namespaced: true,
    actions: {
        addAge({commit, state}, value: number) {
            commit('SET_USER_AGE', state.age + value);
        }
    },
    getters: {
        age: state => state.age
    },
    mutations: {
        SET_USER_AGE(state, age: State['age']) {
            state.age = age;
        }
    },
    state: {
        age: 5
    }
} as Module<State, RootState>;
  1. 在main.ts 中引入 store。
import {createApp} from 'vue';
import router from './router';
import {store, key} from './store'; // <---
import App from './App.vue';

createApp(App)
    .use(store, key) // <---
    .use(router)
    .mount('#app')
  1. 在 Home.vue 中调用 user 模块的状态,再次运行项目。
<template>
    <!-- .... -->
    <p>
        <span>vuex user/age: {{age}}</span>
        <button @click="addAge">age++</button>
    </p>
</template>
<script lang="ts">
import {defineComponent, computed} from 'vue';
import {useStore} from 'vuex';
import {key} from '../store';

export default defineComponent({
  name: 'Home',
  setup() {
    const store = useStore(key); // 调用 vuex 的 Composition API 获取 store ,相当于 $store
    const age = computed(() => store.getters['user/age']);
    const addAge = () => store.dispatch('user/addAge', 1);
    return {age,addAge};
  }
})
</script>

image.png

ant-design-vue

  1. 载入 ant-design-vue2.x
npm i ant-design-vue@next

# 处理按需加载的插件
npm i vite-plugin-importer -D
  1. 在 vite.config.ts 加入 vite-plugin-importer
import {defineConfig} from 'vite';
import vue from '@vitejs/plugin-vue';
import importerPlugin from 'vite-plugin-importer'; // <---

// https://vitejs.dev/config/
export default defineConfig({
    css: {
        preprocessorOptions: {
            less: {
                javascriptEnabled: true // 使用 antd 的 less 样式 需要开启
            }
        },
    },
    plugins: [
        vue(),
        importerPlugin({ // <---
            libraryName: "ant-design-vue",
            libraryDirectory: "es",
            style: true // less
        })
    ]
});
  1. 在 src 目录下新建 config/antd.config.ts 全局引入 antd 组件。
import {Plugin} from 'vue';
import {Button, message} from 'ant-design-vue';

const antdInstall: Plugin = function (app) { // 声明为中间件暴露,调用时更优雅
    app.use(Button);
    app.config.globalProperties.$message = message;
};
export default antdInstall;
declare module '@vue/runtime-core' {
    interface ComponentCustomProperties {
        $message: typeof message;
    }
}
  1. 在 main.ts 引入 antd 的配置。
import {createApp} from 'vue';
import antd from './config/antd.config'; // <---
import router from './router';
import {store, key} from './store';
import App from './App.vue';

createApp(App)
    .use(store, key)
    .use(router)
    .use(antd) // <---
    .mount('#app')
  1. 在 Home.vue 使用 antd 的 button 组件,再次运行项目。
<template>
    <!-- .... -->
    <a-button type="primary" @click="addAge">age++</a-button>
    <!-- .... -->
</template>

image.png

低版本浏览器兼容

  1. 载入 @vitejs/plugin-legacy
npm i @vitejs/plugin-legacy -D
  1. 在 vite.config.ts 中启用 @vitejs/plugin-legacy
import {defineConfig} from 'vite';
// ...
import legacyPlugin from '@vitejs/plugin-legacy';

// https://vitejs.dev/config/
export default defineConfig({
    // ...
    plugins: [
        // ...
        legacyPlugin({
            targets: ['defaults', 'not IE 11']
        })
    ]
});

如此构建时,vite 将按 targets 配置编译产物。

国际化

  1. 载入vue-i18n
npm i vue-i18n@next
  1. 在 src 目录下新建 config/i18n.config.ts,配置多语言文案。
import {createI18n} from 'vue-i18n';

const messages = {
    cn: {
        message: {
            hello: '你好,中国'
        }
    },
    en: {
        message: {
            hello: 'Hello World'
        }
    }
};

export default createI18n({
    locale: 'en',
    fallbackLocale: 'en',
    messages
});
  1. 在 main.ts 中引入国际化配置。
import {createApp} from 'vue';
// ...
import i18n from 'config/i18n.config';
import App from './App.vue';

createApp(App)
    .use(i18n)
    // ...
    .mount('#app');
  1. 在 Home.vue 调用语言输出函数,再次运行项目。
<template>
    <!-- ... -->
    <p>{{$t('message.hello')}}</p>
    <p>{{t('message.hello')}}</p>
</template>
<script lang="ts">
import {defineComponent} from 'vue';
import {useI18n} from 'vue-i18n';

export default defineComponent({
  name: 'Home',
  setup() {
    // ...
    return {
        // ...
        t: useI18n().t
    };
  }
})
</script>

使用已全局注入的 $t 和 useI18n().t 都可以,useI18n负责在setup中获取文案输出。

image.png

别名

  1. 在 vite.config.ts 配置vue文件中对别名的映射。
import {defineConfig} from 'vite';
// ...

// https://vitejs.dev/config/
export default defineConfig({
    // ...
    resolve: {
        alias: {
            '@': '/src',
            'assets': '/src/assets',
            'components': '/src/components',
            'config': '/src/config',
            'router': '/src/router',
            'store': '/src/store',
            'views': '/src/views'
        }
    }
});
  1. 在 ts.config.json 中配置ts文件中对别名的映射。
{
    "compilerOptions": {
        // ...
        "paths": {
          "@/*": ["src/*"],
          "*": ["*", "src/*"]
        },
        // ...
    }
  }

eslint

给项目集成一个eslint,这样在多人协作时可以保持代码风格一致。

  1. 载入vite-plugin-eslint
npm install vite-plugin-eslint --save-dev

vite 兼容 rollup 插件,因此理论上 @rollup/plugin-eslint 也是可以的。

  1. 在 vite.config.ts 中启用 vite-plugin-eslint
import {defineConfig} from 'vite';
// ...
import eslintPlugin from 'vite-plugin-eslint';

// https://vitejs.dev/config/
export default defineConfig({
    // ...
    plugins: [
        // ...
        eslintPlugin({
            cache: false,
            fix: false,
            include: [
                'src/**/*.ts',
                'src/**/*.d.ts',
                'src/**/*.tsx',
                'src/**/*.vue',
                'tests/**/*.ts',
                'tests/**/*.tsx'
            ],
            exclude: [
                'node_modules'
            ]
        })
    ]
});

以上配置关闭了缓存,include 和 exclude 的范围也根据实际情况做了修改(参考 ts.config.json默认的范围配置)。

  1. 同其他使用eslint的项目一样,根目录下需要一个 eslintrc.js 文件,用于定义代码规范检查的规则(以下是从 vue-cli 初始化项目中拿到的 eslintrc.js)。
module.exports = {
  root: true,
  env: {
    node: true
  },
  'extends': [
    'plugin:vue/vue3-essential',
    'eslint:recommended',
    '@vue/typescript/recommended'
  ],
  parserOptions: {
    ecmaVersion: 2020
  },
  rules: {
    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
  }
}
  1. 以上配置用到了一些外置的插件,需要另外载入。
npm install eslint eslint-plugin-vue  --save-dev
npm install @typescript-eslint/eslint-plugin @typescript-eslint/parser --save-dev
npm install @vue/eslint-config-typescript --save-dev
  1. 可根据个人需要扩充 eslintrc.js 文件中的规则。

总结

  • 每个依赖官网都有指南和详细的文档,按步骤即可完成搭建。
  • 大部分依赖都有了typescript 的实现,配置时,使用依赖中提供的类型约束将带来良好的提示和检查。
  • vite 使用 rollup 实现,扩展时可使用 rollup 插件。