Webpack-Day2¶
统计信息:字数 22979 阅读46分钟
2021-10-26
讲师的技术细节和配置讲的一般,课程翻车两次,最后一定以官网配置为准,最好自己实践一下
Style-loader 的配置可能更改了,一切以官方文档为准,不用版本需要参考不同的官方文档
{
test: /\.less$/,
use: [
{
loader: "style-loader",
options: {
esModule: true, // css modules
}
},
{
loader: "css-loader",
options: {
modules: true,
}
},
"less-loader"
],
}
详见 github.com/webpack-contrib/css-loader 默认 modules 属性是 false(为了性能),可以设置 true 把CSS单独模块化(便于调试)。
主要内容:
- CSS 的常见 loader,postCSS 做浏览器兼容
- 文件的 loader(字体、图片)
- HTML 插件(html-webpack-plugin)
- sourceMap——帮助我们在开发时进行调试(显示报错位置)生产环境不开启 sourceMap,开发环境默认开启(cheap-inline-source-map)不显示 source-map。线上避免暴露源代码。
- webpack-dev-server 本地开发服务器-socket 实现热更新
post-css¶
作用:自动兼容早期浏览器
安装依赖
npm install -D postcss-loader autoprefixer
更改 CSS 文件的 loader
{
test: /\.css$/,
use: ["style-loader", "css-loader", "postcss-loader"]
}
需要写一个单独的配置文件 postcss.config.js
第一个表示兼容各大浏览器最近的两个大版本浏览器,第二个值市场占有率大于1%的浏览器
require('autoprefixer') 引入的是一个对象(方法),所以直接传递一个对象
module.exports = {
plugins: [
require('autoprefixer')({
overrideBrowserslist: ["last 2 versions", ">1%"]
})
]
};
// 或者写成一般形式
const autoprefixer = require("autoprefixer");
autoprefixer({
overrideBrowerslist: ["last 2 versions", ">1%"]
})
File-loader¶
作用:把文件(模块)从源代码部分,移动到打包的目录
通常是字体图标 txt svg cvs 大图片等文件
import pic from './images/logo.png';
// 实际上 pic 就是文件的路径
var img = new Image();
img.src = pic;
var root = document.getElementById('Root');
root.append(img);
那么我们需要使用 file-loader
{
test: /\.(png|jpe?g|tiff|gif)$/,
use: {
loader: "file-loader",
options: {
name: "[name]-[hash:6].[ext]"
}
}
}
Url-loader 和 file-loader 功能类似,主要支持 limit 参数,不同大小的文件使用不同的处理方法
小体积的图片资源可以转换成 base64 然后直接写入 js 文件
{
test: /\.(png|tiff)$/,
use: {
loader: 'url-loader',
options: {
name: "[name]-[hash:12].[ext]",
outputPath: "images/",
limit: 2 * 1024,
}
}
}
HTML-webpack-plugin¶
插件,用于把源代码中 HTML 更改移动到打包目录中
{
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: "用于传参的部分"
template: "./src/index.html",
filename: "index.html",
})
]
}
模板是源目录中的文件,支持 cjs 进行传参,适用于多个 HTML 传参,filename 是转换后的文件名
source-map¶
是否显示打包后的不同文件的依赖关系
默认生产环境下面不显示(为了性能,源代码保密),开发环境下面显示(为了调试方便)
{
devtool: "source-map"
}
devServer¶
开启本地开发服务器,便于调试
{
devServer: {
contentBase: path.resolved(__dirname, "./dist"), // 本地项目目录
open: true, // 是否自动打开浏览器
port: 8080,
}
}
Mini-css-extract¶
插件,用于抽取 css 到单独的文件中。使用插件时,需要改变 css loader
{
rules: {
test: '/\.css$',
use: [
MiniCssExtractPlugin.loader, // 不使用 style-loader
{
loader: "css-loader",
options: {
modules: true,
}
}
]
},
// ...
plugins: [
new MiniCSSExtract({
filename: "[name].css"
})
],
}
原始课程笔记(结构比较乱)
作业:
webpack⽀持第三⽅字体!!!!!!!
- loader: file-loader:处理静态资源模块:原理是把打包⼊⼝中识别出的资源模块,移动到输出⽬录,并且返回⼀个地址名称
使用场景:就是当我们需要模块,仅仅是从源代码挪移到打包⽬录, 就可以使⽤file-loader来处理,txt,svg,csv,excel,图⽚资 源啦等等
npm install file-loader -D
案例:
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/, //use使⽤⼀个loader可以⽤对象,字符串串,两个loader需要⽤数组
use: {
loader: "file-loader",
// options额外的配置,⽐比如资源名称
options: {
// placeholder 占位符 [name]老资源模块的名称
// [ext]老资源模块的后缀
// https://webpack.js.org/loaders/file-loader#placeholders
name: "[name]_[hash].[ext]", //打包后的存放位置
outputPath: "images/"
}
}
}
]
},
实例
import pic from "./logo.png";
var img = new Image();
img.src = pic;
img.classList.add("logo");
var root = document.getElementById("root");
root.append(img);
处理字体 https://www.iconfont.cn/?spm=a313x.7781069.199 8910419.d4d0a486a
@font-face {
font-family: "webfont";
font-display: swap;
src: url("webfont.woff2") format("woff2");
}
body {
background: blue;
font-family: "webfont" !important;
}
//webpack.config.js
{
test: /\.(eot|ttf|woff|woff2|svg)$/,
use: "file-loader"
}
- url-loader file-loader加强版本
url-loader内部使⽤了file-loader,所以可以处理file-loader所有 的事情,但是遇到jpg格式的模块,会把该图⽚转换成base64格 式字符串串,并打包到js里。对⼩小体积的图⽚⽐比较合适,⼤大图⽚不 合适。
npm install url-loader -D
案例;
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/,
use: {
loader: "url-loader",
options: {
name: "[name]_[hash].[ext]",
outputPath: "images/", //⼩小于2048,才转换成base64
limit: 2048
} }
} ]
},
样式处理:
Css-loader 分析css模块之间的关系,并合成⼀个css Style-loader 会把css-loader⽣成的内容,以style挂载到⻚⾯的
heade部分
npm install style-loader css-loader -D
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
}
{
test:/\.css$/,
use:[{
loader:"style-loader",
options: {
injectType: "singletonStyleTag" // 将所 有的style标签合并成⼀个
}
},"css-loader"]
}
- Less样式处理:less-loader 把less语法转换成css
$ npm install less less-loader --save-dev
案例: loader有顺序,从右到左,从下到上
{
test: /\.scss$/,
use: ["style-loader", "css-loader", "less-loader"]
}
样式⾃自动添加前缀:
Postcss-loader
npm i postcss-loader autoprefixer -D
新建postcss.config.js
//webpack.config.js
{
test: /\.css$/,
use: ["style-loader", "css-loader", "postcss-
loader"]
},
//postcss.config.js
module.exports = {
plugins: [
require("autoprefixer")({
overrideBrowserslist: ["last 2 versions",">1%"] })
]
};
loader 处理webpack不⽀持的格式⽂件,模块 ⼀个loader只处理⼀件事情 loader有执行顺序
5.Plugins
作⽤于webpack打包整个过程 webpack的打包过程是有(⽣命周期概念)钩⼦子
plugin 可以在webpack运行到某个阶段的时候,帮你做⼀些事情, 类似于⽣命周期的概念
扩展插件,在 Webpack 构建流程中的特定时机注⼊扩展逻辑来改 变构建结果或做你想要的事情。
作⽤于整个构建过程
HtmlWebpackPlugin
htmlwebpackplugin会在打包结束后,⾃自动⽣成⼀个html⽂件,并 把打包⽣成的js模块引⼊到该html中。
npm install --save-dev html-webpack-plugin
配置:
title: ⽤来⽣成⻚⾯的 title 元素
filename: 输出的 HTML ⽂件名,默认是 index.html, 也可以直 接配置带有⼦子⽬录。
template: 模板⽂件路路径,⽀持加载器器,⽐比如 html!./index.html
inject: true | 'head' | 'body' | false ,注⼊所有的资 源到特定的 template 或者 templateContent 中,如果设置为 true 或者 body,所有的 javascript 资源将被放置到 body 元 素的底部,'head' 将放置到 head 元素中。
favicon: 添加特定的 favicon 路路径到输出的 HTML ⽂件中。 minify: {} | false , 传递 html-minifier 选项给 minify 输出
hash: true | false, 如果为 true, 将添加⼀个唯⼀的 webpack 编译 hash 到所有包含的脚本和 CSS ⽂件,对于解除 cache 很有⽤。
cache: true | false,如果为 true, 这是默认值,仅仅在⽂件 修改之后才会发布⽂件。
showErrors: true | false, 如果为 true, 这是默认值,错误 信息会写⼊到 HTML ⻚⾯中
chunks: 允许只添加某些块 (⽐比如,仅仅 unit test 块) chunksSortMode: 允许控制块在添加到⻚⾯之前的排序⽅式,⽀持 的值:'none' | 'default' | {function}-default:'auto' excludeChunks: 允许跳过某些块,(⽐比如,跳过单元测试的块)
案例:
const path = require("path");
const htmlWebpackPlugin = require("html-webpack-
plugin");
module.exports = {
...
plugins: [
new htmlWebpackPlugin({
title: "My App",
filename: "app.html",
template: "./src/index.html"
})
]
};
//index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-
width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible"
content="ie=edge" />
<title><%= htmlWebpackPlugin.options.title %>
</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
clean-webpack-plugin¶
npm install --save-dev clean-webpack-plugin
const { CleanWebpackPlugin } = require("clean-
webpack-plugin");
plugins: [
new CleanWebpackPlugin()
]
mini-css-extract-plugin¶
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader"]
}
new MiniCssExtractPlugin({
filename: "[name][chunkhash:8].css"
})
chunk:⼀个⼊⼝肯定是⼀个chunk,但是⼀个chunk不⼀定只有 ⼀个依赖
webpack5.x 减少hash的概念 Contenthash
6.sourceMap¶
源代码与打包后的代码的映射关系,通过sourceMap定位到源代 码。
在dev模式中,默认开启,关闭的话 可以在配置⽂件里
devtool:"none"
devtool的介绍:https://webpack.js.org/configuration/devtool#d evtool
eval:速度最快,使⽤eval包裹模块代码,
source-map: 产⽣.map⽂件
cheap:较快,不包含列列信息
Module:第三⽅模块,包含loader的sourcemap(⽐比如jsx to js , babel的sourcemap)
inline: 将.map作为DataURI嵌⼊,不单独⽣成.map⽂件 配置推荐:
WebpackDevServer¶
- 提升开发效率的利利器器 每次改完代码都需要重新打包⼀次,打开浏览器器,刷新⼀次,很麻烦,我们可以安装使⽤webpackdevserver来改善这块的体验
- 安装
devtool:"cheap-module-eval-source-map",// 开发环境配置
//线上不推荐开启 devtool:"cheap-module-source-map", // 线上⽣成配置
npm install webpack-dev-server -D
配置 修改下package.json
在webpack.config.js配置:
启动
npm run server
启动服务后,会发现dist⽬录没有了,这是因为devServer把打包后 的模块不会放在dist⽬录下,⽽是放到内存中,从⽽提升速度
- 本地mock,解决跨域: 联调期间,前后端分离,直接获取数据会跨域,上线后我们使⽤
nginx转发,开发期间,webpack就可以搞定这件事 启动⼀个服务器,mock⼀个接⼝:
// npm i express -D
"scripts": {
"server": "webpack-dev-server"
},
devServer: {
contentBase: "./dist",
open: true,
port: 8081
},
// 创建⼀个server.js 修改scripts "server":"node server.js"
//server.js
const express = require('express')
const app = express()
app.get('/api/info', (req,res)=>{
res.json({
name:'开课吧',
age:5, msg:'欢迎来到开课吧学习前端⾼高级课程'
})
})
app.listen('9092')
//node server.js
http://localhost:9092/api/info
项⽬中安装axios⼯具
//npm i axios -D
//index.js
import axios from 'axios'
axios.get('http://localhost:9092/api/info').then(res
=>{
console.log(res)
})
会有跨域问题
修改webpack.config.js 设置服务器代理
proxy: {
"/api": {
target: "http://localhost:9092"
}
}
修改index.js
axios.get("/api/info").then(res => {
console.log(res);
});
Hot Module Replacement (HMR:热模块 替换)¶
- 不⽀持抽离出的css 我们要使⽤css-loader 启动hmr
devServer: {
contentBase: "./dist",
open: true,
hot:true, //即便便HMR不⽣效,浏览器器也不⾃自动刷新,就开启hotOnly hotOnly:true
},
配置⽂件头部引⼊webpack
//const path = require("path");
//const HtmlWebpackPlugin = require("html-webpack-plugin");
//const CleanWebpackPlugin = require("clean-webpack-plugin");
const webpack = require("webpack");
在插件配置处添加:
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: "src/index.html"
}),
new webpack.HotModuleReplacementPlugin()
],
案例:
//index.js
import "./css/index.css";
var btn = document.createElement("button");
btn.innerHTML = "新增";
document.body.appendChild(btn);
btn.onclick = function() {
var div = document.createElement("div");
div.innerHTML = "item";
document.body.appendChild(div);
};
//index.css
div:nth-of-type(odd) {
background: yellow;
}
注意启动HMR后,css抽离会不⽣效,还有不⽀持contenthash, chunkhash
处理js模块HMR 需要使⽤module.hot.accept来观察模块更新 从⽽更新
案例:
//counter.js
function counter() {
var div = document.createElement("div");
div.setAttribute("id", "counter");
div.innerHTML = 1;
div.onclick = function() {
div.innerHTML = parseInt(div.innerHTML, 10) + 1;
};
document.body.appendChild(div);
}
export default counter;
//number.js
function number() {
var div = document.createElement("div");
div.setAttribute("id", "number");
div.innerHTML = 13000;
document.body.appendChild(div);
}
export default number;
//index.js
import counter from "./counter";
import number from "./number";
counter();
number();
if (module.hot) {
module.hot.accept("./b", function() {
document.body.removeChild(document.getElementById("number"));
number();
});
}
Babel处理ES6¶
官⽅⽹网站:https://babeljs.io/
中⽂⽹网站:https://www.babeljs.cn/
Babel是JavaScript编译器,能将ES6代码转换成ES5代码,让我们 开发过程中放⼼使⽤JS新特性⽽不⽤担⼼兼容性问题。并且还可以 通过插件机制根据需求灵活的扩展。
Babel在执行编译的过程中,会从项⽬根⽬录下的 .babelrc JSON ⽂件中读取配置。没有该⽂件会从loader的options地⽅读取配 置。
测试代码¶
//index.js
const arr = [new Promise(() => {}), new Promise(()
=> {})];
arr.map(item => {
console.log(item);
});
安装¶
npm i babel-loader @babel/core @babel/preset-env -D
1.babel-loader是webpack 与 babel的通信桥梁梁,不会做把es6转成
es5的⼯作,这部分⼯作需要⽤到@babel/preset-env来做 2.@babel/preset-env里包含了es,6,7,8转es5的转换规则
Ecma 5 6 7 8... 草案(评审通过的,还有未通过的) ⾯向未来的 env是babel7之后推行的预设插件 env{
ecma 5 ecma 6 ecma 7 ecma 8 。。。 }
Webpack.config.js
通过上⾯的⼏步 还不够,默认的Babel只⽀持let等⼀些基础的特性 转换,Promise等⼀些还有转换过来,这时候需要借助 @babel/polyfill,把es的新特性都装进来,来弥补低版本浏览器中 缺失的特性
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"]
}
}
}
@babel/polyfill¶
以全局变量的⽅式注⼊进来的。windows.Promise,它会造成全局 对象的污染
npm install --save @babel/polyfill
//index.js 顶部 import "@babel/polyfill";
按需加载,减少冗余
会发现打包的体积⼤大了很多,这是因为polyfill默认会把所有特性注 ⼊进来,假如我想我⽤到的es6+,才会注⼊,没⽤到的不注⼊,从 ⽽减少打包的体积,可不可以呢
当然可以 修改Webpack.config.js
options: {
[
presets: [
"@babel/preset-env",
{
targets: {
edge: "17",
firefox: "60",
chrome: "67",
safari: "11.1"
},
corejs: 2,//新版本需要指定核⼼库版本
useBuiltIns: "usage"//按需注⼊
}
]
]
}
useBuiltIns选项是babel 7的新功能,这个选项告诉babel如何 配置@babel/polyfill。 它有三个参数可以使⽤: 1entry: 需要 在webpack的⼊⼝⽂件里import "@babel/polyfill"⼀ 次。 babel 会根据你的使⽤情况导⼊垫⽚,没有使⽤的功能不会被 导⼊相应的垫⽚。 2usage: 不需要import,全⾃自动检测,但是要 安装@babel/polyfill。(试验阶段) 3false: 如果你import "@babel/polyfill" ,它不会排除掉没有使⽤的垫⽚,程序体积会 庞⼤大。(不推荐)
请注意: usage 的行为类似 babel-transform-runtime,不会造成 全局污染,因此也会不会对类似 Array.prototype.includes() 进行 polyfill。
扩展: babelrc⽂件: 新建.babelrc⽂件,把options部分移⼊到该⽂件中,就可以了
//.babelrc
{
presets: [
[
"@babel/preset-env",
{
targets: {
edge: "17",
firefox: "60",
chrome: "67",
safari: "11.1"
},
corejs: 2, //新版本需要指定核⼼库版本 useBuiltIns: "usage" //按需注⼊
} ]
] }
//webpack.config.js
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
}
配置React打包环境 安装¶
npm install react react-dom --save
编写react代码:
//index.js
import React, { Component } from "react";
import ReactDom from "react-dom";
class App extends Component {
render() {
return <div>hello world</div>;
}
}
ReactDom.render(<App />,
document.getElementById("app"));
安装babel与react转换的插件: npm install --save-dev @babel/preset-react
在babelrc⽂件里添加:
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1",
"Android":"6.0"
},
"useBuiltIns": "usage", //按需注⼊
}
],
"@babel/preset-react"
]
}
entry:{
index:"./src/index",
list:"./src/list",
detail:"./src/detail"
}
new htmlWebpackPlugins({
title: "index.html",
template: path.join(__dirname, "./src/index/index.html"),
filename:"index.html",
chunks:[index]
})
index
index.js
index.html list
index.js
index.html detail
index.js
index.html 2.使⽤ glob.sync 第三⽅库来匹配路径
npm i glob -D
const glob = require("glob")
//MPA多⻚⾯打包通⽤⽅案
const setMPA = () => {
const entry = {};
const htmlWebpackPlugins = [];
return {
entry,
htmlWebpackPlugins
};
};
const { entry, htmlWebpackPlugins } = setMPA();
const setMPA = () => {
const entry = {};
const htmlWebpackPlugins = [];
const entryFiles = glob.sync(path.join(__dirname,"./src/*/index.js"));
entryFiles.map((item, index) => {
const entryFile = entryFiles[index];
const match = entryFile.match(/src\/(.*)\/index\.js$/);
const pageName = match && match[1];
entry[pageName] = entryFile;
htmlWebpackPlugins.push(
new htmlWebpackPlugin({
title: pageName,
template: path.join(__dirname, `src/${pageName}/index.html`),
filename: `${pageName}.html`,
chunks: [pageName],
inject: true
})
);
});
return {
entry,
htmlWebpackPlugins
};
};
const { entry, htmlWebpackPlugins } = setMPA();
module.exports = {
entry,
output:{
path: path.resolve(__dirname, "./dist"),
filename: "[name].js"
}
plugins: [
// ...
...htmlWebpackPlugins//展开数组
]
}
扩展¶
@babel/plugin-transform-runtime¶
当我们开发的是组件库,⼯具库这些场景的时候,polyfill就不适合 了,因为polyfill是注⼊到全局变量,window下的,会污染全局环 境,所以推荐闭包⽅式:@babel/plugin-transform-runtime,它 不会造成全局污染
安装
npm install --save-dev @babel/plugin-transform-
runtime
npm install --save @babel/runtime
修改配置⽂件:注释掉之前的presets,添加plugins
options: {
presets: [
[
"@babel/preset-env",
{
targets: {
edge: "17",
firefox: "60",
chrome: "67",
safari: "11.1"
},
useBuiltIns: "usage",
corejs: 2
} ]
],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"absoluteRuntime": false,
"corejs": false,
"helpers": true,
"regenerator": true,
"useESModules": false
} ]
] }
⽂件监听¶
轮询判断⽂件的最后编辑时间是否变化,某个⽂件发⽣了变化,并
不会⽴刻告诉监听者,先缓存起来
webpack开启监听模式,有两种
1.启动webpack命令式 带上--watch 参数,启动监听后,需要⼿手动刷新浏览器
scripts:{
"watch":"webpack --watch"
}
2.在配置⽂件里设置 watch:true
watch: true, //默认false,不开启 //配合watch,只有开启才有作⽤
watchOptions: {
//默认为空,不监听的⽂件或者⽬录,⽀持正则
ignored: /node_modules/,
//监听到⽂件变化后,等300ms再去执行行,默认300ms,
aggregateTimeout: 300, //判断⽂件是否发⽣变化是通过不停的询问系统指定⽂件有没有变化,默认每秒问1次
poll: 1000 //ms 轮询 1s查看1次
}
}