天命有余畏,祖宗有余法。 ——王安石

前言

本文并非题目党,而是实实在在的硬核文章,如果有想要学习Vue3的网友,能够大抵的浏览一下本文,总体来说本篇博客涵盖了Vue3中绝大部分内容,蕴含罕用的CompositionAPI(组合式API)、其它CompositionAPI以及一些新的个性:Fragment、Teleport、Suspense、provide和inject。

我的项目搭建

既然是学习Vue3,那么首先应该须要的是如何初始化我的项目,在这里提供了两种形式供大家参考

  • 形式一:vue-cli脚手架初始化Vue3我的项目

官网文档:https://cli.vuejs.org/zh/guid...

//    查看@vue/cli版本,确保@vue/cli版本在4.5.0以上vue --version//    装置或者降级你的@vue/clinpm install -g @vue/cli//     创立vue create vue_test// 启动cd vue_testnpm run serve
  • 形式二:vite初始化Vue3我的项目

vite官网:https://vitejs.cn/

//     创立工程npm init vite-app <project-name>//    进入工程目录cd <project-name>//     装置依赖npm install//    运行npm run dev

我的项目目录构造剖析

这里的我的项目目录构造剖析次要是main.js文件

  • Vue2外面的main.js

    new Vue({el: '#app',components: {},template: ''});
  • Vue3外面的main.js

    import { createApp } from 'vue'import App from './App.vue'createApp(App).mount('#app')

在Vue2外面,通过new Vue({})构造函数创立利用实例对象,而Vue3引入的不再是Vue的构造函数,引入的是一个名为createApp的工厂函数创立利用实例对象。

Vue3-devtool获取

devtool:https://chrome.zzzmh.cn/info?...

Composition API

setup

  • 了解:Vue3.0中一个新的配置项,值为一个函数
  • setup是所有Composition API(组合式API)的入口
  • 组件中所用到的数据、办法等等,均要配置在setup外面
  • setup函数的两种返回值

    • 若返回一个对象,则对象中的属性、办法,在模板中均能够间接应用
    • 若返回一个渲染函数,则能够自定义渲染内容
  • setup的执行机会

    • 在beforeCreate之前执行一次,此时this为undefined
  • setup的参数

    props:值为对象,蕴含:组件内部传递过去,且组件外部申明接管了的属性

    context:上下文对象

    • attrs:值为对象,蕴含:组件内部传递过去,但没有在props配置中申明的属性,相当于this.$attrs
    • slots:收到的插槽内容,相当于this.$slots
    • emit:散发自定义事件的函数,相当于this.$emit

注意事项:

  • 尽量不要与Vue2x的配置应用

    • Vue2x的配置(data、methods、computed)均能够拜访到setup中的属性、办法
    • setup中不能拜访Vue2x的配置(data、methods、computed)
    • 如果data外面的属性和setup外面的属性有重名,则setup优先
  • setup不能是一个async函数,因为返回值不再是return的对象,而是Promise,模板看不到return对象中的属性,然而前期也能够返回一个Promise实例,须要Suspense和异步组件的配合

示例一:setup函数的两种返回值

