【模块化开发】之 Webpack、Rollup、Parcel

项目说明

笔记来源:拉勾教育 大前端高薪训练营
阅读建议:内容较多,建议通过左侧导航栏进行阅读

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

产生原因

  • ES Modules 存在环境兼容问题;
  • 模块文件过多,网络请求频繁;
  • 所有的前端资源都需要模块化。

Webpack

基本介绍

webpack,常用的模块打包器(Module bundler),可以将零散的JavaScript 代码打包到一个 JS 文件中。
对于那些存在环境兼容问题的代码,可以在打包过程中通过模块加载器(Loader)对其进行编译转换。
webpack,具有代码拆分(Code Splitting)的能力,可以将所有的代码都按照需要进行打包,可以实现渐进式的打包方式。

适用场景

开发应用程序,使用 Webpack。

快速上手

  • 1,准备三个文件,本次使用 http-server 构建服务器,也可以采用其他的

    代码示例如下(heading.js):

      export default () => {
          const element = document.createElement('h2')
      
          element.textContent = 'hello world'
          element.addEventListener('click', () => {
              alert('Hello webpack')
          })
      
          return element
      }
    

    代码示例如下(index.js):

      import createHeading from './heading.js'
      
      const heading = createHeading()
      
      document.body.append(heading)
    

    代码示例如下(index.html):

       <script type="module" src="./src/index.js"></script>    
    
  • 2,webpack 是一个 NPM 工具模块,需要初始化包管理文件 package.json

      $ yarn init --yes # or npm init -y
    
  • 3,安装 webpack核心模块 ,以及对应的 webpack-cli模块

      $ yarn add webpack webpack-cli --dev # or npm i webpack webpack-cli --save-dev
    
  • 4,查看 webpack 是否安装成功

      $ yarn webpack --version
    

    运行结果,如下图所示:

    在这里插入图片描述

  • 5,使用 webpack ,进行打包

      $ yarn webpack
    

    运行结果,如下图所示:

    在这里插入图片描述
    可以看到,打包后,会生成一个 dist 文件夹,里面包含一个 main.js

  • 6,在 package.json 中,使用 NPM Scripts 对打包命令进行包装

    配置代码如下:

      {
          "scripts": {
              "build": "webpack"
          }
      }
    
  • 7,将 index.html 中引入的 js,进行修改

    代码示例如下(index.html):

      <script src="dist/main.js"></script>    
    

基本使用

webpack 4.0+ 支持按照约定的内容,进行打包,即 src/index.js(默认入口文件) 打包到 dist/main.js

配置文件

在项目根目录添加 webpack.config.js 配置文件。

工作模式
  • development(开发模式),优化打包的速度
      $ yarn webpack --mode development
    
  • production(生产模式),启动多个插件,进行代码的压缩等
      $ yarn webpack --mode production
    
  • none,运行最原始状态的打包,不会做任何的处理
      $ yarn webpack --mode none
    
基本配置
  • 指定 webpack 打包的入口文件

    配置代码如下(webpack.config.js):

      module.exports = {
          entry: './src/main.js' // 相对路径时,./ 不能省略
      }
    
  • 设置输出文件的位置

    配置代码如下(webpack.config.js):

      const path = require('path')
      module.exports = {
          output: {
              filename: 'bundle.js', // 设置输出文件的名称
              // 指定输出文件所在的目录,必须是绝对路径
              // 利用 node 的 path 模块,组合生成绝对路径
              path: path.join(__dirname, 'output')     
          }
      }
    
  • 配置打包的工作模式 值为:development | production | none

    配置代码如下(webpack.config.js):

      module.exports = {
          mode: 'development',
      }
    

导入资源模块

JavaScript 驱动整个前端应用的业务,因此需要把 打包入口 设置为 js 文件,它也相当于是 运行入口。在 js 代码中 通过 import 导入其他资源文件。

  • 1,在 main.js 中引入 css 文件

    代码示例如下(main.js):

      import createHeading from './heading.js'
      import './main.css'
      
      const heading = createHeading()
      document.body.append(heading)
    
  • 2,在 webpack.config.js 中设置入口文件

    配置代码如下(webpack.config.js):

      module.exports = {
          entry: './src/main.js' // 相对路径时,./ 不能省略
      }
    

    页面效果,如下图所示:

    在这里插入图片描述
    注意

    一般需要根据代码的需要动态导入资源,因为需要资源的不是应用,而是此时正在编写的代码。

    优势

    1)逻辑合理,JS 确实需要这些资源文件;
    2)确保上线资源不缺失,都是必要的。

模块加载

webapck 进行打包过程中,会使用 loader 进行编译转换。下面,来看一下都有那些编码方式会触发对应的 loader ,并最终将其打包为 资源模块?

Js 代码

webpack 兼容多种模块化标准,但是建议不要混合使用标准

  • 1,遵循 ES Modules 标准的 import 声明

    语法代码如下:

      import ... from 'module_path'
    
  • 2,遵循 CommonJs 标准的 require 函数

    语法代码如下:

      require('module_path')
      //通过 require 函数载入 ES Modules,对于 ES Modules 的默认导出
      // 需要通过导入require 结果的default 属性进行获取
      const xxx = require('module_path').default
    
  • 3,遵循 AMD 标准的 define 函数 和 require 函数

    语法代码如下:

      define(['module_path', 'module_path', ...], ( mod1, mod2) => { })
              
      require(['module_path', 'module_path', ...], ( mod1, mod2) => { })
    
样式代码
  • @import 指令 和 url 函数,会触发相应的模块加载
      @import url('./reset.css');
      
      body {
      	background-color: url('./u0.png');
      }
    
HTML 代码
  • 图片标签的 src 属性,会触发相应的模块加载
      <footer>
          <img src="./u0.png">
          <a:href></a:href>
      </footer>
    
      import footerHtml from './footer.html'
      document.write(footerHtml)
    

Loader

Loader (加载器) 是 webpack 的核心特性,借助于 Loader 就可以加载任何类型的资源,从而实现资源模块加载的功能。

webpack 内部内置的 loader 只能对 JS 文件进行打包,其他的资源文件需要引入其他的 loader 进行处理,最终都会转换成 js 模块。

Loader Kinds
编译转换类

编译转换器,会把加载到的资源模块转换为 JavaScript 代码,如 css-loader。

css-loader

css-loader,用来对 css 文件进行转换的加载器,将资源文件转换成 js 代码。

基本使用

  • 1,安装 loader 模块

      $ yarn add css-loader --dev # or npm i css-loader --save-dev
    
  • 2,在 css 文件中,书写 body 的样式

    样式代码如下(main.css):

      body {
          margin: 0 auto;
          padding: 0 20px;
          max-width: 800px;
          background-color: #000000;
      }
    
  • 3,在 webpack.config.js 中进行配置

    配置代码如下(webpack.config.js):

      module.exports = {
          entry: './src/main.css',
          module: {
              rules: [  // rules 数组,是指针对其他资源模块的加载规则的配置
                  {   
                      test: /.css$/,  // 正则表达式,用来匹配在打包过程中遇到的文件路径
                      use: 'css-loader' // 用来指定匹配到的文件,需要去使用的 loader
                  }
              ]
          }
      }
    

    页面效果,如下图所示:

    在这里插入图片描述

    可以看到,此时页面中并没有对应的样式。这是因为还需要 style-loader 将转换后的结果追加到页面中。

