从vue2转向vue3,初步了解组合式写法,丧失响应性怎么办

131 阅读3分钟

第一步:从vue解构你想要的内容


<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
//创建根实例
let {createApp} = Vue;

//数据响应式
let {ref,shallowRef,reactive} = Vue;

//主动触发视图渲染
let {,triggerRef} = Vue;

//将ref对象(对象数据),返回只读代理
let {readonly} = Vue;

//监听相关
let {watch,watchEffect } = Vue;


创建vue根实例


let app = createApp({
    
    //这里写内容
    
})

app.mount("#app")



让数据成为响应式


//ref
let count = ref(10); // {value:10}
let addCount = ()=>{
    console.log("addCount执行了",count.value);
    count.value ++;
}

// reactive()  创建一个对'对象'数据的引用 返回一个对象的响应式代理。(只能获取修改代理对象的子属性)
// Vue响应式检测reactive()创建的响应式代理数据时是深层的(可以检测根对象及其子对象的属性改变)
let zhang = reactive({name:"张三",age:18,info:{height:177,weight:70}});
console.log("zhang",zhang);
console.log("zhang",zhang.name);
console.log("zhang",zhang.age);
console.log("zhang",zhang.info);
console.log("zhang",zhang.info.height);
console.log("zhang",zhang.info.weight);



ref和shallowref的区别

在vue3中,想讲数据转变为响应式,可以用到ref和shallowref这两个api,目的是使数据的变化可以触发视图的重新渲染。

但是这两个是存在区别的,修改ref响应数据的深层内容,视图会相应改变。而修改shallowref响应数据的深层内容,数据会改变,但是视图没变化,可以使用triggerRef主动去更新视图。

特别地,如果你先点击shallowref响应数据,视图不变,但是如果之后你再去改变ref相应数据改变视图的同时,前面未改变的shallow响应数据视图也会一起更新。

这里的原理就是虚拟dom,一处更新的时候,会整体渲染出虚拟dom,所以顺带把其他地方的数据也同步修改了。

为什么要强制视图更新?

因为shallowRef是浅层的响应式,深层的数据变化无法及时更新到视图

第二种方法 this.$forceUpdate();


let {createApp,getCurrentInstance} = Vue;

// 获取当前Vue实例
let app = getCurrentInstance();
console.log("当前Vue实例",app);

app.config.globalProperties.$a =1;
app.config.globalProperties.$b =2;
let {$a,$b} = app.appContext.config.globalProperties;  // 获取全局属性
let ctx = app.ctx;  // 获取当前Vue实例的代理对象
ctx.$forceUpdate();//此处的ctx就是我们苦苦寻找的this



简单总结就是三个步骤:用getCurrentInstance获取当前实例,再用ctx获取代理对象,最后执行$forceUpdate

组合式写法

将定义的数据,方法以及计算属性都写在setup(props)里面,最后用return返回暴露即可。、


let { createApp } = Vue;
    let { ref, reactive, readonly, computed } = Vue;


    let app = createApp({
        setup(props) {
            var count = ref(10); // {value:10}
            console.log("count", count);

            var zhang = reactive({ name: "张三", age: 18, info: { height: 177, weight: 70 } });
            console.log("zhang", zhang);


            // 通过count得到的计算属性

            // computed  
            // 1. 接受一个 getter 函数,返回一个只读的响应式 ref 对象, 此写法默认只有取值没有赋值(只读)
            // 2. 也可以接受一个带有 get 和 set 函数的对象来创建一个可写的 ref 对象。

            // 返回一个只读的响应式 ref 对象
            // let doubleCount = computed(()=>{
            //     return count.value * 2;
            // })

            let doubleCount = computed({
                get: () => {
                    return count.value * 2;
                },
                set: (val) => {
                    count.value  = val /  2;
                }
            })
            console.log("doubleCount", doubleCount);

            // 该 ref 通过 .value 取值
            console.log("doubleCount", doubleCount.value);
            let addCount = () => {
                console.log("addCount执行了", count.value);
                count.value++;
            }


            let changeName = () => {
                zhang.name = "李四"
            }

            let changeAge = () => {
                zhang.age++
            }

            let changeHeight = () => {
                zhang.info.height++
            }

            return {
                count,
                doubleCount,
                addCount,
                zhang,
                changeName,
                changeAge,
                changeHeight,
            }
        },
        mounted() {
            console.log("代理对象,this", this);

            /*  new Proxy({},{
                 get(){
                     // 判断是否是ref对象  => ref.value
                 },
                 set(){
                     // 判断是否是ref对象   => ref.value = xxx
                 }
             }) */
        },
    })


    app.mount("#app");



丧失响应性

起因是我们嫌弃写起来太麻烦,想要将对应变量解构出来


//直接对代理对象解构, 会丧失响应性
//因为后续对name,age赋值已经无法触发源数据的赋值拦截
let { name, age, info: { height, weight } } = zhang;



解决办法一:手搓计算属性形成依赖



let name = computed({
                get: () => {
                    return zhang.name;
                },
                set: (val) => {
                    zhang.name = val;
                }
            });

            let age = computed({
                get: () => {
                    return zhang.age;
                },
                set: (val) => {
                    zhang.age = val;
                }
            });

            let height = computed({
                get: () => {
                    return zhang.info.height;
                },
                set: (val) => {
                    zhang.info.height = val;
                }
            });


解决办法二:


 let {toRef,toRefs } = Vue;

let {name,age} = toRefs(zhang);   // {name:ref(),age:ref(),info:ref}
let {height,weight} = toRefs(zhang.info);   // {height:ref(),weight:ref()