手把手从底层搭建react应用(如何自己实现react脚手架)(webpack环境搭建)

先附上原文链接 https://blog.usejournal.com/creating-a-react-app-from-scratch-f3c693b84658

在这里插入图片描述
React如果脱离我们所熟悉的环境,可能就不会很好的工作了。原因是React中有很多的关键字以及语法是node目前支持不了的(作者写这篇文章的时候是9.3.0版本)。要想很好的运行React,需要进行一系列相当麻烦的设置。对此呢,Facebook已经提供了一套配置选项来快速启动React应用,那么老铁为啥还要写这篇文章呢?

我是这么认为的,create-react-app虽好,但是它让你对React应用变得更加的陌生了(至少在你手动使用eject命令之前是这样的)。当然,还是有很多驱使你自己配置一个react应用的动机,最起码你可以搞懂它的底层到底是怎么运作的。可以极大的满足你的好奇心。

正如我提到的,在你搭建React应用的过程中会有相当多的障碍。第一点是因为node有很多语法处理不了(比方说import/export以及JSX)。第二点原因是你需要build你的文件或者在开发的过程中为你的应用启动服务—尤其是这后一种情况相当的重要。

所幸的是,我们可以通过Babel以及Webpack来解决这些问题。

安装(Setup)

开始了,第一步创建一个目录。然后使用 npm init 初始化你的应用并使用你喜爱的编辑器打开它。此时也是使用 git init 进行初始化操作的好时候。在根目录下创建如下目录结构
在这里插入图片描述
想的再远一些,我们最终生成的应用在提交的时候会排除掉很多的文件比如node_module。此时我们还可以创建一个.gitignore文件来排除最后的node_modules以及dist文件。(在git bash下使用touch gitignore即可创建)

public目录用来存放所有静态资源以及我们最重要的的index.html文件,毕竟react会用它来初始化render我们的app。 下面的代码是用react文档中经过小小修改的源码。放心大胆的粘贴到你的index.html中。(当然要先在public文件夹中创建一个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>
view rawreact-app-tutorial index.html hosted with ❤ by GitHub

这里有两行需要特别留意。一个是div根容器。我们的react 应用可是要挂在里面

<div id="root"></div>

另一个是

<script src="../dist/bundle.js"></script>

这个文件里面会写我们的react代码。当然你可以随便取名字,但我这里就用bundle.js称呼它好了

到此,我们把我们的HTML页面弄好了。接下来我们要严肃认真起来了哦。接下来我们要做其他一些事情了。首先呢,我们要确保我们的代码会被编译,所以我们需要Babel

Babel

继续运行如下命令

npm install --save-dev @babel/[email protected] @babel/[email protected] @babel/[email protected] @babel/[email protected]

babel-core是主要的babel包,我们进行任何代码转换都离不开它。babel-cli允许你通过命令行的方式来进行文件的编译。preset-reactpreset-env都是用来进行代码的特殊转换。在这个例子中,env预设让我们能够将ES6+的代码转换成传统js代码。react预设则是处理JSX相关的转换问题。

在项目根目录下,创建一个文件叫做.babelrc 粘入如下代码 这里呢 我们要告诉babel 我们要使用env和react这两个预设。

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

babel还有很多很多插件你可以使用,如果env预设不能满足你的需要的话。这不是我们所需要担心的,当然你可以看这里查看详情

Webpack

接下来我们需要配置Webpack了,这里我们还需要安装一些包并将他们保存为本地依赖。

npm install --save-dev 
[email protected] [email protected] 
[email protected] [email protected] [email protected] [email protected]

webpack使用loader来处理不同类型的文件来进行打包。它还能帮我们在开发过程中启动应用服务,并且在代码进行改变的时候及时刷新页面。为了使用这些功能,我们需要安装不同的loaders以及准备dev-server

在我们的根目录下创建一个文件叫做webpack.config.js 这个文件会输出一个记录webpack配置的对象(Object),如下

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()]
};

让我们快速来看一下这个配置文件

entry告诉webpack我们的应用程序从哪儿开始以及从哪里开始打包我们的文件。接下来的这行
**mode: “development”,**告诉webpack我们正在以开发模式运行,这使我们不必在运行开发服务器时添加模式标志

module对象定义你导出的javascript模块如何转换以及那些文件要根据给定的rules进行转换

