關於Vue和React的一些對比及我的思考(中)

Vue和React都是目前最流行、生態最好的前端框架之一。框架自己沒有優劣之分,只有適用之別,選擇符合自身業務場景、團隊基礎的技術纔是咱們最主要的目的。css

博主1年前用的是Vue框架,近半年轉技術棧到React框架,對於Vue和React都有一些基本的瞭解。接下來博主將與你們經過上、中、下三部一塊兒走近Vue和React,共同探討它們的差別。(好比你正在用vue,對react感興趣也能夠看下,反之亦然)html

總體內容概覽:前端

總體內容概覽

中部將主要從條件渲染是否顯示列表渲染計算屬性偵聽器ref表單插槽八個方面對比vue和react,歡迎多多交流,共同探討,感謝支持。vue

上部連接:關於Vue和React的一些對比及我的思考(上)react

9.條件渲染(v-if vs &&)

條件渲染用於根據條件判斷是否渲染一塊內容。數組

vue

vue中用v-if指令條件性地渲染一塊內容。只有當表達式返回真值時才被渲染,v-if還能夠和v-else-ifv-else配合使用,用於不一樣條件下的渲染,相似js裏的if else語法。緩存

(1)基本用法

<div v-if="showContent">This is content.</div>
複製代碼

data性能優化

data() {
    return {
      showContent: false
    }
  }
複製代碼

showContentfalse時,不會渲染DOM節點,會留下一個註釋標誌。bash

v-if爲false

showContenttrue時,纔會渲染DOM節點。前端框架

v-if爲true

(2)v-else 二重判斷

v-ifv-else配合使用時,v-else必需要和v-if相鄰,不然會報錯。

<div>
    <div v-if="showContent">This is content.</div>
    <div v-else>Hide content.</div>
  </div>
複製代碼

(3)v-else-if 多重判斷

當有多重判斷條件時,可使用v-else-if,相似v-ifelse-if塊v-else 元素必須緊跟在帶v-if或者v-else-if的元素的後面,不然它將不會被識別。

<div v-if="type === 'A'">
  A
</div>
<div v-else-if="type === 'B'">
  B
</div>
<div v-else-if="type === 'C'">
  C
</div>
<div v-else>
  Not A/B/C
</div>
複製代碼

(4)template 使用 v-if

另外,當想切換多個元素時,在<template>上使用v-if能夠針對元素進行分組。

<template v-if="ok">
  <h1>Title</h1>
  <p>Paragraph 1</p>
  <p>Paragraph 2</p>
</template>
複製代碼

react

react使用與運算符&&、三目運算符(?:)、if else來實現條件渲染的效果。

(1)與運算符

與運算符&&,實現效果相似v-if,左邊變量爲真值時,渲染右邊的元素。

return (
      <div>
        {showItem && <div>This is content.</div>}
      </div>
    );
複製代碼

(2)三目運算符(?:

使用三目運算符(?:),實現相似v-if v-else效果,showItem變量爲true時顯示第一個元素,變量爲false時顯示第二個元素。

return (
      <div>
        {
          showItem ?
            (<div>This is true content.</div>) : (<div>This is false content.</div>)
        }
      </div>
    );
複製代碼

(3)多重判斷

當處理多重判斷時,可使用函數加if else多重判斷或者switch case的形式來實現,實現相似v-if v-else-if v-else的效果。

Ⅰ.if-else多重判斷

render() {
    const { type } = this.state;
    const toggeleShow = (type) => {
      if (type === 'A') {
        return <div>A</div>;
      } else if (type === 'B') {
        return <div>B</div>;
      } else if (type === 'C') {
        return <div>C</div>;
      } else {
        return null;
      }
    };

    return (
      <div>
        {toggeleShow(type)}
      </div>
    );
  }
複製代碼

Ⅱ.switch case多重判斷

render () {
    const { type } = this.state;
    const toggeleShow = (type) => {
      switch (type) {
        case 'A':
          return <div>A</div>;
        case 'B':
          return <div>B</div>;
        case 'C':
          return <div>C</div>;
        default:
          return null;
      }
    };

    return (
      <div>
        {toggeleShow(type)}
      </div>
    );
  }
複製代碼

10.是否顯示(v-show vs style+class)

另外一個用於展現條件的元素的選項是v-show,react中能夠經過style或者切換class的形式實現是否顯示。

vue

v-show渲染的元素會被渲染並保留在DOM中。v-show只是簡單地切換元素的css屬性display

<div v-show="showContent">This is content.</div>
複製代碼

showContentfalse時,styledisplay屬性值爲none

v-show爲false

showContenttrue時,styledisplay屬性值爲block(元素默認display屬性值)。

v-show爲true

注意,v-show 不支持 <template> 元素,也不支持 v-else。

v-ifv-show對比總結:

  • 1)v-if是「真正」的條件渲染,由於它會確保在切換過程當中條件塊內的事件監聽器和子組件適當地被銷燬和重建。
  • 2)v-if也是惰性的:若是在初始渲染時條件爲假,則什麼也不作——直到條件第一次變爲真時,纔會開始渲染條件塊。
  • 3)相比之下,v-show就簡單得多——無論初始條件是什麼,元素老是會被渲染,而且只是簡單地基於 CSS 進行切換。
  • 4)通常來講,v-if有更高的切換開銷,而v-show有更高的初始渲染開銷。所以,若是須要很是頻繁地切換,則使用v-show較好;若是在運行時條件不多改變,則使用v-if較好。

