从零开始学习Webpack

前言:由于项目要进行性能优化,项目有点老:Vue2+Webpack3。正好借此机会系统的学习一下Webpack。以下是我总结的Webpack的一些内容(如有不对,欢迎指正)。这篇文章还未涉及到CSS代码分割、Tree shaking等,因为我也没想好怎么弄😂。下篇文章我会记录使用Webpack对项目进行的一些代码优化。

Webpack的由来

在传统的页面设计中,当随着业务的增多,我们的脚本文件也越来越多,然后按照特定的顺序把脚本依次引入到页面中。这种方法存在很多问题:全局命名变量污染、脚本请求过多导致页面加载时间过长。

Webpack是什么

Webpack是Javascript应用程序的静态模块打包器,它允许我们将多个脚本文件打包成少数几个可以通过浏览器加载的文件。

使用Webpack进行打包

命令:npx webpack <文件名>

  • npx
    • Node.js的包执行工具,它随Node.js的包管理器npm一起安装,npx允许你执行在本地或远程npm包中的命令,而不需要全局安装这些包。这意味着即使你没有全局安装Webpack,也可以使用npx来运行Webpack。
  • webpack
    • 指的是webpack本身,一个静态模块打包工具。你在项目中运行npx webpack时,Webpack会根据你的配置(通常在一个名为webpack.config.js的文件中)来打包你的JavaScript应用。

补充:也可指定一个Webpack配置文件进行打包 npx webpack --config <文件名>,显然npx 这个命令不常见,我们经常使用 npm run build那是因为 npm run命令用于执行在Node.js项目的 package.json 文件中定义的脚本。如:

json 复制代码
{
  "name": "1",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "webpack"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^4.41.2",
    "webpack-cli": "^3.3.10"
  }
}

我们执行 npm run build相当于执行scripts脚本中的buildNode.js 会帮我们从node_module文件夹下的.bin文件夹下找到webpack,然后执行

Webpack基本配置项

Webpack默认只支持CommonJs规范,需使用require进行模块引入

entry

Webpack打包时文件的入口

js 复制代码
module.exports = {
  entry: "./index.js"  
}

output

Webpack在哪里输出打包后的文件。该字段是一个对象,通常包含以下关键属性:

  • filename:定义输出文件的名称
  • path:指定输出文件的绝对路径,通常结合Node.js的path模块结合使用
  • publicPath:一个非常重要的配置项,它指定了输出文件的公共URL。当我们打包生产环境代码时。它用于在文件前加入CDN链接来确保资源的正常加载
js 复制代码
const path = require ("path") // 引入Node中的path模块

module.exports = {
    entry: "./index.js"  // 表示以webpack当前文件夹下的index.js文件作为入口
    output: {
        // 文件打包完之后的文件名称
        filename: "bundle.js",
        // __dirname是当前目录下, bundle生成文件夹名称
        path: path.resolve(__dirname, "bundle"),
        publicPath: "http://cdn.com.cn/"
    }
}

打包后index.html:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app"></div>
    // 在bundle.js前加入了 http://cdn.com.cn/
    <script src="http://cdn.com.cn/bundle.js"></script></body>
</html>

补充:上述案例都是一个入口Js文件,如果在页面中引用多个Js文件怎么办?

答:可以将entry写成一个对象,提供多个文件

js 复制代码
const path = require ("path")

module.exports = {
  entry: {
    // main -> 打包后文件名 "./index.js" -> 指定打包路径
    main: "./index.js",
    test: "./test.js"
  },
  output: {
    filename: "[name].js",
    path: path.resolve(__dirname, "dist")
  }
}

上述案例中 maintest 为我们想要打包后生成的文件名,需要结合[name]占位符进行使用

module

