vue學習之組件

組件從註冊方式分爲全局組件和局部組件。css

從功能類型又能夠分爲偏視圖表現的(presentational)和偏邏輯的(動態生成dom),推薦在前者中使用模板,在後者中使用 JSX 或渲染函數動態生成組件模板內容,總體來講表現類的組件遠遠多於邏輯類組件。html

定義組件

注意:組件名最好使用全小寫加短橫線,即便用 kebab-case (短橫線分隔命名) 定義一個組件,在引用這個自定義元素時必須使用 kebab-case,vue

當使用 PascalCase (首字母大寫命名) 定義一個組件時,在引用這個自定義元素時兩種命名法均可以使用。webpack

Vue.component('MyComponentName', { /* ... */ })

也就是說 <my-component-name><MyComponentName> 都是可接受的。git

全局組件

Vue.component,在new根實例以前定義全局組件github

// 定義一個名爲 button-counter 的全局組件
Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})

//使用:
<button-counter></button-counter>

顯示如圖:web

局部組件

全局註冊的組件,對於再也不使用的,仍會被包含在最終的構建結果中,形成了用戶下載的 JavaScript 的增長。正則表達式

局部組件有幾種註冊形式,這裏介紹單文件組件。vue-router

一個單文件組件即一個vue文件,包含template(必須有),script,style三部分。vue-cli

JavaScript、CSS 分離成獨立的文件而後作到熱重載和預編譯。

<!-- my-component.vue -->
<template>
  <div>This will be pre-compiled</div>
</template>
<script src="./my-component.js"></script>
<style src="./my-component.css"></style>

每一個組件必須有且只有一個根元素,使用組件時,組件上的特性也會被添加到根元素上,根元素上已有的同名特性會被覆蓋,除了class和style,會與組件的class和style合併,詳細見下面「引用第三方庫」

//com.vue
<template lang="html"> <div>//只能有一個html標籤包裹裏面全部節點    child component{{name}}
</div> </template> <script> export default{    
    data(){//data部分必須是個函數,返回一個對象。當前組件可使用data裏面的數據
      return {
       name:'com'
       }
    }
  }
</script>
<style lang="css">
</style>

 引入單文件組件

//App.vue引入組件 import HelloWorld from "./components/HelloWorld"; //在頁面實例注入組件  components: { HelloWorld }, //在頁面使用組件 <HelloWorld /> //或者 <HelloWorld><HelloWorld />

組件的定義方法,參考邊界處理之模板定義的替代品

關於template標籤。

組件的複用

每用一次組件,就會有一個它的新實例被建立,每一個組件都會各自獨立維護自身屬性,互相獨立不干擾,這也是爲何組件的data是一個函數的緣由:

<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>

父子組件

上面例子中,App.vue的components上掛載了HelloWorld這個組件,其中App.vue是父組件,HelloWorld是子組件。

父子組件通訊

props(參考博文):父組件傳遞消息給子組件,爲單向的,意味着應該在一個子組件內部改變 prop。

每次父組件發生更新時,子組件中全部的 prop 都將會刷新爲最新的。

//子組件中聲明props
<template lang="html">
   <div>
    年齡:{{age}}
    </div>
</template>
<script>
    export default{
      props:['age'],
      data(){
        return{
          name:'com'
        }
      }
    }
</script>

//組件特性傳入prop值
 <com age="19"></com>//靜態數據
 <com :age="ageNum"></com>//動態數據:變量

兩種常見的試圖改變一個 prop 的情形

一、這個 prop 用來傳遞一個初始值;這個子組件接下來但願將其做爲一個本地的 prop 數據來使用。在這種狀況下,最好定義一個本地的 data 屬性並將這個 prop 用做其初始值

props: ['initialCounter'],
data: function () {
  return {
    counter: this.initialCounter
  }
}

