likes
comments
collection
share

Vue3+TS知识

作者站长头像
站长
· 阅读数 49

本篇内容全部采用TypeScript+组合式API+setup语法糖的规范形式

Vue3==兼容==>Vue2--基本不用2的写法

目前进程 --014

创建Vue3工程

vite和webpack是等价的,两个不同的构建工具,vitewebpack的区别在于,vite需要用到什么,也就是取决于你看什么,就加载什么;而webpack则是全部加载了再给你展示,速度相对就慢很多,类似懒加载

基于Vue-cli创建(脚手架-webpack)

基于vite创建 https://vitejs.cn/

1.创建指令(需要安装node.js--获得npm环境) 
$ pnpm create vue@latest

2.输入项目名称
3.选择其他配置完成配置

Vue3+TS知识

目录介绍

src内存放源代码在src中,必不可少的文件是App.vuemain.ts

Vue3+TS知识

main.ts文件

//引入createApp用于创建应用
import {createApp} from 'vue'//createApp创建应用,类似造花盆
//引入App根组件
import App from './App.vue'//App组件,类似花盆里的根,后续其他的vue组件类似叶子树枝,都安装在App根上

createApp(App).mount('#app')//将花插在花盆里,也就是将APP传入;mount是挂在到一个名为#app的容器中-->在index.html中

.Vue文件的组成

<template>
  <!-- html -->
</template>

<script lang="ts">//lang="ts"作用是指定该 <script> 部分使用 TypeScriptTS)语言编写
//JS或TS
</script>

<style>
/* 样式 */
</style>

总结

Vite项目中,index.html是项目的入口文件,在项目的最外层

加载index.html后,Vite解析<script type="module" src="xxx">指向的JavaScript

Vue3中是通过createApp函数创建一个应用实例

Vue3中使用vue2语法实现简单效果demo

在Vue2中使用OptionsAPI选项式 -->name;data;methods等都是选项

Person.vue

<template>
  <div class="person">
    <h2>姓名{{ name }}</h2>
    <h2>年龄{{ age }}</h2>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年龄</button>
    <button @click="showTel">查看联系方式</button>
  </div>
</template>

<script lang="ts">
export default {
  name: "Person",
  data() {
    return {
      name: "张三",
      age: 18,
      tel: "12324124",
    };
  },
  methods: {
    changeName() {
      this.name = "zhang-3";
    },
    changeAge() {
      this.age += 1;
    },
    showTel() {
      alert(this.tel);
    },
  },
};
</script>

<style>
.person {
  background-color: skyblue;
}
</style>

App.vue

<template>
  <div class="app">
    <h1>你好啊!</h1>
    <Person></Person>
  </div>
</template>

<script lang="ts">
import Person from "./components/Person.vue";
export default {
  name: "App", //组件名
  components: { Person }, //组成组件
};
</script>

<style>
.app {
  background-color: red;
}
</style>

vue3跟vue2的区别

<template>
  <!-- vue3中可以使用多个根标签,在vue2中不能这样玩 -->
  <Person></Person>
  <Person></Person>
  <Person></Person>
  <Person></Person>
</template>

<script lang="ts">
import Person from "./components/Person.vue";
export default {
  name: "App", //组件名
  components: { Person }, //组成组件
};
</script>

<style></style>

Vue3核心语法

Setup

Setup

setupVue3中一个新的配置项,值是一个函数,它是Componsition API表演的舞台,组件中所用到的:数据、方法、计算属性、监视......等等,均在setup中。

特点图下:

setup函数返回的对象中的内容,可直接在模板中使用

setup中访问thisundefined

setup函数会在beforeCreate之前调用,它是"领先"所有钩子执行的

注意在setup中,不能使用this,值为undefined

注意在vue3中,setup执行的时间比beforeCreate还要早

<script lang="ts">
export default {
  name: "Person",
  data() {//vue2中的选项式api写法,配置数据
    return {
      name: "张三",
      age: 18,
      tel: "12324124",
    };
  },
  methods: {//vue2中的选项式api写法,也叫配置式写法
    changeName() {
      this.name = "zhang-3";
    },
    changeAge() {
      this.age += 1;
    },
    showTel() {
      alert(this.tel);
    },
  },
  setup(){//vue3的写法

  }
};
</script>

在vue3中通过setup定义数据也就是组合式api写法

<template>
  <div class="person">
    <h2>姓名:{{ a }}</h2>
    <h2>年龄:{{ b }}</h2>
  </div>
</template>

<script lang="ts">
export default {
  name: "Person",
  setup() {
    //vue3的写法
    //数据--直接定义了在template中并不能直接使用,需要return一个返回值
    let name = "张三"; //这样定义的name不是响应式的
    let age = 18; //这样定义的age不是响应式的
    let tel = "138111111"; //这样定义的tel不是响应式的

    // return {key:value}
    //在template中使用key把值引用
    return { a: name, b: age };
  },
};
</script>

当然也可以简写定义引入

<template>
  <div class="person">
    <h2>简写姓名:{{ name }}</h2>
    <h2>简写年龄:{{ age }}</h2>
  </div>
</template>

<script lang="ts">
export default {
  name: "Person",
  setup() {
    //vue3的写法
    let name = "张三"; //这样定义的name不是响应式的
    let age = 18; //这样定义的age不是响应式的
    let tel = "138111111"; //这样定义的tel不是响应式的

    //return { name: name, age: age };这样可以直接简写
    return { name, age };
  },
};
</script>

数据/方法

<template>
  <div class="person">
    <h2>简写姓名:{{ name }}</h2>
    <h2>简写年龄:{{ age }}</h2>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年龄</button>
    <button @click="showTel">查看联系方式</button>
  </div>
</template>

<script lang="ts">
export default {
  name: "Person",
  setup() {
    //vue3的写法 
    //数据
    let name = "张三"; //这样定义的name不是响应式的
    let age = 18; 
    let tel = "138111111"; 

    //方法--定义的方法也需要return出去
    //注意:setup函数中的this是undefined,在vue3中已经开始弱化this
    function changeName() {//这里调用方法已经更改值,
      //但是因为上面定义的数据不是响应式,所以看不出变化
      name = "zhang-san";
    }
    function changeAge() {
      age += 1;
    }
    function showTel() {
      alert(tel);
    }

    return { name, age, changeName, changeAge, showTel };
  },
};
</script>

setup的返回值

setup的返回值不止可以是对象,也可以是一个函数

<script lang="ts">
export default {
  name: "Person",
  setup() {
    // return { name, age, changeName, changeAge, showTel };
    return function () {
      //这样返回函数,在页面直接渲染haha
      return "haha"; //连template怎么定义的都不重要了
    };

    //因为setup中不能用this,所以可以用箭头函数简写
    return () => {
      return "haha";
    };
    //一行代码当然还可以再简写
    return () => "haha";
  },
};
</script>

setup语法糖-setup简单的写法

在script标签上加上setup后,在script标签内写的东西就相当于在setup函数中写的东西了,而且还会自动return

<template>
  <div class="person">
    {{ a }}
  </div>
</template>

<script setup>
let a = 666;
</script>
<style>
.person {
  background-color: skyblue;
}
</style>

上面demo这样当然可以使用,但是对于组件的名字就无法控制了,组件的名字只能和文件名字一样,这个时候就可以再定义一个script(如果其中一个script加了lang='ts',那两个script都要加)

<template>
  <div class="person">
    {{ a }}
  </div>
</template>

<script lang="ts">
export default {
  name: "Person",
};
</script>

<script lang="ts" setup>
let a = 666;
</script>
<style>
.person {
  background-color: skyblue;
}
</style>

这样就可以定义组件的名字了,但是又出现了新的问题,写起来太复杂,想简单一点实现更改组件名字,就需要安装一个插件

首先

pnpm i vite-plugin-vue-setup-extend -D

然后

找到vite.config.ts引入并定义一下插件

...
import VueSetupExtend from 'vite-plugin-vue-setup-extend'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    ...
    VueSetupExtend()-------调用一下定义的VueSetupExtend
  ],
  resolve: {
    alias: {
      ...
    }
  }
})

最后

在组件中的script标签直接添加name='组件名字'属性即可

<script setup lang='ts' name='Peson123'>
</script>

ref-创建基本类型的响应式数据(也可以对象)

在vue2中,只要把数据丢到data中,那数据自然就是响应式的,自动做了数据代理和数据劫持

<template>
  <div class="person">
    {{ a }}
    <button @click="handleRef">修改ref值</button>
  </div>
</template>

<script lang="ts" setup name="Person">
import { ref } from "vue";
//ref()调用时可以看出,ref是个函数,而且要传入数据
//想让哪个数据是响应式的,就把哪个数据用ref包一下
//被ref()包过的数据会转换成RefImpl对象,真正的值在对象的value里(带下划线的不是我需要用的)
//而且需要注意,在template中不需要.value
//但是在js部分,只要使用ref,就需要.value
let a = ref(666);
function handleRef() {
  a.value++;
}
</script>
<style>
.person {
  background-color: skyblue;
}
</style>

reactive - 对象/数组类型的响应式数据

需要注意,reactive的响应式是深层次的,不管数据被嵌套几层,有多深入,都可以修改的到

<template>
  <div class="person">
    <h2>一辆{{ car.brand }}车,价值{{ car.price }}万</h2>
    <button @click="changePrice">修改汽车价格</button>
    <br />
    <h2>游戏列表:</h2>
    <ul>
      <!-- g是每一个数据,可以自己定义 -->
      <!-- games是数据源 -->
      <!-- key是遍历时节点的唯一标识 -->
      <!-- :是将内容当成js表达式去解析--全称v-bind -->
      <li v-for="g in games" :key="g.id">{{ g.name }}</li>
    </ul>
    <button @click="changeFirstGame">修改第一个游戏名字</button>
  </div>
</template>

<script lang="ts" setup name="Person">
import { reactive } from "vue";
//数据
//Proxy是js自带的一个代理,也就是一个函数
//和ref类似,使用reactive包裹的对象,会转变成Proxy对象,我自己所写的内容在Target中
let car = reactive({ brand: "奔驰", price: 100 }); //这是对象的
let games = reactive([
  //数组
  { id: "abcd01", name: "原神" },
  { id: "abcd02", name: "启动" },
  { id: "abcd03", name: "泵贴" },
]);
//方法
function changePrice() {
  car.price += 10;
}
function changeFirstGame() {
  games[0].name = "12345";
}
</script>
<style>
.person {
  background-color: skyblue;
}
</style>

ref -定义对象类型的响应式数据

通过ref也可以定义对象或者数组的响应式数据,但是其实ref还是使用reactive来定义的响应式数据,写法上只是把reactive换成ref,方法中添加了value

<template>
  <div class="person">
    <h2>一辆{{ car.brand }}车,价值{{ car.price }}万</h2>
    <button @click="changePrice">修改汽车价格</button>
    <br />
    <h2>游戏列表:</h2>
    <ul>
      <li v-for="g in games" :key="g.id">{{ g.name }}</li>
    </ul>
    <button @click="changeFirstGame">修改第一个游戏名字</button>
  </div>
</template>

<script lang="ts" setup name="Person">
import { ref } from "vue";
//数据
let car = ref({ brand: "奔驰", price: 100 }); //对象
let games = ref([
  //数组
  { id: "abcd01", name: "原神" },
  { id: "abcd02", name: "启动" },
  { id: "abcd03", name: "泵贴" },
]);
//方法
function changePrice() {
  car.value.price += 10;
}
function changeFirstGame() {
  games.value[0].name = "12345";
}
</script>
<style>
.person {
  background-color: skyblue;
}
</style>

ref 对比 reactive

宏观角度

ref用来定义: 基本类型数据、对象类型数据
reactive用来定义: 对象类型数据

区别

ref创建的变量必须使用.value(可以使用volar插件自动添加.value)
reactive重新分配一个新对象,会失去响应式(可以使用Object.assign去整体替换)

reactive重新分配一个新对象为什么会失去响应式呢?演示一下

<template>
  <div class="person">
    <h2>一辆{{ car.brand }}车,价值{{ car.price }}万</h2>
    <button @click="changeCar">重新分配对象</button>
    <br />
  </div>
</template>

<script lang="ts" setup name="Person">
import { reactive } from "vue";
//数据
let car = reactive({ brand: "奔驰", price: 100 }); //对象
//方法
function changeCar() {
  //这样写就等于是重新分配对象给reactive,一般出现在服务器返回给很多条数据的情况下
  //但是这样就算成功修改了,也不是响应式的了,这也是reactive的局限性
  car = { brand: "奥迪", price: 1 };
  console.log(car);
}
</script>

出现这种情况就可以使用Object.assign(obj1,obj2,obj3)API来解决这个问题

<template>
  <div class="person">
    <h2>一辆{{ car.brand }}车,价值{{ car.price }}万</h2>
    <button @click="changeCar">重新分配对象</button>
    <br />
  </div>
</template>

<script lang="ts" setup name="Person">
import { reactive } from "vue";
//数据
let car = reactive({ brand: "奔驰", price: 100 }); //对象
//方法
function changeCar() {
  // car = { brand: "奥迪", price: 1 };
  //将obj2的参数传给obj1,也就是将{ brand: "奥迪", price: 1 }传给car
  Object.assign(car, { brand: "奥迪", price: 1 });
  //car.value = { brand: "奥迪", price: 1};使用ref的情况直接改即可
}
</script>

使用原则

若需要一个基本类型的响应式数据,必须使用ref
若需要一个响应式对象,层级不深,ref、reactive都可以
若需要一个响应式对象,且层级较深,推荐使用reactive

toRefs与toRef

toRefs与toRef都是通过解构把响应式对象的东西以响应式的方式拿出来,toRefs可以拿很多;toRef只能一个一个拿

通过解构赋值去解构出的数据不是响应式的

<template>
  <div>
    <h2>姓名:{{ name }}</h2>
    <!-- 同理,这里显示的name是我解构赋值出的name,并不是响应式的 -->
    <h2>年龄:{{ person.age }}</h2>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年龄</button>
  </div>
</template>

<script lang="ts" setup name="Person">
import { reactive } from "vue";
//数据
let person = reactive({
  name: "张三",
  age: 18,
});

//解构赋值
let { name, age } = person;
//let name = person.name;
//let age = person.age
console.log(name, age);

//方法
//这里方法的name是修改的我结构赋值自己定义的name
//所以person.name的响应式数据不会变化
function changeName() {
  name += "~";
}
function changeAge() {
  person.age += 1;
}
</script>

那怎么让他变成响应式的呢?当然是使用toRefs

先上结论:toRefs接收一组响应式对象,将响应式对象里的每一个key;value拿出来形成一个新的对象

<template>
  <div>
    <h2>姓名:{{ name }}</h2>
    <!-- 同理,这里显示的name是我解构赋值出的name,并不是响应式的 -->
    <!-- 但是使用了toRefs后,这里的name其实是被ref包裹过的name,就变成响应式的了 -->
    <h2>年龄:{{ person.age }}</h2>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年龄</button>
  </div>
</template>

<script lang="ts" setup name="Person">
import { reactive, toRefs } from "vue";
//数据
let person = reactive({
  name: "张三",
  age: 18,
});

//解构赋值
let { name, age } = toRefs(person);
//加了toRefs,那么name和age就变成了ref定义的响应式对象
console.log(name, age);

//方法
//所以加了toRefs后,这里对解构的数据进行操作,也就是对加了ref的数据进行操作
//所以需要加上.value,也就能实现响应式了
function changeName() {
  name.value += "~";
}
function changeAge() {
  person.age += 1;
}
</script>

toRef