配置项是webpack用于定义如何处理项目中不同类型的模块。以下是它的一些关键属性

  • rules :module对象中最重要的一个属性,用于定义一系列的加载器(loaders)。加载器可以转换文件,或者对文件进行编译处理。如:ES6转ES5,TypeScript转JavaScript,Sass转CSS 。每条规则包含以下属性:
    • test:一个正则表达式,用于匹配需要应用这个规则的文件。
    • use :是rules配置中核心部分,它用于指定模块的加载器(loaders),通常是一个包。以下是它的一些关键属性:
      • loader:文件加载器,指定相应loader进行处理
      • options :设置文件的命名规则,输出路径。它也存在几个关键属性
        • name :指定输出文件的名称,可使用占位符来定义文件的格式: [name][ext][hash] , 分别表示原始文件名称、扩展名、将文件名转换为hash
        • outputPath:指定输出文件所在的文件夹名称
    • exclude :排除某些文件,通常用于排除 node_modules 目录。
    • include:和exclude相反,用于指定包含的文件

plugins

用于指定一系列的插件,用于扩展和增强Webpack的功能。它是一个数组,数组中的每一个元素都是一个插件实例。 常见的Webpack插件:
HtmlWebpackPlugin(生成html文件)CleanWebpackPlugin(清除原有的打包的文件)HotModuleReplacementPlugin(热模块)

js 复制代码
const HtmlWebpackPlugin = require ("html-webpack-plugin")
const { CleanWebpackPlugin } = require ("clean-webpack-plugin")
const webpack = require ("webpack")

module.exports = {
    plugins: [
        new HtmlWebpackPlugin ({
            template: "./index.html" // 这里用于指定一个Html文件
        }),
        new CleanWebpackPlugin(), // 删除掉原本存在的打包文件
        new webpack.HotModuleReplacementPlugin()
    ]
}

sourceMap

sourceMap是一种在已编译的代码中提供原始源代码信息的技术。它使得开发者能够在调试已压缩、合并或转换(TypeScript -> JavaScript)的代码时,追踪到原始源代码。

  • 配置sourceMap
js 复制代码
module.exports = {
    // 开发模式建议配置 cheap-module-eval-source-map
    mode: "development"
    devtool: "cheap-module-eval-source-map"
    // 生产打包建议配置:cheap-module-source-map
    mode: "production",
    devtool: "cheap-module-source-map"
}

sourceMap文档

WebpackDevServer

webpack-dev-server包为我们提供了一个简单的web服务器和实时重新加载的功能。

  • 特性:
    • 支持热模块替换(HMR):HTM允许模块在运行时被更新,而无需完全刷新页面
    • 提供 HTTP 服务:它为您的应用程序提供了一个简单的 HTTP 服务器,这对于开发 SPA(单页应用程序)特别有用。
    • 代理API请求:可自己进行配置
  • 它的一些主要配置项

contentBase

指定服务器从哪个目录提供静态资源。这些资源不会被webpack处理。如:使用VUE CLI创建的项目,publicstatic会存放静态资源 --> 静态的html文件,大型的图片等

js 复制代码
module.exports = {
    devServer: {
        contentBase: "./public"
    }
}

port

指定要监听的端口

js 复制代码
module.exports = {
    devServer: {
        port: 8080
    }
}

hot

是否启用HMR(热模块替换),不刷新整个页面的情况下通过新模块替换旧模块, 通常结合HotModuleReplacementPlugin使用

js 复制代码
const webpack = require ("webpack")

module.exports = {
    devServer: {
        hot: true
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin()
    ]
}

open

启动服务器后自动打开浏览器

js 复制代码
module.exports = {
    devServer: {
        open: true
    }
}

proxy

代理一些特定的URL到另一台服务器上。用于前后端分离时请求后端的API,以下是它的一些关键属性:

target

指定要代理到的目标服务器地址

js 复制代码
module.exports = {
    devServer: {
        proxy: {
        // 以/api为开头的地址代理到                   http://localhost:3000
            "^/api": {
                target: "http://localhost:3000"
            }
        }
    }
}

