之前一直都是看别人写的vuex感觉还挺好理解的,今天自己根据需求写了下vuex,一下子不知道怎么写了,
想要用好vuex还是先要知道原理:
参考好博客写的非常到位:https://www.cnblogs.com/DM428/p/7293867.html
基本组成:
注意到这个store对象包含三个子对象:
state、mutations、actions
其中state用于存储数据,类似vue实例的data属性。
mutations用于递交更改,对state对象中的属性数据进行更改。
actions用于进行递交异步更改,通过调用mutations实现对数据的更改。
actions与mutations的区别:
其中actions区别于mutations的地方在于mutations只能进行同步更改,而actions中的更改可以是异步执行。所以基本上所有用户执行的直接数据更改都是触发mutations属性
函数执行,而需要与后端进行数据交互的数据更改通常是通过actions属性函数去执行。
定义actions与mutations属性函数的注意事项:
其中定义mutations属性函数时必须传递的第一个参数是state,因为要对state进行更改,第二个参数代表传入的新参数。mutations属性函数只接受两个参数,如果要同时更改多个属性值,可以通过对象传入。
在actions属性函数中可以通过context.commit()方法触发mutations属性函数。定义actions属性函数时,必须传递的第一个参数是context,用于触发mutations函数。
触发actions与mutations属性函数的方法:
在子组件中通过this.$store.commit()方法触发mutations属性函数。在注册store的Vue实例中(第三步中将会讲到)可以通过store.commit()触发。
commit函数第一个参数是mutations的属性函数名,第二个参数是传入的新值。
actions属性函数中可以进行异步操作,比如通过ajax或者Vue.Resource()进行数据获取,获取数据后再通过context.commit()触发更改。
触发actions属性函数使用this.$store.dispatch()或者store.dispatch() (在注册store的Vue实例中)函数。dispatch函数传递的一个参数是actions属性函数名称。如果希望在
Vue实例创建完成还未挂载时就从后端获取数据,则可以在created钩子函数中调用actions属性函数。
在组件中访问数据中心state的注意事项:
在Vue实例中可以通过this.$store.state对象获取state中的数据。如果希望在state中的数据发生更改之后,组件会自动更新,则应该使用组件的computed属性定义数据,而
不是通过data属性定义。如果使用data定义组件数据,则state中的数据发生更改之后组件不会发生变化。
练习:vuex+父子组件间通信
项目目录:
页面组件:AppFilm.vue
<template> <div class="app-film"> <AppFilmNav></AppFilmNav> <AppFilmBox></AppFilmBox> </div> </template> <script> import AppFilmNav from '@/components/AppFilmNav' import AppFilmBox from '@/components/AppFilmBox' export default { name: 'app-film', components:{AppFilmNav,AppFilmBox}, data () { return { } }, computed:{ } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> .app-film{ height: 0.5rem; } </style>
AppFilmBox:
<template> <div class="app-film-box"> <AppFilmItem :film="film" v-for="(film,index) in films" :key="index"></AppFilmItem> /// :film 绑定数据传递给子组件 <!--{{ infor }}--> </div> </template> <script> import axios from 'axios' import AppFilmItem from './AppFilmItem' export default { // props:['infor'], name: 'app-film-box', components:{AppFilmItem}, data () { return { // films:[] } },
// 必须通过computed属性使用state数据!否则state属性中的数据发生更改时不会反映在组件上! computed: { films(){ return this.$store.state.film } }, methods: { getfilms(){ let that = this; this.$store.dispatch('Toggle',this.$store.state.posturl); } }, created(){ this.getfilms() } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style> .app-film-box{ padding:0 15px; } </style>
appFilmItem.vue
<template> <div class="app-film-item"> <img :src="film.poster" alt=""> <div class="info"> <h5>{{film.name}}</h5> <p class="for">{{film.intro}}</p> <p class="for1">{{film.cinemaCount}}家影院上映<i>{{film.watchCount}}人购票</i></p> </div> <div class="other"> <div class="range">8.5</div> <i class="fa fa-angle-right"></i> </div> </div> </template> <script> export default { name:'app-film-item', props:['film'], // 从父组件那里接受数据 film } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style lang="scss"> body{ background-color:#f9f9f9; } .app-film-item{ // padding-left: 20px; // margin-top: -0.3rem; display: flex; align-items: center; padding-bottom: 28px; border-bottom: 1px solid #ccc; padding-top: 25px; img{ width:0.6rem; } .info{ padding-left: 15px; display: inline-block; width: 75%; h5{ font-size: 16px; line-height: 32px; color: #000; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .for{ height: 24px; line-height: 24px; color: #8e8e8e; font-size: 14px; overflow: hidden; text-overflow: ellipsis; width: 100%; display: inline-block; } .for1{ line-height: 24px; color: #8e8e8e; font-size: 12px; i{ margin-left: 15px; } } } } .other{ display: flex; justify-content: space-between; margin-right: 0.2rem; margin-bottom:0.5rem; .range{ font-size: 16px; line-height: 32px; color: #fc7103; } i{ padding-left: 5px; line-height: 29px; color: #c6c6c6; } } </style>
AppFilmNav.vue
<template> <div class="app-film-nav"> <div class="now_playing" @touchend.stop.prevent="toggle(0)" :class="{active:active==0}"> 正在热映 </div> <div class="coming_soon" @touchend.stop.prevent="toggle(1)" :class="{active:active==1}"> 即将上映 </div> </div> </template> <script> export default { name:'app-film-nav', data () { return { active:0 } }, methods: { toggle (i) { this.active = i; this.$store.state.posturl = this.$store.state.urls[i].url; this.$store.dispatch('Toggle',this.$store.state.posturl); } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style lang="scss" scoped> .app-film-nav{ height: 47px; margin: 0.5rem 15px 5px 15px; border-bottom: solid #fe6e00 1px; // padding-left: 15px; // padding-right:15px; .now_playing{ float: left; width: 50%; height: 100%; text-align: center; font-size: 16px; line-height: 46px; // color: #6a6a6a; cursor: pointer; } .coming_soon{ float: left; width: 50%; height: 100%; text-align: center; font-size: 16px; line-height: 46px; color: #6a6a6a; cursor: pointer; } .active{ color: #fe6e00; border-bottom: solid; } } </style>
vuex写在main.js里
// The Vue build version to load with the `import` command // (runtime-only or standalone) has been set in webpack.base.conf with an alias. import Vue from 'vue' import App from './App' import router from './router' import Vuex from 'vuex' import axios from 'axios' Vue.use(Vuex) Vue.config.productionTip = false const store = new Vuex.Store({ state:{ urls:[ {title:'正在热映',url:'/static/mock/now-playing.json'}, {title:'即将上映',url:'/static/mock/coming-soon.json'} ], posturl:'/static/mock/now-playing.json', film:'' }, mutations:{ SET_FILM: (state, film) => { state.film = film } }, actions: { Toggle: ({commit},url) => { return new Promise((resolve, reject) => { axios.get(url).then((res) => { const data = res.data.data commit('SET_FILM', data) resolve() }).catch((err) => { reject(err) }) }) } } }) /* eslint-disable no-new */ new Vue({ el: '#app', store, // 根组件通过store选项将store实例注入所有地子组件 created (){ store.dispatch('Toggle',store.state.posturl); }, router, components: { App }, template: '<App/>' }) // 上面的代码能够让子组件通过this.$store访问到store实例。
页面效果:
这里用Vuex来切换get请求的地址