Vue3+TS知识
本篇内容全部采用TypeScript+组合式API+setup语法糖的规范形式
Vue3==兼容==>Vue2--基本不用2的写法
目前进程 --014
创建Vue3工程
vite和webpack是等价的,两个不同的构建工具,vite
和webpack
的区别在于,vite
需要用到什么,也就是取决于你看什么,就加载什么;而webpack
则是全部加载了再给你展示,速度相对就慢很多,类似懒加载
基于Vue-cli创建(脚手架-webpack)
基于vite创建
https://vitejs.cn/
1.创建指令(需要安装node.js--获得npm环境)
$ pnpm create vue@latest
2.输入项目名称
3.选择其他配置完成配置
目录介绍
src内存放源代码
在src中,必不可少的文件是App.vue
和main.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> 部分使用 TypeScript(TS)语言编写
//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
setup
是Vue3
中一个新的配置项,值是一个函数,它是Componsition API
表演的舞台,组件中所用到的:数据、方法、计算属性、监视......等等,均在setup
中。
特点图下:
setup
函数返回的对象中的内容,可直接在模板中使用
setup
中访问this
是undefined
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