pathRewrite

重写请求路径。 它的使用场景:
前端和后端分离 :前端开发时,可能会有一个统一的 API 前缀,如 /api,而在后端服务器上,实际的路径可能不包含这个前缀。
开发环境代理:为了方便开发,需要一个代理来转发请求到后端 API 服务器,同时去掉不必要的路径前缀。

js 复制代码
module.exports = {
devServer: {
    proxy: {
        "/api": {
            target: "http://localhost:3000",
            pathWrite: {"^/api": ""}
        }
    }
}
}

上述案例:请求路径中的/api部分会被删除,如/api/demo会被代理到http:localhost:3000/demo

changeOrigin

通常设为 true

js 复制代码
module.exports = {
    devServer: {
        "/api": {
            target: "http://localhost:3000",
            changeOrigin: true
        }
    }
}

secure

若代理目标是https服务器,通常设置为false

js 复制代码
module.exports = {
    devServer: {
        proxy: {
             "/api": {
                target: "https://localhost:3000",
                secure: false
            }
        }
    }
}

context

指定特定的路径被代理

js 复制代码
module.exports = {
   devServer: {
       proxy: [{
           context: ["/auth", "/api"],
           target: "http://localhost:3000"
       }]
   }
}

上述案例:指定以authapi为开头的URL路径被代理

处理图片模块

webpack默认只能处理JS模块部分,无法处理图片。如果想要解析图片,需要用到两个包 url-loaderfile-loader,并在webpackmodule配置项中进行配置

url-loader

将图片解析为base64位编码。应用于占据内存较小的图片

file-loader

将图片单独声明一个文件,在页面上进行引用。 应用于较大图片

我们再使用这两个loader中,肯定是希望较小的图片解析为base64位编码,直接在页面中进行引用。图片过大则使用flie-loader单独解析。那如何进行区分呢?答:在options配置项中使用limit进行限制,通常是20480(20kb)字节

js 复制代码
const path = require ("path")

module.exports = {
    module: {
        rules: [
            {
            test: /.(png|jpe?g|gif)$/,
            use: {
                loader: "url-loader",
                options: {
                  name: "[name].[ext]", // 输出原始文件名和扩展名
                  outputPath: "images", // 输出文件所在目录
                  limit: 20480 // 小于20480使用url-loader进行解析,大于则使用file-loader进行解析
                 }
               }
            }
        ]
    }
}

file-loader官方文档

url-loader官方文档

处理样式模块

Webpack默认只能处理Js模块,无法处理引用的Css文件。需要使用处理Css模块的loader:style-loader、css-loader

css-loader

用于处理Css文件,例如:我们在JS文件中使用import引入Css文件时,css-loader会处理这个Css文件

style-loader

将处理过的Css文件注入到页面中的Style标签中

js 复制代码
module.exports = {
    module: {
        rules: [
            {
                test: /\.css$/,
                use: ["style-loader", "css-loader"] // 执行顺序,从右往左,从下往上
            }
        ]
    }
}

当我们想使用sass语法时也需要使用相应的加载器(loader)来将sass与语法解析成css

loader: sass-loader node-sass

sass-loader

和css-loader类似,用来处理引入的.scss/.sass文件

node-sass

它是一个库,将.sass/.scss文件转换为有效的Css

js 复制代码
module.exports = {
    module: {
        rules: [
            {
                test: /\.scss$/,
                use: ["style-loader", "css-loader", "sass-loader"]
            }
        ]
    }
}

此外当我们使用Css3样式时 如:transform属性时,某些浏览器版本可能过低造成不兼容或需要兼容不同类型浏览器时,我们需要使用postcss-loaderautoprefix对css进行转换处理。

postcss-loader

它主要允许我们在使用webpack构建项目时使用到postCss
postCss的作用:通过一系列插件来拓展和增强Css的功能。这些插件可以有:

  • 添加浏览器前缀:通过autoprefix插件来自动添加不同浏览器所需的CSS前缀。
  • 优化Css:压缩CSS代码,删除不必要的代码和注释,提高性能。如purgecss

