# 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`


