
🌈个人主页:前端青山
🔥系列专栏:vue篇
🔖人终将被年少不可得之物困其一生
依旧青山,本期给大家带来vue篇专栏内容:从入门到精通:Vue3一篇文章搞定!
目录
vue3语法
1、vue3初识
1、vue3简介
2020.9 vue.js 发布3版本。 耗时2年多开发完毕。由尤雨溪团队打造。
2、vue3特点:
1、性能提升
-
打包大小减少41%
-
初次渲染快51%,更新渲染快133%
-
内存占用减少54%
2、源码升级
-
使用Proxy 代替Object.defineProperty 实现双向数据绑定
-
重写虚拟DOM的实现和Tree-Shaking
-
vue3 更好的支持typescript
3、新的特性
composition API(组合式api)
-
setup配置
-
ref和reactive
-
watch 和watchEffect
-
provide和inject
提供了新的内置组件
-
Fragment
-
Teleport
-
Suspense
其他改变
-
新的生命周期钩子
-
移除keyCode 作为v-on的修饰符
3、创建vue3 工程项目
// 1. 确保你的vue-cli 脚手架版本在4.5.0以上,这样才能创建
// 2. 检查脚手架版本 vue -V 或 vue --version
// 3. 如果版本低话,重新安装vue-cli npm install -g @vue/cli
// 4. vue create vue3-project
// 5. cd vue3-project
// 6. npm run serve 启动项目
vite模板搭建vue3项目
npm init vue@latest

4、vue3目录结构分析
// vue3 入口文件 main.js
// 引入的不是Vue构造函数了,而是createApp 这个工厂函数(工厂函数就是返回值为一个对象的函数)
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App).mount('#app') // app 相当于vue2中的vm,但是比vm轻,里面绑定的属性和方法更少,而不是像vm那样用的不用的都绑上,显的有点重
// vue2入口文件 main.js
import Vue from 'vue'
import App from './App.vue'
const vm = new Vue({
render: h => h(App),
}).$mount('#app')
// 比较
// vue3 引入的是createApp 这个函数,而不是vue2中的的 Vue 构造函数啦。
// vue3中的 app 类似于之前vue2中的vm, 但是app应用实例对象比vm 更轻,里面绑定的属性和方法更少
-
打开app.vue,发现vue3模板结构可以不使用根标签。
<template>
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
</template>
2、Composition API
Composition API 也叫组合式api 主要包括如下及部分:
1、setup函数
-
它是vue3新增的一个配置项,值为一个函数,所有的组合式API 方法都写在在setup函数中。
-
组件中用到的数据,方法 计算属性,事件方法,监听函数,都配置在setup 中。
-
setup的函数值为一个函数,该函数的返回值为一个对象,对象中的属性和方法均可以在模板中直接使用。代码如下:
<template>
<div>
<p>姓名:{{ name }}</p>
<p>年龄:{{ age }}</p>
<p @click="sayHello(10)">说话</p>
</div>
</template>
<script>
export default {
name: "App",
setup() { //为一个函数
//定义变量
let name = "张三";
let age = 20;
// 定义方法
function sayHello(m) {
alert(`${name}--${age}--${m}`);
}
return { //setup函数返回值为一个对象
name,
age,
sayHello,
};
},
};
</script>
-
注意:setup 函数尽量不要和vue2.x混合使用
-
vue2.x中的(data,methods,computed)可以访问到 setup中的属性和方法
-
在setup中不能访问vue2.x中的配置数据和方法如(data,methods,computed)
代码如下:
<template> <div> <p>姓名:{{ name }}</p> <p>年龄:{{ age }}</p> <p @click="sayHello(10)">说话</p> <p>{{ height }}</p> </div> <!-- vue2.x中的事件 --> <div @click="test1">vue2中的事件test1</div> <!-- vue3中的事件 --> <div @click="test2">vue3中的事件test2</div> </template> <script> export default { name: "App", data() { return { sex: "男", // vue2中的变量 height: 186, // vue2中的变量 }; }, methods: { test1() { console.log(this.name); // "张三" console.log(this.age); // 20; console.log(this.sayHello); // 函数 console.log(this.sex); // 男 }, }, setup() { let name = "张三"; let age = 20; let height = 176; // setup中的变量 function sayHello(m) { alert(`${name}--${age}--${m}`); } function test2() { console.log(this.name); // "张三 console.log(this.sayHello); // 函数 console.log(this.sex); // undefined vue3中的setup 访问不了vue2中数据 console.log(this.test1);// undefined vue3中的setup 访问不了vue2中方法 } return { name, age, sayHello, test2, height,// setup中的变量 }; }, }; -
2、setup执行时间
1、 setup 在beforeCreate之前执行,且setup中获取不到this this为undefined
beforeCreate() {
console.log("beforeCreate");
console.log(this); // proxy 实例对象
},
setup() {
console.log("setup");
console.log(this); // undefined
}
3、ref 响应式函数
1、引言:
如下代码:当点击执行changeFn函数,修改setup中定义的变量时,发现页面中的name和age的数据并没有修改,说明该数据不是响应式数据
<template>
<div>111</div>
<p>{{ name }}--{{ age }}</p>
<button @click="changeFn">changeFn</button>
</template>
<script>
export default {
name: "App",
setup() {
//定义变量
let name = "张三";
let age = 20;
// 定义方法
function changeFn() {
name = "李四";
age = 30;
}
return {
//setup函数返回值为一个对象
name,
age,
changeFn,
};
},
};
</script>
2、ref 函数
-
它是 vue3中的一个函数,一般用于将基本类型数据处理成响应式数据。
-
作用:定义一个基本类型的响应式的数据,只有数据成为响应式数据,这样当数据变化时,才能被监测到。
-
使用时需要从vue中引入
<script> import { ref } from "vue"; // 引入响应式函数ref ... </script> -
语法:const xxx =ref(数据变量);结果 返回一个 RefImpl 的引用对象,获取时为 xxx.value
-
在页面模板中使用数据直接 使用插值表达式,不需要加value
姓名:{{ name }}
,因为vue3会自动帮你.value,所以可以拿到值 -
ref 函数实现数据响应式的原理还是利用了vue2的Object.defineProperty() 给数据设置 get set 监听函数,如下图:
image-20211130230403826
-
接收的数据类型可以是基本类型(**实现响应式原理为Object.defineProperty()**),也可以是对象类型(当为对象时,实现响应式的原理就是Proxy不是Object.defineProperty()) -
这里根据大佬的反馈有点小问题哈,
改正后的表述
Vue 2 中的响应式系统基于
Object.defineProperty(),它用于对象的属性拦截和响应式处理。不论是基本数据类型还是对象类型,Vue 2 都是通过Object.defineProperty()的 getter 和 setter 方法来实现响应式的。Vue 3 则采用了
Proxy实现响应式系统,不再使用Object.defineProperty()。在 Vue 3 中,Proxy可以直接代理整个对象或数组,而不仅限于属性级别的拦截。这意味着,Vue 3 可以对任何类型的数据进行响应式处理,包括基本类型和对象类型。具体来说,在 Vue 3 中:
- 基本数据类型:使用
ref包装,通过 ES6 类的get和set方法实现响应式,这些方法在引擎层面是通过Object.defineProperty()实现的。 - 对象类型:使用
reactive或ref包装,内部基于Proxy实现响应式。
点击如下change事件,修改name 和age
<template>
<div>
<!--name这个ref 引用对象在使用时不需要加value,vue3会自动帮你加value,所以可以拿到值-->
<p>{{ name }}--{{ age }}</p>
<p>{{ job.type }}--{{job.salary}}</p>
<p @click="change">说话</p>
</div>
</template>
<script>
import { ref } from "vue"; // 引入响应式函数ref
export default {
name: "App",
setup() {
let name = ref("张三"); //返回一个 ref 引用对象
let age = ref(20);
console.log(name)
// 当ref传的参数为对象时
let job = ref({
type: "前端工程师",
salary: "20k",
});
function change() {
name.value = "李四"; // ref对象.value 修改其值
age.value = 30;
job.value.type = "后端开发工程师";
job.value.salary = "30k";
}
return {
name,
age,
change,
job
};
},
};
</script>
4、reactive 函数
1、定义一个对象类型的响应式数据,返回一个Proxy 实例对象,不能用于基本数据类型,否则报错。(基本类型响应式数据使用ref)。
语法:const 代理对象= reactive(源对象) ,接收一个对象或数组,返回一个代理对象(即Proxy源对象)
-
使用时先从vue中引入
<script>
import { reactive } from "vue";
...
</script>
-
代码如下:
<template>
<p>{{ job.type }} --{{ job.salary }}--{{ job.like.a.b }}--{{job.content}}</p>
<p v-for="(item, index) in foodArr" :key="index">{{ item }}</p>
<button @click="changeFn">changeFn</button>
</template>
<script>
import { reactive } from "vue";
export default {
name: "App",
components: {},
setup() {
//定义对象
let job = reactive({
type: "前端工程师",
salary: "20k",
like:{
a:{
b:'不告诉你'
}
}
});
console.log(job);
//定义数组
let foodArr = reactive(["刷抖音", "敲代码"]);
console.log(foodArr);
// 定义方法
function changeFn() {
job.type = "后端开发工程师";
job.salary = "30k";
job.content = "写代码"; // 给对象添加属性
foodArr[0]='买包包' // 通过下标修改数组
}
return {
//setup函数返回值为一个对象
job,
changeFn,
foodArr
};
},
};
</script>
-
vue3中使用proxy 实现的数据响应去掉了vue2中不能检测到对象添加属性和通过下标修改数组而无法检测的情况。
5、vue3响应式原理
1、vue2.x的响应式原理
-
实现原理:
通过Object.defineProperty()对属性的读取,修改进行拦截。(也叫数据劫持)即对数据的操作进行监听
let data = { name: "张三", age: 30 } let p = {} Object.defineProperty(p, 'name', { get() { console.log('访问了name属性'); return data.name }, set(val) { console.log('设置name属性'); data.name = val } }) //如上存在2个问题: // 无法添加和删除属性 // 无法捕获到修改数组下标改变对应数组 -
vue2.x存在的问题
-
对象数据新增属性和删除属性,界面不会响应更新
<template> <div id="app"> <p>{{ person.name }}</p> <p>{{ person.age }}</p> <p v-if="person.sex">{{ person.sex }}</p> <button @click="addSex">添加属性</button> <button @click="deleteAge">删除属性</button> </div> </template> <script> import Vue from "vue"; export default { name: "App", data() { return { person: { name: "张三", age: 20, }, }; }, components: {}, methods: { addSex() { // this.person.sex = "男"; // console.log(this.person); // 数据被修改,但是vue监测不到 // this.$set(this.person, "sex", "男"); // 修改为第一种方式,vue可以检测到 Vue.set(this.person, "sex", "男"); // 修改为第二种方式,vue也可以检测到 console.log(this.person); }, deleteAge() { // delete this.person.age; // age被删除改,但是vue监测不到 this.$delete(this.person, "age"); // 修改为第一种方式,vue可以检测到 Vue.delete(this.person, "age"); // 修改为第二种方式,vue可以检测到 console.log(this.person); }, }, }; </script>-
数组数据直接通过修改下标,界面不会响应更新
<div id="app"> <p v-for="(item, index) in nameArr" :key="index">{{ item }}</p> <button @click="update">添加属性</button> </div> <script> import Vue from "vue"; export default { name: "App", data() { return { nameArr: ["刘备", "关羽"], }; }, methods: { update() { // this.nameArr[0] = "张飞"; // 数据被修改了,但是vue 检测不到 // this.nameArr.splice(0, 1, "张飞"); // 修改为第一种方式,数据可以被检测到 // this.$set(this.nameArr, 0, "张飞"); // 修改为第二种方式,数据可以被检测到 Vue.set(this.nameArr, 0, "张飞"); // 修改为第三种方式,数据可以被检测到 console.log(this.nameArr); }, }, }; </script> -
2、vue3响应式原理
1、首先看一下vue3是否还存在vue2.x 中的问题。
-
对象数据新增属性和删除属性,不存在vue2.x 中的问题了。
<template>
<div>
<p>{{ person.name }}</p>
<p>{{ person.age }}</p>
<p>{{ person.sex }}</p>
<button @click="addSex">添加属性</button>
<button @click="deleteSex">删除属性</button>
</div>
</template>
<script>
import { reactive } from "vue";
export default {
name: "App",
components: {},
setup() {
let person = reactive({
name: "张三",
age: 20,
});
console.log(person);
//定义添加属性
function addSex() {
person.sex = "男";
console.log(person);
}
// 定义删除属性
function deleteSex() {
delete person.sex;
console.log(person);
}
return {
person,
addSex,
deleteSex,
};
},
};
</script>
-
数组数据直接通过修改下标,修改数组,不存在vue2.x 中的问题了。
<template>
<div>
<p v-for="(item, index) in person.like" :key="index">{{ item }}</p>
<button @click="change">修改数组的值</button>
</div>
</template>
<script>
import { reactive } from "vue";
export default {
name: "App",
components: {},
setup() {
let person = reactive({
name: "张三",
age: 20,
like: ["打球", "敲代码"],
});
console.log(person); // proxy 实例对象
function change() {
person.like[0] = "打台球";
}
return {
person,
change,
};
},
};
</script>
2、vue3响应式原理
首先说一下Reflect的作用。
// Reflect是window下的一个内置对象
// 1. 使用reflect 访问数据
let obj = {
name: '张三',
age: 20
}
console.log(Reflect.get(obj, 'name')); // 张三
// 2.使用Reflect 修改数据
Reflect.set(obj, 'age', 50)
console.log(obj);
//3.使用Reflect删除数据
Reflect.deleteProperty(obj, 'name')
console.log(obj);
vue3响应原理代码:
通过Proxy代理,拦截对象中任意属性的变化,包括属性的读取,修改、设置、删除。
通过Reflect 反射对被代理对象的属性进行操作。
let data = {
name: "张三",
age: 30
}
console.log(Proxy);
// 使用p 对象代理data, Proxy为window 下的内置代理函数
let p = new Proxy(data, {
// 读取属性
get(target, propName) {
// target 就是 data
console.log(`读取p上个${propName}属性`);
return Reflect.get(target, propName)
},
// 修改和设置属性
set(target, propName, value) {
// value 为赋的值
console.log(`修改p的${propName}属性`);
// target[propName] = value
Reflect.set(target, propName, value)
},
//删除属性
deleteProperty(target, propName) {
console.log(`删除p上的${propName}属性`);
// return delete target[propName]
return Reflect.deleteProperty(target, propName)
}
})
6、Reactive 与 ref 对比
1、数据定义角度对比:
-
ref用来定义:基本数据类型
-
reactive 用来定义: 对象或数组类型数据
注意:ref也可以定义对象或数组类型数据,它内部还是通过reactive转换成代理对象
2、原理角度对比:
-
ref通过Object.defineProperty() 的get和set 实现数据的响应式 即(数据劫持)
-
reactive 通过Proxy 实现数据响应式的,并通过Reflect 来操作源对象数据的。
3、从使用角度对比:
-
ref 定义的数据操作时需要使用 .value, 而插值表达式中使用时,不需要使用 .value
-
reactive 定义的数据操作都不需要 .value
7、vue3中的组件传参
“组件关系:
父子 props、$panrent
子父 emit自定义事件 refs
兄弟 eventbus中央事件总线 vue3如果需要实现eventbus 安装第三方库mitt
跨层级 provider inject
组件状态共享工具: vuex pinia
1、父传子
-
在父组件中给子组件设置自定义属性 tit,将要传递的参数赋值给tit属性
<!--父组件 -->
<template>
<p></p>
<Testvue3 :tit="schoolName">
<span>123</span>
</Testvue3>
</template>
<script>
import Testvue3 from "@/components/Testvue3";
export default {
name: "App",
components: { Testvue3 },
setup() {
let schoolName = "青山"; // 定义要传给子组件的数据
return {
schoolName, // 要使用的变量抛出去,这样就可以在页面模板中使用该变量
};
},
};
</script>
-
在子组件中接收传过来的属性通过props ,这个和vue2 一样没有变化。
<!-- 子组件 -->
<template>
<p>{{ tit }}</p>
<button>点击事件,子传父</button>
</template>
<script>
export default {
data() {
return {};
},
props: ["tit"],
setup(props) {
// 参数props即为父组件传过来的参数
console.log(props)
return {
//setup函数返回值为一个对象
};
},
};
</script>
2、子传父
-
给子组件绑定自定义事件,然后在setup中定义该事件对应的方法,因为setup中没有this ,this为undefined,所以vue的开发者为了解决该问题,在setup中提供了2个形参,prop和context
-
props 为父传子的参数
-
context 上下文对象,里面有emit 方法,可以实现子传父
-
-
子组件中多了 emits选项,该选项类似于props,接收父组件给子组件绑定的自定义方法,如果不加该选项,vue3 会提示警告。但不影响功能
<!-- 子组件 -->
<template>
<p>{{ tit }}</p>
<button @click="emit">点击事件,子传父</button>
</template>
<script>
import { reactive } from "vue";
export default {
data() {
return {};
},
emits: ["transfer"], // 在子组件中使用emits配置项,接收父组件给我绑定的自定义事件,用法类似于props, // 不加该配置项,控制台会提示警告
setup(props, context) {
console.log(11, props);
console.log(22, context);
// 定义方法
function emit() {
// 子传父 此处不用this,使用context上下文对象
context.emit("transfer", 666);
}
return {
//setup函数返回值为一个对象
emit,
};
},
};
</script>
-
在父组件接收自定义事件,该事件对应的执行函数的形参就是传过来的数据,这个就和vue2一样啦。
<!--父组件 --> <template> <p></p> <Testvue3 @transfer="showdata"> <span>123</span> </Testvue3> </template> <script> import Testvue3 from "@/components/Testvue3"; export default { name: "App", components: { Testvue3 }, setup() { function showdata(value) { console.log(value); } return { showdata, }; }, }; </script>
8、vue3中的计算属性
同vue2不同,使用计算属性需要引入computed 方法
<template>
<p>姓:<input type="text" v-model="data.firstname" /></p>
<p>名:<input type="text" v-model="data.lastname" /></p>
<p>姓名:<input type="text" v-model="data.fullname" /></p>
</template>
<script>
// 引入对应的计算属性方法
import { reactive, computed } from "vue";
export default {
name: "App",
setup() {
let data = reactive({
firstname: "",
lastname: "",
fullname: "",
});
// 计算属性--简写
// data.fullname = computed(() => {
// return data.firstname + data.lastname;
// });
// 计算属性--完整写法
data.fullname = computed({
get() {
return data.firstname + data.lastname;
},
set(value) {
console.log(value);
data.firstname = value.substr(0, 1);
data.lastname = value.substr(1);
},
});
return {
data,
};
},
};
</script>
9、vue3 中的 watch监听器
vue3 中的watch 也是 组合式api中的一个方法,所以使用时,需要引入
<template>
<p>{{ sum }} <button @click="sum++">sum++</button></p>
<p>{{ fullname }} <button @click="fullname += '-'">修改姓名</button></p>
<p>
{{ userinfo.name }}--{{ userinfo.age }}--{{ userinfo.job.type }}--{{
userinfo.job.salary
}}K
<button @click="userinfo.age++">修改年龄</button>
<button @click="userinfo.job.salary++">修改薪水</button>
</p>
</template>
<script>
// 引入对应的计算属性方法
import { ref, watch, reactive } from "vue";
export default {
name: "App",
setup() {
let sum = ref(0);
let fullname = ref("张三");
let userinfo = reactive({
name: "李四",
age: 20,
job: {
type: "web开发",
salary: 20,
},
});
//1、监听ref定义的响应式数据 immediate初始化就执行watch
watch(sum, (newvalue, oldvalue) => {
console.log(newvalue, oldvalue);
},{immediate:true});
//2、 监听ref定义的多个响应式数据,immediate初始化就执行watch
watch([sum, fullname], (newvalue, oldvalue) => {
console.log(newvalue, oldvalue);
}, { immediate: true });
//3、 监听reactive 定义的响应式数据
// 注意:此处oldvalue 无效(新值与旧值一样),默认是深度监听,immediate初始化就执行watch
watch(
userinfo,
(newvalue, oldvalue) => {
console.log(newvalue, oldvalue);
},
{ immediate: true });
return {
sum,
fullname,
userinfo,
};
},
};
</script>
“
watch和watchEffect都能响应式地执行有副作用的回调。它们之间的主要区别是追踪响应式依赖的方式:
watch只追踪明确侦听的数据源。它不会追踪任何在回调中访问到的东西。另外,仅在数据源确实改变时才会触发回调。watch会避免在发生副作用时追踪依赖,因此,我们能更加精确地控制回调函数的触发时机。
watchEffect,则会在副作用发生期间追踪依赖。它会在同步执行过程中,自动追踪所有能访问到的响应式属性。这更方便,而且代码往往更简洁,但有时其响应性依赖关系会不那么明确。
10、vue3生命周期
1、vue3中的生命周期钩子函数与vue2中基本相同,只有如下发生变化,其他都不变.
-
beforeDestory => beforeUnmount
-
destoryed => unmounted
第一种方式:不使用组合式api的方式使用生命周期函数
<!-- 子组件 -->
<template>
<p>{{ sum }} <button @click="sum++">sum++</button></p> <!--修改sum数据触发update生命周期 -->
</template>
<script>
import { ref } from "vue";
export default {
setup() {
let sum = ref(0);
return {
sum,
};
},
beforeCreate() {
console.log("beforeCreate");
},
created() {
console.log("created");
},
beforeMount() {
console.log("beforeMount");
},
mounted() {
console.log("mounted");
},
beforeUpdate() {
console.log("beforeUpdate"); //页面数据发生改变触发
},
updated() {
console.log("updated"); //页面数据发生改变触发
},
beforeUnmount() {
console.log("beforeUnmount"); //组件卸载前触发
},
unmounted() {
console.log("unmounted"); //组件卸载后触发
},
};
</script>
在app.vue 中引入子组件
<template>
<Testvue3 v-if="flag"></Testvue3>
<button @click="flag = !flag">显示/隐藏</button> <!--修改flag 触发 beforeUnmount和 unmounted 生命周期-->
</template>
<script>
import { ref } from "vue";
import Testvue3 from "@/components/Testvue3";
export default {
name: "App",
components: {
Testvue3,
},
setup() {
let flag = ref(true);// 修改flag
return {
flag,
};
},
};
</script>
-
第二种方式:通过使用组合式api的形式使用生命周期 ,vue3和vue2.x的生命周期对照表如下:
setup 等同于 beforeCreate 和created

image-20211205222257250
<!-- 子组件 -->
<template>
<p>{{ sum }} <button @click="sum++">sum++</button></p>
</template>
<script>
//引入对应的生命周期钩子函数
import {
ref,
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted,
} from "vue";
export default {
setup() {
let sum = ref(0);
onBeforeMount(() => {
console.log("beforeMount");
});
onMounted(() => {
console.log("mounted");
});
onBeforeUpdate(() => {
console.log("beforeUpdate");
});
onUpdated(() => {
console.log("updated");
});
onBeforeUnmount(() => {
console.log("beforeUnmount");
});
onUnmounted(() => {
console.log("unmounted");
});
return {
sum,
};
},
};
</script>
11、vue3中自定义hook函数
hook函数定义:本质是一个函数,将setup中的公共逻辑抽离出来放在一个单独的js文件,这样哪个组件使用导入即可。
实现一个点击页面任意位置,在页面中输出当前点击位置的横纵坐标。
-
实现代码:未使用hook 钩子函数前
<!-- 子组件 -->
<template>
<p>
<span>鼠标点击位置的X坐标为:{{ point.x }}</span>
<span>鼠标点击位置的Y坐标为:{{ point.y }}</span>
</p>
</template>
<script>
import { reactive, onMounted, onBeforeUnmount } from "vue";
export default {
setup() {
let point = reactive({
x: 0,
y: 0,
});
onMounted(() => {
document.onclick = function (event) {
point.x = event.pageX;
point.y = event.pageY;
};
});
onBeforeUnmount(() => {
document.onclick = null;
});
return {
point,
};
},
};
</script>
-
使用hook 钩子函数 ,首先在src 目录下创建一个hooks文件夹,然后再该目录下创建usepoint.js文件,该文件用来存放抽来出来的公共方法。代码如下:
import { reactive, onMounted, onBeforeUnmount } from "vue";
export function usePoint() {
let point = reactive({
x: 0,
y: 0,
});
onMounted(() => {
document.onclick = function (event) {
point.x = event.pageX;
point.y = event.pageY;
};
});
onBeforeUnmount(() => {
document.onclick = null;
});
return point // 最后抛出该point
}
<!-- 子组件 -->
<template>
<p>
<span>鼠标点击位置的X坐标为:{{ point.x }}</span
>,
<span>鼠标点击位置的Y坐标为:{{ point.y }}</span>
</p>
</template>
<script>
// 在子组件中引入该hook方法
import { usePoint } from "@/hooks/usepoint";
export default {
setup() {
let point = usePoint();
return {
point,
};
},
};
</script>
12、toRef 与 toRefs
定义:toRef 创建一个ref 响应数据
语法:let name = toRef(person,'name') 将响应对象person中的name属性单独拿出来变成响应属性。
应用:一般用于将响应对象中的某个属性单独提供给外部使用
-
如下是使用toRef 前的代码: 插值表达式模板中的 person 有点繁琐
<!-- 子组件 -->
<template>
<div>
<p>
{{ person.name }} -- {{ person.age }} -- {{ person.job.type }} --
{{ person.job.salary }}
</p>
</div>
</template>
<script>
import { reactive } from "vue";
export default {
setup() {
let person = reactive({
name: "张三",
age: 20,
job: {
type: "web前端开发",
salary: 30,
},
});
return {
person,
};
},
};
</script>
-
如下是使用toRef 后 的代码,
<!-- 子组件 -->
<template>
<div>
<p>
<!-- 在模板中直接使用name,age,type,salary -->
{{ name }} -- {{ age }} -- {{ type }} --
{{ salary }}
</p>
<p>
<button @click="name += '-'">修改name</button>
<button @click="salary++">修改薪水</button>
</p>
</div>
</template>
<script>
import { reactive, toRef } from "vue";
export default {
setup() {
let person = reactive({
name: "张三",
age: 20,
job: {
type: "web前端开发",
salary: 30,
},
});
// 将person 中的name 属性转换成ref 响应式数据,这样就可以直接在模板中使用了,以此类推
let name = toRef(person, "name");
let age = toRef(person, "age");
let type = toRef(person.job, "type");
let salary = toRef(person.job, "salary");
return {
name,
age,
type,
salary,
};
},
};
</script>
<style scoped>
/* @import url(); 引入css类 */
</style>
-
使用toRefs 可以将对象中的多个属性转换成响应数据,代码如下:
<!-- 子组件 -->
<template>
<div>
<p>
{{ name }} -- {{ age }} -- {{ job.type }} --
{{ job.salary }}
</p>
<p>
<button @click="name += '-'">修改name</button>
<button @click="job.salary++">修改薪水</button>
</p>
</div>
</template>
<script>
import { reactive, toRefs } from "vue";
export default {
setup() {
let person = reactive({
name: "张三",
age: 20,
job: {
type: "web前端开发",
salary: 30,
},
});
//toRefs返回一个响应对象,该对象中每个属性都变成了响应属性了。这样就可以直接拿来在模板插值表达式中使用了
let person1 = toRefs(person);
// console.log(person1);
return {
...person1, // 使用后扩展运算符展开对象
};
},
};
</script>
<style scoped>
/* @import url(); 引入css类 */
</style>
“关于数据响应式设置的问题:
1、如何设置一个响应式数据? ref reactive
2、如何将非响应式数据转为响应式数据? toRef toRefs
3、如何将数据设置为只读数据?不能够修改 readOnly
13、vue3 中的路由
1、首先下载安装vue3 对应的vue-router
npm install vue-router@4
2、在src目录下新建router目录,在该目录下新建index.js 文件,代码如下
//1. 从vue-router 引入对应的方法createRouter 和 createWebHashHistory
import { createRouter, createWebHashHistory } from "vue-router";
//2.配置路由规则
const routes = [
{ path: '/home', component: () => import('@/views/Home') },
{ path: '/category', component: () => import('@/views/Category') },
]
//3.创建路由对象
const router = createRouter({
// 4. Provide the history implementation to use. We are using the hash history for simplicity here.
history: createWebHashHistory(),
routes, // short for `routes: routes`
})
export default router
3、在main.js 中引入并绑定
// 引入路由
import router from '@/router'
// 在全局实例上挂载router,这样每个页面都可以访问路由
createApp(App).use(store).use(router).mount('#app')
路由参数获取
<template>
<div>
新闻页
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { useRoute } from 'vue-router';
export default defineComponent({
setup() {
// useRoute获取路由参数
const route = useRoute()
console.log(route);
console.log(route.query.id);
return {}
}
})
</script>
<style scoped></style>
路由方法使用
<template>
<div>
Index首页
<!-- <button @click="$router.push('/news')">新闻</button> -->
<button @click="goNews">新闻</button>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
// 导入路由相关的组合式函数hook use开头的函数
import { useRouter } from 'vue-router'
export default defineComponent({
setup() {
// 组合式函数必须先调用一次
// useRouter 获取路由操作方法
const router = useRouter()
const goNews = () => {
// this.$router.push()
// console.log(router);
router.push('/news')
}
return { goNews }
}
})
</script>
<style scoped></style>
14、vue3 中的vuex
1、首先需要安装vuex
npm install vuex@next --save
2、在src目录下创建store 文件夹,该目录下创建index.js文件,代码如下:
import { createStore } from 'vuex'
// console.log(createStore);
export default createStore({
state: {
count: 100
},
getters: {
getCount(state) {
return state.count * 2
}
},
mutations: {
setTestA(state, value) {
state.count = value
}
},
actions: {
},
modules: {
}
})
3、在main.js 引入然后使用挂载
import { createApp } from 'vue'
import App from './App.vue'
// 引入 store
import store from '@/store'
console.log(store);
// 挂载使用 这样全局就可以使用store了
createApp(App).use(store).mount('#app')
// console.log(createApp(App));
4.在页面组件中使用store
<!-- 子组件 -->
<template>
{{ count }} ----{{ double }}
<button @click="updateFun">修改数据</button>
</template>
<script>
import { computed } from "vue";
import { useStore } from "vuex";
export default {
setup() {
const store = useStore();
// 必须以计算属性形式输出,否则无法修改state中的数据 如count
const count = computed(() => {
return store.state.count;
});
const double = computed(() => {
return store.getters.getCount;
});
const updateFun = () => {
store.commit("setTestA", 20);
};
return {
count,
double,
updateFun,
};
},
};
</script>
vue官方在使用vue3时,更加推荐使用Pinia状态管理。Pinia和vuex实际一个开发团队在维护。vuex最终版本是4,可以将Pinia看作是vuex5。
src\stores\counter.ts
import { ref, computed } from 'vue'
// defineStore store构造函数
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => {
// 初始化state
const count = ref(0)
// 使用计算属性获取结果
const doubleCount = computed(() => count.value * 2)
// actions方法 修改state的操作方法
function increment() {
count.value++
// console.log(count)
}
// 将需要外部使用到的值和方法按需导出
return { count, doubleCount, increment }
})
src\App.vue
<template>
<div>
<button @click="increment"> {{ store.count }}</button>
<div>{{ store.doubleCount }}</div>
</div>
</template>
<script lang="ts">
import { defineComponent, toRef } from 'vue'
// 将usestore引入
import { useCounterStore } from './stores/counter'
export default defineComponent({
setup() {
// 调用store获取对象
const store = useCounterStore()
console.log(store);
// 从store对象中获取state值和修改state的方法
return { store, increment: store.increment }
}
})
</script>
<style scoped></style>
806