style-loader

style-loader,用来把 css-loader 转换后的结果,通过 style 标签的形式追加到页面中。

基本使用

  • 1,安装 loader 模块

      $ yarn add style-loader --dev # or npm i style-loader --save-dev
    
  • 2,修改 webpack.config.js 中的配置

    配置代码如下(webpack.config.js):

      module.exports = {
          entry: './src/main.css',
          module: {
              rules: [ // rules数组,是指针对其他资源模块的加载规则的配置
                  {   
                      test: /.css$/, // 正则表达式,用来匹配在打包过程中遇到的文件路径
                      // 用来指定匹配到的文件,需要去使用的 loader
                      use: [ // 若配置多个loader,执行顺序是 从后往前 的
                          'style-loader', 
                          'css-loader'   // 先将 css 代码转换成 js 模块
                      ]
                  }
              ]
          }
      }
    
babel-loader

babel-loader,用来将 ES6+ 的新特性,转换为 ES5,需要 @babel/core 核心模块,以及用于去完成具体特性转换插件的集合 @babel/preset-env

基本使用

  • 1,安装 loader 模块

      $ yarn add babel-loader @babel/core @babel/preset-env --dev 
    
  • 2,在 webpack.config.js 中添加配置规则

    配置代码如下(webpack.config.js):

      module.exports = {
          module: {
              rules: [
                  { // 通过 babel-loader 取代默认的加载器,处理代码中的新特性
                      test: /.js$/,
                      use: 'babel-loader', 
                      options: {
                          presets: ['@babel/preset-env']
                      }
                  },
              ]
          }
      }
    

    总结

    1)Webpack 只是 打包工具;
    2)加载器可以用来编译转换代码。

html-loader

html-loader,用来处理 HTML 中制定的标签属性。

基本使用

  • 1,安装 loader 模块

      $ yarn add html-loader --dev
    
  • 2,在 HTML 中编写代码

    代码示例如下(footer.html):

      <footer>
          <img src="./u0.png">
          <a:href></a:href>
      </footer>
    
  • 3,在 JS 文件中进行导入

    代码示例如下(main.js):

      import footerHtml from './footer.html'
      document.write(footerHtml)
    
  • 4,在 webpack.config.js 中,添加配置

      module.exports = {
          module: {
              // 针对其他资源模块的加载规则的配置
              rules: [
                  {
                      test: /.html$/,
                      use: {
                          loader: 'html-loader',
                          options: {
                              // 指定哪个标签属性组合应该被此 loader 处理
                              // 这个属性默认只有 'img:src'
                              attrs: ['img:src', 'a:href']
                          }
                      }
                  }
              ]
          }
      }
    
文件操作类

文件操作类加载器,会把加载到的资源模块拷贝到输出的目录,同时会将文件的访问路径向外导出,如 file-loader。

file-loader

file-loader,文件资源加载器,这里主要指图片、字体等资源。

基本使用

  • 1,安装 loader 模块

      $ yarn add file-loader --dev # or npm i file-loader --save-dev
    
  • 2,在 main.js 引入图片

    代码示例如下(main.js):

      import createHeading from './heading.js'
      import './main.css'
      import u from './u0.png'
      
      const heading = createHeading()
      document.body.append(heading)
      
      const img = new Image()
      img.src = u
      document.body.append(img)
    
  • 3,在 webpack.config.js 中添加配置规则

    配置代码如下(webpack.config.js):

      module.exports = {
          output: {
              filename: 'main.js',
              path: path.join(__dirname, 'dist'),
              publicPath: 'dist/'  // 设置网站的根目录  / 不能省略,默认为 ''
          },
          module: {
              rules: [
                  {   // 配置图片规则
                      test: /.png$/, 
                      use: 'file-loader'
                  }
              ]
          }
      }
    
url-loader

url-loader,用来将资源(图片、字体等)文件转换为 Data URL 的形式,Data URLs 是一种特殊的 url 协议,url 可以直接去表示文件内容的方式,也就是说,这种 URL 中的文本就已经包含了文件内容。

在使用过程中,不会再去发送任何的 HTTP请求。如果要将图片、字体等二进制的文件进行编译时,会将文件的内容进行 base64 编码,然后以 base64编码 过后的结果(一个字符串)去表示文件的内容。

url-loader,适合转换体积比较小的文件资源。

基本使用

  • 1,安装 loader 模块

      $ yarn add url-loader --dev # or npm i url-loader --save-dev
    
  • 2,在 webpack.config.js 中进行配置

    配置代码如下(webpack.config.js):

      module.exports = {
          module: {
              rules: [
                  {
                      test: /.png$/,
                      use: {
                          loader: 'url-loader',
                          options: { // 设置配置选项
                              limit: 10 * 1024 // 10 KB 单位字节,只匹配 10 KB 以下的
                          }
                      }
                  }
              ]
          }
      }
    

    最佳实践

    1)小文件使用 Data URLs,转换为 Data URLs 嵌入代码中,减少请求次数;
    2)大文件单独提取存放,提高加载速度。

    注意

    在对 url-loader 适合的文件大小进行限制后,需要同时安装 file-loader,因为超出限制的文件,会去查找 file-loader 进行转换。如果不安装 file-loader,将会因为找不到,而引起程序报错。

代码检查类

代码检查类加载器,会对所加载到的资源文件(一般指代码)去进行校验。目的是为了统一代码的风格,从而提高代码质量,这种加载器一般不会修改生产环境的代码,如 eslint-loader。

Loader Dev
开发需求

希望得到的结果是 markdown 转换过后的字符串。

准备工作
  • 1,新建一个名为 markdown-loader 的项目,并创建 src 文件夹;

  • 2,在 src 文件夹下,新建 xxx.md 文件,测试内容随便写;

  • 3,在 src 文件夹下,新建 main.js 入口文件,并引入 xxx.md 文件

    代码示例如下(main.js):

      import about from './about.md'
      console.log(about); 
    
  • 4,在根目录下,新建 index.html,页面展示的入口文件;

  • 5,初始化包管理文件 package.json

      $ yarn init --yes # or npm init -y
    
  • 6,安装 webpack 模块,以及其依赖命令模块 webpack-cli

      $ yarn add webpack webpack-cli --dev # or npm i webpack webpack-cli --save-dev
    
  • 7,在根目录下,新建 markdown-loader.js,即 md 文件的转换 loader

  • 8,在根目录下,新建 webpack.config.js,并设置对 md文件的规则配置

    配置代码如下(webpack.config.js):

      const path = require('path')
      
      module.exports = {
          mode: 'none',
          entry: './src/main.js',
          output: {
              filename: 'bundle.js',
              path: path.join(__dirname, 'dist'),
              publicPath: 'dist/'
          },
          module: {
              rules: [
                  {   // 用来转换的 loader 既可以是一个模块,也可以是一个 js 文件
                      test: /.md$/,
                      use: './markdown-loader' 
                  }
              ]
          }
      }
    
基本配置