使用postcss-loader:需要分别在 webpack.config.jspostcss.config.jspackage.json三个文件夹中进行修改

webpack.config.js基本配置

js 复制代码
module.exports = {
    module: {
        rules: [
            {
                test: /\.scss$/,
                use: ["style-loader", "css-loader", "postcss-loader", "sass-loader"]
            }
        ]
    }
}

postcss.config.js文件配置(若无需手动创建)

js 复制代码
module.exports = {
    plugins: [
        require ("autoprefixer") // 引入 autoprefixer插件
    ]
}

package.json文件:添加 browserslist字段 -> 指定项目支持浏览器的范围

json 复制代码
"browserslist": [
    "> 1%", 
    "last 2 versions"
]

Css模块化

当我们在main.js(项目文件的入口)文件中引入一个.css文件,它的样式默认对全局的的标签生效,那么如何只对一个指定的模块化生效呢?答:css-loader进行模块配置,将modules 字段设置为true

webpack.config.js

js 复制代码
module.exports = {
    module: {
        rules: [
            {
                test: /\.scss$/
                use: [
                    "style-loader",
                    {
                        loader: "css-loader",
                        options: {
                            modules: true 
                        }
                    },
                    "postcss-loader",
                    "sass-loader",
                ]
            }
        ]
    }
}

main.js示例:

js 复制代码
import jspp from "./images/1.jpeg" // 引用本地图片,当作src
import styles from "./index.scss" // 引入scss

const img = new Image()
img.src = jspp
img.className += `${styles.logo}` // Css模块化

const app = document.getElementById("app")
app.appendChild(img)

补充:postcss-loader官方文档

处理字体样式

和处理图片类似,需要使用 url-loader和file-loader

webpack.config.js

js 复制代码
module.exports = {
    module: {
        rules: [
            {
                test: /\.(eot|svg|ttf|woff)$/,
                loader: "url-loader",
                options: {
                    limit: 20480
                }
            }
        ]
    }
}

上述案例:我们使用url-loader解析字体文件,小于20480B使用url-loader解析成base64直接渲染到页面上,大于则使用了file-loader单独解析引用

ES6 -> ES5

前言:因为Babel的版本不同,版本的配置也不同。以下讲解的是Babel@7版本以上的配置。

当我们使用ES6语法(如: 箭头函数、let、const等)时,一些老版本浏览器兼容不了这些API,就需要将ES6转成ES5。所需依赖:babel-loader@babel/core@babel/p引擎set-env 。但同时ES6新增的内置对象(如:Promise、async/await)和方法(如:map)低版本的JavaScript引擎根本不存在,也需要进行解决。所需依赖:core-jsregenerator-runtime

babel-loader

它相当于一个中间件,将Babel和Webpack连接起来。在Webpack编译中,它将.js文件传递给Babel进行编译

@babel/core

Babel的核心功能包,它包含了Babel的转换引擎和核心API。其本身不参与代码转换,而是作为其他Babel工具(插件和预设)的基础 它的编译过程如下:
babel-loader调用@babel/core的API,而@babel/core又会根据配置的插件和预设来转换代码

@babel/preset-env

Babel的预设,ES6转换为向后兼容的JavaScript版本

core-js

一个标准的polyfill ,它包含ES6新增的一些(如:Promise、Map、Set、Symbol等),将这些特性运行在低版本浏览器中。和preset-env搭配使用,可以按需进行引入

regenerator-runtime

