vue3相较于vue2的变化,以及js不常见合集

365 阅读6分钟

一、vue3快速上手

初始须知:要使用vue-cli来创建项目必须要保证vue-cli的版本在4.5.0以上。
vue3相比较vue2明显的变化:速度更快,打包之后的体积更小。

二、常用Composition API

1.setup(是一个函数)

setup里面能写data,methods,computed,watch,生命周期钩子,等等东西,全都往setup里面写

上面这句话我们这么理解,以前我们vue2是这样写的

export default {
  name: 'App',
  components: {},
  data(){
      return {}
  },
  methods: {},
  watch: {},
  //生命周期
  created(){},
  //还有若干个生命周期...
}

现在我们vue3这样写

export default {
  name: 'App',
  components: {
    HelloWorld
  },
  //暂时不考虑响应式,只是测试一下setup
  setup(){
    //数据
    let name = '张三'
    let age = 18
    //方法
    function sayHello(){
        alert(`我叫${name}今年${age}岁了`)
    }
    return {
        name,
        age,
        sayHello
    }
  }
}

2.ref函(函数)

它的作用是将一个数据变成响应式数据,其具体的使用方法如下:


import {ref} from 'vue'
export default {
 setup(){
   let name = ref('张三')
   let obj = ref({
       type = '职位',
       salary = '10k'
   })
   name.value = '李四'
   obj.value.type = '前端开发工程师'
 }
}

2.1 unref

如果参数是一个ref,则返回内部值,否则返回参数本身。这是 val = isRef(val) ? val.value : val 的语法糖函数。

import { unref } from "vue";
let msgref = ref('你好')
console.log(unref(msgref)) // 你好
let msg = '你好'
console.log(unref(msg)) // 你好

3.reactive(函数)

它的作用是对于非基本类型的数据实现响应式,使用方法如下

import {ref,reactive} form 'vue'
export default {
    setup(){
        let obj = reactive({
            type: '前端开发工程师',
            salary: '30k'
        })
        obj.type = '后端开发工程师'
        let hobby = reactive(['抽烟','喝酒','烫头'])
        hobby[0] = '学习'
    }
}

4.vue2存在的一些问题

vue2bug1

如下图,vue2中的对象新增一个sex属性检测不到它的变化,这是vue2的一个bug