二、這個 prop 以一種原始的值傳入且須要對這個值進行操做在這種狀況下,最好使用這個 prop 的值來定義一個計算屬性

props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}

注意在 JavaScript 中對象和數組是經過引用傳入的,因此對於一個數組或對象類型的 prop 來講,在子組件中改變這個對象或數組自己將會影響到父組件的狀態。

$emit:子組件向父組件傳遞數據

使用.sync修飾符雙向綁定父子組件的變量

好比下面,點擊子組件的這個按鈕(子組件接收的用戶行爲),觸發組件自定義的patch事件,執行patch事件的處理函數msg3,改變父組件的ageNum數據

在子組件中代碼:

<button type="button" name="button" @click="$emit('patch',[參數])">發送到父組件</button>

父組件中代碼:

<com :age="ageNum" @patch='msg3'></com>//動態數據:變量
export default{
    data(){
      return {
        ageNum: 18
      }
    },

methods: {
  msg3(params){
     //params:子組件傳遞過來的參數 
    this.ageNum++
  }
 }
}

vm.$attrs 屬性和vm.$listeners屬性(單向傳值,查閱博文:實例屬性和方法

經常使用在跨級組件中,祖孫組件通訊。

 

組件上使用v-model指令

查閱博文:v-model

組件上的原生事件

在一個組件的根元素上直接監聽一個原生事件,使用 v-on.native 修飾符。

<base-input v-on:focus.native="onFocus"></base-input>

插槽和做用域插槽

查閱博文:slot

引用第三方庫的組件

組件能夠接受任意的特性,而這些特性會被添加到這個組件的根元素上。

例如,你經過一個 Bootstrap 插件使用了一個第三方的 <bootstrap-date-input> 組件,而後這個 data-date-picker="activated" 特性就會自動添加到 <bootstrap-date-input> 這個組件的根元素上:

<bootstrap-date-input
  data-date-picker="activated"
  class="date-picker-theme-dark"
></bootstrap-date-input>

該組件根元素爲

<input type="date" class="form-control">

替換/合併根元素已有特性

對於絕大多數特性來講,從外部提供給組件的值會替換掉組件內部設置好的值。因此若是組件上有 type="text" ,input的 type="date" 就會被替換掉並被破壞!classstyle 特性會把兩邊的值會被合併起來,最終的值:form-control date-picker-theme-dark

最終,input爲:

<input
type="date"
data-date-picker="activated"
  class="form-control date-picker-theme-dark
">

禁用特性繼承

若是你但願組件的根元素繼承特性,你能夠在組件的選項中設置 inheritAttrs: false,該選項不會影響 styleclass 的綁定。適合配合實例的 $attrs 屬性使用。

組件嵌套在有嵌套規則的標籤裏

有些 HTML 元素,諸如 <ul><ol><table><select>,對於哪些元素能夠出如今其內部是有嚴格限制的。而有些元素,諸如 <li><tr><option>,只能出如今其它某些特定的元素內部。

//這個自定義組件 <blog-post-row> 會被做爲無效的內容提高到外部
<table>
  <blog-post-row></blog-post-row>
</table>

經過is特性解決:

<table>
  <tr is="blog-post-row"></tr>
</table>

動態組件

使用vue內置標籤<component>,經過添加is特性來切換不一樣的組件。

好比:在一個多標籤的界面中切換不一樣的組件(這個標籤的位置渲染爲那個組件)

<!-- 組件會在 `currentTabComponent` 改變時改變 -->
<component v-bind:is="currentTabComponent"></component>

在上述示例中,currentTabComponent 能夠是

  • 已註冊組件的名字
  • 一個組件的選項對象(推薦)

當想保持組件被切換時的狀態,以免反覆重渲染致使的性能問題。

好比下例,posts的第二個文章被選中,切換到 Archive 標籤,而後再切換回 Posts,是不會繼續展現以前選擇的文章的。

需求:組件實例可以在它們第一次被建立的時候緩存下來。

方案:<keep-alive> 元素將動態組件包裹起來。

參考博文:vue內置標籤

異步組件

在須要的時候才從服務器加載一個模塊,Vue 提供一個工廠函數,resolve 回調會在從服務器獲得組件定義的時候被調用。

Vue.component('async-example', function (resolve, reject) {
  setTimeout(function () {//setTimeout 是爲了演示用的,如何獲取組件取決於業務。     // 向 `resolve` 回調傳遞組件定義
    resolve({
      template: '<div>I am async!</div>'
    })
  }, 1000)
})

一個推薦的作法是將異步組件和 webpack 的 code-splitting 功能一塊兒配合使用:

Vue.component('async-webpack-example', function (resolve) {
  // 這個特殊的 `require` 語法將會告訴 webpack
  // 自動將你的構建代碼切割成多個包,這些包
  // 會經過 Ajax 請求加載
  require(['./my-async-component'], resolve)
})

也能夠在工廠函數中返回一個 Promise:

Vue.component(
  'async-webpack-example',
  // 這個 `import` 函數會返回一個 `Promise` 對象。
  () => import('./my-async-component')
)

當使用局部註冊的時候,你也能夠直接提供一個返回 Promise 的函數:

new Vue({
  // ...
  components: {
    'my-component': () => import('./my-async-component')
  }
})

處理加載狀態:

const AsyncComponent = () => ({
  // 須要加載的組件 (應該是一個 `Promise` 對象)
  component: import('./MyComponent.vue'),
  // 異步組件加載時使用的組件
  loading: LoadingComponent,
  // 加載失敗時使用的組件
  error: ErrorComponent,
  // 展現加載時組件的延時時間。默認值是 200 (毫秒)
  delay: 200,
  // 若是提供了超時時間且組件加載也超時了,
  // 則使用加載失敗時使用的組件。默認值是:`Infinity`
  timeout: 3000
})

Vue Router 的路由組件中使用上述語法的話,必須使用 Vue Router 2.4.0+ 版本。

組件值之處理邊界狀況

介紹組件使用中遇到的一些問題:父子孫組件的訪問,組件循環或者互相引用,模板其餘的定義方式,強制更新(數組對象的變動檢測注意事項,或者可能依賴了非響應式狀態),在根元素使用v-once建立靜態組件。

基礎組件一次性引入

推薦建立一個 components 目錄管理單文件組件。

針對基礎組件的長列表的改善:

import BaseButton from './BaseButton.vue' import BaseIcon from './BaseIcon.vue' import BaseInput from './BaseInput.vue'

export default {
  components: {
    BaseButton,
    BaseIcon,
    BaseInput
  }
}

使用webpack(或在內部使用了 webpack 的 Vue CLI 3+)的前提下,使用 require.context 全局註冊這些基礎組件。

如下代碼放在應用入口文件 (好比 src/main.js) 中全局導入基礎組件

import Vue from 'vue'
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'

const requireComponent = require.context(
  // 其組件目錄的相對路徑
  './components',
  // 是否查詢其子目錄
  false,
  // 匹配基礎組件文件名的正則表達式
  /Base[A-Z]\w+\.(vue|js)$/
)

requireComponent.keys().forEach(fileName => {
  // 獲取組件配置
  const componentConfig = requireComponent(fileName)

  // 獲取組件的 PascalCase 命名
  const componentName = upperFirst(
    camelCase(
      // 剝去文件名開頭的 `./` 和結尾的擴展名
      fileName.replace(/^\.\/(.*)\.\w+$/, '$1')
    )
  )

  // 全局註冊組件
  Vue.component(
    componentName,
    // 若是這個組件選項是經過 `export default` 導出的,
    // 那麼就會優先使用 `.default`,
    // 不然回退到使用模塊的根。
    componentConfig.default || componentConfig
  )
})
相關文章
相關標籤/搜索