Vue3笔记——(尚硅谷张天禹Vue笔记)

Vue3

Vue3简介

1.Vue3简介

.2020年9月18日,Vue.js发布3.0版本,代号: One Piece(海贼王)。耗时2年多、2600+次提交、30+个RFC、600+次PR、99位贡献者

. github上的tags地址: https://github.com/vuejs/vue-next/releases/tag/v3.0.0

2.Vue3带来了什么

.性能的提升

.打包大小减少41%

.初次渲染快55%,更新渲染快133%。内存减少54%

3.拥抱TypeScript

. vue3可以更好的支持TypeScript

4.新的特性

1.Composition API(组合API)

. setup配置

. ref与reactive

. watch与watchEffect

. provide与inject

. ...

2.新的内置组件

. Fragment

. Teleport

. suspense

3.其他改变

. 新的生命周期钩子

. data选项应始终被声明为一个函数

. 移除keyCode支持作为v-on的修饰符

. ...

使用vue-cli创建工程

1.使用vue-cli 创建

官方文档: https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create

查看@vue/cli版本,确保@vue/cli版本在4.5.o以上

vue --version

安装或者升级你的@vue/cli

npm install -g @vue/cli

创建

vue create vue_test

启动

cd vue_test

npm run serve

使用vite创建工程

⒉使用vite 创建

官方文档: https://v3.cn.vuejs.org/guide/installation.html#vite

vite官网: https://vitejs.cn

.什么是vite? ------相对于webpack,新一代前端构建工具。

.优势如下:

。开发环境中,无需打包操作,可快速的冷启动。

。轻量快速的热重载(HMR)。

。真正的按需编译,不再等待整个应用编译完成。

##创建工程

npm init vite-app <project-name>

npm init vite-app vue3_test_vite

##进入工程目录

cd <project-name>

##安装依赖

npm install

##运行

npm run dev

分析Vue3的工程结构

分析vue-cli所创建出的vue3工程

关闭语法检查:vue3自带的vue.config.js中的内容直接替换为vue2的写法也行,如下:

module.exports = {

lintOnSave:false,//关闭语法检查

}

App.vue中vue3中template标签中可以没有根标签括起来

main.js

javascript 复制代码
// 引入的不再是Vue构造函数了,引入的是一个名为createApp的工厂函数
import { createApp } from 'vue'
//引入App组件
import App from './App.vue'

// createApp(App).mount('#app')
// 创建应用实例对象--app(类似于之前Vue2中的vm,但app比vm更"轻")
const app = createApp(App)

app.mount('#app')

setTimeout(() => {    
    app.unmount('#app')
}, 1000);

/* 类似vue2中的如下方式的创建vm挂载Vue到app挂载点,但main.js中不兼容vue2的写法,把vue2的main.js拿来替换不能用,因为无法引入Vue
const vm = new VueElement({
    render:h =>h(App)
})
vm.$mount('app') */

vue3的开发者工具

Vue3开发者工具,可在极简插件中下载,选那个vue图标右下角带圆角矩形的

极简插件_Chrome扩展插件商店_优质crx应用下载

别使用谷歌浏览器下载,会自动删除下载的文件

初识setup

是常用Composition API 组合式API

拉开序幕的setup

1.理解: vue3.0中一个新的配置项,值为一个函数。

2.setup是所有Composition API(组合API)"表演的舞台"。

3.组件中所用到的:数据、方法等等,均要配置在setup中。

4.setup函数的两种返回值:

1.若返回一个对象,则对象中的属性、方法,在模板中均可以直接使用。(重点关注! )

2.若返回一个渲染函数:则可以自定义渲染内容。(了解)

5.注意点:

1.尽量不要与Vue2.x配置混用

. Vue2.x配置(data、methos、computed...)中可以访问到setup中的属性、方法。

·但在setup中不能访问到Vue2.x配置(data、methos、computed...)。

·如果有重名, setup优先。

2.setup不能是一个async函数,因为返回值不再是return的对象,而是promise,模板看不到return对象中的属性。(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合)

App.vue

javascript 复制代码
<template>
  <h1>一个人的信息</h1>
  <h2>姓名:{{name}}</h2>
  <h2>年龄:{{age}}</h2>
  <h2>性别:{{sex}}</h2>
  <h2>vue2和vue3的数据a冲突的时候的以vue3的setup为主{{a}}</h2>
  <button @click="sayHello">说话(Vue3配置的sayHello方法)</button><br/><br/>
  <button @click="sayWelcome">说话(Vue2配置的sayWelcome方法)</button><br/><br/>
  <button @click="test1">测试一下在vue2中读取vue3中的数据和方法</button><br/><br/>
  <button @click="test2">测试一下在vue3中读取vue2中的数据和方法</button>
</template>

<script>
import {h} from "vue"
export default {
  name: 'App',
  data() {
    return {
      sex:"男"  ,
      a:100
    }
  },
  methods:{
    sayWelcome(){
      alert('欢迎来到尚硅谷学习'); 
    },
    test1(){//在vue2中读取vue3是可以的
      console.log(this.sex)
      console.log(this.name)
      console.log(this.age)
      console.log(this.sayHello)
    }
  },
  setup(){  // setup是所有Composition API(组合API)"表演的舞台"。组件中所用到的:数据、方法等等,均要配置在setup中。
    //data直接往这里面写 ,变量用let,常量用const
    let name = "张三";
    let age = 18;
    let a = 200;
    //方法也直接写function
    function sayHello(){
      alert(`我叫${name},我${age}岁了,你好啊`)//使用``加${}传数据
    };

    function test2(){//在vue3中读取vue2,读取不到
      console.log(this.name)
      console.log(this.age)
      console.log(this.sex)  // undefined
      console.log(this.sayWelcome)  // undefined
    }

    //setup的return若返回一个对象,则对象中的属性、方法,在模板中均可以直接使用。(重点关注! )
    return{
      name,
      age,
      sayHello,
      test2,
      a,
    }
    // 若返回一个渲染函数:则可以自定义渲染内容。(了解)  会覆盖掉template中的所有内容
    // return()=>{ return h("h1","尚硅谷")}
    /* return()=> h("h1","尚硅谷") */

  }
}
</script>

ref函数处理基本数据类型

除了html中标签中ref属性,vue3多了一个ref函数。

