Vue組件開發-高級玩法

在文章《Vue組件開發三板斧:prop、event、slot》中聊了常用的組件開發常用API和一些采坑心得,這里,再說說一些可能不太常用的高級玩法,可參考https://cn.vuejs.org/v2/api/

1. 組件掛載

方式一:components屬性

我們常用的創建組件方式就是文件聲明,例如,在一個假設的 headTop.js 或 headTop.vue 文件中定義組件。然后通過components 引入組件,將其掛載在DOM節點上。

// layout.vue文件
<template>
  <div class="fillcontain">
    <head-top></head-top>
    <div class="table_container container">
      <div class="container_wrap">
        <slot></slot>
      </div>
    </div>
  </div>
</template>

<script>
import headTop from '@/components/headTop'

export default {
  name: 'layout',
  components: {
    headTop // 引用組件
  }
}
</script>

<style lang="less">
@import '../style/mixin';
.table_container {
  min-height: calc(100% - 100px);
}
</style>

組件headTop是掛載在組件layout中某個DOM節點下。

方式二:$mount

還有兩種方式可以創建組件:

  • new Vue()
  • Vue.extend()

new Vue()創建一個 Vue 實例時,都會有一個選項 el,可以用來指定實例的根節點。如果不寫 el 選項,那組件就處于未掛載狀態。看看最頂層的App.vue是如何掛載到根節點上的:

import App from './App'
......
new Vue({
  el: '#app',
  router,
  store,
  template: '<App/>',
  components: { App }
})

Vue.extend 是基于 Vue 構造器,創建一個“子類”,它的參數跟 new Vue 的基本一樣,但是data寫法和組件類似,需要返回一個函數。

import Vue from 'vue';

const AlertComponent = Vue.extend({
  template: '<div>{{ message }}</div>',
  data () {
    return {
      message: 'Hello world!'
    };
  },
});

Vue.extend是無法掛載組件的,此時需要:

  1. 使用$mount 渲染組件或者渲染并掛載組件
  2. 使用JS原生方法,掛載組件
// 方式一:僅僅渲染
const component = new AlertComponent().$mount();
// 通過JS方法組件添加到body節點上
document.body.appendChild(component.$el);

// 方式二:渲染掛載同時做
// 創建并掛載到 #app (會替換 #app)
new AlertComponent().$mount('#app')

應用場景:最常見的應該是自定義全局消息彈窗了。需要將組件掛載在body根節點上,此時,就可以通過$mount指定掛載節點。


同步歪歪一下React......

React 16 的portal也有異曲同工之妙。
portal可以幫助我們在JSX中跟普通組件一樣直接使用dialog, 但是又可以讓dialog內容層級不在父組件內,而是顯示在獨立于原來app在外的同層級組件。

HTML:

<div id="app-root"></div>
// 這里為我們定義Dialog想要放入的位置
<div id="modal-root"></div>

JS:

const modalRoot = document.getElementById('modal-root');

// Let's create a Modal component that is an abstraction around the portal API.
class Modal extends React.Component {
  constructor(props) {
    super(props);
    this.el = document.createElement('div');
  }

  componentDidMount() {
    // Append the element into the DOM on mount. We'll render
    // into the modal container element (see the HTML tab).
    modalRoot.appendChild(this.el);
  }

  componentWillUnmount() {
    // Remove the element from the DOM when we unmount
    modalRoot.removeChild(this.el);
  }
  
  render() {
    // Use a portal to render the children into the element
    return ReactDOM.createPortal(
      this.props.children,
      this.el,
    );
  }
}

2. 渲染函數 render

Vue.js 2.0使用了 Virtual DOM(虛擬 DOM)來更新 DOM 節點,提升渲染性能。

一般我們寫 Vue.js 組件,模板都是寫在 <template> 內的,但它并不是最終呈現的內容,在 Vue.js 編譯階段,會解析為 Virtual DOM。與 DOM 操作相比,Virtual DOM 是基于 JavaScript 計算的,所以開銷會小很多。下圖演示了 Virtual DOM 運行的過程(來自網絡):

vue.png

