webpack
webpack
打包工具的选择
INFO
Vue React的封装都使用rollup打包,那rollup打包工具和项目开发使用的webpack打包工具的区别是什么?
- Vue github的package.json

- react github的package.json

rollup
- rollup的打包体积小
- rollup推荐开发者去写esm模块,esm模块适合做静态代码分析,做tree shaking减少代码体积,esm是浏览器除了script标签外,拥有的模块化能力。
- rollup诞生在浏览器支持esm模块之后,esm是未来js的模块发展方向,rollup直接使用esm模式进行开发。
- 高版本浏览器原生支持esm模块,无需额外代码注入,打包后的代码结构清晰。
- 不用像webpack写iife(自执行函数)
webpack
- webpack的打包体积大,注入了很多的兼容代码。
- webpack诞生在commonjs后,esm模块之前。
- 浏览器当时只支持script标签代码引入,且都是自执行函数,webpack为了在浏览器端也有模块的引用,自己在浏览器端实现了一套commonjs语法__webpack_require__。
- 后续浏览器支持了esm模块,但是webpack为了兼容之前的npm包,仍然延续了之前iife自执行函数和代码注入。
- webpack不能生成esm模块的包,对静态代码分析及 tree shaking不友好
- 适合做应用项目,对于引入、css、scss、图片、字体文件有更好的解析,用法多样化,生态大而全。
两者引用源代码模块的对比
| 纯esm | 纯cjs | 两者混用 | |
|---|---|---|---|
| webpack | 支持(有代码注入) | 支持(有代码注入) | 支持(有代码注入) |
| rollup | 支持(无注入) | 原生不支持(需增加插件@rollup/plugin-commonjs) | 原生不支持(需增加插件@rollup/plugin-commonjs) |
项目中 webpack tree shaking 不生效的原因
INFO
注:先把sourcemap关闭,为了看到的是打包后的源码,而不是看到映射的源代码

代码
import { log } from '@xxx/sentry'
console.log(log, 'log====');
npm run dev项目启动后查看 打印内容

INFO
结果代码并没有tree shaking,其他没有用到的方法也被打入了包中。 引入esm模块 按照命名导入,正常来说应该会tree shaking,此时却没有tree shaking,这是为什么?
打包后的代码

webpack的 tree shaking 只在production模式生效
INFO
webpack的tree shaking,默认只在 使用 mode 为 production 的配置项以启用。
此时
npm run build对打包结果本地启动服务跑项目查看。


批量导入后整体使用tree shaking不生效
如果使用了* as sentry 别名批量导入的写法时,由于是批量引入,也不会进行tree shaking。 如果HxSentry.log() 单独使用其中的一个方法是会进行tree shaking的。
代码
import * as sentry from '@xxx/sentry'
console.log(sentry, 'sentry====');
打印结果

打包后的代码

批量导入后单独使用tree shaking生效
代码
import * as sentry from '@xxx/sentry'
console.log(sentry.log, 'log====');
打印结果

打包后的代码

webpack
webpack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其打包为合适的格式以供浏览器使用。

可以做的事情
代码转换、文件优化、代码分割、模块合并、自动刷新、代码校验、自动发布
webpack基础配置
webpack安装
- 安装本地webpack
webpack webpack-cli -D
webpack可以进行0配置
- 打包工具 -> 输出后的结果(js模块)
- 打包(支持我们的js的模块化)
如果当前目录下有node.exe执行程序,会用这个执行程序执行webpack\bin目录下的webpack.js文件


在开发时会用到require()来引入文件,但是如果放到浏览器时不支持node这种规范的,这样的js文件是不可以直接用于浏览器。打包之后是可以的,把语法全部转化为es5了,手动创建一个html文件,引入刚打包好的main.js文件就可以使用了

手动配置webpack
- 默认配置文件的名字是webpack.config.js
// webpack 是node写出来的 node的写法
let path = require('path');
module.exports = {
mode: 'development', // 模式 默认两种 production development
entry: './src/index.js', // 入口
output: {
filename: 'bundle.js', // 打包后的文件名
path: path.resolve(__dirname, 'dist'),// 路径必须是一个绝对路径,加__dirname是以当前目录下加一个dist目录
}
}
config.webpack.js文件名是默认的,webpack会默认调用webpack-cli中bin下的yargs.js文件执行。config.webpack.js名字也是可以更改的。


webpack打包出的文件解析
- 打包好之后的js文件
(function (modules) { // webpackBootstrap
// The module cache 先定义一个缓存
var installedModules = {};
// "./src/index.js" : {}
// The require function 配置了 实现了require方法 因为require是不能在浏览器中运行的
function __webpack_require__ (moduleId) { // "./src/index.js"
// Check if module is in cache 检查这个模块是否在缓存中
if (installedModules[moduleId]) { // 不在缓存中
return installedModules[moduleId].exports;
}
// Create a new module (and put it into the cache)
var module = installedModules[moduleId] = {
i: moduleId,
l: false, // 是否加载完成
exports: {}
};
// Execute the module function 第一个参数this指向,然后是module模块, module.exports空对象
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
// Flag the module as loaded
module.l = true;
// Return the exports of the module
return module.exports;
}
// Load entry module and return exports
return __webpack_require__(__webpack_require__.s = "./src/index.js"); // 入口模块
})
({
"./src/a.js": // key => 模块的路径
(function (module, exports) { // value 函数
eval("module.exports = 'zfpx'\n\n//# sourceURL=webpack:///./src/a.js?");
}),
"./src/index.js":
(function (module, exports, __webpack_require__) {
eval("let str = __webpack_require__( \"./src/a.js\")\r\nconsole.log(str)\n\n//# sourceURL=webpack:///./src/index.js?");
})
});
需要自定义webpack.config.js名字
直接在终端
npx webpack --config [filename]知道自己配置的文件名字直接在package.json文件中写
webpack --config [filename]在终端需要npm run build

如果不知道自己起的文件名字, 临时需要打包配置输入文件名
在package.json文件中这样写
webpack在终端需要在
--config前面再加--,表示后面写的是字符串npm run build -- --config [filename]

html插件
每次打包完需要在手动打包后的文件夹中点击index.html在浏览器中打开,地址是file:\\,我们需要地址是localhost:,官方有webpack-dev-server,开发时直接打包进内存中,浏览器输入直接可以打开
webpack中有内置的express做的开发服务插件webpack-dev-server
安装
npm i webpack-dev-server -D
使用:并不会真的打包,会把打包好的存到内存中
命令行中:npx webpack-dev-server
package.json中:npm run dev
{
"name": "webpack-dev-1",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"build": "webpack --config webpack.config.js",
"dev": "webpack-dev-server" // npm run dev
},
"devDependencies": {
"html-webpack-plugin": "^3.2.0",
"webpack": "^4.35.3",
"webpack-cli": "^3.3.5",
"webpack-dev-server": "^3.7.2"
}
}
webpack.config.js中配置
let path = require('path');
module.exports = {
devServer: { // 开发服务器的配置
port: 3000, //改端口号
progress: true, // 加进度条
contentBase: './build', // 在哪个文件夹下起服务
open: true, // 自动打开浏览器
compress: true, // gzip压缩
},
mode: 'development',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'build'),
}
}
在打包的build文件夹中新建了index.html,引入指定的build.js,每次使用webpack-dev-server打包进内存,都是可以指定引入新的打包的build.js文件。但是,如果没有index.html呢,或许连build文件夹都没有。
html-webpack-plugin插件提供了打包后自动生成指定的自定义html模板并引入新打包的js文件,此时依然会产生两个文件,只是看不到
// webpack.config.js
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin'); // 引入html-webpack-plugin
module.exports = {
devServer: {
port: 3000,
progress: true,
contentBase: './build',
open: true,
compress: true,
},
mode: 'development',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'build'),
},
plugins:[ // 数组 放着所有的webpack插件
new HtmlWebpackPlugin({
template: './src/index.html', // 以哪个html作为模板
filename: 'index.html', // 设置打包后生成的html模板,不设置默认也是index.html
})
]
}
配置各种参数:
// webpack.config.js
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
devServer: {
port: 3000,
progress: true,
contentBase: './build',
open: true,
compress: true,
},
mode: 'production',
entry: './src/index.js',
output: {
filename: 'bundle.[hash:8].js', // 打包后的文件名 [hash]加入哈希,产生不同的文件,防止覆盖和出现缓存的问题 [hash:8]只显示8位
path: path.resolve(__dirname, 'build'),
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
minify: { // 简化html
removeAttributeQuotes: true, // 删除属性的双引号
collapseWhitespace: true, // 折叠空行
},
hash: true, // 引用文件加哈希戳 防止缓存的问题
})
]
}
打包后文件的变化

打包后模板引擎的变化

样式处理(1)
在index.html模板中是不允许引入css文件的,因为模板打包完会原封不动的到打包文件夹中,如果引入了css文件,此时会找不到打包文件夹外面的css文件,所以需要把css文件引入js文件中,一起打包,此时就需要css-loader,如果直接打包,js文件中是不会处理引入的 css文件的。

<!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>Document</title>
</head>
<body>
<!-- 模板 -->
<link rel="stylesheet" href="./index.css">不允许
</body>
</html>
打包提示需要引入loader

- css-loader是解析css文件里专属的语法,比如@import
/* a.css */
body {
color: yellow;
}
/* index.css */
@import './a.css';
body {
background: red;
}
- style-loader是把打包好的css文件插入到模板引擎中的
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
devServer: {
port: 3000,
progress: true,
contentBase: './build',
open: true,
compress: true,
},
mode: 'production',
entry: './src/index.js',
output: {
filename: 'bundle.[hash:8].js',
path: path.resolve(__dirname, 'build'),
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
minify: {
removeAttributeQuotes: true,
collapseWhitespace: true,
},
hash: true,
})
],
module: { // 模块
rules: [ // 规则 css-loader 解析 @import这种语法的
// style-loader 它是把css 插入到head的标签中
// loader的特点 希望功能单一
// loader的用法 一个loader就用字符串
// { test: /\.css$/, use: 'css-loader' }
// 多个loader需要用 []
// loader的顺序 默认是从右向左执行 从下到上执行
// 先使用css-loader打包好之后再使用style-loader插入模板
// { test: /\.css$/, use: ['style-loader','css-loader'] }
// loader还可以写成 对象方式,好处是可以再写一个参数
{
test: /\.css$/, use: [
{
loader: 'style-loader',
options: {
}
},
'css-loader']
}
]
}
}
- 我们自己在html模板head中写的样式优先级不是最高,可以配置style-loader的属性

{
test: /\.css$/, use: [
{
loader: 'style-loader', // 插入模板head标签的位置
options: {
insertAt: 'top' // 插入head标签顶部
}
},
'css-loader']
}

- 配置less
安装yarn add less less-loader -D
less-loader需要用less来进行转化
/*index.less*/
body {
div {
border: 1px solid #dadade;
}
}
/*index.js*/
let str = require('./a.js')
console.log(str)
require('./index.css')
require('./index.less')
/*webpack.config.js*/
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: 'style-loader',
options: {
insertAt: 'top'
}
},
'css-loader'
]
},
{ // 可以处理less文件
test: /\.less$/,
use: [
{ // 把css文件插入到模板中
loader: 'style-loader',
options: {
insertAt: 'top' // 插入模板head标签的位置
}
},
'css-loader', // @import 解析路径
'less-loader' // 把less 解析成 css
]
}
]
}
同样可以配置sass需要安装node-sass和sass-loader,sass-loader需要放在下面先执行
stylus需要安装stylus和stylus-loader
样式处理(2)
抽离模板中的style标签中css样式为link,抽离css插件mini-css-extract-plugin
安装yarn add mini-css-extract-plugin -D
- 引入同一个'mini-css-extract-plugin'
/*webpack.config.js*/
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin')
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: 'bundle.[hash:8].js',
path: path.resolve(__dirname, 'build'),
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
}),
new MiniCssExtractPlugin({
filename: 'main.css' // 抽离出css文件名
}),
],
module: {
rules: [
{ // 我们不希望css抽离出来再放到模板的style标签中,要把style-loader去掉
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader'
]
},
{ // 引入同一个'mini-css-extract-plugin'会全都抽离成一个文件main.css。
// 也可以拷贝,引入两个'mini-css-extract-plugin',分别new两个实例和用这两个的loader。比如一个用来抽离.css文件到main.css,一个用来抽离.less文件到main1.css,虽然是不同的两个css文件,但他们内容是一样的。有可能.less文件less-loader完成之后,和.css文件一起css-loader。
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader'
]
}
]
}
}
/*main.css*/
body {
color: yellow;
}
body {
background: red;
transform: 45deg
}
body div {
border: 1px solid #dadade;
}

- 引入两个'mini-css-extract-plugin',分别new两个实例和用这两个的loader
/*webpack.config.js*/
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin')
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
let MiniCssExtractPlugin1 = require('mini-css-extract-plugin');
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: 'bundle.[hash:8].js',
path: path.resolve(__dirname, 'build'),
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
}),
new MiniCssExtractPlugin({
filename: 'main.css'
}),
new MiniCssExtractPlugin1({
filename: 'main1.css'
})
],
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader'
]
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin1.loader,
'css-loader',
'less-loader'
]
}
]
}
}
/*main.css*/
body {
color: yellow;
}
body {
background: red;
transform: 45deg
}
body div {
border: 1px solid #dadade;
}
/*main1.css*/
body {
color: yellow;
}
body {
background: red;
transform: 45deg
}
body div {
border: 1px solid #dadade;
}
- 希望添加的属性自动加前缀
插件包autoprefixer和loaderpostcss-loader
安装yarn add postcss-loader autoprefixer
需要新建postcss.config.js配置文件,这个配置文件里面要用到autoprefixer插件

/*webpack.config.js*/
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin')
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: 'bundle.[hash:8].js',
path: path.resolve(__dirname, 'build'),
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
}),
new MiniCssExtractPlugin({
filename: 'main.css'
}),
],
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader', // 解析css之前就要加前缀
'postcss-loader',
]
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'less-loader'
]
}
]
}
}
/*postcss.config.js*/
module.exports = {
plugins: [require('autoprefixer')({
"browsers": [ // 9.6.0版本之后不能使用"browsers"
"defaults",
"not ie < 11",
"last 2 versions",
"> 1%",
"iOS 7",
"last 3 iOS versions"
]
})
]
}
// 更改后
module.exports = {
plugins: [require('autoprefixer')({
"overrideBrowserslist": [
"defaults",
"not ie < 11",
"last 2 versions",
"> 1%",
"iOS 7",
"last 3 iOS versions"
]
})
]
}
打包后的main.css
/*main.css*/
body {
color: yellow;
}
body {
background: red;
-webkit-transform: rotate(45deg);
transform: rotate(45deg)
}
body div {
border: 1px solid #dadade;
}
- 压缩css文件、js文件体积 在npmjs中
mini-css-extract-plugin包下推荐用optimize-css-assets-webpack-plugin压缩css文件体积,还有配套terser-webpack-pluginjs文件压缩体积。
/* webpack.config.js */
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin')
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
let OptimizeCss = require('optimize-css-assets-webpack-plugin');
let TerserJSPlugin = require('terser-webpack-plugin');
module.exports = {
optimization:{ // 优化项
minimizer:[
new TerserJSPlugin({}), // 压缩js文件体积
new OptimizeCss({}), // 压缩css文件体积
]
},
mode: 'production',
entry: './src/index.js',
output: {
filename: 'bundle.[hash:8].js',
path: path.resolve(__dirname, 'build'),
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
}),
new MiniCssExtractPlugin({
filename: 'main.css'
}),
],
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader', // 解析css之前就要加前缀
'postcss-loader',
]
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'less-loader'
]
}
]
}
}
转化es6语法
在js文件中如果用到了箭头函数,没有引入babel,打包之后没有变成普通函数,仍然是箭头函数。
需要把源文件转化的加载器,转化成另一种文件,就要用到babel-loader
转化需要babel的核心模块@babel/core,调用transform方法
需要@babel/preset-env告诉如何将es6转化es5的转化模块,可以将标准的语法转换成低级的语法
如果有class A{}语法,打包提示要安装@babel/plugin-proposal-class-properties插件
如果有@log语法,打包提示需要安装@babel/plugin-proposal-class-properties,去babel官网看,还需要安装@babel/plugin-proposal-decorators
/* index.js */
let str = require('./a.js')
console.log(str)
require('./index.css')
require('./index.less')
fn = () => {
console.log('sss')
}
@log // 类的装饰器 装饰类或者属性
class A { // new A() a=1 // es7写法
a = 1;
}
let a = new A()
console.log(a.a)
function log (target) {
console.log(target); // 查看被修饰的A类
}
/* webpack.config.js */
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin')
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
let OptimizeCss = require('optimize-css-assets-webpack-plugin');
let TerserJSPlugin = require('terser-webpack-plugin');
module.exports = {
optimization:
minimizer: [
new TerserJSPlugin({}),
new OptimizeCss({}),
]
},
mode: 'development',
entry: './src/index.js',
output: {
filename: 'bundle.[hash:8].js',
path: path.resolve(__dirname, 'build'),
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
}),
new MiniCssExtractPlugin({
filename: 'main.css'
}),
],
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: { // options可以写在外面也可以写在里面 用babel-loader 需要把es6->es5
presets: [ // 映射
'@babel/preset-env'
],
plugins: [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose": true }] // 转化class A{}语法
]
}
}
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
]
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'less-loader'
]
}
]
}
}
处理js语法及校验
代码转换babel
如果写了generator语法会报错
// generator语法,处理异步
function* gen (params) {
yield 1;
}
console.log(gen().next())
regeneratorRuntime is not defined
@babel/plugin-transform-runtime将更高级语法转化低级语法

