一篇文章搞懂webpack

146 阅读6分钟

image-20210613105758785

本文编写基于

webpack 5

1.入门(熟悉一下webpack的配置)

1.1创建一个工程化项目

这样会在当前文件下生成一个package.json文件

@1 工程化一个项目

npm init -y

@2 按照官方要求我们下载两个包

npm i webpack webpack-cli -D
  • 默认安装在生产环境下 production
  • -D安装在开发环境下 development

webpack是按照依赖打包 所有安装在开哪个环境下最终都会打包 这样区分只是为了规范

@3 在根目录下创建一个src 并且创建一个 index.js

@4 修改package.json

@5 执行

npm run build1

到此已经 完成了webpack的简单打包 你会看到根目录下生成了一个dist文件夹

image-20210613101201341

1.2 介绍核心配置

首先要知道webpack的底层是基于express写的,express是基于nodejs开发的所以我们在配置文件 webpack.config.js 中要遵循CommonJS的规范

@1 创建一个 webpack.config.js

在根目录下创建一个 webpack.config.js 之后我们可以在这个文件中配置webpack (就是在webpack中写规则)

image-20210613102653823

@2 导出配置

// @1 方法一
module.exports = {

}
// @2 方法二
module.exports = function (){
    // 这里可以用arguments命令接口传递的参数
    console.log(arguments);
    return {
        
    }
}

如何用命令接口传参呢?

image-20210613103819526

方法二中的输出

image-20210613104318552

@3 核心概念

模式 mode

通过选择 development, productionnone 之中的一个,来设置 mode 参数

module.exports = {
  mode: 'production',
};
入口 entry

入口起点(entry point) 指示 webpack 应该使用哪个模块,来作为构建其内部 依赖图(dependency graph) 的开始。

webpack 会递归的构建一个 *依赖关系图*下文会分析打包后的文件

一个入口

module.exports = {
  //无论配置文件在哪里./都是根目录
  entry: './src/index.js',
};

多个入口

 等待写

输出 output

output 属性告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件。主要输出文件的默认值是 ./dist/main.js,其他生成文件默认放置在 ./dist 文件夹中。

  • path 打包后的文件放在哪里
  • filename 打包后的文件名称
const path = require('path');

module.exports = {
  output: {
    // 必须是个绝对地址
    path: path.resolve(__dirname, 'dist'),
    filename: 'mydist,
  },
};
loader

什么是loader?

让我们一起看看官方文旦怎么说

webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效 模块,以供应用程序使用,以及被添加到依赖图中。

image-20210613105951192

loader 从右到左(或从下到上)地取值(evaluate)/执行(execute)

下面1.3.2会具体说明

让我们通过代码来进一步了解一下loader

const path = require('path');

module.exports = {
  output: {
    filename: 'my-first-webpack.bundle.js',
  },
  module: {
    rules: [{ test: /\.txt$/, use: 'raw-loader' }],
  },
};

以上配置中,对一个单独的 module 对象定义了 rules 属性,里面包含两个必须属性:testuse。这告诉 webpack 编译器(compiler) 如下信息:

“嘿,webpack 编译器,当你碰到「在 require()/import 语句中被解析为 '.txt' 的路径」时,在你对它打包之前,先 use(使用) raw-loader 转换一下。”

下面我们会细说一下常用的loader

插件 plugin

让我们看看官方介绍

想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new 操作符来创建一个插件实例。

@1 引入

@2 在plugins数组中 new 出来

在这一步我们简单了解一下如何使用plugin 就好

下面我们会细说一下常用的 plugin

const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装
const webpack = require('webpack'); // 用于访问内置插件

module.exports = {
  plugins: [
      new HtmlWebpackPlugin({ template: './src/index.html' })
  ],
};

1.3一些基本需求

1.3.1 引入html 使其与 打包好的js自动关联

www.npmjs.com/package/htm…

@1 npm i -D html-webpack-plugin

这是一个plugin 所以遵循上面所说的plugin的使用规则

const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: 'index.js',
  output: {
    path: __dirname + '/dist',
    filename: 'index_bundle.js'
  },
  plugins: [
    new HtmlWebpackPlugin()
  ]
}

new HtmlWebpackPlugin()中传入一个对象中有些常用参数

  • template 指定html模板地址 string

  • templateContent 指定模板

    templateContent = `<html>
    	.....
    </html>`
    
  • filename 打包后的文件名字

    //[hash]会随机生成hash值  对清除缓存很有效
    filename:'webpack[hash].html'
    
  • title 指定html1的title 前提是你遵循了ejs的语法

    • <title><%= htmlWebpackPlugin.options.title %></title>

    我们可以这样玩

    @1 在根目录下新建一个public文件夹

    @2 在public文件夹下创建一个index.html 为html模板

    @3 配置

    plugins:[
        new HtmlWebpackPlugin({
            template:'./public/index.html'
        })
    ]
    

    @4 打包

    之后就可以在dist文件夹下看到生成了一个index.html并且自动帮我们关联上打包的js

    image-20210613130749830

1.3.2 每次打包清除之前打包的文件

www.npmjs.com/package/cle…

@1 引入

const { CleanWebpackPlugin } = require('clean-webpack-plugin');

@2 使用一下plugin

plugins:[
    CleanWebpackPlugin();
]

这样以后你会发现每次npm run build都是一个新的dist

1.3.3 引入css

因为webpack只认识js和json文件所以你需要引入loader告诉 webpack 加载 CSS 文件

@1 安装相关loader

npm install  css-loader style-loader

@2 引入css到js中

import './test.css'

@3 配置loader

module: {
    rules: [
        {
            test:/\.txt$/,
            use:'raw-loader'
        },
        {
            test:/\.css$/,
            // 从右向左解析原则
            use:['style-loader','css-loader']
        },
    ]
},

先执行 css-loader

  • css-loader 的作用是处理css中的外部资源
  • 通俗的说就是把import/require 引入的css变成webpack认识的资源

再执行 style-loader

+  把样式插入到 DOM中,方法是在head中插入一个style标签,并把样式写入到这个标签的 innerHTML里

之后打包的index.html中就会有样式

image-20210613140049471

1.3.4 提高引入css的兼容性

www.npmjs.com/package/pos…

为啥要引入 postcss-loader 呢

@1 安装

npm install --save-dev postcss-loader postcss

@2 使用方法

  • 在loader中引入

  • 在根目录下写一个postcss.config.js配置文件

    module.exports = {
        plugins: [
            [
                "postcss-preset-env",
                {
                    // Options
                },
            ],
        ],
    };
    
  • 配置 .browserslistrc

那什么是browserslistrc呢?

可以参考这个

www.jianshu.com/p/d45a31c50…

cover 99.9%		
  • 兼容市面上百分之99.9的浏览器

这东西主要是配置浏览器兼容性

//.browserslistrc
last 1 version
> 1%
maintained node versions
not dead

1.3.5 拆分css

由直接插入style标签转为link引入

www.npmjs.com/package/min…

@1 安装

npm install --save-dev mini-css-extract-plugin

@2 使用

const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  plugins: [new MiniCssExtractPlugin()],
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [MiniCssExtractPlugin.loader, 'css-loader'],
      },
    ],
  },
};
  • filename 打包后的css放在dist的哪里

1.3.5 引入图片

在 webpack 5 之前,通常使用:

我们先使用一下url-loader file-loader

@1 安装

npm install url-loader file-loader --save-dev

@2 引入图片资源

import img from './image.png';

@3 配置

{
    test: /\.(png|jpg|gif)$/i,
    use: [
        {
            loader: 'url-loader',
            options: {
                limit: 1024 * 50,
                name:'imgs/[name].[ext]'
            },
        },
    ],
},
  • limit 限制大小 1024 * 50 就是 50kb
  • name 文件放到哪里 【name】表示原来的名字 【ext】表示原来的文件后缀

官网说如果超过了limit限制的大小就会使用file-loader

就是说没超过是一个 base64的图片 不会把图片打包过去

超过了是一个http路径地址 会把图片打包过去

使用base64可以减少http请求优化网页


效果

图片原本大小51.4KB 我设置的limit是50kb

image-20210613165650256

image-20210613170600843

image-20210613170622385


webpack5

资源模块类型(asset module type),通过添加 4 种新的模块类型,来替换所有这些 loader:

  • asset/resource 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现。
  • asset/inline 导出一个资源的 data URI。之前通过使用 url-loader 实现。
  • asset/source 导出资源的源代码。之前通过使用 raw-loader 实现。
  • asset 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源体积限制实现。

举一个例子打包成base64

{
    test: /\.(png|jpg|gif)$/i,
    type: 'asset/inline'
}

那么asset/resource打包后的图怎么指定路径和文字呢

看一眼官方文档

默认情况下,asset/resource 模块以 [hash][ext][query] 文件名发送到输出目录。

可以通过在 webpack 配置中设置 output.assetModuleFilename 来修改此模板字符串:

所以我们需要在output中设置

output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'mywebpack.js',
    assetModuleFilename: 'images/[name][ext]'
},

1.4 拆分配置文件/拆分webpack.config.js

看一眼官方怎么说

development(开发环境)production(生产环境) 这两个环境下的构建目标存在着巨大差异。在开发环境中,我们需要:强大的 source map 和一个有着 live reloading(实时重新加载) 或 hot module replacement(热模块替换) 能力的 localhost server。而生产环境目标则转移至其他方面,关注点在于压缩 bundle、更轻量的 source map、资源优化等,通过这些优化方式改善加载时间。由于要遵循逻辑分离,我们通常建议为每个环境编写彼此独立的 webpack 配置