react

react中經過修改style或者class來實現v-show相似效果。

經過修改style屬性的display屬性來實現切換是否顯示。

<div style={{ display: showItem ? 'block' : 'none' }}>
    This is content.
</div>
複製代碼

經過變量判斷修改class來實現切換效果,本質上也是切換元素的display屬性來實現切換效果。

在react中動態修改元素樣式時(好比切換tab、按鈕選中狀態),適用於使用class來實現。

const itemClass = showItem ? 'show-item-class' : 'hide-item-class';
    return (
      <div className={itemClass}>
        This is content.
      </div >
    );
複製代碼

class樣式:

.show-item-class {
  display: block;
}
.hide-item-class {
  display: none;
}
複製代碼

11.列表渲染(v-for vs map)

vue中使用v-for來渲染列表,react中使用map來渲染列表。不論是v-for仍是map來渲染列表都須要添加key值(key在兄弟節點之間必須惟一),方便快速比較出新舊虛擬DOM樹間的差別。

vue

vue中可使用v-for來渲染數組、對象、<template>、組件。

(1)渲染數組

渲染數組時,使用(item, index) in items形式的特殊語法,其中items是源數據數組,item則是被迭代的數組元素的別名,index表示當前元素的索引。

<div>
    <div v-for="(item, index) in items" :key="item.message + index">
      {{item.message}}
    </div>
  </div>
複製代碼

data

data() {
    return {
      items: [
        {
          message: 'Hello'
        },
        {
          message: 'Welcome'
        }
      ]
    }
  }
複製代碼

(2)渲染對象

v-for也能夠用來遍歷一個對象的屬性,使用(value, key, index) in obj的形式,其中key表示對象的key值,value表示對象的value值,index表示當前索引。

在遍歷對象時,採用Object.keys()的結果遍歷,可是不能保證它的結果在不一樣的 JavaScript 引擎下都一致。

<div v-for="(value, key, index) in obj" :key="key + index">
  {{index}}.{{key}}: {{value}}
</div>
複製代碼

data

data() {
    return {
      obj: {
        name: 'xiaoming',
        age: 18,
        sex: 'male',
        height: 175
      }
    }
  }
複製代碼

(3)渲染多個元素

<template>上使用v-for來渲染一段包含多個元素的內容。

<template>上使用v-for來渲染元素段時,不容許綁定key值。由於template上並不會生成實際Dom節點。能夠給底下的子元素綁定key值。

<div>
    <template v-for="(item, index) in items">
      <div :key="item.message">{{ item.message }}</div>
      <div :key="item.message + index" class="divider"></div>
    </template>
  </div>
複製代碼

data

data() {
    return {
     items: [
       {
         message: 'hello'
       },
       {
         message: 'world'
       },
       {
         message: 'welcome'
       }
     ]
    }
  },
複製代碼

生成DOM時,並不會生成實際DOM節點。

template使用v-for

(4)渲染自定義組件列表

在自定義組件上,可使用v-for渲染自定義組件列表,經過props將數據傳遞給組件。

在組件上使用v-for時,key是必須的。

v-for渲染自定義組件列表,將item經過props傳遞給組件。

<my-component 
    v-for="(item, index) in items" 
    :key="item.message + index" 
    :item="item">
  </my-component>
