笔者所处环境
node:v14.2.0
npm:6.14.4
搭建一个webpack项目分为如下几个步骤:
1. 创建一个项目目录,并进行npm初始化
2. 基础项目目录创建
3. 基础依赖包安装
4. webpack基础配置
5. package配置
6. 运行并打包项目
1、创建一个项目目录,并进行npm初始化
执行如下命令:
mkdir webpack-test // 创建一个目录
cd webpack-test
npm init -y // npm初始化,生成package文件
2、基础项目目录创建
创建基础的项目目录结构,包括public、scripts、src三个基础的项目目录。
(1)public下的index.html文件是项目所被访问到的页面,里面创建有个div用于后面react的挂载
(2)scripts是webpack的基础配置文件夹,下文会被谈到内部各个文件的作用
(3)src是项目的入口文件,也就是我们在package.json里面的main字段指向的路径,默认为src/index.js
3、基础依赖包安装
在package.json里面我们可以通过dependencies、devDependencies、peerDependencies和optionalDependencies区管理区分我们安装的包,合理的安装包更有利于项目的健壮性,同时也避免了一些开发环境的包使项目打包体积变大。
(1)dependencies:应用依赖,或者叫做业务依赖/生产环境依赖,这是我们最常用的依赖包管理对象!它用于指定应用依赖的外部包,这些依赖是应用发布后正常执行时所需要的,但不包含测试时或者本地打包时所使用的包
(2)devDependencies:开发环境依赖,仅次于dependencies的使用频率!它的作用和dependencies一样,只不过它里面的包只用于开发环境,不用于生产环境,这些包通常是单元测试或者打包工具等,(如:webpack、loader、plugins等打包过程所需要的依赖)
(3)peerDependencies:常常用于开发依赖包时使用,在此目录下的依赖可以依靠宿主项目的依赖进行运作,这样可以避免重复装包所造成版本不一致的问题
(4)optionalDependencies:仅在使用特定功能时才需要包,请使用可选的对等依赖项
安装开发环境使用的包:
核心依赖:webpack、webpack-cli、webpack-dev-server、webpack-merge、babel等
loader依赖:
(1)babel-loader 用于处理js、jsx文件
(2)style-loader、css-loader、less-loader、postcss-loader、postcss-preset-env 用于css加载,less文件加载
(3)url-loader 用于处理图片和字体图标
插件:
(1)html-webpack-plugin、react-dev-utils 使能html使用模板语法
(2)optimize-css-assets-webpack-plugin css兼容优化处理
(3)react-refresh、@pmmmwh/react-refresh-webpack-plugin 引入热更新
(4)@babel/plugin-proposal-decorators 引入装饰器,项目支持装饰器写法
(5)css-minimizer-webpack-plugin、terser-webpack-plugin css压缩和js压缩(用于生产打包使用)
以下指令逐一指向进行开发依赖的安装,–save-dev 保存为开发依赖
npm i webpack webpack-cli webpack-dev-server webpack-merge --save-dev
npm i babel-loader @babel/core @babel/preset-flow babel-preset-react-app --save-dev
npm i style-loader css-loader less-loader postcss-loader postcss-preset-env url-loader --save-dev
npm i html-webpack-plugin react-dev-utils optimize-css-assets-webpack-plugin @pmmmwh/react-refresh-webpack-plugin @babel/plugin-proposal-decorators --save-dev
npm i css-minimizer-webpack-plugin terser-webpack-plugin --save-dev
安装生产环境使用的包:react、react-dom
npm i react react-dom --save-dev
4、基础依赖包安装
编写scripts文件代码,主要为start文件,用于运行开发环境、打包项目的基础脚本文件;webpack.common.js是生产、开发环境共用的基础配置;webpack.dev.js是开发环境webpack配置,webpack.prod.js是开发环境webpack配置
(1)webpack.common.js基础配置
module.exports = {
resolve: {
alias: { // 别名,可以通过别名快速访问到指定路径
"~": "./src",
"react-native": "react-native-web",
},
// 支持的拓展文件
extensions: [".js", ".css", ".jsx", ".json", ".web.jsx", ".web.js", ".mjs"],
// node核心模块加载
fallback: {
process: false,
},
},
};
(2)webpack.dev.js基础配置
const path = require('path');
const fs = require('fs');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
const publicPath = '/';
module.exports = {
mode: process.env.NODE_ENV,
target: 'web',
devtool: 'cheap-module-source-map',
// 入口
entry: {
app: './src/index.js'
},
// 输出
output: {
clean: true,
filename: 'static/js/bundle.js',
publicPath: publicPath,
devtoolModuleFilenameTemplate: info =>
path.resolve(info.absoluteResourcePath).replace(/\\/g, '/'),
},
module: {
rules: [
// 处理图片和字体图标
{
test: /\.(png|gif|jpg|jpeg|woff|woff2|eot|ttf|otf|svg)$/,
type: 'javascript/auto',
use: [
{
loader: 'url-loader',
options: {
esModule: false,
limit: 1000,
name: '[name].[hash:8].[ext]',
}
}
]
},
// 处理jsx文件
{
test: /.jsx|.js?$/,
exclude: /node_modules/,
include: path.join(__dirname, '../src'),
use: [
{
loader: 'babel-loader',
options: {
cacheDirectory: true,
}
}
],
},
// 匹配css文件
{
test: /.css$/,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => {
require('postcss-preset-env')()
}
}
}
]
},
// 匹配less文件
{
test: /.less$/,
use: [
'style-loader',
'css-loader',
{
loader: "less-loader",
options: {
modifyVars: { },
lessOptions: {
javascriptEnabled: true
},
}
}
]
}
]
},
plugins: [
// html文件中引入的外部资源,生成创建html入口文件
new HtmlWebpackPlugin({ filename: 'index.html', inject: 'body', template: resolveApp('public/index.html') }),
// 解决html文件中标签语法问题
new InterpolateHtmlPlugin(HtmlWebpackPlugin, process.env),
// css兼容优化
new OptimizeCssAssetsWebpackPlugin(),
// 解决process无法获取问题
new webpack.ProvidePlugin({ process: 'process' }),
// 进度条
new webpack.ProgressPlugin({ percentBy: 'entries' }),
// 热更新
new webpack.HotModuleReplacementPlugin(),
new ReactRefreshPlugin({
forceEnable: true,
exclude: [/node_modules/, /bootstrap\.jsx$/],
overlay: false //修改了错误叠加集成在插件中的工作方式,false则不显示报错
}),
],
optimization: {
usedExports: true,
providedExports: true,
},
devServer: {
open: true,
historyApiFallback: true, // 使用history模式需要进行开启
hot: true,
port: 3000,
host: "localhost",
proxy: {
"/api": {
target: "https://localhost:3001",
secure: false,
changeOrigin: true
}
}
}
}
(3)webpack.prod.js基础配置
const path = require('path');
const fs = require('fs');
const webpack = require('webpack');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const TerserPlugin = require("terser-webpack-plugin");
const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
const publicPath = '/';
const staticName = './';
module.exports = {
mode: process.env.NODE_ENV,
bail: true,
entry: './src/index.js',
// 输出
output: {
// path: './dist',
filename: staticName + `/main.${process.env.NODE_ENV}.js`,
// 公共路径.
publicPath: publicPath,
},
module: {
rules: [
// 处理图片和处理字体图标
{
test: /\.(png|gif|jpg|jpeg|woff|woff2|eot|ttf|otf|svg)$/,
type: 'javascript/auto',
use: [
{
loader: 'url-loader',
options: {
esModule: false,
limit: 1000 * 1024, // 小于200k使用base64进行图片处理
name: staticName + '/media/[name].[hash:8].[ext]',
}
}
]
},
// 处理jsx文件
{
test: /\.(js|jsx|mjs)$/,
loader: "babel-loader",
exclude: /node_modules/,
include: path.join(__dirname, '../src'),
options: {
compact: true,
},
},
// 匹配css文件
{
test: /.css$/,
exclude: path.resolve(__dirname, 'node_modules'),
use: [
"style-loader",
"css-loader",
{
loader: "postcss-loader",
options: {
ident: 'postcss',
plugins: () => {
require('postcss-preset-env')()
}
}
}
]
},
// 匹配less文件
{
test: /.less$/,
exclude: path.resolve(__dirname, 'node_modules'),
use: [
"style-loader",
"css-loader",
{
"loader": "less-loader",
"options": {
"modifyVars": { },
"lessOptions": {
"javascriptEnabled": true
},
}
}
]
}
]
},
plugins: [
// css兼容优化
new OptimizeCssAssetsWebpackPlugin(),
// 解决process无法获取问题
new webpack.ProvidePlugin({ process: 'process' }),
// 进度条
new webpack.ProgressPlugin({ percentBy: 'entries' }),
],
optimization: {
// 告知 webpack使用TerserPlugin插件压缩
minimize: true,
// 确定那些由模块提供的导出内容
providedExports: true,
// 决定每个模块使用的导出内容
usedExports: true,
// 寻找模块图形中的片段,哪些是可以安全地被合并到单一模块中
concatenateModules: true,
// 告知 webpack 去辨识 package.json 中的 副作用 标记或规则,以跳过那些当导出不被使用且被标记不包含副作用的模块
//识别package.json中的sideEffects以剔除无用的模块,用来做tree-shake
//依赖于optimization.providedExports和optimization.usedExports
sideEffects: true,
// 告知 webpack 检测或移除这些 chunk
removeEmptyChunks: true,
//取代 new webpack.NoEmitOnErrorsPlugin(),编译错误时不打印输出资源。
emitOnErrors: true,
minimizer: [
new TerserPlugin({
parallel: true,
exclude: /node_modules/,
terserOptions: {
compress: {
warnings: true,
drop_console: true,
drop_debugger: true,
pure_funcs: ['console.log']
}
}
}),
new CssMinimizerPlugin(),
],
}
}
(4)start.js
const { merge } = require('webpack-merge');
const base = require('./webpack.common');
module.exports = function (env) {
const mode = env.NODE_ENV;
// 设置环境
process.env.NODE_ENV = mode;
process.env.BABEL_ENV = mode;
// 选择环境配置
const config = mode === 'production' ? require('./webpack.prod') : require('./webpack.dev');
return merge(base, config)
};
5、package配置
package.json的配置主要有scripts,表示我们需要启动项目需要执行的脚本,在写入执行命令后,scripts的指令将会生成一个脚本存放在node_modules文件的.bin下进行运行。除了scripts我们可以在package里面配置其他的属性配置如babel配置。
(1)scripts配置
在scripts里面加入下面两条指令用于启动和打包项目
"start": "webpack serve --env NODE_ENV=development --config ./scripts/start.js",
"build": "webpack --env NODE_ENV=production --config ./scripts/start.js"
(2)配置babel(也可以在根目录创建.babelrc文件进行配置也可)
"babel": {
"plugins": [
[
"@babel/plugin-proposal-decorators",
{
"legacy": true
}
]
],
"presets": [
"react-app",
"@babel/preset-flow"
]
}
6、运行并打包项目
src/index.js文件写入一个简单的demo测试
import React from "react";
import ReactDOM from "react-dom/client";
const App = () => {
return "Hello Reai 🔥";
};
ReactDOM.createRoot(document.getElementById("root")).render(<App />);
(1)测试开发环境运行项目
运行如下命令
npm run start
结果如下:
(2)测试打包
运行如下命令
npm run build
结果如下: