Vue-issue-and-typescript

Vue 路由的坑和 SSR 的坑

因为我没有自己的服务器,所以我只能使用 github 提供的 pages 服务。

但是这个服务是静态的,所以我需要使用 vue SSG 渲染 SSR 的页面,使其成为静态页面。

但是这个过程中遇到了很多问题,唯独 router 和 SSR 的问题最为棘手。

踩坑

在正常的 vue 项目中,我们可以使用 vue-router 来进行路由的管理。

这时我们使用官方的方法构造 main.ts 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { createApp } from 'vue'
import App from './App.vue'

import { createRouter, createWebHistory } from 'vue-router'
import Home from './views/Home.vue'
import About from './views/About.vue'

const router = createRouter({
history: createWebHistory(),
routes: [
{ path: '/', component: Home },
{ path: '/about', component: About }
]
})

const app = createApp(App)
app.use(router)
app.mount('#app')

这样很好,路由也可以正常工作。

但是我们在使用 SSR 的时候,我们需要使用 SSG 的方法构造 main.ts 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { ViteSSG } from 'vite-ssg'
import App from './App.vue'
import routes from './router' // 确保你已经创建了 router 文件
import { ID_INJECTION_KEY } from 'element-plus'

// `export const createApp` is required instead of the original `createApp(App).mount('#app')`
export const createApp = ViteSSG(
// the root component
App,
// vue-router options
{ routes },
// function to have custom setups
({ app, router, routes, isClient, initialState }) => {
// install plugins etc.
app.provide(ID_INJECTION_KEY, {
prefix: 100,
current: 0,
})

},
)

这个文件中,我使用了 ID_INJECTION_KEY 这个变量,这个变量是element-plus的一个插件,我在这里只是举例说明。

并且,我们需要在 router 文件中不能使用 createRouter 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

// const router = createRouter({
// history: createWebHistory(process.env.VUE_APP_BASE_URL),
// routes
// })

// const isServer = typeof window === 'undefined';
// const history = isServer
// ? createMemoryHistory(import.meta.env.BASE_URL)
// : createWebHistory(import.meta.env.BASE_URL);
//
// const routerOptions = {
// history: history,
// routes,
// }
//
// const router = createRouter(routerOptions);

export default routes

可以看到,直接 export default routes

然后运行 vue-ssg 的命令 vite-ssg build,然后我们会发现,运行直接报错。

我们需要在 vite.config.ts 文件中添加一些配置:

1
2
3
4
5
6
7
8
resolve: {
alias: {
'~/': `${pathSrc}/`,
// 需要使用 ssg 编译时,要把 vue 的别名注释掉
'vue': 'vue/dist/vue.esm-bundler.js',
"@": path.resolve(__dirname, 'src'),
},
},

这样就可以解决这个问题。

TypeScript

很久之前,我就开始使用 TypeScript 了,现在开了一个新的项目,使用了 Vue3 和 TypeScript。

故复习一遍,做一些记录。


TypeScript 是 JavaScript 的超集,它可以编译成纯 JavaScript。TypeScript 是一种由微软开发的自由和开源的编程语言。

TypeScript is a strongly typed programming language that builds on JavaScript, giving you better tooling at any scale.

TypeScript 官方文档

使用

在 Vue3 中,我们可以直接使用 TypeScript,不需要额外的配置。


本地安装 TypeScript:

1
npm install -g typescript

类型推断

TypeScript 会根据你的代码,自动推断出类型。

1
2
let a = 1;
a = '1'; // Error: Type 'string' is not assignable to type 'number'.

类型注解

我们可以使用类型注解,来明确的告诉 TypeScript 变量的类型。

1
2
let a: number = 1;
a = '1'; // Error: Type 'string' is not assignable to type 'number'.
1
2
3
let number = [1, 2, 3];
const result = number.find((item) => item > 2);
result * 2; // Error: Object is possibly 'undefined'.

如果把第2行改成:

1
const result = number.find((item) => item > 2) as number;

这样就不会报错了。

基础类型以及联合类型

1
2
3
4
5
6
7
8
9
10
let v1: string = 'abc';
let v2: number = 123;
let v3: boolean = true;
let v4: null = null;
let v5: undefined = undefined;
let v6: symbol = Symbol('key');
let v7: bigint = 100n;

let v8: string | number = 'abc';
v8 = 123;

数组

1
2
let arr1: number[] = [1, 2, 3];
let arr2: Array<string> = ['a', 'b', 'c'];

元组

1
let tuple: [string, number] = ['a', 1];

枚举

1
2
3
4
5
6
7
8
9
10
enum Color {
Red,
Green,
Blue,
}

let c: Color = Color.Green;
console.log(c); // 1
console.log(Color[1]); // Green
console.log(Color['Green']); // 1

void

1
function log(message: string): void

void 表示没有任何类型,通常用于函数没有返回值。

函数

1
2
3
function add(a: number, b: number): number {
return a + b;
}

接口

1
2
3
4
5
6
7
8
9
10
11
12
13
interface Person {
name: string;
age: number;
}

const person: Person = {
name: 'Tom',
age: 18,
};

function printPerson(person: Person): void {
console.log(person.name, person.age);
}// Tom 18

Type

type 是 TypeScript 的一个关键字,用来定义类型别名。

1
2
3
4
type UserName = string | undefined;

let name: UserName = 'Tom';
name = undefined;

泛型

泛型是 TypeScript 的一个重要特性,它可以让我们在定义函数、接口、类的时候,可以使用任意类型。

1
2
3
4
5
6
function myFn <T>(arg: T): T {
return arg;
}

let result = myFn<string>('Hello');
console.log(result); // Hello

这里的 T 是一个占位符,可以是任意类型。这个例子中,我们使用了泛型来定义一个函数,这个函数接受一个参数,返回这个参数。