data(){
    return {
        person:{
            name: '张三',
            age: 18
    }
}
methods:{
    addSex(){
        //能新增成功,但是界面不更新,原因是它不是响应式的
        //console.log(this.person.sex)
        //this.person.sex = '女'
        //console.log(this.person.sex)
        //新增成功,且界面更新,是响应式的
        this.$set(this.person,'sex','女')
        //Vue.set(this.person,'sex','女')
    }
}

vue2bug2

vue2删除对象属性界面监测不到bug

data(){
    return {
        person:{
            name: '张三',
            age: 18
        }
    }
}
methods:{
    deleteName(){
        //能删除成功但是界面不响应
        //console.log(this.person.name)
        //delete this.person.name
        //console.log(this.person.name)
        //删除成功,且界面响应,是响应式的
        this.$delete(this.person,'name')
    }
}

5.vue2响应式原理

下面例子触发方式:控制台p.name = '李四'

let person = {
    name: '张三',
    age: 18
}

let p = {}
Object.defineProperty(p,'name',{
    configurable: true,
    get() {//有人读取name时调用
        return person.name
    },
    set(value) {//有人修改name时调用
        console.log('有人修改了name属性'
        person.name = value
    }
})

6.vue3响应式原理

下面例子触发方式:控制台p.name = '李四'

let person = {
    name: '张三',
    age: 18
}
const p = new Proxy(person,{
    //有人读取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 Reflect.deleteProperty(target,propName)
    }
})

7.setup的props,content。

export default {
    //在vue3中使用自定义事件需要声明
    props: ['msg','school'],
    emits: ['hello'],
    setup(props, context){
        //props就是我们vue2中的props
        //context里面有一项是emit其意思其实跟vue2的有一句是一样的
        this.$emit('test',value)
        context.emit('test',value)
        //context.attrs
        //context.emit
        //context.slot
    }
}

8.vue3中computed

import {computed} from 'vue'
setup() {
    ...
    //计算属性-简写
    let fullName = computed(()=>{
        return person.firstName + '-' + person.lastName
    })
    //计算属性-完整
    //vue3中的computed属性必须要有返回值
    let fullName = computed({
        get(){
            return person.firstName + '-' + person.lastName
        },
        set(value){
            const nameArr = value.split('-')
            person.firstName = nameArr[0]
            person.lastName = nameArr[1]
        }
    })
    person.fullName = fullName
}

9.vue3中watch监听reactive数据

情景一:监听ref定义的基本数据类型

import {watch} from 'vue'

export default {
    setup(){
        //监听单个的值
        watch(sum,(newval,oldval)=>{
            console.log(newval,oldval)
        })
        //立即执行监听
        watch(sum,(newval,oldval)=>{
            console.log(newval,oldval)
        },{immediate: true,deep: true})
        
    }
}

情景二:监听ref定义的复杂数据类型

setup(){
    //监听多个值
    watch([sum,msg],(newval,oldval)=>{
        //[],[]
        console.log(newval,oldval)
    })
}

情景三:监听reactive定义的一个响应式数据的全部属性

注意1:此处无法正确的获取oldValue 注意2:强制开启了深度监听(deep配置无效)

let person = reactive({
    name: '张三',
    age: 18
})
setup(){
    //监听多个值
    watch(person,(newval,oldval)=>{
        console.log('person变化了',newval,oldval)
    })
}

情景四:监听reactive定义的复杂数据类型的某个数据

let person = reactive({
    name: '张三',
    age: 18
})
setup(){
    //监听多个值
    watch(()=>person.age,(newval,oldval)=>{
        console.log('person的age变化了',newval,oldval)
    })
}

情景五:监听reactive定义的某些响应式数据

setup(){
    watch([()=>person.name,()=>person.age],(newval,oldval)=>{
        console.log('person的age或者name变化了',newval,oldval)
    })
}

特殊情况

setup(){
    let person = reactive({
        name: '张三',
        age: 18,
        job: {
            j1: {
                salary: 20
            }
        }
    })
    watch(()=>person.job, (newval,oldval)=>{
        console.log('person的age或者name变化了',newval,oldval)
    },{deep: true})
    //此处由于监视的是reactive定义的对象中的某个属性,所以deep配置有效,不配置监听不到
}

10.vue3中watch监听ref数据

import {watch,ref} from 'vue'
export default {
    let sum = ref(0)
    let person = ref({
        name: '张三',
        age: 18,
        job:{
            j1: {
                salary:20
            }
        }
    })
    setup(){
        watch(sum,(newValue, oldValue)=>{
            console.log('sum的值变化了',newValue, oldValue)
        })
        watch(person,(newValue, oldValue)=>{
            console.log('person的值变化了',newValue, oldValue)
        },{deep:true})
    }
}

11.watchEffect函数

watchEffect不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性

import {watchEffect} from 'vue'

export default {
    setup(){
        watchEffect(()=>{
            const x1 = sum.value
            const x2 = person.age
            console.log('watchEffect配置的回调执行了')
        })
    }
    
}

12.vue3里面的生命周期

有两个生命周期发生了改变

  • beforeDestroy ===> beforeUnmount
  • destroyed ===> unmounted

12.1 api形式的生命周期钩子,与vue2中钩子的对应关系如下

  • beforeCreate ===> setup()
  • created ===> setup()
  • beforeMount ===> onBeforeMount
  • mounted ===> onMounted
  • beforeUpdate ===> onBeforeUpdate
  • updated ===> onUpdated
  • beforeUnmount ===> onBeforeUnmount
  • unmounted ===> onUnmounted

注意:组合式生命周期比配置项生命周期要快一点。

13.自定义hook函数

什么是hook?--本质是一个函数,把setup函数中使用的Composition API进行了封装。 类似于vue2.x中的mixin。 自定义hook的优势:复用代码,让setup中的逻辑更清晰易懂

example: hooks文件夹下usePoint.js文件

import {reactive, onMounted, onBeforeUnmount} from 'vue'
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
}

另一个文件引入使用:

import usePoint from '../hook/usePoint'
export default {
    setup(){
        const point = usePoint()
        return {point}
    }
}

14.toRef,toRefs

  • 作用:创建一个ref对象,其value值指向另一个对象中的某个属性。
  • 应用:要将响应式对象中的某个属性单独提供给外部使用时。
  • 拓展:toRefs跟toRef功能一致,但可以批量创建多个ref对象。

example:

import {reactive, toRef, toRefs}
setup(){
    let person = reactive({ 
        name: '张三', 
        age: 18, 
        job: { 
            j1: { 
                salary: 20 
            } 
        } 
    })
    return {
        person,
        //name: toRef(person, 'name'),
        //age: toRef(person, 'age'),
        //salary: toRef(person.job.j1, 'salary'),
        ...toRefs(person)//name,age,job
    }
}

15.nextTick

import { nextTick } from 'vue';
nextTick(() => {
    //这里写逻辑
})

JS合集

1.Promise

function getTreeselect() {
    return new Promise((resolve, reject) => {
        listMenuOptions().then(res => {
            resolve(res.data)
        }).catch(()=>{
            reject()
        })
    })
}
getTreeselect().then(()=>{})

async created() {
    let res = await this.getTreeselect()
    console.log('getTreeselect回来的结果', res)
}

2.try{}catch{}

try {
    this.getBusinessFn().then(res => {
        console.log('getBusinessFn回来的结果', res)
    }) // 从事业务
} catch (exception) {
    console.log(exception)
}

3.throw new Error()

4.在控制台抛出错误

throw new Error(`跑错`);

image.png

5.callback

function A(callback){
  let a = 5
  console.log('A')
  if (callback) callback(a);
}
function B(b){
  console.log('B')
  console.log('B中打印的a为:',b)
}
A(B)
//A B B中打印的a为: 5

6.实现定隔多久执行几次定时器然后清除定时器

let second = 3;
const timer = setInterval(() => { 
    second--; 
    if (second) { 
        toast.message = `倒计时 ${second} 秒`; 
    } else { 
        clearInterval(timer); 
    } 
}, 1000);

三、其Composition API

1.shallowReactive与shallowRef

  • shallowReactive:只处理最外层属性的响应式(浅响应式)。
  • shallowRef:只处理基本类型的响应式,不进行对象类型的响应式处理
  • 什么时候使用?
    • 如果有一个对象数据,结构比较深,但变化时只是外层属性的变化 ===>shoallowReactive。
    • 如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 ===> shallowRef。
      example:
import {reactive, shallowReactive, shallowRef} 
setup(){ 
    //只考虑第一层数据的响应式
    let person = shallowReactive({ 
        name: '张三', 
        age: 18, 
        job: { 
            j1: { 
                salary: 20 
            } 
        } 
     })
     //基本类型数据与ref效果相同,但是引用类型数据就不一样了
     //let x = shallowRef(0)
     let x = shallowRef({
         y: 0
     })
     //替换,不能修改,只能替换
     //<button @click="x = {y:888}" />
}

2.readonly, shallowReadonly

这种使用场景可以是从别人那里拿过来这么一个数据,然后不能改,先readonly然后再开始用

import {ref, reactive, toRefs, readonly, shallowReadonly} from 'vue'
export default {
    setup(){
        let sum = ref(0)
        let person = reactive({
            name: '张三',
            age: 18,
            job: {
                j1: {
                    salary: 20
                }
            }
        })
        //person = readonly(person)
        //这个对象里面的所有数据都只能只读
        person = shallowReadonly(person)
        //只是name,age不能改,但是可以改深层次的数据job里面的数据
        //sum = readonly(sum)
        sum = shallowReadonly
        return {
            sum,
            ...toRefs(person)
        }
    }
}

3.toRaw与markRaw

  • toRaw:
    • 作用:将一个由reactive生成的响应式对象转为普通对象。
    • 使用场景:用于读取响应式对象的普通对象,对这个普通对象的所有操作,不会引起页面更新。
  • markRaw:
    • 作用:标记一个对象,使其永远不会再成为响应式对象。
    • 应用场景:
      1.有些值不应该设置为响应式的,例如复杂的第三方类库等。 2.当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。

toRow只能处理reactive制造的响应式对象

import {ref, reactive, toRefs, toRaw, markRaw} from 'vue'
export default {
    setup(){
        let sum = ref(0)
        let person = reactive({
            name: '张三',
            age: 18,
            job: {
                j1: {
                    salary: 20
                }
            }
        })
        function showRawPerson() {
            const p = toRaw(person)
            console.log(p)
            //将proxy对象变为普通对象,不能响应
        }
        function addCar() {
            let car = {name: '奔驰',price: 40}
            person.car = markRaw(car)
            //car添加到proxy对象上会变成响应式,经此处理挂载上去之后不会再变成响应式。
        }
        return {
            sum,
            ...toRefs(person),
            showRawPerson
        }
    }
}

4.customRef

  • 作用:创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显示控制。
  • 实现防抖效果
<template>
    <input type="text" v-model="keyword">
    <h3>{{keyword}}</h3>
<template/>
<script>
    import {ref,customRef} from 'vue'
    export default {
        name: 'Demo',
        setup(){
            //let keyword = ref('hello')//使用vue准备好的内置ref
            //自定义一个myRef
            function myRef(value, delay){
                let timer
                //通过customRef去实现自定义
                return customRef((track, trigger)=>{
                    return {
                        get(){
                            track()//告诉vue这个value值需要被'追踪'的
                            return value
                        },
                        set(newValue){
                            clearTimeout(timer)
                            timer = setTimeout(()=>{
                                value = newValue
                                trigger()//告诉vue去更新界面
                            }, delay)
                        }
                    }
                })
            }
            let keyword = myRef('hello')
            return {
                keyword
            }
        }
    }
</script>

5.provide与inject

provide: 提供 inject:注入

  • 作用:实现祖孙组件间通信。

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

  • 具体写法:

1.祖组件中:

import {provide, reactive} from 'vue'
setup(){
    ...
    let car = reactive({name: '奔驰',price: '40万'})
    provide('car',car)
    ...
}

2.孙组件中:

import {inject} from 'vue'
setup(props,context){
    ...
    const car = inject('car')
    return {car}
}

6.响应式数据的判断

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

example:

setup(){
    let car = reactive({name: '奔驰',price: '40万'})
    let sum = ref(0)
    let car2 = readonly(car)
    
    console.log(isRef(sum))
    console.log(isReactive(car))
    console.log(isReadonly(car2))
    console.log(isProxy(car))
    console.log(isProxy(car2))
    //需要注意这一点,car2虽然被限制只读了,但是它任然是一个Proxy对象。
}

四、Composition API的优势

Opitions API存在的问题

使用传统Options API时,新增或者修改一个需求,就需要分别在data,methods,computed里修改。

Composition API的优势

我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起。

五、新的组件

1.Fragment

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

2.Teleport

teleport:传送(译)

  • 什么是Teleport? ——Teleport是一种能够将我们的组件html结构移动到指定位置的技术。
<template to="移动位置">
    <div v-if="isShow" class="mask">
        <div class="dialog">
            <h3>我是一个弹窗</h3>
            <button @click="isShow = false">关闭弹窗</button>
        </div>
    </div>
</template>

3.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>
    
    

六、其它

1.全局api的转义

  • Vue3.0中对这些API做出了调整:
    • 将全局的API,即:Vue.xxx调整到应用实例(app)上

image.png

2.其它改变

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

  • 移除v-on.native修饰符

  • 父组件中绑定事件

<my-component
    v-on: close="handleComponentEvent"
    v-on:click="handleNativeclickEvent"
/>
  • 子组件中声明自定义事件
<script>
    export default {
        emits: ['close']
    }
</script>
  • 移除过滤器(filter) ...

七、父子组件通信

1.vue3中父子组件通信

//子组件接收参数
const props = defineProps({
  pagination: {
    type: Boolean,
    required: false,
    default: false
  },
  appUnitSelect: {
    type: Number,
    required: false,
    default: 0
  }
})
//子组件接收参数简化写法
const props = defineProps({
  roleId: {
    type: [Number, String]
  }
});

//不带参数
@ok="handleQuery"
const emit = defineEmits(["ok"]);
emit("ok");

//父组件传递定义方法,带参数function getTabHeightFn(data: heightData)
@getTabHeightFn="getTabHeightFn"
//子组件接收方法
const emits = defineEmits(['getTabHeightFn'])
//子组件调用方法
emits('getTabHeightFn', { baseHeight, optionHeight });

2.vue2中父子组件通信

//子组件接收参数
props: { 
    childMsg: { 
        type: Array, 
        default: [0,0,0//这样可以指定默认的值 
    }
}
//父组件传递事件
<child @on-result-change="mychangHandle" :msg="msg">
//子组件调用事件
this.$emit('resultChange','hehe');

3.vue2中任意组件间通信

如果2个组件不是父子组件那么如何通信呢?这时可以通过eventHub来实现通信.

所谓eventHub就是创建一个事件中心,相当于中转站,可以用它来传递事件和接收事件.

使用一个空的 Vue 实例作为中央事件总线:

let Hub = new Vue(); //创建事件中心,注意Hub要放在全局

组件1触发:

<div @click="eve">
</div>
methods: { eve() { Hub.$emit('change','hehe'); //Hub触发事件 }}

组件2接收:

<div></div>
created() { Hub.$on('change'() => { //Hub接收事件 this.msg = 'hehe'; });}

4.vue3中页面缓存

参考:www.cnblogs.com/zhilu/p/138…keep-alive>裹的组件其会被缓存
例如:动态添加到UI上的一个li
没有被keep-alive>裹的话,ctivated不起作用的。 挂载后和更新前被调用的。

import { onActivated } from 'vue';
// 页面缓存
onActivated(() => {
  getList();
})

5.vue3中获取ref元素

import { getCurrentInstance } from 'vue';
const { proxy } = getCurrentInstance() as any;
proxy.$refs['pipe-echart']

scss常见合集

<div class="colour-display" :style="{'--color': scope.row.colour }"></div>
background-color: var(--color);