複製代碼

my-component組件使用props接收父組件傳來的數據。

<template>
  <div>{{item.message}}</div>
</template>

<script>
export default {
  props: ['item'],
  data() {
    return { }
  }
}
</script>
複製代碼

react

react中使用map()方法來實現列表渲染。

(1)渲染數組

遍歷數組中的每一個元素,獲得一組jsx元素列表。數組中的每個元素須要添加惟一的key值。

render() {
    const items = [
      {
        message: 'hello'
      },
      {
        message: 'world'
      },
      {
        message: 'welcome'
      }
    ];
    const listItems = items.map((item, index) => <div key={item.message + index}>{item.message}</div>);
    return (
      <div>
        {listItems}
      </div>
    );
  }
複製代碼

(2)渲染對象

對於對象,能夠採用方法經過Object.keys()或者Object.entries()來遍歷對象。

render() {
    const obj = {
      name: 'xiaoming',
      age: 18,
      sex: 'male',
      height: 175
    };
    const renderObj = (obj) => {
      const keys = Object.keys(obj);
      return keys.map((item, index) => <div key={index}>{obj[item]}</div>);
    };
    return (
      <div>
        {renderObj(obj)}
      </div>
    );
  }
複製代碼

(3)渲染自定義組件列表

渲染自定義組件列表與vue中相似,須要給組件添加key值標識。

render() {
    const items = [
      {
        message: 'hello'
      },
      {
        message: 'world'
      },
      {
        message: 'welcome'
      }
    ];
    const listItems = items.map((item, index) => 
          <ListItem message={item.message} key={item.message + index} />);
    return (
      <div>
        {listItems}
      </div>
    );
  }
複製代碼

12.計算屬性(computed vs useMemo+useCallback)

計算屬性表示根據組件的數據(包含組件自身的數據和接收父組件的props)須要二次計算並「保存」的數據,使用計算屬性的好處是避免每次重複計算的開銷(好比遍歷一個巨大的數組並作大量的計算)。

vue

vue中用computed來表示計算屬性,能夠定義多個計算屬性,計算屬性能夠互相調用。計算屬性是基於它們的響應式依賴進行緩存的。只在相關響應式依賴發生改變時它們纔會從新求值。vue中能夠直接使用this.xxx直接獲取到計算屬性。

(1)基本用法

下面聲明計算屬性reversedMessage依賴於message,這就意味着只要 message尚未發生改變,屢次訪問reversedMessage計算屬性會當即返回以前的計算結果。

<div>
    message: <input type="text" v-model="message" />
    <div>{{reversedMessage}}</div>
</div>
複製代碼

script

data() {
    return {
      message:''
    }
  },
  computed: {
    reversedMessage() {
      return this.message.split('').reverse().join('')
    }
  }
複製代碼

(2)計算屬性的setter

技術屬性默認只有getter,也可使用setter,當修改計算屬性的時候,會觸發setter回調。

script

data() {
    return {
      message:'',
      info: ''
    }
  },
  computed: {
    reversedMessage: {
      get() { // get回調
        return this.message.split('').reverse().join('')
      },
      set(newValue) { // set回調
        this.info = newValue
      }
    }
  },
  methods: {
    changeMessage(event) {
      // 修改reversedMessage計算屬性
      this.reversedMessage = event.target.value;
    }
  }
複製代碼

react

react hooks使用useMemo表示memoized的值,使用useCallback表示memoized的回調函數,實現與vue中computed相似的功能。

適用場景:子組件使用了PureComponent或者React.memo,那麼你能夠考慮使用useMemouseCallback封裝提供給他們的props,這樣就可以充分利用這些組件的淺比較能力。

(1)useMemo

useMemo返回一個memoized的值。useMemo會依賴某些依賴值,只有在某個依賴項改變時纔會從新計算memoized值。若是沒有提供依賴項數組,useMemo 在每次渲染時都會計算新的值。useMemo能夠做爲性能優化的手段。

傳入useMemo的函數會在渲染期間執行。請不要在這個函數內部執行與渲染無關的操做,諸如反作用這類的操做屬於useEffect的適用範疇,而不是useMemo

function NewComponent(props) {
  const { num } = props;
  const [size, setSize] = useState(0);
  // max是useMemo返回的一個memoized的值
  const max = useMemo(() => Math.max(num, size), [num, size]);
  return (<div>
    <input
      type="number"
      value={size}
      onChange={(e) => setSize(e.target.value)} />
    <div>Max {max}</div>
  </div>);
}
複製代碼