markdown-loader.js 文件需求,输入:就是每次加载到的资源文件;输出:就是此次加工后的结果。

  • 1,在 markdown-loader.js 中进行配置

    配置代码如下(markdown-loader.js):

      // 通过 source 参数,接收输入
      module.exports = source => {
          console.log(source);
          return `console.log('hello ~')`
      }
    

    可以看到,使用的 js 的代码标准进行了输出字符串,这是因为 loader 要求输出结果必须是一段标准的 JavaScript代码

  • 2,若要解析 md 文件,则需要安装 md 文件的解析模块

      $ yarn add marked --dev # or npm i marked --save-dev
    
  • 3,在 markdown-loader.js 中,使用 marked 模块解析 md 文件,并将结果以模块形式导出

    配置代码如下(markdown-loader.js):

      const marked = require('marked') // 导入 md 文件的解析模块
      
      module.exports = source => { 
          // 解析 source, 返回值是一串html字符串,即转换后的结果
          const html = marked(source)
          // 将其结果进行导出,需要输出 JavaScript 代码
          return `module.exports = ${JSON.stringify(html)}`
          // or 
          // return `export default ${JSON.stringify(html)}`
      }
    
  • 4,返回 html字符串,交给下一个 loader 处理

    配置代码如下(markdown-loader.js):

      const marked = require('marked') // 导入 md 文件的解析模块
      
      module.exports = source => { 
          // 解析 source, 返回值是一串html字符串,即转换后的结果
          const html = marked(source)
          // 返回 html字符串,交给下一个 loader 处理
          return html
      }
    
  • 5,安装用于处理 html 加载的 loader

      $ yarn add html-loader --dev # or npm i html-loader --save-dev
    
  • 6,修改 webpack.config.js 配置文件

    配置代码如下(webpack.config.js):

      const path = require('path')
      
      module.exports = {
          mode: 'none',
          entry: './src/main.js',
          output: {
              filename: 'bundle.js',
              path: path.join(__dirname, 'dist'),
              publicPath: 'dist/'
          },
          module: {
              rules: [
                  {
                      test: /.md$/,
                      use: [
                          'html-loader',
                          './markdown-loader' // 按照 从后往前 的顺序进行执行
                      ]
                  }
              ]
          }
      }
    

Plugins

webpack plugins 可以增强 webpack 自动化能力,可以解决除了资源加载以外的其他自动化工作,例如:打包之前,自动清除 dist 目录;拷贝静态文件至输出目录;压缩输出代码等。

clean-webpack-plugin

clean-webpack-plugin,自动清除输出目录插件。

基本使用

  • 1,安装插件模块

      $ yarn add clean-webpack-plugin --dev
    
  • 2,在 webpack.config.js 中,添加插件配置

    配置代码如下(webpack.config.js):

      // 导入 clean-webpack-plugin 插件,这个插件导出一个 CleanWebpackPlugin 成员
      const { CleanWebpackPlugin } = require('clean-webpack-plugin')
      
      module.exports = {
      	// ...   
          plugins: [  // plugins 专门用来配置插件的属性
              // 一般插件导出的都是一个类型,通过这个类型创建实例
              // 然后将这个实例,放入到 plugins 数组中
              new CleanWebpackPlugin()
          ]
      }
    

    注意

    plugins 属性,是一个数组,它与 module 属于同级。

html-webpack-plugin

html-webpack-plugin,自动生成 HTML 插件,不需要在手动书写 html 入口文件。

基本使用

  • 1,安装插件模块

      $ yarn add html-webpack-plugin --dev
    
  • 2,在 webpack.config.js 中,导入插件,并进行配置

    配置代码如下(webpack.config.js):

      const path = require('path')
      const { CleanWebpackPlugin } = require('clean-webpack-plugin')
      // html-webpack-plugin 默认导出的就是一个类型,无需解构其内部成员
      const HtmlWebpackPlugin = require('html-webpack-plugin')
      
      module.exports = {
        mode: 'none',
        entry: './src/main.js',
        output: {
          filename: 'bundle.js',
          path: path.join(__dirname, 'dist'),
          // publicPath: 'dist/' // 自动生成 html 时,不需配置
        },
        plugins: [
          new CleanWebpackPlugin(),
          // 添加 HtmlWebpackPlugin 实例对象    
          // 自定义输出文件内容:给构造函数传入对象参数,指定配置选项
          new HtmlWebpackPlugin({ // 用于生成 index.html
            title: 'Webpack Plugin Sample', // 设置 HTML 的标题
            meta: { // 设置页面中的 元数据标签
              viewport: 'width=device-width'
            },
            // 指定模板文件,使生成的HTML文件根据模板文件进行生成
            template: './src/index.html'
          }),
          // 同时输出多个页面文件, 即可以添加多个 HtmlWebpackPlugin 实例
          // 每一个 HtmlWebpackPlugin 实例,就是用来生成一个 HTML文件的
          new HtmlWebpackPlugin({ // 用于生成 about.html
            filename: 'about.html' // 指定输出的文件名
          })
        ]
      }
    
copy-webpack-plugin

copy-webpack-plugin,将不需要编译转换的 静态资源文件 拷贝到输出目录,一般存放在 public 目录下。

基本使用

  • 1,安装插件模块

      $ yarn add copy-webpack-plugin --dev
    
  • 2,在 webpack.config.js 中,导入插件,并进行配置

    配置代码如下(webpack.config.js):

      const path = require('path')
      const CopyWebpackPlugin = require('copy-webpack-plugin')
      
      module.exports = {
        // ...
        plugins: [
          // ...
          // 传入 数组参数,用于指定需要去拷贝的文件路径
          new CopyWebpackPlugin([ 
            // 'public/**'  // 可以是一个目录,通配符,文件路径
            'public'  // 表示会将 public 目录下所有文件全部拷贝到输出目录
          ])
        ]
      }
    
plugins dev

Plugin 是通过 在webpack 生命周期的钩子中挂载函数实现扩展的。Webpack 要求,插件必须是一个函数或者是一个包含 apply 方法的对象,最后将其挂载到 钩子上。

开发需求

删除 bundle.js 中生成的注释。

基本配置
  • 在 webpack.config.js 中,进行插件的创建和使用

    配置代码如下(webpack.config.js):

      class MyPlugin { // 定义一个插件类型
        /**
         * 在 webpack 启动时,自动被调用
         * @param {*} compiler 
         * compiler 对象参数,是 webpack 工作过程中最核心的一个对象
         * compiler 对象参数,包含了此次构建的所有配置信息
         * 通过 compiler 对象注册钩子函数
         */
        apply(compiler) {
          console.log('MyPlugin 启动');
          /**
           * 通过 compiler.hooks 可以访问到钩子
           * 通过 tap 方法去注册钩子函数
           * tap方法,接收两个参数:
           * -- 第一个参数:插件的名称
           * -- 第二个参数:需要挂载到钩子上的函数
           */
          compiler.hooks.emit.tap('MyPlugin', compilation => {
            // compilation 对象 => 可以理解为此次打包的上下文
            // 所有打包的结果,都会放到 compilation 对象当中
      
            for (const name in compilation) {
              // 对象中的键(属性名),代表每一个资源文件的名称
      
              // 判断只对 js文件 进行处理
              if (name.endsWith('.js')) {
                // assets 获取即将写入目录当中的资源文件信息
                // source() 拿到对应的资源文件的内容
                const contents = compilation.assets[name].source()
                // 使用正则替换代码中的注释
                const withoutComments = contents.replace(/\/*\**\*\//g, '')
                // 将结果 覆盖到原有的文件中
                compilation.assets[name] = {
                  source: () => withoutComments, // 返回新内容
                  size: () => withoutComments.length // 返回内容的大小,必须方法
                }
              }
            }
          })
        }
      }
      
      module.exports = {
          // ...
          plugins: [
              // ...
              // 应用 MyPlugin 插件
              new MyPlugin()
          ]
      }
    

体验优化

自动编译
  • 1,直接在命令行启动 watch 工作模式,监听文件变化,自动重新打包。

      $ yarn webpack --watch
    
  • 2,在 package.json 中,配置 NPM Scripts

    配置代码如下(package.json):

      {
          "scripts": {
              "build": "webpack --watch"
          }
      }
    

    可以看到,开启 watch 工作模式以后,打包命令会一直处于工作状态,当文件修改后,会自动进行打包,直到手动结束 cli 命令。

自动刷新

使用 BrowserSync 启动热更新开发 Web服务器,实现自动刷新浏览器

  • 1,全局安装 BrowserSync 模块

      $ yarn global add browser-sync # or npm i browser-sync -g
    
  • 2,使用命令启动 web服务器

      $ browser-sync dist --files "**/*"
    

    缺点

    1)操作上太麻烦了;
    2)效率上降低了。

Dev Server

Webpack Dev Server,是 Webpack 官方推出的开发工具,提供用于开发的 HTTP Server,集成了 自动编译自动刷新浏览器 等功能。

版本说明

本次安装 Webpack Dev Server 版本,要求 webpack 4 版本以下webpack 5+ 版本已经集成了 Webpack Dev Server 开发工具,无需再进行安装,直接使用 webpack server 即可开启服务。

注意

Webpack Dev Server 的版本,要比 Webpack 的版本低一级,否则无法使用。

准备工作
  • 1,安装 开发工具

      $ yarn add webpack-dev-server --dev
    
  • 2,运行命令,使用 --open 自动唤醒浏览器,并打开运行地址

      $ yarn webpack-dev-server --open
    

    执行说明

    1)运行时,内部自动使用 webpack 进行打包;
    2)启动 HTTP Server,自动运行打包结果;
    3)自动监听代码变化,自动立即重新打包;
    4)打包结果不会写入到磁盘中,打包结果会暂时存放在内存中;
    5)HTTP Server 会从内存中读取这些文件,然后发送给浏览器。

    优势

    Webpack Dev Server,会减少很多磁盘读写操作,从而大大提高构建效率。

    问题

    Dev Server 默认只会 serve 打包输出文件,即只要是 webpack 打包输出的文件,都可以直接被访问。其他静态资源如果也需要 serve,则需要去告知 webpack。

基本配置
  • 1,配置静态资源访问

    配置代码如下(webpack.config.js):

      const path = require('path')
      const { CleanWebpackPlugin } = require('clean-webpack-plugin')
      const HtmlWebpackPlugin = require('html-webpack-plugin')
      // const CopyWebpackPlugin = require('copy-webpack-plugin')
      
      module.exports = {
        // 专门为 webpack-dev-server 指定的配置选项
        devServer: {
          // 额外为开发服务器指定查找静态资源目录,可以是字符串或数组,配置一个或多个
          contentBase: ['./public']
        },
        plugins: [
          new CleanWebpackPlugin(),
          // 用于生成 index.html
          new HtmlWebpackPlugin({
            title: 'Webpack Tutorials',
            meta: {
              viewport: 'width=device-width'
            },
            template: './src/index.html'
          }),
          // 开发阶段最好不要使用这个插件
          // 一般放在上线前的最后一次打包使用
          // 开发阶段需要频繁的serve, 多次使用插件会影响运行效率
          // new CopyWebpackPlugin(['public'])
        ]
      }
    
  • 2,开发阶段接口跨域问题,需要配置 代理API

    配置代码如下(webpack.config.js):

      module.exports = {
        // 专门为 webpack-dev-server 指定的配置选项, 开发阶段的配置
        devServer: {
          // 额外为开发服务器指定查找静态资源目录,可以是字符串或数组,配置一个或多个
          contentBase: ['./public'],
          // proxy属性,用来添加代理服务配置
          proxy: {
            /**
             * 每一个属性就是一个代理规则的配置
             * 属性名: 需要被代理的请求路径前缀,
             *        即请求以哪一个地址开始,就会走对应的代理请求
             * 属性值: 为这个前缀所匹配到的代理规则配置 
             */ 
            '/api': {
              // http://localhost:8080/api/users => https://api.github.com/api/users
              target: 'https://api.github.com', // 代理目标
              // http://localhost:8080/api/users => https://api.github.com/users
              // 如果代理目标地址中没有‘/api’,则需要重写代理目标地址
              pathRewrite: {
                '^/api': '' // 以正则的形式进行匹配,以 ^ 开头
              },
              // 不能使用 localhost:8080 作为请求 GitHub 的主机名
              // 设置改变主机名
              changeOrigin: true
            }
          }
        }
      }
    
Source map

Source map,源代码地图,用来映射转换过后的代码与源代码之间的关系。一段转换过后的代码,通过转换过程中生成的 Source Map 文件,可以逆向得到源代码。主要用来在开发阶段进行调试和定位错误。

基本使用

  • 1,引用 Source Map 的注释

      //# sourceMapppingURL=jquery-3.4.3.min.map
    

    在浏览器中,打开开发人员工具,开发人员工具加载到的 js文件最后存在上面的注释,会自动去请求 Source Map 文件,然后根据文件的内容逆向解析出对应的源代码,以便于调试。又因为存在映射关系,所以源代码中如果出现错误,就会很容易定位到源代码中错误的位置。

  • 2,在 webpack.config.js中,配置 Source Map

    配置代码如下(webpack.config.js):

      // 其余代码省略
      module.exports = {
        // 配置开发过程中的辅助工具,
        // 也就是与 Source Map 相关的一些功能配置
        devtool: 'source-map',
      }
    
  • 3,运行打包命令,查看 bundle.js 底部是否存在 Source Map 的注释

      $ yarn webpack
    

    查看 bundle.js 底部,如下图所示:

    在这里插入图片描述
    Webpack 对 Source Map的风格支持

    Webpack 目前支持 12 种不同的方式,每种方式所生成的 Source Map 效果,以及生成 Source Map 的速度都是不一样的。效果最好的,生成速度越慢;反之,生成最快的,生成的 Source Map 效果不好,几乎没有。

  • 12 种方式对比,如下图所示:

    在这里插入图片描述

eval 模式

eval() 函数,用来运行字符串中的 JavaScript 代码。默认情况下,运行的代码会运行在一个临时的虚拟机环境中,可以通过 sourceURL 来声明这段代码所属的文件路径。