ref加工后的数据就是一个对象RefImpl,reference implement引用实现,引用实现的对象简称引用对象。里面有个value属性,为实现数据响应式的,通过还是get和set做的响应式在__proto__原型对象中的。像vue2中的_data中的东西放在了vm上。

App.vue

javascript 复制代码
<template>
  <h1>一个人的信息</h1>
  <!-- ref引用对象,在模板中不用name.value,模板自动会解析加上.value -->
  <h2>姓名:{{name}}</h2>
  <h2>年龄:{{age}}</h2>
  <button @click="changeInfo">修改人的信息</button>
</template>

<script>
import {h} from "vue"
import {ref} from 'vue'
export default {
  name: 'App',
  setup(){
    /* let name = "张三";
    let age = 18; */
    // 把普通数据变成一个响应式的数据
    let name = ref("张三")
    let age = ref(18)

    function changeInfo(){
      /* name = '李四'
      age = 48 */
      // 由于name和age为ref函数得到的引用对象,通过get和set做的响应式,所以我们要通过.vaue
      name.value = '李四'
      age.value = 48
    }

    return{
      name,
      age,
      changeInfo
    }


  }
}
</script>

ref函数处理对象类型的数据

对于ref函数处理的对象类型,没有直接变为RefIMpl应用对象,而是变为了Proxy代理对象。是内部求助的reactive函数,这个函数封装了proxy。

App.vue

javascript 复制代码
<template>
  <h1>一个人的信息</h1>
  <!-- ref引用对象,在模板中不用name.value,模板自动会解析加上.value -->
  <h2>姓名:{{name}}</h2>
  <h2>年龄:{{age}}</h2>
  <h3>手机品牌:{{phone.brand}}</h3>
  <h3>手机颜色:{{phone.color}}</h3>
  <button @click="changeInfo">修改人的信息</button>
</template>

<script>
import {h} from "vue"
import {ref} from 'vue'
export default {
  name: 'App',
  setup(){
    /* let name = "张三";
    let age = 18; */
    // 把普通数据变成一个响应式的数据
    let name = ref("张三")
    let age = ref(18)
    // ref函数处理对象类型
    let phone = ref({
      brand:"redmi", 
      color:"red"
    })

    function changeInfo(){
      /* name = '李四'
      age = 48 */
      // 由于name和age为ref函数得到的引用对象,通过get和set做的响应式,所以我们要通过.vaue
      name.value = '李四'
      age.value = 48
      phone.value.brand="xiaomi" //ref函数对象类型的属性修改数据
      phone.value.color="green"
    }

    return{
      name,
      age,
      changeInfo,
      phone
    }


  }
}
</script>

reactive函数

reactive函数

。作用:定义一个对象类型的响应式数据(基本类型不要用它,要用ref函数)

。语法: const 代理对象= reactive(源对象)接收一个对象(或数组),返回一个代理对象〈Proxy的实例对象,简称proxy对象)

。 reactive定义的响应式数据是"深层次的"。

。内部基于ES6的Proxy实现,通过代理对象操作源对象内部数据进行操作。

App.vue

javascript 复制代码
<template>
  <h1>一个人的信息</h1>
  <!-- ref引用对象,在模板中不用name.value,模板自动会解析加上.value -->
  <h2>姓名:{{name}}</h2>
  <h2>年龄:{{age}}</h2>
  <h3>手机品牌:{{phone.brand}}</h3>
  <h3>手机颜色:{{phone.color}}</h3>
  <h3>hobby:{{hobby}}</h3>
  <h3>测试的数据d:{{a.b.c.d}}</h3>
  <button @click="changeInfo">修改数据</button>
</template>

<script>
import {h} from "vue"
import {ref,reactive} from 'vue'
export default {
  name: 'App',
  setup(){
    // 把普通数据变成一个响应式的数据
    let name = ref("张三")
    let age = ref(18)
    // reactive将对象变为响应式的
    let phone = reactive({
      brand:"redmi", 
      color:"red"
    })
    let a = reactive({
      b:{
        c:{
          d:666
        }
      }
    })
    let hobby = reactive(['看喜羊羊','看灰太狼','看光头强'])

    function changeInfo(){
      name.value = '李四'
      age.value = 48
      phone.brand="xiaomi" //active函数对象类型的属性修改数据
      phone.color="green"
      a.b.c.d=999
      hobby[0] = '看熊出没'
    }

    return{
      name,
      age,
      changeInfo,
      phone,
      a,
      hobby
    }


  }
}
</script>

Vue2的响应式原理的回顾

vue2.x的响应式

. 实现原理:

。对象类型:通过object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。

。数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。

object.defineProperty(data, 'count ', {

get(){},

set(){}

})

. 存在问题:

。新增属性、删除属性,界面不会更新。

。直接通过下标修改数组,界面不会自动更新。

Vue2项目中的App.vue,来看vue2的响应式存在的问题

javascript 复制代码
<template>
  <div>
      <h1>我是Vue2的写法</h1>
      <h2 v-show="person.name">姓名:{{person.name}}</h2>
      <h2>年龄:{{person.age}}</h2>
      <h2>爱好:{{person.hobby}}</h2>
      <h2 v-show="person.sex">性别:{{person.sex}}</h2>
      <button @click="addSex">添加一个sex属性</button>
      <button @click="deleteName">添加一个name属性</button>
      <button @click="updateHobby">修改第一个爱好的名字</button>
  </div>
</template>
<script>
import Vue from 'vue'
export default {
  name:"App",
  data() {
    return {
      person:{
        name:'张三',
        age:18,
        hobby:["看喜羊羊",'看灰太狼','看光头强']
      }
    }
  },
  methods:{
    addSex(){
      /* console.log(this.person.sex) //undefined
      this.person.sex='女'
      console.log(this.person.sex) //'女'  确实新增了sex属性了,但页面不更新,defineProperties监测不到 */
      this.$set(this.person,"sex",'女') //可通过这种和下面这种方式加上这个属性 
      // Vue.set(this.person,"sex",'女')
    },
    deleteName(){
      /* console.log(this.person.name) // '张三'
      delete this.person.name
      console.log(this.person.name)// undefined 确实删除了name,但页面中监测不到,页面还是不变 */
      //this.$delete(this.person,"name")
      Vue.delete(this.person,"name")
    },
     updateHobby(){
      // this.person.hobby[0] = '看大大怪' // 这样修改确实能修改,但页面监测不到,页面上的数据不变
      //this.$set(this.person.hobby,0,"看大大怪")
      // 或使用数组的方法,如push shift splice等,这些方法为vue对普通方法的封装,可以监测到
      this.person.hobby.splice(0,1,'看小小怪')
     }
  }
}
</script>

