+3

Tìm hiểu về VueX

Hi, chào mọi người tiếp tục chương trình tìm hiểu về VueJS của tớ, trong bài viết này tớ cùng các bạn cùng tìm hiểu về VueX nhé.

Nội dung bài viết này bao gồm các phần :

  • VueX là gì ?
  • Cài đặt
  • Sử dụng như thế nào ?

VueX là gì ?.

Đặt vấn đề.

Các cậu có bao giờ tự hỏi rằng những data dùng chung mà chúng ta thường hoặc có thể là bắt buộc phải sử dụng để thao tác với những component không ? ví dụ nha :3 làm thế nào để chúng ta biết rằng chúng ta đã đăng nhập hoặc không đăng nhập khi mà mỗi lần chúng ta chuyển page từ Home đến About ?

Đó là những thắc mắc của bản thân mình khi mình bắt đầu học Vue :v, rất ngu ngơ đúng hong vì làm gì mà kiểu nghĩ sâu xa đến thế ?. Sau thời gian nghịch ngợm với những thứ chẳng giống ai thì vô tình mình thấy có npm package để quản lý các state cùng tớ tìm hiểu là gì nhé -_~.

state ở đây các cậu cứ hiểu là quản lý dữ liệu cho dễ hiểu :3 vì tớ thấy nó cũng có kiểu thêm sửa xóa lấy ra dữ liệu nên đọc cho dễ hiểu .

Vuex ?

VueX là thư viện giúp chúng ta quản lý các state cho ứng dụng VueJS, nó là trung tâm để lưu trữ dữ liệu cho các component đồng nghĩa các component sử dụng dữ liệu chung.

Vì sao lại có VueX ?

Chúng ta cùng tìm hiểu một ví dụ component này nhé .

new Vue({
  // state
  data () {
    return {
      count: 0
    }
  },
  // view
  template: `
    <div>{{ count }}</div>
  `,
  // actions
  methods: {
    increment () {
      this.count++
    }
  }
})

Ở ví dụ trên chúng được hoạt động dựa trên mô hình one-way data flow với các thành phần chính :

  • State : Là nơi khởi nguồn của dữ liệu (có thể hiểu là nơi lữu trữ dữ liệu).
  • View : Là nơi khai báo được ánh xạ tới state (dữ liệu).
  • Actions : Là nơi mà state thay đổi và phản ứng lại mỗi lần người dùng thao tác trên view.

Tuy nhiên mô hình trên dễ bị phá vỡ khi mà có nhiều component sử dụng chung state

  • Nhiều view phụ thuôc vào một state.
  • Nhiều actions ở các view khác nhau lại cần thay đổi một state.

Có thể hiểu như sau, giả sử có khoảng 2 cái ví dụ trên ở 1 page đều sử dụng data count và action imcrement khi người dùng click button để tăng data count lên một đơn vị thì ở component còn lại cũng cần được tăng để đúng với logic đúng không ?.

trả lời :

Cách 1 : Có thể các bạn sẽ nghĩ ngay đến propsemit đúng không ? .

nếu mà các compoent lồng nhau tầm khoảng 5 cái compoent xem chúng ta cứ props và emit rồi tiếp tục .... => đâm ra chửi ông phát minh ra Vue hại anh em vãi 😃)).

Cách 2 : nếu không được thì sử dụng provide/Inject xem sao ? .

Cái này ok này :3 thế cậu đã nghĩ đến trường hợp compoent này tăng data count 2 đơn vị và component còn lại tăng data count một đơn vị nào ? oh thế là chúng ta lại thêm 2 action để truyền hả ? @@ thôi quả này chắc phải vài nghìn dòng code rồi.

Đấy là lý do vì sao sinh ra VueX cũng để giải đáp với thắc mắc của tớ ✌️

Mô hình của VueX

Vuex đã nhìn nhận những vấn đề trên thì cũng đưa ra cách rất hay chúng biến :

  • Quản lý các state và biến chúng thành thành sử dụng global.
  • Biến những component thành các view và chúng có thể truy cập vào statetrigger action và bất kỳ ở đâu.

Và đây là mô hình của Vuex.

Chắc tớ cũng không cần giải thích về mô hình hoạt động nữa nhỉ, cùng xem cách cài đặt và thành phần bên trong nó nhé.

Cài Đặt VueX ?

Đối với những phiên bản mới của Vue thì khi cài đặt cũng có hỏi bạn có cài Vuex hay không, ở phần cài đặt này tớ sẽ cài đặt ở phiên bản cũ với Vue version 2.

  • Chạy câu lệnh npm install vuex --save ở trong project
  • Tạo một thư mục store và trong store có file index.

  • Trong file store/index.js
import vuex from 'vuex'
import Vue from 'vue'
Vue.use(vuex);

const store = new vuex.Store({
   state: {
        count: 0,
   },
    mutations: {

    },
    actions: {

    },
    getters: {
       
    },
    modules: {
       
    }

});

export default store;
  • Khai báo VueX để sử dụng cho ứng dụng nào . Ở file main.js khai báo
import Vue from 'vue'
import App from './App.vue'
import store from './store/index.js';
Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  store,
}).$mount('#app')

  • Tạo một component và thử gọi this.$store.state.count xem nó có hiển thị không nhé.

Tìm hiểu và sử dụng các thành phần VueX

State

Giống như ở component, chúng thường có một đối tượng data() để lưu các biến của component thì state được coi là data của cả ứng dụng, sử dụng như vậy giúp chúng ta đồng bộ được dữ liệu chính xác.

Ở mỗi component chúng ta có thể lấy data một cách dễ dàng bằng cú pháp:

this.$store.state + tên khai báo trong state

Lưu ý: this + $ + cú pháp : là cách bạn khai báo ở main.js như thế nào thì chúng ta gọi như thế.

Nếu cậu quá chán ngán với việc lấy dữ liệu với một đống câu dài dài kia thì đừng lo VueX cũng tạo một helper tên là Mapstate giúp chúng lấy các giá trị mà chúng ta sử dụng.

<script>
import {mapState} from "vuex";

export default {
  name: 'HelloWorld',
  computed: {
    ...mapState([
        'count'
    ]),
    actionsd () {
      return this.count;
       // return this.$store.state;
    }
  }
}
</script>

state tớ đã viết ở trên rồi nên tớ sẽ không nói lại.

Getters

Chúng ta cũng đã hiểu thuộc tính computed trong component rồi đúng không, thì Getters cũng giống như vậy, chúng sinh ra để giải quyết vấn đề một số compoent có sử dụng chung dữ liệu đã được tính toán.

const store = new vuex.Store({
   state: {
        count: 0,
   },
    getters: {
        getCountPlus: state => state.count + 1
    },

});

Để sử dụng getters trong component this.$store.getters.getCountPlus

Cũng giống ở state ở trên, thì VueX cũng tạo ra một hàm helper để lấy ra những getters mà chúng ta gần bằng cách sử dụng mapGetters cú pháp cũng giống như ở trên ✌️

Mutations

Theo sách giáo khoa của VueX thì Mutations là cách duy nhất để thay đổi state, nhưng mà khi tớ nghịch bằng cách không gọi mutations thì chúng vẫn thay đổi bình thường, chắc mutation sinh ra để chúng ta kiểm soát được những hành động thay đổi state.

Để gọi được Mutations chúng ta sử dụng store.commit ở component.

File store/index.js

const store = new vuex.Store({
   state: {
        count: 0,
   },
    mutations: {
        increment(state,n) {
           state.count += n;
        }
    },

});

Chúng ta có thể thêm một tham số để có thể thay đổi với dữ liệu bên ngoài truyền vào, nhưng tham số đầu tiên bắt buộc là state.

Để gọi mutation trong component

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <p>{{actionsd}}</p>
    <button @click="hand">click</button>
  </div>
</template>

<script>
import {mapState} from "vuex";

export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  methods: {
    hand() {
      this.$store.commit('increment', 2);
    }
  },
  computed: {
    ...mapState([
        'count'
    ]),
    actionsd () {
        return this.$store.state;
    },

  }

}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>

</style>

Thì cũng giống như anh em của chúng, Mutations cũng có một helper để lấy mutations cần sử dụng -_ - bằng cách sử dụng mapMutations và cú pháp cũng y hệt nhưng khác ở chỗ là khai báo ở methods.

Actions

Actions cũng giống mutation nhưng nó khác ở chỗ:

  • Thay vì thay đổi trực tiếp state thì chúng thay đổi thông qua mutations
  • Actions có thể chứa các hàm bất đồng bộ.

ví dụ:

const store = new vuex.Store({
   state: {
        count: 0,
   },
    mutations: {
        increment(state, n) {
           state.count += n;
        }
    },
    actions: {
       test(context) {
           context.commit('increment', 20);
       }
    }

});

VÌ nó giống Mutations lên nó có thể truyền tham số đầu vào và tham số context là tham số bắt buộc.

Để gọi actions

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <p>{{actionsd}}</p>
    <button @click="hand">click</button>
  </div>
</template>

<script>
import {mapState} from "vuex";

export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  methods: {
    hand() {
      this.$store.dispatch('test');
    }
  },
  computed: {
    ...mapState([
        'count'
    ]),
    actionsd () {
        return this.$store.state;
    },

  }

}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>

</style>

Giống anh em ở trên thì actions cũng có hàm hepler để lấy ra những actions mà chúng ta cần sử dụng.

Modules

GIờ chúng ta giả sử có nhiều state,getter,action,mutation có thể lên tới rất nhiều dòng thì đồng nghĩa việc mò từng hàm từng data thì nói đến đấy cũng đau đầu rồi @@. VueX đã nhìn ra vấn đề đó, nó có sinh ra thuộc tính Mudules .

Modules giúp chúng ta tách các hàm có chung mục đích ra một file dễ tiện quản lý.

Tớ tạo một file dog.js để lưu tên dog:

  • file store/modules/dog.js
const dog = {
    state: {
        name: 'pitpull'
    },
    getters: {

    },
    actions: {

    },
    mutations: {

    }
}

export default dog;
  • Ở file store/index.js:
import vuex from 'vuex'
import Vue from 'vue'
import dog from './modules/dog';
Vue.use(vuex);

const store = new vuex.Store({
   state: {
        count: 0,
   },
    mutations: {
        increment(state, n) {
           state.count += n;
        }
    },
    actions: {
       test(context) {
           context.commit('increment', 20);
       }
    },
    modules: {
       dog: dog
    }

});

export default store;
  • Để sử dụng
<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <p>{{actionsd}}</p>
    <button @click="hand">click</button>
  </div>
</template>

<script>
import {mapState} from "vuex";

export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  methods: {
    hand() {
      console.log(this.$store.state.dog.name);
    }
  },
  computed: {
    ...mapState([
        'count'
    ]),
    actionsd () {
        return this.$store.state;
    },

  }

}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>

</style>

Kết Luận

Bài viết này khá sơ sài nhưng cũng đủ để chúng ta tiếp cận đến VueX. Rất mong có được ý kiến đóng góp từ mọi người dưới phần comment ạ. Cảm ơn mọi người.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí