天天看點

對react的研究20200723

JSX

線上嘗試

1. 什麼是JSX

文法糖

React 使⽤ JSX 來替代正常的 JavaScript。

JSX 是⼀個看起來很像 XML 的 JavaScript 文法擴充。

2. 為什麼需要JSX

開發效率:使⽤ JSX 編寫模闆簡單快速。

執⾏效率:JSX編譯為 JavaScript 代碼後進⾏了優化,執⾏更快。

類型安全:在編譯過程中就能發現錯誤。

3. 原理:babel-loader會預編譯JSX為React.createElement(...)

4. 與vue的異同:

react中虛拟dom+jsx的設計⼀開始就有,vue則是演進過程中才出

現的

jsx本來就是js擴充,轉義過程簡單直接的多;vue把template編譯

為render函數的過程需要複雜的編譯器轉換字元串-ast-js函數字元

JSX預處理前:

import React, { Component } from "react";
import ReactDOM from "react-dom";
import "./index.css";
function FuncCmp(props) {
return <div>name: {props.name}</div>;
}
class ClassCmp extends Component {
render() {
return <div>name: {this.props.name}</div>;
}
}
const jsx = (
<div>
<p>我是内容</p>
<FuncCmp name="我是function元件" />
<ClassCmp name="我是class元件" />
</div>
);
console.log("jsx", jsx);
ReactDOM.render(jsx, document.getElementById("root"));
build後
function FuncCmp(props) {
return React.createElement(
"div",
null,
"name: ",
props.name
);
}
class ClassCmp extends React.Component {
render() {開課吧web全棧工程師
return React.createElement(
"div",
null,
"name: ",
this.props.name
);
}
}
 
let jsx = React.createElement(
"div",
null,
" ",
React.createElement(
"div",
{ className: "border" },
"我是内容"
),
" ",
React.createElement(FuncCmp, { name: "我是function元件"
}),
" ",
React.createElement(ClassCmp, { name: "我是class元件" }),
" "
);
ReactDOM.render(jsx, document.getElementById('root'));
 
實作三⼤接⼝:React.createElement,
React.Component, ReactDom.render
 
CreateElement
将傳⼊的節點定義轉換為vdom。
src/index.js
 
// import React, { Component } from "react";
// import ReactDOM from "react-dom";
import React from "./kreact/";
import ReactDOM from "./kreact/ReactDOM";
import "./index.css";
function FuncCmp(props) {
return <div>name: {props.name}</div>;
}
class ClassCmp extends React.Component {
constructor(props) {
super(props);
this.state = { counter: 0 };
}
clickHandle = () => {
console.log("clickHandle");
};
render() {
const { counter } = this.state;
return (
<div>
name: {this.props.name}
<p>counter: {counter}</p>
<button onCclick={this.clickHandle}>點選</button>
{[0, 1, 2].map(item => {
return <FuncCmp name={"我是function元件" + item}
key={item} />;
})}
</div>
);
}
}
let jsx = (開課吧web全棧工程師
<div>
<div className="border">我是内容</div>
<FuncCmp name="我是function元件" />
<ClassCmp name="我是class元件" />
</div>
);
ReactDOM.render(jsx, document.getElementById("root"));
 
建立./src/kkreact/index.js,它需要包含createElement⽅法
import { Component } from "./Component";
function createElement(type, props, ...children) {
props.children = children;
//判斷元件類型
let vtype;
if (typeof type === "string") {
// 原⽣标簽
vtype = 1;
} else if (typeof type === "function") {
// 類元件 函數元件
vtype = type.isReactComponent ? 3 : 2;
}
return {
vtype,
type,
props,
};
}
const React = {
createElement,
Component,
};
export default React;
 
render
建立react-dom.js
需要實作⼀個render函數,能夠将vdom渲染出來,這⾥先列印vdom
結構
import { mount } from "./virtual-dom";
function render(vnode, container) {
//vnode->node
mount(vnode, container);
// container.appendChild(node)
console.log("render", vnode);
}
export default {
render,
};
實作Component
 
要實作class元件,需要添加Component類,kreact.js
export class Component {
static isReactComponent = {};
constructor(props) {
this.props = props;
this.state={}
}
}
元件類型判斷
傳遞給createElememnt的元件有三種元件類型,1:dom元件, 2. class
元件, 3. 函數元件,使⽤vtype屬性辨別
轉換vdom為真實dom
export function mount(vnode, container) {
const { vtype } = vnode;
//建立⽂本節點
if (!vtype) {
mountText(vnode, container);
}
// 建立原⽣節點
if (vtype === 1) {
mountHtml(vnode, container);
}
// 建立函數元件
if (vtype === 2) {
mountFunc(vnode, container);
}開課吧web全棧工程師
//建立類元件
if (vtype === 3) {
mountClass(vnode, container);
}
}
function mountText(vnode, container) {
const textNode = document.createTextNode(vnode);
container.appendChild(textNode);
}
// 建立原⽣節點
function mountHtml(vnode, container) {
const { type, props } = vnode;
const node = document.createElement(type);
const { children, ...rest } = props;
Object.keys(rest).map(item => {
if (item === "className") {
node.setAttribute("class", rest[item]);
} else if (item.slice(0, 2) === "on") {
node.addEventListener("click", rest[item]);
}
});
children.map(item => {
if (Array.isArray(item)) {
item.map(c => {
mount(c, node);
});
} else {
mount(item, node);
}
});
container.appendChild(node);
}開課吧web全棧工程師
// 建立函數元件
function mountFunc(vnode, container) {
const { type, props } = vnode;
const node = type(props);
mount(node, container);
}
//建立類元件
function mountClass(vnode, container) {
const { type, props } = vnode;
const cmp = new type(props);
const node = cmp.render();
mount(node, container);
}
執⾏渲染,kreact-dom.js
import { mount } from "./virtual-dom";
function render(vnode, container) {
//vnode->node
mount(vnode, container);
// container.appendChild(node)
console.log("render", vnode);
}
export default {
render,
};      

總結:

1. webpack+babel編譯時,替換JSX為

React.createElement(type,props,...children)開課吧web全棧工程師

2. 所有React.createElement()執⾏

結束後得到⼀個JS對象即vdom,它能

夠完整描述dom結構

3. ReactDOM.render(vdom, container)可以将vdom轉換為dom并追加

繼續閱讀