vue與react元件的思考

李夢克發表於2018-04-15

前言

我最一開始是先學的react,然後也就前段時間開始學習vue,一開始給我的感受是兩者很相似,react給我的感覺是靈活vue是一種死板的感覺。為什麼有這種感覺呢,react有一種很強烈的慾望,all in js只要能夠用夠js寫的,就全用js寫,所以一切都很靈活,jsx很酷,高階元件牛逼,es7的裝飾器也是可以玩的飛起。反觀vue,搞的.vue單檔案還是儘量保留原來前端開發的模式,留下了templatescriptstyle三個最基本的東西,其實內部運作應該還是和react類似。

react

react看的各種道聽途說的文章,這玩意大概是怎麼玩的呢?簡單的說jsx被解析成createElement的方法,裡面傳到引數啊什麼的就是告訴react怎麼渲染之類,然後方法返回一個虛擬dom,大概是這麼搞的,具體細節我也不是太懂。

vue

vue一開始讓我挺暈的,官網的學習教程和一般寫的專案不太一樣,官網教程的例子沒有單檔案,全是在選項物件中加入一個template屬性,一般的專案開發的都是用的.vue的單檔案。一開始我是沒太理解.vue這檔案到底是怎麼玩,只是按著這樣寫,不理解原理好像也能夠基本完成自己想要的效果。

最近看了一些文章以及看了好幾遍官網的api官網的api還是很有必要每一個都看一遍的,之前大致看一遍感覺好多東西都沒有見到過,別人程式碼看多了,發現很多沒用的api還是很強的。下面講講我對vue的一些理解。

vue是怎麼玩的呢?每一個vue元件都是一個Vue例項,這樣說也不是很準確,應該每一個自己寫的元件都是通過呼叫Vue.extend繼承Vue後擴充套件出來的新的類的例項,想一想怎麼定義一個vue元件,就是呼叫了Vue.component方法

// 註冊元件,傳入一個擴充套件過的構造器
Vue.component(`my-component`, Vue.extend({ /* ... */ }))

// 註冊元件,傳入一個選項物件 (自動呼叫 Vue.extend)
Vue.component(`my-component`, { /* ... */ })

// 獲取註冊的元件 (始終返回構造器)
var MyComponent = Vue.component(`my-component`)

一般情況下我們寫的.vue單檔案暴露出來的就是一個選項物件,這個方法的內部自動呼叫了Vue.extend。所以.vue單檔案本質上是vue-loader去把<template></template>裡的內容轉成字串形式,塞到<scirpt></script>裡面的export default {}的物件裡面的template去,當然了,這還沒有完,template屬性裡面的字串也可以說是一種糖,vue內部是要呼叫Vue.compile去做一次轉換,最終把template的值轉換成render,這個屬性的值是一個方法,這裡就和react統一了,用是有個同名的函式createElement去生成Vnode。所以在選項物件中,可以不給出template屬性而是給出render屬性,同時存在templaterender的時候會忽視template

看看官網render用法:

var getChildrenTextContent = function (children) {
  return children.map(function (node) {
    return node.children
      ? getChildrenTextContent(node.children)
      : node.text
  }).join(``)
}

Vue.component(`anchored-heading`, {
  render: function (createElement) {
    // create kebabCase id
    var headingId = getChildrenTextContent(this.$slots.default)
      .toLowerCase()
      .replace(/W+/g, `-`)
      .replace(/(^-|-$)/g, ``)

    return createElement(
      `h` + this.level,
      [
        createElement(`a`, {
          attrs: {
            name: headingId,
            href: `#` + headingId
          }
        }, this.$slots.default)
      ]
    )
  },
  props: {
    level: {
      type: Number,
      required: true
    }
  }
})

看的出來這種直接寫render比起template要麻煩的多,所以為了簡化這種寫法就搞出來了一個jsx寫法,這裡又和react又統一了。

jsx用法:

import AnchoredHeading from `./AnchoredHeading.vue`

new Vue({
  el: `#demo`,
  render: function (h) {
    return (
      <AnchoredHeading level={1}>
        <span>Hello</span> world!
      </AnchoredHeading>
    )
  }
})

一個需求引發的思考

通常我們寫reactvue都是一個寫一個元件customComp,然後在別的元件要用的時候都是直接<customComp/>寫進去的,但是有的元件就不能這麼做了,比如一個提醒的message元件,我希望在react中是這樣呼叫,像是一個工具類函式一般使用:

import message from `message.js`
import React from `react`

class XXXcomponent extends React.Component{
    ...
    componentDidMount(){
        message.info({
            type:`success`,
            text:`hello react!`
        })
    }
}

vue中我希望是這樣的呼叫的:

{
    mounted(){
        this.$message({
            type:`success`,
            text:"hello vue!"
        })
    }
}

在看了一些三方庫的這種元件的實現方式,來回折騰了一段時間,對reactvue理解的更深了一些。

vue的實現

先拿vue說,先寫一個message.vue的元件檔案,然後在index.js檔案中把這個message.vue檔案import進來,你可以試著列印下這個vue檔案長得什麼樣,其實前面也分析過了,其實引進來的就是一個選項物件,而且<template></template>標籤的內容也被弄成了render屬性了,前面也鋪墊過了vue.extend,這裡把引入的選項物件傳入vue.extend中就返回了一個擴充套件過的Vue類了,然後我們手動去new一個message的元件例項出來,可以傳入一個propsData就能把資料傳給到元件內部的prop中去,列印下例項就會發現定義在.vue檔案中的dataprop之類的都有,這個時候我們只是例項化了一個例項,其實在document上是找不到這個節點的,只需要最後在例項上呼叫$mount(el),就能把例項掛載到document上去了。直接修改例項上data裡面定義的值,會發現也是響應的,至此這個message元件我們能夠手動去控制這個元件了,具體細節該怎麼暴露出api來個外部呼叫就不說了。

//虛擬碼
import message from `message.vue`
import Vue from `vue`

const Constructor = Vue.extend(message) //擴充套件出一個新的類

const options = {
    innerProps:"inner props"
}

const instance = new Constructor({propsData:options})  //建立一個例項,也可以傳給一個props

instance.$mount(document.body) //元件掛在body下

//如果元件內部methods有方法,例項就能直接呼叫

// instance.innerFunction()

react實現

大體上是和vue類似,有幾個地方有變化,引進來的元件,因為是用class extends React.Component的形式寫的,看的出來已經是擴充套件過react後元件類了,直接去new就能拿到例項了,或者是用React.createElement傳入元件拿到虛擬dom。和vue不同的事,vue有個$mount方法幫助掛載元件到指定的位置,在react中要用到react-dom裡面的render方法,傳入前面拿到的虛擬dom以及要掛載的位置,這個方法會返回這個元件例項,這個時候就可以呼叫元件例項的setState方法去做一些事情了。

總結

reactvue隨便用用帶還是挺簡單的,我的觀點是api文件只能教會你70%的東西,但是你有了這70%的功力,大部分的東西基本都沒什麼問題,但是文件之外的30%,往往是最難學的,可能需要剝開vuereact簡單易用的外表,來回看官方文件,每次都能有新的體會,翻看第三方優秀的元件庫是最佳學習的方案,深入地去理解內部原理才能真正的掌握。

這種元件函式式用法在我的fantastic-carnival(一個部落格系統)中有體現:
其中有一個loadingBar元件是一個載入條的元件,參考的是iview元件庫中的vue實現,同時我用react也實現了一遍。

參考資料

  1. vue元件思考
  2. vue官網渲染函式

相關文章