目录
八、setup执行时机在beforeCreate之前执行一次,里面打印this是undefined
- // 引入的不再是构造函数,引入了一个名为creacteApp的工厂函数
- import { createApp } from 'vue'
- import './style.css'
- import App from './App.vue'
- // 创建应用示例对象--->app
- const app = createApp(App)
- //把组件APP挂载到#app节点上
- app.mount('#app')

组件中用到的:数据、方法等等,均要配置配置在setup中
setup中的变量不用this.变量获取了 因为本身就在同一个方法中(同一个作用域)
setup返回一个对象,则对象中的属性、方法,在模板中均可以直接使用(重点)
- <h1>一个人的信息h1>
- <h2>姓名:{{name}}h2>
- <h2>年龄:{{name}}h2>
- <button @click="sayHello">说话button>
-
- <script>
- export default {
- name: 'App',
- // 此时只是测试一下setup, 暂时不考虑响应式的问题。
- setup() {
- // 数据
- let name = '张三'
- let age = 18
- // 方法
- function sayHello() {
- alert(`我叫${name}. 我${age}岁了, 你好呀`)
- }
- //返回一个对象(常用) 对象里的属性可以直接在模板中使用
- return {
- name: name,
- age: age,
- sayHello,
- }
- }
- }
- script>
效果:
作用: 定义一个响应式的数据
语法: const xxx = ref(initValue)
创建一个包含响应式数据的引用对象(reference对象,简称ref对象)
操作数据: xxx.value
模板中读取数据不需要.value, 例如;
- <h1>一个人的信息h1>
- <h2>姓名:{{name}}h2>
- <h2>年龄:{{age}}h2>
- <h2>职业:{{obj.type}}h2>
- <h2>职业:{{obj.salery}}h2>
- <button @click="changeInfo">修改人员信息button>
- <script>
- import {ref} from 'vue'
- export default {
- name: 'App',
- setup() {
- // 数据
- let name = ref('张三')
- let age = ref(18)
- let obj = ref({ // 这里对象用的是ref
- type: '前端工程师',
- salery: '30k',
- })
- // 方法
- function changeInfo() {
- name.value = "李四",
- age.value = 48,
- obj.value.type = 'UI设计师', // 由于用的是ref 所以修改值才用obj.value.type
- obj.value.salary = '60k', // 由于用的是ref 所以修改值才用obj.value.salary
- }
- //返回一个对象(常用) 对象里的属性可以直接在模板中使用
- return {
- name: name,
- age: age,
- obj,
- changeInfo,
- }
- }
- }
- script>
效果:

题外话:对象解构会失去响应式 大聪明们
作用: 定义一个对象类型的响应式数据(基本类型不要用它, 要用ref函数)
语法: const 代理对象 = reactive(源对象) 用来接收一个数组或对象,返回一个代理对象(proxy的实例对象)
reactive定义的响应式数据是“深层次的”
内部是基于ES6的proxy实现的,通过代理对象操作源对象内部数据进行操作
- // 大家有没有发现 上面用ref函数处理对象的时候如果对象多层嵌套呢
- // 例如:
- let obj = ref( { obj: {obj: '旧值'} } )
- // 修改值的时候
- obj.value.obj.value.obj = '修改的新值'
- // 上面是不是很麻烦
-
- // 用reactive函数就省略了上面的.value
- // 例如:
- let obj1 = reactive( { obj: {obj: '旧值'} } )
- // 修改值的时候
- obj1.obj.obj = '修改的新值'
- // 修改数组:
- let hobby = reactive( ['抽烟', '喝酒', '烫头'] )
- // 修改值的时候
- hobby[0] = '学习' ==> 结果为: ['学习', '喝酒', '烫头']
- <h1>一个人的信息h1>
- <h2>姓名:{{person.name}}h2>
- <h2>年龄:{{person.age}}h2>
- <h2>工作种类:{{person.obj.type}}h2>
- <h2>工作薪水:{{person.obj.salery}}h2>
- <h2>爱好:{{person.hobby}}h2>
- <h2>测试数据:{{person.obj.a.b.c}}h2>
- <button @click="changeInfo">修改人员信息button>
- <script>
- import {ref, reactive} from 'vue'
- export default {
- name: 'App',
- setup() {
- // 把所有的数据都定义在一个类里面 然后用reactive进行改造响应式数据
- let person = reactive({
- name: '张三',
- age: 18,
- obj: {
- type: '前端工程师',
- salery: '30k',
- a: {
- b: {
- c: 666
- }
- }
- },
- hobby: ['抽烟', '喝酒', '烫头']
- })
- // 方法
- function changeInfo() {
- person.name = "李四",
- person.age = 48,
- person.obj.type = 'UI设计师',
- person.obj.salery = '60k',
- person.obj.a.b.c = '999',
- person.hobby[0] = '学习'
- }
- //返回一个对象(常用) 对象里的属性可以直接在模板中使用
- return {
- person,
- changeInfo,
- }
- }
- }
- script>
效果:

父传子props接收 ,props没有接收$attrs就接收,$attrs是一个对象,在模板用的时候通过$.attrs.属性名获取父组件传过来的值 props接收的 可以直接在模板上写属性名
但是我们不建议这样用 最好都要用props接收 因为能控制接收的类型
1.参数一:是props,props要定义props接收父传过来的属性,然后props再作为setup参数一,
这是为什么这么做呢: 我猜是这样的 因为setup执行时机在beforeCreate之前执行一次,this是undefined,所以无法通过this获取值只能通过传入props, 不然setup内部获取不了外面传进来的值
在setup内部打印出来的数据类型是一个Proxy对象 也就是一个响应式数据
2.参数二:参数二是一个普普通通的参数,里面有
1)、$attrs(就是捡漏props没写的属性就写在这里)
2)、emit(触发自定义事件):
用法:参数二.emit('方法名', 传出去的数据) ==> 例如: 参数二.emit('hello', 666)
上面写法如果有警告的话在setup外面写一个emits: ['hello']就好了
3)、slots插槽
注意: vue3中已经废弃slot="aaa"
改用v-slot:aaa
在vue3中也可以像vue2那种套路写计算属性,但是我不推荐使用,你想想都他么学vue3了,还使用旧的干嘛,别人新的东西有新的好,不然别人弄新的干嘛 所以这里用vue3的的computed
- <h1>一个人的信息h1>
- 姓: <input type="text" v-model="person.firstName" />
- <br />
- 名: <input type="text" v-model="person.lastName" />
- <br />
- <span>全名:{{person.fullName}}span>
- 全名:"text" v-model="person.fullName" />
- <script>
- import {reactive, computed} from 'vue'
- export default {
- name: 'Demo',
- setup() {
- let person = reactive({
- firstName: '张',
- lastName: '三',
- })
- // 计算属性 -- 简写 (没有考虑计算属性被修改)
- //persom.fullName = computed(() => {
- //return person.firstName + '-' + person.lastName
- //})
- // 计算属性 -- 完整写法(考虑读写)
- persom.fullName = computed({
- get() {
- return person.firstName + '-' + person.lastName
- },
- set(value) {
- const nameArr = value.split('-')
- person.firstName = nameArr[0]
- person.lastName = nameArr[1]
- }
- })
- return {
- person
- }
- }
- }
- script>
watch的三个参数: 参数一: 监视谁 参数二: 监视的回调 参数三: 监视的配置
1)、watch监视ref定义的数据
- <h1>当前求和为:{{sum}}h1>
- <button @click='sum++'>点我+1button>
-
- <h2>当前的信息为:{{msg}}h2>
- <button @click='msg +=!'>修改信息button>
- <br/>
- <h1>当前求和为:{{sum}}h1>
- <button @click='sum++'>点我+1button>
-
- <h2>当前的信息为:{{msg}}h2>
- <button @click='msg +=!'>修改信息button>
- <script>
- import {ref, watch} from 'vue'
- export default {
- name: 'Demo',
- setup() {
- // 数据
- let sum = ref(0)
- let msg = ref('你好啊')
- // 情况一:监视ref所定义的一个响应式数据
- // watch(sum, (newValue, oldValue) => {
- // console.log('sum变了', newValue, oldValue); // sum变了 1 0
- //}, {immediate: true, deep: true})
- // 情况二:监视ref所定义的多个响应式数据
- watch([sum, msg], (newValue, oldValue) => {
- console.log('sum或msg变了', newValue, oldValue);
- // 1.点击加1按钮 sum或msg变 [1, '你好啊'] [0, '你好啊']
- // 2.点击修改信息按钮 sum或msg变 [1, '你好啊!'] [0, '你好啊']
- }, {immediate: true})
-
-
- // 返回对象
- return {
- sum,
- msg
- }
- }
- }
- script>
2)、watch监视reactive定义的数据(对象监听默认开启深度监听)
注意: 只要reactive直接改造过的对象 :
(1) deep默认是true 并且是无法设置的
(2)无法正确的获取oldValue的值
- <h2>姓名: {{person.name}}h2>
- <h2>年龄: {{person.age}}h2>
- <button @click='person.name += ~'>修改姓名button>
- <button @click='person.age++'>增长年龄button>
- <script>
- import {ref, reactive, watch} from 'vue'
- export default {
- name: 'Demo',
- setup() {
- let person = reactive({
- name: '张三',
- age: 18,
- job: {
- j1: {
- salary: 20
- }
- }
- })
- /*
- 情况三:监视reactive所定义的一个响应式数据的全部数据
- 1.注意:此处无法正确获取odlValue(odlValue每次获取都是最新的数据)
- 2.注意: 强制开启了深度监视(deep配置无效)
- */
- watch(person, (newValue, oldValue) => {
- console.log('person变了', newValue, oldValue);
- }, {immediate: true}) // 此处deep无效
- // 情况四:监视reactive所定义的一个响应式数据中的某个属性(这个能用)
- watch(() => person.name, (newValue, oldValue) => {
- console.log('person的name变了', newValue, oldValue); // 张三~ 张三
- },)
- // 情况五: 监视reactive所定义的一个响应式数据中的某写属性(这个能用)
- watch([() => person.name, () => person.age], (newValue, oldValue) => {
- console.log(newValue, oldValue); // ['张三~', 18] ['张三', 18]
- },)
- //特殊情况 能监听 要开启深度监听
- watch(() => person.job, (newValue, oldValue) => {
- console.log(newValue, oldValue);
- }, {deep: true}) // 这里监听的是通过reactive改造过的一个对象里面的一个属性并且属性的值是一个对像 可以开启深度监听 但是也是拿不到正确是oldValue的值
- // 返回对象
- return {
- person
- }
- }
- }
- script>
3)、watch时value的问题
- <h2>姓名: {{person.name}}h2>
- <h2>年龄: {{person.age}}h2>
- <button @click='person.name += ~'>修改姓名button>
- <button @click='person.age++'>增长年龄button>
- <script>
- import {ref, reactive, watch} from 'vue'
- export default {
- name: 'Demo',
- setup() {
- let sum = ref(0)
- let msg = ref('你好吗')
- let person = reactive({
- name: '张三',
- age: 18,
- job: {
- j1: {
- salary: 20
- }
- }
- })
- // 基本类型
- // 注意为什么监听的不是sum.value呢 因为sum.value是一个值 值为0 你监视个0干嘛
- // 要监听sum sum是一个ref加工过的 是一个对像
- watch(sum, (newValue, oldValue) => {
- console.log('值变化了', newValue, oldValue)
- })
- // 复杂类型
- // 用ref改造的对象 有两种监听方法
- // 方法一:用.value
- watch(person.value, (newValue, oldValue) => {
- console.log('值变化了', newValue, oldValue)
- })
- // 方法二: 不用.value 用deep
- watch(person, (newValue, oldValue) => {
- console.log('值变化了', newValue, oldValue)
- }, {deep: true})
- // 返回对象
- return {
- sum,
- msg,
- person
- }
- }
- }
- script>
1)、watch的套路是:既要指明监视的属性,也要指明监视的回调。
2)、watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,就监视哪个属性。
3)、watchEffect有点像computet:
①但computed注重的计算出来的值(回调的返回值),所以必须写返回值。
②而watchEffect更注重的是过程(回调的函数体),所以不用写返回值。
- <h2>姓名: {{person.name}}h2>
- <h2>年龄: {{person.age}}h2>
- <button @click='person.name += ~'>修改姓名button>
- <button @click='person.age++'>增长年龄button>
- <script>
- import {ref, reactive, watchEffect} from 'vue'
- export default {
- name: 'Demo',
- setup() {
- let sum = ref(0)
- let msg = ref('你好吗')
- let person = reactive({
- name: '张三',
- age: 18,
- job: {
- j1: {
- salary: 20
- }
- }
- })
- watchEffect(() => {
- const x1 = sum.value
- const x2 = person.job.j1.salary
- console.log('z只要sum.value和person.job.j1.salary的值发生变化就执行watchEffect')
- })
- // 返回对象
- return {
- sum,
- msg,
- person
- }
- }
- }
- script>
vue3.0提供了Composition API形式的生命周期钩子
vue2 ==>vue3的生命周期对应
beforeCreate ==> setup() : data 和 methods 中的 数据都还没有初始化
created ==> setup():data 和 methods 都已经被初始化好了
beforeCreate不能写在setup内 因为setup相当于beforeCreate和created生命周期 但是这两个可以写在setup外面
beforeMount ==> onBeforeMount:尚未把模板渲染到 页面中
mounted ==> onMounted:模板渲染到 页面中了
beforeUpdate ==> onBeforeUpdate:data中数据是最新的 页面中显示的数据还是旧的
updated ==> onUpdated:页面中显示的数据和data里面的数据都是最新的
beforeUnmount ==> onBeforeUnmount:页面销毁前
unmounted ==> onUnmounted: 页面销毁后


