vue-vue响应式编程

目录

一、响应式编程的原理及在vue中的应用

1、手动追踪变量的变化

2、vue中的响应式对象

3、独立的响应式值Ref的应用

二、响应式的计算与监听

1、关于计算变量

2、监听响应式变量

三、组合式API的应用

1、关于setup方法

2、在setup方法中定义生命周期行为。

四、范例

1、常规风格的示例工程开发

2、使用组合式API重构用户列表


一、响应式编程的原理及在vue中的应用

响应式的本质是对变量的监听,当监听到变量发生变化时,我们可以做一些预定义的逻辑。例如对于数据绑定技术来说,需要做的是在变量发生改变时及对页面的元素进行刷新。

1、手动追踪变量的变化

<script>
        let a = 1
        let b = 2
        let sum = a + b
        console.log(sum);
        a = 3
        b = 4
        console.log(sum);
    </script>

 结果:

两次输出的sum变量的值都是3,虽然从逻辑上理解,sum值得意义是变量a和变量b的值得和,但是当变量a和变量b发生改变时,变量sum的值并不会响应式地进行改变。

如何为sum这类变量增加响应式?在js中,可以使用Proxy对原对象进行包装,从而实现对对象属性设置和获取的监听。

<script>
        let a = {
            value: 1
        }
        let b = {
            value: 2
        }
        //定义触发器,用来刷新数据
        let trigger = null
        handlerA = {
            get(target, prop) {
                return target[prop]
            },
            set(target, key, value) {
                target[key] = value
                if (trigger) {
                    trigger()
                }
            }
        }
        handlerB = {
            get(target, prop) {
                return target[prop]
            },
            set(target, key, value) {
                target[key] = value
                if (trigger) {
                    trigger()
                }
            }
        }
        let pa = new Proxy(a, handlerA)
        let pb = new Proxy(b, handlerB)
        let sum = 0
        trigger = () => {
            sum = pa.value + pb.value
        }
        trigger()
        console.log(sum);
        pa.value = 3
        pb.value = 4
        console.log(sum);
    </script>

结果:

2、vue中的响应式对象

在vue3.0中引入了组合式API的新特性,这种新特性允许我们在setup方法中定义组件需要的数据和函数,set方法可以在组件被创建前定义组件需要的数据和函数。

<div class="app">
        <h1>测试数据:{{myData.value}}</h1>
        <button @click="click">点击</button>
    </div>
    <script>
        const app = Vue.createApp({
            setup() {
                //定义数据
                let myData = {
                    value: 0
                }
                //定义点击事件
                function click() {
                    myData.value++
                    console.log(myData.value);
                }
                //返回数据和方法
                return {
                    myData,
                    click
                }
            }
        })
        app.mount('.app')
    </script>

结果:

为什么页面没有被刷新?这是因为myData对象是我们自己定义的普通js对象,本身没有响应式,对其进行修改也不会同步刷新到页面上,这与我们常规使用组件的data方法返回的数据不同,data方法返回的数据会被默认保存到proxy对象,从而获得响应式。

为了解决上面的额问题,vue3提供了reactive方法,使用这个方法对自定义的js对象进行包装,即可以方便地为其添加响应式。

//定义数据

let myData = Vue.reactive({

value: 0

})

结果:

3、独立的响应式值Ref的应用

在实际开发中,很多时候我们需要的只是一个独立的原始值,对于独立的原始值,不需要手动将其包装为对象的属性,可以直接使用vue提供的ref方法来定义响应式独立值,ref方法会帮我们完成对象的包装。使用ref方法创建了响应式对象后,在setup方法内要修改数据,就需要对myObject中的value属性值进行修改,value属性值是vue内部生成的,但是对于setup方法导出的数据来说,我们在模板中使用myObject数据已经是最终的独立值,可以直接使用。

<div class="app">
        <h1>测试数据:{{myObj}}</h1>
        <button @click="click">点击</button>
    </div>
    <script>
        const app = Vue.createApp({
            setup() {
                //定义数据
                let myObj = Vue.ref(0)
                //定义点击事件
                function click() {
                    myObj.value++
                    console.log(myObj.value);
                }
                //返回数据和方法
                return {
                    myObj,
                    click
                }
            }
        })
        app.mount('.app')
    </script>

 结果:

vue还提供了一个名为toRefs的方法来支持响应式对象的解构赋值。

什么是解构赋值?

const myObj = {

name: 'zs',

age: 20,

sex: '男',

play() {

console.log('打游戏!');

}

}

const { name, age, sex, play } = myObj

console.log(name, age, sex);

play()

 结果:

<div class="app">
        <h1>测试数据:{{value}}</h1>
        <button @click="click">点击</button>
    </div>
    <script>
        const app = Vue.createApp({
            setup() {
                //定义数据
                let myObj = Vue.reactive({
                    value: 1
                })
                //对myObj进行解构赋值
                let { value } = myObj
                //定义点击事件
                function click() {
                    value++
                    console.log(value);
                }
                //返回数据和方法
                return {
                    value,
                    click
                }
            }
        })
        app.mount('.app')
    </script>

结果:

