vue+node+mysql搭建我的博客(二)

在線地址: 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'],
  • props 中的變量和 data 的變量同樣,直接使用 this.childMes 獲取值,
  • :childMes="message" 中的 "message" 便是父組件自身 data 中的變量,

這樣在父組件中更改 message 的值,子組件便會獲得相應的更新。es6

  • 方法 getChild 中的 this.$children[0].childMes 能夠的到子組件中 childMes的值,但不可給其賦值,否值的話會報錯,只能經過父組件中的 message 來更改值。

子組件向父組件通訊

父組件向子組件傳遞事件方法,子組件經過 $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)

Vuex 是什麼

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
  • state:定義了應用狀態的數據結構,"狀態值"的存放處
  • action:定義提交觸發更改信息的描述,常見的例子有從服務端獲取數據,在數據獲取完成後會調用 store.commit() 來調用更改 Store 中的狀態。能夠在組件中使用 dispatch 來發出 actions
  • mutations:惟一容許更新應用狀態的地方
  • getters:Getters 容許組件從 Store 中獲取數據
  • modules:modules 對象容許將單一的 Store 拆分爲多個 Store 的同時保存在單一的狀態樹中

初始化 state

在 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 的新語法 ES6教程

  1. 對象的解構賦值,變量必須與屬性同名,才能取到正確的值

    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
  2. 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
    })
  3. 對象方法屬性能夠簡寫

    const o = {
      method() {
        return "Hello!";
      }
    };
    
    // 等同於
    
    const o = {
      method: function() {
        return "Hello!";
      }
    };
  4. 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

言歸正傳,咱們定義一個 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

Action 提交的是 mutation,而不是直接修改狀態

分發 Action

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)。

輔助函數 mapAction

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 比較難理解,因此筆記就記下這兩塊,其餘官方文檔應該能夠看明白,和這兩個用法也都比較相似。

模塊化 store

對於大型項目,通常把 vuex 相關代碼分割到模塊中

在 src/store 下新建文件

  • index.js // 初始化 state,導出 Vuex.Store 實例
  • actions.js // acitons 對象
  • mutation_type.js // 常量化 mutation 類型
  • mutation.js // mutation 對象
// 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/>'
})
相關文章
相關標籤/搜索