# react-study **Repository Path**: vue000/react-study ## Basic Information - **Project Name**: react-study - **Description**: react 手写 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-03-27 - **Last Updated**: 2021-03-31 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 设置项目 首先,为项目创建目录: ``` mkdir webpack-react-tutorial && cd webpack-react-tutorial ``` 创建用于保存代码的最小目录结构: ``` mkdir -p src ``` 通过运行以下内容来初始化项目: ``` npm init -y ``` ## 设置Webpack 让我们通过运行以下命令安装webpack和webpack-cli: ``` npm i webpack webpack-cli --save-dev ``` 现在在里面添加webpack命令package.json: ``` "scripts": { "build": "webpack --mode production" } ``` ## 设置 Babel 解释 JSX 安装依赖项: ``` npm i @babel/core babel-loader @babel/preset-env @babel/preset-react --save-dev ``` 以上工具的作用是:webpack项目里当 import 一个`.jsx`文件时,使用 `babel-loader` 来处理这个文件, `babel-loader` 使用 `@babel/core` 来执行转换, 在转换的过程中使用了babel的 `@babel/preset-env`插件用于把最新的ES转换为ES5,使用 `@babel/preset-react`把 JSX转换为正常的JavaScript。 在项目根目录创建 .babelrc 文件,该文件的作用是 告诉 babel-core 在执行转换的时候使用如下插件: ``` { "presets": ["@babel/preset-env", "@babel/preset-react"] } ``` src目录上创建一个名为的文件webpack.config.js:内容如下: ``` const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: { index: './src/index.js' }, module: { rules: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, use: { loader: 'babel-loader' } } ] }, plugins: [ new HtmlWebpackPlugin() ], output: { filename: '[name].[hash:5].bundle.js', path: path.resolve(__dirname, 'dist') }, devtool: 'inline-source-map', devServer: { contentBase: './dist' } } ``` 为了使环境能正常启动,需要安装 `html-webpack-plugin` 和 `wepack-dev-server` ``` npm i --save-dev html-webpack-plugin webpack-dev-server ``` 修改 package.json,加上start ``` { ... "scripts": { "start": "webpack-dev-server", "build": "webpack" }, ... } ``` ## 丰富代码 - **index.js** ```js import React,{Component} from './lib/react' import ReactDOM from './lib/react-dom' function Menu(props) { return (

React 组件Menu

{props.title}

) } class App extends Component{ constructor(props) { super(props) this.state = { title:'title属性' } this.handClick=this.handClick.bind(this) } handClick() { this.setState({ title:'已经改变这个题目' }) } render() { return (

下面为App组件部分

{this.state.title}

这是绑定的标签id:{this.props.id }

点击我改变题目
) } } ReactDOM.render(( React 手写版 ),document.body) // function clickMe(){ // console.log('clickMe') // } // let styleObj = { // color: 'red', // fontSize: '20px' // } // // jsx // let div = ( // ); // console.log(div); // // 将虚拟dom渲染到页面上 // ReactDOM.render(div,document.body) ``` - `src`下新建`lib`文件夹 包含react.js和react-dom.js **react.js** ```js import {renderComponent} from './react-dom' function createElement(tag, attrs, ...children) { return { tag, attrs, children } }; class Component{ constructor(props) { this.props = props; this.state = {}; } setState(newState) { //修改状态 Object.assign(this.state, newState) console.log('hello') renderComponent(this) } } export { createElement, Component } export default { createElement, Component } ``` **react-dom.js** ```js import React from './react.js' // 设置属性方法 function setAttribute(node, attrs) { if (!attrs) return; for (let key in attrs) { if (key.startsWith('on')) {//以on开头的属性 node[key.toLocaleLowerCase()] = attrs[key] } else if (key === 'style') { //以style开头的属性 Object.assign(node.style,attrs[key]) } else { node[key]=attrs[key] } } } // 渲染render()方法 function renderVdom(vdom, container) { let node = createDomfromVdom(vdom) container.appendChild(node) } //把vdom变成真实dom function createDomfromVdom(vdom) { let node; if (typeof vdom === "string") { //如果只有一个 node = document.createTextNode(vdom) return node } if (typeof vdom === 'object') { if (typeof vdom.tag === 'function') { // let component = new vdom.tag(vdom.attrs) let component=getComponent(vdom.tag,vdom.attrs) let vnode = component.render() node = createDomfromVdom(vnode) component.$root=node //把真实DOM绑定在$Root上 } else {//如果内部包得有children node = document.createElement(vdom.tag) setAttribute(node, vdom.attrs) vdom.children.forEach(childVdom => renderVdom(childVdom, node)); } } return node } function getComponent(constructor, attrs) { if (constructor.prototype instanceof React.Component) {//是继承于React.component return new constructor(attrs) } else {//是函数 let App = class extends React.Component{ } App.prototype.render = function () { return constructor(attrs) } return new App(attrs) } } function renderComponent(component) { let vdom = component.render() let node = createDomfromVdom(vdom) if (component.$root) {//替换掉父 component.$root.parentNode.replaceChild(node,component.$root) } } function render(vdom, container) { container.innerHTML = '' renderVdom(vdom,container) } //接收虚拟Dom export { render, renderComponent } export default { render, renderComponent } ``` ## 测试JSX 执行终端,启动测试 ``` npm run start ``` **如果报错:Cannot find module ‘webpack-cli/bin/config-yargs’** `npm uninstall webpack-cli `然后再` npm install webpack-cli@3 -D` ![image-20210327014805793](https://yufeng-1.oss-cn-beijing.aliyuncs.com/img/20210331005330.png) ![image-20210331011804292](https://yufeng-1.oss-cn-beijing.aliyuncs.com/img/20210331011804.png) ![image-20210331011827145](https://yufeng-1.oss-cn-beijing.aliyuncs.com/img/20210331011827.png)