Vuex
認識Vuex
Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。它採用集中式存儲管理應用的所有組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。Vuex 也集成到 Vue 的官方調試工具 devtools extension,提供了諸如零配置的 time-travel 調試、狀態快照導入導出等高級調試功能。
Vuex:
state: 數據
view : 視圖;將state數據展示到視圖上;
actions : 動態,更改或者是觸發視圖的動作
數據傳遞:把各個組件共享的狀態抽離出來,並且每個組件都可以使用它,並且還可以更改它;既然是公共狀態,當其中組件把這個公共狀態更改以後,凡是依賴這個公共狀態的組件都要更新;
- VUEX的數據存儲是響應式的;vue組件中使用了vuex的數據時,當vuex中的數據更新時,那麼使用數據的組件也會得到高效的更新
- 不能直接修改store中的狀態;需要commit一個mutation,這樣方便我們追蹤每一個數據的變化;
Vuex : 適用中大型單頁面應用
<body>
<div id="app">
{{count}}
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
// VUEX:
// state: 數據
// view : 視圖;將state數據展示到視圖上;
// actions : 動態,更改或者是觸發視圖的動作
// 數據傳遞:把各個組件共享的狀態抽離出來,並且每個組件都可以使用它,並且還可以更改它;
既然是公共狀態,當其中組件把這個公共狀態更改以後,凡是依賴這個公共狀態的組件都要更新;
let num =100;
// 1. VUEX的數據存儲是響應式的;vue組件中使用了vuex的數據時,當vuex中的數據更新時,
那麼使用數據的組件也會得到高效的更新
// 2. 不能直接修改store中的狀態;需要commit一個mutation,
這樣方便我們追蹤每一個數據的變化;
// Vuex : 適用中大型單頁面應用
let vm = new Vue({
el:"#app",
data:{
count:0
},
methods:{
addCount(){
this.count++;
}
},
components:{
}
})
</script>
</body>
表示“單向數據流”理念的簡單示意
- 狀態自管理應用包含以下幾個部分:
- state,驅動應用的數據源;
- view,以聲明方式將 state 映射到視圖;
- actions,響應在 view 上的用戶輸入導致的狀態變化。
Vuex多個組件共享狀態
數據傳遞–>VueX(store)
組件: 組件就是抽離的公共的部分,這個公共的部分可以有自己獨立的html,css,js
實現複用:可複用,可維護
數據傳遞: vue中數據傳遞有哪些方式?
Vue是單項數據流,當父組件的數據發生更改,如果子組件也使用了父組件的數據,那麼子組件也會隨着更新;如果改變子組件這個數據,那麼父組件不能更改;
- 父傳子(props) props props=[“a”] :a=“num”
- 子傳父(自定義事件) @change=‘num’ this.emit(“updata:a”,2000)
3.兄弟組件(eventBus) let eventBus=new Vue eventBus.emit(事件池)
4.VueX(store) 把每一個stroe注入到各個組件裏 [state,mutations,actions,modules,getters]
Vuex的原理圖
- let store = new Vuex.Store({})
Vuex中的使用-state
如果這個數據是好幾個組件共享的,這個數據放到VUEX的state上;如果這個數據只有當前這一個組件使用,那麼放到對應的組件的data中
store // 當前vue的實例就會新增一個store屬性
vuex更改store中狀態的唯一方法就是commit;
在vuex中更改公有的數據,必須通過commit一個mutation;
當共享的數據發生改變以後,那麼依賴這個數據的組件都會高效的更新;
<body>
<div id="app">
<first></first>
<second></second>
<!-- <div>{{this.$store.state.count}}</div> -->
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script src="../node_modules/vuex/dist/vuex.min.js"></script>
<script>
// 如果這個數據是好幾個組件共享的,這個數據放到VUEX的state上;
如果這個數據只有當前這一個組件使用,那麼放到對應的組件的data中
// 在vuex中更改公有的數據,必須通過commit一個mutation;
// 當共享的數據發生改變以後,那麼依賴這個數據的組件都會高效的更新;
let store = new Vuex.Store({
state: {
count: 1001, // count : 這就是公共的數據
shop: [{
name: "奔馳"
}, {
name: "奧迪"
}]
},
mutations: {
increment(state, a) { // 參數接收到當前state
state.count += a;
},
min() {
// mutation 這個對象中的函數的第一個參數的實參都是state;
}
}
});
let first = {
data() {
return {}
},
methods: {
addCount(val) {
// 提交的是mutation裏面的函數名;用於修改store中的數據
this.$store.commit("increment", val);
}
},
// computed:{
// shop(){
// return this.$store.state.shop
// }
// },
// VUEx.mapState:將vuex的store中的state數據,映射到當前實例的計算屬性上;
//computed:Vuex.mapState(["shop","count"]),
computed: {
tt() {
},
...Vuex.mapState(["shop", "count"])
},
template: "<div>{{count}}<button @click='addCount(2)'>++</button>
<li v-for='a in shop'>{{a.name}}</li></div>"
};
let second = {
data() {
return {}
},
template: "<div>{{this.$store.state.count}}</div>"
}
let vm = new Vue({
el: "#app",
data: {
},
components: {
first,
second
},
store // 當前vue的實例就會新增一個$store的屬性,屬性值時一個對象;
同時也會給當前實例每一個子孫組件也新增一個$store屬性
});
console.log(vm);
</script>
</body>
Vuex-getters
getters : vue允許定義getter;在store中的計算屬性;也會被緩存;當依賴的值發生變化,纔會發生改變
Vuex的私有屬性提供了mapState mapGetters MapMutations,可以將store中的state、getters、mutations都映射當前的實例的computed或methods;
<body>
<div id="app">
<first></first>
<second></second>
<!-- <div>{{this.$store.state.count}}</div> -->
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script src="../node_modules/vuex/dist/vuex.min.js"></script>
<script>
// getters : vue允許定義getter;在store中的計算屬性;也會被緩存;當依賴的值發生變化,
纔會發生改變
let state= {
count:1001,// count : 這就是公共的數據
shop:[{name:"奔馳"},{name:"奧迪"}]
}
let getters={
str(state){// state 就是store中的state
return state.count%2===0?"偶數":"奇數"
},
a(){
}
}
let store= new Vuex.Store({
state,
getters,
mutations:{
increment(state,a){// 參數接收到當前state
state.count+=a;
},
min(){
// mutation 這個對象中的函數的第一個參數的實參都是state;
}
}
});
let first = {
data(){
return {
}
},
methods:{
addCount(val){
// 提交的是mutation裏面的函數名;用於修改store中的數據
this.$store.commit("increment",val);
}
},
// computed:{
// shop(){
// return this.$store.state.shop
// }
// },
// VUEx.mapState:將vuex的store中的state數據,映射到當前實例的計算屬性上;
//computed:Vuex.mapState(["shop","count"]),
computed:{
tt(){
},
// Vuex的私有屬性提供了mapState mapGetters MapMutations;
可以將store中的state、getters、mutations都映射當前的實例的computed或methods;
// 這幾個函數都返回一個包裝後的對象 {shop:12,count:22}
// ... : 展開運算符展開,放到了computed這個對象中
...Vuex.mapState(["shop","count"]),
...Vuex.mapGetters(["str"])
},
template:"<div>{{count}}<button @click='addCount(1)'>++</button>
<li v-for='a in shop'>{{a.name}}</li>{{str}}</div>"
};
let second = {
data(){
return {}
},
template:"<div>{{this.$store.state.count}}</div>"
}
let vm = new Vue({
el:"#app",
data:{
},
components:{
first,
second
},
store // 當前vue的實例就會新增一個$store的屬性,屬性值時一個對象;
同時也會給當前的子組件新增一個$store屬性
});
console.log(vm);
</script>
</body>
Vuex-mutations :同步
在mutation的方法中,都是同步事務,不支持異步;
methods:{
//同步
// add(val){
// this.$store.commit(“add_count”,val);
// }
…Vuex.mapMutations([“add_count”]),
}
<body>
<div id="app">
<first></first>
<second></second>
<!-- <div>{{this.$store.state.count}}</div> -->
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script src="../node_modules/vuex/dist/vuex.min.js"></script>
<script>
let state= {
count:1001,// count : 這就是公共的數據
shop:[{name:"奔馳"},{name:"奧迪"}]
}
let getters={
str(state){// state 就是store中的state
return state.count%2===0?"偶數":"奇數"
},
a(){
}
}
let store= new Vuex.Store({
state,
getters,
mutations:{
increment(state,a){// vuex更改store中狀態的唯一方法就是commit;
// state: 狀態 payload: 載荷
//state.count+=a;//在mutation的方法中,都是同步事務,不支持異步;
setTimeout(()=>{
state.count+=a;
},2000)
},
min(){
}
}
});
let first = {
data(){
return {
}
},
methods:{
// addCount(val){
// this.$store.commit("increment",val);
// }
//...Vuex.mapMutations(["increment"])
...Vuex.mapMutations({
add:"increment"// 將this.add()映射成'this.$store.commit("increment")
})
},
computed:{
tt(){
},
...Vuex.mapState(["shop","count"]),
...Vuex.mapGetters(["str"])
},
template:"<div>{{count}}<button @click='add(1)'>++</button>
<li v-for='a in shop'>{{a.name}}</li>{{str}}</div>"
};
let second = {
data(){
return {}
},
template:"<div>{{this.$store.state.count}}</div>"
}
let vm = new Vue({
el:"#app",
data:{
},
components:{
first,
second
},
store
});
</script>
</body>
commit提交
Vuex-actions :異步
actions 支持異步的,這裏面提交一個mutation的動作;
actions:{
// 支持異步
// 在項目中,經常從後端獲取數據賦值更改store中的state的值,
但是如果這個請求是異步的,大家需要在actions中發送請求,異步地更改state;
add({commit},val){// 提交一個mutations
// commit===KaTeX parse error: Expected 'EOF', got '}' at position 279: … }̲ } <…store.dispatch(“add”,val);
}
// commit==>mutations dipatch===> actions 在actions中是提交的mutations
// …Vuex.mapActions([“add”])
},
<body>
<div id="app">
<first></first>
<second></second>
<!-- <div>{{this.$store.state.count}}</div> -->
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script src="../node_modules/vuex/dist/vuex.min.js"></script>
<script>
// 工作需求中,經常發送異步的請求,請求回來的數據會賦值給Store中的state;
let state= {
count:1001,// count : 這就是公共的數據
shop:[{name:"奔馳"},{name:"奧迪"}]
}
let getters={
str(state){// state 就是store中的state
return state.count%2===0?"偶數":"奇數"
},
a(){
}
}
let store= new Vuex.Store({
state,
getters,
mutations:{
increment(state,a){// vuex更改store中狀態的唯一方法就是commit;
// state: 狀態 payload: 載荷
//state.count+=a;//在mutation的方法中,都是同步事務,不支持異步;
state.count+=a;
},
min(){
}
},
actions:{
addNum({commit},a){
// commit : 就是store上的commit;提交
// actions 支持異步的,這裏面提價一個mutation的動作;
setTimeout(function(){
commit("increment",a);
},2000);
}
}
});
let first = {
data(){
return {
}
},
methods:{
// addCount(val){
// this.$store.commit("increment",val);
// }
//...Vuex.mapMutations(["increment"])
// ...Vuex.mapMutations({
// add:"increment"// 將this.add()映射成'this.$store.commit("increment")
// }),
...Vuex.mapActions({
addadd:"addNum"
})
},
computed:{
tt(){
},
...Vuex.mapState(["shop","count"]),
...Vuex.mapGetters(["str"])
},
template:"<div>{{count}}<button @click='addadd(1)'>++</button><li v-for='a in shop'>{{a.name}}</li>{{str}}</div>"
};
let second = {
data(){
return {}
},
template:"<div>{{this.$store.state.count}}</div>"
}
let vm = new Vue({
el:"#app",
data:{
},
components:{
first,
second
},
store
});
</script>
</body>
Vuex-module模塊
// module
let moduesA = {
state: {
a: 100
},
getters: {
str1() {
return 99
}
}
};
let store = new Vuex.Store({
state: {
count: 0
},
modules: {
// 這個modules會把各自模塊的state,getters,mutations,actions最終都會注入到該Store中
first: moduesA,
second: moduesB,
}
});
使用: {{$store.state.first.a}}
<body>
<div id="app">
<first></first>
<second></second>
</div>
<!-- IMPORT JS -->
<script src="../node_modules/vuex/dist/vuex.min.js"></script>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
// module
let moduesA = {
state: {
a: 100
},
getters: {
str1() {
return 99
}
}
};
let moduesB = {
state: {
num: 88
}
};
let store = new Vuex.Store({
state: {
count: 0
},
getters: { //vuex 計算屬性
str(state) {
return state.count % 2 === 0 ? "偶數" : "奇數"
}
},
mutations: {
add_count(state, payload) { //state,默認接受一個state
state.count += payload
},
cut_count(state, payload) { //state,默認接受一個state
state.count -= payload
}
},
actions: {
// 支持異步
cut({
commit
}, val) { //提交一個mutations
// commit===$store.commit
setTimeout(_ => {
commit("cut_count", val)
}, 1000)
}
},
modules: {
// 這個modules會把各自模塊的state,getters,mutations,actions
最終都會注入到該Store中
first: moduesA,
second: moduesB,
}
});
let first = {
data() {
return {}
},
methods: {
// add(val) {
// this.$store.commit("add_count", val)
// }
...Vuex.mapMutations(["add_count"]),
// 1
// ...Vuex.mapActions(["cut"]),
// 2
cut(val) {
this.$store.dispatch("cut", val)
}
},
computed: {
...Vuex.mapGetters(["str","str1"])
},
template: "<div>{{$store.state.count}}<button @click='add_count(1)'>++
</button>{{str}}{{$store.state.first.a}}{{str1}}<button @click=' cut(1)'>++
</button></div>"
};
let second = {
data() {
return {}
},
template: "<div></div>"
};
let vm = new Vue({
el: '#app',
created() {
console.log(this.$store);
},
data: {
},
components: {
first,
second
},
store //給當前實例新增一個$store的屬性,並且給當前實例每一個子孫組件也新增一個$store
});
</script>
</body>
Vuex核心源碼封裝
<body>
<div id="app">
{{$store.state.msg}}
{{$store.getters.str}}
<child></child>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
// vuex:只要改變了state,凡是依賴state的組件都會高效的更新;
// new Vue的data屬性;
let Vuex=(function(){
class Store{
constructor(options){
// this==> 返回值store實例
// 初始化一個state;如果傳遞進來的state,會給其默認一個{}
// 爲了能讓數據能夠監聽到,當數據發生改變,依賴的視圖也要更新的;
let vm = new Vue({
data:{
state:options.state||{}
}
});
//this.state=options.state||{};
// 將Vm的state的空間地址賦值給了this.state
this.state = vm.state;
// this==> $store
this.mutations={};
let mutations=options.mutations;//這個就是傳遞進來的那個mutations
// Object.keys : 把對象中的屬性名挑出來放到一個數組中
// 就是在實例身上準備一個mutations對象,裏面包含了options外界傳遞進來的方法,
那麼方法中的this已經是指向了store這個實例;
//
Object.keys(options.mutations).forEach(key=>{
//this.mutations[key].bind(this,this.state)
this.mutations[key]=(payload)=>{
options.mutations[key].call(this,this.state,payload)
}
});
// 執行私有屬性的方法時,調用原型上的方法;
let commit = this.commit;// 把原型的commit給了變量commit;
// 給當前實例新增一個commit屬性,屬性值是一個函數
this.commit=(type,option)=>{
commit.call(this,type,option)
}
// this.commit=function(type,option){
// options.mutations[type].call(this,option)
// }
// actions
this.actions = {};
let actions = options.actions||{};
Object.keys(actions).forEach(key=>{
this.actions[key]=(option)=>{
// 第一個this指向把函數中的this改成當前實例store
// 把store傳給action的方法;
actions[key].call(this,this,option)
}
});
let dispatch = this.dispatch;
this.dispatch=(type,option)=>{
dispatch.call(this,type,option)
}
// getters
this.getters={};
let getters = options.getters||{};
// Object.keys : 將對象的屬性名收集起來放到一個數組中
Object.keys(getters).forEach(key=>{
// 給getters中每一個屬性新增一個get方法;
Object.defineProperty(this.getters,key,{
get:()=>{
// 會進行緩存,只有依賴的屬性發生改變會執行;
return getters[key].call(this,this.state)
}
});
});
}
// 把commit 放到store的原型上
commit(type,payload){
console.log(this);// Store的實例
this.mutations[type](payload)
}
dispatch(type,option){
this.actions[type](option)
}
}
//...Vuex.mapState(['a',"b"]);
// 將store中的state放到當前的computed屬性中
function mapState(ary){
let obj ={};
ary.forEach(key=>{
obj[key]=function(){
console.log(this);
// this 執行組件的實例
return this.$store.state[key]
}
})
return obj;
}
function mapGetters(ary){
let obj ={};
ary.forEach(key=>{
obj[key]=function(){
return this.$store.getters[key]
}
})
return obj;
}
function mapActions(ary){
let obj ={};
ary.forEach(key=>{
obj[key]=function(option){
return this.$store.dispatch(key,option)
}
})
return obj;
}
function mapMutations(ary){
let obj ={};
ary.forEach(key=>{
obj[key]=function(option){
return this.$store.commit(key,option)
}
})
return obj;
}
// ...Vuex.mapState(["count"])
function install(_Vue){
// _Vue和外面的Vue指向同一個空間地址
// _Vue : 就是Vue這個類函數;
// mixin : 將生成store通過mixin方法注入到每一個組件上
_Vue.mixin({
beforeCreate(){// 比組件內部的beforeCreate早執行
// 生成一個組件的實例,就會執行一次;並且在自己的beforecreate之前執行的;
//console.log(this);// 代表每一個組件實例;
// this --> Vue的實例vm
// 第二次執行 組件的實例
//this.$store=this.$options.store
//console.log(this);
// 這個會進行循環遍歷,
if(this.$options&&this.$options.store){
// 如果該條件是成立的,說明是vm實例;
this.$store=this.$options.store;
}else{
// 如果不成立,說明是子孫組件
// 如果父組件存在,那麼就把父組件的$store屬性賦值給子組件的$store屬性;
// $parent : 指的是當前組件的父組件
this.$store =this.$parent&&this.$parent.$store
}
}
})
}
return {
Store,
install,
mapState,
mapActions,
mapGetters,
mapMutations
}
})();
// Vue的插件必須使用Vue.use;只是vuex會默認檢測到是vue的官方插件,看不到vue.use;vue.use
執行時,會默認調用裏面的install;
Vue.use(Vuex);
// Vuex.Store
// Vuex.mapState
// store 是Store的一個實例;並且這個實例最後放到了vue的實例屬性上;
let store = new Vuex.Store({
state:{
count:100,
msg:"李明帥"
},
mutations:{
add(state,val){
// this==> store
console.log(state);
state.count+=val;
}
},
actions:{
addNum({commit},val){
commit("add",val);
}
},
getters:{
str(state){
return state.count%2===0?"偶數":"奇數";
}
}
});
let child={
created(){
// 組件在使用時,纔會觸發其鉤子函數
},
methods:{
fn(){
// this.$store===store==Store的實例
this.$store.commit("add",100);
// this.$store.dispatch("addNum",1)
}
},
computed:{
str(){
},
...Vuex.mapState(['count'])
},
template:"<div>{{$store.state.count}}{{count}}<button @click='fn'>增加
</button></div>"
}
// $store 會添加到每一個組件的實例上;
let vm = new Vue({
el:"#app",
store,// 會給當前實例以及當前實例的所有子孫組件都會新增一個$store屬性;
//a:100,
beforeCreate(){
//console.log(this);
// debugger
},
// 把Store的一個實例給了這個store屬性
// 組件在註冊時,不會調用生命週期鉤子函數,
components:{child}
});
//console.log(vm);// 目前vm身上沒有$store
// $options:代表 :new的對象,會把對象中的鍵值對添加到當前實例的$options上
// 1.先準備vuex對象【Store,install】;
// 2. Vue.use執行調用了裏面install,install執行調用了Vuex.mixin,對Vue這個類進行了修改
// 3.生成了一個store
// 4.new Vue;把store放到了實例的$options
// 5.隨後vm的生命週期,執行了mixin中的beforeCreate=>把options的store直接賦值給了實例的
$store屬性;
</script>
</body>