在文章《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
是無法掛載組件的,此時需要:
- 使用
$mount
渲染組件或者渲染并掛載組件 - 使用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.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,可以發現很多有趣的功能。