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 的模板初始化(更多模板),模板载入完成后,按提示进入项目目录、载入依赖并尝试运行项目。
less-CSS预处理器
npm i less -D
vue-router
- 载入vue-router4.x
npm i vue-router@4
- 在 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;
- 在 main.ts 中引入路由配置
import {createApp} from 'vue';
import router from './router'; // <---
import App from './App.vue';
createApp(App)
.use(router) // <---
.mount('#app');
- 在 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>
- 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>
- 运行项目,Home 页已经呈现出来。
vuex
- 载入vuex4.x
npm i vuex@4
- 在 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>;
}
}
- 模块化,新建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>;
- 在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')
- 在 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>
ant-design-vue
- 载入 ant-design-vue2.x
npm i ant-design-vue@next
# 处理按需加载的插件
npm i vite-plugin-importer -D
- 在 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
})
]
});
- 在 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;
}
}
- 在 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')
- 在 Home.vue 使用 antd 的 button 组件,再次运行项目。
<template>
<!-- .... -->
<a-button type="primary" @click="addAge">age++</a-button>
<!-- .... -->
</template>
低版本浏览器兼容
npm i @vitejs/plugin-legacy -D
- 在 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 配置编译产物。
国际化
- 载入vue-i18n
npm i vue-i18n@next
- 在 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
});
- 在 main.ts 中引入国际化配置。
import {createApp} from 'vue';
// ...
import i18n from 'config/i18n.config';
import App from './App.vue';
createApp(App)
.use(i18n)
// ...
.mount('#app');
- 在 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中获取文案输出。
别名
- 在 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'
}
}
});
- 在 ts.config.json 中配置ts文件中对别名的映射。
{
"compilerOptions": {
// ...
"paths": {
"@/*": ["src/*"],
"*": ["*", "src/*"]
},
// ...
}
}
eslint
给项目集成一个eslint,这样在多人协作时可以保持代码风格一致。
npm install vite-plugin-eslint --save-dev
vite 兼容 rollup 插件,因此理论上 @rollup/plugin-eslint 也是可以的。
- 在 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默认的范围配置)。
- 同其他使用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'
}
}
- 以上配置用到了一些外置的插件,需要另外载入。
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
- 可根据个人需要扩充 eslintrc.js 文件中的规则。
总结
- 每个依赖官网都有指南和详细的文档,按步骤即可完成搭建。
- 大部分依赖都有了typescript 的实现,配置时,使用依赖中提供的类型约束将带来良好的提示和检查。
- vite 使用 rollup 实现,扩展时可使用 rollup 插件。