当前位置:   article > 正文

vue3+ts

vue3+ts

在这里插入图片描述

cat ‘不是内部或外部命令…’ 用type代替cat
在这里插入图片描述
https://github.com/sunniejs/vue-h5-template

JS

最新的ECMAScript标准定义了8种数据类型:

  • 7种原始类型:
    Boolean
    Null
    Undefined
    Number
    Biglnt
    String
    Symbol
  • 和 Object
    原始值:除Object以外的所有类型都是不可变的(值本身无法被改变)。

ts

1、基本数据类型

// 原始数据类型: Boolean;Null;Undefined;Number;BigInt;String;Symbol

let isDone: boolean = false

// es6 中 number还支持二进制和八进制
let age: number = 10
let binaryNumber: number = 0b1111

// 字符串类型,也可使用es6中的模板字符串
let firstName: string = 'wly'
let message: string = `hello, ${firstName}`

// undefinde 和 null 所有类型的子类型
let u: undefined = undefined
let n: null = null

// undefined类型可以赋值给number类型的变量[官方这么讲,但是我码出来就是报错,暂时还未知]
let num: number = undefined

// any类型
let notSure: any = 4
notSure = 'maybe a string'
notSure = false
notSure.myName  // 在任意值上访问任何属性都是允许的
notSure.getName()  // 也允许调用任何方法
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

2、数组Array和元组Tuple

// 数组
let arrOfNumbers: number[] = [1,2,3]  //定义数字类型的数组
arrOfNumbers.push(4)

function test() {
    console.log(arguments) //arguments 是一个对应于传递给函数的参数的类数组对象。
    
}

function func1(a, b, c) {
    console.log(arguments[0])  // 1
    console.log(arguments[1])  // 2
    console.log(arguments[2])  // 3
}
func1(1, 2, 3)

// 元组Tuple 声明几个类型就能写几个,不能多写少些
let user: [string, number] = ['wly', 123]
user.push(12)  //但是可以push,push的值只能是string、number-联合类型
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

3、interface接口

// 定义一个接口 IPerson
interface IPerson {
    readonly id: number;  //readonly只读属性
    name: string;
    age?: number;  //?可选属性,这一项可有可无,否则不可多项和少项
}

// 定义一个变量viking, 他的类型是IPerson
let viking: IPerson = {
    id: 1,
    name: 'viking'
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

4、函数

// 约定输入,约定输出
function add(x: number, y: number): number {
    return x + y
}

let result = add(1, 2)

// z是个可选参数
const add2 = (x: number, y: number, z?: number): number => {
    if(typeof z === 'number') {
        return x + y + z
    } else  {
        return x + y
    }
}

// 函数本身的类型,注意在函数类型里面返回类型是个箭头
const add3: (x: number, y: number, z?: number) => number = add2

// 接口里面返回类型是个冒号
interface ISum {
    (x: number, y: number, z?:number): number
}
let add4: ISum = add2
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

5、类型推论、联合类型、类型断言、类型守卫

let str = 'str'

// union types-联合类型 中间用竖线来分割
let umberOrString: number | string
umberOrString = 1
umberOrString.toString()  // 只能访问此联合类型里共有的属性或方法

// type assertions-类型断言 as作为关键字-当编辑器无法判断我的代码,但是本人很清楚,我把它成string,你也可以用string方法
function getLength(input: string | number): number {
    const str = input as string
    if(str.length) {
        return str.length
    } else {
        const number = input as number
        return number.toString().length
    }
}

//type guard-类型守卫 在不同条件的分支里面,智能缩小范围,降低代码出错
function getLength2(input: string | number): number {
    if(typeof input === 'string') {
        return input.length
    } else {
        return input.toString().length
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

6、枚举

// 数字枚举 关键词enum, 我们定义了一系列得方向,这里得值,枚举成员会被赋值为从 0 开始递增的数字
enum Direction {
    Up,
    Down,
    Left,
    Right
}
console.log(Direction.Up)  // 0
console.log(Direction[0])  // Up // 这个枚举还做了反向映射

enum Direction2 {
    Up = 'up',
    Down = 'down',
    Left = 'left',
    Right = 'right'
}
const value = 'up'
if(value === Direction2.Up) {
    console.log('go up!!')
}

// 常量枚举
const enum Direction3 {
    Up = 'UP',
    Down = 'DOWN',
    Left = 'LEFT',
    Right = 'RIGHT'
}
const value3 = 'UP'
if(value3 === Direction3.Up) {
    console.log("go up!!!")
}// An highlighted block
var foo = 'bar';
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

注意字符串枚举跟常量枚举最本质得区分就是js中,字符串枚举只会执行Direction3.Up,其余在Direction3得其他属性值都不会执行
在这里插入图片描述

7、泛型

function echo<T>(arg: T):T {
    return arg
}
// const str: string = 'str'
// const result = echo(str)

const result1 = echo(123)

function swap<T, U>(tuple: [T, U]):[U, T] {
    return [tuple[1], tuple[0]]
}
const result2 = swap(['string', 123])


// 约束泛型
function echoWithArr<T>(arg: T[]):T[] {
    console.log(arg.length)
    return arg
}
const arrs = echoWithArr([1, 2, 3])


interface IWithLength {
    length: number
}
function echoWithArrLength<T extends IWithLength>(arg: T): T {
    return arg
}
const str = echoWithArrLength('str')
const obj = echoWithArrLength({ length: 10, width: 10 })
const arr2 = echoWithArrLength([1, 2, 3])
echoWithArrLength(13)  // number里面没有length属性,所以报错


// 类的公有类型(public)和私有类型(private)
class Queue<T> {
    private data: T[] = [];
    push(item: T) {
        return this.data.push(item)
    }
    pop() {
        return this.data.shift()
    }
}
const queue = new Queue<number>()
queue.push(1)
const popedValue = queue.pop()
if(popedValue) {
    console.log(popedValue.toFixed())
}


// 泛型和interface
interface KeyPair<T, U> {
    key: T
    value: U
}
let kp1: KeyPair<number, string> = {key: 1, value: 'string'}
let kp2: KeyPair<string, number> = {key: 'str', value: 2}
let arr: number[] = [1, 2, 3]
let arrTwo: Array<number> = [1, 2, 3]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

8、类型别名 和 交叉类型

// 类型别名 和 交叉类型

import { type } from "os"

let sum: (x: number, y: number) => number
const result = sum(1, 2)
type PlustType = (x: number, y: number) => number
let sum2: PlustType
const result2 = sum2(2,3)

// 支持联合类型
type StringOrNumber = string | number
let result3: StringOrNumber = '123'
result3 = 123

const str: 'name' = 'name'
const number: 1 = 1

// 字符串字面量
type Direcions = 'Up' | 'Down' | 'Left' | 'Right'
let toWhere: Direcions = 'Left'

// 交叉类型
interface IName {
    name: string
}
type IPerson = IName & {age: number}
let person: IPerson = {name: '123', age: 123}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

9、声明文件

(1) axios.d.ts //注.d.ts固定写法

注:这两个文件需要同时在编辑器中打开,不然axio会报错

// 它里面没有任何的实际实现代码,只有类型声明
// 只有类型 - 比如 interface,funciton 或者 class 等等

// declare function axios(url: string): string
interface IAxios {
    get: (url: string) => string;
    post: (url: string, data: any) => string
}
declare const axios: IAxios  // declare 声明类型
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

declaration-files.ts

// axios('test.url')
axios.get('url')
axios.post('url', {name: 'viking'})
  • 1
  • 2
  • 3
(2) 引入第三方声明文件

npm i axios
declaration-files.ts

import axios from 'axios'
axios.get('ulr')
axios.post('url', {name: 'viking'})
  • 1
  • 2
  • 3
(3)声明文件-小例子 计算器
//第一种
// caculator.d.ts
type IOperator = 'plus' | 'minus'
type ICalculator = (operator: IOperator, numbers: number[]) => number
declare const calculator: ICalculator
// declaration-files.ts
calculator('minus', [1,2])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
// 第二种
// caculator.d.ts
type IOperator = 'plus' | 'minus'
interface ICalculator {
	(operator: IOperator, numbers: number[]): number;
	plus: (numbers: number[]) => number;
	minus: (numbers: number[]) => number;
}
declare const calculator: ICalculator
export default calculator // 也可以使用es6
// declaration-files.ts
import calculator from './calculator'
calculator.minus([1,3])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

将第二种的caculator.d.ts代码,挪到node_modules下的@types中创建文件夹caculator里面建立文件index.d.ts里面(作为模块来使用)

// declaration-files.ts
import calculator from 'calculator'
  • 1
  • 2

10、内置类型

const a: Array<number> = [1, 2, 3]
// 大家可以看到这个类型,不同的文件中有多处定义,但是它们都是 内部定义的一部分,然后根据不同的版本或者功能合并在了一起,一个interface 或者 类多次定义会合并在一起。这些文件一般都是以 lib 开头,以 d.ts 结尾,告诉大家,我是一个内置对象类型欧
const date: Date = new Date()
date.getTime()
const reg = /abc/
// 我们还可以使用一些 build in object,内置对象,比如 Math 与其他全局对象不同的是,Math 不是一个构造器。Math 的所有属性与方法都是静态的。
reg.test('abc')

Math.pow(2, 2)

// DOM 和 BOM 标准对象
// document 对象,返回的是一个 HTMLElement
let body: HTMLElement = document.body
// document 上面的query 方法,返回的是一个 nodeList 类型
let allList = document.querySelectorAll('li')
allList.keys()

//当然添加事件也是很重要的一部分,document 上面有 addEventListener 方法,注意这个回调函数,因为类型推断,这里面的 e 事件对象也自动获得了类型,这里是个 mouseEvent 类型,因为点击是一个鼠标事件,现在我们可以方便的使用 e 上面的方法和属性。
document.addEventListener('click', (e) => {
    e.preventDefault()
})


interface IPerson {
    name: string,
    age: number
}
let viking: IPerson = { name: 'viking', age: 20 }
type IPartial = Partial<IPerson>
let viking2: IPartial = { name: 'viking'}
type IOmit = Omit<IPerson, 'name'>  // IPerson name属性忽略掉
let viking3: IOmit = { age: 20 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

11、配置文件

{
  "files": ["test.ts", "test2.d.ts"],
  "compilerOptions": {
    "outDir": "./output",
    "module": "ESNext",
    "target":"ES5",
    "declaration": true
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

vue 3.0

vue3
vue-cli

vue-ui 图形化界面创捷项目
vite脚手架 不同于vue-cli的创新

// vue 3.0推荐vscode种volar插件,注将原来vetur禁掉

// 查看项目下的eslint版本

在这里插入图片描述
在这里插入图片描述
vite:https://cn.vitejs.dev/

vscode中之前用的插件是vetur,vue3.0用的插件是volar

ref

// 注:ref定义变量读取的时候写法是 该变量名.value

<template>
  <h1>{{ count }}</h1>
  <button @click="increase">ddddd</button>
  <h2>{{ double }}</h2>
</template>

<script lang="ts">
// ref 是一个函数,它接受一个参数,返回的是一个神奇的 响应式对象。我们初始化的这个0作为参数包裹到这个对象中去,在未来可以检测到改变并作出对应的响应
import  { defineComponent, ref, computed } from 'vue';
export default defineComponent({
  name: 'App',
  setup() {
    const count = ref(0)
    const double = computed(() => {
      return count.value * 2
    })
    const increase = () => {
      count.value++
    }
    return {
      count,
      increase,
      double
    }
  }
});
</script>

<style></style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

reactive、toRefs

使用ref还是reactive可以选择准则
(1)就像刚才的原生javascript的代码一样,像你平常写普通的js代码选择原始类型和对象类型一样来选择使用ref还是reactive
(2)所有场景使用reactive,但要记得使用toRefs保证reactive
对象属性保持响应性

<template>
  <h1>{{ count }}</h1>
  <button @click="increase">ddddd</button>
  <h2>{{ double }}</h2>
</template>

<script lang="ts">
import  { defineComponent, computed, reactive, toRefs } from 'vue';
interface DataProps {
  count: number;
  double: number;
  increase: () => void
}
export default defineComponent({
  name: 'App',
  setup() {
    const data: DataProps = reactive({
      count: 0,
      increase: () => {
        data.count++
      },
      double: computed(() => data.count * 2)
    })
    const refData = toRefs(data)
    return {
      ...refData
    }
  }
});
</script>

<style>
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

ref:原始类型(number, string, boolean, undefined, null)和对象都可以进行相应的数据做响应式处理

const ref1 = ref(0)  //ok
const ref2 = ref({ a: 0 })  //ok

// ref可以处理原始类型的值,对于引用类型值也可。如果ref函数传递一个对象,那么这个对象要通过reactive()这个方法将其转换成深层次的响应式对象,换句话来说也就是内部本质还是调用了reactive()方法
  • 1
  • 2
  • 3
  • 4

reactive:该方法是不允许传递原始类型的值,它并没有和ref一样检测到原始类型之后进行做对应的转换

const reactive1 = reactive(0)  // not ok
const reactive2 = reactive({ a: 0 })  //ok

// reactive1这种写法不会报错,页面也能渲染出来,但是没有办法改变它,失去了意义,对于reactive只能是对象最主要的原因在于在vue内部的相应式的实现,是依据proxy实现的,但是proxy不适用于基本类型数据
  • 1
  • 2
  • 3
  • 4

ref和reactive区别: ref对于基本类型和引用类型都可以,但是reactive只适用于引用类型
数据访问方式中ref:无论是原始类型还是对象,访问数据都需要通过.value的形式进行,更新数据的话也是通过.value

const ref1 = ref(0)
console.log(ref1.value)  // 0
const ref2 = ref({ a: 0 })
console.log(ref2.value.a)  // 0
ref1.value = 1
console.log(ref1.value)  // 1

<div>{{ ref1 }}</div>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

数据访问方式中reactive:由于是proxy代理的对象数据,可以直接更新和访问数据

const reactive1 = reactive({ num : 0})
console.log(reactive1.num)  // 0
  • 1
  • 2

TS类型:ref

import  { ref, Ref } from 'vue'
const ref1: Ref<number> = ref(0)
  • 1
  • 2

TS类型:reactive

import { reactive } from 'vue'
const reactive1: { num: number } = reactive({ num: 0 })
  • 1
  • 2

watch监听方式:ref

const ref1 = ref(0)
watch(ref1, () => {
	console.log("changed")
})
// 注:ref中原始类型数据,这样写没啥问题当ref1.value改变时能被监听到
// 但如果是对象则需要深度监听才能监听到深层
const ref2 = ref({ a: 1 })
watch(ref2, () => {
	console.log('changed ref2')
}, { deep: true })
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

watch监听方式:reactive

const reactive1 = reactive({ a: 1 })
watch(reactive1, () => {
	console.log('changed reactive1')
})
  • 1
  • 2
  • 3
  • 4

ref和reactive总结:

  1. 响应式原始值,就使用ref(),注是原始值
  2. 响应式对象,层级不深,使用ref也可
  3. 响应式可变对象,并且对象层级较深,使用reactive

生命周期

vue 2.0 -> vue 3.0
beforeCreate -> use setup()
created -> use setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
activated -> onActivated
deactivated -> onDeactivated
errorCaptured -> onErrorCaptured

//调试 debugger钩子函数
onRenderTracked
onRenderTriggered
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

watch

watch数据源可以是ref(包括计算属性)、响应式对象、getter函数、或多个数据源组成的数组

1、ref值微基本类型
const simplePerson = ref('张三')

// 情景一
// 数据源为RefImpl
watch(simplePerson, (newVal, oldVal) => {
    console.log("simplePerson==", newVal)  // 张三
}, {
    immediate: true
})
// 情景二
// 数据源为'张三'
watch(simplePerson.value, (newVal, oldVal) => {  //非法数据源,监听不到且控制台告警
    console.log("simplePerson.value==", newVal)  // undefined
}, {
    immediate: true
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
2、ref值为引用类型

ref值为引用类型,等价于:person.value = reactive({name: ‘张三’})

// 2、ref值为引用类型,等价于:person.value = reactive({name: '张三'})
const person = ref({
    name: '张三'
})

// 情景三
// 数据源为RefImpl,但是.value才是响应式对象,所以要加deep
watch(person, (newVal, oldVal) => {
    console.log("person==", newVal) // Proxy {name: '张三'}
}, {
    immediate: true,
    deep: true  // 必须设置,否则监听不到内部变化
})
// 情景四
// 数据源为响应式对象
watch(person.value, (newVal, oldVal) => {
    console.log('person.value==', newVal)  // Proxy {name: '张三'}
}, {
    immediate: true
})
// 情景五
//数据源为'张三'
watch(person.value.name, (newVal, oldVal) => {  // 非法数据源,监听不到且控制警告
    console.log('person.value.name==', newVal)  // undefined
}, {
    immediate: true
})
// 情景六
// 数据源为getter函数,返回基本类型
watch(
    () => person.value.name,
    (newVal, oldVal) => {
        console.log("person.value.name==getter函数=", newVal)  // '张三'
    }
, {
    immediate: true
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
3、ref值包含嵌套的引用类型

ref值包含嵌套的引用类型,等价于:complexPerson.value = reactive({name: ‘张三’, info: {age: 18}})

const complexPerson = ref({name: '张三', info: {age: 18}})

// 情景七
// 数据源为响应式对象(在vue3中状态都是默认深层响应式的)
watch(complexPerson.value.info, (newVal, oldVal) => {
    console.log("complexPerson.value.info===", newVal)  // Proxy {age: 18}
},{
    immediate: true
})
// 情景八
// 数据源为getter函数,返回相应式对象
watch(
    () => complexPerson.value.info,
    (newVal, oldVal) => {
        console.log("complexPerson.value.info=getter函数==", newVal)  // Proxy {age: 18}
    }
, {
    immediate: true,
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
4、reactive
const reactivePerson = reactive({name: '张三', info: {age: 18}})

// 情景九
// 数据源为响应式对象
watch(reactivePerson, (newVal) => {
    console.log("reactivePerson==", newVal)  // Proxy {name: '张三', info: {age: 18}}
},{
    immediate: true
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

总结:
1、在Vue3中状态都是默认深层响应式的(情景七),嵌套的引用类型在取值(get)时一定是返回Proxy响应式对象

2、watch数据源为响应式对象时(情景四、七、九),会隐式的创建一个深层侦听器,不需要再显示设置deep: true

3、情景三和情景八两种情况下,必须显示设置deep: true,强制转换为深层侦听器

4、情景五和情景七对比下,虽然写法完全相同,但是如果属性值为基本类型时是监听不到的,尤其是ts类型声明为any时,ide也不会提示告警,导致排查问题比较费力

5、所以精确的ts类型声明很重要,否则经常会出现莫名其妙的watch不生效的问题

6、ref值为基本类型时通过get\set拦截实现响应式;ref值为引用类型时通过将.value属性转换为reactive响应式对象实现;

7、deep会影响性能,而reactive会隐式的设置deep: true,所以只有明确状态数据结构比较简单且数据量不大时使用reactive,其他一律使用ref

watch、watchEffect

watch、watchEffect区别:
(1)、执行时机:watchEffect是立即执行的,在页面加载时会主动执行一次,来收集依赖;而watch是惰性地执行副作用,它不会立即执行,但可以配置immediate,使其主动触发。
(2)、参数不同:watchEffect只需传递一个回调函数,不需要传递侦听的数据,它会在页面加载时主动执行一次,来收集依赖;而watch至少要有两个参数(第三个参数是配置项),第一个参数是侦听的数据,第二个参数是回调函数。
(3)、结果不同:watchEffect获取不到更改前的值;而watch可以获取更改前和更改后的值。

// 侦听一个单一源
// (1)、直接侦听一个ref
const count = ref(0)
watch(count, (count, prevCount) => {  // 基本类型写法
	/**...*/
},{immediate: true})  // watch是惰性地执行副作用,它不会立即执行,但可以配置 immediate,使其主动触发
// (2)、侦听一个getter
const state = reactive({ count: 0 })
watch(() => state.count, (count, prevCount) => {  //watch引用类型写法
})

// watchEffect
const bb = reactive({ count: 0 })
watchEffect(() => {
	console.log(bb.count)
})


 // watch 简单应用
    watch(data, () => {
      document.title = 'update' + data.count
    })
    // watch的两个参数,代表新的值和旧的值
    watch(refData.count, (newValue, oldValue) => {
      console.log("oldValue=",oldValue)
      console.log("newValue", newValue)
      document.title = 'update' + data.count
    })
    // watch多个值,返回的也是多个值的数组
    watch([greetings, data], (newValue, oldValue) => {
      console.log("oldValue", oldValue)
      console.log("newValue", newValue)
      document.title = 'updated' + greetings.value + data.count
    })
    // 使用greetings的写法watch reactive 对象中的一项
    watch([greetings, () => data.count], (newValue, oldValue) => {
      console.log('oldValue', oldValue)
      console.log("newValue", newValue)
      document.title = 'updated' + greetings.value + data.count
    })
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

hooks

vue3的hooks函数相当于vue2的mixin,不同在与hooks是函数
vue3的hooks函数可以提高代码的复用性,可以在不同组件中利用hooks函数
例:
1、在src目录下创建一个hooks文件夹
2、创建文件名.ts
在这里插入图片描述
useMousePosition.ts

import { ref, onMounted, onUnmounted} from 'vue'
function useMousePosition() {
    const x = ref(0)  // x绑定响应式数据
    const y = ref(0)
    const updateMouse = (e: MouseEvent) => {
      x.value = e.pageX
      y.value = e.pageY
    }
    onMounted(() => {
      document.addEventListener('click', updateMouse)
    })
    onUnmounted(() => {
        document.removeEventListener('click', updateMouse)
    })
    return {x, y}
}
export default useMousePosition
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

在***.vue中

<template>
  <div>
    <h1>x{{x}}</h1>
    <h1>y{{y}}</h1>
  </div>
</template>

<script lang="ts">
import  { defineComponent,ref, computed, reactive, toRefs, watch, onMounted, onUnmounted} from 'vue';
import useMousePostion from './hooks/useMousePosition'
export default defineComponent({
  name: 'App',
  setup() {
    const { x, y } = useMousePostion()
    return {
      x,
      y
    }
  }
});
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

reactive版本

import { reactive, toRefs, onMounted, onUnmounted } from 'vue'
interface UseMouse {
    x: number,
    y: number
}
function useMousePosition() {
    const useMouse: UseMouse = reactive({
        x: 0,
        y: 0
    })
    const updateMouse = ((e: MouseEvent) => {
        useMouse.x = e.pageX
        useMouse.y = e.pageY
    })
    onMounted(() => {
        document.addEventListener('click', updateMouse)
    })
    onUnmounted(() => {
        document.removeEventListener('click', updateMouse)
    })
    return {
        ...toRefs(useMouse)
    }
}
export default useMousePosition
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

vue3中单文件组件

一、相比普通script语法的优势

script setup>是在单文件组件(SFC)中使用组合式API的编译时语法糖。相比于普通的

  1. 更少的样板内容,更简洁的代码
  2. 使用纯TypeScript声明props和抛出事件
  3. 更好的运行时性能(其模板会被编译成与其同一作用域的渲染函数,没有任何的中间代理)
  4. 更好的IDE类型推断性能(减少语言服务器从代码中抽离型的工作)
二、基本语法
// 需要将setup attribute添加到<script>代码块上
<script setup>
  console.log('hello script setup')
</script>
// 里面的代码会被编译成组件setup()函数的内容。这意味着与普通的<script>只在组件被首次引入的时候执行一次不同,<script setup>中的代码会在每次组件实例被创建的时候执行
  • 1
  • 2
  • 3
  • 4
  • 5

顶层的绑定会被暴露给模板

// 当使用 <script setup> 的时候,任何在<script setup>声明的顶层的绑定(包括变量、函数声明,以及import引入的内容)都能在模板中直接使用

<template>
	<div @click="log">{{ msg }}</div>
</template>
<script setup>
// 变量
const msg = 'hello!'

// 函数
function log() {
  console.log(msg)
}
</script>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
// import 导入的内容也会以同样的方式暴露。意味着可以在模板表达式中直接使用导入的helper函数,并不需要通过methods选项来暴露它
<script setup>
	import { capitalize } from './helper'
</script>
<template>
	<div>{{ capitalize('hello') }}</div>
</template>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
三、响应式
// 响应式状态需要明确使用响应式API来创建。和setup()函数中返回值一样,ref值在模板中使用的时候会自动解包

<template>
	<div @click="count++">{{ count }}</div>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
四、使用组件
// <script setup>范围里的值也能被直接座位自定义组件的标签名使用
<template>
	<ChildrenComponent />
</template>
<script setup>
	import ChildrenComponent from './ChildrenComponent.vue'
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
动态组件
// 由于组件被引用为变量而不是作为字符串来注册的,在<script setup>中要使用动态组件的时候,就应该使用动态的:is来绑定
<template>
	<component :is="Foo"/>
	<component :is="someCondition ? Foo : Bar"/>
</template>
<script setup>
	import Foo from './Foo.vue'
	import Bar from './Bar.vue'
	const someCondition = true
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
递归组件
// 一个单文件组件可以通过它的文件名被其自己所引用。例如: 名为FooBar.vue的组件可以在其模板中用<FooBar/>引用它自己
// 请注意这种方式相比import导入的组件优先级更低。如果有命名的import导入和组件的推断名冲突了,可以使用import别名导入
import { FooBar as FooBarChild } from './components'
  • 1
  • 2
  • 3
命名空间组件
// 可以使用带点的组件标记,例如<Foo.Bar>来引入嵌套在对象属性中的组件。这在需要从文件中导入多个组件的时候非常有用
<template>
	<Form.Input>
		<Form.label>label</Form.label>
	</Form.Input>
</template>
<script setup>
	import * as form './from-components'
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
五、defineProps 和 defineEmits

不需要引入

.sync改成v-model

// 在<script setup>中必须使用defineProps和defineEmits API来声明props 和 emits,他们具备完整的类型推断并且在<script setup>中直接可用的
<script setup>
	const props = defineProps({
		foo: String
	})
	const emit = defineEmits(['change', 'delete'])
</script>
// defineProps 和 defineEmits 都是只在<script setup>中才能使用得编译器宏。他们不需要导入且会随着<script setup>处理过程一同被编译掉
// defineProps 接收与props 选项相同得值,defineEmits也接收emits选项相同得值
// defineProps 和 defineEmits 在选项传入后,会提供恰当得类型推断
// 传入到defineProps 和 defineEmits 得选项会从setup中提升到模块得范围。因此,传入得选项不能引用在setup范围中声明得局部变量。这样做会引起编译错误。
``````javascript
// 子组件中
<template>
    <div @click="tableChange('21d32332')">
        dfd {{ businessTableUuid }}
    </div>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue'
const props = defineProps({
    businessTableUuid: {
        type: String,
        required: true
    },
})
const emit = defineEmits(['update:businessTableUuid'])
// const businessTableUuid = ref('')
// const businessTableFieldUuid = ref('')

const tableChange = (businessTableUuid) => {
    emit('update:businessTableUuid', businessTableUuid) // 注意:update:businessTableUuid之间没有空格
}
</script>



// 父组件中
// 在vue3.0中 v-model:businessTableUuid代替了vue2.0中:businessTableUuid.sync
<template>
	<div>
        <Home2 v-model:businessTableUuid="businessTableUuid"></Home2>
    </div>
</template>
<script setup>
import { onMounted, nextTick, watch, watchEffect, ref, reactive, defineExpose } from "vue";
const businessTableUuid = ref('ddfff')
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
六、defineExpose
// 使用<script setup>得组件是默认关闭得,也即通过模板ref或者$parent获取到得组件得公开实例,不会暴露任何在<script setup>中声明得绑定
// 为了在<script setup>组件中明确暴露出去得属性,使用defineExpose编译器宏

//父组件引用子组件得事件或值
//子组件中
<script setup>
	import { ref, defineExpose } from 'vue'
	const count = ref(100)
	function changeCount() {
    	count.value = count.value + 1
	}
	defineExpose({
		count,
		changeCount
	})
</script>

//父组件中
<children ref="child"/>
const child = ref(null)
function getExposes() {
    console.log("getExposes==",child.value.count)
}
// 当父组件通过模板ref的方式获取到当前组件的实例,(ref会和在普通实例中一样被自动解包)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
七、useSlots 和 useAttrs
// 在<script setup>使用slots 和 attrs 的情况应该是罕见的,可以在模板中通过$slots和$attrs来访问它们,可以分别用useSlots 和 useAttrs 两个辅助函数
<script setup>
	import { useSlots, useAttrs } from 'vue'
	const slots = useSlots()
	const attrs = useAttrs()
</script>
// useSlots 和 useAttrs与setupContext.slots 和 setupContext.attrs等价值,同样也能在普通的组合式API中使用
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
八、顶层await
// <script setup>中可以使用顶层await。结果代码会被编译成async setup()
<script setup>
	const post = await fetch('/api/post/1').then(r => r.json())
</script>
// await的表达式会自动编译成在await之后保留当前组件实例上下文的格式
// async setup()必须与Suspense组合使用,Suspensen目前还是处于实验阶段的特性
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
九、css
动态绑定css v-bind()
<template>
	<div>123</div>
</template>
<script setup>
import { ref } from 'vue'
const theme = ref({
	color: 'red'
})
</script>
<style lang="less" scoped>
div {
	color: v-bind('theme.color')
}
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
:deep深度选择器
<style scoped>
.a :deep(.b) {
}
</style>
  • 1
  • 2
  • 3
  • 4
:slotted插槽选择器

修改插槽过来的组件的样式

<style scoped>
:slotted(.box) {
	color: blue
}
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
:global全局选择器

全局样式: 通常新建一个style标签,不加scoped,:global就是一种解决方案

<style lang="less" scoped>
:global(.box) {
	color: red;
}
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
混合使用局部与全局样式
<style>
/* 全局样式 */
</style>
<style scoped>
/* 局部样式 */
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

vite+vue3.0+ts+pinia项目搭建

在这里插入图片描述

eslint安装

npm install eslint
npx eslint --init  // eslint初始化

//  根目录下创建 .eslintrc.js
module.exports = {
    "env": {
        "browser": true,
        "es2021": true
    },
    "extends": [
        "eslint:recommended",
        "plugin:vue/vue3-essential",
        "plugin:@typescript-eslint/recommended"
    ],
    "overrides": [
    ],
    "parser": "vue-eslint-parser",
    "parserOptions": {
        "parser":"@typescript-eslint/parser",
        "ecmaVersion": "latest",
        "ecmaFeatures": {
            jsx: true
        }
    },
    "plugins": [
        "vue",
        "@typescript-eslint"
    ],
    "rules": {
        'space-before-function-paren': 0,
        'no-console': 0,
    }
}


npm install prettier eslint-config-prettier

// 其中process报错解决方法是
npm i @types/node
// 在vite.config.ts中添加
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  define: {
    'process.env': {}  // 新添加的部分
  }
})


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

router安装

// https://router.vuejs.org/zh/introduction.html
npm install vue-router

// 在src文件夹下创建router文件夹里面文件名为index.ts
/**
 * createRouter 这个为创建路由的方法
 * createWebHashHistory 这个就是vue2中路由的模式,这里的是hash模式,这个还可以是createWebHistory等
 * RouteRecordRaw 这个为要添加的路由记录,也可以说是routes的ts类型
 */
import { createRouter, createWebHashHistory } from 'vue-router';
const routes = [
    {
        path: '',  // 重定向。默认首页是加载home组件
        redirect: '/home'
    },
    {
        path: '/home',
        name: 'home',
        component: () => import('@/views/home.vue'),
        meta: {
            title: '首页'
        }
    },
    {
        // 匹配所有路径  vue2使用*   vue3使用/:pathMatch(.*)*或/:pathMatch(.*)或/:catchAll(.*)
        path: '/:pathMatch(.*)*',
        name: '404',
        component: () => import('@/views/404.vue')
    }
]
const router = createRouter({
    history: createWebHashHistory(),  // hash模式:createWebHashHistory,history模式:createWebHistory
    routes: routes
})
export default router


//main.ts中的文件
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'  // 添加的router

const app = createApp(App)
app.use(router)  // 添加的router
app.mount('#app')

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

组件

https://cn.vuejs.org/guide/introduction.html#api-styles
element组件库安装,注意vue3.0不适用于element-ui,用的是element-plus
并且注意 import ‘element-plus/dist/index.scss’
各种报错
在这里插入图片描述
此时耐着性子往下看文档,在vite.config.ts中添加相关内容,并且将 import 'element-plus/dist/index.scss’去除
在这里插入图片描述

模块路径

// 在tsconfig.json文件的“compilerOptions”中
{
  "compilerOptions": {
  	……
    "paths": {
      "@/*": [
        "src/*"
      ]
    }
  },
……
}


// 在tsconfig.node.json的文件"compilerOptions"下添加
{
  "compilerOptions": {
  	……
    "allowSyntheticDefaultImports": true
  }
}


// 在vite.config.js文件中
……
import path from 'path'  // 不按照上述修改 tsconfig.node.json 文件会报错
export default defineConfig({
  plugins: [
    vue(),
    ……
  ],
  resolve: {
    alias: {
      '~': path.resolve(__dirname, './node_modules/'),
      '@': path.resolve(__dirname, './src'),
      api: path.resolve('./src/api'),
      components: path.resolve('./src/components'),
      views: path.resolve('./src/views'),
      router: path.resolve('./src/router'),
    }
  }
})

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

pinia

https://pinia.vuejs.org/zh/getting-started.html
之前使用vuex进行状态管理,那么pinia相对于一个类似于的插件,是新一代轻量级状态管理插件

npm install pinia

// 安装完成后再src下创建文件夹strore,下再创建index.ts
// index.ts内容如下:
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
// defineStore()的返回值进行任意命名,最好使用store的名字,同时以`use`开头且以`Store`(比如`useUserStore`、`useIndexStore`)
// 第一个参数是你的应用中 Store 的唯一 ID。

// // 写法一
export const useCounterStore = defineStore('counter', {
  // 相当于vue的data
  state: () => {
    return {
        count: 1,
        userList: [] as UserInfo[],
        user: null as UserInfo | null
    }
  },
  // 相当于vue的computed,在getters中使用了this则必须手动指定返回值类型,否则类型推导不出来
  getters: {
    double: (state) => state.count * 6
  },
  // 相当于vue的methods,在actions中不能使用箭头函数,因为箭头函数绑定外部this
  actions: {
    increment() {
        this.count++
    }
  }
})

// // 写法二
// // ref() 就是state属性
// // computed() 就是getters
// // function() 就是actions
export const useCounterStore = defineStore('counter', () => {
    const count = ref(1)
    const double = computed(() => {
        return count.value * 6
    })
    function increment() {
        count.value++
    }
    return {count, double, increment}
})

// 在正常页面应用store
import { storeToRefs } from 'pinia';
import { useCounterStore } from '@/store/index'  // 引入store内容
const store = useCounterStore()
// `count`是响应式refs
// 这也将由插件添加的属性创建refs
// 同时会跳过任何action或非响应式(非ref响应式)属性
const { count, double } = storeToRefs(store)
function rest() {
    store.$reset()  // 重置 state
}
function changeStateValue() {
    store.$patch((state: any) => {
        state.userList.push({
            name: 'hahaha',
            age: 20
        })
        state.count = 100
    })
}
// $subscribe()方法侦听state及变化,相当于watch()
// { detached: true }作为第二个参数,当该组件被卸载时,它将自动删除
store.$subscribe((mutation: any, state: any) => {
    console.log("mutation, state=====",mutation, state.count)
}, {
    detached: true
})
<el-button class="header-nav-btn" @click="() => store.increment()">{{double}}注册{{count}}</el-button>
<el-button @click="rest">重置</el-button>
<el-button @click="changeStateValue">更改state</el-button>
<div>userList值:{{userList}}</div>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77

import legacy from “@vitejs/plugin-legacy”;兼容低版本浏览器

问题:
在使用vue3.2和vite2.0+开发一个移动端H5,测试时发现很多低版本的安卓手机浏览器出现白屏的现象,而ios机型基本上是好的,原因是很多低版本浏览器并不支持原生ESM导入的方式,下面给出解决方案:
兼容原生ESM的浏览器:

默认情况下,Vite 的目标浏览器是指能够 支持原生 ESM script 标签 和 支持原生 ESM 动态导入 的。作为参考,Vite 使用这个 browserslist 作为查询标准:

Chrome >=87
Firefox >=78
Safari >=13
Edge >=88
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

解决方案
通过插件 @vitejs/plugin-legacy 来自动生成传统浏览器的 chunk 及与其相对应 ES 语言特性方面的 polyfill。兼容版的 chunk 只会在不支持原生 ESM 的浏览器中进行按需加载。
安装: npm install @vitejs/plugin-legacy
在vite.config.js中

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import dns from 'dns'
dns.setDefaultResultOrder('verbatim')

import { resolve } from 'path'  // vite版本不支持commonJs所以不能写成const path = require('path')
import legacy from "@vitejs/plugin-legacy";  // 新增!!!!!!!!!!!!

export default defineConfig({
  // publicPath: './',
  plugins: [vue(),
  legacy({   // 新增!!!!!!!!!!!!
    targets: [
      "Android > 39",
      "Chrome >= 60",
      "Safari >= 10.1",
      "iOS >= 10.3",
      "Firefox >= 54",
      "Edge >= 15",
      "ie >= 11",
    ],
    additionalLegacyPolyfills: ["regenerator-runtime/runtime"],
  })
  ],
  define: {
    'process.env': {}
  },
  resolve: {
    alias: {
      '~': resolve(__dirname, './node_modules/'),
      '@': resolve(__dirname, './src'),
      'assets': resolve(__dirname, './src/assets'),
      'components': resolve(__dirname, './src/components'),
      'common': resolve(__dirname, './src/common'),
      'api': resolve(__dirname, './src/api'),
      'views': resolve(__dirname, './src/views'),
      'styles': resolve(__dirname, './src/styles'),
      'store': resolve(__dirname, './src/store'),
    }
  },
  server: {
    proxy: {
      '/columns': {
        target: 'http://apis.sss.com/api/',
        changeOrigin: true
      },
    },
  },
})

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

axios

注:该内容还未对api进行封装,后续再来补充

npm install axios

// 在src下创建api文件夹,创建两个子文件index.ts和http.ts
// index.ts
import { http } from './http'
import { ref } from 'vue'

export const dataLoading = ref(true)
export const getData = async () => {
  const errorMsg = ref('')
  const result = ref([])
  dataLoading.value = true
  await http
    .get('/xxxxx/getData') // 待访问的服务接口
    .then((res) => {
      dataLoading.value = false
      result.value = res.data
      errorMsg.value = ''
    })
    .catch((err) => {
      dataLoading.value = false
      result.value = []
      errorMsg.value = err.message
    })
  return {
    result,
    errorMsg
  }
}

// https.ts
import axios from 'axios'

const env = process.env.NODE_ENV // 此处调用process会报错,在后配置项解决
const apis = {
  production: 'http://localhost:8480',
  staging: 'http://localhost:8480',
  development: 'http://localhost:8480'
}

const vueAPI = apis[env as keyof typeof apis]
export const http = axios.create({
  baseURL: `${vueAPI}/api`,
  timeout: 10000,
  headers: {
    accept: 'application/json',
    'Content-Type': 'application/json'
  }
})

// 请求拦截
http.interceptors.request.use(
  function (request) {
    console.log('
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/318607
推荐阅读