作为一个有追求的前端开发,掌握一门源码框架势在必行。特别是对于想要换工作或者想要进大厂的开发者来说,源码的考察必不可少。
然而源码晦涩难懂,没有扎实的基础或者足够的毅力很难坚持下去。但是如果能自己手动实现一个简易的 React,再去理解晦涩的 React 框架将会事半功倍。
本栏目《手写一个简易的 React》通过手动实现一个简易的 React 来疏通 React 的实现机制,为大家在 “ 啃 React 源码” 之路上尽一份绵薄之力。
创建 MiniReact 文件夹,通过编辑器(webstorm 或者 vcode )打开。
在控制台执行以下命令初始化项目,安装完成之后会自动创建 package.json 文件。
npm init
注意:在执行 npm init
初始化项目时会有一系列配置提示,直接 enter 进入下一步即可。
工程化项目必须安装 webpack 打包工具。
在控制台执行以下命令安装 webpack,安装成功后会出现 node_modules 文件夹和 package-lock.json。
其中 node_modules 文件夹是 webpack 相关的包依赖,package-lock.json 文件是对包依赖的一些描述(名称、版本、依赖包等)。
注意:在安装 webpack、webpack-cli、webpack-dev-server 这三个包时一定要按照指定的版本安装,如果随意安装,这三个包之间可能会出现版本不兼容的情况。
webpack 本身只能打包 JS(ES5) 文件,所以如果涉及到 JS ES6 语法和 CSS 样式。就必须安装相关的 loader,以及一些必要的 plugin。
安装 babel 编译 ES6 语法。
npm i @babel/[email protected] [email protected] @babel/[email protected]
其中 @babel/core 是核心功能包,babel 从版本 7 开始用 @开头的 scope 包,老版本是babel-core,其他的包都是一样的规律。
babel-preset-env 包中的 preset 是预设的一些 plugin 的集合。而 env 预设目前已经使用很久,并且现在已经完全替代了先前 babel 提出并建议的一些预设。
安装 loader 编译 CSS 文件。
安装 plugin 打包 HTML 模板
npm i [email protected]
注意:在安装 @babel/core 和 @babel/preset-react 这两个包时一定要安装相同大版本的包,比如要么都安装 版本 7,要么都降级到版本 6,否则会报各种错,大概率就是版本不兼容导致。
安装完成之后 package.json 文件,内容如下:
{
"name": "minireact",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "webpack-dev-server --progress --colors --mode=development --open",
"dev": "webpack --mode development",
"build": "webpack --mode production",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"@babel/core": "^7.12.3",
"@babel/preset-react": "^7.12.1",
"babel-loader": "^8.1.0",
"css-loader": "^5.0.0",
"html-webpack-plugin": "^4.5.0",
"style-loader": "^2.0.0",
"webpack": "^4.32.2",
"webpack-cli": "^2.0.9",
"webpack-dev-server": "^3.0.0"
}
}
安装了相关的依赖之后需要配置 webpack。在根目录下新建 webpack.config.js 文件,内容如下。
const path = require('path');
const htmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry:'./src/index.js',
output:{
path:path.resolve(__dirname,'dist'),
filename:"main.js"
},
mode: 'development',
devServer: {
contentBase: "./public",//本地服务器所加载的页面所在的目录
historyApiFallback: true,//不跳转
inline: true,//实时刷新
},
module:{
rules:[
{
test: /\.js$/,
exclude: path.resolve(__dirname, 'node_modules'),
include: path.resolve(__dirname, 'src'),
loader:'babel-loader',
options:{
presets: [
"@babel/react"
]
}
},
{
test: /\.css$/,
use:['style-loader','css-loader'] // 从右到左执行,所以注意顺序
}
]
},
plugins:[new htmlWebpackPlugin({
template:path.join(__dirname,'./public/index.html'),
filename: 'index.html'
})
],
}
在根目录下新建 public 文件夹,在该文件夹下新建 index.html,内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<title>MiniReact</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
在根目录下新建 src 文件夹,在 src 文件夹下:
新建 react 文件夹,用于手写 react 框架。
新建 index.js 作为入口文件。
新建 index.css 用于编写样式。
index.js
import React from './react/';
import ReactDOM from './react/react-dom';
import './index.css';
const App = (
<div>
<h1 className="border">Mini React</h1>
</div>
)
ReactDOM.render(App,document.getElementById('root'));
index.css
.border {
margin: 10px;
padding: 10px;
border: solid 1px red;
border-radius: 5px;
text-align: center;
}
最终效果: