使用Vite初始化Vue3.x+TS项目

1,737 阅读5分钟

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

安装lint-staged

npx mrm@2 lint-staged

配置lint-staged

{
  "lint-staged": {
    "*.{js,jsx,ts,tsx,vue}": [
      "npm run lint",
      "git add"
    ]
  }
}

代码提交规范

使用commitlint

# 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"'

生成change log

如果提交是出现下面报错,需要将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
    })
  ]
}

相关文档地址

Vue Router

安装Vue Router

npm install vue-router@4

router-linkrouter-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>
...

处理文件路径

配置vite alias

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)
  • 配置代理
  • 其他方案如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…