avatar
fireworks99
keep hungry keep foolish

Vue Vuex

需求:一个计数器组件,要渲染三次,这个计数器组件展示count变量,添加一个按钮,点击这个按钮一次,三个计数器都++。

1.属性传递

count数据存储在共同的最近公共祖先组件中,通过props属性接收。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>

    <div id="app">
        <counter :count="count"></counter>
        <counter :count="count"></counter>
        <counter :count="count"></counter>
        <button @click="count++">count++</button>
    </div>

    <script>
        const app = new Vue({
            el: "#app",
            data() {
                return {
                    count: 0
                }
            },
            components: {
                Counter: {
                    props: ["count"],
                    template: `<div>{{ count }}</div>`
                }
            }
        })
    </script>
</body>
</html>

2.状态管理

上述方案中,如果组件树很庞大,而LCA组件又是根组件,那么需要不断传递props,这样不合理。

所以要有一种方法

  • 使所有这些子组件共享同一状态
  • 根组件能够更新这一状态

①.Shared objects

共享对象当作状态

(将共享对象作为组件data,组件data返回的变量会被Vue跟踪监听,从而具有“响应性”)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>

    <div id="app">
        <counter></counter>
        <counter></counter>
        <counter></counter>
        <button @click="inc()">count++</button>
    </div>

    <script>
        const state = {
            count: 0
        };

        const Counter = {
            data() {
                //组件data属性返回的变量会被Vue跟踪监听
                //从而具有“响应性”
                return state;
            },
            render(h) {
                return h('div', this.count);
            }
        }

        const app = new Vue({
            el: "#app",
            components: {
                Counter
            },
            methods: {
                inc() {
                    state.count++;
                }
            }
        })
    </script>
</body>
</html>

②.Shared Vue instances

共享Vue实例作为状态

(Vue实例默认具有响应性)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>

    <div id="app">
        <counter></counter>
        <counter></counter>
        <counter></counter>
        <button @click="inc()">count++</button>
    </div>

    <script>
        const state = new Vue({
            data: {
                count: 0
            },
            methods: {
                inc() {
                    this.count++;
                }
            }
        });

        const Counter = {
            render: h => h('div', state.count)
        }

        const app = new Vue({
            el: "#app",
            components: {
                Counter
            },
            methods: {
                inc() {
                    state.inc();
                }
            }
        })
    </script>
</body>
</html>

③.Mutations APl

mutations:是真正变更状态的地方。必须是同步的。(快照)

actions:可以有异步代码。

实现一个简单的VueX:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>

    <div id="app">
        <counter></counter>
        <counter></counter>
        <counter></counter>
        <button @click="inc()">count++</button>
    </div>

    <script>
        //按照使用方法,实现这个功能
        function createStore({state, mutations}) {
            return new Vue({
                data: {
                    state
                },
                methods: {
                    commit(mutation) {
                        if(!mutations.hasOwnProperty(mutation)) {
                            throw new Error('Unknown mutation');
                        }
                        mutations[mutation](state);
                    }
                }
            })
        }

        const store = createStore({
            state: {count: 0},
            mutations: {
                inc(state) {
                    state.count++;
                }
            }
        });

        const Counter = {
            render(h) {
                return h('div', store.state.count);
            }
        };

        const app = new Vue({
            el: "#app",
            components: {
                Counter
            },
            methods: {
                inc() {
                    store.commit('inc');
                }
            }
        })
    </script>
</body>
</html>

④Functional interface

函数式编程

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

<body>

    <div id="app">
        <counter></counter>
        <counter></counter>
        <counter></counter>
        <button @click="inc()">count++</button>
    </div>

    <script>
        /**
         * 解构赋值:
         * 有名字时,是按名字来的,与顺序无关
         * 没名字时,按顺序来
         * */
        //按照使用方法,实现这个功能
        function app({ el, model, view, actions }) {
            const wrappedActions = {}

            Object.keys(actions).forEach(key => {
                const originalAction = actions[key]
                wrappedActions[key] = () => {
                    vm.model = originalAction(vm.model)
                }
            })

            const vm = new Vue({
                el,
                data: {
                    model
                },
                render(h) {
                    return view(h, this.model, wrappedActions)
                },
                methods: actions
            })
        }

        app({
            el: '#app',
            model: {
                count: 0
            },
            actions: {
                inc: ({ count }) => ({ count: count + 1 }),
                dec: ({ count }) => ({ count: count - 1 })
            },
            view: (h, model, actions) => h('div', { attrs: { id: 'app' } }, [
                model.count,
                ' ',
                h('button', { on: { click: actions.inc } }, '+'),
                h('button', { on: { click: actions.dec } }, '-')
            ])
        })

    </script>

</body>

</html>

参数actions那里挺绕的。

Site by Baole Zhao | Powered by Hexo | theme PreciousJoy