各位小伙伴,春节假期已过大半,这个春节应该是历年来过的最安静的一个春节了吧,每天家里蹲,吃了睡,睡了吃,没想到不出门居然成了对社会最大的贡献。废话很少说,开始说正题。css
相信不少人都用过react开发项目,也有不少人好奇页面中明明没有直接用到React,可是页面却必须引入React,不然就会报错。初学者必定有这个疑问,不要着急,今天我就为你解答这个疑问。html
jsx其实就是个语法糖,用写html的方式来写js,一个很像xml的js扩展。React使用jsx来替代常规的js。在线体验node
写js不香吗?为何要引入一个新概念jsx来迷惑你们?归纳来说jsx来说有如下几个好处:react
尝试过上面的在线体验后,就会发现实际上babel-loader会把jsx预编译为React.createElement(xxx)。
jsx预处理前:webpack
jsx预处理后git
接下来咱们就本身动手来实现这三个API吧!github
做用:将传入的节点转化成vdom。
(1)建立./simple-react/component.js。实现class组件必备条件。web
export class Component{
static isReactComponent={};
constructor(props){
this.props=props;
this.state={}
}
}
复制代码
(2)建立./simple-react/index.js文件数组
import {Component} from "./component"
function createElement(type,props,...children){
props.children=children;
// console.log(type);
// 判断组件类型
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;
复制代码
上面我只是简单使用了一、二、3来分别标示来标签类型,你也可使用别的方式来处理。createElement被调用时会传入标签类型type,标签属性props及若⼲子元素children。安全
做用:渲染vdom,挂载到真实dom树上。
建立./simple-react/ReactDOM.js文件,包含render函数。
function render(vnode,container){
// vnode->node
mount(vnode,container); // 待实现
}
const ReactDOM={
render
}
export default ReactDOM;
复制代码
这样就实现了代码中的常见的ReactDOM.render(jsx,container)。接下来重点实现mount函数来处理vnode挂载。
建立./simple-react/virtual-dom.js文件。
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)
}
if(vtype===3){
// 建立class类型组件
mountClass(vnode,container);
}
}
function mountText(vnode,container){
let textNode=document.createTextNode(vnode);
container.appendChild(textNode)
}
function mountHtml(vnode,container){
const {type,props}=vnode;
let htmlNode=document.createElement(type);
const {children,...rest}=props;
Object.keys(rest).map(item=> {
if(item==="className"){
htmlNode.setAttribute("class",rest[item]);
}
if(item.slice(0,2)==="on"){
// 简单处理click事件,实际状况很复杂,须要考虑多种状况
htmlNode.addEventListener("click",rest[item])
}
})
children.map(item=>{
if(Array.isArray(item)){
item.map(i=>mount(i,htmlNode))
}else{
mount(item,htmlNode)
}
})
container.appendChild(htmlNode)
}
function mountFunc(vnode,container){
const {type,props}=vnode;
let node=type(props);
mount(node,container);
}
function mountClass(vnode,container){
const {type,props}=vnode;
let cmp=new type(props);
let node=cmp.render();
mount(node,container);
}
复制代码
mount函数建立好以后,完整的ReactDOM文件就能够改写为以下:
import {mount} from "./virtual-dom";
function render(vnode,container){
// vnode->node
mount(vnode,container)
}
const ReactDOM={
render
}
export default ReactDOM;
复制代码
相关文件建立好以后,在由creat-react-app建立好的项目中改写index.js文件,引入本身写好的简版react文件,进行测试。
import React from "./simple-react";
import ReactDOM from "./simple-react/ReactDOM";
import './index.css'
function Funcomp(props){
return (
<div className="border">{props.name}</div>
)
}
class ClassComp extends React.Component {
constructor(props) {
super(props);
this.state = { }
}
handleClick=()=>{
alert("hello")
}
render() {
return (
<div className="border">
<h1>{this.props.name}</h1>
{
[1,2,3].map(item=>
(
<h1 key={item}>{item}</h1>
)
)
}
<button onClick={this.handleClick}>点我</button>
</div>
);
}
}
let jsx=(
<div>
<div className="border">我是内容</div>
<Funcomp name="我是函数组件内容" />
<ClassComp name="我是class组件内容" />
</div>
)
ReactDOM.render(jsx,document.getElementById("root"))
复制代码
实际页面效果若是和下面截图同样,表明着简版react大功告成。
固然在实际react处理中,要处理的状况远比上面写的复杂多得多,dom的更新、替换、删除要通过diff的过程,打补丁,更新布丁等过程,v16.8以后的fiber更是相似于时间分片的方式,将任务拆分,将高优先级任务优先执行,提升页面渲染的流畅度等等,有不少须要咱们掌握的东西。
应了那句话:路漫漫其修远兮,吾将上下而求索。
欢迎点赞、留言、交流。