Vue3的响应式原理Proxy

Vue3中不存在vue2中的添加或属性或修改数组元素的不是响应式的页面不变的问题

Vue3中的App.vue

javascript 复制代码
<template>
  <h1>一个人的信息</h1>
  <!-- ref引用对象,在模板中不用name.value,模板自动会解析加上.value -->
  <h2 v-show="person.name">姓名:{{person.name}}</h2>
  <h2>年龄:{{person.age}}</h2>
  <h2 v-show="person.sex">性别:{{person.sex}}</h2>
  <h3>手机品牌:{{person.phone.brand}}</h3>
  <h3>手机颜色:{{person.phone.color}}</h3>
  <h3>hobby:{{person.hobby}}</h3>
  <button @click="changeInfo">修改数据</button>
  <button @click="addSex">添加一个sex属性</button>
  <button @click="deleteName">删除一个name属性</button>

</template>

<script>
import {ref,reactive} from 'vue'
export default {
  name: 'App',
  setup(){
    let person = reactive({
      name:"张三",
      age:18,
      phone:{
        brand:"redmi", 
        color:"red"
      },
      hobby:['看喜羊羊','看灰太狼','看光头强']
    })

    function changeInfo(){
      person.name = '李四'
      person.age = 48
      person.phone.brand="xiaomi" //active函数对象类型的属性修改数据
      person.phone.color="green"
      person.hobby[0] = '看熊出没'   //直接通过数组下标修改元素也是可以响应式的,页面也会发生变化
    }
    function addSex(){
      person.sex = '男' // 添加一个不存在的属性,页面可以发生变化,做了响应式,reactive的原因。
    }
    function deleteName(){
      delete person.name
    }

    return{
      person,
      changeInfo,
      addSex,
      deleteName
    }


  }
}
</script>

Object.defineProperties、new Proxy()模拟vue2和vue3的响应式原理

响应式原理.html

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script type="text/javascript">
        //源数据
        let person = {
            name : "张三",
            age : 18
        }
        //模拟vue2中实现响应式
        //#region
        /* let p = {}
        Object.defineProperties(p,"name",{
            configurable:true, //声明为可配置的,就允许被删除了
            get(){ //有人读取的时候调用
                return person.name
            },
            set(value){
                console.log("有人修改了name属性,发现了,要去更新界面!")
                person.name = value
            }
        })
        Object.defineProperties(p,"age",{
            get(){ //有人读取的时候调用
                return person.age
            },
            set(value){
                console.log("有人修改了age属性,发现了,要去更新界面!")
                person.age = value
            }
        })
        // 然后再f12控制台中,person.name = '李四' person.sex = '男' delete person.name 之类操作查看结果 */
        //#endregion


        //模拟vue3中实现响应式
        const p = new Proxy(person,{ //就会进行代理,person中的属性都会有get和set了,下面代码可以里面的api做响应式
            //有人读取p的某个属性式调用
            get(target,propName){
                console.log(`有人读取了p身上的${propName}属性`)
                return target[propName]
            },
            // 有人修改了p的某个属性,或给p追加某个属性时调用
            set(target,propName,value){
                console.log(`有人修改了p身上的${propName}属性,我要去更新页面了!`)
                target[propName] = value
            },
            //有人删除p的某个属性时调用
            deleteProperty(target,propName){
                console.log(`有人删除了p身上的${propName}属性,我要去更新页面了!`)
                return delete target[propName]
            }

        })
        // f12中person.name='李四'等操作即可
    </script>
</body>
</html>

Vue3响应式原理Reflect

Reflect反射的方式,容错性更好,我们修改上面的Vue3的模拟中的操作

Vue3的响应式.html

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script type="text/javascript">
        //源数据
        let person = {
            name : "张三",
            age : 18
        }
        //模拟vue2中实现响应式
        //#region
        /* let p = {}
        Object.defineProperties(p,"name",{
            configurable:true, //声明为可配置的,就允许被删除了
            get(){ //有人读取的时候调用
                return person.name
            },
            set(value){
                console.log("有人修改了name属性,发现了,要去更新界面!")
                person.name = value
            }
        })
        Object.defineProperties(p,"age",{
            get(){ //有人读取的时候调用
                return person.age
            },
            set(value){
                console.log("有人修改了age属性,发现了,要去更新界面!")
                person.age = value
            }
        })
        // 然后再f12控制台中,person.name = '李四' person.sex = '男' delete person.name 之类操作查看结果 */
        //#endregion


        //模拟vue3中实现响应式
        //#region
        const p = new Proxy(person,{ //就会进行代理,person中的属性都会有get和set了,下面代码可以里面的api做响应式
            //有人读取p的某个属性式调用
            get(target,propName){
                console.log(`有人读取了p身上的${propName}属性`)
                // return target[propName]
                return Reflect.get(target,propName)
            },
            // 有人修改了p的某个属性,或给p追加某个属性时调用
            set(target,propName,value){
                console.log(`有人修改了p身上的${propName}属性,我要去更新页面了!`)
                // target[propName] = value
                Reflect.set(target,propName,value)
            },
            //有人删除p的某个属性时调用
            deleteProperty(target,propName){
                console.log(`有人删除了p身上的${propName}属性,我要去更新页面了!`)
                // return delete target[propName]
                return Reflect.deleteProperty(target,propName)
            }
        })
        //#endregion

        //#region
        /* //Reflect.definedProperty反射对象去操作
        let obj = {a:1,b:2}
        //除了obj.a = 666 还能Reflect.set(obj,'a',666)的方式修改a Reflect.deleteProperty(obj,'a')的方式删除a
        //通过Object.defineProperty去操作
        Object.defineProperty(obj,'c',{
            get(){
                return 3
            }
        })
        Object.defineProperty(obj,'c',{
            get(){
                return 4
            }
        })
        //如上Object.defineProperty对obj定义追加了c重复了,就会报错,这样容错性不太好,需要try-catch包裹起来
        //但使用如下Reflect.definedProperty反射对象去操作后,重复后就不会报错
        const x1 = Reflect.defineProperty(obj,'c',{
            get(){
                return 3
            }
        })
        const x2 = Reflect.defineProperty(obj,'c',{
            get(){
                return 4
            }
        })
        //可通过if-else判断
        if(x2){
            console.log("某某操作成功了")
        }else{
            console.log("某某操作失败了")
        } */
        //#endregion

    </script>
</body>
</html>

实现原理:

。通过Proxy(代理)︰拦截对象中任意属性的变化,包括:属性值的读写、属性的添加、属性的删除等。

。通过Reflect(反射)︰对源对象的属性进行操作。

。MDN文档中描述的Proxy与Reflect:

.Proxy: https:/ldeveloper.mozilla.org/zh-CN/docs/WeblJavaScript/Reference/Global_Objects/Proxy

.Reflect: https:/ldeveloper.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect

reactive和ref的对比

. 从定义数据角度对比:

。ref用来定义:基本类型数据。

。 reactive用来定义:对象(或数组)类型数据。

。备注: ref也可以用来定义对象(或数组)类型数据,它内部会自动通过reactive转为代理对象。

. 从原理角度对比:

。ref通过 object.definePropertv()的get与set来实现响应式(数据劫持)。

。reactive通过使用Proxy来实现响应式(数据劫持)﹐并通过Reflect操作源对象内部的数据。

. 从使用角度对比:

。ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value 。

。reactive定义的数据:操作数据与读取数据:均不需要.value。

setup的两个注意点

setup的两个注意点

. setup执行的时机

。在beforeCreate之前执行一次,this是undefined。

. setup的参数

。props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。

。context: 上下文对象

` attrs:值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性,相当于[this .$attrs 。

` slots: 收到的插槽内容,相当于this.$slots 。

` emit:分发自定义事件的函数,相当于this.emit 。 vue2中的props传参,父组件写的参数,子组件props:\[\]没声明的参数,就会被atts捡漏。vue2中slots父组件中写的额插槽的内容,会放在$slot中作为虚拟节点。

set第一个参数需要props声明,第二个参数用到的emit需要在上面声明给emits

vue3中使用具名插槽尽量用v-slot

App.vue

javascript 复制代码
<template>
  <Demo msg='你好' school='尚硅谷' @hello='showHelloMsg'>
    <span>尚硅谷</span>
  </Demo>
</template>

<script>
import Demo from "./components/Demo.vue"
export default {
  name: 'App',
  components:{
    Demo
  },
  setup(){
  function showHelloMsg(value){
    alert(`你好啊,你触发了hello事件,我收到的参数是:${value}`)
  }
  return {
    showHelloMsg
  }
  }
}
</script>

Demo.vue

javascript 复制代码
<template>
  <h1>一个人的信息</h1>
  <h2>姓名:{{person.name}}</h2>
  <h2>年龄:{{person.age}}</h2>
  <button @click="test">测试触发一下Demo组件的Hello事件</button>
</template>

<script>
import { reactive } from 'vue'
export default {
    name:'Demo',
    props:['msg','school'],
    emits:['hello'],
    setup(props,context) {
        // console.log("-----setup-----",props)
        // console.log("---setup---",context)
        // console.log("---setup---",context.attrs)//相当于vue2中的$attrs
        // console.log("---setup---",context.emit) // 触发自定义事件的
        console.log("---setup---",context.slots) //插槽
        //数据
        let person = reactive({
            name:'张三',
            age:18
        })

        function test(){
            context.emit('hello',666)
        }

        return {
            person,
            test
        }
    } 
}
</script>

<style>

</style>

Vue3computed计算属性

App.vue

javascript 复制代码
<template>
  <Demo/>
</template>

<script>
import Demo from "./components/Demo.vue"
export default {
  name: 'App',
  components:{
    Demo
  },
}
</script>

Demo.vue

javascript 复制代码
<template>
  <h1>一个人的信息</h1>
  姓:<input type="text" v-model="person.firstName">
  <br/>
  名:<input type="text" v-model="person.lastName">
  <br/>
  <!-- <span>全名:{{fullName}}</span> -->
  <span>全名:{{person.fullName}}</span>
  <br/>
  全名:<input type="text" v-model="person.fullName">
</template>

<script>
import { reactive ,computed} from 'vue'
export default {
    name:'Demo',
    /* computed:{ // 在vue3中是可以像vue2中写计算属性的。但不推荐。
        fullName(){
            return this.person.firstName + '-' + this.person.lastName
        }
    }, */
    setup() {
        let person = reactive({
            firstName:'张',
            lastName:"三"
        })
        // 计算属性-简写(没有考虑计算属性被修改的情况)
        /* let fullName = computed(()=>{
            return person.firstName + "-" + person.lastName
        }) */
        /* person.fullName = computed(()=>{
            return person.firstName + "-" + person.lastName
        }) */
        // 计算属性的完整写法(考虑读和写)
        person.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>

<style>

</style>

watch监视ref定义的数据

Demo.vue

javascript 复制代码
<template>
  <h2>当前求和为:{{sum}}</h2>
  <button @click="sum++">点我+1</button>
  <hr>
  <h2>当前的信息为:{{msg}}</h2>
  <button @click="msg+='!'">修改信息</button>
</template>

<script>
import { ref, watch } from 'vue'
export default {
    name:'Demo',
    /* watch:{// vue3中也可使用vue2中的watch,不过不推荐
        sum(newValue,oldValue){//简写的方式,详细写法见vue2中的watch
            console.log('sum的值变化了',newValue,oldValue)
        }
    }, */
    setup() {
        let sum = ref(0)
        let msg = ref('你好啊')

        //情况1,监视ref所定义的一个响应式数据
        /* watch(sum,(newValue,oldValue)=>{
            console.log("sum变化了",newValue,oldValue)
        }) */

        //情况2,监视ref所定义的多个响应式数据  可写多个watch在vue3中
        /* watch(sum,(newValue,oldValue)=>{
            console.log("sum变化了",newValue,oldValue)
        })
        watch(msg,(newValue,oldValue)=>{
            console.log("msg变化了",newValue,oldValue)
        }) */
        /* watch([sum,msg],(newValue,oldValue)=>{ // newValue,oldValue分别为数组
            console.log("sum或msg变化了",newValue,oldValue)
        }) */

        watch(sum,(newValue,oldValue)=>{
            console.log("sum变化了",newValue,oldValue)
        },{immediate:true})//可加第三个参数为配置 但deep:true在vue3中有小问题

        return {
            sum,
            msg
        }
    } 
}
</script>

<style>

</style>

App.vue中引用注册并渲染这个Demo.vue

watch监视reactive定义的数据

watch函数

。与Vue2.x中watch配置功能一致。

两个小"坑":

。监视reactive定义的响应式数据时: oldValue无法正确获取、强制开启了深度监视(deep配置失效)。

。监视reactive定义的响应式数据中某个属性时: deep配置有效。

Demo.vue

javascript 复制代码
<template>
  <h2>姓名:{{person.name}}</h2>
  <h2>年龄:{{person.age}}</h2>
  <h2>薪水:{{person.job.j1.salary}}</h2>
  <button @click="person.name+='~'">修改姓名</button>
  <button @click="person.age++">增长年龄</button>
  <button @click="person.job.j1.salary++">增长薪资</button>
</template>

<script>
import { reactive, ref, watch } from 'vue'
export default {
    name:'Demo',
    setup() {
        let person = reactive({
            name:'张三',
            age:18,
            job:{
                j1:{
                    salary:20
                }
            }
        })
        
        //情况三:监视reactive所定义的一个响应式数据的全部属性,注意,此处无法正确的获取oldValue,这是vue3的小bug
        watch(person,(newValue,oldValue)=>{   //vue3中默认就开启了深度监视了,所以salary修改也能显示,且关不掉这个深度监视
            console.log('person发生变化了',newValue,oldValue);
        })
        
        //情况四:监视reactive所定义的一个响应式数据中的某个属性
        watch(()=>person.age,(newValue,oldValue)=>{   //vue3中默认就开启了深度监视了,所以salary修改也能显示,且关不掉这个深度监视
            console.log('person的age发生变化了',newValue,oldValue);
        })

        //情况五:监视reactive所定义的一个响应式数据中的某些属性
        watch([()=>person.age,()=>person.name],(newValue,oldValue)=>{   //vue3中默认就开启了深度监视了,所以salary修改也能显示,且关不掉这个深度监视
            console.log('person的age或name发生变化了',newValue,oldValue);
        })

        //特殊情况  ()=>person.job 如果改job下的子属性,需要开启深度监视deep:true
        watch(()=>person.job,(newValue,oldValue)=>{   //vue3中默认就开启了深度监视了,所以salary修改也能显示,且关不掉这个深度监视
            console.log('person的job发生变化了',newValue,oldValue);
        },{deep:true})


        return {
            person
        }
    } 
}
</script>

<style>

</style>

App.vue中引用注册并渲染这个Demo.vue

watch时的value问题

ref()对基本类型,.value就是基本类型,但ref对对象类型,.value的是proxy代理对象。

所以watch为ref普通属性直接写,但watch为ref的对象时,修改person里面的属性,用person.value,还可在后面加上deep:true属性

watchEffect函数

watchEffect函数

. watch的套路是:既要指明监视的属性,也要指明监视的回调。

. watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。

. watchEffect有点像computed:

。但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。

。而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。

Demo.vue

javascript 复制代码
<template>
  <h2>当前求和为:{{sum}}</h2>
  <button @click="sum++">点我+1</button>
  <hr>
  <h2>当前的信息为:{{msg}}</h2>
  <button @click="msg+='!'">修改信息</button>
  <hr>
    <h2>姓名:{{person.name}}</h2>
  <h2>年龄:{{person.age}}</h2>
  <h2>薪水:{{person.job.j1.salary}}</h2>
  <button @click="person.name+='~'">修改姓名</button>
  <button @click="person.age++">增长年龄</button>
  <button @click="person.job.j1.salary++">增长薪资</button>
</template>

<script>
import { ref, reactive, watch, 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
                }
            }
        })
        /* watch(sum,(newValue,oldValue)=>{
            console.log("sum变化了",newValue,oldValue)
        },{immediate:true}) */

        watchEffect(()=>{//里面用到了哪些属性就监视谁,而且还默认第一次就加载
            const x1  = sum.value
            const x2 = person.job.j1.salary
            console.log("watchEffect所指定的回调执行了");
        })

        return {
            sum,
            msg,
            person
        }
    } 
}
</script>

<style>

</style>

App.vue中引用注册并渲染这个Demo.vue

Vue3生命周期钩子

vue2生命周期钩子前6个都一样,最后beforeDestroy和destroyed与vue3的beforeUnmount和unmounted不一样.vue2中即使没写el,也能执行beforeCreate和created这两步,但vue3得都有才能开始执行beforeCreate。vue3也能通过配置项的形式引用生命周期钩子,也可通过组合式api的形式去使用生命周期钩子
Vue3.0也提供了Composition API形式的生命周期钩子,与Vue2.x中钩子对应关系如下:

。beforeCreate ===> setup()

。created =======> setup()

。beforeMount =======> onBeforeMount

。mounted =======> onMounted

。beforeUpdate ===> onBeforeUpdate

。updated =======> onUpdated

。beforeUnmount ==> onBeforeUnmount

。unmounted =====> onUnmounted

Demo.vue

html 复制代码
<template>
  <h2>当前求和为:{{sum}}</h2>
  <button @click="sum++">点我+1</button>
</template>

<script>
import { ref,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from 'vue'
export default {
    name:'Demo',
    setup() {
        let sum = ref(0)
        
        //通过组合式api的形式去使用生命周期钩子 优先级setup的高于配置项的
        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 {
            sum,
        }
    },
    //#region 
    // 通过配置项的形式引用生命周期钩子
/*     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")
    },  */
    //#endregion
}
</script>

<style>

</style>

App.vue

html 复制代码
<template>
  <button @click="isShowDemo = !isShowDemo">切换隐场显示</button>
  <Demo v-if="isShowDemo"/>
</template>

<script>
import { ref } from 'vue'
import Demo from "./components/Demo.vue"
export default {
  name: 'App',
  components:{
    Demo
  },
  setup(){
    let isShowDemo = ref(true)
    return {
      isShowDemo
    }
  },
}
</script>

自定义hook

.什么是hook?------本质是一个函数,把setup函数中使用的Composition APlI进行了封装。

·类似于vue2.x中的mixin。

·自定义hook的优势:复用代码,让setup中的逻辑更清楚易懂。
本来在Demo.vue中写的鼠标打点的方法,但可能其他组件也需要使用改鼠标打点的功能,要么将这段代码复制粘贴,或者我们可以在src下建一个hooks文件夹,创建userPoint.js文件,然后在这里写那些代码,然后返回出去,然后Demo.vue中再引入

usePoint.js

javascript 复制代码
import {reactive,onMounted, onBeforeUnmount} from 'vue'

// function savePoint(){
export default function (){

    //实现鼠标打点的数据
    let point = reactive({
        x:0,
        y:0
    })

    //实现鼠标打点的方法
    function savePoint(event){
            point.x = event.pageX
            point.y = event.pageY
            console.log(event.pageX,event.pageY);
    }

    //实现鼠标打点相关的生命周期钩子
    onMounted(()=>{
        window.addEventListener("click",savePoint)
    })

    onBeforeUnmount(()=>{
        window.removeEventListener("click",savePoint)
    })

    return point
}

// export default savePoint

Demo.vue

javascript 复制代码
<template>
  <h2>当前求和为:{{sum}}</h2>
  <button @click="sum++">点我+1</button>
  <hr>
  <h2>当前点击时鼠标的坐标为:x:{{point.x}},y:{{point.y}}</h2>
</template>

<script>
import usePoint from '../hooks/usePoint'
import { ref,reactive,onMounted, onBeforeUnmount} from 'vue'
export default {
    name:'Demo',
    setup() {
        let sum = ref(0)


        /* let point = reactive({
            x:0,
            y:0
        })

        function savePoint(event){
                point.x = event.pageX
                point.y = event.pageY
                console.log(event.pageX,event.pageY);
        }

        onMounted(()=>{
            window.addEventListener("click",savePoint)
        })

        onBeforeUnmount(()=>{
            window.removeEventListener("click",savePoint)
        }) */
        

        //然后复用
        let point = usePoint()

        return {
            sum,point
        }
    },
    
}
</script>

<style>

</style>

toRef与toRefs

toRef:将一个不是ref的变成ref

. 作用:创建一个ref对象,其value值指向另一个对象中的某个属性。

· 语法:const name = toRef(person , ' name ')

. 应用:要将响应式对象中的某个属性单独提供给外部使用时。

. 扩展: toRefs 与toRef功能一致,但可以批量创建多个ref对象,语法: toRefs(person)

Demo.vue

javascript 复制代码
<template>
  <h2>姓名:{{name}}</h2>
  <h2>年龄:{{age}}</h2>
  <h2>薪水:{{job.j1.salary}}</h2>
  <button @click="name+='~'">修改姓名</button>
  <button @click="person.age++">增长年龄</button>
  <button @click="job.j1.salary++">增长薪资</button>
</template>

<script>
import {reactive, toRef, toRefs} from 'vue'
export default {
    name:'Demo',
    setup() {
        let person = reactive({
            name:'张三',
            age:18,
            job:{
                j1:{
                    salary:20
                }
            }
        })

        /* const name1 = person.name
        console.log('%%%%%%',name1);
        const name2 = toRef(person,'name')  // 这里并不是又复制了一份,而是这里读取的指向的还是上面的let person里面的
        console.log('@@@@@',name2);  */

        const x = toRefs(person)  //将person中所有属性变成ref引用对象
        console.log(x);
        
        return {
            /* person,
            name:toRef(person,'name'),
            age:toRef(person,'age'),
            salary:toRef(person.job.j1,'salary'), */
            //name:ref(person.name)  //用ref的话,之后修改的就不是上面的person中的name了,而是新的ref,所以不能这么写

            ...toRefs(person)
        }
    } 
}
</script>

<style>

</style>

App.vue中引入即可

其他组合式API

不常用

shallowReactive与shallowRef

App.vue中导入注册渲染Demo.vue. shallowReactive:只处理对象最外层属性的响应式(浅响应式)。

. shallowRef:只处理基本数据类型的响应式,不进行对象的响应式处理。

. 什么时候使用?

。如果有一个对象数据,结构比较深,但变化时只是外层属性变化===> shallowReactive。

。如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换===>shallowRef。

Demo.vue

javascript 复制代码
<template>
    <h4>当前x的值是:{{x.y}}</h4>
    <button @click="x.y++">点我x+1</button>
  <h2>姓名:{{name}}</h2>
  <h2>年龄:{{age}}</h2>
  <h2>薪水:{{job.j1.salary}}</h2>
  <button @click="name+='~'">修改姓名</button>
  <button @click="person.age++">增长年龄</button>
  <button @click="job.j1.salary++">增长薪资</button>
</template>

<script>
import {ref,reactive, toRef, toRefs,shallowReactive,shallowRef} from 'vue'
export default {
    name:'Demo',
    setup() {
        //let person = shallowReactive({   //浅层次的reactive,只有第一层可响应式,即下面的name和age有响应式,但salary没有响应式
        let person = reactive({
            name:'张三',
            age:18,
            job:{
                j1:{
                    salary:20
                }
            }
        })
        // let x = shallowRef({ //shallowRef和ref区别为,当值为对象时,shallowRef只能修改一层,如下x的y属性就没有响应式
        let x = ref({
            y:0
        })
        return {
            ...toRefs(person),
            x
        }
    } 
}
</script>

<style>

</style>

readOnly与shallowReadOny

readonly:让一个响应式数据变为只读的(深只读)。

shallowReadonly:让一个响应式数据变为只读的(浅只读)

应用场景:不希望数据被修改时。

Demo.vue

javascript 复制代码
<template>
    <h4>当前求和为:{{sum}}</h4>
    <button @click="sum++">点我+1</button>
  <h2>姓名:{{name}}</h2>
  <h2>年龄:{{age}}</h2>
  <h2>薪水:{{job.j1.salary}}</h2>
  <button @click="name+='~'">修改姓名</button>
  <button @click="age++">增长年龄</button>
  <button @click="job.j1.salary++">增长薪资</button>
</template>

<script>
import {ref,reactive, toRef, toRefs,shallowReactive,shallowRef, readonly, shallowReadonly} from 'vue'
export default {
    name:'Demo',
    setup() {
        let person = reactive({
            name:'张三',
            age:18,
            job:{
                j1:{
                    salary:20
                }
            }
        })
        let sum = ref(0)

        // person = readonly(person) //不让任何人修改这个数据,就可以用readonly,并且用person接收覆盖了原来的person
        person = shallowReadonly(person) //底有第一层年龄和姓名改不了,但薪资可改的

        // sum = readonly(sum)
        // sum = shallowReadonly(sum)


        return {
            ...toRefs(person),
            sum
        }
    } 
}
</script>

<style>

</style>

toRaw与markRaw

. toRaw:

。作用:将一个由reactive生成的响应式对象转为普通对象。

。使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新.

. markRaw":

。作用:标记一个对象,使其永远不会再成为响应式对象。

。应用场景:

1.有些值不应被设置为响应式的,例如复杂的第三方类库等。

2.当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。

Demo.vue

javascript 复制代码
<template>
    <h4>当前求和为:{{sum}}</h4>
    <button @click="sum++">点我+1</button>
  <h2>姓名:{{name}}</h2>
  <h2>年龄:{{age}}</h2>
  <h2>薪水:{{job.j1.salary}}</h2>
  <h2>手机:{{person.phone}}</h2>
  <button @click="name+='~'">修改姓名</button>
  <button @click="age++">增长年龄</button>
  <button @click="job.j1.salary++">增长薪资</button>
  <button @click="showRawPerson">输出最原始的person</button>
  <button @click="addPhone">给人添加一个手机</button>
  <button @click="person.phone.brand+='!'">换手机品牌</button>
  <button @click="person.phone.color+='@'">换手机颜色</button>
</template>

<script>
import {markRaw,ref,reactive, toRef, toRefs,shallowReactive,shallowRef, toRaw} from 'vue'
export default {
    name:'Demo',
    setup() {
        let person = reactive({
            name:'张三',
            age:18,
            job:{
                j1:{
                    salary:20
                }
            }
        })
        let sum = ref(0)

        function showRawPerson(){
            const p  = toRaw(person)  // 将将一个由reactive生成的响应式对象转为普通对象。ref的行不通。
            console.log(person);
            console.log(p);
        }
 
        function addPhone(){
            /* let phone = {brand:"小米",color:"red"}
            person.phone = phone //追加的内容为响应式的 */

            let phone = {brand:"小米",color:"red"}
            person.phone = markRaw(phone) //使用markRaw标记后这个数据永远不做响应式了,修改时数据确实改了,但页面不变化
        }

        return {
            ...toRefs(person),
            sum,
            person,//把person也交出去,后期添加person属性可直接用person.的方式获取值
            showRawPerson,
            addPhone
        }
    } 
}
</script>

<style>

</style>

App.vue中引用Demo.vue

customRef自定义ref

作用:创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显式控制。

通过一个防抖的案例:即输入框中输入内容,显示的h4标签过1秒再显示。

App.vue

javascript 复制代码
<template>
  <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,delay){
      let timer;
      return customRef((track,trigger)=>{ //要返回一个对象
        return{
          get(){
            console.log(`有人从myRef这个容器中读取数据了,我把${value}给他了`);
            track()  // 2 通知vue追踪value的变化,(提前和get商量一下,让他认为这个value是有用的)
            return value
          },
          set(newValue){
            console.log(`有人把myRef这个容器中数据改为了${newValue}`);
            clearTimeout(timer)  //防抖
            //value = newValue
            // trigger()  // 1 通知vue去重新解析模板
            timer = setTimeout(() => {
            value = newValue
              trigger()  // 1 通知vue去重新解析模板
            }, delay);
          }
        }
      })
    }
    // let keyWord = ref("hello")// 使用vue提供的ref
    let keyWord = myRef("hello",500)// 使用vue提供的ref

    return {
      keyWord
    }
  },
}
</script>

provide与inject

。作用:实现祖与后代组件间通信

。套路:父组件有一个provide选项来提供数据,后代组件有一个inject选项来开始使用这些数据

App.vue

javascript 复制代码
<template>
  <div class="app">
    <h3>我是App组件(祖)</h3>
    <Child/>
  </div>
</template>

<script>
import { provide, reactive, toRefs } from 'vue'
import Child from "./components/Child.vue"
export default {
  name: 'App', 
  components:{
    Child
  },
  setup(){
    let phone = reactive({brand:"小米",color:"red"})
    provide('phone',phone)
    return {...toRefs(phone)}
  }
}
</script>
<style>
.app{
  background-color: grey;
  padding: 10px;
}
</style>

Child.vue

javascript 复制代码
<template>
  <div class="child">
    <h3>我是Child组件(子),{{phone.brand}}--{{phone.color}}</h3>
    <Sun/>
  </div>
</template>

<script>
import { inject } from 'vue'
import Sun from './Sun.vue'
export default {
  name: 'Child', 
  components:{
    Sun
  },
  setup(){
    let phone = inject('phone')
    return{phone}
  }
}
</script>
<style>
.child{
  background-color: skyblue;
  padding: 10px;
}
</style>

Sun.vue

javascript 复制代码
<template>
  <div class="sun">
    <h3>我是Sun组件(孙),{{phone.brand}}--{{phone.color}}</h3>
  </div>
</template>

<script>
import { inject } from 'vue'
export default {
  name: 'Sun', 
  setup(){
    let phone = inject('phone')
    return{phone}
  }
}
</script>
<style>
.sun{
  background-color: orange;
  padding: 10px;
}
</style>

响应式数据的判断

响应式数据的判断API

isRef:检查一个值是否为一个ref 对象

isReactive:检查一个对象是否是由reactive创建的响应式代理

isReadonly:检查一个对象是否是由readonly 创建的只读代理

isProxy:检查一个对象是否是由reactive或者readonly方法创建的代理

App.vue

javascript 复制代码
<template>
    <h3>我是App组件(祖)</h3>
</template>

<script>
import { isProxy, isReactive, isReadonly, isRef, provide, reactive, readonly, ref, toRefs } from 'vue'
export default {
  name: 'App', 
  setup(){
    let phone = reactive({brand:"小米",color:"red"})
    let sum = ref(0)
    let phone2 = readonly(phone)

    console.log(isRef(sum));//true
    console.log(isReactive(phone));//true
    console.log(isReadonly(phone2));//true
    console.log(isProxy(phone));//true
    console.log(isProxy(phone2));//true readOnly处理后的也是proxy


    provide('phone',phone)
    return {...toRefs(phone)}
  }
}
</script>
<style>
</style>

组合式API的优势

Composition API组合式API,Options API配置式API
Options API存在的问题:使用传统OptionsAPI中,新增或者修改一个需求,就需要分别在data,methods,computed里修改。

2.Composition API的优势:我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起。还可借助hook函数发挥组合式API的威力。

新的组件

Fragment与Teleport

1.Fragment

。在Vue2中:组件必须有一个根标签

。在Vue3中:组件可以没有根标签,内部会将多个标签包含在一个Fragment虚拟元素中。好处:减少标签层级,减小内存占用

2.Teleport。什么是Teleport?

Teleport是一种能够将我们的组件html结构移动到指定位置的技术。

案例:传统的方式写弹窗会将外几层的组件全都撑开。使用teleport指定to='html'或to='body'就可将这段代码的东西传送到html标签或body标签中,也可加遮罩层

App.vue

javascript 复制代码
<template>
  <div class="app">
    <h3>我是App组件</h3>
    <Child/>
  </div>
</template>

<script>
import Child from "./components/Child.vue"
export default {
  name: 'App', 
  components:{
    Child
  }
}
</script>
<style>
.app{
  background-color: grey;
  padding: 10px;
}
</style>

Child.vue

javascript 复制代码
<template>
  <div class="child">
    <h3>我是Child组件</h3>
    <Sun/>
  </div>
</template>

<script>
import Sun from './Sun.vue'
export default {
  name: 'Child', 
  components:{
    Sun
  },
}
</script>
<style>
.child{
  background-color: skyblue;
  padding: 10px;
}
</style>

Son.vue

javascript 复制代码
<template>
  <div class="sun">
    <h3>我是Sun组件</h3>
    <Dialog/>
  </div>
</template>

<script>
import Dialog from './Dialog.vue'
export default {
  name: 'Sun', 
  components:{Dialog}
}
</script>
<style>
.sun{
  background-color: orange; 
  padding: 10px;
}
</style>

Dialog.vue

javascript 复制代码
<template>
  <div>
    <button @click="isShow = true">点我弹个窗</button>
<!--     <div v-if="isShow" class="dialog">
        <h3>我是一个弹窗</h3>
        <h4>一些内容</h4>
        <h4>一些内容</h4>
        <h4>一些内容</h4>
        <button @click="isShow = false">关闭弹窗</button>
    </div> -->
        <teleport to="body">
            <div v-if="isShow" class="mask">
                <div class="dialog">
                    <h3>我是一个弹窗</h3>
                    <h4>一些内容</h4>
                    <h4>一些内容</h4>
                    <h4>一些内容</h4>
                    <button @click="isShow = false">关闭弹窗</button>
                </div>                
            </div>

        </teleport>
    
  </div>
</template>

<script>
import { ref } from 'vue'

export default {
    name:"Dialog",
    setup(){
        let isShow = ref(false)
        return {isShow}
    }
}
</script>

<style>
.dialog{
    width: 300px;
    height: 300px;
    background-color: green;
    
    position: absolute;
    top:50%;
    left:50%;
    transform: translate(-50%,-50%);
}
.mask{/* 遮罩层样式,点被其遮住的元素的点不了 */
    position: absolute;
    top: 0 ;bottom: 0;left: 0;right: 0;
    background-color: rgba(0, 0, 0, 0.5);
}
</style>

Suspense组件

网速快的时候,静态引入和动态引入一样,但网速慢的时候,静态引入是App和Child组件一起出来(最后引入取决于组件套娃中最后出来的那个组件出来后才显示),动态引入是App和Child谁加载完谁出来。但动态引入当某个先出来展示,某个组件加载慢1秒中后才出来就会用户体验不好,页面有抖动,可以用Suspense将子标签Child标签包起来,然后再Child组件还未加载好的时候先用一个loading替换

App.vue

javascript 复制代码
<template>
  <div class="app">
    <h3>我是App组件</h3>
    <!-- <Child/> -->
    <Suspense>
      <template v-slot:default>
        <Child/>
      </template>
      <template v-slot:fallback>
        <h3>稍等,加载中......</h3>
      </template>
    </Suspense>
  </div>
</template>

<script>
// import Child from "./components/Child.vue"  //静态引入
import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(()=>import('./components/Child.vue'))  //动态引入  异步引入
export default {
  name: 'App', 
  components:{
    Child
  }
}
</script>
<style>
.app{
  background-color: grey;
  padding: 10px;
}
</style>

Child.vue

javascript 复制代码
<template>
  <div class="child">
    <h3>我是Child组件</h3>
    {{sum}}
  </div>
</template>

<script>
import { ref } from 'vue';
export default {
  name: 'Child', 
  async setup(){ //因为App.vue中使用了Suspense,且引入Child组件时为异步引入,所以这里的setup可返回Promise对象
    let sum = ref(0)
    let p = new Promise((resolve,reject)=>{
        setTimeout(() => {
            resolve({sum})
        }, 3000);
    })
    return await p
  }
}
</script>
<style>
.child{
  background-color: skyblue;
  padding: 10px;
}
</style>

其他

全局API的转移

. Vue 2.x有许多全局API和配置。

。例如:注册全局组件、注册全局指令等。

//注册全局组件

Vue. component( ' MyButton ', {

data: () =>({

count: 0}),

template: '<button @click="count++">clicked {{ count }} times.</button>'

})

//注册全局指令

Vue.directive( 'focus ', {

inserted : el => el.focus()

}

. Vue3.0中对这些API做出了调整:

。将全局的API,即:Vue.xxx调整到应用实例(app ) 上

2.x全局APl (vue ) => 3.x 实例APl( app)

Vue.config.xxxX => app.config.xxXx

Vue.config.productionTip => 移除

Vue.component => app.component

Vue.directive => app.directive

Vue.mixin => app.mixin

Vue.use => app.use

Vue.prototype => app.config.globalProperties
2.其他改变

. data选项应始终被声明为一个函数。

。过度类名的更改:

o Vue2.x写法

.v-enter,

.v-leave-to {

opacity: 0;

}

.v-leave,

.v-enter-to {

opacity: 1;

}

o Vue3.x写法

.v-enter-from,

.v-leave-to {

opacity: 0;

}

.v-leave-from,

.v-enter-to {

opacity: 1;

}

移除keyCode作为v-on 的修饰符,同时也不再支持config.keyCodes

移除v-on.native修饰符

移除了过滤器filter

. . . . . .

相关推荐
崔庆才丨静觅9 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606110 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了10 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅10 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅10 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅11 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment11 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅11 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊11 小时前
jwt介绍
前端
爱敲代码的小鱼11 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax