快来跟我一起学 React(Day3)

Posted vv_小虫

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了快来跟我一起学 React(Day3)相关的知识,希望对你有一定的参考价值。

简介

上一节我们介绍了 React 官方提供的脚手架(create-react-app),然后用官方脚手架创建了一个 react-demo1 项目,用脚手架创建出来的项目算是 React 官方认为的最佳项目实践了,但是从上一节使用中我们可以看出,官方脚手架可还是有一些弊端,它只提供一些可改的 webpack 配置,如果你想自己添加一个 loaderplugin 什么的,你可能就需自定义 webpack 配置了,就不能在依赖脚手架了,既然是重新学习 React,我们就来挑战一下自己,从 0 开始搭建一个企业级的 React 项目,大家跟紧节奏吧。

知识点

  • Webpack (打包编译)
  • TypeScript(语法规范)
  • React(js 框架)
  • html-webpack-plugin(webpack 插件)
  • Sass(css 预处理框架)
  • PostCss(css 预处理框架)
  • mini-css-extract-plugin(提取 css 模块插件)
  • Babel( es 语法转化)
  • Jsx & Tsx(jsx 语法)
  • ESLint(代码质量校验)
  • Optimization(项目优化)

ok!要搭建一个企业级的项目需要准备的东西还是有点多的,我们分一下类:

  • Js 框架(Vue、TypeScript、Tsx、Jsx)
  • Css 样式(Sass)
  • 工程化工具(Eslint、Babel、webpack、webpack-chain)

我们开始吧。

项目初始化

我们创建一个 cus-react-demo 目录,然后在 cus-react-demo 目录下执行 npx init -y 命令:

mkdir cus-react-demo && cd cus-react-demo && npm init -y

如上图所示,初始化完毕后会看到一个 package.json 文件。

webpack

接下来我们需要安装 webpack 相关的依赖:

安装 webpack

webpack 核心库。

在工程目录 cus-react-demo 执行以下命令安装 webpack:

npm install -D webpack --registry https://registry.npm.taobao.org

目前 webpack 的版本是 5.26.3

安装 webpack-cli

webpack 指令库。

在工程目录 cus-react-demo 执行以下命令:

npm install -D webpack-cli --registry https://registry.npm.taobao.org

安装 webpack-dev-server

webpack 开发者服务框架。

在工程目录 cus-react-demo 执行以下命令:

npm install -D webpack-dev-server --registry https://registry.npm.taobao.org

安装 webpack-chain

webpack 配置工具。

在工程目录 cus-react-demo 执行以下命令:

npm install -D webpack-chain --registry https://registry.npm.taobao.org

创建 webpack 配置

在工程目录 cus-react-demo 下创建一个 webpack.config.js 文件:

touch webpack.config.js

然后对 webpack.config.js 进行配置,用 webpack-chain 导入一个 webpack 配置:

const config = new (require('webpack-chain'))();

module.exports = config.toConfig();

安装 cross-env

定义 node 中的 process.env 中的变量。

在工程目录 cus-react-demo 执行以下命令:

npm install -D cross-env --registry https://registry.npm.taobao.org

为了开发方便,我们在 package.json 中声明两个脚本 builddev


  "name": "cus-react-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": 
    "test": "echo \\"Error: no test specified\\" && exit 1",
    "build": "rimraf dist && cross-env NODE_ENV=production webpack --mode=production",
    "start": "cross-env NODE_ENV=development webpack serve --mode=development --progress"
  ,
  "author": "",
  "license": "ISC",
  "devDependencies": 
    "cross-env": "^7.0.3",
    "webpack": "^5.26.3",
    "webpack-chain": "^6.5.1",
    "webpack-cli": "^4.5.0",
    "webpack-dev-server": "^3.11.2"
  

我们可以试着运行一下 buildstart 脚本命令:

npm start

现在运行肯定是报错的,因为我们还没有对项目进行任何配置。

入口与出口