(2)useCallback

useCallback把內聯回調函數及依賴項數組做爲參數傳入useCallback,它將返回該回調函數的memoized版本,該回調函數僅在某個依賴項改變時纔會更新。當你把回調函數傳遞給通過優化的並使用引用相等性去避免非必要渲染(例如shouldComponentUpdate的子組件時,它將很是有用。

function NewComponent(props) {
  const [message, setMessage] = useState('hello world.');
  const handleChange = useCallback((value) => {
    setMessage(value);
  }, []);
  return (<div>
    <input
      type="number"
      value={message}
      onChange={(e) => handleChange(e.target.value)} />
    <div>{message}</div>
  </div>);
}
複製代碼

13.偵聽器(watch vs getDerivedStateFromProps + componentDidUpdate)

偵聽器是指經過監聽props或者組件數據(datastate)的變化來執行一些異步或者數據操做。

vue

vue中主要經過watch監聽propsdatacomputed(計算屬性)的變化,執行異步或開銷較大的操做。

下面ProjectManage組件經過watch監聽projectId prop的變化獲取對應的項目信息。

export default {
  name: "ProjectManage",
  props: ["projectId"],
  data() {
    return {
      projectInfo: null
    };
  },
  watch: {
    projectId(newVaue, oldValue) {
      if (newVaue !== oldValue) {
        this.getProject(newValue);
      }
    }
  },
  methods: {
    getProject(projectId) {
      projectApi
        .getProject(projectId)
        .then(res => {
          this.projectInfo = res.data;
        })
        .catch(err => {
          this.$message({
            type: "error",
            message: err.message
          });
        });
    }
  }
};
複製代碼

react

react中經過static getDerivedStateFromProps()componentDidUpdate()實現監聽器的功能。

(1)static getDerivedStateFromProps()

getDerivedStateFromProps會在調用render方法以前調用,而且在初始掛載及後續更新時都會被調用。它應返回一個對象來更新state,若是返回null則不更新任何內容。

關於getDerivedStateFromProps有2點說明:

  • 1)不論是props變化、執行setState或者forceUpdate操做都會在每次渲染前觸發此方法。
  • 2)當state的值在任什麼時候候都取決於props的時候適用該方法。
class NewComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      info: ''
    }
  }
  static getDerivedStateFromProps(nextProps, prevState) {
    // state中的info根據props中的info保持同步
    if (nextProps.info !== prevState.info) {
      return {
        info: nextProps.info
      }
    }
    return null;
  }
  render() {
    const { info } = this.state;
    return <div>{info}</div>
  }
}
複製代碼

(2)componentDidUpdate()

componentDidUpdate()方法在組件更新後被調用。首次渲染不會執行此方法。當組件更新後,能夠在此處操做DOM、執行setState或者執行異步請求操做。

componentDidUpdate(prevProps, prevState, snapshot)
複製代碼

關於componentDidUpdate有4點說明:

  • 1)componentDidUpdate()的第三個參數snapshot參數來源於getSnapshotBeforeUpdate()生命週期的返回值。若沒有實現getSnapshotBeforeUpdate(),此參數值爲undefined
  • 2)能夠在componentDidUpdate()中直接調用setState(),可是它必需被包裹在一個條件語句裏,不然會致使死循環。
  • 3)能夠在componentDidUpdate()對更新先後的props進行比較,執行異步操做。
  • 4)若是shouldComponentUpdate()返回值爲false,則不會調用componentDidUpdate()

下面NewComponent組件在componentDidUpdate()裏判斷

class NewComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      projectInfo: null
    }
  }
  getProject = (projectId) => {
    projectApi
      .getProject(projectId)
      .then(res => {
        this.projectInfo = res.data;
      })
      .catch(err => {
        message.error(err.message);
      });
  }
  componentDidUpdate(prevProps) {
    if (this.props.projectId !== prevProps.projectId) {
      this.getProject(this.props.projectId);
    }
  }
  render() {
    const { projectInfo } = this.state;
    return <React.Fragment>
      <div>{projectInfo.name}</div>
      <div>{projectInfo.detail}</div>
      </React.Fragment>
  }
}
複製代碼

14.ref

ref用來給元素或子組件註冊引用信息,容許咱們訪問子組件或者子節點。

ref經常使用於:

  • 管理焦點,文本選擇或媒體播放。
  • 觸發強制動畫。

vue

經過給組件或者子元素設置ref這個attribute爲子組件或者子元素賦予一個ID引用。

$refs只會在組件渲染完成以後生效,而且它們不是響應式的。這僅做爲一個用於直接操做子組件的「逃生艙」——你應該避免在模板或計算屬性中訪問$refs

(1)子元素引用ref

子元素上引用ref

<div>
    <input type="text" v-model="message" ref="inputMessage"  />
  </div>
複製代碼

加載完畢後使輸入框自動獲取焦點

mounted() {
  this.$refs.inputMessage.focus();
}
複製代碼

(2)子組件引用ref

子組件引用ref經常使用於父組件使用子組件的方法。

經常使用表單驗證就是採用這種方式驗證的。

<template>
  <div>
    <el-form ref="createForm" label-width="80px" :model="form" :rules="rules">
      <el-form-item label="名稱" prop="name">
        <el-input v-model="form.name"></el-input>
      </el-form-item>
      <el-form-item label="郵箱" prop="email">
        <el-input v-model="form.email"></el-input>
      </el-form-item>
    </el-form>
    <el-button @click="handleSubmit">提交</el-button>
  </div>
</template>

<script>
export default {
  name: 'CreateForm',
  data() {
    return {
      form: {
        name: '',
        email: ''
      },
      rules: {
        name: [{required: true, message: '名稱不能爲空', trigger: 'blur'}],
        email: [
          { required: true, message: '請輸入郵箱地址', trigger: 'blur' },
          { type: 'email', message: '請輸入正確的郵箱地址', trigger: ['blur', 'change']}
        ] 
      }
    }
  },
  methods: {
    handleSubmit() {
      this.$refs.createForm.validate((valid) => {
        console.log(valid);
      })
    }
  }
}
</script>
複製代碼

react

react中不像vue中直接給ref傳字符串類型值,class組件經過React.createRef綁定ref屬性(React v16.3版本以後),函數組件經過useRef綁定ref屬性,還可使用React.forwardRef用於轉發ref屬性到子組件中。

(1)class組件綁定ref

經過React.createRef在構造函數中生成ref,在綁定到input元素上,加載完成後自動聚焦。

class NewComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      message: 'hello world'
    };
    this.inputRef = React.createRef();
  }
  componentDidMount() {
    this.inputRef.current.focus();
  }
  render() {
    const { message } = this.state;
    return (<div>
      <input
        type="number"
        ref={this.inputRef}
      />
      <div>{message}</div>
    </div>);
  }
}
複製代碼

(2)函數組件綁定ref

函數組件可使用useRef綁定ref屬性。useRef返回一個可變的ref對象,其 .current屬性被初始化爲傳入的參數(initialValue)。返回的ref 對象在組件的整個生命週期內保持不變。

function NewComponent() {
  const [message, setMessage] = useState('hello world.');
  const inputRef = useRef(null);
  useEffect(() => {
    inputRef.current.focus();
  }, []);
  return (<div>
    <input type="number" ref={inputRef} />
    <div>{message}</div>
  </div>);
}
複製代碼

(3)React.forwardRef 轉發 ref 到子組件

React.forwardRef會建立一個React組件,這個組件可以將其接受的ref屬性轉發到其組件樹下的另外一個組件中。

這種技術並不常見,但在如下兩種場景中特別有用:

  • 轉發refsDOM組件
  • 在高階組件中轉發refs

父組件直接傳遞ref屬性給子組件NewComponent

function Parent() {
  const inputRef = useRef(null);
  useEffect(() => {
    inputRef.current.focus();
  }, []);
  return <div>
     <NewComponent ref={inputRef}><h2>This is refs.</h2></NewComponent>
  </div>;
}
複製代碼

子組件使用React.forwardRef 接受渲染函數做爲參數,父組件加載完成後聚焦到input輸入框。

const NewComponent = React.forwardRef((props, ref) => (<div>
  <input type="number" ref={ref} />
  <div>{props.children}</div>
</div>));
複製代碼

15.表單(v-model vs value)

對於表單,vue中使用v-model在表單組件上實現雙向數據綁定,react中經過在表單組件上綁定value屬性以受控組件的形式管理表單數據。

vue

