Skip to content

一. webpack打包图片


1.加载图片案例准备

  • 为了演示我们项目中可以加载图片,我们需要在项目中使用图片,比较常见的使用图片的方式是两种:
    • img元素,设置src属性
    • 其他元素(比如div),设置background-imagecss属性
image-20220724233458797

2.认识asset module type

  • 我们当前使用的webpack版本是webpack5

    • webpack5之前,加载这些资源我们需要使用一些loader,比如raw-loaderurl-loaderfile-loader
    • webpack5开始,我们可以直接使用资源模块类型asset module type),来替代上面的这些loader
  • 资源模块类型,通过添加4种新的模块类型,来替换所有这些loader

    • asset/resource 发送一个单独的文件并导出 URL
      • 之前通过使用file-loader实现
    • asset/inline 导出一个资源的 data URI
      • 之前通过使用url-loader实现
    • asset/source 导出资源的源代码
      • 之前通过使用raw-loader实现
    • asset 在导出一个data URI和发送一个单独的文件之间自动选择
      • 之前通过使用 url-loader,并且配置资源体积限制实现
    js
          {
            test: /\.(png|jpe?g|svg|gif)$/,
            type: "asset/resource",
            type: "asset/inline",
            type: "asset"
          },

3.asset module type的使用

  • 比如加载图片,我们可以使用下面的方式:

    js
          {
            test: /\.(png|jpe?g|svg|gif)$/,
            type: "asset/resource"
          },
  • 但是,如何可以自定义文件的输出路径和文件名呢?

    • 方式一:修改output,添加assetModuleFilename属性

    image-20220725143814662

    • 方式二:在Rule中,添加一个generator属性,并且设置filename
  • 我们这里介绍几个最常用的placeholder

    • [ext]: 处理文件的扩展名
    • [name]:处理文件的名称
    • [hash]:文件的内容,使用MD4的散列函数处理,生成的一个128位的hash值(32个十六进制)
    js
          {
            test: /\.(png|jpe?g|svg|gif)$/,
            type: "asset/resource",
            generator: {
              // 占位符
              // name: 指向原来的图片名称
              // ext: 扩展名
              // hash: webpack生成的hash,保留下来hash值,防止同名图片冲突,截取哈希值位数 :n
              // filename: "abc.png"
              filename: "./img/[name]_[hash:8][ext]"
            }
          },

4.url-loaderlimit效果

  • 开发中我们往往是小的图片需要转换,但是大的图片直接使用图片即可

    • 这是因为小的图片转换base64之后可以和页面一起被请求,减少不必要的请求过程
    • 而大的图片也进行转换,反而会影响页面的请求速度
  • 我们需要两个步骤来实现:

    • 步骤一:将type修改为asset
    • 步骤二:添加一个parser属性,并且制定dataUrl的条件,添加maxSize属性
    js
    	 {
            test: /\.(png|jpe?g|svg|gif)$/,
            // 1.打包两张图片, 并且这两张图片有自己的地址, 将地址设置到img/bg中
            // // 缺点: 多图片加载的两次网络请求,增加网络请求次数
            // type: "asset/resource" 
            // 2.将图片进行base64的编码, 并且直接编码后的源码放到打包的js文件中
            // 缺点: 造成js文件非常大, 下载js文件本身消耗时间非常长, 造成js代码的下载和解析/执行时间过长,阻塞js文件后面代码的解析
            // type: "asset/inline" 
            // 3.合理的规范:
            // 3.1.对于小一点的图片, 可以进行base64编码
            // 3.2.对于大一点的图片, 单独的图片打包, 形成url地址, 单独的请求这个url图片
            type: "asset",
            parser: {
              dataUrlCondition: {
                maxSize: 60 * 1024 // 60kb
              }
            },
            generator: {
              // 占位符
              // name: 指向原来的图片名称
              // ext: 扩展名
              // hash: webpack生成的hash,保留下来hash值,防止同名图片冲突,截取哈希值位数 :n
              // filename: "abc.png"
              filename: "./img/[name]_[hash:8][ext]"
            }
          }

二.babelbabel-loader


1.为什么需要babel

  • webpack默认打包js代码是不会将es6+代码转换为es5代码,所以需要使用babel来转换

  • 事实上,在开发中我们很少直接去接触babel,但是babel对于前端开发来说,目前是不可缺少的一部分:

    • 开发中,我们想要使用ES6+的语法,想要使用TypeScript,开发React项目(编写jsx代码),它们都是离不开Babel
    • 所以,学习Babel对于我们理解代码从编写到线上的转变过程至关重要
  • 那么,Babel到底是什么呢?

    • Babel是一个工具链,主要用于旧浏览器或者环境中将ECMAScript 2015+代码转换为向后兼容版本的JavaScript
    • 包括:语法转换、源代码转换
    js
    	[1, 2, 3].map(n => n + 1)
    	// babel转换
    	[1, 2, 3].map(function() {
            return n + 1
        })

2.Babel命令行使用

  • babel本身可以作为一个独立的工具(和postcss一样),可以不搭配webpack等构建工具配置来单独使用

  • 如果我们希望在命令行尝试使用babel,需要安装如下库:

    • @babel/core:babel核心代码,必须安装
    • @babel/cli:可以让我们在命令行使用babel
    js
    	npm i @babel/cli @babel/core -D
  • 使用babel来处理我们的源代码:

    • src:是源文件的目录
    • --out-dir:指定要输出的文件夹dist
    js
    	npx babel src --out-dir dist
  • 转换对应的代码还需安装对应的babel插件

3.babel插件的使用

  • 比如我们需要转换箭头函数,那么我们就可以使用箭头函数转换相关的插件:

    js
        npm install @babel/plugin-transform-arrow-functions -D
        
    	npx babel src --out-dir dist --plugins=@babel/plugin-transform-arrow-functions
  • 查看转换后的结果:我们会发现const并没有转成var

    • 这是因为plugin-transform-arrow-functions,并没有提供这样的功能
    • 我们需要使用plugin-transform-block-scoping来完成这样的功能;
    js
        npm install @babel/plugin-transform-block-scoping -D 
    
        npx babel src --out-dir dist --plugins=@babel/plugin-transform-block-scoping, @babel/plugin-transform-arrow-functions
  • 实际上不会这样去操作,直接安装babel-loader,安装对应的plugin插件,然后配置webpack.config.js文件中的规则

    js
          {
            test: /\.js$/,
            use: [
              {
                loader: "babel-loader",
                options: {
                  plugins: [
                    "@babel/plugin-transform-block-scoping",
                    "@babel/plugin-transform-arrow-functions"
                  ]
                }
              }
            ]
          }
  • 或者在当前目录下新建一个babel.config.js配置文件,将plugins导出,webpack.config.js中的options就可以省略了

    js
        module.exports = {
          plugins: [
            "@babel/plugin-transform-block-scoping",
            "@babel/plugin-transform-arrow-functions"
          ]
        }

4.babel的预设preset

  • 但是如果要转换的内容过多,一个个设置是比较麻烦的,我们可以使用预设(preset):

    • 后面我们再具体来讲预设代表的含义
  • 安装@babel/preset-env预设:

    js
        npm install @babel/preset-env -D
  • 执行如下命令:

    js
        npx babel src --out-dir dist --presets=@babel/preset-env
  • 安装之后,一般都是在babel.config.js文件中配置即可

    • babel将插件和预设是分开的
    js
        module.exports = {
          // plugins: [
          //   "@babel/plugin-transform-block-scoping",
          //   "@babel/plugin-transform-arrow-functions"
          // ]
          presets: [
            "@babel/preset-env"
          ]
        }

5.babel-loader

  • 在实际开发中,我们通常会在构建工具中通过配置babel来对其进行使用的,比如在webpack

  • 那么我们就需要去安装相关的依赖:

    • 如果之前已经安装了@babel/core,那么这里不需要再次安装
  • 我们可以设置一个规则,在加载js文件时,使用我们的babel

    js
          {
            test: /\.js$/,
            use: [
              {
                loader: "babel-loader",
                // options: {
                //   plugins: [
                //     "@babel/plugin-transform-block-scoping",
                //     "@babel/plugin-transform-arrow-functions"
                //   ]
                // }
              }
            ]
          }

6.babel-preset

  • 如果我们一个个去安装使用插件,那么需要手动来管理大量的babel插件,我们可以直接给webpack提供一个presetwebpack会根据我们的预设来加载对应的插件列表,并且将其传递给babel

  • 比如常见的预设有三个:

    • env(转换es6+)
    • react(转换react)
    • TypeScript(转换ts)
  • 安装preset-env

    js
        npm install @babel/preset-env -D

image-20220725164725467

三. webpack打包vue


1.编写App.vue代码

  • 在开发中我们会编写Vue相关的代码,webpack可以对Vue代码进行解析:

    • 安装vue

      js
      	npm i vue // 开发生产都依赖
    • 接下来我们编写自己的App.vue代码

    vue
        <template>
          <div>
            <h2 class="title">{{title}}</h2>
            <p class="content_vue">我是p</p>
          </div>
        </template>
    
        <script>
          export default {
            data() {
              return {
                title: '我是h2'
              }
            }
          }
        </script>
    
        <style>
          .title {
            color: skyblue;
            font-size: 100px;
          }
          .content_vue {
            color: pink;
            font-size: 30px;
          }
        </style>
  • 入口文件引入vue文件

    js
        import { createApp } from 'vue'
        import Hello from './vue/Hello.vue'
    
        import { sum } from "./utils/math"
        // import "./components/div_cpn"
    
        const message = "Hello World"
    
        console.log(sum(20, 30))
        console.log(sum(10, 12))
    
        console.log(message.length)
    
        const bar = () => {
          console.log("bar function execution~")
        }
    
        bar()
        bar()
    
        createApp(Hello).mount('#app')
  • 挂载到index.html文件

    html
        <!DOCTYPE html>
        <html lang="zh-CN">
        <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>
          <script src="../bundle/aaa.js"></script>
        </body>
        </html>

2.App.vue的打包过程

  • 我们对代码打包会报错:我们需要合适的Loader来处理文件

  • 这个时候我们需要使用vue-loader

    js
    	npm i vue-loader -D // 打包时依赖
  • webpack的模板规则中进行配置:

    js
        const path = require('path')
    
        module.exports = {
          entry: './src/main.js',
          output: {
            filename: 'aaa.js',
            path: path.resolve(__dirname, './bundle'),
          },
          module: {
            rules: [
              {
                test: /\.vue$/,
                loader: "vue-loader"
              }
            ]
          }
        }

3.@vue/compiler-sfc

  • 打包依然会报错,这是因为我们必须添加@vue/compiler-sfc来对template进行解析:

    js
    	npm i @vue/complier-sfc -D // 打包时依赖,安装vue的时候已经内置安装了
  • 另外我们需要配置对应的Vue插件:

    js
        const path = require('path')
        const { VueLoaderPlugin } = require("vue-loader/dist/index")
    
        module.exports = {
          entry: './src/main.js',
          output: {
            filename: 'aaa.js',
            path: path.resolve(__dirname, './bundle'),
          },
          module: {
            rules: [
              {
                test: /\.vue$/,
                loader: "vue-loader"
              }
            ]
          },
          plugins: [
            new VueLoaderPlugin()
          ]
        }
  • 重新打包即可支持App.vue的写法

  • 另外,我们也可以编写其他的.vue文件来编写自己的组件

四. webpack对文件路径的解析和配置


1.resolve模块解析

  • resolve用于设置模块如何被解析:
    • 在开发中我们会有各种各样的模块依赖,这些模块可能来自于自己编写的代码,也可能来自第三方库
    • resolve可以帮助webpack从每个require/import语句中,找到需要引入到合适的模块代码
    • webpack使用enhanced-resolve来解析文件路径
  • webpack能解析三种文件路径:
    • 绝对路径
      • 由于已经获得文件的绝对路径,因此不需要再做进一步解析
    • 相对路径
      • 在这种情况下,使用import/require的资源文件所处的目录,被认为是上下文目录
      • import/require中给定的相对路径,会拼接此上下文路径,来生成模块的绝对路径
    • 模块路径
      • resolve.modules中指定的所有目录检索模块
        • 默认值是['node_modules'],所以默认会从node_modules中查找文件
      • 我们可以通过设置别名的方式来替换初识模块路径,具体后面讲解alias的配置

2.解析文件和文件夹

  • 如果是一个文件:
    • 如果文件具有扩展名,则直接打包文件
    • 否则,将使用resolve.extensions选项作为文件扩展名解析
  • 如果是一个文件夹:
    • 会在文件夹中根据resolve.mainFiles配置选项中指定的文件顺序查找
      • resolve.mainFiles的默认值是['index']
      • 再根据resolve.extensions来解析扩展名

3.extensionsalias配置

  • extensions是解析到文件时自动添加扩展名:

    • 默认值是['.wasm', '.mjs', '.js', '.json']
    • 所以如果我们代码中想要添加加载.vue或者jsx或者ts等文件时,我们必须自己写上扩展名
  • 另一个非常好用的功能是配置别名alias

    • 特别是当我们项目的目录结构比较深的时候,或者一个文件的路径可能需要../../../这种路径片段
    • 我们可以给某些常见的路径起一个别名
    js
      resolve: {
        extensions: [".js", ".json", ".vue", ".jsx", ".ts", ".tsx"],
        alias: {
          utils: path.resolve(__dirname, "./src/utils")
        }
      }

Released under the MIT License.