我们首先在工程目录 cus-react-demo 下创建一个 src 目录,然后在 src 目录下创建一个 main.tsx 文件:

mkdir src && cd src && touch main.tsx && cd ..

然后我们找到 webpack.config.js 文件,对 webpack 的入口和出口进行配置:

const path = require('path');
const config = new (require('webpack-chain'))();
const isDev = process.env.NODE_ENV === 'development'; // 判断是否是开发环境
config
    .context(path.resolve(__dirname, '.')) // webpack 上下文目录为项目根目录
    .entry('app') // 入口文件名称为 app
        .add('./src/main.tsx') // 入口文件为 ./src/main.tsx
        .end()
    .output
        .path(path.join(__dirname, './dist')) // webpack 输出的目录为根目录的 dist 目录
        .filename(isDev ? '[name].[hash:8].js' : '[name].[contenthash:8].js') // 打包出来的 bundle 名称为 "[name].[contenthash:8].js"
        .publicPath('/') // publicpath 配置为 "/"
        .end();
module.exports = config.toConfig();

可以看到,我们配置了一个 app 入口 ./src/main.ts,然后给所有的输出文件名称改成了 [name].[hash:8].js

我们再次执行 build 命令:

npm run build

可以看到,这次没有报错了。

但是当我们给 src/main.tsx 添加点内容试试:

let a: string = 'hello';

我们再次运行 npm run build 命令:

可以看到,又报错了,这次是因为 webpack 没法去解析 main.tsx 中的 ts 语法,所以接下来我们就需要配置一下 webpackloader 了,让我们的项目能够支持 jsxtstsx 语法。

Babel & TypeScript

因为我们项目是需要支持 ts 语法的,所以我们需要安装一下 TypeScript 相关依赖:

babel-preset-react-app

很幸运哈,React 官方已经提供了一套 babel 插件集合,它会自动的帮我们添加 babel 相关的配置,让我们能够轻松的使用 jsxtstsx 语法,而且不需要考虑 es 语法的兼容性问题,会根据我们的浏览器配置自动做语法兼容等操作。

在工程目录 cus-react-demo 执行以下命令:

npm install -D babel-preset-react-app --registry https://registry.npm.taobao.org

babel-loader

webpack 加载器。

在工程目录 cus-react-demo 下执行以下命令安装:

npm install -D babel-loader --registry https://registry.npm.taobao.org

ok!安装完 Babel 的一些依赖后,我们开始配置 webpack。

找到 webpack.config.js 文件,然后添加 babel-loader 配置:

const path = require('path');
const config = new (require('webpack-chain'))();
const isDev = process.env.NODE_ENV === 'development'; // 判断是否是开发环境
config
    .context(path.resolve(__dirname, '.')) // webpack 上下文目录为项目根目录
    .entry('app') // 入口文件名称为 app
        .add('./src/main.tsx') // 入口文件为 ./src/main.tsx
        .end()
    .output
        .path(path.join(__dirname, './dist')) // webpack 输出的目录为根目录的 dist 目录
        .filename('[name].[contenthash:8].js') // 打包出来的 bundle 名称为 "[name].[contenthash:8].js"
        .publicPath('/') // publicpath 配置为 "/"
        .end()
    .resolve
        .extensions.add('.js').add('.jsx').add('.ts').add('.tsx').end() // 添加后缀自动解析
        .end()
    .module
        .rule('ts') // 配置 typescript
            .test(/\\.(js|mjs|jsx|ts|tsx)$/)
            .exclude
                .add(filepath => 
                    // Don't transpile node_modules
                    return /node_modules/.test(filepath)
                )
                .end()
            .use('babel-loader')
                .loader('babel-loader')
                .end()
            .end()
module.exports = config.toConfig();

然后我们需要在工程目录 cus-react-demo 下创建一个文件 babel.config.js

touch babel.config.js

然后写入以下代码到 babel.config.js 文件:

module.exports = 
    presets: [
        [
            "babel-preset-react-app", // 添加 react-app 插件集合
        ],
    ]

