基于webpack从零开始自己配置react项目

前言

作为一个前端开发小学生,虽然说也写过不少react的项目,但是每当自己想要用心写一个主页、博客、或者demo的时候,会发现项目开始之前的配置总是让我很烦,如果直接使用create-react-app脚手架生成一个react项目,看起来是很方便,但是事实上一拥而来的一大堆配置文件很多都用不上,或者看不懂,而且create-react-app本身就是基于webpack实现的,但是它的配置文件并没有暴露出来,开发者也不知到它内置了哪些功能,哪些插件,非常难受,所以从零开始配置属于自己的项目框架,并且根据自己的习惯修改或者完善它,对于学习和工作都非常有必要

初始化项目录

首先新建一个项目目录

mkdir project

进入该目录

cd project

初始化项目

备注:该步如果执行失败则说明没有安装node或者没有将node可执行文件添加到全局变量,请自行到node官网学习或下载

npm init -y

执行完该命令之后项目目录中会自动生成一个package.josn文件,结构如下

{
  "name": "project",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

这是一个宏观描述你的项目的文件,每个字段的意思分别是

字段解释
name包的名称
version包的版本
description包的描述
main包的入口文件
script可执行脚本
keywoeds搜索是的关键字
author作者
license通行证

其实在我们写项目的过程中,以上这些配置项几乎都不用管,之所以会自动生成这些信息,是因为npm默认将我们的项目看作是一个包;如果我们开发的是一个包,那么我们当然要写清楚自己包的名字,版本,描述等一系列信息,以方便npm统一管理,这样当你的包发布后,别人就可以下载我们的包,就像我们下载别人的包一样;

不过我们是做一个项目,并不是想发布一个包,因此我们还要做一点修改

我们将package.json中的"main": index.js删除,并将其改为"private": true来告诉npm我们是一个私人项目,不是一个待发布的包,所以不需要什么执行入口文件

{
  "name": "projec",
  "version": "1.0.0",
  "description": "",
  "private": true,
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "html-webpack-plugin": "^5.5.0",
    "webpack": "^5.75.0",
    "webpack-cli": "^5.0.0"
  }
}

至此,项目构建的地基就搭建好了

创建index.js

一个项目里只有一个package.json文件多少显得有点空虚,因此现在我们就在项目根目录下创建一个src目录,并且在该目录下创建一个index.js文件
src目录

里边写点什么代码呢?先就来个最简单的吧

// index.js
console.log('Hello World')

代码已经写好了,现在我们需要做的,就是将它打包

安装webpack包

node中有两个包,webpackwebpack-cli
webpack内置了项目文件的加载、解析、打包等一些列核心功能
webpack-cli的作用是让开发者能够方便的使用使用命令行调用webpack功能

所以这两个包是必须是最先下载的

npm install webpack webpack-cli -D

如果执行成功,不出意外的话,我们的package.json文件会多出这么一段内容
package.json变化
没错,我们的package.josn会记录我们项目用到的包,并且将包名添加到文件中,这也正验证了前边说过的,package.json文件是项目的描述文件,所以通常情况下都不要轻易改动package.json里边的内容

当然了,项目目录中也多了一个名叫node_modules的文件夹,它正是我们项目中安装的包所存放的地方

by the way(-D -S -g区别)

这里说一下在npm install的时候 -D -S -g的区别,它们都是简写,具体全拼是什么自己去搜吧,这里只说区别

-g很简单,就是全局安装,举个例子,假如我们用了npm install typescript -g安装了typescript包,那么这个包就被全局安装到了你的电脑里,它的可执行文件也会被添加到环境变量中,这时我们在任意文件夹都可以执行tsc命令来编译.ts文件
但是-D -S就不一样了,加了-D或者-S的安装命令,npm会将它们安装到项目目录中,并且会记录在项目的package.json文件里,还是拿typescript举例子,npm install typescript-D会将typescript包安装到项目目录的node_module文件夹中,这个时候直接用tsc命令编译是会报错的,因为电脑找不到该命令,如果我们想在该项目的命令行中调用它的tsc编译功能,可以采用npx tsc命令,npx命令会在本地项目目录中查看是否有可执行文件,没有的话就去网上下载,下载下来用完再删除,不占用内存,npx create-react-app也是这个原理,因此通常我不会将任何包安装到全局当中,因为它相当于装了一个软件,会永久占用电脑内存
-D-S的区别是什么呢,我们刚才就已经看到,-D安装的包,会被记录到package.json文件中的devDependencies字段,大家不妨试试-S呢?其实它也会被记录,只不过被记录到了dependencies字段下
-d-s区别

有什么区别呢?

有很多网上说-D是开发环境,-S是生产环境,太过于肤浅,事实上,在开发项目的过程中,它俩是没有任何区别的,只有在我们发布本地项目为一个包的时候,才会有区别,-D安装的包表示在开发中需要用到,-S安装的包表示项目执行的依赖,具体来讲,下载一个包的时候,-S中的包会直接同时被连带下载,而-D安装的包需要手动执行npm i后才会被下载,不过开发项目的时候我们通常都只使用-D
好了,废话不多说,继续

奥对了,如果刚才尝试安装了无关包,可以使用npm uninstall package_name删除包

配置webpack文件

上一步我们下载好了wenpack相关的包,这里我们要给它写一个配置文件

在项目根目录下,新建命名为webpack.config.js的文件,然后在该文件中粘贴这些内容

// webpack.config.js
const path = require('path')

module.exports = {
  mode: 'development',
  entry: {
    main: './src/index.js'
  },
  output: {
    filename: 'bundle.js',
    path: path.join(__dirname, '/dist')
  }
}

这就是一个最基本的webpack配置文件
mode表示当前的开发模式,通常值就为development
entry表示入口,即告诉wenpack从哪个文件开始解析
output表示出口,即告诉webpack解析后的文件输出命名、路径的问题

然后执行打包命令

npx webpack

正如刚才所说,npx会找到项目中webpack的可执行文件,然后webpack会在项目根目录中找到名为webpack.config.js文件,并根据文件中的配置打包我们的项目,我们的配置文件里声明了入口文件为./src目录下的index.js文件,出口文件名为bundle.js并且路径为项目目录中的./dist目录,所以我们会看到,项目中自动生成./dist文件夹,并且里边有个bundle.js,它就是打包好的index.js
到这里,我们就完成了项目的第一次打包了,还是很激动的

html-webpack-plugin

这是一个自动生成html的插件
一个前端的项目怎么可能没有.html文件,当然了,经过前边的一番折腾我们对这个项目的原理也有了一些了解,我大可以在./dist目录里自己创建一个index.html,然后用script标签引入bundle.js文件,这样每当我们写完一波代码,一个npx webpack命令就可以将代码打包到./dist文件夹里,非常方便
但是这样会存在很多问题,如果bundle.js文件命名被修改,还要修改index.html文件重新引入,或者如果项目里新增了其它标签,也要重新手动引入,所以我们使用html-webpack-plugin插件,来让index.html文件自动生成并自动引入其他文件

首先安装该插件

npm install html-webpack-plugin -D

然后还是webpack.config.js文件,新增导入插件、声明插件

// webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  mode: 'development',
  entry: {
    main: './src/index.js'
  },
  output: {
    filename: 'bundle.js',
    path: path.join(__dirname, '/dist'),
    clean: true
  },
  plugins: [new HtmlWebpackPlugin({})]
}

plugins字段的意思就是告诉webpack我们要用到的插件
这里还有一个需要注意的地方,即webpack在编译的时候是基于node的,因此导入文件只能使用node标准的commomJS,不能使用import,不懂得同学自行搜索二者区别

然后再次执行打包

npx webpack

可以看到打包后的文件结构就变成了这样
打包后的./dist目录
太好了,配了这么久终于见到index.html文件了
我们直接在浏览器打开这个打包好的index.html文件,F12调试发现确实输出了hello word
浏览器输出

这也说明html-webpack-plugin这个插件可以从webpack.config.js中的配置识别出哪些文件是index.html文件需要引入的,并且自动生成一个html文件

当然,这个插件的强大之处远不止这些,用到的话我们后边再说

配置ts

一个合格的前端开发人当然不会用js写自己的react项目,我说的没错吧嘿嘿
为了跟上时代潮流,为了用到类型检查、泛型等一些列让自己代码更健壮的强大功能,我们当然还是要用ts来写自己的项目
所以直接将./src中的index.js文件改名为index.tsindex.tsx(.ts.tsx的关系其实和.js.jsx的关系是一样的,不明白的同学请查阅React官方文档)
但是!!!,直接改名是不行的,打包的时候是报错的,因为webpack不是万能的,它并不能识别并且编译ts文件
因此我们要下载一些新的包,并且请出webpack配置里最好玩的配置module

首先下载两个包typescript和ts-loader

npm install typescript ts-loader -D

typescript是编译ts的核心包,不用过多解释
ts-loader是告诉webapck怎么解析编译ts的包
日后我们在webpack的学习和使用中会经常碰到类似什么什么-loader的形式的包,它们的功能都有类似之处,就是告诉webpack怎么解析某种类型的文件

再回到webpack.config.js文件

// webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  mode: 'development',
  entry: {
    main: './src/index.js'
  },
  output: {
    filename: 'bundle.js',
    path: path.join(__dirname, '/dist'),
    clean: true
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/
      }
    ]
  },
  plugins: [new HtmlWebpackPlugin({})]
}

我们新增了一个module.rules,并配置了相应的配置项
这个配置的意思是,告诉webpack除了node_modules文件夹下的内容,所有.tsx或者.ts后缀的文件,统统用ts-loader处理,我这么一说,你就知道这几个配置项是什么意思了吧

