在項目中, 咱們會遇到將Vnode或者component渲染到指定的dom中. 這時候, 咱們可能須要去研究Vue中的源碼, 會有如何建立Vnode? 如何渲染Vnode?等相關的疑問. 今天咱們不研究源碼, 咱們從實際實踐中來看看實現的方式.javascript
首先, 在vue中咱們一般是經過建立一個Vue實例對象或者建立Vue後經過$mount來掛載到一個Dom元素上,完成Vue項目的渲染.html
import Vue from "vue";
import App from "../app.vue";
new Vue({
el: "#app",
render: h => h(App)
})
複製代碼
或者vue
import Vue from "vue";
import App from "../app.vue";
new Vue({
render: h => h(App)
}).$mount("#app")
複製代碼
基於這個咱們會想, 是否是再建立一個Vue實例來掛載到另一個指定的Dom中呢?java
<div id="app"></div>
<div id="app2"></div>
複製代碼
new Vue({
render: h => h(App)
}).$mount("#app")
new Vue({
render: h => h(App)
}).$mount("#app2")
複製代碼
效果:node
其實這種想法是可行的, 可是不合理, 咱們不可能每遇到這種狀況就這樣處理吧. 按照習慣, 咱們建立一個Vue就相似建立了一個應用程序/app. 那有沒有其餘辦法呢?app
辦法固然是有的, 這一次, 咱們結合虛擬dom和dom的關係來作這一次思考. 虛擬dom其實就是一個標籤的語法樹, 經過這棵樹經過patch函數建立相關的標籤, 渲染到root Dom. 語法樹上的數據有所變化, 則會更新數據相關的dom標籤. 也就是說數據對應標籤, 數據變化, 標籤內容也會變化, 他們是對應的. 基於這一個想法, 咱們來完成一個簡單的實例.dom
實例說明:函數
目的: 經過dom來建立本身的組件, 而後將組件融入到Vue組件中, 實現互相通訊.ui
步驟:this
export default {
data() {
return {
dom: null
}
},
render(create) {
return create('div', {}, [this.$slots.click, this.$slots.body])
}
}
複製代碼
在App.vue中加入Hello代碼, hello中的slot設定了一個按鈕, 按鈕擁有點擊事件, 事件是改變slot body中的內容.
<template>
<section>
<h3>{{message}}</h3>
<hello>
<button slot="click" @click="random">點擊我</button>
<div slot="body">我是內容: {{content}}</div>
</hello>
</section>
</template>
<script>
import Hello from './hello';
export default {
components: {Hello},
data() {
return {
content: '',
message: "Hello Vuejs"
}
},
methods: {
random() {
this.content = new Date();
}
}
}
</script>
複製代碼
效果:
只要點擊按鈕就會改變內容.
這樣一個基本的組件就完成了, 可是他仍是純粹的一個vue功能的組件. 咱們須要加入實際DOM的功能. 那咱們繼續跟進. 在Hello組件中加入一個建立的DOM功能. 這個dom展現在右上角.
function DomDemo(){
this.root = document.createElement("div");
this.body = document.createElement('div');
this.root.appendChild(this.body);
this.root.className = "domdemo";
this.setContent = function(html) {
this.body.innerHTML = html;
}
document.body.appendChild(this.root);
}
export default {
mounted() {
this.dom = new DomDemo();
console.log(this.dom)
},
data() {
return {
dom: null
}
},
render(create) {
return create('div', {}, [this.$slots.click, this.$slots.body])
}
}
複製代碼
固然他們尚未直接的關係, 這裏咱們須要將hello組件中的slot body 的內容放入到dom中. 而後點擊按鈕, dom中的內容也會改變, 這也就實現了響應式. vue組件和dom組件實現了交互. 那如何處理呢? 咱們修改一下Hello組件代碼.
function DomDemo(){
this.root = document.createElement("div");
this.body = document.createElement('div');
this.root.appendChild(this.body);
this.root.className = "domdemo";
this.vnodeToElment = function(nodes) {
if(!nodes) return [];
const elements = [];
nodes.forEach(node => {
elements.push(node.elm);
});
return elements;
}
this.setContent = function(nodes) {
this.body.innerHTML = '';
this.vnodeToElment(nodes).forEach(item => {
this.body.appendChild(item);
})
}
document.body.appendChild(this.root);
}
export default {
mounted() {
this.dom = new DomDemo();
this.dom.setContent(this.$slots.body);
},
data() {
return {
dom: null
}
},
render(create) {
return create('div', {}, [this.$slots.click, this.$slots.body])
}
}
複製代碼
這樣咱們的內容就到了dom中, 點擊vue組件中的按鈕, dom中的內容也會改變了: 首先將vue虛擬dom渲染到界面中, 再移動元素到dom中, 這樣就完成了整個效果. 其實本質仍是引用的問題, 內存地址的關係.
這個例子有什麼做用呢? 其實用處仍是蠻大的. 好比咱們開發的時候, 須要引入dom相關的組件, 好比popover等, 均可以使用這種方式.
從Vue實例對象掛載到DOM, 再到實例對象中的VNODE掛載到指定的dom中, 來實現咱們業務須要的功能, 也方便掛載dom相關的組件. 咱們須要知道Vue的渲染過程, 在不去看源碼的時候, 多思考, 多設想在渲染過程當中會作什麼樣的處理. 以及咱們應該如何應對, 巧妙的融合起來. 這裏咱們也須要把基礎打牢固, 瞭解引用的本質.