我们的第一条规则是转换ES6以及JSX语法。test和exclude是用来匹配文件的条件。在这个例子中,它会匹配node_module
s和bower_components之外的所有目录。接下来我们要知道webpack上去使用babel来转换我们的js以及jsx文件(test中定义的规则),最后我们告诉webpack使用env中的预设。

接下来的规则是用来处理css的。因为我们这里并没有用到pre或者post css等高级功能,我们只需要确保加入style-loader以及css-loader到use属性中。css-loader需要依赖style-loader才能工作。

resolve属性告诉我们 哪些类型的文件要进行处理,这也允许我们在导入modules的时候不用写扩展名。

output属性告诉webpack我们打包之后的代码在哪里输出。publicPath属性定义了我们打包后的文件应该跑到哪个目录里。同时告诉webpack-dev-server从哪里去启动我们的服务

这个publicPath属性是一个特殊的属性 ,它可以帮助我们进行dev-server操作。他可以定义我们的公共url的目录,起码webpack-dev-server会很关心这一点。如果设置错了,就会报404错误。

最后我们在devServer属性中启动webpack-dev-server服务。这里并没有太多的要求。只要标出我们的静态资源放那儿(比方说我们的index.html)以及我们要在哪个端口启动该服务。你可以看到devServer也有一个publicPath,这里的publicPath是为了告诉server我们打包后的文件在哪儿

最后需要留心的是 output.publicPath 和 devServer.publicPath是不一样的。重要的事情说三遍。

最后呢 为了实现热更新,及时响应我们的changes 只需要在devServer中将hotOnly设置为true 当然我们还需要实例化我们的HMR插件(Hot Module Replacement)

大功告成了

接下来看React代码了

React

首先我们需要额外安装两个依赖包 [email protected] 以及 [email protected]

我们需要告诉Reactapp 我们要把reactapp挂在到哪个文件(这里是index.html)
在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(马上就要创建了)的组件,渲染到我们index.html中的一个div中。

现在在src中创建另外一个文件 叫做App.js 粘入以下代码(如果你习惯使用create-react-app的话,这段代码你应该比较熟悉,这是一个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;

接下来我们再加点儿css src目录下创建一个App.css


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

你最终的代码结构看起来是这个样子的
在这里插入图片描述
最后我们可以执行webpack-dev-server --mode development 命令启动我们的服务。当然我建议你把这个放到package.json中的start命令里面

(令人愤怒的结果搞到现在跑不起来)
问题出在哪里?!!!!!

检查之后发现自己少执行了 没有安装[email protected] 以及 [email protected]
以及配置babelrc 这个文件里面写了个才能在webpack里面用

Finishing HMR

如果你现在启动服务,当你做了内容更改之后,需要刷新浏览器才能看到效果,我们想让浏览器内容自动改变怎么办?

emm HMR需要知道替换什么,目前我们并没有给它任何东西。因此,我们要使用react团队提供给我们的一个包:
[email protected]
你可以按照惯例安装好它

你可以安全的将react-hot-loader作为常规依赖项安装而不是开发以来项,因为他可确保不会再生产环境执行并且占用的空间很小。

接下来 在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);

现在当你更改内容的时候,按下保存 浏览器就会自动刷新了

最后一些细节

你可能注意到一些有趣或者说惊讶的事情,当你启动项目的时候 你在dist目录中看不到编译之后的文件。 这是因为webpack-dev-server将打包后的文件在内存当中运行了,一旦服务停止,文件就消失了。所以要想生成build之后的文件。我们需要好好的利用webpack。 要在package.json文件中添加一条build指令 webpack --mode development 你可以用生产来代替开发。但是如果你忘记写–mode 它会自动执行后一种情况并且给你一个警告。

以上的内容几乎涵盖了你在开发react应用中的所有内容,而不需要去触碰craete-react-app 当然 还有很多的东西要添加。比方说,webpack无法处理图片,但是我们有处理图片所需的loader。将这个任务留给你,

我希望这篇文章能让你对react-app架构搭建带来一些启发。关于babel以及webpack我没有太深入,但是建议你不放过本文的任何一个链接。这两个工具乍一看令人生畏,但他们可以让你的开发水平提升一个质的飞跃!

如果你还搞不懂,这里有github项目。你也可以查看更老版本的实现(这个可能有点难)。或者在twitter上打招呼

这篇文章写于2018.4.24
软件包版本 2018.5.13
反馈webpack-dev-server的bug修复 2018.6.16
反馈对React以及软件包的更新 2018.9.23

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章