在生产环境需要npm install --save @babel/runtime
如果写'aaa'.includes('a')语法,上面的插件是不支持实例上的方法调用,还需要引入@babel/runtime-corejs3这里没有实现,有问题
polyfill这个选项在v7中通过将其设置为默认值而被删除。
/* a.js */
module.exports = 'aaaaa'
require('core-js')
class B {
}
// generator语法,处理异步
function* gen (params) {
yield 1;
}
console.log(gen().next())
'aaa'.includes('a')
/* webpack.config.js */
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin')
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
let OptimizeCss = require('optimize-css-assets-webpack-plugin');
let TerserJSPlugin = require('terser-webpack-plugin');
module.exports = {
optimization: {
minimizer: [
// new TerserJSPlugin({}),
new OptimizeCss({}),
]
},
mode: 'development',
entry: './src/index.js',
output: {
filename: 'bundle.[hash:8].js',
path: path.resolve(__dirname, 'build'),
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
}),
new MiniCssExtractPlugin({
filename: 'main.css'
}),
],
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env'
],
plugins: [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose": true }],
[
"@babel/plugin-transform-runtime",
{
corejs: 3, // [].includes这样的实例方法只适用于core-js@3。
proposals: true
}
]
]
}
},
include: path.resolve(__dirname, 'src'),
exclude: /node_modules/
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader', // 解析css之前就要加前缀
'postcss-loader',
]
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'less-loader'
]
}
]
}
}
校验规范eslint
可以到eslint官网自定义eslint配置后下载eslintrc.json文件,之后在文件前面加.使用

