vue2到vue3的极简迁移之路

1、项目创建

支持两种方式:vuecli和vite。前者不在细说。提供vite的创建方法:

npm init vite-app vue3-demo

cd vue3-demo

npm install && npm run dev

可以看到,构建几乎是秒开。vite之所以构建迅速就是因为它和webpack走了相反的道路,如下图:

 

 很显然,vite一上来直接就把server准备好了,然后异步引入,你需要什么就引入什么,所以速度上占了优势。

2、组合api

目前vue3在写法上还是兼容vue2的,options API(data, computed,methods)还是可以写入组件,但是新增的组合api却应该更令人关注。

2.1、setup函数

setup函数就是组合api们大展身手的舞台了。所有相关的数据、方法全部写在里边。举个例子

export default {
    name: 'app',
    setup () {
        // 定义一个数据,注意这个数据不是响应式的
        let num = 100;
        function changNum () {
            num = 1000
        }
        return {
            num,
            changeNum
        }
    }
}

这里需要注意的就是以下两点:

1、setup的执行时机是在beforeCreate和created之间,所以没有this

2、setup中定义的方法和数据都要return出去,才能在模板中使用,否则无效

setup函数有个两个参数 - props和context。

第一个props,不再多说,就是父级传递过来的数据。

第二个context,字面意思是上下文。他里边主要包含了三个东西:attrs,slots,emits

attrs这个东西就是vue2中$attrs,俗称“捡漏之王”。只要是传递过来的数据,不在props中,就会在attrs中。这个很好理解。

slots这个就是插槽。所有的slot都存在这里。

emits对应的就是$emit。

很好理解,可以自己打印出来多试试。

2.2、响应式数据的创建 - ref和reactive

vue2中创建一个响应式的数据,直接写在data中就可以。但是在vue3中创建响应式数据,就必须引入组合api -- ref和reactive了。

这两个api都是创建响应式数据,他们之间有什么不同?

简单来说,ref创建基本数据类型的数据,reactive创建引用数据类型的数据。举个例子:

// 注意使用组合api比如显性引入  不能直接使用

import {ref, reactive} from 'vue'

export default {
    name: 'app',
    setup () {
        let num = ref(0);
        let person = reactive({
            name: 'www',
            age: 20,
            from: {
                country: {
                    province: 'xx'
                }
            }
        })

        // 注意 创建好的数据一定要return出去
        return {num, person}
    }
}

但是,这并不代表ref不能创建引用数据类型。只不过,ref创建的引用数据类型,底层走的还是reactive的方法。

那么reactive和ref的底层分别是什么?

vue2的响应式原理都是基于Object.defineProperty的。那么ref的响应式就是基于此,只有get和set,面对基本数据类型非常简单。但是对于负责的引用数据类型,就会使用proxy作为响应式映射。

两者区别,不在细说。

2.3、计算属性 - computed

同样的需要显性引入,然后传入一个函数,在函数中计算data,如下

import {computed} from 'vue'

export default {
    name: 'app',
    setup () {
       let str = ref('hello');
       let _str = computed(() => {
            return str + 'world'
        })

        return {
            str,
            _str
        }
    }
}

2.4、监控 - watch和watchEffect

数据的监控,基本用法还是和vue2类似,watch函数接收三个参数,第一个是监控的属性,第二个是一个函数,包含newval和oldval,第三个是配置项,比如immidate:true。举个例子:

这里需要注意,监控基本类型和引用类型的数据之间的写法是不同的

import {
    ref,
    watch
} from 'index.js';
setup () {
    
    // 基本类型的
    let num = ref(0);
    watch (num, (newVal, oldVal) => {
    	        
    }, {
        immidate: true    
    }) 

    // 引用类型的
    let person = {
        name: 'www',
        age: 30,
        job: {
            a: {
                b: 'aaaa'
            }
        }
    }
    // 需要以函数的形式
    watch (() => person.name, (newVal, oldVal) => {
    	        
    }, {
        immidate: true    
    }) 

}

