大前端学习--模块化开发 学习笔记

本文详细探讨了前端模块化的演变过程,从文件划分、命名空间到IIFE,再到各种模块化规范如CommonJS、AMD、CMD、ES Module。深入讲解了webpack的使用、配置、工作模式、资源模块加载、URL加载器、Loader分类等核心概念,以及rollup和Parcel的使用场景。最后,对比了webpack和rollup的优缺点,提出选择原则,简要介绍了 Parcel 零配置打包器。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

模块化开发 学习笔记

文章内容输出来源:大前端高薪训练营

模块化开发

模块化开发时当前最重要的前端开发范式之一 模块化只是思想

一、模块化演变过程

Stage1 文件划分方式
  • 污染全局作用域
  • 命名冲突问题
  • 无法管理模块依赖
  • 早起模块化完全依靠约定
Stage2 命名空间方式
  • 每个模块只暴露一个全局对象,所有模块都挂载到这个对象上
  • 减少了命名冲突的可能
  • 但是没有私有空间,模块成员可以在外部被访问或修改
  • 模块之间的依赖关系没有得到解决
Stage3 IIFE 立即执行函数
  • 使用立即执行函数包裹代码,要输出的遍历挂载到一个全局对象上
  • 变量拥有了私有空间,只有通过闭包修改和访问变量
  • 参数作为依赖声明去使用,使得每个模块的依赖关系变得明显

二、模块化规范

1. CommonJS规范
  • 一个文件就是一个模块
  • 每个模块都有单独的作用域
  • 通过module.exports导出成员
  • 通过require函数载入模块
  • CommonJS是以同步模式加载模块
2. AMD(Asynchronous Module Definition)异步模块规范
  • 模块加载器:Require.js

    // 定义一个模块
    define('module1', ['jquery', './module2'], function ($, module2) {
         
         
      return {
         
         
        start: function () {
         
         
          $('body').animate({
         
          margin: '200px' })
          module2()
        }
      }
    })
    
    // 载入一个模块
    require(['./module1'], function (module1) {
         
         
      module1.start()
    })
    
  • 目前绝大多数第三方库都支持AMD规范

  • AMD使用起来相对复杂

  • 模块JS文件请求频繁

3. 淘宝推出的Sea.js + CMD(Common Module Definition)通用模块规范
// CMD 规范 (类似 CommonJS 规范)
define(function (require, exports, module) {
   
   
  // 通过 require 引入依赖
  var $ = require('jquery')
  // 通过 exports 或者 module.exports 对外暴露成员
  module.exports = function () {
   
   
    console.log('module 2~')
    $('body').append('<p>module2</p>')
  }
})
4. ES Module
// ./modulejs
const foo = 'es modules'
export {
   
    foo }
// ./app.js
import {
   
    foo } from './module.js'
console.log(foo) // => es modules
  • 自动采用严格模式,忽略’use strict’
  • 每个ESM模块都是单独的私有作用域
  • ESM是通过CORS去请求外部JS模块的
  • ESM的script标签会延迟脚本执行
5. 模块化标准规范
  • 在node.js中使用CommonJS

    CommonJS是node.js内置的模块化工具,只需要遵循CommonJS的标准即可,不需要引入别的依赖

  • 在浏览器中使用ES Modules

    ES Modules是ECMAScript2015

三、常用的模块化打包工具

模块化打包工具的由来:ES Modules存在环境兼容问题、模块文件过多,网络请求频繁,而且所有的前端资源都需要模块化。

打包工具解决的是前端整体的模块化,并不单指JavaScript模块化

1. webpack

webpack:模块打包器、模块加载器、代码拆分、载入资源模块

(1) webpack的基本使用
  • 先在项目的根目录下执行yarn init -y,创建package.json
  • 安装webpack相关依赖:yarn add webpack webpack-cli --dev
  • 查看webpack版本:yarn webpack --version, 4.43.0
  • 执行yarn webpack进行打包,生成了dist目录,里面有main.js文件
  • 修改index.html中的index.js的路径为dist/main.js,并且去掉script标签的type=module的属性
  • 去package.json的scripts中定义一个build任务:"build": "webpack",以后执行yarn build进行打包
(2) webpack的配置文件

webpack.config.js文件是运行在nodejs文件下的js文件,我们需要按照CommonJS的方式编写代码。这个文件需要导出一个对象,我们完成对应的配置选项。

webpack.config.js文件内容:

const path = require('path')

module.exports = {
   
   
  entry: './src/index.js', // 指定打包入口文件,如果是相对路径,前面的点不能少
  output: {
   
   
    filename: 'bundle.js', // 输出文件的名称
    path: path.join(__dirname, 'output'), // 输出路径,为绝对路径
  }
}
(3) webpack工作模式

直接执行webpack打包的时候,控制台会有警告:

