Vue3入门

1.Vue快速上手

vue3地址 

地址:https://github.com/vue3

 8.2 Vue3优势

(1)性能的提升。

(2)源码升级。

(3)支持TypeScript。

(4)新特性。

8.1创建Vue3工程

 官方文档:快速上手 | Vue.jsVue.js - 渐进式的 JavaScript 框架icon-default.png?t=N7T8https://cn.vuejs.org/guide/quick-start.html

8.1.1 vue-cli创建

##1.查看vue-cli版本:
vue --version 
vue -V

##2.如果版本在4.5.0以下升级或安装执行:
npm install -g @vue/cli 

##3.创建:
vue create vue3_test

##4.启动:
cd vue_test
npm run serve

 8.1.2 vite 创建

vite 官网:

Vite | 下一代的前端工具链下一代前端工具链icon-default.png?t=N7T8https://cn.vitejs.dev/weback打包模式:

vite打包模式:

##1.创建工程:
npm init vite-app 

##2.进入工程目录:
cd 

##3.安装依赖:
npm install

##4.启动:
npm run serve

 8.2常用composition API

组合式API

8.2.1 setup函数

(1)概念:Vue3中的一个新的配置项,值为一个函数。

(2)setup是所有Compositon API的“表演的舞台”。

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

(4)setup函数中的两种返回值:

        (4.1)若返回一个对象,则对象中的属性,方法,在模板中均可以直接使用(常用)。

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

(5)注意:

        (5.1)尽量不要与Vue2.x混用:

                (5.1.1)Vue2.x中的(data,methods,computed等等)可以访问到setup的方

                               法,属性。

                (5.1.2)setup中不能访问到Vue2.x中的(data,methods,computed等等)。

                (5.1.3)如果重名,setup优先级高。

        (5.2)setup不能是一个async函数,因为返回值不再是一个return对象,而是

                    promise,模板看不到return中的属性

总结:setup用于替代 Vue2 中的 beforeCreate 和 created 钩子函数。setup 选项是一个函数,它在组件实例被创建之前执行,并返回一个包含状态和方法等配置信息的对象。

import { h } from "vue";
export default {
  name: "App",
  setup() {
    let person = {
      name: "张三",
      age: 18,
    };
    function sayHello() {
      alert(`${person.name}:hello word,年龄:${person.age}`);
    }
    //返回一个对象
    return {
      person,
      sayHello,
    };
    //返回一个渲染对象
    /* return ()=>{
    return h('h1',person.name)
  } */
  },
};

8.2.2 ref函数

(1)作用:用于定义一个响应式的数据。

(2)语法:const xxx=ref(initValue)。

        (2.1)创建一个包含响应式数据的引用对象(reference对象)。

        (2.2)js中操作数据用xxx.value操作。

        (2.3)模板中读取:直接{{xxx}}。

(3)备注:

        (3.1)接收基本类型:响应式还是依靠Object.defineProperty()的get与set实现。

        (3.2)接收对象类型:内部使用了Vue3中的一个新函数reactive函数,底层是用Proxy

                     实现的。

import { ref } from "vue";
export default {
  name: "App",
  setup() {
    /* let person = {
      name: ref("张三"),
      age: ref(18),
    }; */
     let person =ref({
      name: "张三",
      age: 18
    });
    function changeInfo(){
      console.log(person)
      person.value.age=25
    }
    return {
      person,
      changeInfo
    };
  },
};

8.2.3 reactive函数

(1)作用:定义一个响应式数据(基本数据不用他,用ref函数)。

(2)语法:const 代理对象=reactive(源对象) 接受一个对象或数组,返回一个代理对象(proxy对象)。

(3)reactive定义的响应式数据是深层次的。

(4)内部基于es的proxy实现,通过代理对象操作源对象内部的数据。

let person = reactive({
      name: "张三",
      age: 18,
      hobby: [1, 2, 3, 4, 5],
      a: {
        b: {
          c: {
            d: 666,
          },
        },
      },
    });

    function changeInfo() {
      person.hobby[0]=111;
    }
    return {
      person,
      changeInfo,
    };

8.2.4 响应式原理

8.2.4.1 vue2中的响应式

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

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

问题:

        (1)新增属性,删除属性,界面不会更新。(监测不到数据修改)

        (2)不通过制定方法修改数组,界面不会自动更新。(监测不到数据修改)


    
    Document