监控多个属性

A、监控基本类型的数据多个属性

和vue2不同的地方就是watch可以同时监控多个属性,以数组的形式传入参数,同样也会以数组的形式接收结果。

......

watch ([num1, num2], (newVal, oldVal) => {
    console.log('watch -- val', newVal) // newVal -> [1, 2] 数组形式
}, {
    immidate: true
});

......

B、监控基本类型的数据多个属性

......

watch([() => person.name, () => person.age], (newVal, oldVal) => {
    console.log('person.name, person.age', newVal, oldVal);   
})

......

还有个watchEffect。这个api不在指定具体的监控的某个属性。它内部的函数中出现了谁,就会去监控谁。举个例子

watchEffect(() => {
    // 用到谁就监控谁
    let num = firstname.value;
    console.log('watchEffect ---')
})

2.5、hooks

这个hooks可以看作vue2中的mixins。具体做法就是新建一个js文件,把需要提取的公共代码写入进去。举个例子

// 获取当前鼠标点击的坐标 
// 提取这个公共功能模块
export default function () {
    let point = reactive({
        x: 0,
        y: 0
    })
    function savePoint (event) {
        point.x = event.pageX;
        point.y = event.pageY;
    }
    onMounted(() => {
        window.addEventListener('click', savePoint);
    })
    onBeforeUnmount(() => {
        window.removeEventListener('click', savePoint);
    })
    return point
}

// 使用的时候直接引入即可
import usePoint from 'xxxx.js'

setup () {
    let point = usePoint();

    return {point} 
}

2.6、provide和inject

这个很vue2的用法几乎一样,只不过需要单独引入。举个例子

// 祖辈组件负责提供数据
import {
    provide,
    reactive
}
setup () {
    let car = reactive({
        name: 'bmw',
        price: '40w'    
    })
    provide('car', car)
    
    return {
        car    
    }
}
// 后代组件
import {
    inject
} from 'vue'
setup () {
    inject('car');
    return {
        car    
    }
}

至此,习惯vue2开发的直接可以快速迁移到vue3,并且可以开始做一些功能了。上边这个几个api已经可以满足基本的开发需求了。

除此之外,还有一些黑科技需要提一下。

1、customRef

自定义ref。就是自己写get,set。实现一个自定义的响应式。举个例子,实现一个延迟的响应式

import {cutomRef} from 'vue'
let key = myRef('a'); // 自定义一个ref,它是一个函数
function myRef (value) {
    let timer;
    return customRef((track, trigger) => {
    // cutomRef内部需要写入个人实现的逻辑,所以是一个函数,参数也是一个函数
        return {
            get () {
                track();// track 是追踪变化,有了变化及时输出
                return value;
            },
            set (newVal) {
                clearTimeout(timer);
                timer = setTimeout(() => {
                    value = newVal;
                    trigger();// trigger 是触发修改
                }, 500);       
            }
        }
    })
}

2、teleport组件

这是个内部组件,可以直接使用。它的目的就是把这个组件包裹起来的dom,转移到某一个dom元素下。比如,写在一个组件中的对话框,你可以把它直接以到body下。

<Teleport to="body">

    <div></div>

</Teleport>

// 之后这个div的上级节点就会变成body

暂时这么多吧,自己再项目中慢慢体会一下。

最后,需要了解vite+vue3+ts+setup+pinia的,请移步至我的另外一篇文章:

一步步打造最新前端框架组合 —— vite+vue3+pinia+vue-router@4+setup+ts_ws_code_csdn的博客-CSDN博客vue3+vite+ts+setup+pinia+scss最新前端框架组合https://blog.csdn.net/jmszl1991/article/details/122825151?spm=1001.2014.3001.5502

猜你喜欢

转载自blog.csdn.net/jmszl1991/article/details/120022577