WARNING in configuration
The ‘mode’ option has not been set, webpack will fallback to ‘production’ for this value. Set ‘mode’ option to ‘development’ or ‘production’ to enable defaults for each environment.
You can also set it to ‘none’ to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/

说没有指定工作模式,默认以生产模式打包,会进行代码的压缩。

我们可以通过cli命令指定工作模式,就是增加一个--mode的参数,属性有三种选择,production、development、none

  • production:生产模式会默认启动优化,优化我们的打包结果
  • development:开发模式,会自动优化打包的速度,添加一些调试过程中的辅助到代码中
  • none:原始状态的打包,不会做任何处理

可以通过yarn webpack --mode development来执行.

此外,还可以在webpack的配置文件中指定工作模式,也就是增加一个mode属性,例如:mode: "development"

(4) webpack资源模块加载

将配置文件中的entry属性的值改为./src/main.css,然后执行打包命令yarn webpack,会报错:

ERROR in ./src/main.css 1:5
Module parse failed: Unexpected token (1:5)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders

body {
| margin: 0 auto;
| padding: 0 20px;
error Command failed with exit code 2.

因为webpack默认会把文件当做js解析,所以打包css文件时,文件内容不符合JS语法则报错了,报错中提示我们可以寻找正确的loader去解析代码。webpack内部的loader只能解析js,所以我们要手动安装css-loader去处理css代码。

执行命令:yarn add css-loader --dev

然后在webpack的配置文件中增加属性:

module: {
   
   
  rules: [
    {
   
   
      test: /.css$/,
      use: 'css-loader'
    }
  ]
}

我们增加外部的loader需要在配置文件中增加资源模块module属性,属性值是一个对象,对象中有一个rules数组,数组里每个元素都是一个对象,对象中的test属性是正则式,指明要处理的资源文件,use属性是对该资源进行处理的loader名称。

再次执行打包命令,发现css没有作用,是因为我们使用css-loader只是对css文件进行了打包,但是并没有作用到页面上,接下来还要安装一个style-loader,执行命令:yarn add style-loader --dev

style-loader是将css-loader处理后的结果,通过style的形式追加到页面上

然后将配置文件中的rules对应的处理css资源模块的use属性由'css-loader'改为['style-loader', 'css-loader'],use配置了多个loader,是一个数组,里面的loader从右往左执行,所以要将css-loader写在后面,我们要先用css-loadercss代码转化成js模块,才可以正常打包。

(5) webpack 导入资源模块

虽然webpack的入口文件可以是别的类型文件,但由于前端项目是由JS驱动,所以我们开发时一般将入口文件设置为JS文件,需要用到CSS时,就直接在JS文件中通过import导入即可,如:import './main.css'

webpack建议我们根据代码的需要在JS中动态导入资源文件,因为需要资源的不是应用,而是代码。因为是JavaScript驱动了整个前端应用,这样做的好处是:

  • 逻辑合理,JS确实需要这些资源文件
  • 确保上线资源不缺失,都是必要的
(6) webpack文件资源加载器

安装文件资源加载器:yarn add file-loader --dev,相当于直接拷贝物理文件。不过此时资源文件路径会出现问题,webpack默认认为它打包过后的文件会放在网站的根目录下面,此时需要在配置文件中的output属性中指定publicPath属性值为dist/,即:publicPath: 'dist/',这样在打包时,文件的输出路径前面会拼接上publicPath的值。

(7) webpack URL 加载器

格式:协议 + 媒体类型和编码 + 文件内容

格式: data:[<mediatype>][;base64],<data>

例如:data:text/html;charset=UTF-8,<h1>html content</h1>

  • 先安装url-loader:yarn add url-loader --dev

  • 修改png文件的loader为url-loader

{
   
   
  test: /.png$/,
    // use: 'file-loader',
  use: 'url-loader'
}
  • 执行yarn webpack,此时的png文件的URL则为data协议的了。

最佳使用方式:

  • 小文件使用Data URLs,减少请求次数
  • 大文件独立提取存放,提高加载速度

配置方式:

{
   
   
  	test: /.png$/,
    // use: 'file-loader',
    use: {
   
   
        loader: 'url-loader',
        options: {
   
   
          limit: 10 * 1024, // 单位是字节 10KB
        }
    }
}
  • 超过10KB的文件单独提取存放
  • 小于10KB文件转换为Data URLs嵌入代码中

注意:这种方式还是要安装file-loader,因为对超出大小的文件还是会调用file-loader,如果没有file-loader会报错。

(8) webpack 常用加载器分类
  1. 编译转换类,转换为JS代码,如css-loader
  2. 文件操作类,将资源文件拷贝到输出目录,将文件访问路径向外导出,如:file-loader
  3. 代码检查器,统一代码风格,提高代码质量,如:es-loader
(9) webpack 处理ES2015

因为模块带包需要,所以处理importexport,除此之外,并不能转换其他的ES6特性。如果想要处理ES6,需要安装转化ES6的编译型loader,最常用的就是babel-loaderbabel-loader依赖于babel的核心模块,@babel/core@babel/preset-env

  • 执行命令:yarn add babel-loader @babel/core @babel/preset-env --dev
  • 修改js的loader
{
   
   
   test: /.js$/,
   use: {
   
   
     loader: 'babel-loader',
     options: {
   
   
       presets: ['@babel/preset-env']
     }
   },
    exclude: /(node_modules)/, // 这里很重要,千万别忘了,否则会出错的。
 }

注意:

Webpack只是打包工具,

加载器可以用来编译转化代码

(10) webpack的模块加载方式
  1. 遵循ES Modules标准的import声明

  2. 遵循CommonJS标准的require函数。对于ES的默认导出,要通过require('./XXX').default的形式获取

  3. 遵循AMD标准的define函数和require函数

  4. Loader加载的非JavaScript也会触发资源加载

    1. 样式代码中的@import指令和url函数

      @import url(reset.css);
      
      body {
             
             
        margin: 0 auto;
        padding: 0 20px;
        max-width: 800px;
        background: url(1.png);
        background-size: cover;
      }
      

      ​ css-loader在处理css代码时,遇到了background属性中的url函数,发现是引入的资源文件是png格式的文件,则将这个资源文件 交给url-loader处理

    2. HTML代码中的图片标签的src属性

      		{
             
             
              test: /.html$/,
              use: {
             
             
                loader: 'html-loader',
                options: {
             
             
                   // html-loader默认只处理页面中的img标签的src属性的资源文件,所以指定其他标签的资源文件也要处理
                  attributes: {
             
             
                    list: [
                      {
             
             
                        tag: 'img',
                        attribute: 'src',
                        type: 'src'
                      },
                      {
             
             
                        tag: 'a',
                        attribute: 'href',
                        type: 'src'
                      }
                    ]
                  }
                }
              }
            }
      
(11) webpack的核心工作原理

在这里插入图片描述

(12) webpack Loader的工作原理

实现一个markdown的文件加载器。

Loader作为webpack的核心机制,内部的工作原理也非常简单,我们通过开发一个自己的loader,来深入了解loader的工作原理。

我们的需求是实现一个markdown文件的加载器,这个加载器可以在代码当中直接导入markdown文件。markdown文件一般是被转换为html过后再去呈现到页面上的,所以我们导入的markdown文件得到的结果就是转换过后的html字符串。

在项目的根目录下新建一个markdown-loader.js文件,每一个webpackloader都需要去导出一个函数,这个函数就是我们这个loader的对我们所加载到的资源的一个处理过程,它的输入就是我们资源文件的内容,输出就是我们此次加工过后的一个结果。

那我们通过source参数去接收输入,然后通过我们的返回值去输出,那这里呢,我们先尝试打印一下这个source,然后直接去返回一个中hello,我们去看一下结果,我们回到webpack配置文件中去添加一个加载新的规则配置,那这里呢,我们匹配到的扩展名就是.md,就是我们刚刚所编写的markdown-loader的模块,那这里呢,我们的use属性不仅仅只可以使用模块的名称,其实对于模块的文件路径也是可以的,那这一点呢其实与node当中的require函数是一样的,所以说我们大家直接使用相对路径去找到这个markdown-loader的,那配置好过后呢,我们运行打包命令,打包过程当中命令他们确实打印出来了我们所导入的markdown的内容,那这也就意味着我们的source确实是所导入的文件内容,但是呢,它同时也爆出一个解析错误,那说的是you many need additional load to handle the result of this loader,就是我们还需要一个额外的加载器来去处理我们当前的加载结果,那这究竟是为什么呢?

module.exports = source => {
   
   
  console.log(source)
  return 'hello'
}

其实webpack的加载资源的过程有点类似于一个工作管道,你可以在这个过程当中一次去使用多个loader,但是呢,还要求我们最终这个管道工作过后的结果必须是一段JavaScript代码,那因为我们这返回的内容是一个hello,那它不是一个标准的JavaScript代码,所以我们这才会出现这样的错误提示,那知道这个错误的原因过后呢,解决的办法其实也就很明显了,那要么就是我们这个loader的直接去返回一段标准的JavaScript代码,要么就是我们再去找一个合适的加载器,接着去处理我们这里返回的结果。

那这里呢,我们先来尝试第1种办法回到我们markdown-loader.js的当中,那这里我们将返回的这个内容修改为'console.log("hello")',那这就是一段标准的JavaScript代码,然后呢,我们再一次运行打包,那此时打包过程当中就不再会报错了。


                
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值