Vue.js 的 Render 函數就是將template 的內容改寫成一個 JavaScript 對象。官網文檔上有個極好的例子:https://cn.vuejs.org/v2/guide/render-function.html

Vue.component('my-component', {
  render: (h)=> {
    return h('div', {
            style: {
              color: 'red'
            }
          }, '自定義內容');
  }
})

應用場景:如果模板條件太多,用JS處理比HTML處理更加便利時,推薦使用render函數。

3. 遞歸組件

遞歸組件就是指組件在模板中調用自己,其核心是:在組件中設置一個 name 選項。如下:

<template>
  <div>
   這是一個組件,遞歸調用自己
   <my-component></my-component> 
  </div>
</template>
<script>
  export default {
    name: 'my-component'
  }
</script>

當然,上面的代碼是有問題的。如果直接運行,會拋出 max stack size exceeded 的錯誤,因為沒有終止條件,所以組件會無限的遞歸下去,循環至死。

所以,遞歸組件的第二個核心:設置終止條件
改造一下上面的代碼:

<template>
  <div>
   這是一個組件,遞歸調用3次
   <my-component :count="count + 1" v-if="count <= 3"></my-component>
  </div>
</template>
<script>
  export default {
    name: 'my-component',
    props: {
      count: {
        type: Number,
        default: 1
      }
    }
  }
</script>

應用場景:樹形組件

4. 組件通信:provide / inject

這對選項需要一起使用!( Vue.js 2.2.0 版本后新增的 API)
允許一個祖先組件向其所有子孫后代注入一個依賴,不論組件層次有多深,并在起上下游關系成立的時間里始終生效。

是不是和React context很相似!!

// 父級組件提供 'foo'
var Provider = {
  provide: {
    foo: 'bar'
  },
  // ...
}

// 子組件注入 'foo'
var Child = {
  inject: ['foo'],
  created () {
    console.log(this.foo) // => "bar"
  }
  // ...
}

應用場景:某種意義上可以代替Vuex。如果你的項目只是需要全局共享一些公共狀態信息,比如用戶名,那么,用provide / inject足夠了。

比如,在app.vue中注入根組件。

<script>
  export default {
    provide () {
      return {
        app: this
      }
    },
    data () {
      return {
        userInfo: null
      }
    },
    methods: {
      getUserInfo () {
        // 通過 ajax 獲取用戶信息后,賦值給 this.userInfo
        $.ajax('/user/info', (data) => {
          this.userInfo = data;
        });
      }
    },
    mounted () {
      this.getUserInfo();
    }
  }
</script>

然后,任何組件都可以使用到userInfo數據:

<template>
  <div>
    {{ app.userInfo }}
  </div>
</template>
<script>
  export default {
    inject: ['app']
  }
</script>

是不是比用Vuex簡潔多了!

5. 數據更新:$set

之前提過,向響應式對象中添加一個屬性,該新屬性是非響應式的,視圖也無法更新。所以為了保證新屬性的響應性,可以用此API。

this.$set(data, 'checked', true);

小結

https://cn.vuejs.org/v2/api/是個好東西,多翻翻里面的api,可以發現很多有趣的功能。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 基于Vue的一些資料 內容 UI組件 開發框架 實用庫 服務端 輔助工具 應用實例 Demo示例 element★...
    嘗了又嘗閱讀 1,171評論 0 1
  • 回憶 首先,render函數中手寫h=>h(app),new Vue()實例初始化init()和原來一樣。$mou...
    LoveBugs_King閱讀 2,298評論 1 2
  • 前幾天想學學Vue中怎么編寫可復用的組件,提到要對Vue的render函數有所了解。可仔細一想,對于Vue的ren...
    kangaroo_v閱讀 116,125評論 13 171
  • UI組件 element- 餓了么出品的Vue2的web UI工具套件 Vux- 基于Vue和WeUI的組件庫 m...
    小姜先森o0O閱讀 9,609評論 0 72
  • UI組件 element- 餓了么出品的Vue2的web UI工具套件 Vux- 基于Vue和WeUI的組件庫 m...
    流觴小菜鳥閱讀 1,816評論 2 8