虽然,以上我们将 生产环境开发环境 做了细微区分,但是,请注意,我们还是会遵循不重复原则(Don't repeat yourself - DRY),保留一个 "common(通用)" 配置。为了将这些配置合并在一起,我们将使用一个名为 webpack-merge 的工具。此工具会引用 "common" 配置,因此我们不必再在环境特定(environment-specific)的配置中编写重复代码。

webpack.docschina.org/guides/prod…

@1 安装

npm install --save-dev webpack-merge

@2 新建一个config目录

把原先的webpack.config.js拆分

+ 公共配置    	   webpack.common.js
+ 开发环境配置   webpack.dev.js
+ 生产环境配置   webpack.production
- |- webpack.config.js
+ |- webpack.common.js
+ |- webpack.dev.js
+ |- webpack.prod.js

image-20210613192927132

@3 写配置

公共配置 webpack.common.js

const path = require("path");
module.exports = {
    entry:'./src/index.js',
    output:{
        path: path.resolve(__dirname),
        filename: 'mywebpack.js',
    }
}

开发环境配置 webpack.dev.js

const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
    mode: 'development'
});

生产环境配置 webpack.prod.js

const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
    mode: 'production'
});

2.开发服务器(devServer)

首先我们要知道 webpack-dev-server 的底层是nodejs 的 http模块

运行它会把打包后的index.html运行

@1 安装

npm i webpack-dev-server

@2 使用webpack-dev-server

配置 @2.1

var path = require('path');

module.exports = {
  //...
  devServer: {
    contentBase: path.join(__dirname, 'dist'),
    compress: true,
    port: 9000,
  },
};

@2.2 在webpack 后面加上 serve

image-20210613202415830

当服务(server)启动后,在解析模块列表之前输出一条消息:

image-20210613202517708


接下来就是一些和webpack server相关的属性

contentBase

告诉服务器内容的来源。仅在需要提供静态文件时才进行配置

const path = require('path');
module.exports = {
  //...
  devServer: {
    contentBase: path.join(__dirname, 'public'),
  },
};

before

提供了一个在 devServer 内部的 所有中间件执行之前的自定义执行函数。

看一下官方给的例子

module.exports = {
  //...
  devServer: {
    before: function (app, server, compiler) {
      app.get('/some/path', function (req, res) {
        res.json({ custom: 'response' });
      });
    },
  },
};

会node express框架的小伙伴有没有觉得很像express 的 接口语法

我们用get请求一下

image-20210613204819143

是可以跑通的

我们可以利用这一特点模拟请求做一些假数据


在模板 index.html中

fetch('/api/list').then((res) => {
    return res.json()
}).then((res) => {
    console.log(res);
})

webpack.config.js

before: function (app, server, compiler) {
    app.get('/api/list', function (req, res) {
        res.json({ custom: '你好' });
    });
},

image-20210613212643286

open

告诉 dev-server 在服务器启动后打开浏览器。 将其设置为 true 以打开默认浏览器

module.exports = {
  //...
  devServer: {
    open: true,
  },
};

proxy

配置代理的 Vue.config.js 和这个差不多它的底层可能是和webpack配置合并

简单写法

module.exports = {
  //...
  devServer: {
    proxy: {
      '/api': 'http://localhost:3000',
    },
  },
};

只要是/api开头的请求都会转发给 http://localhost:3000

拓展写法

module.exports = {
  //...
  devServer: {
    proxy: {
      '/api': {
        target: 'http://localhost:3000',
        pathRewrite: { '^/api': '' },
      },
    },
  },
};

只要是/api开头的请求都会转发给 http://localhost:3000 并且 ‘/api’ 会重写为 空字符串

比如 实际请求的是 /list 接口

axios('/api/list').then(res=>{})

changeOrigin: true

默认情况下,代理时会保留主机头的来源,可以将 changeOrigin 设置为 true 以覆盖此行为

这玩意有时候可以有时候可以骗过后端的拦截

比如 localhost:3000 发给 localhost:9000 请求的接口是/list

changeOrigin: true -> 后端获取的源(host / origin) localhost:9000

changeOrigin: false -> 后端获取的源(host / origin) localhost:3000

progress

将运行进度输出到控制台

使用方法

webpack serve --progress

hotOnly

启用热模块替换(请参见 devServer.hot ),而无需页面刷新作为构建失败时的回退. 热更新

webpack.config.js

module.exports = {
  //...
  devServer: {
    hotOnly: true,
  },
};

通过命令行使用

webpack serve --hot-only