背景

  • 随着前端的快速发展,目前前端的开发已经变的越来越复杂了
    • 比如开发过程中我们需要通过模块化的方式来开发
    • 比如也会使用一些高级的特性来加快我们的开发效率或者安全性,比如通过ES6+、TypeScript开发脚本逻辑,通过sass、less等方式来编写css样式代码
    • 比如开发过程中,我们还希望实时的监听文件的变化来并且反映到浏览器上,提高开发的效率;比如开发完成后我们还需要将代码进行压缩、合并以及其他相关的优化
    • 等等….
  • 但是对于很多的前端开发者来说,并不需要思考这些问题,日常的开发中根本就没有面临这些问题
    • 这是因为目前前端开发我们通常都会直接使用三大框架来开发:Vue、React、Angular
    • 但是事实上,这三大框架的创建过程我们都是借助于脚手架(CLI )的
    • 事实上Vue-CLI、create-react-app、Angular-CLI都是基于webpack来帮助我们支持模块化、less、TypeScript、打包优化等的

      介绍

      官方解释

      webpack is a static module bundler for modern JavaScript applications

webpack是一个静态的模块化打包工具,为现代的JavaScript应用程序;我们来对上面的解释进行拆解
打包bundler : webpack可以将帮助我们进行打包,所以它是一个打包工具
静态的static :这样表述的原因是我们最终可以将代码打包成最终的静态资源(部署到静态服务器)
模块化module : webpack默认支持各种模块化开发,ES Module、CommonJS、AMD等
现代的modern:正是因为现代前端开发面临各种各样的问题,才催生了webpack的出现和发展
WebPack介绍 - 图1

  • JavaScript的打包
    • 将ES6转换成ES5的语法
    • TypeScript的处理,将其转换成JavaScript
  • Css的处理
    • CSS文件模块的加载、提取
    • Less、Sass等预处理器的处理
  • 资源文件img、font
    • 图片img文件的加载
    • 字体font文件的加载
  • HTML资源的处理
    • 打包HTML资源文件
  • 处理vue项目的SFC文件

    • vue文件

      配置/设置

  • 我们发现是可以正常进行打包的,但是有一个问题,webpack是如何确定我们的入口的呢?

    • 事实上,当我们运行webpack时,webpack会查找当前目录下的src/index.js作为入口
    • 所以,如果当前项目中没有存在src/index.js文件,那么会报错

      通过命令(局限)

      通过配置来指定入口和出口
      1. npx webpack --entry ./src/main.js --output-path ./build

      通过scripts(局限)

      但是每次这样执行命令来对源码进行编译,会非常繁琐,所以我们可以在package.json中增加一个新的脚本
      WebPack介绍 - 图2

      通过配置文件(常用)

      webpack内置了一些配置,无需我们自己手动配置。
      如果我们希望自己手动配置一些东西,就要在项目下创建一个 webpack.config.js 文件,这个就是webpack的配置文件。
      webpack每次打包时,都会检查项目下有没有这个文件,没有的话就用默认配置,有的话就用这里的配置。
      下面是一个最基本的 webpack.config.js 的例子: ```javascript // ./src/webpack.config.js

// 这个是nodejs的路径api,这里用于帮助我们得到绝对路径 const path = require(‘path’);

// 这里是导出一个对象,对象的属性就是配置 module.exports = { // 指定项目的js入口 entry: “./src/main.js”,

// 指定打包输出后到哪个文件夹,默认是项目下的dist文件夹 output: { // 这里提供nodejs的path 路径API,获取当前文件 webpack.config.js 所在文件夹的绝对路径,然后指向build文件夹 // 意思是输出到项目下的build文件夹,而不是默认的dist path: path.resolve(__dirname, “./build”),

  1. // 指定输出后的文件名,默认就是bundle.js
  2. filename: "bundle.js"

} }

  1. 如果我不想用 webpack.config.js 这个名字,想换一个,可以在package.json里的scripts里面设置<br />![](https://cdn.nlark.com/yuque/0/2021/png/725902/1622281318170-7455ae87-c5ea-45ab-8b80-365afbaf9b14.png#crop=0&crop=0&crop=1&crop=1&from=url&id=kmWg2&margin=%5Bobject%20Object%5D&originHeight=89&originWidth=492&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)<br />其他配置可以查看官网:[https://webpack.docschina.org/configuration/](https://webpack.docschina.org/configuration/)
  2. <a name="QuR9b"></a>
  3. # 入口
  4. 什么是入口?
  5. 就是整个项目js的入口,webpack需要一个入口,才能识别到哪些文件、代码需要打包,哪些不需要。
  6. <a name="uvAhm"></a>
  7. # 依赖图
  8. - webpack到底是如何对我们的项目进行打包的呢?
  9. - 事实上webpack在处理应用程序时,它会根据命令或者配置文件找到入口文件
  10. - 从入口开始,会生成一个依赖关系图,这个依赖关系图会包含应用程序中所需的所有模块(比如.js文件、css文件、图片、字体等)
  11. - 然后遍历图结构,打包一个个模块(根据文件的不同使用不同的loader来解析)
  12. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/12695178/1653638360056-0164a051-abbc-4247-ace5-7636b5755485.png#clientId=ufc41e7ac-3eae-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=465&id=u068a5021&margin=%5Bobject%20Object%5D&name=image.png&originHeight=581&originWidth=1360&originalType=binary&ratio=1&rotation=0&showTitle=false&size=59268&status=done&style=none&taskId=ubc391768-c2ad-48c9-a19e-ffd9411b8af&title=&width=1088)<br />也就是所有和主入口有关系的文件,都会被打包。
  13. 怎么产生关系?通过主入口引入的文件(如importrequire等),以及后面再引入到这个文件的文件,这些都是。<br />这是一种图结构,文件和文件可能相互依赖,同时也会识别哪些已经加载过,不会重复打包
  14. <a name="zptT8"></a>
  15. # 加载器 Loader
  16. - loader可以用于对模块的源代码进行转换
  17. - 我们可以将css文件也看成是一个模块,我们是通过import来加载这个模块的
  18. - 在加载这个模块时,webpack其实并不知道如何对其进行加载,我们必须制定对应的loader来完成这个功能
  19. webpack 目前来说只是帮我们打包js文件,其他文件如.css是不认识的。<br />而通过安装一些加载器Loader,可以帮助webpack认识这些文件,并且一起打包。
  20. <a name="pfPoz"></a>
  21. ## 如何使用Loader?
  22. 1、安装对应的loader,比如要处理和识别css,就要安装css-loader
  23. ```bash
  24. npm install loader名字 -D

2、给webpack添加对应loader的配置,告诉webpack怎么使用

  1. // ./src/webpack.config.js
  2. const path = require('path');
  3. module.exports = {
  4. entry: "./src/main.js",
  5. output: {
  6. path: path.resolve(__dirname, "./build"),
  7. filename: "bundle.js"
  8. },
  9. // 配置loader模块
  10. module: {
  11. rules: [ // 规则属性
  12. {}, // loader对应的配置
  13. {},
  14. ......
  15. {},
  16. ]
  17. }
  18. }

其他loader可以查看:
WebPack-loader 加载器

插件 Plugin

loader做不了的事情,webpack某个阶段想做的事情,都是通过插件完成(如打包时自动删除没有使用的文件)
loader只是处理webpack打包,而插件是在任何webpack生命周期都会起作用。

  • Loader是用于特定的模块类型进行转换
  • Plugin可以用于执行更加广泛的任务,比如打包优化、资源管理、环境变量注入等

WebPack介绍 - 图3
其他插件可以查看
Webpack - Plugin 插件

打包模式 Mode

背景

默认打包,是把所有js代码都拷贝到1个叫 bundle.js 的文件内,丑化和优化过的,只有1行。

但是缺点是,自己调试的时候,如果有个地方出错了,你很难找到到底是哪里出错了,因为都是只有1行,而且不会告诉你哪个位置(如下图)
WebPack介绍 - 图4
WebPack介绍 - 图5

开发模式(方便调试错误)

WebPack介绍 - 图6

如果把mode设置成开发模式,那么打包出来的bundle.js 就是没有压缩和丑化的。
WebPack介绍 - 图7
但结果还是看的有点吃力,这是因为devtool默认值是”eval”,把这个改为”source-map”就可以了。
会多一个 .js.map 的文件,这个是一个映射文件,意思是打包后的bundle.js 通过映射,映射到我们的源代码里面,这样就可以查看了。
image.png
image.png
image.png

其他选项

  • Mode配置选项,可以告知webpack使用响应模式的内置优化
    • 默认值是production(什么都不设置的情况下)
    • 可选值有: 'none' | 'development' | 'production'