当然了ts的编译也是需要配置文件的,由于我们已经下载了typescript,直接用命令行生成ts配置文件

npx tsc --init

执行完会看到项目目录中又生成了一个tsconfig.json文件,该文件的具体配置和相关解释也请查看tsconfig.json官方文档
不过现在,最简单的配置内容我也帮你写好了,就是这样

{
  "compilerOptions": {
    "module": "ES2015",
    "removeComments": true,
    "sourceMap": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

这下,再打包一次

npx webpack

完美!没有任何问题

配置react

配置了这么半天,搞得我都差点忘了我们的最终目的是配置react,前期有多麻烦,后期就有多方便,不然我们何必费这么大劲呢

第一步还是下载包

npm install react react-dom @types/react-dom @types/react -D

reactreact-dom还是与webpackwepack-cli的原理差不多,一个集成了核心功能,一个集成了接口调用,什么?你问我那为什么不直接集成统一为一个包呢,当然不行,因为有的脚手架只用到核心包,而调用包会自己重新有自己的一套,就比如vue中的webpack就用的是vue-cli
至于@types/react-dom@types/react也很简单啦,就是reactreact-dom是基于js的,想要在ts中顺利使用,需要这两个接口类型包进行一个过滤

下载好之后我们简单搭建一个react代码框架
新建./src/pages/App.tsx

import React from 'react'

const App: React.FC = () => {
  return <div className="test">Hello world</div>
}

export default App

./src/index.tsx内容改为

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './pages/App'

const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
)

打包

npx webpack

成功!!!
等等,但是我明明再App组件里声明了一个节点,为什么调试浏览器里并没有显示呢?
原因很简单啦,我们再这里创建了一个idroot的节点
在这里插入图片描述
但是反过来检查我们生成的index.html文件
在这里插入图片描述
压根就没有什么类似<div id="root"><div>的东西,当然不可能显示
这里当然还要说明一下,前面说到过html-webpack-plugin插件神通广大,但是也不可能神通广大到自动给你生成一个类似<div id="root"><div>的东西,它并不能深入到每一个文件里去细纠哪里创建了根节点,再者说html-webpack-plugin插件也不是为react一家服务的

通常我们会用模板index.html来解决这个问题
在项目根目录新./public目录,在该目录中新建一个index.html文件,内容就是最基本的内容
但是要添加一个idroot的节点

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>test</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

修改`webpack.json文件的内容

在这里插入图片描述
这里就是告诉webpack我们以public里的index.html文件为模板来自动生成新的html文件,同时也可以在public下的index.html文件中提前写好title标签,也会自动打包到新生成的html文件中
当然了,新建的这个public也不是仅仅用来存放index.html的模板文件的,我们的网站Logo以及favivon.ico图标文件都可以放到这里,然后通过webpack打包到项目中,具体操作可以去webpack官方文档查看
我就习惯自己先找一个favivon.ico文件放到public目录下
在这里插入图片描述

然后通过html-webpack-plugin打包
在这里插入图片描述
这次打包后页面就成功显示了我们的hello world!!!

支持css

其实这一步的原理之前已经讲过了,直接上代码

安装包

npm install css-loader mini-css-extract-plugin -D

webpack.config.js文件的module字段

 module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader'],
        exclude: /node_modules/
      }
    ]
  },

具体的意思网上都有,不做赘述,总的来说就是支持css,并且生成单独的css打包文件,而不是集成在js文件里

优化

讲了这么多,给大家看一下我做一个项目之前webpack.config.js文件的样子是什么样子的

// node自带的path,可以进行路径处理相关的操作
const path = require('path')
// HtmlWebpackPlugin插件,自动生成index.html文件
const HtmlWebpackPlugin = require('html-webpack-plugin')
// MiniCssExtractPlugin插件,用来生成单独的css文件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
  // 模式,当前为调试模式
  mode: 'development',
  // 启用source-map,方便调试定位
  devtool: 'source-map',
  // 入口,通常只有一个
  entry: {
    main: './src/index.tsx'
  },
  // 出口,为打包好的文件配置输出路径,命名等
  output: {
    filename: '[name].[contenthash:6].js',
    path: path.join(__dirname, '/dist'),
    clean: true
  },
  // 插件
  plugins: [
    new HtmlWebpackPlugin({
      favicon: path.join(__dirname, '/public/favicon.ico'),
      template: './public/index.html'
    }),
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash:6].css'
    })
  ],
  // 对不同模块的处理方式
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader'],
        exclude: /node_modules/
      }
    ]
  },
  // 声明的后缀,引入的时候不用加后缀
  resolve: {
    extensions: ['.ts', '.tsx', '.js']
  }
}

相比与之前讲到的内容,我还有这些优化

  • devtool: 'source-map'方便调试
  • 输出的命名都采用内容哈希,便于刷新
  • clean:true 输出时先清除打包文件里之前的内容
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值