基本使用

  • 1,在浏览器控制台,进行测试

      eval('console.log(123)//# sourceURL=./foo/bar.js')
    

    运行示例,如下图所示:

    在这里插入图片描述
    点击./foo/bar.js,显示效果,如下图所示:

    在这里插入图片描述

  • 2,在 webpack.config.js 中,进行配置

    配置代码如下(webpack.config.js):

      // 其余代码省略
      module.exports = {  
          // 配置开发过程中的辅助工具,  
          // 也就是与 Source Map 相关的一些功能配置  
          devtool: 'eval',
      }
    

    浏览器定位错误,如下图所示:

    在这里插入图片描述
    可以看到,当点击进去的时候,显示的却是打包过后的模块代码。

    在 eval 模式下,会将每个模块的js 代码都放到 eval() 函数中执行,在执行的字符串最后通过 sourceURL 的方式去说明所对应的文件路径,这样浏览器通过 eval 在执行代码时,就会知道代码所对应的源代码是哪一个文件,从而去定位错误所出现的文件。

    总结

    eval 模式下,不会生成 Source Map 文件,因此构建速度最快,但是其效果比较简单,只能知道源代码文件的名称,而不知道具体的行列信息。

模式对比

一次打包过程中,同时生成所有模式下的不同结果。

基本使用

  • 1,在 webpack.config.js 中,进行配置

配置代码如下(webpack.config.js):

  const HtmlWebpackPlugin = require('html-webpack-plugin')
  
  const allModes = [
  	'eval',
  	'cheap-eval-source-map',
  	'cheap-module-eval-source-map',
  	'eval-source-map',
  	'cheap-source-map',
  	'cheap-module-source-map',
  	'inline-cheap-source-map',
  	'inline-cheap-module-source-map',
  	'source-map',
  	'inline-source-map',
  	'hidden-source-map',
  	'nosources-source-map'
  ]
  
  module.exports = allModes.map(item => {
      return {
          devtool: item,
          mode: 'none',
          entry: './src/main.js',
          output: {
              filename: `js/${item}.js`
          },
          module: {
              rules: [
                  {
                      test: /\.js$/,
                      use: {
                          // 辨别其中一类模式的差异
                          loader: 'babel-loader',
                          options: {
                              presets: ['@babel/preset-env']
                          }
                      }
                  }
              ]
          },
          plugins: [
              // 为每一个打包任务生成一个 HTML文件
              new HtmlWebpackPlugin({
                  filename: `${item}.html`
              })
          ]
      }
  })
  • 2,使用 http-server 开启服务器

      $ http-server dist
    

    运行结果,如下图所示:

    在这里插入图片描述

  • 3,通过查看每一个 HTML 文件,得出各个模式的对比结果。

    1)带有 module 的模式,不会经过 loader 的编译转换,生成的 Source Map 就是项目的源代码。而不带有 module 的模式,则是经过 babel 转换后的代码。
    2)hidden-source-map,一般用在生成第三方包的时候,需要生成 Source Map 文件,但又不想在 包 中引入。
    3)nosources-source-map,用在生产环境中,保护源代码不被暴露。

选择合适的 Source Map

下面是建议选择模式,应根据具体情况具体选择。

  • 开发模式,选择cheap-module-eval-source-map

    1)代码每行不会超过 80 个字符;
    2)代码经过 Loader 转换过后的差异较大;
    3)首次打包速度慢无所谓,重写打包相对较快。

  • 生产模式,选择 none

    1)Source Map 会暴露源代码;
    2)调试是开发阶段的事情。

  • 生产模式,方便定位错误,选择 nosource-source-map

    1)不会向外暴露源代码;
    2)出现错误时,可以找到源代码对应的位置。

HMR

HMR(Hot Module Replacement),模块热替换,又叫模块热更新,他是 Webpack 中最强大的功能之一,它能够实现在应用运行过程中实时替换某个模块,而应用运行状态不会改变。HMR 极大程度的提高了开发者的工作效率,因此很受欢迎。

HMR ,已经集成在 webpack-dev-server 中,无需再单独安装模块。

开启 HMR

  • 1,直接在命令中,使用 --hot 开启热更新

      $ yarn webpack-dev-server --hot
    
  • 2,在 webpack.config.js 中,进行配置开启热更新

    配置代码如下(webpack.config.js):

      const webpack = require('webpack')
      
      module.exports = {
        mode: 'development',
        devServer: {
          hot: true
        },
        plugins: [
          // 载入 webpack 的内置插件
          new webpack.HotModuleReplacementPlugin()
        ]
      }
    

    Webpack 中的 HMR 并不可以开箱即用,他需要手动处理 JS 模块热替换逻辑。

HMR APIs

  • 在 main.js 中,使用 HMR APIs 手动处理热替换

    代码示例如下(main.js):

      // module.hot 对象,是 HMR API 的核心对象 
      /**
       * accept(),用于注册模块更新过后的处理函数
       * 第一个参数,指的是 依赖模块的路径
       * 第二个参数,指的是 依赖更新过后的处理函数
       */ 
      module.hot.accept('./editor', () => {
          // 热替换逻辑
      })
    

注意事项

  • 1,处理 HMR 的代码报错会导致自动刷新

    配置代码如下(webpack.config.js):

       const webpack = require('webpack')
       
       module.exports = {
         devServer: {
           hotOnly: true // 只使用 HMR,不会 fallback 到 live reloading
         },
         plugins: [
           // 载入 webpack 的内置插件
           new webpack.HotModuleReplacementPlugin()
         ]
       }
    
  • 2,启用 HMR 的情况下,HMR API 报错

    代码示例如下(main.js):

       if (module.hot) { // 先判断这个对象是否存在,再进行任务的注册
           module.hot.accept('./editor', () => {
               // 热替换逻辑
           })
       }
    
  • 3,业务中添加的处理代码,会在打包过后自动移除。

环境配置

配置文件根据环境不同导出不同配置

  • 在 webpack.config.js 中,进行导出配置

    配置代码如下(webpack.config.js):

      const webpack = require('webpack')
      const { CleanWebpackPlugin } = require('clean-webpack-plugin')
      const HtmlWebpackPlugin = require('html-webpack-plugin')
      const CopyWebpackPlugin = require('copy-webpack-plugin')
      
      /**
       * 导出一个函数,在这个函数中设置所需的配置对象
       * 接收两个参数,
       *    第一个参数 env:即 通过 cli 传递的环境名参数
       *    第二个参数 argv: 指 运行 cli 过程中传递的所有参数
       */
      module.exports = (env, argv) => {
        // 设置默认模式:开发模式
        const config = {
          mode: 'development',
          entry: './src/main.js',
          output: {
            filename: 'js/bundle.js'
          },
          devtool: 'cheap-eval-module-source-map',
          devServer: {
            hot: true,
            contentBase: 'public'
          },
          module: {
            rules: [
              {
                test: /\.css$/,
                use: [
                  'style-loader',
                  'css-loader'
                ]
              },
              {
                test: /\.(png|jpe?g|gif)$/,
                use: {
                  loader: 'file-loader',
                  options: {
                    outputPath: 'img',
                    name: '[name].[ext]'
                  }
                }
              }
            ]
          },
          plugins: [
            new HtmlWebpackPlugin({
              title: 'Webpack Tutorial',
              template: './src/index.html'
            }),
            new webpack.HotModuleReplacementPlugin()
          ]
        }
      
        // 生产模式
        if (env === 'production') {
          config.mode = 'production'
          config.devtool = false // 禁用 Source Map
          config.plugins = [
            ...config.plugins,
            new CleanWebpackPlugin(),
            new CopyWebpackPlugin(['public'])
          ]
        }
      
        return config
      }
    

    启动开发模式,默认模式为 开发模式,无需指定工作模式

      $ yarn webpack
    

    启动生产模式,使用 --env 指定工作模式

      $ yarn webpack --env production
    

    注意

    只适用中小型项目的配置,大型项目不适合。

不同环境对应不同配置文件

  • 1,新建 webpack.common.js,用来存放 公共 配置信息

    配置代码如下(webpack.common.js):

      const HtmlWebpackPlugin = require('html-webpack-plugin')
      // 公共配置
      module.exports = {
        entry: './src/main.js',
        output: {
          filename: 'js/bundle.js'
        },
        module: {
          rules: [
            {
              test: /\.css$/,
              use: [
                'style-loader',
                'css-loader'
              ]
            },
            {
              test: /\.(png|jpe?g|gif)$/,
              use: {
                loader: 'file-loader',
                options: {
                  outputPath: 'img',
                  name: '[name].[ext]'
                }
              }
            }
          ]
        },
        plugins: [
          new HtmlWebpackPlugin({
            title: 'Webpack Tutorial',
            template: './src/index.html'
          })
        ]
      }
    
  • 2,安装 webpack-merge 模块, 合并 webpack 配置

      $ yarn add webpack-merge --dev
    
  • 3,新建 webpack.prod.js ,用来存放 生产模式 配置信息

    配置代码如下(webpack.prod.js):

      // 导入 webpack-merge
      const merge = require('webpack-merge')
      const { CleanWebpackPlugin } = require('clean-webpack-plugin')
      const CopyWebpackPlugin = require('copy-webpack-plugin')
      const common = require('./webpack.common')
      
      // webpack-merge模块 导出 merge(), 合并 webpack 配置
      module.exports = merge(common, {
        mode: 'production',
        plugins: [
          new CleanWebpackPlugin(),
          new CopyWebpackPlugin(['public'])
        ]
      })
    
  • 4,新建 webpack.dev.js ,用来存放 开发模式 配置信息

    配置代码如下(webpack.dev.js):

      const webpack = require('webpack')
      const merge = require('webpack-merge')
      const common = require('./webpack.common')
      
      module.exports = merge(common, {
        mode: 'development',
        devtool: 'cheap-eval-module-source-map',
        devServer: {
          hot: true,
          contentBase: 'public'
        },
        plugins: [
          new webpack.HotModuleReplacementPlugin()
        ]
      })
    
  • 5,运行时,可以直接使用 --config 参数指定配置文件

      $ yarn webpack --config webpack.prod.js
    
  • 6,或者 在 package.json 中,配置 NPM Scripts

    配置代码如下(package.json):

      {
          "scripts": {
              "dev": "webpack --config webpack.dev.js",
              "build": "webpack --config webpack.prod.js"
          }
      }
    

配置优化

DefinePlugin

DefinePlugin,为代码注入全局成员。在 production 模式下,默认启用,会为代码中注入一个 process.env.NODE_ENV 的常量。一般通过这个成员去判断当前的运行环境,从而去执行对应的操作。

基本使用

  • 在 webpack.config.js 中,进行配置

    配置代码如下(webpack.config.js):

      const webpack = require('webpack')
      
      module.exports = {
          // ...
          plugins: [
              // DefinePlugin是 webpack 的内置插件
              // 构造函数接收一个对象,对象中的每一个键值都会被注入到代码中
              new webpack.DefinePlugin({
                  // 值要求的是一个 JS代码片段
                  API_BASE_URL: JSON.stringify('https://api.example.com')
              })
          ]
      }
    
Tree-shaking

Tree-shaking,摇掉 代码中未引用代码(dead-code)。在启动生产模式时,自动检测出未引用的代码,并自动将未引用代码移除,从而减少冗余代码。

Tree-shaking 并不是指某个配置选项,它是一组功能搭配使用后的优化效果,在 production 模式下自动开启。

基本使用

不使用 production 模式,其他模式下开启 Tree-shaking。

  • 1,在 webpack.config.js 中,进行配置

    配置代码如下(webpack.config.js):

      module.exports = { // 其他代码省略
          mode: 'none',
          // 集中配置 webpack 内部的优化功能
          optimization: {
              // 模块只导出被使用的成员
              usedExports: true,
              // 尽可能将所有模块合并并输出到到一个函数中
              concatenateModules: true,
              // 压缩输出结果
              minimize: true
          }
      }
    

    Tree-shaking 前提是 ES Modules,也就是说,由 webpack 打包的代码必须使用 ES Modules。

  • 2,最新的 babel-loader 默认关闭 ESM 转换,可以在 webpack.config.js 中,手动设置关闭

    配置代码如下(webpack.config.js):

      module.exports = {
          mode: 'none',
          module: {
              rules: [
                  {
                      test: /\.js$/,
                      use: {
                          loader: 'babel-loader',
                          options: {
                              presets: [
                     // 如果 Babel 加载模块时已经转换了 ESM,则会导致 Tree Shaking 失效
                     				// ['@babel/preset-env', { modules: 'commonjs' }]
                    // 配置为 false,确保 preset-env 内部不会开启 ES Module 转换的插件
                                  // ['@babel/preset-env', { modules: false }]
                    // 使用默认配置:auto,这样 babel-loader 会自动关闭 ESM 转换
                                  ['@babel/preset-env', { modules: 'auto' }]
                              ]
                          }
                      }
                  }
              ]
          },
          optimization: {
              // 模块只导出被使用的成员
              usedExports: true,
              // 尽可能合并每一个模块到一个函数中
              // concatenateModules: true,
              // 压缩输出结果
              // minimize: true
          }
      }
    
sideEffects
基本介绍

sideEffects,webpack 4 中新增的特性,它允许通过配置的方式去标识代码是否有副作用,从而为 Tree-shaking 提供更大的压缩空间,在 production 模式下自动开启。

副作用,是指模块执行时除了导出成员之外所做的事情。

适用场景

sideEffects,一般用于 npm 包标记是否有副作用。

前提条件

确保你的代码真的没有副作用,否则会误删掉那些有副作用的代码。

基本使用
  • 1,在 webpack.config.js 中,开启sideEffects特性

    配置代码如下(webpack.config.js):

      module.exports = {
          mode: 'none',
          optimization: {
              // 手动开启特性
              sideEffects: true,
              // 模块只导出被使用的成员
              // usedExports: true,
              // 尽可能合并每一个模块到一个函数中
              // concatenateModules: true,
              // 压缩输出结果
              // minimize: true,
          }
      }
    

    检查 package.json 中是否有 sideEffects 的标识,以此来判断这个模块是否有副作用。

  • 2,设置所有模块都没有副作用,模块中没有被用到的代码就不会再被打包,则将会被移除掉。

    配置代码如下(package.json):

      {    
          "sideEffects": false
      }
    
  • 3,设置某些模块具有副作用,即 将这些模块会被打包进输出结果,不会被移除掉。

    配置代码如下(package.json):

      {
          "sideEffects": [
              "./src/extend.js",
              "*.css"
          ]
      }
    
Code Splitting

Code Splitting,代码分包/代码分割,在应用中按需加载模块,从而提高应用的响应速度和运行效率。

多入口打包

多入口打包,一般适用于多页应用程序,即 一个页面对应一个打包入口,公共部分单独提取。

