5. Vue 組件化

Vue 組件官網(wǎng):https://cn.vuejs.org/v2/guide/components.html

組件化的思想

  • 模塊化是一種思想,一種構(gòu)建方式,把一種很復(fù)雜的事物拆分成一個一個的小模塊,然后通過某種特定的方式把這些小模塊組織到一起相互協(xié)作完成這個復(fù)雜的應(yīng)用功能。
  • 在 Vue 中,組件就是用來封裝視圖(HTML)的。組件思想就是把一個很大的復(fù)雜的 Web 頁面視圖拆分成一塊一塊的組件視圖,然后利用某種特定的方式把他們組織到一起完成完整的 Web 應(yīng)用構(gòu)建。
    • HTML 結(jié)構(gòu)
    • CSS 樣式
    • JavaScript 行為
  • 為什么要把視圖組件化,優(yōu)勢?
    • 開發(fā)效率
    • 可維護性
    • 可重用性
    • 便于分工

通過 Element 體會組件的威力

Element 是基于 Vue 開發(fā)的一個知名的第三方組件庫,它能幫助我們更加快速的構(gòu)建應(yīng)用。
Element 官網(wǎng)

使用:

  • 安裝:`npm i element-ui -S``
  • 引入 element-ui 組件庫的 CSS 樣式
  • 導(dǎo)入 Vue
  • 引入 element-ui 組件庫的 JavaScript 腳本
  • 調(diào)用 element-ui 組件

組件是什么

組件在代碼中的直觀體現(xiàn)就是封裝了一個自定義的 HTML 標簽

使用組件

組件的定義方式分為兩種,全局定義和局部定義:

  • 全局組件定義在全局,在任意組件中都能直接使用
  • 局部組件定義在局部,只能在當前組件使用
  • 建議把通用組件定義在全局,把不通用組件定義在局部

全局注冊

注冊:
基本形式:Vue.component('組件名字', 組件模板對象)
方式 一:

Vue.component('my-component', 
  Vue.extend({
    template: '<div>A custom component!</div>'
  })
)
new Vue({
  el: '#app'
})

方式二:

Vue.component('my-component', {
  template: '<div>A custom component!</div>'
})
new Vue({
  el: '#app'
})

方式三:

<template id="tmpl">
  <div>A custom component!</div>
</template>
<script>
  Vue.component('my-component', {
    template: '#tmpl'
  })
  new Vue({
    el: '#app'
  })
</script>

方式四:

<script type="text/x-template" id="tmpl">
  <div>A custom component!</div>
</script>
<script>
  Vue.component('my-component', {
    template: '#tmpl'
  })
  var vm = new Vue({
    el: '#app'
  })
</script>

在模板中使用組件:

<div id="app">
  <my-component></my-component>
</div>

渲染結(jié)果:

<div id="app">
  <div>A custom component!</div>
</div>

局部注冊

不必把每個組件都注冊到全局??梢酝ㄟ^某個 Vue 實例/組件的實例選項 components 注冊,僅作用在其作用域中可用的組件:
注冊:

new Vue({
  el: '#app'
  components: {
    // <my-component> 將只能在父組件模板中可用
    // 鍵名就是組件名稱,值是一個對象,對象中配置組件的選項
    'my-component': {
      template: '<div>A custom component!</div>'
    }
  }
})

使用:

<div id="app">
  <my-component></my-component>
</div>
  • 組件一般分為兩種
    • 通用的組件
    • 不通用的業(yè)務(wù)組件
      所以設(shè)計具體業(yè)務(wù)盡量定義成局部。不要污染全局

組件的模板

  • 組件的模板 template 只能有有一個根元素,否則警告報錯:Component template should contain exactly one root element.

  • 組件定義 template 可以是字面量字符串或是一個定義了字面量字符串的變量,缺點是沒有高亮,內(nèi)置在 JavaScript 中,寫起來麻煩。

  • X-Templates:template 可以寫在 script 標簽中,解決了高亮的問題,但是當組件數(shù)量多的時候,也麻煩。

  • 以上方式都不好,我們最終的解決方案是使用 Vue 的 .vue 單文件組件來寫。但是要想使用這種方式必須結(jié)合 webpack 構(gòu)建工具。

    image.png

組件的復(fù)用

  • 組件是可復(fù)用的 Vue 實例,可以有自己的 data、methods、computed、watch 等等選項
  • 組件是獨立的作用域,不能訪問父組件的數(shù)據(jù)
  • 組件的 data 必須是函數(shù),函數(shù)中返回一個對象作為組件的 data。因此每個實例可以維護一份被返回對象的獨立的拷貝。
data: function () {
  return {
    count: 0
  }
}

組件的組織

  • 采用組件化構(gòu)建方式,一個應(yīng)用被一個根組件管理起來,根組件中嵌套了子組件,子組件還可以嵌套自己的子子組件。


    組件樹

放到匿名自執(zhí)行函數(shù)中,將某些代碼包裹起來可以實現(xiàn)塊級作用域的效果,減少全局變量的數(shù)量,減少命名沖突,在匿名自執(zhí)行函數(shù)執(zhí)行結(jié)束后變量就會被內(nèi)存釋放掉,從而也會節(jié)省了內(nèi)存。

圖解 Vue 組件化構(gòu)建方式

從程序角度實現(xiàn)組件化應(yīng)用構(gòu)建的架構(gòu)方式如下圖,這種方式的缺點是:

  • 組件的模板沒有高亮
  • 沒有模塊化之前,首頁一堆 script 標簽,比較麻煩
todoMVC 的 Vue 組件化構(gòu)建

使用 vue 實例的 render 方法渲染組件

官網(wǎng) render 選項:https://cn.vuejs.org/v2/api/#render
我們可以用 render 選項進行組件渲染。該渲染函數(shù)接收一個 createElement 方法作為第一個參數(shù)用來創(chuàng)建 VNode,與前面介紹的的組件渲染不同的是,使用 render 渲染會把 app 的 div 給覆蓋。所以也就只能進行一次模板替代。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="node_modules/vue/dist/vue.js"></script>
</head>

<body>
    <div id="app">
    </div>
    <script>
        var login = {
            template: '<h1>這是登錄組件</h1>'     
        }
        var vm=new Vue({
           el:'#app',
           render: function (createElement, context) {
               return createElement(login)
           }
        });
    </script>
</body>
</html>
運行結(jié)果

組件通信

在 Vue 中,父子組件的關(guān)系可以總結(jié)為 prop 向下傳遞,事件向上傳遞。父組件通過 prop 給子組件下發(fā)數(shù)據(jù),子組件通過事件給父組件發(fā)送消息。


父子組件關(guān)系

父子組件通信:Props Down

  1. 在父組件中通過子組件標簽聲明屬性的方式傳遞數(shù)據(jù)。注意:只有 v-bind 才可以傳遞動態(tài)數(shù)據(jù)。
<child message="hello!"></child>
  1. 在子組件中聲明 props 接收父組件傳遞的數(shù)據(jù),組件接收到 props 就可以像訪問 data 中的數(shù)據(jù)一樣,來訪問 props 中的數(shù)據(jù)并使用。
Vue.component('child', {
  // 聲明 props
  props: ['message'],
  // 就像 data 一樣,prop 也可以再模板中使用
  // 同樣也可以再 vm 實例中通過 this.message 來使用
  template: '<span>{{ message }}</span>'
})
  1. Vue 組件通信原則:單向數(shù)據(jù)流
    父組件數(shù)據(jù)的改變可以影響到子組件,但是子組件不要去修改父組件的數(shù)據(jù)。因為當你的組件嵌套過深的時候,在子組件中修改某個父組件的數(shù)據(jù)可能會讓你的應(yīng)用數(shù)據(jù)流變得非常復(fù)雜而難以理解。

注意:在 JavaScript 中對象和數(shù)組是通過引用傳入的,所以對于一個數(shù)組或?qū)ο箢愋偷?prop 來說,在子組件中改變這個對象或數(shù)組本身將會影響到父組件的狀態(tài)。引用類型數(shù)據(jù)雖然可以修改,但是不建議使用,因為這樣就違背了 Vue 組件的通信原則

思考:那子組件要改數(shù)據(jù)呢?子組件能不能把數(shù)據(jù)給父親呢,讓父組件自己去修改自己的數(shù)據(jù)呢?

父子組件通信:Event Up

父組件使用 prop 傳遞數(shù)據(jù)給子組件。但子組件怎么跟父組件通信呢?這個時候 Vue 的自定義事件系統(tǒng)就派的上用場了。

  1. 在子組件中調(diào)用 $emit() 方法發(fā)布一個事件
Vue.component('button-counter', {
  template: `<button v-on:click="incrementCounter">{{ counter }}</button>`
  data: function() {
    return {
      counter: 0
    }
  }
  methods: {
    incrementCounter: function () {
      this.counter += 1
      // 發(fā)布一個名字叫 increment 的事件
      this.$emit('increment')
    }
  }
})
  1. 在父組件中提供一個子組件內(nèi)部發(fā)布的事件處理函數(shù)
new Vue({
  el: '#counter-event-example',
  data: {
    total: 0
  },
  methods: {
    incrementTotal : function () {
      this.total += 1
    }
  }
})
  1. 在子組件的模板的標簽上訂閱子組件內(nèi)部發(fā)布的事件
<div id="counter-event-example">
  <p>{{ total }}</p>
  <!--訂閱子組件內(nèi)部發(fā)布的 increment 事件,當子組件內(nèi)部 $emit('increment') 
      發(fā)布的時候,就會調(diào)用到父組件中的 incrementTotal 方法-->
  <button-counter @increment="incrementTotal"></button-counter>
</div>

非父子組件通信:Event Bus

專業(yè)組件通信:Vuex

Vuex 是 Vue 配套的公共數(shù)據(jù)管理工具,它可以把一些共享的數(shù)據(jù),保存到 Vuex 中,方便整個程序的任何組件直接獲取或修改我們的公共數(shù)據(jù)。如果組件之間有共享的數(shù)據(jù),可以直接掛載到 Vuex 中,而不必通過父子組件之間傳值。

Vuex 是一個全局的共享數(shù)據(jù)存儲區(qū)域,就相當于是一個數(shù)據(jù)的倉庫

vuex 的使用

配置vuex的步驟

  1. 運行 cnpm i vuex -S
  2. 導(dǎo)入包:import Vuex from 'vuex'
  3. 注冊 vuex 到 vue 中:Vue.use(Vuex)
  4. new Vuex.Store() 實例,得到一個 數(shù)據(jù)倉儲對象。
var store = new Vuex.Store({
  state: {
    // 專門用來存儲數(shù)據(jù)的
  },
  mutations: {
  // 專門用來定義方法的
}
  1. 將 vuex 創(chuàng)建的 store 掛載到 VM 實例上, 只要掛載到了 vm 上,任何組件都能使用 store 來存取數(shù)據(jù)
    })
import App from './App.vue'

const vm = new Vue({
  el: '#app',
  render: c => c(App),
  store 
  1. 操作共享的數(shù)據(jù)與方法。
  • 如果在組件中想要訪問store 中的數(shù)據(jù),只能通過this.$store.state來訪問。
  • 如果組件想要調(diào)用 mutations 中的方法,只能使用this.$store.commit('方法名')。
  • 如果要操作 store 中的 state 值,只能通過 調(diào)用 mutations 提供的方法,才能操作對應(yīng)的數(shù)據(jù),不推薦直接操作 state 中的數(shù)據(jù),因為萬一導(dǎo)致了數(shù)據(jù)的紊亂,不能快速定位到錯誤的原因,因為,每個組件都可能有操作數(shù)據(jù)的方法。
  • mutations 的 函數(shù)參數(shù)列表中,最多支持兩個參數(shù):
    • param1: 是 state 狀態(tài)
      +param2: 通過 commit 提交過來的參數(shù)
      要想傳遞多個參數(shù),可以通過對象或者數(shù)組去傳遞。
  • Vuex 中除了 state 和 mutations 選項參數(shù),還有 getters 選項參數(shù),只負責(zé)對外提供數(shù)據(jù),不修改數(shù)據(jù)(想要修改 state 中的數(shù)據(jù)用 mutations) 。
    • getters 中的方法,和過濾器比較類似,因為過濾器和 getter 都沒有修改原數(shù)據(jù),只是把原數(shù)據(jù)做了一層包裝,提供給調(diào)用者
    • getters 正好也引用了這個數(shù)據(jù),那么就會立即觸發(fā) getters 重新求值。
    • 組件訪問 getters 要用this.$store.getters來訪問

路由

什么是路由?

  1. 后端路由:對于普通的網(wǎng)站,所有的超鏈接都是URL地址,所有的URL地址都對應(yīng)服務(wù)器上對應(yīng)的資源。
  2. 前端路由:對于單頁面應(yīng)用程序來說,主要通過URL中的hash(#號)來實現(xiàn)不同頁面之間的切換,同時,hash有一個特點:HTTP請求中不會包含hash相關(guān)的內(nèi)容;所以,單頁面程序中的頁面跳轉(zhuǎn)主要用hash實現(xiàn)。

vue-router

vue-router官方文檔:https://router.vuejs.org/zh/

安裝:

npm install vue-router

引包:

<script src="node_modules/vue-router/dist/vue-router.js"></script>

箭頭函數(shù)綁定父級上下的 this

路由、和服務(wù)端交互、webpack

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容