<template>
  <div>
    <h2>toRef年龄:{{ nl }}</h2>
    <h2>reactive年龄:{{ person.age }}</h2>
    <button @click="changeAge">修改年龄</button>
  </div>
</template>

<script lang="ts" setup name="Person">
import { reactive, toRef } from "vue";
//数据
let person = reactive({
  age: 18,
});

//解构赋值+toRef
let nl = toRef(person, "age");

//方法
function changeAge() {
  person.age += 1;
}
</script>

computed计算属性

为什么需要计算属性呢?

demo

<template>
  <div>
    <!-- 使用v-bind:value单向绑定 -->
    <!-- 姓<input type="text" v-bind:value="firstName" /> <br /> -->

    <!-- 使用v-model双向绑定 -->
<input type="text" v-model="firstName" /> <br />
<input type="text" v-model="lastName" /> <br />
    全名: <span>{{ firstName }}-{{ lastName }}</span><br />
    <!-- 需求--让首字母z大写 -->
    全名: <span>{{ firstName.slice(0,1).toUpperCase()+firstName.slice(1) }}-{{ lastName }}</span><br />
    <!-- 这样写虽然可以完成效果,但是违背了vue3的特性-让模板区,也就是template区变简单 -->
    <!-- 这个时候就需要用到computed计算属性了 -->
  </div>
</template>

<script lang="ts" setup name="Person">
import { ref } from "vue";

let firstName = ref("zhang");
let lastName = ref("三");
</script>
<style></style>

那在vue3中computed应该怎么使用呢?--计算属性是有缓存的,方法没有缓存,要注意

<template>
  <div>
    <!-- 使用v-bind:value单向绑定 -->
    <!-- 姓<input type="text" v-bind:value="firstName" /> <br /> -->

    <!-- 使用v-model双向绑定 -->
<input type="text" v-model="firstName" /> <br />
<input type="text" v-model="lastName" /> <br />
    全名: <span>{{ fullName }}</span><br />
    全名: <span>{{ fullName }}</span><br />
    全名: <span>{{ fullName }}</span><br />
    全名: <span>{{ fullName }}</span><br />
  </div>
</template>

<script lang="ts" setup name="Person">
import { ref, computed } from "vue";

let firstName = ref("zhang");
let lastName = ref("三");

//这么定义的fullName是一个计算属性,且是只读的,不能修改
//当输入框的内容改变时,修改的是ref,不是computed计算属性修改,
//computed计算属性发现所依赖的数据有变化时,重新计算

let fullName = computed(() => {//要注意计算属性是有缓存的,函数方法是没有缓存的
  console.log(1);//就算在template中调用了很多次,但是缓存没有变化,控制台也只执行一次 log1
  //如果是函数方法定义输出内容,那调用几次就输出几次log
  return (
    firstName.value.slice(0, 1).toUpperCase() +
    firstName.value.slice(1) +
    "-" +
    lastName.value
  );
});
</script>
<style></style>

因为计算属性是可读的,如果想实现可读可写,那就要使用get()``set()属性

<template>
  <div>
<input type="text" v-model="firstName" /> <br />
<input type="text" v-model="lastName" /> <br />
    全名: <span>{{ fullName }}</span
    ><br />
    <button @click="changeFullName">修改计算属性</button>
  </div>
</template>

<script lang="ts" setup name="Person">
import { ref, computed } from "vue";

let firstName = ref("zhang");
let lastName = ref("三");

let fullName = computed({
  get() {
    return (
      firstName.value.slice(0, 1).toUpperCase() +
      firstName.value.slice(1) +
      "-" +
      lastName.value
    );
  },
  set(val) {
    //这里的val就是changeFullName方法中的li-si
    const [str1, str2] = val.split("-");
    firstName.value = str1;
    lastName.value = str2;
    console.log(str1, str2); //li,si
  },
});
function changeFullName() {
  //这个方法引起set调用
  fullName.value = "li-si";
}
</script>
<style></style>
转载自:https://juejin.cn/post/7357965931480973322
评论
请登录