配合core-js 一起使用,专门用于转换生成器(Generators)和异步函数(async/await

进行配置

webpack.config.js

js 复制代码
module.exports = {
    module: {
        rules: [
            {
                test: /\.js$/,
                loader: "babel-loader",
                options: {
                    presets: [[
                        "@babel/preset-env", {
                            useBuiltIns: "usage",
                            corejs: 3 // 这里需要和core-js包版本一致
                        }
                    ]]
                },
                exclude: /node_modules/
            }
        ]
    }
}

当然你如果不想在webpack.config.js 中配置太多babel 属性,我们也可以创建一个.babelrc文件,Webpack在编译时会自动加载这个文件 如:

webpack.config.js

js 复制代码
module.exports = {
    module: {
        rules: [
            {
                test: /.js$/,
                loader: "babel-loader",
                exclude: /node_modules/
            }
        ]
    }
}

.babelrc

js 复制代码
{
    "presets": [[
        "@babel/preset-env",
        {
            "useBuiltIns": "usage",
            "corejs": 3
        }
    ]]
}

以下是测试的Babel版本(无报错)

json 复制代码
"dependencies": {
    "core-js": "^3.33.2",
    "regenerator-runtime": "^0.14.0",
}
"devDependencies": {
    "babel-loader": "^8.2.2",
    "@babel/core": "^7.12.10",
    "@babel/preset-env": "^7.12.11",
}

安装Babel
Babel7官方文档

打包Vue代码

所需依赖vue-loadervue-template-compiler

  • vue2 版本通常对应 vue-loader@15版本
  • vue3 版本通常对应 vue-loader@16版本
  • vue-template-compiler ,它的版本与vue的版本相对应

vue-loader

.vue文件是一个单文件组件,它将HTMLCSSJS 封装到了一个文件夹内。vue-loader 的作用就是将 HTMLCSSJS 提取出来并将它们转换成JavaScript模块形式

vue-template-compiler

主要是来处理Vue文件中HTML部分。当我们使用vue-loader 时,vue-loader 内部会使用vue-template-compiler 来处理HTML模版编译

Vue2版本配置

package.json

json 复制代码
"dependencies": {
    "vue": "2.6.11"
}
"devDependencies": {
    "vue-loader": "15.9.8",
    "vue-template-compiler": "2.6.11",
}

webpack.config.js

js 复制代码
const VueLoaderPlugin = require ("vue-loader/lib/plugin")

module.exports = {
    module: {
        rules: [
            {
                test: /.vue$/,
                loader: "vue-loader"
            },
            {
                test: /\.js$/,
                loader: "babel-loader",
                exclude: /node_modules/,
            },
            {
                test: /\.css$/,
                // loader执行顺序:从后往前,从下往上。
                // style-loader 解析样式 css-loader 解析css引入之间的关系
                use: [
                    "vue-style-loader", 
                    "css-loader",
                    "postcss-loader"
                ]
            },
        ]
    },
    plugins: [
        new VueLoaderPlugin()
    ]
}

注意: 一定要使用 VueLoaderPlugin 插件。它可以将你的rules 配置项应用到Vue单文件组件内
vue-loader中文文档

相关推荐
亚里士多没有德7751 分钟前
强制删除了windows自带的edge浏览器,重装不了怎么办【已解决】
前端·edge
micro2010144 分钟前
Microsoft Edge 离线安装包制作或获取方法和下载地址分享
前端·edge
.生产的驴9 分钟前
Electron Vue框架环境搭建 Vue3环境搭建
java·前端·vue.js·spring boot·后端·electron·ecmascript
awonw12 分钟前
[前端][easyui]easyui select 默认值
前端·javascript·easyui
九圣残炎32 分钟前
【Vue】vue-admin-template项目搭建
前端·vue.js·arcgis
柏箱1 小时前
使用JavaScript写一个网页端的四则运算器
前端·javascript·css
TU^1 小时前
C语言习题~day16
c语言·前端·算法
学习使我快乐014 小时前
JS进阶 3——深入面向对象、原型
开发语言·前端·javascript
bobostudio19954 小时前
TypeScript 设计模式之【策略模式】
前端·javascript·设计模式·typescript·策略模式