改写后的代码可以正常运行,但是已经失去了响应式,对于这种场景,我们可以使用vue中提供的toRefs方法来进行对象的解构,其会自动解构出的变量转换为ref变量,从而获得响应式。

<div class="app">
        <h1>测试数据:{{value}}</h1>
        <button @click="click">点击</button>
    </div>
    <script>
        const app = Vue.createApp({
            setup() {
                //定义数据
                let myObj = Vue.reactive({
                    value: 1
                })
                //对myObj进行解构赋值
                let { value } = Vue.toRefs(myObj)
                //定义点击事件
                function click() {
                    value.value++
                    console.log(value);
                }
                //返回数据和方法
                return {
                    value,
                    click
                }
            }
        })
        app.mount('.app')
    </script>

 结果:

注意,Vue.toRefs解构时,会将解构出的属性设置为key,vue内部会自动创建一个value属性,用于保存解构出的值。要想改变解构出的属性,必需调用 属性.value才能更改其值。

二、响应式的计算与监听

1、关于计算变量

<div class="app">
        <h2>测试结果:{{sum}}</h2>
        <button @click="click">点击</button>
    </div>
    <script>
        const app = Vue.createApp({
            setup() {
                let a = Vue.ref(1)
                let b = Vue.ref(2)
                let sum = Vue.computed({
                    get() {
                        return a.value + b.value
                    },
                    set(value) {
                        a.value = value
                        b.value = value
                    }
                })
                function click() {
                    a.value += 1
                    b.value += 2
                    if (sum.value >= 10) {
                        sum.value = 0
                    }
                }
                return {
                    sum,
                    click
                }
            }

        })
        app.mount('.app')
    </script>

 结果:

2、监听响应式变量

有时候,当响应式变量发生变化时,我们需要监听其变化行为。在vue3中,watchEffect方法可以自动对其内部用到的响应式变量进行变化监听,由于其原理是在组件初始化时对所有依赖进行收集,因此在使用时无须手动指定要监听的变量。

<div class="app">
        <div>{{a.value}}</div>
    </div>
    <script>
        const app = Vue.createApp({
            setup() {
                let a = {
                    value: 1
                }
                Vue.watchEffect(() => {
                    console.log(a);
                })
                a.value = 2
                return { a }
            }
        })
        app.mount('.app')
    </script>

 结果:

在调用watchEffect方法时,其会立即执行传入的函数参数,并会追踪其内部的响应式变量,在其变更时再次调用此函数参数。需要注意,watchEffect在setup方法中被调用后,其会和当前组件的生命周期绑定在一起,组件卸载时会自动停止监听,如果需要手动停止监听。

<div class="app">
        <div>{{a.value}}</div>
    </div>
    <script>
        const app = Vue.createApp({
            setup() {
                let a = Vue.reactive({ value: 1 })
                let stop = Vue.watchEffect(() => {
                    console.log(a.value);
                })
                stop()
                a.value = 2
                return { a }
            }
        })
        app.mount('.app')
    </script>

watch是一个与watchEffect类似的方法,与watchEffect方法相比,watch方法能够更加精准地监听指定的响应式数据的变化。

<div class="app">
        <div></div>
    </div>
    <script>
        const app = Vue.createApp({
            setup() {
                let a = Vue.ref(1)
                Vue.watch(a, (newVal, oldVal) => {
                    console.log(newVal, oldVal);
                })
                a.value = 2
            }
        })
        app.mount('.app')
    </script>

结果:

 watch方法比watchEffect方法强大的地方在于,其可以分别获取到变化前的值和变化后的值,也可以监听多个数据源。

<div class="app">
        <div></div>
    </div>
    <script>
        const app = Vue.createApp({
            setup() {
                let a = Vue.ref(1)
                let b = Vue.reactive({ value: 2 })
                Vue.watch([a, () => { return b.value }], ([newValA, newValB], [oldValA, oldValB]) => {
                    console.log(newValA, newValB, oldValA, oldValB);
                })
                a.value = 3
                b.value = 4
            }
        })
        app.mount('.app')
    </script>

注意,通过Vue.reactive声明的变量需要使用箭头函数,watch才能监听到。

三、组合式API的应用

组合式API的使用能够帮助我们更好地梳理复杂组件的逻辑分布,能够从代码层面上将分离的相关逻辑点进行聚合,更适合进行复杂模块组件的开发。

1、关于setup方法

setup方法是组合式API的核心方法。

setup方法是组合式API功能的入口方法,如果使用组合式API模式进行组件开发,则逻辑代码都要编写在setup方法中。需要注意的是,setup方法会在组件创建之前执行,即对应组件的生命周期方法beforeCrete方法调用之前被执行。由于setup方法特殊的执行时机,除了可以访问组件的传参外部属性props之外,在其内部我们不能使用this来引用组件的其他属性,在set方法的最后,我们可以将定义的组件所需要的数据、函数等内容暴露给组件的其他选项。

setup方法可以接受两个参数:props和context。props术组件使用时设置的外部参数,其实响应式;context则是一个js对象,其中可用的属性有attrs、slots和emit。

