npm install mini-css-extract-plugin --save-dev
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
+const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
mode: 'development',
devtool: false,
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
+ publicPath: '/'
},
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' },
+ { test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'] },
+ { test: /\.less$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader'] },
+ { test: /\.scss$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'] },
{
test: /\.(jpg|png|gif|bmp|svg)$/,
type:'asset/resource',
generator:{
filename:'images/[hash][ext]'
}
}
]
},
plugins: [
new HtmlWebpackPlugin({ template: './src/index.html' }),
+ new MiniCssExtractPlugin({
+ filename: '[name].css'
+ })
]
};
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
mode: 'development',
devtool: false,
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
publicPath: '/'
},
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' },
{ test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'] },
{ test: /\.less$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader'] },
{ test: /\.scss$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'] },
{
test: /\.(jpg|png|gif|bmp|svg)$/,
type:'asset/resource',
generator:{
filename:'images/[hash][ext]'
}
}
]
},
plugins: [
new HtmlWebpackPlugin({ template: './src/index.html' }),
new MiniCssExtractPlugin({
+ filename: 'css/[name].css'
}),
]
};
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
+const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
+const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
+ mode: 'none',
devtool: false,
entry: './src/index.js',
+ optimization: {
+ minimize: true,
+ minimizer: [
+ new TerserPlugin(),
+ ],
+ },
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
publicPath: '/',
},
devServer: {
contentBase: path.resolve(__dirname, 'dist'),
compress: true,
port: 8080,
open: true,
},
module: {
rules: [
{
test: /\.jsx?$/,
loader: 'eslint-loader',
enforce: 'pre',
options: { fix: true },
exclude: /node_modules/,
},
{
test: /\.jsx?$/,
use: {
loader: 'babel-loader',
options: {
presets: [[
'@babel/preset-env',
{
useBuiltIns: 'usage'
corejs: {
version: 3
},
targets: {
chrome: '60',
firefox: '60',
ie: '9',
safari: '10',
edge: '17',
},
},
], '@babel/preset-react'],
plugins: [
['@babel/plugin-proposal-decorators', { legacy: true }],
['@babel/plugin-proposal-class-properties', { loose: true }],
],
},
},
include: path.join(__dirname, 'src'),
exclude: /node_modules/,
},
{ test: /\.txt$/, use: 'raw-loader' },
{ test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'] },
{ test: /\.less$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'less-loader'] },
{ test: /\.scss$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader'] },
{
test: /\.(jpg|png|gif|bmp|svg)$/,
type:'asset/resource',
generator:{
filename:'images/[hash][ext]'
}
},
{
test: /\.html$/,
loader: 'html-loader',
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
+ minify: {
+ collapseWhitespace: true,
+ removeComments: true
}
}),
new MiniCssExtractPlugin({
filename: 'css/[name].css',
}),
+ new OptimizeCssAssetsWebpackPlugin(),
],
};
mini-css-extract-plugin
配合使用paths
路径是绝对路径 npm i purgecss-webpack-plugin mini-css-extract-plugin css-loader glob -D
webpack.config.js
const path = require("path");
+const glob = require("glob");
+const PurgecssPlugin = require("purgecss-webpack-plugin");
+const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const PATHS = {
src: path.join(__dirname, 'src')
}
module.exports = {
mode: "development",
entry: "./src/index.js",
module: {
rules: [
{
test: /\.js/,
include: path.resolve(__dirname, "src"),
use: [
{
loader: "babel-loader",
options: {
presets: ["@babel/preset-env", "@babel/preset-react"],
},
},
],
},
+ {
+ test: /\.css$/,
+ include: path.resolve(__dirname, "src"),
+ exclude: /node_modules/,
+ use: [
+ {
+ loader: MiniCssExtractPlugin.loader,
+ },
+ "css-loader",
+ ],
+ },
],
},
plugins: [
+ new MiniCssExtractPlugin({
+ filename: "[name].css",
+ }),
+ new PurgecssPlugin({
+ paths: glob.sync(`${PATHS.src}/**/*`, { nodir: true }),
+ })
],
};
style.css
#root{
color: red;
}
#logo{
color:green
}
<link rel="dns-prefetch" href="http://img.zhufengpeixun.cn">
去预解析域名,以降低域名解析带来的延迟指纹占位符
占位符名称 | 含义 |
---|---|
ext | 资源后缀名 |
name | 文件名称 |
path | 文件的相对路径 |
folder | 文件所在的文件夹 |
hash | 每次webpack构建时生成一个唯一的hash值 |
chunkhash | 根据chunk生成hash值,来源于同一个chunk,则hash值就一样 |
contenthash | 根据内容生成hash值,文件内容相同hash值就相同 |
const path = require("path");
const glob = require("glob");
const PurgecssPlugin = require("purgecss-webpack-plugin");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const PATHS = {
src: path.join(__dirname, 'src')
}
module.exports = {
mode: "production",
+ entry: {
+ main: './src/index.js',
+ vender:['lodash']
+ },
output:{
path:path.resolve(__dirname,'dist'),
+ filename:'[name].[hash].js'
},
devServer:{
hot:false
},
module: {
rules: [
{
test: /\.js/,
include: path.resolve(__dirname, "src"),
use: [
{
loader:'thread-loader',
options:{
workers:3
}
},
{
loader: "babel-loader",
options: {
presets: ["@babel/preset-env", "@babel/preset-react"],
},
},
],
},
{
test: /\.css$/,
include: path.resolve(__dirname, "src"),
exclude: /node_modules/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
"css-loader",
],
},
],
},
plugins: [
new MiniCssExtractPlugin({
+ filename: "[name].[hash].css"
}),
new PurgecssPlugin({
paths: glob.sync(`${PATHS.src}/**/*`, { nodir: true }),
}),
],
};
const path = require("path");
const glob = require("glob");
const PurgecssPlugin = require("purgecss-webpack-plugin");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const PATHS = {
src: path.join(__dirname, 'src')
}
module.exports = {
mode: "production",
entry: {
main: './src/index.js',
vender:['lodash']
},
output:{
path:path.resolve(__dirname,'dist'),
+ filename:'[name].[chunkhash].js'
},
devServer:{
hot:false
},
module: {
rules: [
{
test: /\.js/,
include: path.resolve(__dirname, "src"),
use: [
{
loader:'thread-loader',
options:{
workers:3
}
},
{
loader: "babel-loader",
options: {
presets: ["@babel/preset-env", "@babel/preset-react"],
},
},
],
},
{
test: /\.css$/,
include: path.resolve(__dirname, "src"),
exclude: /node_modules/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
"css-loader",
],
},
],
},
plugins: [
new MiniCssExtractPlugin({
+ filename: "[name].[chunkhash].css"
}),
new PurgecssPlugin({
paths: glob.sync(`${PATHS.src}/**/*`, { nodir: true }),
}),
],
};
mini-css-extract-plugin
里的contenthash
值,保证即使css文件所处的模块里就算其他文件内容改变,只要css文件内容不变,那么不会重复构建const path = require("path");
const glob = require("glob");
const PurgecssPlugin = require("purgecss-webpack-plugin");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const PATHS = {
src: path.join(__dirname, 'src')
}
module.exports = {
mode: "production",
entry: {
main: './src/index.js',
vender:['lodash']
},
output:{
path:path.resolve(__dirname,'dist'),
filename:'[name].[chunkhash].js'
},
devServer:{
hot:false
},
module: {
rules: [
{
test: /\.js/,
include: path.resolve(__dirname, "src"),
use: [
{
loader:'thread-loader',
options:{
workers:3
}
},
{
loader: "babel-loader",
options: {
presets: ["@babel/preset-env", "@babel/preset-react"],
},
},
],
},
{
test: /\.css$/,
include: path.resolve(__dirname, "src"),
exclude: /node_modules/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
"css-loader",
],
},
],
},
plugins: [
new MiniCssExtractPlugin({
+ filename: "[name].[contenthash].css"
}),
new PurgecssPlugin({
paths: glob.sync(`${PATHS.src}/**/*`, { nodir: true }),
}),
],
};
function createHash(){
return require('crypto').createHash('md5');
}
let entry = {
entry1:'entry1',
entry2:'entry2'
}
let entry1 = 'require depModule1';//模块entry1
let entry2 = 'require depModule2';//模块entry2
let depModule1 = 'depModule1';//模块depModule1
let depModule2 = 'depModule2';//模块depModule2
//如果都使用hash的话,因为这是工程级别的,即每次修改任何一个文件,所有文件名的hash至都将改变。所以一旦修改了任何一个文件,整个项目的文件缓存都将失效
let hash = createHash()
.update(entry1)
.update(entry2)
.update(depModule1)
.update(depModule2)
.digest('hex');
console.log('hash',hash)
//chunkhash根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的哈希值。
//在生产环境里把一些公共库和程序入口文件区分开,单独打包构建,接着我们采用chunkhash的方式生成哈希值,那么只要我们不改动公共库的代码,就可以保证其哈希值不会受影响
let entry1ChunkHash = createHash()
.update(entry1)
.update(depModule1).digest('hex');;
console.log('entry1ChunkHash',entry1ChunkHash);
let entry2ChunkHash = createHash()
.update(entry2)
.update(depModule2).digest('hex');;
console.log('entry2ChunkHash',entry2ChunkHash);
let entry1File = entry1+depModule1;
let entry1ContentHash = createHash()
.update(entry1File).digest('hex');;
console.log('entry1ContentHash',entry1ContentHash);
let entry2File = entry2+depModule2;
let entry2ContentHash = createHash()
.update(entry2File).digest('hex');;
console.log('entry2ContentHash',entry2ContentHash);
class HashPlugin{
constructor(options){
this.options = options;
}
apply(compiler){
compiler.hooks.compilation.tap('HashPlugin',(compilation,params)=>{
//如果你想改变hash值,可以在hash生成这后修改
compilation.hooks.afterHash.tap('HashPlugin',()=>{
let fullhash = 'fullhash';//时间戳
console.log('本次编译的compilation.hash',compilation.hash);
compilation.hash= fullhash;//output.filename [fullhash]
for(let chunk of compilation.chunks){
console.log('chunk.hash',chunk.hash);
chunk.renderedHash = 'chunkHash';//可以改变chunkhash
console.log('chunk.contentHash',chunk.contentHash);
chunk.contentHash= { javascript: 'javascriptContentHash','css/mini-extract':'cssContentHash' }
}
});
});
}
}
module.exports = HashPlugin;
/**
* 三种hash
* 1. hash compilation.hash
* 2. chunkHash 每个chunk都会有一个hash
* 3. contentHash 内容hash 每个文件会可能有一个hash值
*/
webpack.config.js
const path = require('path');
const DonePlugin = require('./plugins/DonePlugin');
const AssetPlugin = require('./plugins/AssetPlugin');
const ZipPlugin = require('./plugins/ZipPlugin');
const HashPlugin = require('./plugins/HashPlugin');
const AutoExternalPlugin = require('./plugins/AutoExternalPlugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
+ MiniCssExtractPlugin.loader,
'css-loader'
]
}
]
},
plugins: [
+ new HashPlugin(),
]
}
确定性
的方式将短数字 ID(3 或 4 个字符)短hash值分配给 modules 和 chunks可选值 | 含义 | 示例 |
---|---|---|
natural | 按使用顺序的数字ID | 1 |
named | 方便调试的高可读性id | src_two_js.js |
deterministic | 根据模块名称生成简短的hash值 | 915 |
size | 根据模块大小生成的数字id | 0 |
webpack.config.js
const path = require('path');
module.exports = {
mode: 'development',
devtool:false,
+ optimization:{
+ moduleIds:'deterministic',
+ chunkIds:'deterministic'
+ }
}
src\index.js
import('./one');
import('./two');
import('./three');
remote
, 引用者被称为host
,remote
暴露模块给host
, host
则可以使用这些暴露的模块,这些模块被成为remote
模块字段 | 类型 | 含义 |
---|---|---|
name | string | 必传值,即输出的模块名,被远程引用时路径为${name}/${expose} |
library | object | 声明全局变量的方式,name为umd的name |
filename | string | 构建输出的文件名 |
remotes | object | 远程引用的应用名及其别名的映射,使用时以key值作为name |
exposes | object | 被远程引用时可暴露的资源路径及其别名 |
shared | object | 与其他应用之间可以共享的第三方依赖,使你的代码中不用重复加载同一份依赖 |
let path = require("path");
let webpack = require("webpack");
let HtmlWebpackPlugin = require("html-webpack-plugin");
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
publicPath: "http://localhost:3000/",
},
devServer: {
port: 3000
},
module: {
rules: [
{
test: /\.jsx?$/,
use: {
loader: 'babel-loader',
options: {
presets: ["@babel/preset-react"]
},
},
exclude: /node_modules/,
},
]
},
plugins: [
new HtmlWebpackPlugin({
template:'./public/index.html'
}),
new ModuleFederationPlugin({
filename: "remoteEntry.js",
name: "remote",
exposes: {
"./NewsList": "./src/NewsList",
}
})
]
}
remote\src\index.js
import("./bootstrap");
remote\src\bootstrap.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
ReactDOM.render(<App />, document.getElementById("root"));
remote\src\App.js
import React from "react";
import NewsList from './NewsList';
const App = () => (
<div>
<h2>本地组件NewsList</h2>
<NewsList />
</div>
);
export default App;
remote\src\NewsList.js
import React from "react";
export default ()=>(
<div>新闻列表</div>
)
host\webpack.config.js
let path = require("path");
let webpack = require("webpack");
let HtmlWebpackPlugin = require("html-webpack-plugin");
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
publicPath: "http://localhost:8000/",
},
devServer: {
port: 8000
},
module: {
rules: [
{
test: /\.jsx?$/,
use: {
loader: 'babel-loader',
options: {
presets: ["@babel/preset-react"]
},
},
exclude: /node_modules/,
},
]
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html'
}),
new ModuleFederationPlugin({
filename: "remoteEntry.js",
name: "host",
remotes: {
remote: "remote@http://localhost:3000/remoteEntry.js"
}
})
]
}
host\src\index.js
import("./bootstrap");
host\src\bootstrap.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
ReactDOM.render(<App />, document.getElementById("root"));
host\src\App.js
import React from "react";
import Slides from './Slides';
const RemoteNewsList = React.lazy(() => import("remote/NewsList"));
const App = () => (
<div>
<h2 >本地组件Slides, 远程组件NewsList</h2>
<Slides />
<React.Suspense fallback="Loading NewsList">
<RemoteNewsList />
</React.Suspense>
</div>
);
export default App;
host\src\Slides.js
import React from "react";
export default ()=>(
<div>轮播图</div>
)
shared
配置主要是用来避免项目出现多个公共依赖 plugins: [
new HtmlWebpackPlugin({
template:'./public/index.html'
}),
new ModuleFederationPlugin({
filename: "remoteEntry.js",
name: "remote",
exposes: {
"./NewsList": "./src/NewsList",
},
+ shared:{
+ react: { singleton: true },
+ "react-dom": { singleton: true }
+ }
})
]
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html'
}),
new ModuleFederationPlugin({
filename: "remoteEntry.js",
name: "host",
remotes: {
remote: "remote@http://localhost:3000/remoteEntry.js"
},
+ shared:{
+ react: { singleton: true },
+ "react-dom": { singleton: true }
+ }
})
]
plugins: [
new HtmlWebpackPlugin({
template:'./public/index.html'
}),
new ModuleFederationPlugin({
filename: "remoteEntry.js",
name: "remote",
+ remotes: {
+ host: "host@http://localhost:8000/remoteEntry.js"
+ },
exposes: {
"./NewsList": "./src/NewsList",
},
shared:{
react: { singleton: true },
"react-dom": { singleton: true }
}
})
]
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html'
}),
new ModuleFederationPlugin({
filename: "remoteEntry.js",
name: "host",
remotes: {
remote: "remote@http://localhost:3000/remoteEntry.js"
},
+ exposes: {
+ "./Slides": "./src/Slides",
+ },
shared:{
react: { singleton: true },
"react-dom": { singleton: true }
}
})
]
remote\src\App.js
import React from "react";
import NewsList from './NewsList';
+const RemoteSlides = React.lazy(() => import("host/Slides"));
const App = () => (
<div>
+ <h2>本地组件NewsList,远程组件Slides</h2>
<NewsList />
+ <React.Suspense fallback="Loading Slides">
+ <RemoteSlides />
+ </React.Suspense>
</div>
);
export default App;
let path = require("path");
let webpack = require("webpack");
let HtmlWebpackPlugin = require("html-webpack-plugin");
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
publicPath: "http://localhost:3000/",
},
devServer: {
port: 5000
},
module: {
rules: [
{
test: /\.jsx?$/,
use: {
loader: 'babel-loader',
options: {
presets: ["@babel/preset-react"]
},
},
exclude: /node_modules/,
},
]
},
plugins: [
new HtmlWebpackPlugin({
template:'./public/index.html'
}),
new ModuleFederationPlugin({
filename: "remoteEntry.js",
name: "all",
remotes: {
remote: "remote@http://localhost:3000/remoteEntry.js",
host: "host@http://localhost:8000/remoteEntry.js",
},
shared:{
react: { singleton: true },
"react-dom": { singleton: true }
}
})
]
}
remote\src\index.js
import("./bootstrap");
remote\src\bootstrap.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
ReactDOM.render(<App />, document.getElementById("root"));
remote\src\App.js
import React from "react";
const RemoteSlides = React.lazy(() => import("host/Slides"));
const RemoteNewsList = React.lazy(() => import("remote/NewsList"));
const App = () => (
<div>
<h2>远程组件Slides,远程组件NewsList</h2>
<React.Suspense fallback="Loading Slides">
<RemoteSlides />
</React.Suspense>
<React.Suspense fallback="Loading NewsList">
<RemoteNewsList />
</React.Suspense>
</div>
);
export default App;