1.课程大纲 #

1.1 Vite2+Vue3+Typescript 搭建开发环境 #

1.2 rollup和vite2插件系统和自定义插件 #

1.3 从零实现vite2 #

2.安装 vite #

2.1 vite 介绍 #

2.2 vite 安装 #

pnpm install vite -D

3.启动 vite #

3.1 package.json #

{
  "scripts": {
    "dev": "vite",
    "build": "vite build"
  }
}

3.2 vite.config.js #

import { defineConfig } from "vite"
export default defineConfig({})

3.3 index.html #

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite App</title>
  </head>

  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>

3.4 src\env.d.ts #

/// <reference types="vite/client" />

3.5 src\main.ts #

src\main.ts

export function render() {
  document.getElementById("app")!.innerHTML = "main";
}
render();

if (import.meta.hot) {
  import.meta.hot.accept((updatedModule) => updatedModule.render());
}

3.6 tsconfig.json #

tsconfig.json

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "moduleResolution": "node",
    "strict": true,
    "jsx": "preserve",
    "sourceMap": true,
    "esModuleInterop": true,
    "lib": ["esnext", "dom", "es2018.promise"]
  },
  "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
}
参数 解释
target 用于指定编译之后的版本目标
module 生成的模块形式:none、commonjs、amd、system、umd、es6、es2015 或 esnext 只有 amd 和 system 能和 outFile 一起使用 target 为 es5 或更低时可用 es6 和 es2015
moduleResolution 选择模块解析策略,有 node 和 classic 两种类型 module-resolution
strict 是否启动所有类型检查
jsx react 模式会生成 React.createElement,在使用前不需要再进行转换操作了,输出文件的扩展名为.js
sourceMap 把 ts 文件编译成 js 文件的时候,同时生成对应的 sourceMap 文件
esModuleInterop 为导入内容创建命名空间,实现 CommonJS 和 ES 模块之间的互相访问
lib 编译时引入的 ES 功能库,包括:es5 、es6、es7、dom 等。如果未设置,则默认为: target 为 es5 时: ["dom", "es5", "scripthost"] target 为 es6 时: ["dom", "es6", "dom.iterable", "scripthost"]
include include 也可以指定要编译的路径列表,但是和 files 的区别在于,这里的路径可以是文件夹,也可以是文件

3.7 .gitignore #

.gitignore

node_modules
pnpm-debug.log*
.vscode/*
sh.exe.stackdump
dist
coverage

4.支持 vue3 #

4.1 安装 vue #

pnpm install vue
pnpm install @vitejs/plugin-vue -D

4.2 vite.config.ts #

vite.config.ts

import { defineConfig } from 'vite'
+import vue from "@vitejs/plugin-vue";
export default defineConfig({
+ plugins: [vue()]
})

4.3 src\env.d.ts #

src\env.d.ts

/// <reference types="vite/client" />
+declare module "*.vue" {
+  import type { DefineComponent } from "vue";
+  const component: DefineComponent<{}, {}, any>;
+  export default component;
+}

4.4 src\main.ts #

src\main.ts

+import { createApp } from "vue";
+import App from "./App.vue";
+const app = createApp(App)
+app.mount("#app");

4.5 src\App.vue #

src\App.vue

<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue'
</script>

<template>
  <HelloWorld msg="Vue3 + TypeScript + Vite2" />
</template>

<style></style>

4.6 HelloWorld.vue #

src\components\HelloWorld.vue

<script setup lang="ts">
import { ref } from 'vue'
import logoUrl from '../assets/logo.png'
defineProps<{ msg: string }>()
const count = ref(0)
</script>

<template>
  <h1>{{ msg }}</h1>
  <img :src="logoUrl" />
  <div>
    <button type="button" @click="count++">count is: {{ count }}</button>
  </div>
</template>

<style scoped>
img {
  width: 100px;
  height: 100px;
}
</style>

5.支持 typescript #

5.1 安装 #

pnpm install typescript vue-tsc  -D

5.2 package.json #

package.json

{
  "scripts": {
    "dev": "vite",
+   "build": "vue-tsc --noEmit && vite build"
  },
}

5.3 msg.ts #

src\msg.ts

interface Msg {
  text: string;
}
const msg: Msg = {
  text: "zhufeng",
  age: 13
}

export default msg

5.4 App.vue #

src\App.vue

<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue'
+import msg from "./msg"
</script>

<template>
+  <HelloWorld :msg="msg.text" />
</template>

<style></style>

6.ESLint #

6.1 安装 #

pnpm install eslint eslint-plugin-vue  @typescript-eslint/parser @typescript-eslint/eslint-plugin @vue/eslint-config-typescript   -D
名称 说明
eslint ESLint 是一个用于识别和报告在 ECMAScript/JavaScript 代码中发现的模式的工具
eslint-plugin-vue Vue 的官方 ESLint 插件
@typescript-eslint/parser 一个 ESLint 解析器,它利用 TypeScript-ESTree 允许 ESLint 检查 TypeScript 源代码
@typescript-eslint/eslint-plugin 一个 ESLint 插件,为 TypeScript 代码库提供 lint 规则
@vue/eslint-config-typescript Vue 的 eslint-config-typescript

6.2 .eslintrc.js #

.eslintrc.js

module.exports = {
  root: true,
  env: {
    browser: true,
    es2021: true,
    node: true
  },

  extends: [
    "plugin:vue/vue3-recommended",
    "eslint:recommended",
    "@vue/typescript/recommended"
  ],
  parserOptions: {
    parser: "@typescript-eslint/parser",
    ecmaVersion: 2021
  },
  rules: {
    "no-unused-vars": "off",
    "vue/no-unused-vars": "off",
    "@typescript-eslint/no-unused-vars": "off"
  },
  globals: {
    defineProps: "readonly"
  }
}

6.3 .eslintignore #

.eslintignore

node_modules
dist
*.css
*.jpg
*.jpeg
*.png
*.gif
*.d.ts

6.4 package.json #

package.json

{
    "scripts": {
     "dev": "vite",
     "build": "vue-tsc --noEmit && vite build",
+    "lint": "eslint --ext .ts,.tsx,vue src/** --no-error-on-unmatched-pattern --quiet",
+    "lint:fix": "eslint --ext .ts,.tsx,vue src/** --no-error-on-unmatched-pattern --quiet --fix"
  },
}

7.Prettier #

7.1 安装 #

pnpm install prettier eslint-plugin-prettier  @vue/eslint-config-prettier -D
名称 说明
prettier 代码格式化
eslint-plugin-prettier 作为 ESLint 规则运行得 prettier
@vue/eslint-config-prettier Vue 的 eslint-config-prettier

7.2 .eslintrc.js #

.eslintrc.js

module.exports = {
  root: true,
  env: {
    browser: true,
    es2021: true,
    node: true
  },

  extends: [
    "plugin:vue/vue3-recommended",
    "eslint:recommended",
    "@vue/typescript/recommended",
+   "prettier",
+   "@vue/eslint-config-prettier"
  ],
  parserOptions: {
    parser: "@typescript-eslint/parser",
    ecmaVersion: 2021
  },
  rules: {
+   "prettier/prettier": [
+     "error",
+     {
+       singleQuote: false,
+       tabWidth: 2,
+       indent: 2,
+       semi: false,
+       trailingComma: "none",
+       endOfLine: "auto"
+     }
+   ],
+   "no-unused-vars": "off",
+   "vue/no-unused-vars": "off",
+   "@typescript-eslint/no-unused-vars": "off"
+ },
  globals: {
    defineProps: "readonly"
  }
}

7.3 .prettierrc.js #

.prettierrc.js

module.exports = {
  singleQuote: false, //使用单引号
  semi: false, ////末尾添加分号
  tabWidth: 2,
  trailingComma: "none",
  useTabs: false,
  endOfLine: "auto"
}

7.4 .prettierignore #

.prettierignore

node_modules
dist

7.5 editorconfig #

7.5.1 .editorconfig #

root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf

7.6 自动格式化 #

8.git hooks #

8.1 lint-staged #

8.1.1 安装 #

pnpm install mrm  -D
npx mrm lint-staged

8.1.2 package.json #

package.json

{
  "name": "vite2vue3ts-prepare",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "vite",
    "build": "vue-tsc --noEmit && vite build",
    "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",
+   "prepare": "husky install"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "vue": "^3.2.31"
  },
  "devDependencies": {
    "@typescript-eslint/eslint-plugin": "^5.12.1",
    "@typescript-eslint/parser": "^5.12.1",
    "@vitejs/plugin-vue": "^2.2.2",
    "@vue/eslint-config-prettier": "^7.0.0",
    "@vue/eslint-config-typescript": "^10.0.0",
    "eslint": "^8.10.0",
    "eslint-plugin-prettier": "^4.0.0",
    "eslint-plugin-vue": "^8.5.0",
+   "husky": "^7.0.0",
+   "lint-staged": "^12.3.4",
+   "mrm": "^3.0.10",
    "prettier": "^2.5.1",
    "typescript": "^4.5.5",
    "vite": "^2.8.4",
    "vue-tsc": "^0.32.0"
  },
+ "lint-staged": {
+   "*.{ts,vue}": "eslint --cache --fix"
+ }
}

8.2 commitlint #

8.2.1 type #

类型 描述
build 编译相关的修改,例如发布版本、对项目构建或者依赖的改动
chore 其他修改, 比如改变构建流程、或者增加依赖库、工具等
ci 持续集成修改
docs 文档修改
feature 新特性、新功能
fix 修改 bug
perf 优化相关,比如提升性能、体验
refactor 代码重构
revert 回滚到上一个版本
style 代码格式修改
test 测试用例修改

8.2.2 安装 #

pnpm install @commitlint/cli @commitlint/config-conventional -D

8.2.3 配置 #

npx husky add .husky/commit-msg "npx --no-install commitlint --edit $1"

8.2.4 commitlint.config.js #

module.exports = {
  extends: ["@commitlint/config-conventional"],
  rules: {
    "type-enum": [
      2,
      "always",
      [
        "build",
        "chore",
        "ci",
        "docs",
        "feature",
        "fix",
        "perf",
        "refactor",
        "revert",
        "style",
        "test"
      ]
    ]
  }
}

9.配置别名 #

9.1 vite.config.ts #

import { defineConfig } from "vite"
+import { resolve } from "path"
import vue from "@vitejs/plugin-vue"
export default defineConfig({
+ resolve: {
+   alias: {
+     "@": resolve("src")
+   }
+ },
  plugins: [vue()]
})

9.2 tsconfig.json #

tsconfig.json

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "moduleResolution": "node",
    "strict": true,
    "jsx": "preserve",
    "sourceMap": true,
    "esModuleInterop": true,
    "lib": ["esnext", "dom", "es2018.promise"],
+   "baseUrl": ".",
+   "paths": {
+     "@/*": ["src/*"]
+   }
  },
  "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
}

9.3 App.vue #

src\App.vue

<script setup lang="ts">
+import HelloWorld from "@/components/HelloWorld.vue"
import msg from "./msg"
</script>

<template>
  <HelloWorld :msg="msg.text" />
</template>

<style></style>

10.样式处理 #

10.1 全局样式 #

10.1.1 src\global.css #

#app {
  background-color: lightgrey;
}

10.1.2 src\main.ts #

src\main.ts

import { createApp } from "vue"
import App from "./App.vue"
+import "./global.css"
const app = createApp(App)
app.mount("#app")

10.2 局部样式 #

10.2.1 HelloWorld.vue #

src\components\HelloWorld.vue

<script setup lang="ts">
import { ref } from "vue"
import logoUrl from "../assets/logo.png"
defineProps<{ msg: string }>()
const count = ref(0)
</script>

<template>
  <h1>{{ msg }}</h1>
  <img :src="logoUrl" />
  <div>
    <button type="button" @click="count++">count is: {{ count }}</button>
  </div>
+ <a>超链接</a>
</template>

<style scoped>
img {
  width: 100px;
  height: 100px;
}
+a {
+  color: red;
+}
</style>

10.3 CSS Modules #

10.3.1 内联 #

10.3.1.1 HelloWorld.vue #

src\components\HelloWorld.vue

<script setup lang="ts">
import { ref } from "vue"
import logoUrl from "../assets/logo.png"
defineProps<{ msg: string }>()
const count = ref(0)
</script>

<template>
  <h1>{{ msg }}</h1>
  <img :src="logoUrl" />
  <div>
    <button type="button" @click="count++">count is: {{ count }}</button>
  </div>
+ <a :class="$style.link">超链接</a>
</template>

<style scoped>
img {
  width: 100px;
  height: 100px;
}
</style>
+<style module>
+.link {
+  color: red;
+}
+</style>


10.3.2 外联 #

10.3.2.1 HelloWorld.module.css #

src\components\HelloWorld.module.css

.link {
    color: red;
}
10.3.2.2 HelloWorld.vue #

src\components\HelloWorld.vue

<script setup lang="ts">
import { ref } from "vue"
import logoUrl from "../assets/logo.png"
+import style from "./HelloWorld.module.css"
defineProps<{ msg: string }>()
const count = ref(0)
</script>

<template>
  <h1>{{ msg }}</h1>
  <img :src="logoUrl" />
  <div>
    <button type="button" @click="count++">count is: {{ count }}</button>
  </div>
+ <a :class="style.link">超链接</a>
</template>

<style scoped>
img {
  width: 100px;
  height: 100px;
}
</style>
-<style module>
-.link {
-  color: red;
-}
-</style>

10.4 预处理器 #

10.4.1 安装 #

pnpm install sass less -D

10.4.2 src\components\HelloWorld.vue #

src\components\HelloWorld.vue

<script setup lang="ts">
import { ref } from "vue"
import logoUrl from "../assets/logo.png"
import style from "./HelloWorld.module.css"
defineProps<{ msg: string }>()
const count = ref(0)
</script>

<template>
  <h1>{{ msg }}</h1>
  <img :src="logoUrl" />
  <div>
    <button type="button" @click="count++">count is: {{ count }}</button>
  </div>
  <a :class="style.link">超链接</a>
  <h2>less</h2>
  <h3>sass</h3>
</template>

<style scoped>
img {
  width: 100px;
  height: 100px;
}
</style>
<style module>
.link {
  color: red;
}
</style>
+<style scoped lang="less">
+@color: red;
+h2 {
+  color: @color;
+}
+</style>
+<style scoped lang="scss">
+$color: green;
+h3 {
+  color: $color;
+}
+</style>

10.5 全局注入 #

10.5.1 vite.config.ts #

vite.config.ts

import { defineConfig } from "vite"
import { resolve } from "path"
import vue from "@vitejs/plugin-vue"
export default defineConfig({
  resolve: {
    alias: {
      "@": resolve("src")
    }
  },
  plugins: [vue()],
+ css: {
+   preprocessorOptions: {
+     scss: {
+       additionalData: '@import "@/styles/theme.scss";'
+     },
+     less: {
+       additionalData: '@import "@/styles/theme.less";'
+     }
+   }
+ }
})

10.5.2 src\styles\theme.less #

src\styles\theme.less

@color: red;

10.5.3 src\styles\theme.scss #

src\styles\theme.scss

$color: green;

10.5.4 src\components\HelloWorld.vue #

src\components\HelloWorld.vue

<script setup lang="ts">
import { ref } from "vue"
import logoUrl from "../assets/logo.png"
import style from "./HelloWorld.module.css"
defineProps<{ msg: string }>()
const count = ref(0)
</script>

<template>
  <h1>{{ msg }}</h1>
  <img :src="logoUrl" />
  <div>
    <button type="button" @click="count++">count is: {{ count }}</button>
  </div>
  <a :class="style.link">超链接</a>
  <h2>less</h2>
  <h3>sass</h3>
</template>

<style scoped>
img {
  width: 100px;
  height: 100px;
}
</style>
<style module>
.link {
  color: red;
}
</style>
<style scoped lang="less">
-@color: red;
h2 {
  color: @color;
}
</style>
<style scoped lang="scss">
-$color: green;
h3 {
  color: $color;
}
</style>


10.6 PostCSS #

10.6.1 安装 #

pnpm install autoprefixer  -D

10.6.2 postcss.config.js #

postcss.config.js

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

10.6.3 .browserslistrc #

.browserslistrc

>0.2%
not dead
not op_mini all

10.6.4 HelloWorld.vue #

src\components\HelloWorld.vue

<script setup lang="ts">
import { ref } from "vue"
import logoUrl from "../assets/logo.png"
import style from "./HelloWorld.module.css"
defineProps<{ msg: string }>()
const count = ref(0)
</script>

<template>
  <h1>{{ msg }}</h1>
  <img :src="logoUrl" />
  <div>
    <button type="button" @click="count++">count is: {{ count }}</button>
  </div>
  <a :class="style.link">超链接</a>
  <h2>less</h2>
  <h3>sass</h3>
+ <div class="postcss"></div>
</template>

<style scoped>
img {
  width: 100px;
  height: 100px;
}
</style>
<style module>
.link {
  color: red;
}
</style>
<style scoped lang="less">
@color: red;
h2 {
  color: @color;
}
</style>
<style scoped lang="scss">
$color: green;
h3 {
  color: $color;
}
</style>
+<style scoped>
+.postcss {
+  height: 30px;
+  width: 60px;
+  background-color: orange;
+  transform: rotate(25deg);
+}
+</style>

11.静态资源处理 #

11.1 模板引入 #

src\components\HelloWorld.vue

<template>
+ <img src="@/assets/logo.png" style="width: 50px" />
</template>

11.2 JS 中引入 #

src\components\HelloWorld.vue

<script setup lang="ts">
+import logoUrl from "@/assets/logo.png"
</script>
<template>
+ <img :src="logoUrl" style="width: 50px" />
</template>

11.3 CSS 引入 #

src\components\HelloWorld.vue

<template>
+ <div class="logo"></div>
</template>
<style scoped>
.logo {
  width: 50px;
  height: 50px;
  background-image: url(@/assets/logo.png);
  background-size: contain;
}
</style>

11.4 public 目录 #

index.html

<body>
  <img src="/logo.png" style="width:50px" />
</body>

12. mock #

12.1 安装 #

pnpm install mockjs  vite-plugin-mock -D

12.2 vite.config.ts #

vite.config.ts

import { defineConfig } from "vite"
import { resolve } from "path"
import vue from "@vitejs/plugin-vue"
+import { viteMockServe } from "vite-plugin-mock"
export default defineConfig({
  resolve: {
    alias: {
      "@": resolve("src")
    }
  },
+ plugins: [vue(), viteMockServe()],
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: '@import "@/styles/theme.scss";'
      },
      less: {
        additionalData: '@import "@/styles/theme.less";'
      }
    }
  }
})

12.3 mock\auth.ts #

mock\auth.ts

import { MockMethod } from "vite-plugin-mock"
export default [
  {
    url: "/api/currentUser",
    method: "get",
    response: ({ query }) => {
      return {
        code: 0,
        data: "zhufeng"
      }
    }
  }
] as MockMethod[]

13. 封装请求 #

13.1 安装 #

pnpm install axios

13.2 mock\auth.ts #

mock\auth.ts

import { MockMethod } from "vite-plugin-mock"
export default [
+ {
+   url: "/api/currentUser",
+   method: "get",
+   response: ({ headers }) => {
+     const { token } = headers
+     return {
+       code: 0,
+       data: token
+     }
+   }
+ },
+ {
+   url: "/api/login",
+   method: "post",
+   response: ({ body }) => {
+     //url body,query headers
+     return {
+       code: 0,
+       data: `${body.username}-token`
+     }
+   }
+ }
] as MockMethod[]

13.3 http.ts #

src\api\http.ts

import axios, { AxiosRequestConfig } from "axios"
axios.defaults.baseURL = "/api"
axios.defaults.timeout = 10000
axios.defaults.headers.post["Content-Type"] = "application/json;charset=UTF-8"
axios.interceptors.request.use(
  (config): AxiosRequestConfig => {
    const token = window.sessionStorage.getItem("token")
    if (token) {
      ;(config.headers = config.headers || {}).token = token
    }
    return config
  },
  (error) => {
    throw error
  }
)
// 响应拦截
axios.interceptors.response.use((res) => {
  return res.data
})

export default axios

13.4 src\api\auth.ts #

src\api\auth.ts

import http from "./http"
import { LoginParams } from "@/typings/auth"
export function login(loginParams: LoginParams) {
  return http.post("/login", loginParams)
}
export function getCurrentUser() {
  return http.get("/currentUser")
}

13.5 auth.ts #

src\typings\auth.ts

export interface LoginParams {
  username: string
  password: string
}
export interface Response {
  code: number
  data: any
}

13.6 src\request.ts #

src\request.ts

import { LoginParams, Response } from "@/typings/auth"
import { login, getCurrentUser } from "@/api/auth"
const loginParams: LoginParams = { username: "zhufeng", password: "123456" }
login(loginParams).then((result) => {
  const token = result.data
  window.sessionStorage.setItem("token", token)
  getCurrentUser().then((result) => {
    console.log(result.data)
  })
})

13.7 main.ts #

src\main.ts

import { createApp } from "vue"
import App from "./App.vue"
import "./global.css"
+import "@/request"
const app = createApp(App)
app.mount("#app")


14. 状态管理 #

14.1 安装 #

pnpm install pinia

14.2 src\main.ts #

src\main.ts

import { createApp } from "vue"
import App from "./App.vue"
import "./global.css"
import "@/request"
+import { createPinia } from "pinia"
const app = createApp(App)
+app.use(createPinia())
app.mount("#app")

14.3 store\index.ts #

src\store\index.ts

import { defineStore } from "pinia"

export const useMainStore = defineStore({
  id: "main",
  state: () => ({
    count: 0,
    firstName: "张",
    lastName: "三"
  }),
  //计算属性
  getters: {
    name: (state) => state.firstName + state.lastName
  },
  actions: {
    async addAmount(amount: number) {
      this.count += amount
    }
  }
})

14.4 HelloWorld.vue #

src\components\HelloWorld.vue

<script setup lang="ts">
import { ref } from "vue"
import logoUrl from "../assets/logo.png"
import style from "./HelloWorld.module.css"
+import { useMainStore } from "@/store"
+import { storeToRefs } from "pinia"
+defineProps<{ msg: string }>()
+const count = ref(0)
+const mainStore = useMainStore()
+//const storeCount = mainStore.count
+const { count: storeCount } = storeToRefs(mainStore)

+const add = () => {
+  //适合多字段改变
+  mainStore.$patch({
+    name: "arch",
+    count: mainStore.count + 1
+  })
+}
+const add2 = () => {
+  //适合多字段改变
+  mainStore.$patch((state) => ({
+    name: "arch2",
+    count: mainStore.count + 2
+  }))
+}
</script>

<template>
  <h1>{{ msg }}</h1>
  <img :src="logoUrl" />
  <div>
    <button type="button" @click="count++">count is: {{ count }}</button>
  </div>
  <a :class="style.link">超链接</a>
  <h2>less</h2>
  <h3>sass</h3>
  <div class="postcss"></div>
+ <div>
+   <p>name:{{ mainStore.name }}</p>
+   <p>count:{{ mainStore.count }}</p>
+   <p>count:{{ storeCount }}</p>
+   <button @click="mainStore.count++">mainStore.count++</button>
+   <button @click="add()">add()</button>
+   <button @click="add2()">add2()</button>
+   <button @click="mainStore.addAmount(3)">mainStore.addAmount(3)</button>
+   <p>name:{{ mainStore.name }}</p>
+ </div>
</template>

<style scoped>
img {
  width: 100px;
  height: 100px;
}
</style>
<style module>
.link {
  color: red;
}
</style>
<style scoped lang="less">
@color: red;
h2 {
  color: @color;
}
</style>
<style scoped lang="scss">
$color: green;
h3 {
  color: $color;
}
</style>
<style scoped>
.postcss {
  height: 30px;
  width: 60px;
  background-color: orange;
  transform: rotate(25deg);
}
</style>


15. 路由 #

15.1 安装 #

pnpm install vue-router

15.2 router\index.ts #

src\router\index.ts

import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router"

const routes: RouteRecordRaw[] = [
  {
    path: "/",
    name: "home",
    component: () => import("@/pages/HomePage.vue")
  },
  {
    path: "/login",
    name: "login",
    component: () => import("@/pages/LoginPage.vue")
  }
]

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

export default router

15.3 HomePage.vue #

src\pages\HomePage.vue

<template>
  <h1>Home</h1>
</template>

15.4 LoginPage.vue #

src\pages\LoginPage.vue

<template>
  <form>
    用户名 <input />
  </form>
</template>

15.5 src\main.ts #

src\main.ts

import { createApp } from "vue"
import App from "./App.vue"
import "./global.css"
import "@/request"
import { createPinia } from "pinia"
+import router from "./router"
const app = createApp(App)
app.use(createPinia())
+app.use(router)
app.mount("#app")

15.6 App.vue #

src\App.vue

<script setup lang="ts">
import HelloWorld from "@/components/HelloWorld.vue"
import msg from "./msg"
</script>

<template>
+  <router-view></router-view>
</template>

<style></style>

16. UI 组件库 #

16.1 安装 #

pnpm install naive-ui vfonts @vicons/ionicons5

16.2 src\App.vue #

<script setup lang="ts">
import HelloWorld from "@/components/HelloWorld.vue"
import msg from "./msg"
+import { h, Component } from "vue"
+import { NIcon, NMenu } from "naive-ui"
+import type { MenuOption } from "naive-ui"
+import { RouterLink } from "vue-router"
+import { HomeOutline, LogInOutline } from "@vicons/ionicons5"
+const menuOptions: MenuOption[] = [
+  {
+    label: () =>
+      h(RouterLink, { to: { name: "home" } }, { default: () => "首页" }),
+    key: "home",
+    icon: () => h(NIcon, null, { default: () => h(HomeOutline) }) //带有插槽的对象
+  },
+  {
+    label: () =>
+      h(RouterLink, { to: { name: "login" } }, { default: () => "登录" }),
+    key: "login",
+    icon: () => h(NIcon, null, { default: () => h(LogInOutline) })
+  }
+]
</script>

<template>
+  <n-menu :options="menuOptions" />
  <router-view></router-view>
</template>

<style></style>

17. 环境变量和模式 #

17.1 .env.development #

VITE_APP_WEB_URL = "/dev"

17.2 .env.production #

VITE_APP_WEB_URL = "/prod"

17.3 src\main.ts #

+console.log(import.meta.env)

17.4 package.json #

package.json

{
  "scripts": {
+    "build:dev": "vue-tsc --noEmit && vite build --mode development",
+    "build:prod": "vue-tsc --noEmit && vite build --mode production",
  },
}