在线地址: cl8023.com github
未完...
父组件经过 props 向子组件通讯,在父组件内可经过 this.$children 来读取子组件中的值。props 是单向绑定,不可在子组件中赋值。
在 src/components 目录下建立文件夹 common 用于存放公共组件,并在 common 下建立单文件组件 LeftNavItem.vue(子组件)css
<template> <div> {{ childMes }} </div> </template> <script> export default { name: 'LeftNav', props: ['childMes'], } </script> <style lang="scss"> </style>
在 src/components/page/Blog.vue(父组件) 里引入组件 LeftNavItem.vuevue
<template> <div> <div class="child-area"> <Leftnav :childMes="message"></Leftnav> <button @click="getChild">getChild</button> </div> </div> </template> <script> import Leftnav from '../common/LeftNavItem' export default { name: 'blog', data() { return { message: 'father message', } }, components: { Leftnav }, methods: { getChild() { console.log(this.$children[0].childMes); } } } </script> <style scoped> .father-area { background-color: aqua; padding: 10px; } .child-area { background-color: bisque; padding: 10px; } </style>
这里父组件是 Blog.vue,子组件是 LeftNavItem.vue,父组件中调用子组件ios
<Leftnav :childMes="message"></Leftnav>
其中 :childMes 中的 childMes 是要传递到子组件中的变量,即对应 LeftNavItem.vue 中 props 属性中的值,能够传递多个变量git
props: ['childMes', 'childMes2', 'childMes3'],
这样在父组件中更改 message 的值,子组件便会获得相应的更新。es6
父组件向子组件传递事件方法,子组件经过 $emit 触发事件,回调给父组件。使用$parent能够访问父组件的数据
在 LeftNavItem.vue 中增长代码github
<template> <div> {{ childMes }} <button @click="toParent">toParent</button> </div> </template> <script> export default { name: 'LeftNav', props: ['childMes'], methods: { toParent() { this.$emit('mesFunc', 'from children'); console.log(this.$parent); } } } </script> <style lang="scss"> </style>
Blog.vue 增长代码vuex
<template> <div> <div class="father-area"> {{ fatherMes }} </div> <div class="child-area"> <Leftnav :childMes="message" @mesFunc="func"></Leftnav> <button @click="getChild">getChild</button> </div> </div> </template> <script> import Leftnav from '../common/LeftNavItem' export default { name: 'blog', data() { return { message: 'father message', fatherMes: 'Hello World' } }, components: { Leftnav }, methods: { getChild() { console.log(this.$children[0].childMes); }, func(data) { console.log(data); this.fatherMes = data; } } } </script> <style scoped> .father-area { background-color: aqua; padding: 10px; } .child-area { background-color: bisque; padding: 10px; } </style>
父组件 Blog.vue 中经过element-ui
<Leftnav :childMes="message" @mesFunc="func"></Leftnav>
将事件方法 mesFunc 传递到子组件axios
子组件 LeftNavItem.vue 中经过 $emit 能够触发事件,并传递数据数组
this.$emit('mesFunc', 'from children') // 第一个参数:父组件传递过来的事件 // 第二个参数:要传递到父组件的数据
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的全部组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
以上是官方介绍,通俗点将有点相似全局变量,来管理各类状态。
Vuex 中 Store 的模版化定义以下:
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) const store = new Vuex.Store({ state: { }, actions: { }, mutations: { }, getters: { }, modules: { } }) export default store
在 scr 下新建文件夹 store,并在 store 里新建文件 index.js
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex); const state = { currentArticle: {id: '1', title: '学习笔记', tag: 'vue'}, // 当前文章状态, 名字, 标签 count: 1 } // 使用常量替代 Mutation 事件类型 const types = { CURRENT_ARTICLE: 'CURRENT_ARTICLE', COUNT: 'COUNT' } const actions = { currentArticle({commit}, obj) { commit(types.CURRENT_ARTICLE, obj); }, countIncrement({commit}, n) { commit(types.COUNT, n); } } const mutations = { [types.CURRENT_ARTICLE](state, obj) { obj.id == undefined ? false : state.currentArticle.id = obj.id; obj.title == undefined ? false : state.currentArticle.title = obj.title; obj.tag == undefined ? false : state.currentArticle.tag = obj.tag; obj.catalog == undefined ? false : state.currentArticle.catalog = obj.catalog; }, [types.COUNT](state, n = 1) { state.count += n; } } export default new Vuex.Store({ state, actions, mutations, })
往下以前先了解下 ES6 的新语法 ES6教程
对象的解构赋值,变量必须与属性同名,才能取到正确的值
let { foo, bar } = { foo: "aaa", bar: "bbb" }; foo // "aaa" bar // "bbb" const actions = { currentArticle({commit}, obj) { commit(types.CURRENT_ARTICLE, obj); }, } // 等同于 const actions = { currentArticle(context, obj) { context.commit(types.CURRENT_ARTICLE, obj); }, } // 传入 currentArticle 的第一个参数是一个与 store 实例具备相同方法和属性的 context 对象,能够调用 context.commit 提交一个 mutation,或者经过 context.state 和 context.getters 来获取 state 和 getters,这里只要用到 context.commit 这个方法,因此能够 {commit} = context 即 commit = context.commit
ES6 容许在对象之中,直接写变量。这时,属性名为变量名, 属性值为变量的值
const foo = 'bar'; const baz = {foo}; baz // {foo: "bar"} // 等同于 const baz = {foo: foo}; export default new Vuex.Store({ state, actions, mutations, }) // 等同于 export default new Vuex.Store({ state: state, actions: actions, mutations: mutations })
对象方法属性能够简写
const o = { method() { return "Hello!"; } }; // 等同于 const o = { method: function() { return "Hello!"; } };
ES6 容许字面量定义对象时,用方法二(表达式)做为对象的属性名,即把表达式放在方括号内
let propKey = 'foo'; let obj = { [propKey]: true, ['a' + 'bc']: 123;
因此
const mutations = { [types.CURRENT_ARTICLE](state, obj) { }, } // 等同于 const mutations = { ['CURRENT_ARTICLE'](state, obj) { }, } // 等同于 const mutations = { CURRENT_ARTICLE: function(state, obj) { }, }
言归正传,咱们定义一个 state 状态属性 currentArticle 对象,用来记录当前文章的 id,title,tags,catalog,仍是使用 Blog.vue 和 LeftNavItem.vue 来测试
// Blog.vue <template> <div> <div class="father-area"> id: <input type="text" v-model="currentArticle.id"> title: <input type="text" v-model="currentArticle.title"> tag: <input type="text" v-model="currentArticle.tag"> count: <input type="text" v-model="count"> </div> <div class="child-area"> <Leftnav></Leftnav> </div> </div> </template> <script> import Leftnav from '../common/LeftNavItem' import store from '../../store/demo' export default { name: 'blog', data() { return { message: 'father message' } }, components: { Leftnav }, methods: { }, computed: { currentArticle() { return store.state.currentArticle }, count() { return store.state.count } } } </script> <style scoped> .father-area { background-color: aqua; padding: 10px; } .child-area { background-color: bisque; padding: 10px; } </style>
咱们能够在每一个组件内引入 store
import store from '../../store/index'
而后经过计算属性(命名符合规则便可)来获取状态值,能够看到,要得到多个状态时,将这些状态都声明为计算属性会有些重复和冗余,内置 mapState 辅助函数能够帮助咱们生产计算属性。要想使用辅助函数,须要先将 store 实例注册到 vue 实例中,这样 store 实例会注入到跟组件下的全部组件,且子组件能经过 this.$store 访问到。
你们 src/main.js 引入 store 实例,并在 vue 根实例中注册 store 选项
import Vue from 'vue' import App from './App' import router from './router' import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' import axios from 'axios' import store from './store/index.js' Vue.config.productionTip = false Vue.use(ElementUI) Vue.prototype.$http = axios /* eslint-disable no-new */ new Vue({ el: '#app', router, store, components: { App }, template: '<App/>' })
改写 src/components/page/Blog.vue 使用 mapState 辅助函数生产状态属性
<template> <div> <div class="father-area"> id: <input type="text" v-model="currentArticle.id"> title: <input type="text" v-model="currentArticle.title"> tag: <input type="text" v-model="currentArticle.tag"> count: <input type="text" v-model="count"> </div> <div class="child-area"> <Leftnav></Leftnav> </div> </div> </template> <script> import Leftnav from '../common/LeftNavItem' import { mapState } from 'vuex'; export default { name: 'blog', data() { return { message: 'father message' } }, components: { Leftnav }, methods: { }, computed: mapState(['currentArticle', 'count']) } </script> <style scoped> .father-area { background-color: aqua; padding: 10px; } .child-area { background-color: bisque; padding: 10px; } </yle>
mapState 除了能够传入数组外,还可传入对象
// 1. 直接只用函数返回状态住 computed: mapState({ currentArticle: state => state.currentArticle, count: state => state.count }) // 2. 使用别名对应状态属性,对象中的值 value 对应等同于 state => state.value // key 值是状态值的别名,value 是 state 中的状态值,不能简写成 {currentArticle, count},必须有对应的状态属性 computed: mapState({ currentArticle: 'currentArticle', count: 'count' }) // 3. 由于 computed 中也可能会有其余计算属性,不仅有 mapState,要将它与其余计算属性混合使用,就要用到对象的扩展运算符(...),用于取出参数对象的全部可遍历属性,拷贝到当前对象之中,例如: let z = { a: 3, b: 4}; let n = { ...z }; // 等同于 n = Object.assign({}, z) n // ( a: 3, b: 4 ) // mapState 函数返回的是一个对象,因此混合其余计算属性: computed: { otherComputed() { }, ...mapState(['currentArticle', 'count']) // 也可传入对象 }
Action 提交的是 mutation,而不是直接修改状态
Action 经过 store.dispatch 方法触发
在 Blog.vue 中增长一个按钮和一个方法来增长 count 的值
<template> <div> <div class="father-area"> id: <input type="text" v-model="currentArticle.id"> title: <input type="text" v-model="currentArticle.title"> tag: <input type="text" v-model="currentArticle.tag"> count: <input type="text" v-model="count"> <button @click="countAdd">+</button> </div> <div class="child-area"> <Leftnav></Leftnav> </div> </div> </template> <script> import Leftnav from '../common/LeftNavItem' import { mapState } from 'vuex'; export default { name: 'blog', data() { return { message: 'father message' } }, components: { Leftnav }, methods: { countAdd() { this.$store.dispatch('countIncrement'); } }, computed: mapState(['currentArticle', 'count']) } </script> <style scoped> .father-area { background-color: aqua; padding: 10px; } .child-area { background-color: bisque; padding: 10px; } </style>
点击 + 号后调用方法 countAdd 触发 countIncrement 的 Action,接着触发类型为 COUNT 的 mutation,完成状态的修改。根据 action 中 countIncrement 方法的定义,能够传入第二个参数,this.$store.dispatch('countIncrement', 3)。
Action 也有辅助函数 mapAction 将组件的 methods 映射为 store.dispatch(须要如今根结点注入 store)
<template> <div> <div class="father-area"> id: <input type="text" v-model="currentArticle.id"> title: <input type="text" v-model="currentArticle.title"> tag: <input type="text" v-model="currentArticle.tag"> count: <input type="text" v-model="count"> <button @click="countAdd">+</button> </div> <div class="child-area"> <Leftnav></Leftnav> </div> </div> </template> <script> import Leftnav from '../common/LeftNavItem' import { mapState, mapActions } from 'vuex'; export default { name: 'blog', data() { return { message: 'father message' } }, components: { Leftnav }, methods: { countAdd() { this.countIncrement(2); }, ...mapActions(['countIncrement']) // 将 this.countIncrement 映射为 this.$store.dispatch('countIncrement) }, computed: mapState(['currentArticle', 'count']) } </script> <style scoped> .father-area { background-color: aqua; padding: 10px; } .child-area { background-color: bisque; padding: 10px; } </style>
和 mapStata 相似,mapActions 也能够传入对象,使用别名来代替 countIncrement
methods: { countAdd() { this.add(2); }, ...mapActions({ add: 'countIncrement' }) }
我我的以为 vuex 中的 state、actions 比较难理解,因此笔记就记下这两块,其余官方文档应该能够看明白,和这两个用法也都比较相似。
对于大型项目,通常把 vuex 相关代码分割到模块中
在 src/store 下新建文件
// index.js import Vue from 'vue' import Vuex from 'vuex' import actions from './action' import mutations from './mutation' Vue.use(Vuex); const state = { currentArticle: {id: '1', title: '学习笔记', tag: 'vue'}, count: 1 } export default new Vuex.Store({ state, actions, mutations, }) // actions.js import * as types from './mutation_type' export default { currentArticle({commit}, obj) { commit(types.CURRENT_ARTICLE, obj); }, countIncrement({commit}, n) { commit(types.COUNT, n); } } // mutation_type.js export const CURRENT_ARTICLE = 'CURRENT_ARTICLE' export const COUNT = 'COUNT' // mutation.js import * as types from './mutation_type' export default { [types.CURRENT_ARTICLE](state, obj) { obj.id == undefined ? false : state.currentArticle.id = obj.id; obj.title == undefined ? false : state.currentArticle.title = obj.title; obj.tag == undefined ? false : state.currentArticle.tag = obj.tag; obj.catalog == undefined ? false : state.currentArticle.catalog = obj.catalog; }, [types.COUNT](state, n = 1) { state.count += n; } }
而后在 main.js 中注册 Store 实例便可
// main.js import Vue from 'vue' import App from './App' import router from './router' import store from './store/index.js' Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', router, store, components: { App }, template: '<App/>' })