书格前端

create-react-app-from-scratch


create-react-app-from-scratch

React社区中提供了类似create-react-app这样的命令行工具,作为React项目的脚手架,这个工具提供了大而全的功能,直接拿过来使用,你可能不明白背后的原理,这里跟着Creating a React App… From Scratch这篇文章一起从零搭建一个React App,有条件的同学可以直接阅读英文版,这里实现的是一个最简单的功能,支持ES6+和JSX语法、热加载。

起步

创建一个目录并且通过npm init初始化一个项目,有需要也可以通过git init创建版本管理,新项目的文件结构如下:

.
+-- public
+-- src

此时可以添加一个.gitignore文件,并把node_modulesdist目录排除在提交范围。

public目录中,将会存放一些静态的资产,最主要的是放置index.html文件,让react用来渲染app用。

index.html如下:

<!-- sourced from https://raw.githubusercontent.com/reactjs/reactjs.org/master/static/html/single-file-example.html -->
<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <title>React Starter</title>
</head>

<body>
  <div id="root"></div>
  <noscript>
    You need to enable JavaScript to run this app.
  </noscript>
  <script src="../dist/bundle.js"></script>
</body>

</html>

这里react通过root这个id进行关联渲染元素,我们后续打包的文件名称为bundle.js

Babel

接下来添加babel相关的软件包:

npm install --save-dev @babel/core@7.1.0 @babel/cli@7.1.0 @babel/preset-env@7.1.0 @babel/preset-react@7.0.0

babel-core是主要的babel包,用于转译代码。babel-cli用于在命令行中编译文件。preset-reactpreset-env用于预先配置要转换的代码类型,env预置用以转换ES6+的代码到ES5的代码,react预置转换JSX的代码。

接着在项目的根目录,创建一个文件.babelrc,用于告诉bebel我们要配置的预置项。

{
  "presets": ["@babel/env", "@babel/preset-react"]
}

Babel还有很多的插件值得研究。

Webpack

接下来需要配置Webpack来打包和启用开发热加载等功能。

安装开发依赖:

npm install --save-dev webpack@4.19.1 webpack-cli@3.1.1 webpack-dev-server@3.1.8 style-loader@0.23.0 css-loader@1.0.0 babel-loader@8.0.2

Webpack使用loader来处理不同类型的文件并进行打包,并提供一个开发服务器方便高效的进行开发。

在项目根目录下创建一个新文件webapck.config.js,在这里导出一个包含webpack配置的对象:

const path = require("path");
const webpack = require("webpack");

module.exports = {
  entry: "./src/index.js",
  mode: "development",
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /(node_modules|bower_components)/,
        loader: "babel-loader",
        options: { presets: ["@babel/env"] }
      },
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"]
      }
    ]
  },
  resolve: { extensions: ["*", ".js", ".jsx"] },
  output: {
    path: path.resolve(__dirname, "dist/"),
    publicPath: "/dist/",
    filename: "bundle.js"
  },
  devServer: {
    contentBase: path.join(__dirname, "public/"),
    port: 3000,
    publicPath: "http://localhost:3000/dist/",
    hotOnly: true
  },
  plugins: [new webpack.HotModuleReplacementPlugin()]
};

这里webpack的配置项暂时不解释,接下来进行React的配置。

React

首先,我们要添加两个软件包: react@16.5.2react-dom@16.5.2

npm install --save react@16.5.2 react-dom@16.5.2

接下来,要告诉React app在哪里关联DOM,在src文件夹中创建一个index.js文件:

import React from "react";
import ReactDOM from "react-dom";
import App from "./App.js";
ReactDOM.render(<App />, document.getElementById("root"));

ReactDOM.render这个函数告诉React渲染什么和在哪里渲染,这里渲染一个叫做App的组件,并且渲染在ID为root的DOM元素上。

接下来在src文件夹下创建一个App.js文件,这是一个React组件。

import React, { Component} from "react";
import "./App.css";

class App extends Component{
  render(){
    return(
      <div className="App">
        <h1> Hello, World! </h1>
      </div>
    );
  }
}

export default App;

src目录下创建一个App.css文件

.App {
  margin: 1rem;
  font-family: Arial, Helvetica, sans-serif;
}

最终的项目结构如下:

.
+-- public
| +-- index.html
+-- src
| +-- App.css
| +-- App.js
| +-- index.js
+-- .babelrc
+-- .gitignore
+-- package-lock.json
+-- package.json
+-- webpack.config.js

在webpack的script中增加一个脚本命令,start

start: webpack-dev-server --mode development

使用npm start命令即可启动

Finishing HMR

这个时候修改文件内容,页面并不会自动更新,要增加热加载功能,需要另外一个软件包的支持

npm install --save react-hot-loader@4.3.11

这个包可以安装在依赖中,而不是开发依赖,因为包会自动判断是否执行。

App.js中导入react-hot-loader

import React, { Component} from "react";
import {hot} from "react-hot-loader";
import "./App.css";

class App extends Component{
  render(){
    return(
      <div className="App">
        <h1> Hello, World! </h1>
      </div>
    );
  }
}

export default hot(module)(App);

这个时候修改文件的内容,页面就会即时刷新了。

Build

最后,可以在package.json中增加一个build的脚本命令,

build: webpack --mode production