剛纔翻了一下博客,才發現,距離本身寫的第一篇Vue的博客vue.js之綁定class和style(2016-10-30)已通過去一年零兩天。這一年裏,本身從船廠的普通技術員,成爲了一個微型不靠譜創業公司的普通碼農。發過一次燒,搬了兩次家,沒攢下什麼錢。好,牢騷發到這裏,接下來談談傳說中接近Vue底層的api==render函數。html
好久好久之前,前端的數據和視圖居住在一塊兒,在強大的jQuery的管理下,他們相處的還算能夠。可是隨着頁面愈來愈複雜,DOM樹的節點愈來愈多,數據夾雜在DOM中變得愈來愈難於管理。因而一聲炮響,迎來了數據驅動視圖的MVVM框架,數據和視圖被一條天河劃分開來,整個頁面的數據狀態開始變得整潔起來。而鏈接數據視圖的鵲橋是虛擬DOM,關於虛擬DOM參看全面理解虛擬DOM,實現虛擬DOM。構成DOM的每個節點在Vue中被稱爲vnode。(這段不嚴謹,大膽假設沒求證)前端
在咱們生成真實的DOM結構時,能夠寫一個HTML文件描述文檔結構交給瀏覽器去解析,同時也能夠經過DOM 的api innerHTML告訴瀏覽器結構是什麼,還能夠用createElement來構建DOM樹,以喜聞樂見的hello world爲例,html和innerHTML api 對DOM結構的描述都是<h1>hello world</h1>
,可是用createElement就變成了這個樣子:vue
var h1 = document.createElement('h1'); var hw = document.createTextNode('hello world') h1.appendChild(hw); document.body.appendChild(h1);
這就是描述一個DOM結構的方式,你能夠用一個html文件,一個字符串,或者一段js代碼,可是他們都是在作同一件事,就是告訴瀏覽器該怎麼渲染你想要的頁面。如今咱們回頭看vue,在構建vue實例時,咱們要寫一個叫template的屬性,裏面是一個html同樣的字符串。那麼,vue對這個字符串作什麼了?確定不是羞羞的事情。事實上,vue拿它構建了虛擬DOM。node
Vue.compile
這個靜態方法給咱們展現了一個漂亮的字符串模板是怎麼變成一個奇怪的render函數的:api
Vue.compile('<h1>hello world</h1>') //返回 {staticRenderFns: Array(0), render: ƒ}
render屬性對應的是一個函數,在Vue的實例的上下文中調用它會獲得字符串對應的虛擬DOM節點,能夠把下面的代碼粘貼到Vue官網的控制檯下面看看效果:數組
let r = Vue.compile('<h1>hello world</h1>');//獲得{staticRenderFns: Array(0), render: ƒ} r.render.call(new Vue({})) //返回 VNode {tag: "h1", data: undefined, children: Array(1), text: undefined...}
因而咱們抽絲剝繭,終於看到了VNode長什麼樣子,有tag屬性,還有children,text...總之咋一看,還真的跟真實的DOM對象有幾分類似,真實DOM中有tagName
,children
,textContent
...瀏覽器
上面咱們看到了render函數和模板字符串不一樣尋常的關係以及經過Vue.compile進行轉換,下面來看看render函數的具體構造。要注意的是,編譯後獲得的不是VNode樹,而是生成VNode的函數。app
在建立Vue實例的過程當中,若是傳入的選項中有template和render兩個屬性,render會有更高的優先級:框架
new Vue({ template:'...', render:f(){}//優先級高 })
這就表示,Vue在看到你要用render函數描述虛擬DOM時。會很高興,由於它不用本身編譯你給他的模板字符串來獲得render函數,省力又省心。同時它會丟給你一個函數,這個函數是你構建虛擬DOM所須要的工具,官網上給他起了個名字叫createElement。還有約定的簡寫叫h,vm中有一個方法_c,也是這個函數的別名。ide
下面咱們先來講說這個構建虛擬Dom的工具,createElement函數。參考官網createElement-參數,首先思考一個普通的html元素會傳遞給咱們哪些信息,<h1 class='foo'>hello world</h1>
,沒錯,咱們能夠獲得3部分有效信息:
上面提到,render函數和模板字符串是描述虛擬DOM樹的兩種方式,那麼用createElement函數來描述就變成了下面這樣:
craeteElement('h1', {class,style,on,attrs:{name,id}, 'hello world'}) //這裏,第三個參數還有玄機,接收的參數十分靈活,詳情參考官網關於這三個參數的描述
看到了吧,以前咱們從字符串中獲得的有效信息到了函數這邊變成了輸入的參數,而輸出這是一個虛擬DOM節點。咱們不妨叫他們createElement三劍客。
參數類型是一個字符串或者一個對象一個函數。像下面這樣:
'div'//字符串 { data:{}, methods:{}, mounted:{} }//一個組件選項對象 function(){return 'div'}//返回上面兩種
一個數據對象,包括對根元素html屬性的描述,和組件屬性的描述,詳情見官網,比方說你要描述這麼一個節點:
<man class="color" height="1.4m" weight="50kg" v-on:move="handle" />
須要傳入的第二個參數應該是
{ 'class':{color:true}, props:{height:'1.4m', weight:'50kg'}, on:{move:function handle(){}} }
三劍客能夠是一個字符串或者一個數組,數組就表示這個根元素不止有一個虛擬子節點了。仍是舉個例子:
<h1> <span style="color:red">hello</span> <span>world</span> </h1>
要給createElement傳入的第三個參數(第二個參數,因爲根元素沒什麼屬性,能夠省略)應該是:
vm = new Vue({ render:createElement => createElement('h1',[ createElement('span',{style:{color:'red'}},'hello'), createElement('span','world') ]) }); vm.$mount('#logo');//$mount的意思是**附體**
能夠把代碼複製到vue官網的控制檯看效果。有句話說的好,給我一個女人,我能創造一個民族,用到這裏是,給我一個createElement函數,咱們創造出一課虛擬DOM樹。其實render函數和slot還能夠擦出不同的火花,就到下篇介紹了(心虛)。本篇完。