<template>    <h2>练习setup相干内容</h2>    <!--<h2>setup返回一个对象,并应用对象中的属性和办法</h2>-->    <!--<p>姓名:{{student.name}}</p>-->    <!--<p>年龄:{{student.age}}</p>-->    <!--<button @click="hello">点击查看控制台信息</button>-->    <hr>    <h2>setup返回一个函数</h2></template><script>    import {h} from 'vue'    export default {        name: "setupComponent",        setup(){            // 属性             let student={                name:'张三',                age:18,             }            // 办法        function hello() {               console.log(`大家好,我叫${student.name},往年${student.age}`)             }             return{    // 返回一个对象                 student,                 hello,             }            // return()=>h('h1','你好')    // 返回一个函数        }    }</script><style scoped></style>

这里须要留神的是setup外面定义的属性和办法均要return进来,否则无奈应用

示例二:setup外面的参数和办法和配置项混合应用

<template>    <h2>setup和配置项混用</h2>    <h2>姓名:{{name}}</h2>    <h2>年龄:{{age}}</h2>    <h2>性别:{{sex}}</h2>    <button @click="sayHello">sayHello(Vue3外面的办法)</button>    <button @click="sayWelcome">sayWelcome(Vue2外面的办法)</button></template><script>    export default {        name: "setup01_component",        data(){          return{            sex:'男',            sum:0,          }        },        methods:{            sayWelcome(){                console.log(`sayWelcome`)            },        },        setup(){            let sum=100;            let name='张三';            let age=18;            function sayHello() {                console.log(`我叫${name},往年${age}`)            }            return{                name,                age,                sayHello,                test02,                sum            }        }    }</script><style scoped></style>

这段代码是先实现了setup外面的属性和办法,以及Vue2中配置项外面的属性和办法。接下来增加对应的混合办法

<template>    <h2>setup和配置项混用</h2>    <h2>姓名:{{name}}</h2>    <h2>年龄:{{age}}</h2>    <h2>性别:{{sex}}</h2>    <button @click="sayHello">sayHello(Vue3外面的办法)</button>    <button @click="sayWelcome">sayWelcome(Vue2外面的办法)</button>    <br>    <br>    <button @click="test01">测试Vue2外面调用Vue3外面的属性和办法</button>    <br>    <br>    <button @click="test02">测试Vue3setup外面调用Vue2外面的属性和办法</button>    <br>    <h2>sum的值是:{{sum}}</h2></template><script>    export default {        name: "setup01_component",        data(){          return{            sex:'男',            sum:0,          }        },        methods:{            sayWelcome(){                console.log(`sayWelcome`)            },            test01(){                console.log(this.sex);  // Vue2外面的属性(data外面的属性)                // setup外面的属性                console.log(this.name);                console.log(this.age);                // setup外面的办法                this.sayHello();            }        },        setup(){            let sum=100;            let name='张三';            let age=18;            function sayHello() {                console.log(`我叫${name},往年${age}`)            }            function test02() {                // setup外面的属性                console.log(name);                console.log(age);                                // data外面的属性                console.log(this.sex);                console.log(this.sayWelcome);            }            return{                name,                age,                sayHello,                test02,                sum            }        }    }</script><style scoped></style>

这里新增了test01和test02办法,别离点击,控制台能够看到,点击配置项外面test01办法时,除了本身的sex属性有值,setup外面定义的属性也有值以及办法也能够调用,点击setup外面定义的test02办法时,控制台只能输入setup外面定义的属性和办法,而配置项外面定义的属性和办法值均为undefined。

  • setup外面定义的属性和办法均能够在配置项外面应用(methods、computed、watch等),而配置项外面定义的属性和办法无奈在setup外面调用
  • 如果setup外面的属性和data外面的属性有重名,则setup外面的属性优先

示例三:setup的执行机会
setup会在beforeCreate之前执行一次

<template>    <h2>setup的执行机制</h2></template><script>    export default {        name: "setup_component03",        setup(){            console.log('setup')        },        beforeCreate(){            console.log('beforeCreate')        }    }</script><style scoped></style>

查看控制台的话咱们看到的程序是setup>beforeCreate

setup外面context和props的应用

Vue2外面props和slot的应用

解说setup这外面的两个参数之前,先回顾一下Vue2外面的相干常识

  • props和自定义事件的应用
  • attrs
  • slot(插槽)

示例一:Vue2props和自定义事件的应用

筹备两个组件,别离为parent.vue组件和child.vue组件

parent.vue

<template>    <div class="parent">      我是父组件      <child msg="传递信息" name="张三" @sendParentMsg="getMsg"/>    </div></template><script>    import Child from "./Child";    export default {        name: "Parent",      components: {Child},      methods:{        getMsg(msg){          console.log(msg)        }      }    }</script><style scoped>  .parent{    padding: 10px;    background-color: red;  }</style>

child.vue

<template>    <div class="child">      <h2>我是子组件</h2>      <p>父组件传递过去的音讯是:{{msg}}</p>      <p>父组件传递过去的音讯是:{{name}}</p>      <button @click="sendMsg">向父组件的传递信息</button>    </div></template><script>    export default {        name: "Child",        props:{          msg:{            type:String,            default:''          },          name:{            type:String,            default:''          }        },        mounted(){          console.log(this);        },        methods:{          sendMsg(){            this.$emit("sendParentMsg",'告诉父组件更新')          }        }    }</script><style scoped>  .child{    padding: 10px;    background-color: orange;  }</style>

child组件对应的代码如下:

<template>    <div class="child">      <h2>我是子组件</h2>      <!--<p>父组件传递过去的音讯是:{{msg}}</p>-->      <!--<p>父组件传递过去的音讯是:{{name}}</p>-->      <p>父组件传递过去的音讯是:{{$attrs.msg}}</p>      <p>父组件传递过去的音讯是:{{$attrs.name}}</p>      <button @click="sendMsg">向父组件的传递信息</button>    </div></template><script>    export default {        name: "Child",        // props:{        //   msg:{        //     type:String,        //     default:''        //   },        //   name:{        //     type:String,        //     default:''        //   }        // },        mounted(){          console.log(this);        },        methods:{          sendMsg(){            this.$emit("sendParentMsg",'告诉父组件更新')          }        }    }</script><style scoped>  .child{    padding: 10px;    background-color: orange;  }</style>


子组件通过props接管父组件传递的信息,通过this.$emit()自定义事件向父组件传递信息。当应用props接收数据的时候,attrs外面的数据为空,如果没有应用props接收数据的话,那么props外面就有值。

示例二:Vue2外面slot的应用

同理筹备两个组件,一个Index.vue组件,另一个为MySlot.vue组件

Index.vue

<template>    <div class="index">      <h2>我是Index组件</h2>      <!--写法一-->      <my-slot>        <!--插槽外面的内容-->        <h2>传入的slot参数</h2>        <h2>传入的slot参数</h2>        <h2>传入的slot参数</h2>        <h2>传入的slot参数</h2>      </my-slot>      <!--写法二-->      <my-slot>        <template slot="header">          <h2>我是header组件</h2>        </template>        <template slot="footer">          <h2>我是footer附件</h2>        </template>      </my-slot>    </div></template><script>    import MySlot from "./MySlot";    export default {        name: "Index",      components: {MySlot}    }</script><style scoped>  .index{    padding: 10px;    background: red;  }</style>

MySlot.vue

<template>    <div class="slot">      <h2>我是MySlot组件</h2>      <slot></slot>      <br>      <slot name="header"></slot>      <br>      <slot name="footer"></slot>    </div></template><script>    export default {        name: "MySlot",        mounted(){          console.log(this);        }    }</script><style scoped>  .slot{    padding: 10px;    background: orange;  }</style>

ref

  • 作用:定义一个响应式数据
  • 语法:const xxx=ref(initValue)
  • 创立一个蕴含响应式数据的援用对象(reference对象);
  • JS中操作数据:xxx.value=xxx;
  • 模板中读取数据:不须要.value,间接:<div>{{xxx}}</div>

    备注:

    • 接管的数据能够是:根本类型,也能够是对象类型
    • 根本类型的数据:响应式仍然是靠Object.defineProperty()的get和set实现的
    • 对象类型的数据:外部求助了Vue3.0中的一个新函数-reactive函数
      示例

      <template><h1>ref</h1><h2>ref定义根本数据类型</h2><p>姓名:{{name}}</p><p>年龄:{{age}}</p><p>婚否:{{isMarry}}</p><h2>ref定义对象类型</h2><p>喜好:{{hobby}}</p><p>证件类型:{{user.idCard}}</p><p>国籍:{{user.nation}}</p><button @click="changeName">批改信息</button></template><script>import {ref} from 'vue'export default {name: "refComponent",setup(){    // 应用根本数据类型 number,string,boolean,    let name=ref('张三');    let age=ref(18);    let isMarry=ref(false);    // 应用ref定义数组    let hobby=ref(['吃饭','睡觉','打豆豆']);    // 应用ref定义对象    let user=ref({        idCard:'身份证',        nation:['中国','美国','英国','俄罗斯']    })    function changeName() {        //  批改根本数据数据类型        name.value='李四';    // ref定义的响应式数据批改数据时必须要.value        age.value=20;        isMarry.value=true;        //  批改对象数据类型        hobby.value[0]='玩游戏';        user.value.idCard='港澳台居民身份证';        user.value.nation[0]='挪威';    }    return{        name,        age,        isMarry,        changeName,        user,        hobby    }}}</script><style scoped></style>

      留神:

  • ref定义的响应式数据批改数据时必须要.value
  • ref定义的对象数据类型,外部求助了Vue3.0中的一个新函数-reactive函数
  • 模板中应用数据时不须要.value

reactive函数

  • 作用:定义一个对象类型的响应式数据(根本类型别用它,用ref函数)
  • 语法:const 代理对象=reactive(被代理的对象)接管一个对象(或数组),返回一个代理器对象(Proxy的实例对象,简称Proxy对象)
  • reactive定义的响应式数据是深层次的
  • 外部基于ES6的Proxy实现,通过代理对象的操作源对象的外部数据都是响应式的

    <template><h2>reactive响应式数据</h2><p>姓名:{{student.name}}</p><p>年龄:{{student.age}}</p><p>喜好:{{student.hobbies}}</p><button @click="changeStuInfo">扭转学生信息</button></template><script>import {reactive} from 'vue'export default {    name: "reactiveComponent",    setup(){            // 数据        let student=reactive({            name:'张三',            age:19,            hobbies:['吃饭','睡觉','打豆豆']        });        console.log(student)        // 办法        function changeStuInfo() {            student.name='李四';            student.age=20;            student.hobbies[0]='做家务'            }        return{            student,            changeStuInfo,        }    }}</script><style scoped></style>

    reactive比照ref

  • 从定义数据的角度比照

    • ref用来定义:根本类型数据
    • reactive用来定义:对象(或数组)类型数据
    • 备注:ref也能够用来定义对象(或数组)类型的数据,它外部会主动通过reactive转为代理对象
  • 从原理角度比照

    • ref通过Object.defineProperty()的get和set实现(响应式)数据劫持
    • reactive通过应用Proxy来实现响应式(数据劫持),并通过Reflect操作源对象外部的数据
  • 从应用角度

    • ref定义的数据:操作数据须要.value,读取数据时模板中间接读取不须要.value
    • reactive定义的数据:操作数据与读取数据均不须要.value

watch和watchEffect

    //    attr示意须要监督的属性    //  状况一:监督单个ref定义的响应式数据    watch(attr,(newValue,oldValue)=>{        console.log('attr变动了',newValue,oldValue);    })   //  状况二; 监督多个ref定义的响应式数据    watch([attr1,attr2,....,attrn],(newValue,oldValue)=>{        console.log('attr1或attrn变动了',newValue,oldValue);    })    // obj示意须要监听的对象     // 状况三:监督reactive定义的响应式数据    //    若watch监督的是reactive定义的响应式数据,则无奈正确取得oldValue    //    若watch监督的是reactive定义的响应式数据,则强制关上开启了深度监督     watch(obj,(newValue,oldValue)=>{        console.log('obj变动了',newValue,oldValue)     },{immediate:true,deep:false}); // 此处deep配置不在见效    // 状况四,监督reactive定义的响应式数据中的某个属性      watch(()=>person.job,(newValue,oldValue)=>{         console.log('person的job变动了',newValue,oldValue)      },{immediate:true,deep:true})     //  状况五:监督reactive定义的响应式数据中的某一些属性    watch([()=>person.name,()=>person.age],(newValue,oldValue)=>{            console.log('person的job变动了',newValue,oldValue)    })    // 非凡状况    watch(()=>person.job,(newValue,oldValue)=>{        console.log('person的job变动了',newValue,oldValue)    },{deep:false});//  此处因为是监督reactive所定义的对象中的某个属性,所以deep配置无效
  • watch

    • 监督reactive定义的响应式数据时:oldValue无奈正确获取、强制开启了深度监督(deep配置生效)
    • 监督reactive定义的响应式数据中某个属性时deep配置无效
      示例一:wath监听ref定义的响应式数据

      <template><h2>watch监听ref定义的响应式数据</h2><h2>姓名:{{userName}}</h2><h2>年龄:{{age}}</h2><button @click="userName+='!'">批改姓名</button><button @click="age++">批改年龄</button><hr><h2>姓名:{{user.name}}</h2><h2>年龄:{{user.age}}</h2><button @click="user.name+='!'">批改姓名</button><button @click="user.age++">批改年龄</button></template><script>import {ref,watch} from 'vue';export default {name: "watch_component01",setup(){   let userName=ref('张三');   let age=ref(18);   let user=ref({       name:'张三',       age:21,   })   // watch监听ref定义的单个响应式数据   watch(userName,(newValue,oldValue)=>{       console.log(`userName产生了变动,新值是:${newValue},旧值是:${oldValue}`)   });   watch(age,(newValue,oldValue)=>{       console.log(`age产生了变动,新值是:${newValue},旧值是:${oldValue}`);   });   // 如果须要监听多个ref定义的响应式数据的话,代码如下   /**    * newValue和oldValue都是数组的模式,其中数组的第n位示意监听地位的第n位    * 例如:此例子中,监听属性的第一位是userName,那位newValue和oldValue对应的第一位也是    * userName。    * 如果有立刻执行,那么最开始的值为[],而不是[undefined,undefined]    */   watch([userName,age],(newValue,oldValue)=>{      console.log('userName或age中的其中一个产生了变动,',newValue,oldValue)   })      // watch监督ref定义的响应式对象数据   watch(user.value,(newValue,oldValue)=>{       console.log('person产生了变动',newValue,oldValue)   })   watch(user,(newValue,oldValue)=>{       console.log('person产生了变动',newValue,oldValue);   },{deep:false})      return{       userName,       age,       user   }}}</script><style scoped></style>

      示例二:watch监听reactive定义的响应式数据

      <template><h1>watch监听reactive定义的响应式数据</h1><p>姓名:{{user.name}}</p><p>年龄:{{user.age}}</p><p>薪水:{{user.job.salary}}K</p><button @click="user.name+='!'">扭转姓名</button><button @click="user.age++">扭转年龄</button><button @click="user.job.salary++">扭转薪水</button></template><script>import {watch,reactive} from 'vue'export default {name: "watch_component02",setup(){    let user=reactive({        name:'张三',        age:18,        job:{           salary:20        }    });        // 状况一:监听reactive定义的响应式数据,无奈正确获取oldValue    /**     * 此时的newValue和oldValue都是最新的数据     * 默认强制开启深度监督,此时深度监督生效     */    watch(user,(newValue,oldValue)=>{        console.log(newValue,oldValue);    },{deep:false});        //  状况二,监督reactive定义的响应式数据的单个属性    // watch(()=>user.name,(newValue,oldValue)=>{    //     console.log('name产生了变动',newValue,oldValue);    // });    // watch(()=>user.age,(newValue,oldValue)=>{    //     console.log('age产生了变动',newValue,oldValue);    // })        // 状况三:监督reactive定义的响应式数据的多个属性    /**     * newValue和oldValue都是数组的模式,其中数组的第n位示意监听地位的第n位     * 例如:此例子中,监听属性的第一位是userName,那位newValue和oldValue对应的第一位也是     * userName,     */    // watch([()=>user.name,()=>user.age],(newValue,oldValue)=>{   // 写法一    //     console.log('name或age中的某个属性产生了变动',newValue,oldValue);    // })    // watch(()=>[user.name,user.age],(newValue,oldValue)=>{   // 写法二    //     console.log('name或者age中的某个属性产生了变动',newValue,oldValue)    // })    //  状况四:监督reactive定义的响应式数据的对象的某个属性,此时deep无效    /**     * 留神:此时须要区别是reactive定义的对象还是reactive定义的对象外面的某个属性     * 此时deep无效,敞开了监督     */      // watch(()=>user.job,(newValue,oldValue)=>{      //     console.log(newValue,oldValue);      // },{deep:false});    return{        user    }}}</script><style scoped></style>
  • watchEffect

    • watch的套路是:既要指明监督的属性,也要指明监督的回调
    • watchEffect的套路是:不必指明监督那个属性,监督的回调中用到那个属性,那就监督那个属性
    • watchEffect有点像computed

      • 但computed重视的是计算出来的值(回调函数的返回值),所以必须要写返回值
      • 而watchEffect更重视的是过程(回调函数的函数体),所以不必写返回值
        示例

        <template><h1>watchEffect监督ref和reactive定义的响应式数据</h1><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,watchEffect} from 'vue';export default {name: "watch_effect_component01",setup(){let sum=ref(0);let msg=ref('你好');let person=reactive({   name:'张三',   age:18,   job:{       j1:{           salary:100,       }   }});/*** 在watchEffect外面写须要监督的属性,默认会执行一次* 如果是监督ref定义的响应式书则须要.value* 如果是监督reactive定义的响应式数据则间接监督*/watchEffect(()=>{   let x1=sum.value;   let x2=person.job.j1.salary;   console.log('watchEffect所指定的回调函数执行了');})return{   sum,   msg,   person}}}</script><style scoped></style>

Vue2响应式原理VSVue3响应式原理

在Vue2中次要是通过数据劫持来实现响应式原理的,也就是基于Object.defineProperty来实现的,而Vue3中是通过Proxy和Reflect来实现的(集体最低档次上的了解,还望各位大佬海涵)。

那么咱们来比照一下Vue3实现响应式的原理比Vue2实现响应式的原理好在哪里?

Vue2响应式原理

  • 实现原理

    • 对象类型:通过Object.defineProperty()对属性的读取,批改进行拦挡(数据劫持)
    • 数组类型:通过重写更新数组的的一系列办法来实现拦挡。(对数组的变更办法进行了包裹)
    Object.defineProperty(data,'count',{  get(){}  set(){}})
  • 存在问题

    • 新增属性、删除属性、界面不会自动更新
    • 间接通过下标批改数组,界面不会自动更新

    先看一个简略的示例

    <!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8">  <title>Title</title></head><body><div id="app">  <h1>学生信息</h1>  <h4>姓名:{{student.name}}</h4>  <h4>年龄:{{student.age}}</h4>  <h4 v-if="student.sex">性别:{{student.sex}}</h4>  <h4>喜好:{{student.hobbies}}</h4>  <button @click="addSex">新增性别</button>  <button @click="deleteAge">删除年龄</button>  <button @click="updateHobbies">批改喜好</button></div><script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script><script>  let vm=new Vue({      el:'#app',      data(){          return{              student:{                  name:'张三',                  age:18,                  hobbies:['吃饭','睡觉','打豆豆']              }          }      },      methods:{          addSex(){   // 新增性别              this.student.sex='male';              console.log(this.student);          },          deleteAge(){    // 删除年龄              delete this.student.age;              console.log(this.student);          },          updateHobbies(){   //   批改喜好              this.student.hobbies[0]='玩游戏';              console.log(this.student);          }      }  })</script></body></html>

    别离调用按钮对应的办法,控制台能够看到,新增性别属性时,student外面有性别这个属性,然而并没有实现响应式(视图没有更新),同理,其它两个按钮对应的办法也是一样。

起因:Vue2.0想要实现响应式数据的话,必须先在data外面定义,之后从新增加的数据无奈实现响应式。
解决方案:

  • 新增/批改:vue.$set(target,propName/index,value)

    • {Object | Array} target
    • {string | number} propertyName/index
    • {any} value
  • 删除:vue.$delete(target,propName/index)

    • {Object | Array} target
    • {string | number} propertyName/index

此时咱们批改对应的代码如下

    addSex(){   // 新增性别        // this.student.sex='male';        // vm.$set(this.student,'sex','male');        this.$set(this.student,'sex',male);        console.log(this.student);    },    deleteAge(){    // 删除年龄        // delete this.student.age;        // this.$delete(this.student,'age');        vm.$delete(this.student,'age');        console.log(this.student);    },       updateHobbies(){   //   批改喜好         // this.student.hobbies[0]='玩游戏';         // this.$set(this.student.hobbies,0,'玩游戏');         //  vm.$set(this.student.hobbies,0,'玩游戏');         /**                 * 或者应用数组变异的办法                 * push()                 * pop()                 * shift()                 * unshift()                 * splice()                 * sort()                 * reverse()             */         this.student.hobbies.splice(0,1,'玩游戏');         console.log(this.student);       }

弊病

  • 必须定义在data外面的数据能力实现响应式
  • 如果前面增加的数据想要实现响应式,那么就须要调用对应的API

Object.defineProperty的简略示例

    let student={        name:'张三',        age:18,    }    let p={}    Object.defineProperty(p,'name',{        get(){  // 读取name时触发            console.log('读取了name属性');            return student.name;        },        set(value){ // 批改name时触发            console.log('name产生了变动,视图产生了变动');            student.name=value;        }    });    console.log(p.name);    p.name='李四';    p.sex='male';    delete p.name;

Vue3响应式原理

对于Proxy和Reflect的用法这里不过多介绍,如果有想要理解的举荐看MDN或者阮一峰老师的ES6

  • https://es6.ruanyifeng.com/
  • https://developer.mozilla.org...

示例

    let user={        name:'张三',        age:18,    }    let p=new Proxy(user,{        get(target,propName){            console.log(`读取了p外面的${propName}属性`);            Reflect.get(target,propName);            // return target[propName];        },        set(target,propName,value){            console.log(`批改了p外面的${propName}属性`);            // target[propName]=value;            Reflect.set(target,propName,value);        },        deleteProperty(target,propName){            console.log(`删除了p外面的${propName}属性`);            // delete target[propName];            Reflect.deleteProperty(target,propName);        }    });    console.log(p.name);    p.name='李四';    p.sex='male';    delete p.age;

查看控制台,当读取name属性的时候触发get()办法,新增或者批改属性的时候触发set()办法,删除的时候触发deleteProperty()办法,这就是Vue3.0对响应式的改良。

生命周期和钩子函数

Vue2.0生命周期和钩子函数

vue3.0生命周期和钩子函数

Vue2和Vue3生命周期比照
beforeCreateNot needed*
createdNot needed*
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeUnmountonBeforeUnmount
unmountedonUnmounted
activatedonActivated
deactivatedonDeactivated
因为 setup是围绕 beforeCreate和 created生命周期钩子运行的,所以不须要显式地定义它们。换句话说,在这些钩子中编写的任何代码都应该间接在 setup函数中编写。

Vue3x中能够持续应用Vue2x中的生命周期钩子,但有两个被更名

  • beforeDestory改名为beforeUnmout
  • destoryed改名为unmouted

    <template><!--Vue3x生命周期和钩子函数--><h3>Vue3x生命周期和钩子函数</h3><h3>数字:{{num}}</h3><button @click="num++">点我加加</button></template><script>import {ref,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from 'vue'export default {    name: "lifeCycleComponent",    setup(){        let num=ref(0);        console.log("======setup=========");        onBeforeUnmount(()=>{            console.log("======onBeforeUnmount=========");        });        onMounted(()=>{            console.log("======onMounted=========");        });        onBeforeUpdate(()=>{            console.log("======onBeforeUpdate=========");        });        onUpdated(()=>{            console.log("======onUpdated=========");        });        onBeforeUnmount(()=>{            console.log("======onBeforeUnmount=========");        })        onUnmounted(()=>{            console.log("======onUnmounted=========");        });        return{            num,        }    }}</script><style scoped></style>

自定义hook

  • 什么是hook:实质是一个函数,把setup函数中应用的CompositionAPI进行了封装
  • 相似于vue2x中mixin
  • 自定义hook的劣势:复用代码,让setup中的逻辑更分明易懂

作为初学hook的我来说,我对于hook并没有理解多少,这里贴一些本人练习中的示例,现阶段的我感觉不进去export 导出函数和hook的区别和长处。示例的话是鼠标点击的时候获取以后坐标

示例

<template>    <h2>自定义hook</h2>    <h2>以后x的坐标:{{x}},以后y的坐标:{{y}}</h2></template><script>    import {reactive,toRefs,onMounted,onUnmounted} from 'vue'    export default {        name: "hook_component01",        setup(){            // 数据            let point=reactive({                x:0,                y:0,            })            // 办法            function getPoint(event){                console.log(event)                point.x=event.clientX;                point.y=event.clientY;            }            // 生命周期钩子函数            onMounted(()=>{                window.addEventListener('click',getPoint);            })            onUnmounted(()=>{                window.removeEventListener('click',getPoint);            })            return{                ...toRefs(point)            }        }    }</script><style scoped></style>

抽离独自的hook

  • 新建目录hook
  • hook目录下新建文件usePoint.js

usePoint.js

import {reactive,onMounted,onUnmounted} from 'vue'export let getPoint=()=>{    // 数据    let point=reactive({        x:0,        y:0,    })    // 办法    function getPoint(event){        console.log(event)        point.x=event.clientX;        point.y=event.clientY;    }    // 生命周期钩子函数    onMounted(()=>{        window.addEventListener('click',getPoint);    })    onUnmounted(()=>{        window.removeEventListener('click',getPoint);    })    return point}

须要引入hook的.vue文件

    import {reactive,toRefs,onMounted,onUnmounted} from 'vue'    import {getPoint} from "./hook/usePoint";    export default {        name: "hook_component01",        setup(){            let point=getPoint();            return{                ...toRefs(point)            }        }    }

这个就是最简略hook的用法,如果有晓得export 导出函数和hook区别的大佬,能够在下方评论区留言,感谢不敬!!!

其它Composition API

toRef与toRefs

toRef

  • 作用:创立一个ref对象,其value值指向另一个对象中的某个属性值
  • 语法:const name=toRef(obj,'name')
  • 利用:要将响应式对象中的某个属性独自提供给内部应用时
  • 扩大:toRefs与toRef性能统一,但能够批量创立多个ref对象,toRefs(obj)

示例一

<template>    <h2>toRef与toRefs</h2>    <h2>姓名:{{person.name}}</h2>    <h2>年龄:{{person.age}}</h2>    <h2>薪水:{{person.job.salary}}k</h2>    <button @click="person.name+='!'">批改姓名</button>    <button @click="person.age++">批改年龄</button>    <button @click="person.job.salary++">涨点薪资</button></template><script>    import {reactive} from 'vue'    export default {        name: "toRef_component",        setup(){            let person=reactive({                name:'二郎神杨杨戬',                age:18,                job:{                    salary:20                }            })            return{                person,            }        }    }</script><style scoped></style>

示例一外面间接返回person对象,导致每次取值的时候都须要person.xxx,这样既不美观也不优雅,批改一下代码。

示例二

<template>    <h2>toRef与toRefs</h2>    <h2>姓名:{{name}}</h2>    <h2>年龄:{{age}}</h2>    <h2>薪水:{{salary}}k</h2>    <button @click="name+='!'">批改姓名</button>    <button @click="age++">批改年龄</button>    <button @click="salary++">涨点薪资</button></template><script>    import {reactive,toRef} from 'vue'    export default {        name: "toRef_component",        setup(){            let person=reactive({                name:'二郎神杨杨戬',                age:18,                job:{                    salary:20                }            })            return{                name:toRef(person,'name'),                age:toRef(person,'age'),                salary:toRef(person.job,'salary')            }        }    }</script><style scoped></style>

谬误用法示例一

<template>    <h2>toRef与toRefs</h2>    <h2>姓名:{{name}}</h2>    <h2>年龄:{{age}}</h2>    <h2>薪水:{{salary}}k</h2>    <button @click="name+='!'">批改姓名</button>    <button @click="age++">批改年龄</button>    <button @click="salary++">涨点薪资</button></template><script>    import {reactive,toRef,toRefs} from 'vue'    export default {        name: "toRef_component",        setup(){            let person=reactive({                name:'二郎神杨杨戬',                age:18,                job:{                    salary:20                }            })            return{                name:person.name,                age:person.age,                salary:person.job.salary            }        }    }</script><style scoped></style>

谬误用法示例二

<template>    <h2>toRef与toRefs</h2>    <h2>姓名:{{name}}</h2>    <h2>年龄:{{age}}</h2>    <h2>薪水:{{salary}}k</h2>    <h2>peron对象{{person}}</h2>    <button @click="name+='!'">批改姓名</button>    <button @click="age++">批改年龄</button>    <button @click="salary++">涨点薪资</button></template><script>    import {reactive,toRef,toRefs,ref} from 'vue'    export default {        name: "toRef_component",        setup(){            let person=reactive({                name:'二郎神杨杨戬',                age:18,                job:{                    salary:20                }            })            return{                person,                name:ref(person.name),                age:ref(person.age),                salary:ref(person.job.salary)            }        }    }</script><style scoped></style>

toRefs

示例一

<template>    <h2>toRef与toRefs</h2>    <h2>姓名:{{name}}</h2>    <h2>年龄:{{age}}</h2>    <h2>薪水:{{job.salary}}k</h2>    <button @click="name+='!'">批改姓名</button>    <button @click="age++">批改年龄</button>    <button @click="job.salary++">涨点薪资</button></template><script>    import {reactive,toRef,toRefs} from 'vue'    export default {        name: "toRef_component",        setup(){            let person=reactive({                name:'二郎神杨杨戬',                age:18,                job:{                    salary:20                }            })            return{                ...toRefs(person)            }        }    }</script><style scoped></style>

shallowRef和shallowReactive

  • shallowReactive:只解决对象最外层属性的响应式(浅响应式)
  • shallowRef:只解决根本数据类型的响应式,不进行对象的响应式解决
  • 什么时候用

    • 如果有一个对象数据,构造比拟深,但变动时只是外层属性变动用shallowReactive
    • 如果有一个对象数据,后续性能不会批改该对象中的属性,而是生新的对象来替换用shallowRef

shallowRef示例

<template>    <h2>shallowRef示例</h2>    <h2>以后sum的值是:{{sum}}</h2>    <button @click="sum++">点我sum加1</button>    <h2>以后x.y的值是:{{x.y}}</h2>    <button @click="x.y++">点我x.y加1</button>    <button @click="x={y:100}">点我替换y的值</button></template><script>    import {shallowRef,ref} from 'vue'    export default {        name: "shallowRef_component",        setup(){            let sum=ref(0);            let x=shallowRef({                y:0,            })            return{                sum,                x,            }        }    }</script><style scoped></style>

这里咱们通过ref和shallowRef进行比对,点击x.y加1按钮的时候,视图不会触发更新,因为y的值对象作为深层次的,而间接点击sum加1的按钮的时候能够触发更新,sum间接是浅层次的,替换y的值的时候替换的是整个x的值(即整个对象),而不是x外面的值进行操作。

shallowReactive示例

<template>    <h2>shallowReactive示例</h2>    <h2>姓名:{{name}}</h2>    <h2>年龄:{{age}}</h2>    <h2>薪资:{{job.salary}}k</h2>    <button @click="name+='!'">批改姓名</button>    <button @click="age++">批改年龄</button>    <button @click="job.salary++">涨点薪资</button></template><script>    import {shallowReactive,toRefs} from 'vue'    export default {        name: "shallowReactive01_component",        setup(){            let person=shallowReactive({                name:'张三',                age:18,                job:{                    salary:20,                }            })            return{                ...toRefs(person)            }        }    }</script><style scoped></style>

点击批改姓名和批改年龄的按钮时,能够看到视图发生变化,点击涨薪的时候视图不会发生变化,然而数据产生了变动,这个大家能够应用控制台进行测试。

readonly和shallowReadonly

  • readonly:让一个响应式数据变为只读的(深只读)
  • shallowReadonly:让一个响应式变为只读的(浅只读)
    示例一

    <template><h2>readonly与shallowReadonly</h2><h2>姓名:{{name}}</h2><h2>年龄:{{age}}</h2><h2>薪资:{{job.salary}}</h2><h2>以后sum的值是:{{sum}}</h2><button @click="name+='!'">批改姓名</button><button @click="age++">批改年龄</button><button @click="job.salary++">涨薪(readonly)</button><button @click="job.salary++">涨薪(shallowReadonly)</button><button @click="sum++">点我加1</button></template><script>import {ref,reactive,readonly,shallowReadonly,toRefs} from 'vue'export default {    name: "shallowReadonly_component",    setup(){        let sum=ref(0);        let person=reactive({            name:'二郎神杨戬',            age:21,            job:{                salary:200            }        });        person=readonly(person);        sum=readonly(sum);        // person=shallowReadonly(person);        // sum=readonly(sum);        return{            sum,            ...toRefs(person)        }    }}</script><style scoped></style>


应用readonly的时候,按钮点击全副生效,咱们看下shallowReadonly的成果


应用shallowReadonly的时候,批改姓名,批改年龄都不会发生变化,只有涨薪产生了变动

toRaw和markRaw

  • toRaw

    • 作用:将一个由reactive生成的响应式对象转为一般对象
    • 应用场景:用于读取响应式对象对应的一般对象,对这个一般对象的所有操作,不会引起页面更新
  • markRow

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

      利用场景:

    • 有些值不应该被设置为响应式的,例如简单的第三方类库,
    • 当渲染具备不可变的数据源的大列表时,跳过响应式转换能够进步性能
      示例一
<template>    <div style="width: 800px;margin: 0 auto">        <h2>toRaw与markRow</h2>        <h2>姓名:{{name}}</h2>        <h2>年龄:{{age}}</h2>        <h2>薪资:{{job.salary}}k</h2>        <button @click="name+='!'">批改姓名</button>        <button @click="age++">批改年龄</button>        <button @click="job.salary++">涨点薪资</button>        <button @click="showRawPerson">输入最原始的person对象</button>    </div></template><script>    import {ref,reactive,toRaw,markRaw,toRefs} from 'vue'    export default {        name: "toRaw01_component",        setup(){            let sum=ref(0);            let person=reactive({                name:'二郎神杨戬',                age:18,                job:{                    salary:20                }            })            function showRawPerson() {                let p=toRaw(person)                console.log(p);                let sum=toRaw(sum);                console.log(sum);   // 对ref定义的响应式数据有效            }            return{                sum,                ...toRefs(person),                showRawPerson            }        }    }</script><style scoped></style>

调用showRawPerson办法的时候,控制台能够看到输入最原始的person,sum的话,输入的undefined,toRaw对ref定义的响应式数据有效,接下来看下markRow的成果

示例二

<template>    <div style="width: 800px;margin: 0 auto">        <h2>toRaw与markRow</h2>        <h2>姓名:{{name}}</h2>        <h2>年龄:{{age}}</h2>        <h2>薪资:{{job.salary}}k</h2>        <button @click="name+='!'">批改姓名</button>        <button @click="age++">批改年龄</button>        <button @click="job.salary++">涨点薪资</button>        <button @click="showRawPerson">输入最原始的person对象</button>        <h2>车的信息是:{{person.car}}</h2>        <button @click="addCar">给人增加一辆车</button>        <template v-if="person.car">            <button @click="person.car.name+='!'">批改车名</button>            <button @click="person.car.price++">批改车的价格</button>            <button @click="changeCarPrice">批改车的价格</button>        </template>    </div></template><script>    import {ref,reactive,toRaw,markRaw,toRefs} from 'vue'    export default {        name: "toRaw01_component",        setup(){            let sum=ref(0);            let person=reactive({                name:'二郎神杨戬',                age:18,                job:{                    salary:20                }            })            function showRawPerson() {                let p=toRaw(person)                console.log(p);                let sum=toRaw(sum);                console.log(sum);   // 对ref定义的响应式数据有效            }            function addCar() {                let car={name:'宝马',price:40}                person.car=markRaw(car);            }            function changeCarPrice() {                person.car.price++;                console.log(person.car.price)            }            return{                sum,                person,                ...toRefs(person),                showRawPerson,                addCar,                changeCarPrice            }        }    }</script><style scoped></style>

这里新增了一个车信息的办法和相干属性到person对象外面,失常状况下,间接在reactive外面的追加的数据会实现响应式的,然而这里应用了markRaw办法,所以点击批改车的名字和价格时数据产生了变动,然而视图不会更新。

customRef

  • 作用:创立一个自定义的ref,并对其依赖项跟踪和更新触发进行显示管制,它须要一个工厂函数,该函数接管 track 和 trigger 函数作为参数,并且应该返回一个带有 get 和 set 的对象。
    实现防抖成果:
  • 1、先实现自定义双向绑定
  • 2、实现了双向绑定之后,实现防抖
    自定义双向绑定

    <template><h2>customRef示例</h2><input type="text" v-model="msg"><h2>{{msg}}</h2></template><script>import {customRef,} from 'vue'export default {    name: "customRef01_component",    setup(){        function myRef(msg){   // 自定义ref函数           return customRef((track,trigger)=>{               return{                   get(){                       console.log('读取了值')                       track();                       return msg;                   },                   set(newValue){                    console.log(`批改了值,批改后的值是:${newValue}`);                       msg=newValue;                       trigger();                   }               }           })        }        let msg=myRef('你好');        return{            msg        }    }}</script><style scoped></style>


在这里咱们实现了数据的双向绑定,接下来是实现防抖

<template>    <div style="width: 800px;margin: 0 auto">        <h2>customRef示例</h2>        <input type="text" v-model="msg">        <h2>{{msg}}</h2>    </div></template><script>    import {customRef,} from 'vue'    export default {        name: "customRef01_component",        setup(){            function myRef(msg,delay){   // 自定义ref函数                let timer;               return customRef((track,trigger)=>{                   return{                       get(){                           console.log('读取了值')                           track();                           return msg;                       },                       set(newValue){                           timer=setTimeout(()=>{                               console.log(`批改了值,批改后的值是:${newValue}`);                               msg=newValue;                               trigger();                           },delay)                       }                   }               })            }            let msg=myRef('你好',500);            return{                msg            }        }    }</script><style scoped></style>

响应式数据的判断

  • isRef:查看一个值是否为ref对象
  • isReactive:查看一个对象是否由reactive创立的响应式代理
  • isReadonly:查看一个对象是否由readonly创立的只读代理
  • isProxy:查看一个对象是否由reactive或者readonly办法创立的代理

provide和inject

  • 作用:实现祖与后辈组件间通信
  • 套路:父组件有一个provide选项来提供数据,后辈组件有一个inject选项来开始应用这些数据

祖组件

<template>    <h2>我是祖组件</h2>    <h3>汽车信息</h3>    <p>名称:{{name}}</p>    <p>价格:{{price}}</p>    <inject_component></inject_component></template><script>    import {reactive,toRefs,provide} from 'vue'    export default {        name: "provide_component",        setup(){            let car=reactive({                name:'宝马',                price:'40w'            });            provide('car',car);    // 提供provide            return{                ...toRefs(car)            }        }    }</script><style scoped></style>

后辈组件

<template>    <h2>我是孙组件</h2>    <h3>汽车信息</h3>    <p>名称:{{name}}</p>    <p>价格:{{price}}</p></template><script>    import {inject,toRefs,ref} from 'vue'    export default {        name: "inject_component",        setup(){            let car=inject("car");    //应用inject接管            return{                ...toRefs(car)            }        }    }</script><style scoped></style>

Fragment

  • 在vue2中:组件必须有一个根标签
  • 在vue3中:组件能够没有根标签,外部会将多个根标签蕴含在一个Fragment虚构元素中

益处:缩小标签层级,缩小内存占用

Teleport

Teleport是一种可能将咱们的组件html构造挪动到指定地位的技术

<teleport to='挪动地位'>    <div v-if='isShow' class='mark'>        <div class="dialog">            <h3>我是一个弹窗</h3>            <button @click='isShow=true'>敞开弹窗</button>        </div>    </div></teleport>

实现一个弹窗居中显示,有四个组件,别离为:teleport_parent,teleport_child,teleport_son,teleport_dialog,而后须要实现的成果是在teleport_son组件引入teleport_dialog组件,teleport_dialog显示的时候在屏幕正地方

teleport_parent.vue

<template>    <div class="parent">        <h2>我是parent组件</h2>        <teleport_child/>    </div></template><script>    import Teleport_child from "./teleport_child";    export default {        name: "teleport_parent",        components: {Teleport_child}    }</script><style scoped>    .parent{        background-color: red;        padding: 10px;    }</style>

teleport_child.vue

<template>    <div class="child">        <h2>我是child组件</h2>        <teleport_son/>    </div></template><script>    import Teleport_son from "./teleport_son";    export default {        name: "teleport_child",        components: {Teleport_son}    }</script><style scoped>    .child{        background-color: orange;        padding: 10px;    }</style>

teleport_son.vue

<template>    <div class="son">        <h2>我是son组件</h2>        <teleport_dialog/>    </div></template><script>    import Teleport_dialog from "./teleport_dialog";    export default {        name: "teleport_son",        components: {Teleport_dialog}    }</script><style scoped>    .son{        background-color: yellow;        padding: 10px;    }</style>

teleport_dialog.vue

<template>    <div>        <button @click="isShow=true">点我弹窗</button>        <div class="dialog_container" v-if="isShow">            <h2>我是弹窗组件</h2>            <div class="dialog_body">                <h2>我是内容</h2>                <h2>我是内容</h2>                <h2>我是内容</h2>                <h2>我是内容</h2>            </div>            <button @click="isShow=false">敞开按钮</button>        </div>    </div></template><script>    import {ref} from 'vue'    export default {        name: "teleport_dialog",        setup(){            let isShow=ref(false);            return{                isShow            }        }    }</script><style scoped>    .dialog_container{        width: 500px;        height: 300px;        background: red;    }</style>

实现的成果如下


当咱们点击按钮的时候,成果是这样的


点击按钮的时候,弹窗显示,然而在son组件外面会扭转son的高度,这样子不太好看,如果弹窗应用定位的话能够使其脱离文档流,而不会撑开son外面的高度,然而postion:absolute是依据最近的有定位元素的父元素进行定位的,所以此办法不牢靠,咱们须要实现的成果是依据body来定位

批改dialog的款式

<template>    <div>        <button @click="isShow=true">点我弹窗</button>        <teleport to="body">            <div class="mask" v-if="isShow">                <div class="dialog_container">                    <h2>我是弹窗组件</h2>                    <div class="dialog_body">                        <h2>我是内容</h2>                        <h2>我是内容</h2>                        <h2>我是内容</h2>                        <h2>我是内容</h2>                    </div>                    <button @click="isShow=false">敞开按钮</button>                </div>            </div>        </teleport>    </div></template><script>    import {ref} from 'vue'    export default {        name: "teleport_dialog",        setup(){            let isShow=ref(false);            return{                isShow            }        }    }</script><style scoped>    .mask{        position: absolute;        top: 0px;        left: 0px;        right: 0px;        bottom: 0px;        background: rgba(0,0,0,.5);    }    .dialog_container{        width: 500px;        height: 300px;        background: red;        position: absolute;        left: 50%;        top: 50%;        margin-left: -250px;        margin-top: -150px    }</style>

Suspense

作用:期待异步组件时渲染一些额定的内容,让利用有更好的用户体验

应用步骤:

  • 异步引入组件
import {defineAsyncComponent} from 'vue'const child=defineAsyncComponent(()=>import('./components/Child.vue'))
  • 应用Suspense包裹组件,并配置好default和fallback
<template>    <div class="app">        <h3>我是app组件</h3>     <Suspense>        <template v-slot:default>                <Child/>        </template>        <template v-slot:fallback>            <h3>加载中......</h3>        </template>     </Suspense>    </div></template>

示例

suspense.vue

<template>    <div class="suspense">        <h2>我是suspense组件</h2>        <child/>    </div></template><script>    import Child from "./child";    export default {        name: "suspense01_component",        components: {Child}    }</script><style scoped>    .suspense{        background-color: red;        padding: 10px;    }</style>

child.vue

<template>    <div class="child">        <h2>我是child组件</h2>    </div></template><script>    export default {        name: "child"    }</script><style scoped>    .child{        background-color: orange;        padding: 10px;    }</style>

如果应用以上代码引入的话,那么suspense和child组件将会同时加载,如果child外面还有其它子组件的话,子组件外面还有子组件,在网络慢的状况下,用户可能就看不到child组件以及child外面其它的组件。这会影响用户体验,批改对应的代码

suspense.vue

<template>    <div class="suspense">        <h2>我是suspense组件</h2>        <suspense>            <template v-slot:default>                <child/>            </template>            <template v-slot:fallback>                <h3>请稍等,加载中。。。</h3>            </template>        </suspense>    </div></template><script>    // import Child from "./child"; // 动态引入    import {defineAsyncComponent} from 'vue'    const Child=defineAsyncComponent(()=>import('./child')) //  异步引入    export default {        name: "suspense01_component",        components: {Child}    }</script><style scoped>    .suspense{        background-color: red;        padding: 10px;    }</style>

child.vue

<template>    <div class="child">        <h2>我是child组件</h2>        <h2>以后sum的值是:{{sum}}</h2>    </div></template><script>    import {ref} from 'vue'    export default {        name: "child",        setup(){            let sum=ref(0);            return new Promise((resole,reject)=>{                setTimeout(()=>{                    resole({                        sum                    });                },3000)            })        }    }</script><style scoped>    .child{        background-color: orange;        padding: 10px;    }</style>

为了看到成果,提早了3秒之后显示组件

Vue2x和Vue3x的其它变动

1.全局API的转移

vue2.x有许多全局API和配置,例如:全局注册组件、注册全局指令等

  • 注册全局组件
Vue.component('MyButton',{    data:()=>{        count:0,    },        template:'<button @click="count++">clicked {{count}} times</button>'    });
  • 注册全局指令
Vue.directive('focus',{    inserted:el=>el.foucus})
  • Vue3.0中对这些API做出了调整

将全局的API,即Vue.xxx调整到利用实例app上

2.x 全局API(Vue)3.x 实例API(app)
Vue.config.xxxapp.config.xxx
Vue.config.productionTip移除
Vue.componentapp.component
Vue.directiveapp.directive
Vue.mixinapp.mixin
Vue.useapp.use
Vue.prototypeapp.config.globalProperties

2.其它扭转

  • data 选项应始终被申明为一个函数
  • 适度类的写法

Vue2.x的写法

.v-enter.v-leave-to{    opacity:0,}v-leave,v-enter-to{    opacity:1}

Vue3.x的写法

.v-enter-from,.v-leave-to{    opacity:0}.v-leave-to,.v-enter-to{    opacity:1}
  • 移除keyCode作为v-on的修饰符,同时也不再反对config.keyCodes
  • 移除v-on.navtive修饰符

父组件绑定事件

<my-component>    v-on:close="handleComponentEvent"v-on:click="handleNativeClickEvent"</my-component>

子组件中申明自定义组件

export default{    emits:['close']}
  • 移除过滤器(filter)

    过滤器尽管看起来不便,但它须要一个自定义语法,突破大括号内表达式'只是javascript'的假如,这不仅有学习老本,而且有实现成 本, 倡议用法调用或者计算属性去替换过滤器

    Vue3x相干材料

相干库名称在线地址
Vue 3.0 官网文档(英文)在线地址
Vue 3.0 中文文档在线地址
Composition-API手册在线地址
Vue 3.0 源码学习在线地址
Vue-Router 官网文档在线地址
Vuex 4.0Github
vue-devtoolsGithub
Vite 源码学习线上地址
Vite 2.0 中文文档线上地址
Vue3 新动静线上地址

Vue3xUI组件库

Element-plus

  • 仓库地址:https://github.com/element-pl...
  • 文档地址:https://element-plus.gitee.io...
  • 开源我的项目

    • Vue 3.0 + Vite 2.0 + Vue-Router 4.0 + Element-Plus + Echarts 5.0 + Axios 开发的后盾管理系统:https://github.com/newbee-ltd...
    • Vue3.0+TypeScript+NodeJS+MySql编写的一套后盾管理系统:https://github.com/xiaoxian52...

Ant Design of Vue

  • 仓库地址:https://github.com/vueCompone...
  • 文档地址:https://antdv.com/docs/vue/in...
  • 开源我的项目

    • AntdV后盾管理系统:https://github.com/iczer/vue-...
    • vue3.x + ant-design-vue(beta 版本,收费商用,反对 PC、平板、手机):https://github.com/chuzhixin/...
    • 基于 Vue3.0 + Vite + Ant Design Vue:https://github.com/lirongtong...

Vant

  • 仓库地址:https://github.com/youzan/vant
  • 文档地址:https://vant-contrib.gitee.io...
  • 开源我的项目

    • newbee-mall Vue3 版本:https://github.com/newbee-ltd...
    • 高仿微信笔记本:https://github.com/Nick930826...
    • 仿京东淘宝电商:https://github.com/geekskai/v...

NutUI 3

  • 仓库地址:https://github.com/jdf2e/nutui
  • 文档地址:https://nutui.jd.com/#/index

    Vue3X相干视频

相干库名称在线地址
Vue 3.0 实战星座物语 H5 我的项目在线地址
Vue 3.0 UI 组件库开发在线地址
Vue 3.0 + Vite 手册浏览在线地址
Vue 3.0 入门之我的项目搭建(杨村长)在线地址
Vue 3.0 入门(技术胖)【不太倡议举荐】在线地址
Vite 2.0 插件开发指南在线地址
Vue 3.0 + Vite 2.0 疾速搭建 Electron 利用在线地址
Vue3.0视频(强烈推荐)在线地址
Vue3.0驾照题库在线地址

参考资料

掘金地址:https://juejin.cn/post/695512...

Vue.js官网:https://v3.cn.vuejs.org/

bilibili地址:https://www.bilibili.com/vide...

MDN地址:https://developer.mozilla.org...

ES6地址:https://es6.ruanyifeng.com/

总结

总体来说,学完这篇博客根本涵盖了Vue3中的绝大部分内容,写的也特地具体,笔记,配套材料和实战视频都有,次要看你愿不愿意抽出工夫去学习,我本人认为学习是一辈子的事件,当然学习也是一件特地孤单的事件。如果本篇文章对您有所帮忙的话,记得点赞、珍藏和关注,原创不易,三连反对,感激大家!