WebPack介绍 - 图11

webpack中使用Vue

开发环境配置、生产环境配置

背景

  • 目前我们所有的webpack配置信息都是放到一个配置文件中的: webpack.config.js
    • 当配置越来越多时,这个文件会变得越来越不容易维护
    • 并且某些配置是在开发环境需要使用的,某些配置是在生成环境需要使用的,当然某些配置是在开发和生成环境都会使用的
    • 所以,我们最好对配置进行划分,方便我们维护和管理

比如开发的时候,配置是开发环境的,打包上生产环境的时候,还是用这套配置就不合适,但是不可能打包的时候把代码改成生产的,然后又改回开发的。

方案一:两个配置文件

编写两个不同的配置文件,开发和生成时,分别加载不同的配置文件即可;

1、创建文件

image.png

2、修改脚本

package.json中:
image.png

3、安装webpack-merge插件

  1. npm install webpack-merge -D

4、编写公共配置

把开发的配置和生成环境的配置抽出来放到对应的文件,这里只留下两边共有的

  1. // webpack.comm.config.js
  2. const path = require("path");
  3. const HtmlWebpackPlugin = require("html-webpack-plugin");
  4. const { DefinePlugin } = require("webpack");
  5. const { VueLoaderPlugin } = require('vue-loader/dist/index');
  6. module.exports = {
  7. target: "web",
  8. entry: "./src/main.js",
  9. output: {
  10. path: path.resolve(__dirname, "../build"), // 路径要调整一下
  11. filename: "js/bundle.js",
  12. },
  13. resolve: {
  14. extensions: [".js", ".json", ".mjs", ".vue", ".ts", ".jsx", ".tsx"],
  15. alias: {
  16. "@": path.resolve(__dirname, "../src"), // 路径要调整一下
  17. "js": path.resolve(__dirname, "../src/js") // 路径要调整一下
  18. }
  19. },
  20. module: {
  21. rules: [// 公共的规则],
  22. },
  23. plugins: [
  24. new HtmlWebpackPlugin({
  25. template: "./public/index.html",
  26. title: "哈哈哈哈"
  27. }),
  28. new DefinePlugin({
  29. BASE_URL: "'./'",
  30. __VUE_OPTIONS_API__: true,
  31. __VUE_PROD_DEVTOOLS__: false
  32. }),
  33. new VueLoaderPlugin()
  34. ],
  35. };

5、编写开发配置文件

  1. // webpack.dev.config.js
  2. // 1、引入安装的webpack-merge
  3. const { merge } = require('webpack-merge');
  4. // 2、引入公共文件
  5. const commonConfig = require('./webpack.comm.config');
  6. // 3、用nodeJS的方式导出模块,merge函数就是合并两个配置的
  7. module.exports = merge(commonConfig, {
  8. mode: "development",
  9. devtool: "source-map",
  10. devServer: {
  11. contentBase: "./public",
  12. hot: true,
  13. // host: "0.0.0.0",
  14. port: 7777,
  15. open: true,
  16. // compress: true,
  17. proxy: {
  18. "/api": {
  19. target: "http://localhost:8888",
  20. pathRewrite: {
  21. "^/api": ""
  22. },
  23. secure: false,
  24. changeOrigin: true
  25. }
  26. }
  27. },
  28. })

6、编生产配置文件

  1. //webpack.prod.config.js
  2. const { CleanWebpackPlugin } = require("clean-webpack-plugin");
  3. const CopyWebpackPlugin = require('copy-webpack-plugin');
  4. // 1、引入安装的webpack-merge
  5. const {merge} = require('webpack-merge');
  6. // 2、引入公共文件
  7. const commonConfig = require('./webpack.comm.config');
  8. // 3、用nodeJS的方式导出模块,merge函数就是合并两个配置的
  9. module.exports = merge(commonConfig, {
  10. mode: "production",
  11. plugins: [
  12. new CleanWebpackPlugin(),
  13. new CopyWebpackPlugin({
  14. patterns: [
  15. {
  16. from: "./public",
  17. globOptions: {
  18. ignore: [
  19. "**/index.html"
  20. ]
  21. }
  22. }
  23. ]
  24. }),
  25. ]
  26. })

方案二:一个配置文件,设置参数

使用相同的一个入口配置文件,通过设置参数来区分它们

运行原理图

image.png

image.png