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那里挺绕的。