<div class="app">
        <com name="组件名"></com>
    </div>
    <script>
        const app = Vue.createApp({})
        app.component('com', {
            template: `<div></div>`,
            setup(props, context) {
                //props属性值
                console.log(props.name);
                //属性
                console.log(context.attrs);
                //触发事件
                console.log(context.emit);
                //插槽
                console.log(context.slots);
            },
            props: {
                name: String
            }
        })
        app.mount('.app')
    </script>

结果:

注意,在setup方法中不要使用this关键字,setup方法中的this与当前组件实例并不是同一个对象。

2、在setup方法中定义生命周期行为。

setup中的生命周期定义方法需要在周期函数前加上on,注意:setup方法执行时机与beforeCreate和created执行时机基本一致。

组合和setup方法中窦定义了同样的生命周期方法,他们之间并不会冲突。

<div class="app">
        <com></com>
    </div>
    <script>
        const app = Vue.createApp({})
        app.component('com', {
            template: `<div></div>`,
            setup() {
                Vue.onMounted(() => {
                    console.log("setup中定义的mounted方法");
                })
            },
            mounted() {
                console.log('组件内定义的mounted方法');
            }

        })
        app.mount(".app")
    </script>

 结果:

四、范例

实现支持搜索和筛选的用户列表

1、常规风格的示例工程开发

<div class="app">
        <div class="radio">
            <input type="radio" v-model="sex" :value="-1">全部
            <input type="radio" v-model="sex" :value="0">男
            <input type="radio" v-model="sex" :value="1">女
        </div>
        <div class="search">
            搜索:<input type="search" v-model="searchKeyword">
        </div>
        <div class="table" style="margin:10px 0">
            <table border=" 1" width="500px" align="center">
                <tr>
                    <th>姓名</th>
                    <th>性别</th>
                </tr>
                <tr v-for="item in allData">
                    <td>{{item.name}}</td>
                    <td>{{item.sex==0?'男':'女'}}</td>
                </tr>
            </table>
        </div>
    </div>
    <script>
        let mock = [
            {
                name: '小王',
                sex: 0
            },
            {
                name: '小红',
                sex: 1
            },
            {
                name: '小张',
                sex: 0
            },
            {
                name: '小李',
                sex: 1
            }
        ]
        const app = Vue.createApp({
            data() {
                return {
                    sex: -1,
                    searchKeyword: '',
                    allData: []
                }
            },
            mounted() {
                this.allData = mock
            },
            methods: {
                sexFilter() {
                    this.searchKeyword = ''
                    if (this.sex == -1) {
                        this.allData = mock
                    } else {
                        this.allData = mock.filter((item) => {
                            return item.sex == this.sex
                        })
                    }
                },
                keywordFilter() {
                    this.sex = -1
                    this.allData = mock.filter((item) => {
                        return item.name.search(this.searchKeyword) != -1
                    })
                }
            },
            watch: {
                sex(oldVal, newVal) {
                    this.sexFilter()
                },
                searchKeyword(oldVal, newVal) {
                    this.keywordFilter()
                }
            }
        })
        app.mount(".app")
    </script>

结果:

 

 

2、使用组合式API重构用户列表

<style>
        .app {
            width: 800px;
            margin: auto;
            text-align: center;
        }
    </style>

    <body>
        <div class="app">
            <div class="radio">
                <input type="radio" v-model="sex" :value="-1">全部
                <input type="radio" v-model="sex" :value="0">男
                <input type="radio" v-model="sex" :value="1">女
            </div>
            <div class="search">
                搜索:<input type="search" v-model="searchKeyword">
            </div>
            <div class="table" style="margin:10px 0">
                <table border=" 1" width="500px" align="center">
                    <tr>
                        <th>姓名</th>
                        <th>性别</th>
                    </tr>
                    <tr v-for="item in allData">
                        <td>{{item.name}}</td>
                        <td>{{item.sex==0?'男':'女'}}</td>
                    </tr>
                </table>
            </div>
        </div>
        <script>
            let mock = [
                {
                    name: '小王',
                    sex: 0
                },
                {
                    name: '小红',
                    sex: 1
                },
                {
                    name: '小张',
                    sex: 0
                },
                {
                    name: '小李',
                    sex: 1
                }
            ]
            const app = Vue.createApp({
                setup() {
                    let sex = Vue.ref(-1)
                    let searchKeyword = Vue.ref("")
                    let allData = Vue.ref([])
                    Vue.onMounted(() => {
                        allData.value = mock
                    })
                    let sexFilter = () => {
                        searchKeyword.value = ""
                        if (sex.value == -1) {
                            allData.value = mock
                        } else {
                            allData.value = mock.filter((item) => {
                                return item.sex == sex.value
                            })
                        }
                    }
                    let searchKeywordFilter = () => {
                        sex.value = -1
                        allData.value = mock.filter((item) => {
                            return item.name.search(searchKeyword.value) != -1
                        })
                    }
                    Vue.watch(sex, sexFilter)
                    Vue.watch(searchKeyword, searchKeywordFilter)
                    return { sex, searchKeyword, allData }
                }
            })
            app.mount(".app")
        </script>

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值