创建packge.json
npm init -y
安装webpack基本依赖包
npm install webpack webpack-dev-server webpack-cli -D
创建文件
src/index.js, 添加测试代码: console.log(‘hello webpack!’)
在 package.json中添加脚本 “build”: “webpack”
即在终端执行命令 npm run build 时进行webpack打包
在根目录下创建 config 目录,创建config/webpack.config.dev.js 用来进行开发环境的webpack配置
添加webpack基本配置
package.json中修改脚本脚本:
“dev”: “webpack --config ./config/webpack.config.dev.js”
npm run dev 打包测试
开发环境中使用webpack-dev-server插件启动服务
先下载:npm i webpack-dev-server -D
配置devServer:
开发服务器 devServer:用来自动化(自动编译,自动打开浏览器,自动刷新浏览器~~)
安装插件clean-webpack-plugin, 每次打包前删除上次生成的打包目录
npm install clean-webpack-plugin -D
在plugins中配置
创建src/index.html
webpack中添加打包html的配置
安装插件: npm install html-webpack-plugin -D
配置devtool
dev=> devtool: ‘eval-cheap-module-source-map’
test=> devtool: ‘cheap-module-source-map’
prod=> devtool: false
react全家桶(react+react-router+axios+antd+mobx)
先安装react
npm install react react-dom -S
修改index.js,使用react挂在dom
配置支持react的打包构建
要想把react的代码使用webpack编译构建成浏览器可以运行的代码,需要使用babel等工具进行"翻译一下"
(webpack默认只能处理js和json,其他类型的文件需要通过loader和plugins来处理)
Babel的作用:
1.将 ECMAScript 2015+ 代码转换为 JavaScript 向后兼容版本的代码;
2.转换 JSX 语法
Babel 有两种并行的配置文件方式:
1.项目范围的配置
在根目录下创建 babel.config.json 文件,以及不同扩展名的文件 (.js, .cjs, .mjs)
这里我们创建babel.config.js文件来配置babel
2.相对文件的配置
- .babelrc.json 文件,以及不同扩展名的文件 (.babelrc, .js, .cjs, .mjs)
- 带有 “babel” key 的 package.json 文件
官方预设
@babel/preset-env 用于编译 ES2015+ 语法
@babel/preset-typescript 用于 TypeScript
@babel/preset-react 用于 React
安装Babel
- 安装基础模块:
npm install babel-loader @babel/core @babel/preset-env -D
* Babel 的核心功能容纳于 @babel/core 模块
* babel-loader: 对js做兼容性处理 只能做一些简单的如箭头函数,扩展运算符等的处理
* @babel/preset-env: 对Promise等高级的语法处理
在rules中配置babel-loader
在babel.config.js中添加@babel/preset-env
- 安装预设
npm install @babel/preset-typescript @babel/preset-react -D
方案一:@babel/preset-env + @babel/polyfill (不推荐使用)
方案二:@babel/preset-env + @babel/plugin-transform-runtime + @babel/runtime-corejs3
npm install @babel/plugin-transform-runtime -D
npm install @babel/runtime-corejs3 -S
npm install core-js@3 regenerator-runtime -S
修改babel.config.js中plugins的配置
(
解决babel-loader过慢! 引入 Babel runtime 作为一个独立模块,来避免重复引入
corejs@3 兼容性处理的按需加载
)
运行项目,这时会报错
在webpack配置文件中添加
// 解析
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx', '.json', '.mjs', '.cjs', '.css', '.less'],
},
1.下载loaders
npm install less less-loader style-loader css-loader -D
若想配置得更全面,也可以下载和配置sass和sass-loader
配置 rules,注意这时开发环境和生产环境的配置有区别
2.css兼容性处理:postcss
npm install postcss-loader postcss-preset-env -D
帮postcss找到package.json中browserslist里面的配置,通过配置加载指定的css兼容性样式
“browserslist”: {
// 开发环境 --> 设置node环境变量:process.env.NODE_ENV = development
“development”: [
“last 1 chrome version”,
“last 1 firefox version”,
“last 1 safari version”
],
// 生产环境:默认是看生产环境
“production”: [
“>0.2%”,
“not dead”,
“not op_mini all”
]
}
3.在生产环境中提取js中的css成单独的文件,安装插件 mini-css-extract-plugin
npm install mini-css-extract-plugin -D
在生产环境中引入
const MiniCssExtractPlugin = require(‘mini-css-extract-plugin’);
用MiniCssExtractPlugin.loader替换style-loader
在plugins中配置输出的文件名
webpack5之前我们处理静态资源比如。图片字体之类的资源的时候等,需要用到url-loader,file-loader,raw-loader,webpack5则放弃了这三个loader,这三个loader在github上也停止了更新。
webpack5使用四种新增的资源模块(Asset Modules)替代了这些loader的功能。
asset/resource 将资源分割为单独的文件,并导出url,就是之前的 file-loader的功能.
asset/inline 将资源导出为dataURL(url(data:))的形式,之前的 url-loader的功能.
asset/source 将资源导出为源码(source code). 之前的 raw-loader 功能.
asset 自动选择导出为单独文件或者 dataURL形式(默认为8KB). 之前有url-loader设置asset size limit 限制实现。
npm install copy-webpack-plugin -D
(react+react-router+axios+antd+redux)
npm install react-router-dom -S
npm install axios -S
npm install antd -S
路由配置
路由组件的注册使用 import动态导入语法:能将某个文件单独打包
npm install -D
@babel/plugin-proposal-decorators // 配置对装饰器的支持
@babel/plugin-proposal-class-properties // 支持类属性的插件
@babel/plugin-proposal-async-generator-functions
@babel/plugin-syntax-dynamic-import
配置 optimization 属性
压缩css的插件 npm install css-minimizer-webpack-plugin -D
下载插件:
npm install clean-webpack-plugin webpack-bundle-analyzer -D
clean-webpack-plugin 在打包的时候会删除之前的打包目录
webpack-bundle-analyzer 在打包结束的时候,会启动启动一个服务在浏览器查看打包的大小和包含的内容等
配置 new BundleAnalyzerPlugin()
为了区分开发环境和生产环境,我们将webpack的配置文件分为:
webpack.config.base.js // 是webpack在不同环境的公共配置
webpack.config.dev.js // 开发环境的配置
webpack.config.prod.js // 生产环境的配置
webpack.utils.js // 配置用的工具方法
1.下载webpack合并插件
npm install webpack-merge -D
2.提取webpack的dev和prod配置中公共部分到webpack.config.base
3.利用 const { merge } = require(‘webpack-merge’);
将各自的配置与公共配置merge到一起
const output = merge(
{ fruit: “apple”, color: “red” },
{ fruit: “strawberries” }
);
console.log(output);
// { color: “red”, fruit: “strawberries”}
** webpack.config.base.js**
const webpack = require('webpack');
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CssMinimizerPlugin =require('css-minimizer-webpack-plugin');
const copyWebpackPlugin = require('copy-webpack-plugin');
const { getGlobalConstants } = require('./webpack.utils');
// webpack公共配置
module.exports = {
entry: {
index: resolve('./src/index')
},
output: {
path: resolve(__dirname, '../dist'),
filename: 'js/[name].bundle.[contenthash:8].js', // index.js 被打包出来的文件
chunkFilename: 'js/[name].chunk.[contenthash:8].js',
// assetModuleFilename: 'assets/[hash][ext]', //引用资源文件打包后的文件名
},
module: {
rules: [
{
test: /\.(js|jsx|ts|tsx|mjs|cjs)$/, // 匹配哪些文件
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader',
},
{
test: /\.(css|less)$/,
use: ['style-loader', 'css-loader', {
loader: 'postcss-loader',
options: {
postcssOptions: {
ident: 'postcss',
plugins: () => [ // postcss的插件
require('postcss-preset-env')()
]
}
}
}, {
loader: 'less-loader',
options: {
lessOptions: {
javascriptEnabled: true,
}
}
}],
},
{
test: /\.(png|jpg|jpeg|gif)$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 8 * 1024,
},
},
generator: {
filename: 'assets/images/[hash][ext]',
},
},
{ test: /\.(woff(2)?|eot|ttf|otf)$/, type: 'asset/fonts' },
]
},
plugins: [
new HtmlWebpackPlugin({
template: resolve('./src/index.html'),
favicon: resolve('./src/static/favicon.png'),
minify: { // 对html压缩
collapseWhitespace: true, // 移除空格
removeComments: true // 移除注释
}
}),
// 配置全局的常量
new webpack.DefinePlugin(getGlobalConstants(process.env.NODE_ENV)),
// copy 静态目录的资源
new copyWebpackPlugin({
patterns: [{
from: './src/static', // 从哪个目录
to: '../dist/static', // copy到哪个目录,默认是output到输出路径
globOptions: {
ignore: ['.*']
}
}]
}),
],
// 解析
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx', '.json', '.mjs', '.cjs', '.css', '.less'],
alias: { // 配置别名
'src': resolve('./src'),
'components': resolve('./src/components'),
'assets': resolve('./src/assets'),
'pages': resolve('./src/pages'),
'api': resolve('./src/api'),
}
},
// 优化
optimization: {
runtimeChunk: { name: 'runtime' },
minimizer: ['...', new CssMinimizerPlugin()], // '...' 使用默认值(TerserPlugin)
splitChunks: {
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/](react|react-dom|react-router|react-router-dom|axios|history|scheduler|react-is|prop-types|object-assign|mini-create-react-context|hoist-non-react-statics|resolve-pathname|value-equal|tiny-invariant)[\\/]/,
name: 'vendors',
chunks: 'all',
priority: 100,
},
},
}
},
}
** webpack.config.dev.js**
const path = require('path');
const { merge } = require('webpack-merge');
const common = require('./webpack.config.base');
const devProxy = require('../proxyConfig');
// webpack开发环境配置
module.exports = merge(common, {
mode: 'development',
// 优化
optimization: {
moduleIds: 'named',
chunkIds: 'named',
minimize: false, // 开发环境下不启用压缩
},
devtool: 'eval-cheap-module-source-map',
devServer: { // 开发环境本地启动的服务配置
static: {
directory: path.join(__dirname, '../dist'),
},
// 端口号
port: 3000,
// 自动打开浏览器
// open: true,
// 开启HMR(热模块替换)功能, 当修改了webpack配置,新配置要想生效,必须重新webpack服务
hot: true,
// 服务器代理 --> 解决开发环境跨域问题
proxy: devProxy,
historyApiFallback: true, // 当找不到路径时,默认加载index.html
client: { // 不显示[webpack-dev-server]的log
logging: 'none',
},
},
})
** webpack.config.prod.js**
const { resolve } = require('path');
const { merge } = require('webpack-merge');
const common = require('./webpack.config.base');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
// webpack生产环境配置
module.exports = merge(common, {
mode: 'production',
// output: {
// publicPath: '/' // 所有资源引入时的公共路径前缀
// },
module: {
rules: [
{
test: /\.(css|less)$/,
exclude: /node_modules/,
use: [MiniCssExtractPlugin.loader, 'css-loader', {
loader: 'postcss-loader',
options: {
postcssOptions: {
ident: 'postcss',
plugins: () => [ // postcss的插件
require('postcss-preset-env')()
]
}
}
},{
loader: 'less-loader',
options: {
lessOptions: {
javascriptEnabled: true,
}
}
}
],
},
]
},
plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
// prod模式下分离css文件, 对输出的css文件进行重命名
filename: 'css/[name].bundle.[contenthash:8].css',
chunkFilename: 'css/[name].chunk.[contenthash:8].css',
}),
// new BundleAnalyzerPlugin(),
],
// 优化
optimization: {
moduleIds: 'deterministic',
chunkIds: 'deterministic',
minimize: true, // 生产环境开启压缩
},
})
** webpack.config.utils.js**
const getGlobalConstants = (__ENV__ = 'production') => {
// console.log(1,__ENV__)
const _path = `../envConfig/env.${__ENV__}.js`;
const originalConstants = require(_path);
const appliedConstants = {};
Object.keys(originalConstants).forEach(key => {
appliedConstants[key] = JSON.stringify(originalConstants[key]);
});
return appliedConstants;
};
module.exports = {
getGlobalConstants
}
将 webpack.config文件合并成一个,根据环境变量区分
** webpack.config.js**
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CssMinimizerPlugin =require('css-minimizer-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const copyWebpackPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { resolve, getGlobalConstants } = require('./webpack.utils');
const devServer = require('../devConfig/devServer');
// webpack公共配置
module.exports = (env) => {
const isDev = env.__ENV__ === 'dev';
process.env.NODE_ENV = isDev ? 'development': 'production';
const styleLoader = isDev ? 'style-loader' : MiniCssExtractPlugin.loader;
const config = {
mode: isDev ? 'development': 'production',
entry: {
index: resolve('../src/index')
},
output: {
path: resolve('../dist'),
filename: 'js/[name].bundle.[contenthash:8].js', // index.js 被打包出来的文件
chunkFilename: 'js/[name].chunk.[contenthash:8].js',
// assetModuleFilename: 'assets/[hash][ext]', //引用资源文件打包后的文件名
},
module: {
rules: [
{
test: /\.(js|jsx|ts|tsx|mjs|cjs)$/, // 匹配哪些文件
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader',
},
{
test: /\.(css|less)$/,
use: [styleLoader, 'css-loader', {
loader: 'postcss-loader',
options: {
postcssOptions: {
ident: 'postcss',
plugins: () => [ // postcss的插件
require('postcss-preset-env')()
]
}
}
}, {
loader: 'less-loader',
options: {
lessOptions: {
javascriptEnabled: true,
}
}
}],
},
{
test: /\.(png|jpg|jpeg|gif)$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 8 * 1024,
},
},
generator: {
filename: 'assets/images/[hash][ext]',
},
},
{ test: /\.(woff(2)?|eot|ttf|otf)$/, type: 'asset/fonts' },
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: resolve('../src/index.html'),
favicon: resolve('../src/static/favicon.png'),
minify: { // 对html压缩
collapseWhitespace: true, // 移除空格
removeComments: true // 移除注释
}
}),
// 配置全局的常量
new webpack.DefinePlugin(getGlobalConstants(env.__ENV__)),
// copy 静态目录的资源
new copyWebpackPlugin({
patterns: [{
from: './src/static', // 从哪个目录
to: '../dist/static', // copy到哪个目录,默认是output到输出路径
globOptions: {
ignore: ['.*']
}
}]
}),
],
// 解析
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx', '.json', '.mjs', '.cjs', '.css', '.less'],
alias: { // 配置别名
'src': resolve('../src'),
'components': resolve('../src/components'),
'assets': resolve('../src/assets'),
'pages': resolve('../src/pages'),
'api': resolve('../src/api'),
}
},
devtool: isDev?'eval-cheap-module-source-map':false,
// 优化
optimization: {
moduleIds: isDev?'named':'deterministic',
chunkIds: isDev?'named':'deterministic',
minimize: isDev?false:true, // 开发环境下不启用压缩
runtimeChunk: { name: 'runtime' },
minimizer: ['...', new CssMinimizerPlugin()], // '...' 使用默认值(TerserPlugin)
splitChunks: {
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/](react|react-dom|react-router|react-router-dom|axios|history|scheduler|react-is|prop-types|object-assign|mini-create-react-context|hoist-non-react-statics|resolve-pathname|value-equal|tiny-invariant)[\\/]/,
name: 'vendors',
chunks: 'all',
priority: 100,
},
},
}
},
}
// 配置时区分环境
if(isDev) {
config.devServer = devServer;
}else{
config.plugins.push(
new MiniCssExtractPlugin({
// prod模式下分离css文件, 对输出的css文件进行重命名
filename: 'css/[name].bundle.[contenthash:8].css',
chunkFilename: 'css/[name].chunk.[contenthash:8].css',
})
)
}
return config;
}
** webpack.utils.js**
const path = require('path');
const resolve = _path => path.resolve(__dirname, _path);
const getGlobalConstants = (__ENV__ = 'prod') => {
console.log(1,__ENV__)
const _path = `../envConfig/env.${__ENV__}.js`;
const originalConstants = require(_path);
const appliedConstants = {};
Object.keys(originalConstants).forEach(key => {
appliedConstants[key] = JSON.stringify(originalConstants[key]);
});
return appliedConstants;
};
module.exports = {
resolve,
getGlobalConstants
}
entry 的三种写法
1. string ,单入口 ,打包形成一个chunk,输出一个bundle文件
entry: ‘…/src/index.js’
2. array 多入口, 所有入口文件最终只会形成一个chunk, 输出出去只有一个bundle文件
entry: [’…/src/index.js’, ‘…/src/test.js’ ]
3. object 多入口,有几个入口文件就形成几个chunk,输出几个bundle文件, 此时chunk的名称是 key
entry: {
index: ‘…/src/index.js’,
test: ‘…/src/test.js’
}
(以上方法1、3用的比较多 )
–> 特殊用法
{
// 所有入口文件最终只会形成一个chunk, 输出出去只有一个bundle文件。
index: [’./src/index.js’, ‘./src/count.js’],
// 形成一个chunk,输出一个bundle文件。
add: ‘./src/add.js’
}
output:
public 用于生产环境,所有资源引入时的公共路径的前缀
例如一个图片路径是 ‘img/a.png’ —> ‘/img/a.png’ 以当前服务器地址去补充,去服务器根目录下找img文件夹,再找img下的a.png
publicPath: ‘/’