TypeScript 配置

因为我们需要进行 ts 语法的我们还需要在工程目录 cus-react-demo 下创建一个 ts 配置文件 tsconfig.json

touch tsconfig.json

然后写入以下代码到 tsconfig.json 文件:


  "compilerOptions": 
    "target": "esnext",
    "module": "esnext",
    "strict": true,
    "jsx": "preserve",
    "importHelpers": true,
    "moduleResolution": "node",
    "experimentalDecorators": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "suppressImplicitAnyIndexErrors": true,
    "resolveJsonModule": true,
    "sourceMap": true,
    "baseUrl": ".",
    "types": ["webpack-env"],
    "paths": 
      "@/*": ["src/*"]
    ,
    "lib": ["esnext", "dom", "dom.iterable"]
  ,
  "include": ["src/**/*.ts", "src/**/*.tsx",],
  "exclude": ["node_modules"]

ok!我们再次运行 npm run build 命令:

可以看到,这一次 webpack 正常完成了编译打包。

React 配置

我们首先安装一下 React 所需要的依赖:

安装 react & react-dom

React 核心 API。

在工程目录 cus-react-demo 执行以下命令:

npm install react react-dom @types/react @types/react-dom --registry https://registry.npm.taobao.org

然后我们修改一下 src/main.tsx 文件来测试一下:

import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(
    <div>hello react</div>,
    document.getElementById('root')
);

很简单,就是利用 tsx 语法在页面中输出了一句 “hello react” 字符串。

如果需要开启 webpack-dev-server 的话,我们还需要配置一下 devServer

我们找到 webpack.config.js 配置文件,然后添加 devServer 配置:

const path = require('path');
const config = new (require('webpack-chain'))();
const isDev = process.env.NODE_ENV === 'development'; // 判断是否是开发环境
config
    .context(path.resolve(__dirname, '.')) // webpack 上下文目录为项目根目录
    .entry('app') // 入口文件名称为 app
        .add('./src/main.tsx') // 入口文件为 ./src/main.tsx
        .end()
    .output
        .path(path.join(__dirname, './dist')) // webpack 输出的目录为根目录的 dist 目录
        .filename(isDev ? '[name].[hash:8].js' : '[name].[contenthash:8].js') // 打包出来的 bundle 名称为 "[name].[contenthash:8].js"
        .publicPath('/') // publicpath 配置为 "/"
        .end()
    .resolve
        .extensions.add('.js').add('.jsx').add('.ts').add('.tsx').end() // 添加后缀自动解析
        .end()
    .module
        .rule('ts') // 配置 typescript
            .test(/\\.(js|mjs|jsx|ts|tsx)$/)
            .exclude
                .add(filepath => 
                    // Don't transpile node_modules
                    return /node_modules/.test(filepath)
                )
                .end()
            .use('babel-loader')
                .loader('babel-loader')
                .end()
            .end()
    .end()
    .devServer
        .host('0.0.0.0') // 服务器外部可访问
        .disableHostCheck(true) // 关闭白名单校验
        .contentBase(path.resolve(__dirname, './public')) // 设置一个 express 静态目录
        .historyApiFallback(
            disableDotRule: true, // 禁止在链接中使用 "." 符号
            rewrites: [
                 from: /^\\/$/, to: '/index.html' , // 将所有的 404 响应重定向到 index.html 页面
            ],
        )
        .port(8080) // 当前端口号
        .hot(true) // 打开页面热载功能
        .sockPort('location'); // 设置成平台自己的端口
module.exports = config.toConfig();

ok!然后我们在工程目录 cus-react-demo 下执行 npm start 开启 webpack-dev-server

npm start

执行完毕后,我们可以直接用浏览器访问一下打包过后的目录(http://127.0.0.1:8080/webpack-dev-server):

可以看到,webpack-dev-server 列出了 webpack 打包出来的所有 js 资源文件。

接下来我们在工程目录 cus-react-demo 底下创建一个 public 目录,然后在 public 目录下创建一个 index.html 文件作为我们 app 的入口页面:

mkdir public && touch public/index.html

然后写入以下代码到 public/index.html 文件:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
  </head>
  <body>
    <noscript>your browser should support javascript!</noscript>
    <!-- 挂载点 -->
    <div id="root"></div>
    <!-- 引入入口文件 -->
    <!-- 注意!这里的 64a558f4 每个人是不一样的 -->
    <script src="/app.64a558f4.js"></script>
  </body>
</html>

(**注意!**这里的 64a558f4 每个人是不一样的)创建完毕后,我们重启服务:

npm start

然后我们可以直接用浏览器访问 http://127.0.0.1:8080/ 项目入口了:

可以看到,页面渲染了我们在 src/index.js 入口文件中定义的 “hello react” 字符串。

html-webpack-plugin

虽然浏览器中正常显示了我们的结果,但是我们现在还是手动去引入 app.xxx.js 文件的,而 xxx 是经常变化的,所以我们不可能每次都去改 public/index.html 文件,能不能自动去引入 js 资源文件到入口 index.html 呢?

安装 html-webpack-plugin

在工程目录 cus-react-demo 执行以下命令:

npm install -D html-webpack-plugin --registry https://registry.npm.taobao.org

然后修改一下 webpack.config.js 配置文件,引入 html-webpack-plugin 插件:

const path = require('path');
const config = new (require('webpack-chain'))();
const isDev = process.env.NODE_ENV === 'development'; // 判断是否是开发环境
config
		.target('web')
    .context(path.resolve(__dirname, '.')) // webpack 上下文目录为项目根目录
    .entry('app') // 入口文件名称为 app
        .add('./src/main.tsx') // 入口文件为 ./src/main.tsx
        .end()
    .output
        .path(path.join(__dirname, './dist')) // webpack 输出的目录为根目录的 dist 目录
        .filename(isDev ? '[name].[hash:8].js' : '[name].[contenthash:8].js') // 打包出来的 bundle 名称为 "[name].[contenthash:8].js"
        .publicPath('/') // publicpath 配置为 "/"
        .end()
    .resolve
        .extensions.add('.js').add('.jsx').add('.ts').add('.tsx').end() // 添加后缀自动解析
        .end()
    .module
        .rule('ts') // 配置 typescript
            .test(/\\.(js|mjs|jsx|ts|tsx)$/)
            .exclude
                .add(filepath => 
                    // Don't transpile node_modules
                    return /node_modules/.test(filepath)
                )
                .end()
            .use('babel-loader')
                .loader('babel-loader')
                .end()
            .end()
    .end()
    .plugin('html') // 添加 html-webpack-plugin 插件
        .use(require('html-webpack-plugin'), [
            
                template: path.resolve(__dirname, './public/index.html'), // 指定模版文件
                chunks: ['app'], // 指定需要加载的 chunk
                inject: 'body', // 指定 script 脚本注入的位置为 body
            ,
        ])
        .end()
    .devServer
        .host('0.0.0.0') // 服务器外部可访问
        .disableHostCheck(true) // 关闭白名单校验
        .contentBase(path.resolve(__dirname, './public')) // 设置一个 express 静态目录
        .historyApiFallback(
            disableDotRule: true, // 禁止在链接中使用 "." 符号
            rewrites: [
                 from: /^\\/$/, to: '/index.html' , // 将所有的 404 响应重定向到 index.html 页面
            ],
        )
        .port(8080) // 当前端口号
        .hot(true) // 打开页面热载功能
        .sockPort(以上是关于快来跟我一起学 React(Day3)的主要内容,如果未能解决你的问题,请参考以下文章

快来跟我一起学 React(Day4)

快来跟我一起学 React(Day4)

快来跟我一起学 React(Day2)

快来跟我一起学 React(Day7)

快来跟我一起学 React(Day7)

快来跟我一起学 React(Day6)