当我们创建一个响应式对象,修改其中的属性会触发相应的方法,而我们添加的属性,并不是响应式的,并非通过get和set方法操作属性,而vue2的更新dom操作就是在set方法中实现的,所以监控不到属性的新增和删除。

具体参考:1.Vue核心-CSDN博客

Vue核心的1.5小结数据代理以及1.10.5 Vue无法检测到的数据改变和解决方案。

8.2.4.2 vue3中的响应式

(1)了解Reflect(反射):

 let person = {
      name: "张三",
      age: 18,
    };
    let p={}

当出现如下代码时,会出现语法错误导致页面无法正常渲染。

Object.defineProperty(p, "name", {
      get() {
        console.log("有人读取了p.name属性");
        return person.name;
      }
    });
    Object.defineProperty(p, "name", {
      get() {
        console.log("有人读取了p.name属性");
        return person.name;
      }
    });

当我们使用Reflect操作时,失败并不会出现错误,而是返回一个false。

  const a= Reflect.defineProperty(p, "name", {
      get() {
        console.log("有人读取了p.name属性");
        return person.name;
      }
    });
    const b= Reflect.defineProperty(p, "name", {
      get() {
        console.log("有人读取了p.name属性");
        return person.name;
      }
    });
    console.log('a',a,'b',b)
a true b false

所以,当我们使用第一种方法,去操作一个属性有可能会出现异常,导致页面出现问题,而是用第二种方式去操作,会返回一个操作结果是否成功。站在代码兼容性考虑,是有必要使用Reflect来操作属性的。

(2)了解Proxy:

p=new Proxy(person,{
        //读取属性调用
        get(target,propName){
            console.log('有人读取了p的',propName,"属性,值",person.propName)
            return target[propName]
        },
        //修改,新增属性调用
        set(target,propName,value){
            console.log('有人修改了p的',propName,"属性,原值",person.propName,'改为:',value)
             target[propName]=value
        },
        //删除属性调用
        defineProperty(target,propName){
            console.log('有人删除了了p的',propName,"属性")
           return delete target[propName]
        },
        
    }) 

创建一个Proxy对象当我们对该对象进行属性操作时:

当我们对对象的属性进行增删改查操作时,都会被相应的监控的,并且执行的操作会在person上执行。

(3)总结:当结合了Proxy和Reflect后就形成了vue3的响应式:

                   Proxy(代理):拦截对象中任意属性的变化(增删改查)。

                   Reflect(反射):对被代理对象的属性执行操作(不会出现语法错误)。

p=new Proxy(person,{
        //读取属性调用
        get(target,propName){
            console.log('有人读取了p的',propName,"属性,值",person.propName)
            return Reflect.get(target,propName)
        },
        //修改,新增属性调用
        set(target,propName,value){
            console.log('有人修改了p的',propName,"属性,原值",person.propName,'改为:',value)
             Reflect.set(target,propName,value)
        },
        //删除属性调用
        defineProperty(target,propName){
            console.log('有人删除了了p的',propName,"属性")
           return Reflect.defineProperty(target,propName)
        },
        
    }) 

Proxy和Reflect的浅层理解:Proxy和Reflect-CSDN博客

8.2.5 reactive和ref

从定义角度相比:

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

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

从原理角度相比:

        ref:通过Object.defineProperty()的get和set来实现响应式(数据劫持)。

        reactive:通过Proxy来实现响应式(数据劫持),并通过Reflect操作源对象内数据。

从使用角度相比:

        ref:操作数据需要用xxx.value,读取时不需要xxx.value。

        reactive:操作和读取数据都不用xxx.value

8.2.6 setup的两个注意点

(1)setup执行时机在beforeCreate之前,且只执行一次,this是undefined。

(2)setup的参数:

        (2.1)props:值对象,包含:组件外传递过来的,且组件内部生命接受了的属性。

        (2.2)context:上下文对象:

                (2.2.1)attrs:值对象,外部传递进来,但props未接收的属性(this.$attrs)。

                (2.2.2)slots:收到的插槽内容,相当于this.$slots。(最好用v-slot命名)。

                (2.2.3)emit:分发自定义事件的函数(this.$emit)。

8.2.7 计算与监视属性

8.2.7.1 计算属性 computed

(1)与vue2.x的computed配置功能一致。

(2)写法:   

import { computed } from "vue";
setup(props, context) {
    const data = reactive({
      person: {
        name: {
          firstName: "张",
          lastName: "三",
        },
        age: 18,
      },
    });
    //计算属性--简写--只考虑回写没考虑修改
    /* data.person.fullName=computed(()=>{
       return data.person.name.firstName+"--"+ data.person.name.lastName
    }) */
    //计算属性--完整写法
    data.person.fullName = computed({
      get() {
        return data.person.name.firstName + "-" + data.person.name.lastName;
      },
      set(value) {
        const nameArr = value.split("-");
        data.person.name.firstName = nameArr[0];
        data.person.name.lastName = nameArr[1];
      },
    });
    return {
      data,
    };
  },
8.2.7.2 监视属性 watch

与vue2中的watch功能一致。

问题:

        (1)监视reactive定义的响应式数据时,oldValue无法正确获取(由于旧值和新值实际

                 上是同一个引用,因此旧值和新值看起来总是相同的。),且默认开启了深度监

                 视,无法关闭(deep配置失效)。

        (2)监视reactive定义的响应式数据中的某个对象属性时,deep配置又生效了。

 //情况1:监视ref定义的一个响应式数据
    watch(sum,(newValue, oldValue)=>{
        console.log("sum被修改了", newValue, oldValue);
    }) 

    //情况2:监视ref定义的多个响应式数据
    watch([sum,msg],(newValue, oldValue)=>{
        console.log("sum或msg被修改了", newValue, oldValue);
    })
    //传入第三个参数配置对象
    watch(sum,(newValue, oldValue)=>{
        console.log("sum被修改了", newValue, oldValue);
    },{immediate:true})
    /* 
        情况3:监视reactive定义的对象 全部属性
        1.reactive定义的数据无法正确获取oldValue 
        2.强制开启了深度监视且无法关闭(deep配置失效了)
    */
    watch(data,(newValue,oldValue)=>{
        console.log('person变化了',newValue,oldValue)
    })
    /* 
        情况4:监视reactive定义的对象 某个属性
    */
    watch(()=>data.age,(newValue,oldValue)=>{
        console.log('person变化了',newValue,oldValue)
    })
    /* 
        情况5:监视reactive定义的对象 某些属性
    */
     watch([()=>data.age,()=>data.name],(newValue,oldValue)=>{
        console.log('person变化了',newValue,oldValue)
    }) 
    /* 
        特殊情况:监视reactive定义的对象 中的对象属性
    */
     watch(()=>data.job,(newValue,oldValue)=>{
        console.log('person变化了',newValue,oldValue)
    },{deep:true}) 
8.2.7.2 补充

(1)监视ref定义的基本数据类型的值时,不可以用xxx.value否则无法监视到。

(2)监视ref定义的对象属性时,需要用xxx.value否则由于内存中地址未改变,无法监视到ref请求reactive生成的Proxy的代理类,也可以开深度监视来实现,监视对象属性中的内容。

8.2.7.3 wactcEffect函数

watcg:既要指明监视的属性,也要指明监视的回调。

watchEffect:不指明监视的属性,在回调中用到了哪个属性就监视哪个属性。

watchEffect优点类似于computed:

        computed注重的是结果(回调函数的返回值),所以必须return。

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

    watch(data, (newValue, oldValue) => {
      console.log("person变化了", newValue, oldValue);
    },{immediate:true}); 

    watchEffect(()=>{
      console.log('watchEffect的回调')
      const x=data.age
    })

8.2.8 Vue3中的生命周期

Vue3.0中可以继续使用Vue2.x的生命周期钩子,但是有两个被更名:        

beforeDestroy===》beforeUnmount
destroyed    ===》unmounted

Vue3.0也提供了Composition API形式的生命周期钩子,与Vue2.x对应关系如下:

       

beforeCreate   ===>    setup()
created        ===>    setup()
beforemount    ===>    onBeforeMount
mounted        ===>    onMounted
befoerUpdate   ===>    onBeforeUpdate
updated        ===>    onUpdated
beforeUnmount  ===>    onBeforeUnmount
unmounted      ===>    onUnmounted

App:




Demo:




8.2.9 自定义hook函数

定义:本质是一个函数,把steup函数中使用的Composition API进行的封装。

类似于Vue2.x中的mixin。

自定义hook的优势:服用代码,让setup中的逻辑更加清楚。

新建hooks文件夹,并创建功能对应的js文件(以点击获取鼠标当前xy轴位置为例):

 userPoint.js

