一、简介
现在企业当中使用的基本都是vue2.x的版本,vue3.0是尤雨溪官方团队于2020年09月18日正式发布的。
vue3.0版本与其他版本完全不同,它最大限度的减少了开发人员配置工具的次数,另外增添了许多丰富的内置功能,还附带了一个完整的GUI用于创建和管理项目。
二、语法特性
2.1初步入门
2.1.1 vue-cli方式安装
必须保证你的vue-cli的版本在4.5以上,你可以用vue-V测试一下
如果不符合请重新安装
npm install -g @vue/cli
然后执行下面的三个步骤创建vue脚手架项目:
- vue create 项目
- cd 项目
- npm run serve
2.1.1 使用vite方式创建
操作步骤如下:
- npm init vite-app 项目名称
- cd 项目名
- npm install (安装所有依赖)
- npm run dev (启动项目)
三、常用组合式API(Composition API)
3.1 setup
- 以前vue2中定义数据放在data中,定义方法放在methods中,现在vue3全部定义在setup中
- setup中返回一个对象,对象中的属性在页面直接使用
vue页面如下操作:
<template>
<div>
<div>
<p>{{name}}{{score}}</p>
<p>{{fn()}}</p>
</div>
</div>
</template>
<script>
export default {
name: "HelloWorld",
setup() {
let name = "tom";
let score = 80;
function setData() {
return `${name}你好,你考了${score}分`;
}
return {
name: name,
score: score,
fn: setData
};
}
};
</script>
但是注意vue3中vue2的语法也是正常可以使用的
<template>
<div>
<div>
<p>{{name}}{{score}}</p>
<p>{{fn()}}</p>
<p>{{msg}}</p>
<button @click="show">点击显示</button>
</div>
</div>
</template>
<script>
export default {
name: "HelloWorld",
data() {
return {
msg: "hello"
};
},
methods: {
show() {
console.log(this.msg);
}
},
setup() {
let name = "tom";
let score = 80;
function setData() {
return `${name}你好,你考了${score}分`;
}
return {
name: name,
score: score,
fn: setData
};
}
};
</script>
注意
虽然能实现,但是不建议混用,会出各种意向不到的问题
3.2 ref函数
ref函数是把普通的数据进行包裹,形成一个响应式的引用对象
- 不包裹ref函数调用
<template>
<div>
<div>
<p>{{name}}{{score}}</p>
<button @click="fn">点击显示</button>
</div>
</div>
</template>
<script>
export default {
name: "HelloWorld",
setup() {
let name = "tom";
let score = 80;
function setData() {
name = "jim";
score = 100;
console.log(name); //jim
console.log(score); //100
}
return {
name: name,
score: score,
fn: setData
};
}
};
</script>
- ref函数包裹以后
<template>
<div>
<div>
<p>{{name}}{{score}}</p>
<button @click="fn">点击显示</button>
</div>
</div>
</template>
<script>
import { ref } from "vue";
export default {
name: "HelloWorld",
setup() {
let name = ref("tom");
let score = ref(80);
function setData() {
name.value = "jim";
score.value = 100;
console.log(name); //RefImpl {__v_isShallow: false, dep: Set(1), __v_isRef: true, _rawValue: 'tom', _value: 'tom'}
console.log(score); //RefImpl {__v_isShallow: false, dep: Set(1), __v_isRef: true, _rawValue: 80, _value: 80}
}
return {
name: name,
score: score,
fn: setData
};
}
};
</script>
- 如果是处理对象等复杂类型
<template>
<div>
<div>
<p>{{name}}{{score}}</p>
<p>{{info.sex}}</p>
<p>{{info.address}}</p>
<button @click="fn">点击显示</button>
</div>
</div>
</template>
<script>
import { ref } from "vue";
export default {
name: "HelloWorld",
setup() {
let name = ref("tom");
let score = ref(80);
let info = ref({
sex: "男",
address: "保定"
});
function setData() {
name.value = "jim";
score.value = 100;
// 修改引用类型的数据
info.value.sex = "女";
info.value.address = "北京";
console.log(name); //RefImpl {__v_isShallow: false, dep: Set(1), __v_isRef: true, _rawValue: 'tom', _value: 'tom'}
console.log(score); //RefImpl {__v_isShallow: false, dep: Set(1), __v_isRef: true, _rawValue: 80, _value: 80}
console.log(info.value); //Proxy {sex: '女', address: '北京'}
}
return {
name: name,
score: score,
info: info,
fn: setData
};
}
};
</script>
3.3 reactive
- 基本类型用ref,对象类型建议用reactive
- reactive包裹对象,用的时候用对象.属性
- 可以处理对象和数组类型数据
3.3.1处理对象类型数据
<template>
<div>
<div>
<p>{{info.sex}}</p>
<p>{{info.address}}</p>
<button @click="fn">点击显示</button>
</div>
</div>
</template>
<script>
import { reactive } from "vue";
export default {
name: "HelloWorld",
setup() {
let info = reactive({
sex: "男",
address: "保定"
});
function setData() {
// 修改引用类型的数据
info.sex = "女";
info.address = "北京";
console.log(info); //Proxy {sex: '女', address: '北京'}
}
return {
info: info,
fn: setData
};
}
};
</script>
3.3.2处理数组数据
<template>
<div>
<div>
<p>{{arr}}</p>
<button @click="fn">点击显示</button>
</div>
</div>
</template>
<script>
import {reactive } from "vue";
export default {
name: "HelloWorld",
setup() {
let arr = reactive(["敲代码", "听音乐", "打游戏"]);
function setData() {
arr[0] = "改变后的值";
}
return {
arr: arr,
fn: setData
};
}
};
</script>
3.3.3建议处理数据如下
<template>
<div>
<div>
<p>{{person.name}}{{person.score}}</p>
<p>{{person.info.sex}}</p>
<p>{{person.info.address}}</p>
<p>{{person.arr}}</p>
<button @click="fn">点击显示</button>
</div>
</div>
</template>
<script>
import { reactive } from "vue";
export default {
name: "HelloWorld",
setup() {
let person = reactive({
name: "tom",
score: 80,
info: {
sex: "男",
address: "保定"
},
arr: ["敲代码", "听音乐", "打游戏"]
});
function setData() {
person.name = "jim";
person.score = "100";
person.sex = "女";
person.address = "北京";
person.arr[0] = "改变后的值";
}
return {
person: person,
fn: setData
};
}
};
</script>
3.4 setup参数
- setup中有props和context两个参数
- props是接收组件外部传递过来的对象
- context是上下文对象
- 里面的attrs也是接收组件外部传递的对象,但是如果在props中声明的属性不会获取到
- slots相当于接收到的插槽内容
- emit相当于组件分发的自定义事件函数
3.4.1 vue3创建组件并注册
- components中定义Test1.vue
<template>
<div>
<div>
<p>{{person.name}}{{person.score}}</p>
<button @click="fn">点击显示</button>
</div>
</div>
</template>
<script>
import { ref, reactive } from "vue";
export default {
name: "HelloWorld",
setup() {
let person = reactive({
name: "tom",
score: 80
});
function setData() {
person.name = "jim";
person.score = "100";
}
return {
person: person,
fn: setData
};
}
};
</script>
App.vue中引入Test1子组件
<template>
<!-- <HelloWorld /> -->
<Test1/>
</template>
<script>
import Test1 from "./components/Test1.vue";
export default {
name: "App",
components: {
Test1
}
};
</script>
3.4.2 props使用
App.vue传递如下:
<template>
<!-- <HelloWorld /> -->
<Test1 msg="传递数据" num="传递数量"/>
</template>
Test1.vue接收如下:
<template>
<div>
<div>
<p>{{person.name}}{{person.score}}</p>
<p>{{msg}}{{num}}</p>
<button @click="fn">点击显示</button>
</div>
</div>
</template>
<script>
import { ref, reactive } from "vue";
export default {
name: "HelloWorld",
props: ["msg", "num"],
setup(props) {
let person = reactive({
name: "tom",
score: 80
});
function setData() {
person.name = "jim";
person.score = "100";
console.log(props); //Proxy {msg: '传递数据', num: '传递数量'}
}
return {
person: person,
fn: setData
};
}
};
</script>
3.4.3 context
(1)attrs
3.5 vue3中的计算属性
vue3中的计算属性和vue2中的计算属性一致,都是对数据进行监听计算的。
3.5.1 计算属性基础用法
Test2.vue
<template>
<div>
<div>
<h3>请输入一个人的全名信息</h3>
<input type="text" v-model="person.firstname">
<input type="text" v-model="person.lastname">
<p>全名{{fullName}}</p>
</div>
</div>
</template>
<script>
import { ref, reactive, computed } from "vue";
export default {
name: "HelloWorld",
setup(props, context) {
let person = reactive({
firstname: "张",
lastname: "三丰"
});
// 计算属性
let fullName = computed(() => {
return person.firstname + person.lastname;
});
return {
person,
fullName
};
}
};
</script>
App.vue引入并挂载
<template>
<Test2 msg="hello" num="1000"/>
</template>
<script>
import Test2 from "./components/Test2.vue";
export default {
name: "App",
components: {
Test2
}
};
</script>
3.5.2 更改计算属性
上面写法计算属性是只读的,不能更改,如果需要更改需要设置成get和set
get是获取计算属性中的值,set是设置计算属性中的值
Test2修改如下,注意set中的value就是你前面get的拼接的结果值
<template>
<div>
<div>
<h3>请输入一个人的全名信息</h3>
<input type="text" v-model="person.firstname">
<input type="text" v-model="person.lastname">
<p>全名{{person.fullName}}</p>
<!-- 更改计算属性 -->
<input type="text" v-model="person.fullName">
</div>
</div>
</template>
<script>
import { ref, reactive, computed } from "vue";
export default {
name: "HelloWorld",
setup(props, context) {
let person = reactive({
firstname: "张",
lastname: "三丰"
});
// 计算属性
person.fullName = computed({
get() {
return person.firstname + "-" + person.lastname;
},
set(value) {
var arr = value.split("-");
person.firstname = arr[0];
person.lastname = arr[1];
}
});
// person.fullName = computed(() => {
// return person.firstname + person.lastname;
// });
return {
person
};
}
};
</script>
3.6watch监听器
3.6.1 vue2中监听器复习
(1)普通监听方法
<template>
<div>
<p>{{num}}</p>
<button @click="addNum">点击+1</button>
</div>
</template>
<script>
import { ref, reactive, computed } from "vue";
export default {
name: "HelloWorld",
data() {
return {
num: 0
};
},
watch: {
num(newValue, oldValue) {
console.log("初识值是" + oldValue + "变化后的值是" + newValue);
}
},
methods: {
addNum() {
this.num++;
}
}
};
</script>
(2)立即执行和深度监听方法
<template>
<div>
<p>{{num}}</p>
<button @click="addNum">点击+1</button>
</div>
</template>
<script>
import { ref, reactive, computed } from "vue";
export default {
name: "HelloWorld",
data() {
return {
num: 0
};
},
watch: {
num: {
deep: true,
immediate: true,
handler(newValue, oldValue) {
console.log("初识值是" + oldValue + "变化后的值是" + newValue);
}
}
},
methods: {
addNum() {
this.num++;
}
}
};
</script>
3.6.3 vue3中监听器
(1)监听ref函数包裹的响应式数据变化
<template>
<div>
<p>{{num}}</p>
<button @click="addNum">点击+1</button>
</div>
</template>
<script>
import { ref, watch } from "vue";
export default {
name: "HelloWorld",
data() {
return {
num: 0
};
},
setup() {
let num = ref(0);
function addNum() {
num.value++;
}
// 监听器写法
watch(num, (newValue, oldValue) => {
console.log("初识值是" + oldValue + "变化后的值是" + newValue);
});
return {
num,
addNum
};
}
};
</script>
(2)监听ref函数包裹的多个响应式数据变化
<template>
<div>
<p>{{num}}</p>
<button @click="addNum">点击+1</button>
<!-- 另一个监听项 -->
<p>{{msg}}</p>
<button @click="change">点击改变</button>
</div>
</template>
<script>
import { ref, watch } from "vue";
export default {
name: "HelloWorld",
data() {
return {
num: 0
};
},
setup() {
let num = ref(0);
let msg = ref("hello");
function addNum() {
num.value++;
}
function change() {
msg.value += "你好!";
}
// 监听器写法
watch(
[num, msg],
(newValue, oldValue) => {
console.log(newValue); //[1,'hello']
console.log(oldValue); //[0,'hello']
console.log("初识值是" + oldValue + "变化后的值是" + newValue);
},
{ immediate: true }
);
return {
num,
addNum,
change
};
}
};
</script>
总结
vue3中的watch第一个参数可以是单个值,也可以用数组传递多个监听数据,第二个参数是监听函数,第三个参数是对象的形式传递监听项配置
四、新旧生命周期对比
4.1 vue2中的生命周期函数
总结如下
vue 的生命周期函数,有那些?在项目中怎么使用?以及应用场景?
1.vue的生命周期函数分为:创建前
beforeCreate :在实例初始化之后执行此时对象还未创建,el 和data并未初始化,因此无法访问 methods,data ,computed等方法和数据。
2.创建后:created:最早开始使用data和methods中的数据的钩子函数,这个阶段可以数据请求,但是不能dom操作。
3.挂载前:beforeMount:挂载开始之前被调用,把data里面的数据和模板生成html,完成了el和data初始化,注意此时还没有挂载到html页面上。
4.挂载后:mounted:挂载完成,HTML已经被渲染到了页面上,这个阶段可以执行dom操作,可以进行数据请求。
5.数据更新前:beforeUpdate:数据更新前调用,当data的数据发生改变会执行这个钩子 内存中数据是新的 页面是旧的。
6.数据更新后:updated:由于数据更改导致的虚拟 DOM 重新渲染,在这之后会调用该钩子。当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。注意 updated 不会保证所有的子组件也都一起被重绘。如果你希望等到整个视图都重绘完毕,可以在 updated 里使用 vm.$nextTick。
7.销毁前:beforeDestroy:实例销毁之前调用,在这一步还可以做一些释放内存的操作。
8.销毁后:destroy:实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有.的事件监听器被移除,所有的子实例也都被销毁。
4.2vue3中的生命周期函数
4.2.1 生命周期函数是配置项
新建Test3.vue,代码如下:
<template>
<div>
<p>{{num}}</p>
<button @click="addNum">点击+1</button>
</div>
</template>
<script>
import { ref } from "vue";
export default {
name: "Test3",
setup() {
let num = ref(0);
function addNum() {
num.value++;
}
return {
num,
addNum
};
},
beforeCreate() {
console.log("vue实例初始化之前beforeCreate");
},
created() {
console.log("vue实例初始化之后created");
},
beforeMount() {
console.log("DOM结构渲染之前beforeMount");
},
mounted() {
console.log("DOM结构渲染之后mounted,一般用于发网络请求");
},
beforeUpdate() {
console.log("数据更新之前beforeUpdate");
},
updated() {
console.log("数据更新之后updated");
},
beforeUnMount() {
console.log("vue实例销毁之前beforeUnMount");
},
unmounted() {
console.log("vue实例销毁之后unmounted");
}
};
4.2.2 生命周期函数组合式API
- beforeCreate====setup
- created====setup
- beforeMount===onBeforeMount
- mounted===onMounted
- beforeUpdate===onBeforeUpdate
- updated===onUpdated
- beforeUnmount===onBeforeUnmount
- unmouned===onUnmouned
注意
下面这样写是把组合式API放在setup中
<template>
<div>
<p>{{num}}</p>
<button @click="addNum">点击更新</button>
</div>
</template>
<script>
// 导入reactive函数
import {
ref,
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted
} from "vue";
export default {
name: "HelloWorld",
setup() {
let num = ref(0);
function addNum() {
num.value++;
}
// 调用组合式API
onBeforeMount(() => {
console.log("-----onBeforeMount");
});
onMounted(() => {
console.log("-----onMounted");
});
onBeforeUpdate(() => {
console.log("-----onBeforeUpdate");
});
onUpdated(() => {
console.log("-----onUpdated");
});
onBeforeUnmount(() => {
console.log("-----onBeforeUnmount");
});
onUnmounted(() => {
console.log("-----onUnmounted");
});
return {
num,
addNum
};
}
};
</script>
五、hook函数
5.1 概念
hook函数是把setup中用到的组合式API进行封装,方便代码复用
5.2 基础用法
项目的src文件夹下面新建hook文件,新建useXX格式的js文件,代码如下:
比如我们命名usePoint.js
import { reactive, onMounted, onBeforeUnmount } from "vue";
function getPosition() {
// 定义数据
let point = reactive({
x: 0,
y: 0
});
// 定义处理数据的方法
function savePoint(event) {
point.x = event.pageX;
point.y = event.pageY;
}
// 定义生命周期函数
onMounted(() => {
window.addEventListener("click", savePoint);
});
onBeforeUnmount(() => {
window.removeEventListener("click", savePoint);
});
return point;
}
export default getPosition;
然后在需要使用使用导入并且使用,比如在Test1.vue如下:
<template>
<div>
<p>x轴坐标:{{point.x}}y轴坐标:{{point.y}}</p>
</div>
</template>
<script>
// 导入hook
import position from "../hook/usePoint";
export default {
name: "Test4",
setup() {
let point = position();
return {
point
};
}
};
</script>
六、toRef和refs
6.1 需求
如果我们在操作一些对象类型数据的时候如果直接返回,页面展示是比较繁琐的
6.2toRef
toRef的第一个参数是包裹的对象,第二个是解析的属性值
<template>
<div>
<p>{{name}}</p>
<p>{{address}}</p>
<p>{{hot}}</p>
</div>
</template>
<script>
// 导入ref函数
import { reactive, toRef } from "vue";
export default {
name: "HelloWorld",
setup() {
// 定义初识数据
let obj = reactive({
name: "tom",
info: {
sex: "男",
address: "保定",
hobby: {
hot: "music"
}
}
});
return {
name: toRef(obj, "name"),
address: toRef(obj.info, "address"),
hot: toRef(obj.info.hobby, "hot")
};
}
};
</script>
6.3toRefs
import { reactive, toRefs } from "vue";
<template>
<div>
<p>{{name}}</p>
<p>{{info.sex}}</p>
<p>{{info.address}}</p>
</div>
</template>
<script>
// 导入ref函数
import { reactive, toRef, toRefs } from "vue";
export default {
name: "HelloWorld",
setup() {
// 定义初识数据
let obj = reactive({
name: "tom",
info: {
sex: "男",
address: "保定",
hobby: {
hot: "music"
}
}
});
return {
...toRefs(obj)
};
}
};
</script>
总结
如果需要把对象等复杂数据提供给外部,需要保持响应式属性,可以用toRef和toRefs包裹,一个是传递2个参数,一个是默认全部解析,传递一个参数即可。