需要引入eslint和eslint-loader
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin')
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
let OptimizeCss = require('optimize-css-assets-webpack-plugin');
let TerserJSPlugin = require('terser-webpack-plugin');
module.exports = {
optimization: {
minimizer: [
// new TerserJSPlugin({}), // 压缩js文件体积
new OptimizeCss({}),
]
},
mode: 'development',
entry: './src/index.js',
output: {
filename: 'bundle.[hash:8].js',
path: path.resolve(__dirname, 'build'),
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
}),
new MiniCssExtractPlugin({
filename: 'main.css'
}),
],
module: {
rules: [ // loader 默认是从右边向左边执行 从下到上执行
{ // 为了代码解析和代码校验分开,写在两个对象中,也是可以写在一个{}中的,但是需要加enforce: 'pre'配置项;也可以代码校验写在代码解析的下边。
test: /\.js$/,
use: {
loader: 'eslint-loader',
options: {
enforce: 'pre' // previous:强制执行顺序.js文件eslint-loader先执行 post:后执行
}
}
},
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env'
],
plugins: [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose": true }],
[
"@babel/plugin-transform-runtime",
// {
// corejs: 3,
// proposals: true
// }
]
]
}
},
include: path.resolve(__dirname, 'src'),
exclude: /node_modules/
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
]
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'less-loader'
]
}
]
}
}
全局变量引入问题
loader的几种类型
pre:前面执行的loader
normal:普通loader
内联loader
postloader:后置loader
全局暴露变量
引入jquery被webpack打包之后是在闭包中,没有挂到window对象上,别的js文件并不能使用jquery。此时需要引入expose-loader。
- expose-loader暴露到window上
- 不需要在
webpack.config.js配置的情况
// 把jquery使用expose-loader替换成$全局对象
import $ from 'expose-loader?$!jquery'
// expose-loader 暴露 全局的loader 内联的loader
console.log(window.$);
- 在
webpack.config.js配置的情况
// 使用
import $ from 'jquery'
console.log(window.$);
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin')
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
let OptimizeCss = require('optimize-css-assets-webpack-plugin');
let TerserJSPlugin = require('terser-webpack-plugin');
module.exports = {
optimization: {
minimizer: [
// new TerserJSPlugin({}),
new OptimizeCss({}),
]
},
mode: 'development',
entry: './src/index.js',
output: {
filename: 'bundle.[hash:8].js',
path: path.resolve(__dirname, 'build'),
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
}),
new MiniCssExtractPlugin({
filename: 'main.css'
}),
],
module: {
rules: [
{
test: require.resolve('jquery'), // 只要引用了jquery就匹配到
use: 'expose-loader?$'
},
// {
// test: /\.js$/,
// use: {
// loader: 'eslint-loader',
// options: {
// enforce: 'pre'
// }
// }
// },
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env'
],
plugins: [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose": true }],
[
"@babel/plugin-transform-runtime",
// {
// corejs: 3,
// proposals: true
// }
]
]
}
},
include: path.resolve(__dirname, 'src'),
exclude: /node_modules/
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
]
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'less-loader'
]
}
]
}
}
- providePlugin 给每个模块中都注入$,但是没有在window上
let webpack = require('webpack');// webpack.ProvidePlugin
module.exports = {
optimization: {
minimizer: [
// new TerserJSPlugin({}),
new OptimizeCss({}),
]
},
mode: 'development',
entry: './src/index.js',
output: {
filename: 'bundle.[hash:8].js',
path: path.resolve(__dirname, 'build'),
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
}),
new MiniCssExtractPlugin({
filename: 'main.css'
}),
new webpack.ProvidePlugin({ // 在每个模块中都注入$
$: 'jquery'
})
],
}
// 使用
console.log($); // 在每个模块中注入$对象,并没有在window上
- 使用CDN在index.html引入jquery,可以在每一个模块使用window.$或者$
<head>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
</head>
<body>
<!-- 模板 -->
<div>内容区</div>
</body>
- 引入不打包
如果已经把jquery变成每个模块都引入了,单个模块有强迫症,仍然要import jquery from 'jquery',这样会把单独引入的jquery会打包,需要在webpack.config.js配置忽略打包。
/* index.js */
import $ from 'jquery';
console.log($);
<!-- index.html -->
<head>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
</head>
<body>
<!-- 模板 -->
<div>内容区</div>
</body>
/* webpack.config.js */
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin')
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
let OptimizeCss = require('optimize-css-assets-webpack-plugin');
let TerserJSPlugin = require('terser-webpack-plugin');
module.exports = {
optimization: {
minimizer: [
// new TerserJSPlugin({}),
new OptimizeCss({}),
]
},
mode: 'development',
entry: './src/index.js',
output: {
filename: 'bundle.[hash:8].js',
path: path.resolve(__dirname, 'build'),
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
}),
new MiniCssExtractPlugin({
filename: 'main.css'
}),
],
externals: { // 打包时忽略
jquery: '$'
},
module: {
rules: [
// {
// test: /\.js$/,
// use: {
// loader: 'eslint-loader',
// options: {
// enforce: 'pre'
// }
// }
// },
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env'
],
plugins: [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose": true }],
[
"@babel/plugin-transform-runtime",
// {
// corejs: 3,
// proposals: true
// }
]
]
}
},
include: path.resolve(__dirname, 'src'),
exclude: /node_modules/
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
]
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'less-loader'
]
}
]
}
}
图片处理
- 在js中创建图片来引入
如果直接image.src='./logo.jpg'打包会直接认为是个字符串
需要引入file-loader,打包默认会在内部生成一张图片 到build目录下,把生成的图片的名字返回来
import logo from './logo.jpg' // 打包时会生成一个哈希戳的图片名字到打包文件夹下。把图片引入,返回的结果是一个新的图片地址url
console.log(logo)
let image = new Image();
image.src = logo; // 就是一个普通的图片
document.body.appendChild(image);
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin')
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
let OptimizeCss = require('optimize-css-assets-webpack-plugin');
let TerserJSPlugin = require('terser-webpack-plugin');
module.exports = {
optimization: {
minimizer: [
// new TerserJSPlugin({}),
new OptimizeCss({}),
]
},
mode: 'development',
entry: './src/index.js',
output: {
filename: 'bundle.[hash:8].js',
path: path.resolve(__dirname, 'build'),
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
}),
new MiniCssExtractPlugin({
filename: 'main.css'
}),
],
externals: {
jquery: '$'
},
module: {
rules: [
{
test: /\.html$/,
use: 'html-withimg-loader'
},
{
test: /\.(png|jpg|gif)$/,
use: 'file-loader'
},
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env'
],
plugins: [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose": true }],
[
"@babel/plugin-transform-runtime",
// {
// corejs: 3,
// proposals: true
// }
]
]
}
},
include: path.resolve(__dirname, 'src'),
exclude: /node_modules/
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
]
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'less-loader'
]
}
]
}
}
- 在css引入 background('url)
/* index.js */
import './index.css'
div{
width: 100px;
height: 100px;
background: url('./logo.jpg')
/* css中直接引入,因为有file-loader了,相当于require("./logo.jpg") */
}
<img src="" alt=""/>在标签中引入图片,需要引入html-withimg-loader
<body>
<!-- 模板 -->
<div>内容区</div>
<img src="./logo.jpg" alt="">
</body>
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin')
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
let OptimizeCss = require('optimize-css-assets-webpack-plugin');
let TerserJSPlugin = require('terser-webpack-plugin');
module.exports = {
optimization: {
minimizer: [
// new TerserJSPlugin({}),
new OptimizeCss({}),
]
},
mode: 'development',
entry: './src/index.js',
output: {
filename: 'bundle.[hash:8].js',
path: path.resolve(__dirname, 'build'),
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
}),
new MiniCssExtractPlugin({
filename: 'main.css'
}),
],
externals: {
jquery: '$'
},
module: {
rules: [
{
test: /\.html$/,
use: 'html-withimg-loader'
},
{
test: /\.(png|jpg|gif)$/,
use: 'file-loader'
},
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env'
],
plugins: [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose": true }],
[
"@babel/plugin-transform-runtime",
// {
// corejs: 3,
// proposals: true
// }
]
]
}
},
include: path.resolve(__dirname, 'src'),
exclude: /node_modules/
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
]
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'less-loader'
]
}
]
}
}
- 对打包图片做限制,如果有些图片比较小,我们不希望图片发送http请求,直接用base64第一次就加载出来。我们的图片要小于多少k的时候 用base64 来转化,否则用file-loader产生真实的图片
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin')
let MiniCssExtractPlugin = require('mini-css-extract-plugin');
let OptimizeCss = require('optimize-css-assets-webpack-plugin');
let TerserJSPlugin = require('terser-webpack-plugin');
module.exports = {
optimization: {
minimizer: [
// new TerserJSPlugin({}),
new OptimizeCss({}),
]
},
mode: 'development',
entry: './src/index.js',
output: {
filename: 'bundle.[hash:8].js',
path: path.resolve(__dirname, 'build'),
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
}),
new MiniCssExtractPlugin({
filename: 'main.css'
}),
],
externals: {
jquery: '$'
},
module: {
rules: [
{
test: /\.html$/,
use: 'html-withimg-loader'
},
{
test: /\.(png|jpg|gif)$/,
// 做一个限制 当我们的图片 小于多少k的时候 用base64 来转化
// 否则用file-loader产生真实的图片
// use: 'file-loader' // 打包把图片生成一个新图片
use: {
loader: 'url-loader', // 转化为base64
options: {
limit: 200 * 1024 // 图片小于2M的时候直接转化为base64,不会打包成一个新图片
}
}
},
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env'
],
plugins: [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose": true }],
[
"@babel/plugin-transform-runtime",
// {
// corejs: 3,
// proposals: true
// }
]
]
}
},
include: path.resolve(__dirname, 'src'),
exclude: /node_modules/
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
]
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'less-loader'
]
}
]
}
}