import {
    reactive,
    onMounted,
    onBeforeUnmount
} from "vue";

export default  function () {
    let point = reactive({
        x: 0,
        y: 0
    })
    function savePiont(event) {
        point.x = event.pageX
        point.y = event.pageY
        console.log(event.pageX)
        console.log(event.pageY)
    }
    onMounted(() => {
        window.addEventListener('click', savePiont)
    }),
        onBeforeUnmount(() => {
            window.removeEventListener('click', savePiont)
        })
    return point
}

Demo.vue




8.2.10 toRef

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

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

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

扩展:toRefs与toRef功能一致,但是可以创建多个ref对象,已发toRefs(person)。




8.3 其他Composition API

8.3.1 shallowReactive和shallowRef

shallowReactive:只处理对象最外层属性的响应式(浅响应式)。

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

使用场景:

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

        如果有一个对象,后续不会修改对象属性,而是产生新的对象替换===》shallowRef。

8.3.2 readonly和shallowReadonly

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

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

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

8.3.3 toRaw和markRaw

toRaw:

        作用:讲一个有reactive生成的响应式对象转为普通对象。(ref的不行)

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

                          页面的更新。

markRaw:

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

        使用场景:

                (1)如果我们声明了一个reactive的对象,后续对这个对象进行属性添加的操作,

                         添加上的属性默认就是响应式的,而有些值不应该设置为响应式的,例如:

                         异常复杂的第三方类库等等。

                (2)当显然具有不可变数据源的大列表时,条过响应式转换可以提高性能。

8.3.4 customRef

作用:创建一个自定义的ref,并对其依赖项跟踪和更新出发进行显示控制(可以实现自定义的控制页面更新时机,track告诉get方法这个参数需要跟踪,triger通知vue更新dom)。

实现防抖:




8.3.5 provide与inject

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

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

具体写法:

祖组件:

setup() {
    let car=reactive({
      name:'奔驰',
      price:400000
    })
    //给自己的后代组件传递数据
    provide('car',car)

后代组件:

 let car=inject('car')
    console.log("二级子组件",car)
  },

8.3.6 响应式数据的判断

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

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

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

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

8.4 Composition API的优势

8.4.1 Options API 

传统的OptionsAPI(配置API)中,新增或修改一个需求,要分别在data,methods,computed里修改

8.4.2 Composition API

让相同功能的变量,函数等等更加有序的组织在一起(借助hook函数)。

8.5 新的组件

8.5.1 Fragment

在Vue2.x中,组件必须有一个跟标签。

在Vue3中,组件可以没有跟标签,内部会将多个标签包含在一个Fragment的虚拟内存中。

优势:减少标签层级,减小内存占用。

8.5.2 Teleprot

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




将代码直接传送到html的body内。

8.5.3 Suspense

等待异步组件时渲染一些额外内容,让应用有更好的用户体验。

使用步骤:

        异步引入组件:

import { defineAsyncComponent } from "vue";
const Demo = defineAsyncComponent(() => import("./componets/Demo1.vue"));

使用Supense包裹组件,并配置好default与fallback:


      
      
    

8.6 其他

8.6.1 全局API转移

Vue2.x中有序地全局API和配置。

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

//全局组件
Vue.component('MyButton',{
data:()=>({
    cont:0
}),
template:''
})
//全局指令
Vue.directive('focus',{
    inserted:el=>el.focus()
})

Vue3对这些API做出了调整:

        将全局API(Vue.xxx)调整到应用实例app上

8.6.2 其他改变

8.6.2.1 data选项

data选项始终都要被声明为一个函数。

8.6.2.2 过渡类名的更改

        Vue2.x写法:

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

        Vue3写法:

.v-enter-from,
v-leave-to{
    opacity:0
}
.v-leave-from,
v-enter-to{
    opacity:1
}
8.6.2.3 移除keyCode

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

8.6.2.4 移除native

移除native作为v-on修饰符:

        父组件绑定事件:

        子组件js声明自定义事件(不声明则被认为是js原生事件):

export default {
    emits:['close']
}
8.6.2.5 移除过滤器

过滤器虽然看起来很方便,但是他需要一个自定义语法,打破大括号内表达式只是JavaScript的假设,不仅有学习成本还有实现成本,建议用方法或计算属性替换过滤器。

你可能感兴趣的:(vue.js,前端,javascript)