关于Vue新搭档TypeScript快速入门实践

目录
  • 1. 使用官方脚手架构建
  • 2. 项目目录解析
  • 3. TypeScript极速入门
    • 3.1 基本类型和扩展类型
    • 3.2 泛型:Generics
    • 3.3 自定义类型:Interface vs Type alias
    • 3.4 实现与继承:implements vs extends
    • 3.5 声明文件与命名空间:declare 和 namespace
  • 跟其他 JS 库协同
    • 3.6 访问修饰符:private、public、protected
    • 3.7 可选参数 ( ?: )和非空断言操作符(!.)
  • 4. Vue组件的Ts写法
    • 4.1 vue-class-component
    • 4.2 添加全局工具
    • 4.3 Axios 使用与封装
  • 5. 编写一个组件

Vue官方从2.6.X版本开始就部分使用Ts重写了。

我个人对更严格类型限制没有积极的看法,毕竟各类转类型的骚写法写习惯了。

然鹅最近的一个项目中,是TypeScript+ Vue,毛计喇,学之...…真香!

注意此篇标题的“前”,本文旨在讲Ts混入框架的使用,不讲Class API

1. 使用官方脚手架构建

npm install -g @vue/cli# ORyarn global add @vue/cli

新的Vue CLI工具允许开发者 使用 TypeScript 集成环境 创建新项目。

只需运行vue create my-app。

然后,命令行会要求选择预设。使用箭头键选择Manually select features。

接下来,只需确保选择了TypeScript和Babel选项,如下图:

完成此操作后,它会询问你是否要使用class-style component syntax。

然后配置其余设置,使其看起来如下图所示。

Vue CLI工具现在将安装所有依赖项并设置项目。

接下来就跑项目

2. 项目目录解析

通过tree指令查看目录结构后可发现其结构和正常构建的大有不同。

这里主要关注shims-tsx.d.ts和 shims-vue.d.ts两个文件

两句话概括:

  • shims-tsx.d.ts允许你以.tsx结尾的文件,在Vue项目中编写jsx代码
  • shims-vue.d.ts 主要用于 TypeScript 识别.vue 文件,Ts默认并不支持导入 vue 文件,这个文件告诉ts 导入.vue 文件都按VueConstructor<Vue>处理。

此时我们打开亲切的src/components/HelloWorld.vue,将会发现写法已大有不同

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <!-- 省略 -->
  </div>
</template>

<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';

@Component
export default class HelloWorld extends Vue {
  @Prop() private msg!: string;
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped></style>

至此,准备开启新的篇章 TypeScript极速入门 和 vue-property-decorator

3. TypeScript极速入门

3.1 基本类型和扩展类型

Typescript与Javascript共享相同的基本类型,但有一些额外的类型。

  • 元组 Tuple
  • 枚举 enum
  • Any 与Void

1. 基本类型合集

// 数字,二、八、十六进制都支持
let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;

// 字符串,单双引都行
let name: string = "bob";
let sentence: string = `Hello, my name is ${ name }.

// 数组,第二种方式是使用数组泛型,Array<元素类型>:
let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3];

let u: undefined = undefined;
let n: null = null;

2. 特殊类型

1. 元组 Tuple

想象 元组 作为有组织的数组,你需要以正确的顺序预定义数据类型。

const messyArray = [' something', 2, true, undefined, null];
const tuple: [number, string, string] = [24, "Indrek" , "Lasn"]

如果不遵循 为元组 预设排序的索引规则,那么Typescript会警告。

​ (tuple第一项应为number类型)

2. 枚举 enum

enum类型是对JavaScript标准数据类型的一个补充。 像C#等其它语言一样,使用枚举类型可以为一组数值赋予友好的名字。

// 默认情况从0开始为元素编号,也可手动为1开始
enum Color {Red = 1, Green = 2, Blue = 4}
let c: Color = Color.Green;

let colorName: string = Color[2];
console.log(colorName);  // 输出'Green'因为上面代码里它的值是2

另一个很好的例子是使用枚举来存储应用程序状态。

3. Void

在Typescript中,你必须在函数中定义返回类型。像这样:

若没有返回值,则会报错:

我们可以将其返回值定义为void:

此时将无法 return

4. Any

emmm...就是什么类型都行,当你无法确认在处理什么类型时可以用这个。

但要慎重使用,用多了就失去使用Ts的意义。

let person: any = "前端劝退师"
person = 25
person = true

主要应用场景有:

  • 接入第三方库
  • Ts菜逼前期都用

5. Never

用很粗浅的话来描述就是:"Never是你永远得不到的爸爸。"

具体的行为是:

throw new Error(message)
return error("Something failed")
while (true) {} // 存在无法达到的终点

3. 类型断言

简略的定义是:可以用来手动指定一个值的类型。

有两种写法,尖括号和as:

let someValue: any = "this is a string";

let strLength: number = (<string>someValue).length;
let strLength: number = (someValue as string).length;

使用例子有:

当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法:

function getLength(something: string | number): number {
    return something.length;
}

// index.ts(2,22): error TS2339: Property 'length' does not exist on type 'string | number'.
//   Property 'length' does not exist on type 'number'.

如果你访问长度将会报错,而有时候,我们确实需要在还不确定类型的时候就访问其中一个类型的属性或方法,此时需要断言才不会报错:

function getLength(something: string | number): number {
    if ((<string>something).length) {
        return (<string>something).length;
    } else {
        return something.toString().length;
    }
}

3.2 泛型:Generics

软件工程的一个主要部分就是构建组件,构建的组件不仅需要具有明确的定义和统一的接口,同时也需要组件可复用。支持现有的数据类型和将来添加的数据类型的组件为大型软件系统的开发过程提供很好的灵活性。

在C#和Java中,可以使用"泛型"来创建可复用的组件,并且组件可支持多种数据类型。这样便可以让用户根据自己的数据类型来使用组件。

1. 泛型方法

在TypeScript里,声明泛型方法有以下两种方式:

function gen_func1<T>(arg: T): T {
    return arg;
}
// 或者
let gen_func2: <T>(arg: T) => T = function (arg) {
    return arg;
}

调用方式也有两种:

gen_func1<string>('Hello world');
gen_func2('Hello world');
// 第二种调用方式可省略类型参数,因为编译器会根据传入参数来自动识别对应的类型。

2. 泛型与Any

Ts 的特殊类型 Any 在具体使用时,可以代替任意类型,咋一看两者好像没啥区别,其实不然:

// 方法一:带有any参数的方法
function any_func(arg: any): any {
    console.log(arg.length);
		return arg;
}

// 方法二:Array泛型方法
function array_func<T>(arg: Array<T>): Array<T> {
	  console.log(arg.length);
		return arg;
}
  • 方法一,打印了arg参数的length属性。因为any可以代替任意类型,所以该方法在传入参数不是数组或者带有length属性对象时,会抛出异常。
  • 方法二,定义了参数类型是Array的泛型类型,肯定会有length属性,所以不会抛出异常。

3. 泛型类型

泛型接口:

interface Generics_interface<T> {
    (arg: T): T;
}

function func_demo<T>(arg: T): T {
    return arg;
}

let func1: Generics_interface<number> = func_demo;
func1(123);     // 正确类型的实际参数
func1('123');   // 错误类型的实际参数

3.3 自定义类型:Interface vs Type alias

  • Interface,国内翻译成接口。
  • Type alias,类型别名。

1. 相同点

都可以用来描述一个对象或函数:

interface User {
  name: string
  age: number
}

type User = {
  name: string
  age: number
};

interface SetUser {
  (name: string, age: number): void;
}
type SetUser = (name: string, age: number): void;

都允许拓展(extends):

interface 和 type 都可以拓展,并且两者并不是相互独立的,也就是说interface可以 extends type, type 也可以 extends interface 。 虽然效果差不多,但是两者语法不同

interface extends interface

interface Name { 
  name: string; 
}
interface User extends Name { 
  age: number; 
}

type extends type

type Name = { 
  name: string; 
}
type User = Name & { age: number  };

interface extends type

type Name = { 
  name: string; 
}
interface User extends Name { 
  age: number; 
}

type extends interface

interface Name { 
  name: string; 
}
type User = Name & { 
  age: number; 
}

2. 不同点

type 可以而 interface 不行

type 可以声明基本类型别名,联合类型,元组等类型

// 基本类型别名
type Name = string

// 联合类型
interface Dog {
    wong();
}
interface Cat {
    miao();
}

type Pet = Dog | Cat

// 具体定义数组每个位置的类型
type PetList = [Dog, Pet]

type 语句中还可以使用 typeof获取实例的 类型进行赋值

// 当你想获取一个变量的类型时,使用 typeof
let div = document.createElement('div');
type B = typeof div

其他骚操作

type StringOrNumber = string | number;
type Text = string | { text: string };
type NameLookup = Dictionary<string, Person>;
type Callback<T> = (data: T) => void;
type Pair<T> = [T, T];
type Coordinates = Pair<number>;
type Tree<T> = T | { left: Tree<T>, right: Tree<T> };

interface可以而 type不行

interface 能够声明合并

interface User {
  name: string
  age: number
}

interface User {
  sex: string
}

/*
User 接口为 {
  name: string
  age: number
  sex: string
}
*/

interface 有可选属性和只读属性

可选属性

接口里的属性不全都是必需的。 有些是只在某些条件下存在,或者根本不存在。 例如给函数传入的参数对象中只有部分属性赋值了。带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个?符号。如下所示

interface Person {
  name: string;
  age?: number;
  gender?: number;
}

只读属性

顾名思义就是这个属性是不可写的,对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用 readonly来指定只读属性,如下所示:

interface User {
    readonly loginName: string;
    password: string;
}

上面的例子说明,当完成User对象的初始化后loginName就不可以修改了。

3.4 实现与继承:implements vs extends

  • extends很明显就是ES6里面的类继承,那么implement又是做什么的呢?它和extends有什么不同?
  • implement,实现。与C#或Java里接口的基本作用一样,TypeScript也能够用它来明确的强制一个类去符合某种契约

implement基本用法

interface IDeveloper {
   name: string;
   age?: number;
}
// OK
class dev implements IDeveloper {
    name = 'Alex';
    age = 20;
}
// OK
class dev2 implements IDeveloper {
    name = 'Alex';
}
// Error
class dev3 implements IDeveloper {
    name = 'Alex';
    age = '9';
}

而extends是继承父类,两者其实可以混着用:

 class A extends B implements C,D,E

搭配 interface和type的用法有:

3.5 声明文件与命名空间:declare 和 namespace

前面我们讲到Vue项目中的shims-tsx.d.ts和shims-vue.d.ts,其初始内容是这样的:

// shims-tsx.d.ts
import Vue, { VNode } from 'vue';

declare global {
  namespace JSX {
    // tslint:disable no-empty-interface
    interface Element extends VNode {}
    // tslint:disable no-empty-interface
    interface ElementClass extends Vue {}
    interface IntrinsicElements {
      [elem: string]: any;
    }
  }
}

// shims-vue.d.ts
declare module '*.vue' {
  import Vue from 'vue';
  export default Vue;
}

declare:当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能。

这里列举出几个常用的:

declare var 声明全局变量
declare function 声明全局方法
declare class 声明全局类
declare enum 声明全局枚举类型
declare global 扩展全局变量
declare module 扩展模块
  • namespace:“内部模块”现在称做“命名空间”
  • module X { 相当于现在推荐的写法 namespace X {)

跟其他 JS 库协同

类似模块,同样也可以通过为其他 JS 库使用了命名空间的库创建 .d.ts 文件的声明文件,如为 D3 JS 库,可以创建这样的声明文件:

declare namespace D3{
    export interface Selectors { ... }
}
declare var d3: D3.Base;

所以上述两个文件:

  • shims-tsx.d.ts, 在全局变量 global中批量命名了数个内部模块。
  • shims-vue.d.ts,意思是告诉 TypeScript *.vue 后缀的文件可以交给 vue 模块来处理。

3.6 访问修饰符:private、public、protected

其实很好理解:

  • 默认为public
  • 当成员被标记为private时,它就不能在声明它的类的外部访问,比如:
class Animal {
    private name: string;
    constructor(theName: string) {
        this.name = theName;
    }
}
 
let a = new Animal('Cat').name; //错误,‘name'是私有的

protected和private类似,但是,protected成员在派生类中可以访问

class Animal {
    protected name: string;
    constructor(theName: string) {
        this.name = theName;
    }
}
 
class Rhino extends Animal {
    constructor() {
        super('Rhino');
   }         
   getName() {
       console.log(this.name) //此处的name就是Animal类中的name
   }
} 

3.7 可选参数 ( ?: )和非空断言操作符(!.)

可选参数

function buildName(firstName: string, lastName?: string) {
    return firstName + ' ' + lastName
}
 
// 错误演示
buildName("firstName", "lastName", "lastName")
// 正确演示
buildName("firstName")
// 正确演示
buildName("firstName", "lastName")

非空断言操作符:

能确定变量值一定不为空时使用。

与可选参数 不同的是,非空断言操作符不会防止出现 null 或 undefined。

let s = e!.name;  // 断言e是非空并访问name属性

拓展

  • 1. 属性或参数中使用 ?:表示该属性或参数为可选项
  • 2. 属性或参数中使用 !:表示强制解析(告诉typescript编译器,这里一定有值),常用于vue-decorator中的@Prop
  • 3. 变量后使用 !:表示类型推断排除null、undefined

4. Vue组件的Ts写法

从 vue2.5 之后,vue 对 ts 有更好的支持。根据官方文档,vue 结合 typescript ,有两种书写方式:

Vue.extend

  import Vue from 'vue'
 
  const Component = Vue.extend({
      // type inference enabled
  })

vue-class-component

import { Component, Vue, Prop } from 'vue-property-decorator'
 
@Component
export default class Test extends Vue {
  @Prop({ type: Object })
  private test: { value: string }
}

理想情况下,Vue.extend 的书写方式,是学习成本最低的。在现有写法的基础上,几乎 0 成本的迁移。

但是Vue.extend模式,需要与mixins 结合使用。在 mixin 中定义的方法,不会被 typescript 识别到,这就意味着会出现丢失代码提示、类型检查、编译报错等问题。

菜鸟才做选择,大佬都挑最好的。直接讲第二种吧:

4.1 vue-class-component

我们回到src/components/HelloWorld.vue

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <!-- 省略 -->
  </div>
</template>

<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';

@Component
export default class HelloWorld extends Vue {
  @Prop() private msg!: string;
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped></style>

有写过python的同学应该会发现似曾相识:

vue-property-decorator这个官方支持的库里,提供了函数 **装饰器(修饰符)**语法

1. 函数修饰符 @

“@”,与其说是修饰函数倒不如说是引用、调用它修饰的函数。

或者用句大白话描述:@: "下面的被我包围了。"

举个栗子,下面的一段代码,里面两个函数,没有被调用,也会有输出结果:

test(f){
    console.log("before ...");
    f()
		console.log("after ...");
 }

@test
func(){
	console.log("func was called");
}

直接运行,输出结果:

before ...

func was called

after ...

上面代码可以看出来:

  • 只定义了两个函数:test和func,没有调用它们。
  • 如果没有 @test,运行应该是没有任何输出的。

但是,解释器读到函数修饰符“@”的时候,后面步骤会是这样:

  • 去调用test函数,test函数的入口参数就是那个叫“func”的函数;
  • test函数被执行,入口参数的(也就是func函数)会被调用(执行);

换言之,修饰符带的那个函数的入口参数,就是下面的那个整个的函数。有点儿类似JavaScript里面的 function a (function () { ... });

2. vue-property-decorator和vuex-class提供的装饰器

vue-property-decorator的装饰器:

  • @Prop
  • @PropSync
  • @Provide
  • @Model
  • @Watch
  • @Inject
  • @Provide
  • @Emit
  • @Component (provided by vue-class-component)
  • Mixins (the helper function named mixins provided by vue-class-component)

vuex-class的装饰器:

  • @State
  • @Getter
  • @Action
  • @Mutation

我们拿原始Vue组件模版来看:

import {componentA,componentB} from '@/components';
 
export default {
    components: { componentA, componentB},
    props: {
    propA: { type: Number },
    propB: { default: 'default value' },
    propC: { type: [String, Boolean] },
  }
  // 组件数据
  data () {
    return {
      message: 'Hello'
    }
  },
  // 计算属性
  computed: {
    reversedMessage () {
      return this.message.split('').reverse().join('')
    }
    // Vuex数据
    step() {
        return this.$store.state.count
    }
  },
  methods: {
    changeMessage () {
      this.message = "Good bye"
    },
    getName() {
        let name = this.$store.getters['person/name']
        return name
    }
  },
  // 生命周期
  created () { },
  mounted () { },
  updated () { },
  destroyed () { }
}

以上模版替换成修饰符写法则是:

import { Component, Vue, Prop } from 'vue-property-decorator';
import { State, Getter } from 'vuex-class';
import { count, name } from '@/person'
import { componentA, componentB } from '@/components';
 
@Component({
    components:{ componentA, componentB},
})
export default class HelloWorld extends Vue{
    @Prop(Number) readonly propA!: number | undefined
    @Prop({ default: 'default value' }) readonly propB!: string
    @Prop([String, Boolean]) readonly propC!: string | boolean | undefined
  
  // 原data
  message = 'Hello'
  
  // 计算属性
    private get reversedMessage (): string[] {
      return this.message.split('').reverse().join('')
  }
  // Vuex 数据
  @State((state: IRootState) => state . booking. currentStep) step!: number
    @Getter( 'person/name') name!: name
  
  // method
  public changeMessage (): void {
    this.message = 'Good bye'
  },
  public getName(): string {
    let storeName = name
    return storeName
  }
    // 生命周期
  private created ():void { },
  private mounted ():void { },
  private updated ():void { },
  private destroyed ():void { }
}

正如你所看到的,我们在生命周期 列表那都添加private XXXX方法,因为这不应该公开给其他组件。

而不对method做私有约束的原因是,可能会用到@Emit来向父组件传递信息。

4.2 添加全局工具

引入全局模块,需要改main.ts:

import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
 
Vue.config.productionTip = false;
 
new Vue({
  router,
  store,
  render: (h) => h(App),
}).$mount('#app');

npm i VueI18n

import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
// 新模块
import i18n from './i18n';
 
Vue.config.productionTip = false;
 
new Vue({
    router, 
    store, 
    i18n, // 新模块
    render: (h) => h(App),
}).$mount('#app');

但仅仅这样,还不够。你需要动src/vue-shim.d.ts:

// 声明全局方法
declare module 'vue/types/vue' {
  interface Vue {
        readonly $i18n: VueI18Next;
        $t: TranslationFunction;
    }
}

之后使用this.$i18n()的话就不会报错了。

4.3 Axios 使用与封装

Axios的封装千人千面

如果只是想简单在Ts里体验使用Axios,可以安装vue-axios 简单使用Axios

$ npm i axios vue-axios

main.ts添加:

import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
 
Vue.use(VueAxios, axios)

然后在组件内使用:

Vue.axios.get(api).then((response) => {
  console.log(response.data)
})
 
this.axios.get(api).then((response) => {
  console.log(response.data)
})
 
this.$http.get(api).then((response) => {
  console.log(response.data)
})

1. 新建文件request.ts

文件目录:

-api
    - main.ts   // 实际调用
-utils
    - request.ts  // 接口封装

2. request.ts文件解析

import * as axios from 'axios';
import store from '@/store';
// 这里可根据具体使用的UI组件库进行替换
import { Toast } from 'vant';
import { AxiosResponse, AxiosRequestConfig } from 'axios';
 
 /* baseURL 按实际项目来定义 */
const baseURL = process.env.VUE_APP_URL;
 
 /* 创建axios实例 */
const service = axios.default.create({
    baseURL,
    timeout: 0, // 请求超时时间
    maxContentLength: 4000,
});
 
service.interceptors.request.use((config: AxiosRequestConfig) => {
    return config;
}, (error: any) => {
    Promise.reject(error);
});
 
service.interceptors.response.use(
    (response: AxiosResponse) => {
        if (response.status !== 200) {
            Toast.fail('请求错误!');
        } else {
            return response.data;
        }
    },
    (error: any) => {
        return Promise.reject(error);
    });
    
export default service;

为了方便,我们还需要定义一套固定的 axios 返回的格式,新建ajax.ts:

export interface AjaxResponse {
    code: number;
    data: any;
    message: string;
}

3. main.ts接口调用:

// api/main.ts
import request from '../utils/request';
 
// get
export function getSomeThings(params:any) {
    return request({
        url: '/api/getSomethings',
    });
}
 
// post
export function postSomeThings(params:any) {
    return request({
        url: '/api/postSomethings',
        methods: 'post',
        data: params
    });
}

5. 编写一个组件

为了减少时间,我们来替换掉src/components/HelloWorld.vue,做一个博客帖子组件:

<template>
    <div class="blogpost">
        <h2>{{ post.title }}</h2>
        <p>{{ post.body }}</p>
        <p class="meta">Written by {{ post.author }} on {{ date }}</p>
    </div>
</template>
 
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
 
// 在这里对数据进行类型约束
export interface Post {
    title: string;
    body: string;
    author: string;
    datePosted: Date;
}
 
@Component
export default class HelloWorld extends Vue {
    @Prop() private post!: Post;
 
    get date() {
        return `${this.post.datePosted.getDate()}/${this.post.datePosted.getMonth()}/${this.post.datePosted.getFullYear()}`;
    }
}
</script>
 
<style scoped>
h2 {
  text-decoration: underline;
}
p.meta {
  font-style: italic;
}
</style>

然后在Home.vue中使用:

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png">
       <HelloWorld v-for="blogPost in blogPosts" :post="blogPost" :key="blogPost.title" />
  </div>
</template>
 
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import HelloWorld, { Post } from '@/components/HelloWorld.vue'; // @ is an alias to /src
 
@Component({
  components: {
    HelloWorld,
  },
})
export default class Home extends Vue {
    private blogPosts: Post[] = [
        {
          title: 'My first blogpost ever!',
          body: 'Lorem ipsum dolor sit amet.',
          author: 'Elke',
          datePosted: new Date(2019, 1, 18),
        },
        {
          title: 'Look I am blogging!',
          body: 'Hurray for me, this is my second post!',
          author: 'Elke',
          datePosted: new Date(2019, 1, 19),
        },
        {
          title: 'Another one?!',
          body: 'Another one!',
          author: 'Elke',
          datePosted: new Date(2019, 1, 20),
        },
      ];
}
</script>

这时候运行项目:

这就是简单的父子组件

而关于Class API撤销,其实还是挺舒服的。 用class 来编写 Vue组件确实太奇怪了。 (所以本篇Ts入门压根没写Class API)

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • TypeScript Pinia实战分享(Vuex和Pinia对比梳理总结)

    目录 简介 安装 创建pinia并传递给vue实例 创建store state getters actions 在vue组件使用 获取state 获取getters 修改state 数据持久化 总结 简介 今天我们再来实战下官方推荐的新的vue状态管理工具Pinia. pinia 由 vue 团队中成员所开发的,因此也被认为是下一代的 vuex,即 vuex5.x,在 vue3 的项目中使用也是备受推崇.所以 pinia 的学习迫在眉睫. 下面我们正式开始pinia的学习吧. 安装 yarn a

  • Vue3 TypeScript 实现useRequest详情

    目录 前言: 效果展示 Axios interface axios的简单封装 useRequest 如何使用 添加泛型支持 queryKey的问题 完整代码 结语 前言: 自从 Vue3 更新之后,算是投入了比较大的精力写了一个较为完善的Vue3.2 + Vite2 + Pinia + Naive UI的B端模版,在做到网络请求这一块的时候,最初使用的是VueRequest的useRequest,但是因为VueRequest的useRequest的cancel关闭请求并不是真正的关闭,对我个人来

  • Vue3+TypeScript+Vite使用require动态引入图片等静态资源

    问题:Vue3+TypeScript+Vite的项目中如何使用require动态引入类似于图片等静态资源! 描述:今天在开发项目时(项目框架为Vue3+TypeScript+Vite)需要 动态引入静态资源,也就是img标签的src属性值为动态获取,按照以往的做法直接是require引入即可,如下代码: <img class="demo" :src="require(`../../../assets/image/${item.img}`)" /> 写上后

  • TypeScript在Vuex4中使用TS实战分享

    目录 简介 createStore GetterTree MutationTree ActionTree ModuleTree 实战 整体目录结构 首先定义根state的类型 在创建store的时候将根state的类型传递进去. 并且需要导出key 在Vue实例使用store的时候将key一并传入. 在vue组件使用 自定义useStore()方法 modules的使用 总结 简介 虽然TypeScript知识点学了很多,但是在实际项目中很多小伙伴还并不知道怎么用,今天笔者结合Vuex4来使用T

  • 关于Vue3&TypeScript的踩坑汇总

    目录 安装环境 创建项目 1.配置路由 2.安装VueX 3.安装国际化 4.vite.config.ts常用配置 5.找不到模块声明declare 6.path模块找不到 打包问题 Vite打包后显示跨域 Vue3.0的新语法糖-script setup Css中使用JS代码 Ref.toRef.toRefs.Reactive总结 v-model PropType toRefs解构.Reactive Ref.toRef.toRefs.Reactive readonly isProxy toRa

  • vue3+Pinia+TypeScript 实现封装轮播图组件

    目录 为什么封装? 静态结构 后面再进行更改 请求数据都存放在pinia里面 类型检测 页面级组件 全局组件 为什么封装? 迎合es6模块化开发思想 注册为全局组件,可以更好地复用,需要用到的地方,直接使用标签即可 静态结构 后面再进行更改 <script lang="ts" setup name="XtxCarousel"> defineProps() </script> <template> <div class=&qu

  • Vue新搭档TypeScript快速入门实践记录

    目录 1. 使用官方脚手架构建 2. 项目目录解析 3. TypeScript极速入门 3.1 基本类型和扩展类型 3.2 泛型:Generics 3.3 自定义类型:Interface vs Type alias 3.4 实现与继承:implements vs extends 3.5 声明文件与命名空间:declare 和 namespace 3.6 访问修饰符:private.public.protected 3.7 可选参数 ( ?: )和非空断言操作符(!.) 4. Vue组件的Ts写法

  • 关于Vue新搭档TypeScript快速入门实践

    目录 1. 使用官方脚手架构建 2. 项目目录解析 3. TypeScript极速入门 3.1 基本类型和扩展类型 3.2 泛型:Generics 3.3 自定义类型:Interface vs Type alias 3.4 实现与继承:implements vs extends 3.5 声明文件与命名空间:declare 和 namespace 跟其他 JS 库协同 3.6 访问修饰符:private.public.protected 3.7 可选参数 ( ?: )和非空断言操作符(!.) 4.

  • Vue.js 60分钟快速入门教程

    vuejs是当下很火的一个JavaScript MVVM库,它是以数据驱动和组件化的思想构建的.相比于Angular.js,Vue.js提供了更加简洁.更易于理解的API,使得我们能够快速地上手并使用Vue.js. 如果你之前已经习惯了用jQuery操作DOM,学习Vue.js时请先抛开手动操作DOM的思维,因为Vue.js是数据驱动的,你无需手动操作DOM.它通过一些特殊的HTML语法,将DOM和数据绑定起来.一旦你创建了绑定,DOM将和数据保持同步,每当变更了数据,DOM也会相应地更新. 当

  • 详解Vue3.0 前的 TypeScript 最佳入门实践

    前言 我个人对更严格类型限制没有积极的看法,毕竟各类转类型的骚写法写习惯了. 然鹅最近的一个项目中,是 TypeScript + Vue ,毛计喇,学之...-真香! 注意此篇标题的"前",本文旨在讲Ts混入框架的使用,不讲Class API 1. 使用官方脚手架构建 npm install -g @vue/cli # OR yarn global add @vue/cli 新的 Vue CLI 工具允许开发者 使用 TypeScript 集成环境 创建新项目. 只需运行 vue cr

  • Vue.js快速入门教程

    像AngularJS这种前端框架可以让我们非常方便地开发出强大的单页应用,然而有时候Angular这种大型框架对于我们的项目来说过于庞大,很多功能不一定会用到.这时候我们就需要评估一下使用它的必要性了.如果我们仅仅需要在一个简单的网页里添加屈指可数的几个功能,那么用Angular就太麻烦了,必要的安装.配置.编写路由和设计控制器等等工作显得过于繁琐. 这时候我们需要一个更加轻量级的解决方案.Vue.js就是一个不错的选择.Vue.js是一个专注于视图模型(ViewModal)的框架.视图模型是U

  • vue 虚拟DOM快速入门

    虚拟 DOM 什么是虚拟 dom dom 是文档对象模型,以节点树的形式来表现文档. 虚拟 dom 不是真正意义上的 dom.而是一个 javascript 对象. 正常的 dom 节点在 html 中是这样表示: <div class='testId'> <p>你好</p> <p>欢迎光临</p> </div> 而在虚拟 dom 中大概是这样: { tag: 'div', attributes:{ class: ['testId']

  • Vue.js快速入门实例教程

    什么是vue vue是法语中视图的意思,Vue.js是一个轻巧.高性能.可组件化的MVVM库,同时拥有非常容易上手的API. 一.基本结构 index.html代码: <script src="../vue.js"></script> <div id="app"> {{ message }} </div> <script src="app.js"></script> <

  • Vue + OpenLayers 快速入门学习教程

    Openlayers 是一个模块化.高性能并且功能丰富的WebGIS客户端的JavaScript包,用于显示地图及空间数据,并与之进行交互,具有灵活的扩展机制. 简单来说,使用 Openlayers(后面简称ol) 可以很灵活自由的做出各种地图和空间数据的展示.而且这个框架是完全免费和开源的. 前言 本文记录 Vue 使用 OpenLayers 入门,使用 OpenLayers 创建地图组件,分别使用 OpenLayers 提供的地图和本地图片做为地图. Overview OpenLayers

  • vue项目中使用ts(typescript)入门教程

    目录 1.引入Typescript 2.配置文件webpack配置 3.让项目识别.ts 4.vue组件的编写 data()中定义数据 props传值 完整代码案例 最近项目需要将原vue项目结合ts的使用进行改造,这个后面应该是中大型项目的发展趋势,看到一篇不错的入门教程,结合它并进行了一点拓展记录之.本文从安装到vue组件编写进行了说明,适合入门. 1.引入Typescript npm install vue-class-component vue-property-decorator --

  • Vue新的状态管理库Pinia入门教程

    目录 前沿 使用教程 1.安装 2.vue中引入 3.基本使用 4.也可以像vuex一样使用 为什么最近Pinia会火起来呢,主要在于Vue3推出来的时候,Vuex对于Vue3的组合式Api支持的不是特别好,也就是在这个时候Pinia出现了. 前沿 Vue官方推荐的状态管理库是Vuex,那为什么最近Pinia会火起来呢,主要在于Vue3推出来的时候,Vuex对于Vue3的组合式Api支持的不是特别好,也就是在这个时候Pinia出现了,最重要的是,Pinia不但支持Vue3,同时还支持Vue2,这

随机推荐