v-model指令在表單<input><textarea><select>元素上建立雙向數據綁定。它會根據控件類型自動選取正確的方法來更新元素。v-model本質上不過是語法糖。

v-model在內部爲不一樣的輸入元素使用不一樣的屬性並拋出不一樣的事件:

  • texttextarea元素使用value屬性和input事件;
  • checkboxradio使用checked屬性和change事件;
  • select字段將value做爲prop並將change做爲事件。

(1)基本用法

Ⅰ.文本

input輸入框上綁定v-model屬性綁定msg,當修改input輸入值時,msg會自動同步爲用戶輸入值。

<div>
  <input v-model="msg" />
</div>
複製代碼

v-model寫法等價於:value@input的結合,:value綁定輸入值,@input表示接收輸入事件修改msg的值爲輸入的值,從而實現雙向綁定。

<div>
  <input :value="msg" @input="e => (msg = e.target.value)" />
</div>
複製代碼
Ⅱ.複選框

單個複選框綁定到布爾值

<div>
  <input type="checkbox" v-model="checked" />
  <label for="checkbox">{{ checked }}</label>
</div>
複製代碼

多個複選框綁定到數組

<div>
    <input type="checkbox" id="apple" value="apple" v-model="selectedFruits" />
    <label for="jack">apple</label>
    <input
      type="checkbox"
      id="banana"
      value="banana"
      v-model="selectedFruits"
    />
    <label for="john">banana</label>
    <input type="checkbox" id="mango" value="mango" v-model="selectedFruits" />
    <label for="mango">mango</label>
    <div>Selected fruits: {{ selectedFruits }}</div>
  </div>
複製代碼

data

data() {
    return {
      selectedFruits: []
    };
  }
複製代碼
Ⅲ.選擇框

1)選擇框單選時,綁定字符串

<div>
  <select v-model="selected">
    <option
      v-for="option in options"
      :value="option.value"
      :key="option.value"
    >
      {{ option.text }}
    </option>
  </select>
  <div>Selected: {{ selected }}</div>
</div>
複製代碼

data

data() {
  return {
    selected: "A",
    options: [
      { text: "One", value: "A" },
      { text: "Two", value: "B" },
      { text: "Three", value: "C" }
    ]
  };
}
複製代碼

2)選擇框多選時,綁定的是數組

<div>
  <select v-model="selected" multiple>
    <option
      v-for="option in options"
      :value="option.value"
      :key="option.value"
    >
      {{ option.text }}
    </option>
  </select>
  <div>Selected: {{ selected }}</div>
</div>
複製代碼

data

data() {
  return {
    selected: ["A"], //多選時綁定的是數組
    options: [
      { text: "One", value: "A" },
      { text: "Two", value: "B" },
      { text: "Three", value: "C" }
    ]
  };
}
複製代碼

####(2)修飾符 vue對於v-model擴展了.lazy.number.trim修飾符加強功能。

Ⅰ.lazy

在默認狀況下,v-model在每次input事件觸發後將輸入框的值與數據進行同步。添加了.lazy修飾符,轉變爲change事件進行同步。

<!-- 在「change」時而非「input」時更新 -->
<input v-model.lazy="msg" >
複製代碼
Ⅱ.number

.number修飾符能夠自動將用戶的輸入轉換爲數值類型,尤爲是處理數字類型表單項時尤爲有用。

<input type="number" v-model.number="num" />
複製代碼
Ⅲ.trim

.trim修飾符能夠自動過濾用戶輸入的首尾空白字符。

<input v-model.trim="msg" />
複製代碼

(3)自定義組件使用v-model

一個組件上的v-model默認會利用名爲valueprop和名爲 input的事件。

InputMessage綁定v-model值爲msg

<InputMessage v-model="msg" />
複製代碼

InputMessage組件經過value props接收值,emit input事件給父組件,修改父組件中msg的值。

<template>
  <input v-model="message" @input="$emit('input', $event.target.value)" />
</template>

<script>
export default {
  name: "InputMessage",
  props: ["value"],
  data() {
    return {
      message: this.value
    };
  }
};
複製代碼

(4)不能使用v-model管理的表單組件(file等)

對於像<input type="file" />這種類型的表單組件,不能使用v-model管理組件數據,能夠經過refs管理表單組件的數據,這點和react中的非受控組件一致。

<template>
  <div>
    <input type="file" ref="file" @change="fileChange" />
  </div>
