使用Vite初始化项目
为什么要使用Vite
- Vite是一个面向现代浏览器的一个更轻、更快的web开发应用
- 基于ECMAScript标准原生模块系统(ESM)实现 Vite项目依赖项
- Vite只有当文件请求时才会去编译相应的模块
- ...
这里使用npm初始化项目
npm init vite@latest
然后根据提示选择模板、eslint标准等
配置eslint
npm install eslint --save-dev
# 设置eslint规则并生产配置文件
./node_modules/.bin/eslint --init
默认生成的vue的配置文件是Vue 2.x版本的规则,需要手动修改为Vue3的规则
module.exports = {
...
extends: [
'plugin:vue/vue3-strongly-recommended',
'standard'
],
}
如果出现eslint报错,如defineProps
等报错,需要在eslint配置文件中加上
{
globals: {
defineProps: 'readonly',
defineEmits: 'readonly',
defineExpose: 'readonly',
withDefaults: 'readonly'
}
}
vscode编辑器配置
- 安装
Volar
这个插件 - 如果之前安装过Vue2的
Vetur
这个插件,需要把这个插件禁用掉,然后重启编辑器 - 安装eslint,设置eslint为默认格式化代码的方式
配置git commit hook
npx mrm@2 lint-staged
配置lint-staged
{
"lint-staged": {
"*.{js,jsx,ts,tsx,vue}": [
"npm run lint",
"git add"
]
}
}
代码提交规范
# Install commitlint cli and conventional config
npm install --save-dev @commitlint/{config-conventional,cli}
# For Windows:
npm install --save-dev @commitlint/config-conventional @commitlint/cli
# 生成commitlint.config.js
echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
使用commit-msg
钩子
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'
如果提交是出现下面报错,需要将commitlint.config.js
的编码设置为utf-8格式
commitlint.config.js:1 �� SyntaxError: Invalid or unexpected token
相关资料参考 www.ruanyifeng.com/blog/2016/0…
Vue3中的TS支持
文档地址 v3.cn.vuejs.org/guide/types…
Vue3中的script setup
script setup可以是我们的开发代码变得更好,有了这个特性后,开发Vue项目变得更简单了,没有之前那么多大样板代码
文档地址
props和emmit声明
props 和 emits 都可以使用传递字面量类型的纯类型语法做为参数给 defineProps 和 defineEmits 来声明:
const props = defineProps<{
foo: string
bar?: number
}>()
const emit = defineEmits<{
(e: 'change', id: number): void
(e: 'update', value: string): void
}>()
声明props的默认值
interface Props {
msg?: string
labels?: string[]
}
const props = withDefaults(defineProps<Props>(), {
msg: 'hello',
labels: () => ['one', 'two']
})
配置jsx
为什么会用到jsx
Vue 推荐在绝大多数情况下使用模板来创建你的 HTML。然而在一些场景中,你真的需要 JavaScript 的完全编程的能力。这时你可以用渲染函数,它比模板更接近编译器。
当我们创建下面的组件后
const { createApp, h } = Vue
const app = createApp({})
app.component('anchored-heading', {
render() {
return h(
'h' + this.level, // 标签名
{}, // prop 或 attribute
this.$slots.default() // 包含其子节点的数组
)
},
props: {
level: {
type: Number,
required: true
}
}
})
但我们使用渲染函数时,可能会很痛苦
h(
'anchored-heading',
{
level: 1
},
{
default: () => [h('span', 'Hello'), ' world!']
}
)
而模板使用会非常简单
<anchored-heading :level="1"> <span>Hello</span> world! </anchored-heading>
这就是为什么我们会使用jsx,还有就是React的开发者可以更喜欢使用jsx
Vue3提供了一个babel插件,用于在 Vue3 中使用 JSX 语法
import AnchoredHeading from './AnchoredHeading.vue'
const app = createApp({
render() {
return (
<AnchoredHeading level={1}>
<span>Hello</span> world!
</AnchoredHeading>
)
}
})
app.mount('#demo')
配置vite中的jsx
Vue 用户应使用官方提供的 @vitejs/plugin-vue-jsx 插件,它提供了 Vue 3 特性的支持,包括 HMR,全局组件解析,指令和插槽。
import vueJsx from '@vitejs/plugin-vue-jsx'
export default {
plugins: [
vueJsx({
// options are passed on to @vue/babel-plugin-jsx
})
]
}
相关文档地址
- Vue3中的jsx v3.cn.vuejs.org/guide/rende…
- babel-plugin-jsx中文文档 github.com/vuejs/jsx-n…
- vite中配置jsx cn.vitejs.dev/guide/featu…
Vue Router
npm install vue-router@4
router-link
和router-view
和在使用Vue2.x时是一样的
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
const routes:RouteRecordRaw[] = [
{
path: '/',
name: 'Home',
// 使用ts时,这里的文件名和后缀都不能省略掉,因为如果省略,ts没办法识别它的类型
component: () => import('../views/Home/index.vue')
},
{
path: '/login',
name: 'Login',
component: () => import('../views/Login/Login.vue')
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router
在入口文件中使用vue-router
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App)
.use(router)
.mount('#app')
Vuex
安装Vuex
npm install vuex@next --save
使用
// store/index.js
import {
createStore
} from 'vuex'
export interface State {
count: number
}
const store = createStore<State>({
state () {
return {
count: 0
}
},
mutations: {
increment (state) {
state.count++
state.a = 1
}
}
})
export default store
在入口文件引入
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
+ import store from './store'
createApp(App)
+ .use(store)
.use(router)
.mount('#app')
在页面中使用
<script setup lang="ts">
import { useStore } from 'vuex'
const store = useStore()
</script>
<template>
<div>store {{ $store.state.count }}| {{ store.state.count }}</div>
</template>
ts无法推断页面中$store.state.count和store.state.count,我们可以参考vuex中ts支持文档
在项目中创建vuex.d.ts文件
// eslint-disable-next-line no-unused-vars
import { ComponentCustomProperties } from 'vue'
import { Store } from 'vuex'
declare module '@vue/runtime-core' {
// 声明自己的 store state
interface State {
count: number
}
// 为 `this.$store` 提供类型声明
// eslint-disable-next-line no-unused-vars
interface ComponentCustomProperties {
$store: Store<State>
}
}
在使用useStore
的时候,ts无法推断 store.state
// store/index.js
import { InjectionKey } from 'vue'
import {
createStore,
Store,
useStore as baseUseStore
} from 'vuex'
export interface State {
count: number
}
export const key: InjectionKey<Store<State>> = Symbol('store')
export const store = createStore<State>({
state () {
return {
count: 0
}
},
mutations: {
increment (state) {
state.count++
}
}
})
export function useStore () {
return baseUseStore(key)
}
入口文件
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { store, key } from './store'
createApp(App)
.use(store, key)
.use(router)
.mount('#app')
在页面中使用
<script setup lang="ts">
import { useStore } from 'vuex'
import { key } from './store'
const store = useStore(key)
...
</script>
...
简化 useStore 用法
import { InjectionKey } from 'vue'
import {
createStore,
Store,
useStore as baseUseStore
} from 'vuex'
export interface State {
count: number
}
export const key: InjectionKey<Store<State>> = Symbol('store')
export const store = createStore<State>({
state () {
return {
count: 0
}
},
mutations: {
increment (state) {
state.count++
}
}
})
export function useStore () {
return baseUseStore(key)
}
在vue组件中使用
<script setup lang="ts">
import { useStore } from './store'
const store = useStore()
...
</script>
...
处理文件路径
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import eslintPlugin from 'vite-plugin-eslint'
import vueJsx from '@vitejs/plugin-vue-jsx'
import path from 'path'
export default defineConfig({
resolve: {
alias: {
'@': path.join(__dirname, 'src')
}
},
plugins: [
vue(),
eslintPlugin(),
vueJsx({
// options are passed on to @vue/babel-plugin-jsx
})]
})
配置后在项目中代码运行没问题,在ts会报错,需要在ts配置文件中配置下路径。相关文档
{
"compilerOptions": {
...
"baseUrl": "./",
"paths": {
"@/*": [
"src/*"
]
}
},
...
}
配置路径别名后,不仅可以在js中可以使用,在html、css中也可以使用
css处理
Vite中处理css可以参考Vite Css 文档
PostCSS
如果项目包含有效的 PostCSS 配置 (任何受 postcss-load-config 支持的格式,例如 postcss.config.js),它将会自动应用于所有已导入的 CSS。
CSS Modules
任何以 .module.css
为后缀名的 CSS 文件都被认为是一个 CSS modules 文件。导入这样的文件会返回一个相应的模块对象:
CSS预处理器
Vite中使用CSS预处理器无须配置,只需要安装预处理器依赖即可
npm i sass -D
npm i stylus -D
npm i less -D
Vite 为 Sass 和 Less 改进了 @import 解析,以保证 Vite 别名也能被使用。另外,url() 中的相对路径引用的,与根文件不同目录中的 Sass/Less 文件会自动变基以保证正确性。
由于 Stylus API 限制,@import 别名和 URL 变基不支持 Stylus。
vite提供了css.preprocessorOptions这个选项,用来指定传递给css预处理器的选项
export default defineConfig({
css: {
preprocessorOptions: {
scss: {
additionalData: `$injectedColor: orange;`
}
}
}
})
css.preprocessorOptions
也可以传入文件路径
export default defineConfig({
css: {
preprocessorOptions: {
scss: {
additionalData: '@import "@/styles/variables.scss";'
}
}
}
})
跨域处理
在开发过程中,跨域的解决方案主要有两种
- 服务端解决(cors)
- 配置代理
- 这里我们可以使用vite-server.proxy
- 其他方案如jsonp等使用条件有限,这里不考虑使用
在vite项目中,我们可以使用vite-server.proxy,可以参考文档
Vite中的缓存
在我们开发的过程中可能会遇到一些问题,如明明修改了文件,构建完成的结果报的错误还是上一次的内容,这时候我们可以手动删除node_modules/.vite
文件,然后重新构建
Vite 会将预构建的依赖缓存到 node_modules/.vite
。它根据几个源来决定是否需要重新运行预构建步骤:
- package.json 中的 dependencies 列表
- 包管理器的 lockfile,例如 package-lock.json, yarn.lock,或者 pnpm-lock.yaml
- 可能在 vite.config.js 相关字段中配置过的
如果出于某些原因,你想要强制 Vite 重新构建依赖,你可以用 --force 命令行选项启动开发服务器,或者手动删除 node_modules/.vite 目录。
相关文档参考 cn.vitejs.dev/guide/dep-p…