webpack 的配置及背后思想

webpack 介绍

webpack 本身就是一个模块打包工具 将零散的模块代码打包到同一个js文件当中 对于打包中有环境兼容的代码 就可以在打包的过程中通过模块加载器 (loader) 对其进行编译装换 同时webpack还具备代码拆分的能力 在应用当中将我们的代码按照我们的需要去打包 解决了 在应用当中将所有的模块代码打包在一起 产生的文件比较大的问题 应用加载过程当中 初次运行的时候将所必须的模块 打包到一起 对于其他的模块单独存放 当我们需要时 再异步加载这些模块 解决文件太碎 或太大
支持以模块化的方式 载入任意类型的文件

安装

 yarn add webpack webpack-cli --dev
复制代码

配置文件

  • 创建 webpack.config.js
  • 它是一个在 node 环境下运行的文件
  • 控制打包输入输出目录
  • 创建webpack.config.js文件
const path = require('path')
module.exports ={
    entry:'./src/index.js', // 打包入口
    output:{                // 打包后输出目录
        filename:'bundle.js',
        path:path.join(__dirname,'output') // 必须是绝对路径 否则报错
    }
}

复制代码

webpack 打包结果运行原理

bundle.js

  • 一个函数的自调用 参数是一个数组 每个元素都是一个函数 对应源代码当中的模块 也就是说每个模块都会包裹在每个函数当中 从而实现私有作用域

工作入口函数

  • 定义一个对象 缓存加载过的模块
  • 定义了一个加载模块的函数 webpack_require_
  • 在 webpack_require 挂载了一些工具函数
  • return webpack_require 开始加载模块

webpack_require 执行过程

  • 在缓存 判断是否已经加载过
  • 没有缓存 定义对象 属性有 moduleId L export
  • 用 call 方法调用 moduleId 的函数并执行函数中的内容 最后把 module.export 导出的内容 赋值到对象的 export 中 最终返回

webpack4 新增了工作模式的用法

production 模式
  • 默认使用
  • 帮我们的代码进行压缩
    yarn wepack --mode production 
复制代码
development 模式
  • 优化我们的打包速度和方便调试
    yarn webpack --mode development
复制代码
none 模式
  • 运行最原始状态的打包 不会对其进行任何的处理
  • 除此之外 可以在配置文件中设置 mode 的属性来配置打包模式
    yarn webpack --mode none
复制代码

除了命令行的方式 还可以直接在配置文件中使用

const path = require('path')
module.exports ={
    mode:'production',
    entry:'./src/index.js',
    output:{
        filename:'bundle.js',
        path:path.join(__dirname,'output') // 必须是绝对路径 否则报错
    }
}

复制代码

Loader

介绍

  • 专注实现资源模块加载
  • webpack的核心特性
  • 借助loader就可以加载任何类型的资源
  • 将资源模块作为 js 代码去工作
  • webpack默认打包 js 代码 其他文件需要使用loader加载器

Css loader

  • 打包css文件

安装

    yarn add css-loader --dev
    yarn add style-loader --dev
复制代码

配置

const path = require('path')
module.exports ={
    mode:'none',
    entry:'./src/main.css',
    output:{
        filename:'bundle.js',
        path:path.join(__dirname,'dist')
    },
    //对其他资源模块加载资源规则的配置
    //每个规则对象都需要两个属性
    //text 正则表达式 用来匹配打包过程中的文件路径
    //use 匹配到的文件需要使用的loader 先执行最后一个loader
    module:{
        rules:[
            {
                test:/.css$/,
                use:[
                    'style-loader',
                    'css-loader'
                ]
            }
        ]
    }
}
复制代码

工作原理

• css-loader 将 css 文件转换成打包后的模块 将 css 代码 push 到 bundle.js 模块数组当中 这个数组是由 css-lodash 内部的模块提供的 整个过程没有使用到这个数组 需要使用 style-lodash 将 css-lodash 转化过后的结果通过 style 标签的形式追加到页面上

image.png

filer-loader

  • 图片 字体….通过js 的方式表示
  • 安装
  • yarn add file-loader –dev
  • 案例
  • 导入图片 并添加到页面中
    module.export = {
 	entry:'main.js',
        output:{
		filename:'bundle.js',
                path:path.join(__dirname,'dist'),
                publicPath:'dist/', //文件路径默认是在项目路径下 而打包后实在 dist 目录 导致浏览器看不到效果 使用 
             },
      module:{
          rules:[
            {
                test:'/.png/',
              use:['file-loader'] // here
            },
              {
                test:'/.css/',
              use:[
                        'style-loader',
                   'css-loader'
                  ]
            }
          ]	
      }
    }

复制代码

URL资源加载器

介绍

  • 除了用 file-loader 处理 图片 文字 还可以用 url 资源加载器
  • Data url 是一种特殊的 url 协议 传统 url 就必须服务器必须要有一个文件 然后请求对应的地址得到服务器上的 文件 而 Data url 直接用 url 直接表示一个文件内容 使用这种 url 的时候 就不会再去发送任何的 http 请求 也就是文本就包含文件的内容

格式

image.png
比如下面这段代码

image.png
浏览器会解析出来这是一个 html 文件 编码是utf-8 内容是h1标签

image.png
如果是图片 或者是字体 无法通过文本去表示的二进制类型文件 可以通过图片进行base 64编码 将 base64 编码过后的结果作为图片内容

安装

 yarn add url-loader --dev
复制代码

使用建议

适合体积比较小的资源 如果体积比较大会影响打包结果非常大 从而影响运行速度

  • 最佳实践
    • 小文件使用 data url 减少请求数
    • 大文件用 file-loader 单独存放,提高加载速度
  • 限制范围 超过范围大小 则使用 file-loader
             {
                test:/.png$/,
                use:{
                    loader:'url-loader',
                    options:{
                        limit :10*1024 // 将 10kb 以下的文件存入到 url 
                    }
                }

            }
复制代码
  • 超出10kb文件单独存放
  • 小于10kb转换为 data url嵌入代码

注意:

必须要安装file-loader

babel-loader

  • webpack 只是打包工具 只处理 import 和 export 做相应的转换 不能处理es6中其他的特性

安装:

     yarn add babel-loader @babel/core @babel/preset-env --dev
复制代码

配置

         {
                test: /.js$/,
                use: {
                  loader: 'babel-loader',
                  options: {
                    presets: ['@babel/preset-env']
                  }
                }
              },
复制代码

webpack 导入资源的方式

  • 支持的三种导入方式
    • ESmodule 标准的 import 声明

image.png
– commonjs 标准的 require

image.png
– AMD标准的 define函数 和 require 函数

image.png

  • 注意
    • 除非必要否则不要混合使用这些标准 将大大降低项目的可维护性
    • 导入 HTML 模块是一个字符串 需要接受一下
  • 另外 以下几种方式也可以加载模块
    • 样式代码中的 @import 指令和url 函数
    • html 代码中的图片标签 src 属性
      • 引入html模块到入口文件js时 需要安装html-loader
      • html默认处理img src属性 触发其他标签属性打包 需配置

html-lodaer

module:{
	rules:[
            {
  		test:'/.html/'
                 use:[
                        loader:'html-loader',
                        options:{
    			attr:['img:src','a:href']
                         }
                      ]
                }
           ]
 }
复制代码

webpack特点

  • 在代码当中所有需要引用到的资源 都会被 webpack 找出来 然后根据引用配置和不同的 loader 去处理 将处理的结果整体打包的输出目录 webpack 就是用这个特点 实现项目的模块化
  • webpack 打包的核心工作原理与过程
    • 项目当中一般都会有散落的各种各样代码和资源文件 webpack会根据我们的配置找到 js 文件作为打包的入口 然后顺着我们入口文件的代码 根据出现的 import 和 export 这样的语句 解析代码所需要的模块 分别去解析每个资源模块对应的依赖 最后形成整个项目中的所有用到文件的依赖数 然后 webpack 去递归这个依赖数 找到每个节点所配置的文件 然后根据配置文件中 rules 属性 找到模块所对应的加载器 交给加载器去加载这个模块 最后将加载到的结果 放入到 bunner.js 从而实现整个项目的打包

开发自己的loader

  • 每个 loader 都需要导出一个函数 这个函数对所加载到的资源的一个处理过程 输入就是要加载到的文件内容 输出就是加工过后的结果 函数的参数接受输入 返回值输出
  • 概念
    • loader 负责资源文件从输入到输出的转换
    • 可以把 loader 交给下一个 loader 处理
  • 标准
    • loader最后的返回值必须是一段 js 代码
    • 原因 打包后会将结果 push 打包后的 js 模块中 如果不是 js 代码 就会报错
    • 允许像管道一样凭借多个 loader 最终转换为 js 代码

例:
创建 markdom-loader

//导出函数 该函数的参数是输入内容  对内容进行处理 并把结果作为返回值输出出去
//下载并导入marked 第三方模块 将内容转换为html标签
const marked = require('marked')
module.exports= source=>{
    const html = marked(source)
    //将html 转换字符串
    return `export defalut ${JSON.stringify(html)}`
    //将返回的结果交给下一个loader
    // return html
}
复制代码
loader:[
       './markdowm-loader',
       'html-loader'
]
复制代码

webpack 插件机制 plugin

  • 插件机制是 webpack 另外一个核心机制
  • 作用
    • 增强 webpack 自动化能力
    • 解决除了 loader 资源加载以外其他自动化工作
  • 例如
    • 打包之前清除dist目录
    • 拷贝静态文件到输出目录
    • 压缩打包后输出代码
  • 常用插件
    • 绝大多数插件模块导出的是一个类 使用它就是为这个类型创建实例 然后放到 plugins 数组当中

clean-webpack-plugin

  • 解构导入的插件 并创建实例对象
  • 每次打包前清除 dist 目录
  • 安装
    • yarn add clear-webpack-plugin –dev
  • 导入
    • import { ClearWebapckPlugin } from ‘clear-webpack-plugin’
  //plugin是一个数组 添加一个插件就是在数组添加元素
    plugins:[
        new CleanWebpackPlugin()
    ]
复制代码

html-webpack-plugin

  • 硬编码的方式写 html 会导致 上线后无法确保路径引用是否正确 而且当配置文件的入口文件发生变化之后 需要手动修改 html 的引用路径
  • 自动生成使用打包结果 bundle.js 的 html 文件并生成到同一个目录 上线后只要发布 dist 目录就可以了 并且html 对于 bundle 的引用是动态注入进来的 不需要手动的去硬编码 确保路径引用正常
  • 默认导出文件的类型 不需要解构
  • 安装
    • yarn add html-webpack-plugin –dev
  • html 标题 通过修改 htmlwebpackPublic 的属性实现
  • 如果需要自定义 HTML 文件的话
  • 通过模板文件 直接拷贝模板文件
  • 当需要多个页面文件时创建多个 htmlwebpackplugin 实例到 plugins 的属性当中
  • 问题 : 启动项目找不到该 html 文件
    • 修改 publicPat 属性 将 html 目录修改为当前目录
plugins:[
	new htmlWebpackPlugin({
  	title:'' // html 的标题
        meta:{
           viewPort:'width=device-width'
  	},
        //自定义模板内容
        template:'./src/index.html'
 })
      // 生成多个 html 文件
       new htmlWebpackPlugin({
  	filename:'about.html'
  })
]

复制代码

copy-webpack-plugin

  • 使用背景
    • 将静态文件 plugin 文件夹 输入 到 dist
    • 开发阶段不要使用这个插件
  new CopyWebpackPlugin({
            //从那个路径到那个路径
            patterns: [
                { from: "public", to: "public" },
              ],
        })

复制代码

开发自己的插件

  • 插件机制的实现
    • 就是在软件开发中经常看到的钩子机制 钩子机制就是 web 当中的事件机制 在webPack工作中会有很多个环节 为了便于插件的扩展 webpack 给给个环节都埋下了钩子 开发插件的时候 就可以往每个节点挂载不同的任务 轻松扩展 webpack 的能力

    • 要求插件必须是一个函数 或者是包含 apply 方法的对象 通常是创建一个类型 在类型中添加一个 apply 方法 然后通过创建实例来使用这个方法

    • 就是往 webpack 打包的声明周期 挂载函数实现扩展

class myPlugin{
  // compiler 注册的信息
	apply(compiler){ 
        //compilation 本次打包的所有信息
            compiler.emit.tap('myplugin',(compilation)=>{
            //遍历模块
            for(let name for compition){
               if(name.endWidth('.js')){
               // 获取文本内容
        	const content = compilation.assets[name].source()
                //把文本中的注释替换掉
                const widthoutComents = content.replace(/\/\*\*+*\/g,'')
                //把处理结果重新输出到该文件中
                compilation.assets[name] ={
                    source: () => widthoutComents,
                    size:() => widthoutComents.length
          }
        }
      }
    })
  }
}	

复制代码

webpack 思想

  • webpack 建议在 js 当中引入任何你当前代码需要的资源 是因为真正需要资源的不是的应用 而是此时正在编写的代码 是你当前编写代码需要这些资源
  • 由 JavaScript 驱动整个前端应用
    • 逻辑合理,js需要多种文件配合才能实现对应的功能
    • 确保上线时资源不缺失,并且每一个文件都是必要的

预告
下一篇 webpack 打包结果的优化和提高打包体验

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享