什么是hook:本质是一个函数,把setup函数中使用的Composition API进行了封装
自定义hook的优势: 复用代码,让setup中的逻辑更清楚易懂
1.拿到复用的代码

2.在src下创建一个hook文件 再创建一个js文件 把复用的代码方法到一个方法里

3.使用

1.
ref和toRef都可以用于创建一个响应式数据,那他们之间有什么区别呢?
利用ref将对象中的属性变成响应式数据,修改响应式数据是不会影响到原始数据。
- import {ref} from 'vue';
- export default {
- name:'App'
- setup(){
- let obj = {name : 'alice', age : 12};
- let newObj= ref(obj.name); // 这里相当于 newObj= ref('alice') 它与obj没有一毛钱关系
- function change(){
- newObj.value = 'Tom';
- console.log(obj,newObj)
- // obj = {name : 'alice', age : 12}
- // newObj = 'Tom'
- }
- return {newObj,change}
- }
- }
上述代码,当change执行的时候,响应式数据发生改变,而原始数据obj并不会改变。
原因在于,ref的本质是拷贝,与原始数据没有引用关系
2.
toRef将对象中的属性变成响应式数据,修改响应式数据是会影响到原始数据的。
修改通过toRef创建的响应式数据,会触发UI界面的更新。所以,toRef的本质是引用,与原始数据有关联
- import {toRef} from 'vue';
- export default {
- name:'App'
- setup(){
- let obj = {name: 'alice', age: 12};
- let newObj= toRef(obj, 'name'); // toRef的本质时让obj.name的引用指向了变量newObj
- function change(){
- newObj.value = 'Tom'; // 由于obj.name的引用地址也指向了newObj所以修改newObj也会修改obj.name的值
- console.log(obj, newObj)
- // obj = {name: 'Tom', age: 12}
- // newObj 等于 'Tom'
- }
- return {newObj,change}
- }
- }
change执行的时候,响应式数据发生改变,原始数据obj里面的属性改变,UI界面会更新
3.使用toRefs(可以批量创建多个ref对象)

总结:
作用: 创建一个ref对象,其value值指向另一个对象中的某个属性
语法: const name = toRef(person, 'name')
应用: 要将响应式对象中的某个属性单独供应给外部使用时
扩展: toRefs与toRef功能一致,但可以批量创建多个ref对象,
语法:toRefs(person)
- <input type ="text" v-model="keyWord">
- <h3>{{keyWord}}h3>
- template>
-
- <script>
- import {ref, customRef} from 'vue'
- export default {
- name: "App",
- setup() {
- // 自定义一个ref名为: myRef
- function myRef(value) {
- let timer // 防抖
- return customRef((track, trigger) => {
- return {
- get() {
- console.log(`有人从myRef这个容器中读取数据了,我把${value}给了它`)
- track() // 通知vue追踪数据的变化(不写这个数据不变化)
- return value
- },
- set(newValue) {
- console.log(`有人从myRef这个容器中数据改为了:${newValue}`)
- clearTimeout(timer); // 防抖
- timer = setTimeout(() => {
- value = newValu; // 更新数据
- trigger() //通知vue去重新解析模板(不写这个不刷新页面)
- }, 1000)
- },
- }
- })
- }
- // let keyWord = ref('hello') // 使用Vue提供的ref
- let keyWord = myRef('hello') // 使用程序员自定义的ref
- }
- }
- script>
App组件
- <div>
- <h3>我是App组件(祖)h3>
- <Child/>
- div>
-
- <script>
- import {customRef, provide} from 'vue'
- import Child from './components/Child.vue'
- export default {
- name: "App",
- components: {Child},
- setup() {
- let car = reacite({name: '奔驰', price: '40W'})
- provide('car', car); // 给自己的后代组件传递数据
- return {...toRefs(car)}
- }
- }
- script>
Child组件
- <div class="child">
- <h3>我Child组件(子组件)h3>
- <Son/>
- div>
-
- <script>
- import {ref, customRef} from 'vue'
- import Son from './Son.vue'
- export default {
- name: "Child",
- components: {Son}
- }
- script>
Son组件
- <div class="son">
- <h3>我Son组件(孙组件)h3>
- div>
-
- <script>
- import {customRef, inject} from 'vue'
- export default {
- name: "Son",
- setup() {
- // 拿到的是一个响应式的数据
- let car = inject('car')
- console.log(car); // proxy {name: '奔驰', price: '40w'}
- }
- }
- script>
使用:
父组件:
import {customRef, provide} from 'vue'
let car = reacite({name: '奔驰', price: '40W'})
provide('car', car); // 给自己的后代组件传递数据
父组件下的子组件都可以接收:
import {customRef, inject} from 'vue'
// 拿到的是一个响应式的数据
let car = inject('car')
console.log(car); // proxy {name: '奔驰', price: '40w'}
1)、isRef: 检查一个值是否为一个ref对象 使用: isRef(变量)
2)、isReactive:检查一个对象是否由reactive创建的响应式代理 使用: isReactive(变量)
3)、isReadonly: 检查一个对象是否由readonly创建的只读代理 使用: isReadonly(变量)
4)、isProxy: 检查一个对象是否由reactive或者readonly方法创建的代理 使用: isProxy(变量)
用法:
1.定义要传送的dom节点

2.teleport的使用以及传送

3.上面的步骤弄完了在atguigu节点下就有了 传送的内容
1、scr文件下创建一个router文件夹 文件夹下创建要给index.js
- // 引入路由对象
- import { createRouter, createWebHistory } from 'vue-router'
- // 路径配置 .vue 不可省略
- const routes = [
- { path: '/', redirect: '/hello'},
- {path: '/hello', name: 'hello', component: () => import('../components/HelloWorld.vue')},
- {path: '/home', name: 'home', component: () => import('../components/home.vue')},
- ]
- const router = createRouter({
- history: createWebHistory(), // 1、createWebHistory 创建history路由模式 2、createWebHashHistory 创建hash路由模式
- routes: routes
- })
- export default router
2.在main.js下引入配置路由的文件并且挂载在实例中
- import { createApp } from 'vue'
- import './style.css'
- import App from './App.vue'
- import router from './router/index.js'
-
- const app = createApp(App).use(router).mount('#app')
3.App.vue写上
-
- <template>
- <router-view>router-view>
- template>
-
- <style scoped>
- style>
4.效果



祝大家能跨过黑暗 向往光明