</template>

<script>
export default {
  name: "InputFile",
  data() {
    return {};
  },
  methods: {
    fileChange() {
      const file = this.$refs.file.files[0];
      console.log(file);
    }
  }
};
</script>
複製代碼

react

react中,表單元素(<input> <select> <checkbox>)一般維護本身的state,將state賦值給value屬性,並根據用戶輸入經過setState()更新state,以這種方式控制的表單元素稱爲「受控組件」。

(1)受控組件

受控組件中,state做爲組件「惟一的數據源」,組件還控制着用戶操做過程當中表單發生的操做。

class CreateForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      name: ''
    }
  }

  nameChange = (event) => { // 接收事件做爲參數
   this.setState({
     name: event.target.value
   });
  }

  render() {
    const { name } = this.state; 
    return (
    <div>
      <input value={name} onChange={this.nameChange} />
      <div> name: {name} </div>
    </div>)
  }
}
複製代碼

(2)非受控組件

在react中,對於不能使用state方式管理的表單組件稱爲非受控組件,非受控組件的值不能經過代碼控制,表單數據交由DOM節點來處理。對於非受控組件,可使用refDOM節點中獲取表單數據。

<input type="file" />始終是一個非受控組件,經過建立ref的形式獲取文件數據。

class CreateForm extends React.Component {
  constructor(props) {
    super(props);
    this.fileRef = React.createRef(null);
  }

  fileChange = (event) => {
    event.preventDefault();
    const file = this.fileRef.current.files[0];
    console.log(file)
  }

  render() {
    return (
    <div>
      <input type="file" ref={this.fileRef} onChange={this.fileChange} />
    </div>)
  }
}
複製代碼

16.插槽(slot vs Render Props+this.props.children)

vue和react中都實現了「插槽」(內容分發)功能,vue中主要經過slot實現插槽功能,react中經過this.props.childrenRender props實現相似vue中的插槽功能。

vue

vue中經過<slot>實現插槽功能,包含默認插槽、具名插槽、做用域插槽。

(1)默認插槽

默認插槽使用<slot></slot>在組件內預留分發內容的「佔位」,在組件起始標籤和結束標籤能夠包含任何代碼,例如html或者其餘組件。

關於默認插槽,有2點說明:

(1)若是不使用插槽,插入到組件中的內容不會渲染。
(2)插槽能夠設置後備內容,在不插入任何內容時顯示。

使用默認插槽的組件:

<template>
  <div>
    <h2>Slot:</h2>
     <slot>Default content.</slot> // 使用slot預留插槽佔位,slot中的內容做爲後備內容
  </div>
</template>

<script>
export default {
  name: "SlotComponent",
  data() {
    return {};
  }
};
</script>
複製代碼

父組件使用該組件,在組件起始標籤和結束標籤添加插槽內容。

<slot-component>
  <h2>This is slot component.</h2>
</slot-component>
複製代碼

最終插槽內容會被插入到組件<slot></slot>佔位的位置。

<div>
    <h2>Slot:</h2>
    <h2>This is slot component.</h2>
  </div>
複製代碼

<slot-component>沒有添加插槽內容時,會渲染默認插槽內容。

<div>
    <h2>Slot:</h2>
    Default content.
  </div>
複製代碼

(2)具名插槽

默認插槽只能插入一個插槽,當插入多個插槽時須要使用具名插槽。slot元素有一個默認的attribute name,用來定義具名插槽。

默認插槽的namedefault

在向具名插槽提供內容的時候,咱們能夠在一個 <template>元素上使用v-slot指令,並以v-slot的參數的形式提供其名稱,將內容插入到對應的插槽下。

v-slot:也能夠簡寫爲#,例如v-slot:footer能夠被重寫爲#footer

插槽組件slot-componentheader footer兩個具名插槽和一個默認插槽。

<template>
  <div>
    <header>
      <slot name="header">
        Header content.
      </slot>
    </header>
    <main>
      <slot>
        Main content.
      </slot>
    </main>
    <footer>
      <slot name="footer">
        Footer content.
      </slot>
    </footer>
  </div>
</template>
複製代碼

向插槽中分別插入內容:

<slot-component>
  <template v-slot:header>
    <div>This is header content.</div>
  </template>
  <template>
    <div>This is main content.</div>
  </template>
  <template #footer>
    <div>This is footer content.</div>
  </template>