基本使用

  • 1,在 webpack.config.js 中,进行配置

    配置代码如下(webpack.config.js):

      const { CleanWebpackPlugin } = require('clean-webpack-plugin')
      const HtmlWebpackPlugin = require('html-webpack-plugin')
      
      module.exports = {
          mode: 'none',
          /**
         * entry 属性值的形式:
         *       字符串:设置一个打包入口
         *       数 组: 将多个文件打包进一个文件中
         *       对 象: 设置多个打包入口,分别生成对应的打包文件
         *              一个属性对应一个打包入口,
         *              属性名为入口名称,属性值为入口所对应的文件路径
         */ 
          entry: { 
              index: './src/index.js',
              album: './src/album.js'
          },
          output: {
              // 多个入口,就意味着生成多个打包文件
              // 使用 [name] 占位符,动态输出文件名
              // [name] 最终会被替换成 入口的名称
              filename: '[name].bundle.js'
          },
          plugins: [
              new CleanWebpackPlugin(),
              new HtmlWebpackPlugin({
                  title: 'Multi Entry',
                  template: './src/index.html',
                  filename: 'index.html',
                  // 指定每个 HTML 文件所使用的 bundle文件
                  // 每个打包入口会形成独立的 chunks
                  chunks: ['index']
              }),
              new HtmlWebpackPlugin({
                  title: 'Multi Entry',
                  template: './src/album.html',
                  filename: 'album.html',
                  chunks: ['album']
              })
          ]
      }
    
  • 2,在 webpack.config.js 中,配置属性,使其在打包时自动提取公共模块

    配置代码如下(webpack.config.js):

      // 其余部分省略
      module.exports = {
          optimization: {
              splitChunks: {
                  // 自动提取所有公共模块到单独 bundle
                  chunks: 'all'
              }
          },
      }
    
动态导入

按需加载,需要用到某个模块时,再加载这个模块,可以极大的节省带宽和流量。动态导入的模块会被自动提取到对应的 bundle 中,从而实现分包。相对多入口打包,更加灵活。

基本使用

  • 在需要导入的地方,使用 ES Modules 的动态导入

    代码示例如下(index.js):

      if (hash === '#posts') {
          // 魔法注释:命名 bundle 的名称,相同的名称会打包到一个 bundle 中
          // /* webpackChunkName: 'components' */' 
          import(/* webpackChunkName: 'components' */'./posts/posts').then(({ default: posts }) => {
              mainElement.appendChild(posts())
          })
      } else if (hash === '#album') {
          import(/* webpackChunkName: 'components' */'./album/album').then(({ default: album }) => {
              mainElement.appendChild(album())
          })
      }
    
MiniCssExtractPlugin

MiniCssExtractPlugin,将 CSS 代码从打包结果中提取出来的插件,通过这个插件,可以实现 CSS 的按需加载。

基本使用

  • 1,安装插件模块

      $ yarn add mini-css-extract-plugin --dev
    
  • 2,在 webpack.config.js 中,进行导入和配置

    配置代码如下(webpack.config.js):

      // 导入 mini-css-extract-plugin 插件
      const MiniCssExtractPlugin = require('mini-css-extract-plugin')
      
      module.exports = {
          module: {
              rules: [
                  {
                      test: /\.css$/,
                      use: [
                          'style-loader', // 将样式通过 style 标签注入到页面中
                          // 实现样式文件通过 link 标签的方式注入
                          MiniCssExtractPlugin.loader,
                          'css-loader'
                      ]
                  }
              ]
          },
          plugins: [
              // 创建 MiniCssExtractPlugin 插件实例,自动提取 CSS 到单个文件中
              new MiniCssExtractPlugin()
          ]
      }
    

使用建议

当样式代码所占内存较小时,不建议生成单个文件,此时减少请求次数,可能效果更好。

OptimizeCssAssetsWebpackPlugin

webpack 内置的压缩插件,只针对 JS 代码,对于其他的资源文件,都要使用对应的压缩插件进行压缩。

OptimizeCssAssetsWebpackPlugin,压缩输出的 CSS 文件。

基本使用

  • 1,安装插件模块

      $ yarn add optimize-css-assets-webpack-plugin --dev # css 压缩插件
      $ yarn add terser-webpack-plugin --dev      # webpack 内置的 JS 压缩插件
    
  • 2,在 webpack.config.js 中,进行导入和配置

    配置代码如下(webpack.config.js):

      const { CleanWebpackPlugin } = require('clean-webpack-plugin')
      const HtmlWebpackPlugin = require('html-webpack-plugin')
      // 导入 mini-css-extract-plugin 插件
      const MiniCssExtractPlugin = require('mini-css-extract-plugin')
      // 导入 optimize-css-assets-webpack-plugin
      const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
      // 导入 webpack 内置的 JS 压缩插件
      const TerserWebpackPlugin = require('terser-webpack-plugin')
      
      module.exports = {
          optimization: {
              // 只有在 minimizer 特性开启时,才会运行其内部的插件
              // 生产模式时,minimizer 特性自动开启,即下面插件才会工作
              // 配置 minimizer 时,webpack 内部的压缩插件就会失效,需要手动添加
              minimizer: [
                  new TerserWebpackPlugin(),
                  new OptimizeCssAssetsWebpackPlugin()
              ]
          }
      }
    
substitutions

文件名使用 Hash 的原因?

一旦资源文件发生改变,文件名称也可以跟着改变。对于客户端而言,全新的文件名,就是全新的请求,则将不会存在缓存问题。也就是说,把服务端的缓存策略时间设置的非常长,就不用担心文件更新过后的问题。因此,生产模式下,文件名使用 Hash。

三种 Hash

  • 1,[hash] 属于 项目级别,即项目中的任何一个地方变化,都会导致打包时,Hash值 全部改变

    配置代码如下(webpack.config.js):

      const MiniCssExtractPlugin = require('mini-css-extract-plugin')
      module.exports = {
          output: {
              filename: '[name]-[hash].bundle.js'
          },
          plugins: [
              new MiniCssExtractPlugin({
                  filename: '[name]-[hash].bundle.css'
              })
          ]
      }
    
  • 2,[chunkhash] 属于 chunk 级别,即在打包中,只要是同一路的打包,chunkhash 就是相同的。相比于 [hash],控制较精确一点。

    配置代码如下(webpack.config.js):

      const MiniCssExtractPlugin = require('mini-css-extract-plugin')
      module.exports = {
          output: {
              filename: '[name]-[chunkhash].bundle.js'
          },
          plugins: [
              new MiniCssExtractPlugin({
                  filename: '[name]-[chunkhash].bundle.css'
              })
          ]
      }
    
  • 3,[contenthash] 属于 文件级别,根据输出文件的内容输出 hash值,即不同的文件就有不同的hash值,最适合解决缓存问题,通过 :number 指定hash的长度。

    配置代码如下(webpack.config.js):

      const MiniCssExtractPlugin = require('mini-css-extract-plugin')
      module.exports = {
          output: {
              filename: '[name]-[contenthash:8].bundle.js'
          },
          plugins: [
              new MiniCssExtractPlugin({
                  filename: '[name]-[contenthash].bundle.css'
              })
          ]
      }
    

Rollup

基本介绍

Rollup,可以将项目中散落的代码打包成整块的代码,从而使得这些划分的模块,更好的运行在浏览器环境或者 NodeJs 中。相对于 Webpack 来说,Rollup 更为小巧,它仅仅是一款 ES Modules 的打包器,是一个提供充分利用 ES Modules 各项特性的高效打包器。

适用场景

开发一个框架或者类库,使用 Rollup。

快速上手

Rollup打包时,会默认开启 Tree-shaking。

基本使用

  • 1,安装 Rollup 模块
      $ yarn add rollup--dev
    
  • 2,运行 Rollup 的 cli 命令,查看 rollup 各种参数
      $ yarn rollup
    
  • 3,运行命令,指定打包的入口文件、输出文件的格式,以及输出文件的路径,进行打包
      $ yarn rollup ./src/index.js --format iife --file dist/bundle.js
    

基本使用

配置文件

在项目根目录下,新建 rollup.config.js 配置文件。

基本使用

  • 1,安装 Rollup 模块

      $ yarn add rollup --dev
    
  • 2,新建 rollup.config.js 配置文件,进行打包信息的配置

    配置代码如下(rollup.config.js):

      // 导出一个配置对象
      export default {    
          input: 'src/index.js', // 指定打包的入口文件
          // 指定输出文件的配置信息
          output: {
              file: 'dist/bundle.js', // 指定输出文件的文件名        
              format: 'iife' // 指定输出文件的格式,自执行函数
          }
      }
    
  • 3,运行命令,通过 --config 表明使用项目中的配置文件,默认不会读取配置文件,默认读取的配置文件名称:rollup.config.js

      $ yarn rollup --config # [配置文件名称]
    

Plugins

插件是 Rollup 唯一扩展途径。

rollup-plugin-json

rollup-plugin-json,用来在代码中导入 json 文件。

基本使用

  • 1,安装插件模块

      $ yarn add rollup-plugin-json --dev
    
  • 2,在 rollup.config.js 中,进行配置

    配置代码如下(rollup.config.js):

      // 默认导出插件函数
      import json from 'rollup-plugin-json'
      export default {
          // plugins 属性,存放的是插件函数的调用结果
          plugins: [
              json()
          ]
      }
    
rollup-plugin-node-resolve

rollup-plugin-node-resolve,用来加载 NPM 模块。也就是说,可以在代码中,直接使用模块名称导入模块。

基本使用

  • 1,安装插件模块

      $ yarn add rollup-plugin-node-resolve --dev
    
  • 2,在 rollup.config.js 中,进行配置

    配置代码如下(rollup.config.js):

      import resolve from 'rollup-plugin-node-resolve'
      export default {
        plugins: [
          resolve()
        ]
      }
    
rollup-plugin-commonjs

rollup-plugin-commonjs,用来加载 CommonJS 模块。

基本使用

  • 1,安装插件模块

      $ yarn add rollup-plugin-commonjs --dev
    
  • 2,在 rollup.config.js 中,进行配置

    配置代码如下(rollup.config.js):

      import commonjs from 'rollup-plugin-commonjs'
      export default {
          plugins: [
              commonjs()
          ]
      }
    

Code Splitting

动态导入

Code Splitting,代码分包/代码分割。由于 Rollup 遵循 ES Modules,因此他可以使用 ES Modules 的动态导入实现。

基本使用

  • 1,在 JS 文件中,使用 动态导入 进行模块的加载

    代码示例如下(index.js):

      // 返回 Promise 对象
      import('./logger').then(({ log }) => {
          log('code splitting~')
      })
    
  • 2,修改 rollup.config.js 配置文件

    配置代码如下(rollup.config.js):

      export default {
          input: 'src/index.js',
          output: {
              // file: 'dist/bundle.js', // 只能指定一个文件
              // format: 'iife'
              dir: 'dist',  // 需要输出多个文件
         // iife 自执行函数,会把所有的模块放到同一个函数中,没有引导代码,无法实现代码拆分。
              format: 'amd' // 浏览器环境中,遵循 AMD 标准
          }
      }
    
多入口打包

类似于 webpack 中的多入口打包,配置多个入口文件,生成多个输出文件,并在打包过程中,自动将公共部分单独提取。

基本使用

  • 1,在 rollup.config.js 中,配置多个入口文件,类似 webpack

    配置代码如下(rollup.config.js):

      export default {
          // input: ['src/index.js', 'src/album.js'],
          input: { 
              foo: 'src/index.js',
              bar: 'src/album.js'
          },
          // 多入口打包,会提取公共模块,会实行代码拆分
          output: {
              dir: 'dist',
              format: 'amd'
          }
      }
    
  • 2,手动创建 index.html ,在其中使用打包后的 JS 文件

    代码示例如下(index.html):

      <body>
          <!-- AMD 标准格式的输出 bundle 不能直接引用 -->
          <!-- <script src="foo.js"></script> -->
          <!-- 需要 Require.js 这样的库 -->
          <script src="https://unpkg.com/requirejs@2.3.6/require.js" data-main="foo.js"></script>
      </body>
    

优点缺点

优点

  • 1,输出结果更加扁平;
  • 2,自动移除未引用代码;
  • 3,打包结果依然完全可读。

缺点

  • 1,加载非 ESM 的第三方模块比较复杂;
  • 2,模块最终都被打包到一个函数中,无法实现 HMR;
  • 3,浏览器环境中,代码拆分功能依赖 AMD 库。

Parcel

Parcel,是一款完全 零配置 的前端应用打包器。相对于 Webpack 来说,构建速度要快,因为他的内部采用多进程。

基本使用

Parcel 官方建议,使用 HTML 文件作为打包入口。

  • 1,安装 Parcel 打包器

      $ yarn add parcel-bundler --dev
    
  • 2,在 src 目录下,新建 index.html 文件,此文件将作为打包的入口文件

    代码示例如下(index.html):

      <body>
          <script src="main.js"></script>
      </body>
    
  • 3,在 src 目录下,新建 main.js 和 foo.js 文件,作为测试文件

    代码示例如下(main.js):

      import foo from './foo.js'
      foo.bar()
    

    代码示例如下(foo.js):

      export default {
          bar: () => {
              console.log('hello parcel~');
          }
      }
    
  • 4,开发环境,打包命令,将 HTML 文件作为打包入口,同时开启 Web 服务器

      $ yarn parcel src/index.html
    
  • 5,生成环境,打包命令

      $ yarn parcel build src/index.html
    

基本功能

在运行打包命令后,不仅会生成对应的打包文件,还会自动开启一个 web 服务器。

启动的web服务器,会自动监听代码的变化,从而实现自动编译,浏览器自动刷新。也就是说,不管做什么,都不用担心配置等,Parcel 会自动去实现。

测试用例

  • 1,HMR(自动热替换),在 main.js 编写 热替换 逻辑

    代码示例如下(main.js):

      if (module.hot) {
          // accept() 只接收一个回调函数参数,
          // 作用:当前模块更新,或者其所依赖的所有模块更新过后,会自动执行
          module.hot.accept(() => {
              // 热替换逻辑
          })
      }
    
  • 2,自动安装依赖

    代码示例如下(main.js):

      import $ from 'jquery'
      $(document.body).append('<h1>Hello Parcel</h1>')
    

    通过测试,可以知道,在引入 jquery 之后,会自动下载 jquery 的依赖包。

  • 3,动态导入

    代码示例如下(main.js):

      import('jquery').then($ => {
          $(document.body).append('<h1>Hello Parcel</h1>')
      })
    
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值