</slot-component>
複製代碼

最終html會被渲染爲:

<div>
  <header>
    <div>This is header content.</div>
  </header>
  <main>
    <div>This is main content.</div>
  </main>
  <footer>
    <div>This is footer content.</div>
  </footer>
</div>
複製代碼

(3)做用域插槽

有時候咱們須要在父組件中顯示插槽組件的數據內容,這時候做用域插槽就派上用場了。

做用域插槽須要在<slot>元素上綁定attribute,這被稱爲插槽prop。在父級做用域中,可使用帶值的v-slot來定義咱們提供的插槽prop的名字。

插槽組件

<template>
  <div>
    <header>
      <slot name="header">
        Header content.
      </slot>
    </header>
    <main>
      <slot :person="person">
        {{ person.name }}
      </slot>
    </main>
  </div>
</template>

<script>
export default {
  name: "SlotComponent",
  data() {
    return {
      person: {
        name: "xiaoming",
        age: 14
      }
    };
  },
  methods: {}
};
</script>
複製代碼

父組件做用域將包含全部插槽prop的對象命名爲slotProps,也可使用任意你喜歡的名字。

<slot-component>
  <template slot="header">
    <div>This is header content.</div>
  </template>
  // 使用帶值的`v-slot`來定義插槽`prop`的名字
  <template v-slot:default="slotProps"> 
    <div>{{ slotProps.person.name }}</div>
    <div>{{ slotProps.person.age }}</div>
  </template>
</slot-component>
複製代碼

最終html將被渲染爲:

<div>
  <header>
    <div>This is header content.</div>
  </header>
  <main>
    <div>xiaoming</div>
    <div>14</div>
  </main>
</div>
複製代碼

react

react中經過this.props.childrenRender props實現相似vue中的插槽功能。

(1)this.props.children

每一個組件均可以經過this.props.children獲取包含組件開始標籤和結束標籤之間的內容,這個與vue中的默認插槽相似。

class組件中使用this.props.children,在function組件中使用props.childrenclass組件使用this.props.children獲取子元素內容。

class NewComponent extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return <div>{this.props.children}</div>
  }
}
複製代碼

function組件使用props.children獲取子元素內容。

function NewComponent(props) {
  return <div>>{props.children}</div>
}
複製代碼

父組件使用NewComponent組件

<NewComponent>
   <h2>This is new component header.</h2>
   <div>
     This is new component content.
   </div>
 </NewComponent>
複製代碼

最終html將被渲染爲:

<div>
  <h2>This is new component header.</h2>
  <div>This is new component content.</div>
</div>
複製代碼

(2)Render props

render prop是指一種在React組件之間使用一個值爲函數的prop共享代碼的技術。render prop是一個用於告知組件須要渲染什麼內容的函數prop

好比咱們經常使用的react-router-dom中的Routecomponent prop就採用了典型的render prop的用法。

<Router history={browserHistory}>
    <Route path="/" component={Index}> // component props接收具體的組件
      <IndexRoute component={HomePage} />
      <Route path="/users" component={Users} />
    </Route>
  </Router>
複製代碼

經過多個render prop便可實現相似vue中具名插槽的功能。

NewComponent定義了headermainfooter prop

class NewComponent extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    const { header, main, footer, children } = this.props;
    return (<div>
              <header>
                {header || (<div>Header content.</div>)}
              </header>
              <main>
                {main || (<div>Main content.</div>)}
              </main>
              {children}
              <footer>
                {footer || (<div>Footer content.</div>)}
              </footer>
            </div>);
  }
}
複製代碼

父組件向子組件傳遞render prop

<NewComponent 
    header={<div>This is header content.</div>}
    content={<div>This is main content.</div>}
    footer={<div>This is footer content.</div>}>
    <div>
      This is new component children.
    </div>
</NewComponent>
複製代碼

最終html將被渲染爲

<div>
  <header>
    <div>This is header content.</div>
  </header>
  <main>
    <div>This is main content.</div>
  </main>
  <div>This is new component children.</div>
  <footer>
    <div>This is footer content.</div>
  </footer>
</div>
複製代碼

結語

以上就是博主關於react和vue的一些對比以及我的思考的中部,以爲有收穫的能夠關注一波,點贊一波,碼字不易,萬分感謝,後續下部會盡快持續更新,感謝關注。

相關文章
相關標籤/搜索