浅析webpack—第二话(进阶篇)

大家好,我是前端小张同学,这是webpack内容第二次更新了,接下来我会将webpack一步一步深入,并分享给大家。

我正在参与四月更文挑战,一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情

浅析webpack—第二话(进阶篇)

今天我们的主题是 ,webpack打包的进阶篇

上篇文章中我们讲到了,webpack的一些基础配置,配置loader,配置打包入出口文件配置sourceMap等等,基础知识,那我们这篇文章就介绍一下 , webpack的进阶篇 , 什么是进阶呢?请往下看。

1 :进阶篇

进阶篇 ,包含了webpack的,多入口打包,根据对应的环境进行打包,对.html文件中的静态资源的处理,如何注入第三方库资源到全局,等等,接下来跟我一起探索吧。

1.1 知识点目录

  1. webpack插件介绍

  2. HTML中引入静态资源的处理

  3. 第三方库作为全局变量进行配置

  4. 根据环境进行打包配置

  5. 跨域&& 跨域的解决方案和原理

  6. 配置webpack的HMR

    首先声明观点,我的文章是纯技术文章,不参杂任何的水分,每一个字和一行代码都是作者亲自敲出来的,如果你觉得我的文章写的有疑问,可以在评论区进行留言,欢迎讨论。

2 : webpack插件介绍

问题 : 在学习之前我们需要先弄清除一个问题 , 什么是插件 ? 它作用是什么 ?

顾名思义 : 插件就是 东西本身不含有的东西,由外部接入的,称为插件,能够帮助程序或者电脑完成某些事情的,称为插件。

2.1 webpack plugin

2.1.1 CleanWebpackPlugin

功能:自动清除dist目录下所有内容,并将最新打包的结果输出到dist目录下,在webpack打包完成时触发。

配置

1 : 下载 clean-webpack-plugin

yarn add clean-webpack-plugin --save-dev 或者 
npm i --save-dev clean-webpack-plugin

在webpack.donfig.js 配置文件中,plugins节点下,进行配置,当然它还有配置选项,具体可以看npm上
cleanWebpackplugin选项配置

// 将 该插件实例出来即可,执行打包则会自动清除 dist 目录
import CleanWebpackPlugin = require('clean-webpack-plugin')
plugins : [
 new CleanWebpackPlugin() 
]

2.1.2 HtmlWebpackPlugin

功能:将项目根目录下的index.html复制到,dist 目录下,并自动将打包完的js文件引入。

配置

// webpack5 请执行 
 npm i --save-dev html-webpack-plugin 或
 yarn add html-webpack-plugin --dev
 
// webpack 4 则需要 @4 指定版本 
 npm i --save-dev html-webpack-plugin@4 或
 yarn add html-webpack-plugin@4 --dev

依然是在 plugins 节点下 进行 创建,更多内容请看 webpack 官网 –>htmlWebpackPlugin

import htmlWebpackPlugin = require('html-webpack-plugin')
plugins : [
 new htmlWebpackPlugin({
      filename : 'index.html', // 生成的html文件名称
      template : './index.html' // 以哪一个模板作为操作的对象
 }) 
]

2.1.3 copyWebpackPlugin

功能:将指定的资源,复制到指定的目录下去,通常将静态资源图片复制到dist/assets目录下。

配置

 npm i --save-dev copy-webpack-plugin 或
 yarn add copy-webpack-plugin --D
import CopyWebpackPlugin = require('copy-webpack-plugin')
plugins:[
    new CopyWebpackPlugin({
        // 注意 这里的配置 , 考虑到 复制的资源不仅仅只有一个 ,所以 patterns 可以传入多个配置对象
        patterns : [
            {
               // 我们设置资源起点 设置绝对路径或者相对路径
              from : path.resolve(__dirname , 'assets'),
              // 复制完成后 输出到哪里 assets 就代表 dist/assets
              // 想想为什么输出到 dist/assets 目录下,文章末尾给出答案
              to : 'assets'
            }
        ]
    })
]

2.1.4 BannerPlugin

功能:在打包完的文件头部生成注释,注意这是一个webpack内置插件,更多配置项请看官网 bannerPlugin

官网解释 : 为每个 chunk 文件头部添加 banner。

配置

const webpack = require('webpack')
    plugins : [
         new webpack.BannerPlugin({
          banner : '测试bannerPlugin插件', // 添加注释的内容
          entryOnly : true  // 如果值为 true,将只在入口 chunks 文件中添加
            })
    ]

ok,到此就介绍这四个插件,后面还有更多的好用插件,我会继续更新,接下来继续往下看。

3. HTML中引入静态资源的处理

需求: 我们经常用 Vuereact 开发,我们都知道 单页面应用,
但因为功能变更,我需要在项目的index.html 引入一些静态图片进行展示(注意这些资源是不会参与打包的),那我们上线后,在index.html资源消失了,怎么办?

接下来 html-withimg-loader 就为大家解决这个问题。

我们可以借助一个 loader ,当然这个插件已经很老了,在这里只是提一下,最好的解决方案还是通过将静态资源 copy到dist/asstes 下直接引用

npm install html-withimg-loader --save

你就可以 直接 在html中以相对路径进行引用即可,这个loader会帮助你加载。

补充一点

在webpack 5 中,官方已经内置了静态资源模块,可以无需使用 Url-loader进行静态资源处理,配置写在下方了,具体详情可见 webpack官网(静态资源处理)

配置

module : {
    rules : [
        {
         test: /.(htm|html)$/i,
         use: ['html-withimg-loader']
        },
        // webpack 5 中静态资源处理 发送一个单独的文件并导出 URL
        {
         test : /\.(png|jpeg|jpg|bmp)/,
         type : 'asset/resource'
        },
    ]
}

4: 第三方库作为全局变量进行配置

如何把 第三方库,比如 jquerylodash ,这些库 引入到全局,作为一个全局属性进行使用呢?

我们用Vue的开发者应该都知道,通常我们要用一些第三方库,想在所有的Vue组件中都用到 是怎么做的?大家可以回忆一下,是不是,将该库导入创建实例,并把实例挂载到Vue原型身上,然后通过 this 访问 ?

import Vue from 'vue'
impo axios from 'axios'
// 在Vue2 中我们可能会这么做
vue.prototype.$ajax = axios 

但是 其实 webpack 也能帮你解决这个问题,借助 expose-loader

老规矩,先下包。

npm install expose-loader --save-dev //or
yarn add -D expose-loader // or
pnpm add -D expose-loader // and 

// 下载jquery 方便测试 
yarn add jquery --save-dev 

配置

module : {
    rules : [
        {
         // [`require.resolve`(https://nodejs.org/api/modules.html#modules_require_resolve_request_options)
         
// 调用是一个 Node.js
//函数(和 webpack 进程中的 `require.resolve` 无关)。 `require.resolve` 给出模块的绝对路径         
            test : require.resove('jquery'),
            loader : 'expose-loader',
            options : {
                // 映射关系 $ 对应的jquery 构造函数 
                exposes : ['$','jquery']
            }
        }
    ]

}

配置完成后
我们就可以在入口文件 index.js文件中进行导入

index.js

import $ from 'jquery'
import 'useJqueryTest.js'
console.log('expose-loader' , $);
console.log('window-expose-loader' , window.$);

useJqueryTest.js

// 测试 是否能访问$ 暴露了一个 $属性在 window 身上
(function() {
  console.log("子模块的jqurey" , $);
})()

分模块进行打印 $ 属性 确认是否挂载到 window 身上, 如果 子模块能访问证明 已经挂载到 window 身上

浅析webpack—第二话(进阶篇)

在这里我们可以看到 window 身上已经有了 $ 属性,说明该 expose-loader 已经生效了
浅析webpack—第二话(进阶篇)

5. webpack 根据环境进行打包配置

首先,在学习这个之前,我们先要理解,为什么要区分环境进行打包? 它的作用在哪里?能给我们带来什么用处,怎么学习它。

5.1 题外话

插句题外话,其实我们学习任何东西之前,我们一定要 三问自己,搞清楚这三点,你的学习起来会很轻松,而不是强制性贯通到你的脑海里。

  1. 第一问 —> 这个东西是什么?
  2. 第二问 —> 它有什么作用
  3. 第三问 —> 我们应该怎么学习它

5.2 回到正题

5.2.1 为什么 我们要对webpack做环境区分呢?

因为我们开发时候与上线是某些操作是不一样的,如果开发环境生产环境 都是一样的操作,那我们就不必要上线了,我们开发时讲究的是 准确 , 高效 , 快捷 , 生产讲究的是 性能,速度,可访问性等。

5.2.2 它的作用在哪里

它可以 帮助我们在 不同的环境 进行不同的操作 , 比如我们在开发环境中需要开启 sourceMap,更快的映射到准确的打印输出,比如我们在开发环境中访问的 192.168.0.1 服务器 , 但生产上 却访问的是 192.168.1.10等等,在这种情况下我们就需要根据环境 来编写不同的配置

5.2.3 能给我们带来什么用处?

能够帮助我们在,不同的环境打包不同的配置,产出的结果也是不一样的,将我们编写的代码进行依赖分析css压缩treeShaking scpoed-hoisting 等等。

5.3 如何区分环境?

在这里提供 两种方式

方式一 :

我们可以通过 在项目中 .env.developomentenv.production 文件中 配置一个属性 , NODE_ENV.env.developoment文件中 NODE_ENV 属性值 为 develpoment , 反之 在 env.production NODE_ENV 属性值 为 production

例如 :

  entry : './src/index.js',
  output :  {
    path : path.join(__dirname , './dist/'),
    // process.env.NODE_ENV 自动读取配置文件中的值 
    filename : process.env.NODE_ENV === 'developoment' ?  'main.js' : 'index.js'
  },

方式二 :

通过运行不同的脚本指令执行不同的配置文件的方式(也是我接下来要讲的方式)

  1. 在根目录下创建 buildConfig 文件夹
  2. buildConfig 下创建 webpack.base.jswebpack.dev.jswebpack.prdo.js 文件
  3. 将已有的webpack.connfig.js文件中的内容服务到三个文件中去
  4. 根据环境进行区分
  5. 修改package.json文件中的 script

1 :创建文件

浅析webpack—第二话(进阶篇)

2:区分配置(最关键的一步)

我们需要想想 , 哪些配置是 开发和生产环境都需要的,哪些是 开发时配置,哪些是生产配置?

教大家一个小技巧

如果 你发现 一个配置 生产环境开发环境 都需要 那这个配置就是 公共配置没找出它们的交集

webpack.base.js

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin') // 开发也要生成html文件到dist 生产也需要 (公共配置)
const { CleanWebpackPlugin } = require('clean-webpack-plugin') // 开发也要,生产也需要 (公共配置)
const CpoyWebpackPlugin = require('copy-webpack-plugin') // 开发也要生产也要 (公共配置)
const webpack = require('webpack') // webpack核心包 根据情况而定,这里我们暂时先引用 
module.exports = {
// 打包入口配置 开发和生产都需要打包并配置入口(公共配置)
  entry : './src/index.js',
// 打包完成输出文件目录  开发和生产我们这里都 输出到 dist (公共配置)
  output :  {
    path : path.join(__dirname , './dist/'),
    filename : 'main.js'
  },
  //开发 时 主机时 localhost ,生产则应该是 0.0.0.0 (允许所有客户端访问) 所有这个不应该放在 webpack.base.js 中 
  /* devServer : {
    // static : './dist/index.html',
    open : true,
    host : 'localhost'
    port : 8081
  },*/
  /*
  module 选项 
  集成了 css-loader 解析css 模块 
  集成了 less
  集成了 静态资源处理
  集成了 babel-loader 对 js 降级处理 
  集成了 html-withimg-loader
  集成了 expose-loader 
  这些都是 开发环境 和 生产环境都需要的 
  */
  module : {
    rules : [
      {
        test : /\.css$/,
        use : ['style-loader' , 'css-loader']
      },
      {
        test : /\.less$/,
        use : ['style-loader' , 'css-loader' , 'less-loader']
      },
      // 配置 file-loader 解析文件 
      {
        test : /\.(png|jpeg|jpg|bmp)/,
        type : 'asset/resource'
      },
      { 
        test : /\.js$/,
        use : {
          loader : 'babel-loader',

        },
        exclude : /node_modules/
        
      },
      {
        test : /\.(hmt|html)$/,
        use : {
          loader : 'html-withimg-loader'
        }
      },
      {
        test: require.resolve('jquery'),
        loader : 'expose-loader',
        options : {
          exposes : ['$','jquery']
        }
      }
    ]
  },
  /*
  插件 
  HtmlWebpackPlugin 前面说过开发时和生产都需要生成 html 到dist(公共配置)
  CleanWebpackPlugin 开发时和生产时打包都需要自动清除旧的 dist 目录下的所有文件(公共配置)
  CpoyWebpackPlugin 看情况 如果你需要复制静态资源 那就用 ,不需要则 不用就行 (自行决定)
  BannerPlugin 看情况 如果你需要生成注释 那就用 ,不需要则 不用就行 (自行决定)
  */
  plugins : [
    new HtmlWebpackPlugin({
      filename : 'index.html',
      template : './index.html'
    }),
    new CleanWebpackPlugin(), 
    new CpoyWebpackPlugin ({
      patterns : [
        {
          from : './assets',
          to : 'assets'
        }
      ]
    }),
    new webpack.BannerPlugin({
      banner : '测试bannerPlugin插件',
      entryOnly : true
    })
  ],
  
  // mode 这个肯定不是 公共配置 开发时 是development 生产时 production 
  //  mode : 'production',
  // 开启 sourceMap 开发时 需要开启 , 生产 不需要 所以这也不是公共配置 
  // devtool : 'cheap-module-source-map'
}

所以 最后生成的 文件应该是

webpack.config.js

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const CpoyWebpackPlugin = require('copy-webpack-plugin')
const webpack = require('webpack')
module.exports = {
  entry : './src/index.js',
  output :  {
    path : path.join(__dirname , './dist/'),
    filename : 'main.js'
  },
  module : {
    rules : [
      {
        test : /\.css$/,
        use : ['style-loader' , 'css-loader']
      },
      {
        test : /\.less$/,
        use : ['style-loader' , 'css-loader' , 'less-loader']
      },
      // 配置 file-loader 解析文件 
      {
        test : /\.(png|jpeg|jpg|bmp)/,
        type : 'asset/resource'
      },
      { 
        test : /\.js$/,
        use : {
          loader : 'babel-loader',

        },
        exclude : /node_modules/
        
      },
      {
        test : /\.(hmt|html)$/,
        use : {
          loader : 'html-withimg-loader'
        }
      },
      {
        test: require.resolve('jquery'),
        loader : 'expose-loader',
        options : {
          exposes : ['$','jquery']
        }
      }
    ]
  },
  plugins : [
    new HtmlWebpackPlugin({
      filename : 'index.html',
      template : './index.html'
    }),
    new CleanWebpackPlugin(),
    new CpoyWebpackPlugin ({
      patterns : [
        {
          from : './assets',
          to : 'assets'
        }
      ]
    }),
    new webpack.BannerPlugin({
      banner : '测试bannerPlugin插件',
      entryOnly : true
    })
  ],
}

webpack.dev.js 模拟开发时 的配置

module.exports = {
  devServer : {
    host : 'localhost',
    port : 8080 ,
    open : true,
  },
  mode : 'development',
  devtool : 'cheap-module-source-map'
}

webpack.prod.js 模拟 生产时的配置

module.exports = {
  devServer : {
    host : '0.0.0.0',
    port : '8001',
    open : true,
  },
  mode : 'production',
}

3.合并配置

我们可以借助 webpack-merge 这个包 进行 合并操作,将公共的配置与 开发环境 或生产环境配置进行合并。

yarn add webpack-merge --save-dev 或者

npm install webpack-merge --save-dev 

配置

webpack-merge 这个包 解构出一个 merge 函数

合并 基础配置 和 生产时的配置

const { merge } = require('webpack-merge')
const baseConfig = require('./webpack.base.js')
// merge 函数接收 两个配置对象 , 最后合并成一个,如果 开发环境中也有与基础配置相同的属性 ,webpack-merge 会自动合并 并不会覆盖
module.exports = merge(baseConfig ,  {
  devServer : {
    host : '0.0.0.0',
    port : '8001',
    open : true,
  },
  mode : 'production',
})

4.配置 package.json

我们通过 –config 来指定 运行哪个配置文件 例如 当我们终端输入 npm run build webpack --config ./config/webpack.prod.js 就是运行我们刚刚合并的生产配置基础配置 , 反之 当我们终端输入 npm run dev , webpack-dev-server --config ./config/webpack.dev.js 则运行 合并的 开发配置基础配置

 "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --config ./config/webpack.prod.js",
    "watch": "webpack --watch",
    "dev": "webpack-dev-server --config ./config/webpack.dev.js"
  },

到这里 ,恭喜你 ,你已经学会了 区分环境 进行打包了。

别急着高兴,想必做到这里 你已经遇到了一个问题,我们打包完的dist 目录好像生成的路径不对,dist文件夹 怎么生成到 config 目录下了 , 难道不应该是 根目录下吗?

浅析webpack—第二话(进阶篇)

大家还记得,我们之前基础配置中的 output配置选项吗 ?,到这里想必大家已经明白了 ,当前目录就是 /webpack-advanced/config, 那 ./dist/自然也是在 config 目录下生成了,那我们应该怎么做呢?

我们只需要向上翻一层 就可以了

path.join
方法使用特定于平台的分隔符作为定界符将所有给定的 path 片段连接在一起,然后规范化生成的路径。详情见
node-join

path.join(__dirname , '../', './dist/'),
  output :  {
    // 以当前目录为基准合并出一个 绝对路径 
    path : path.join(__dirname , '../', './dist/'),
    filename : 'main.js'
  },

恭喜你,到这里你就学会了 webpack 区分环境 进行 打包配置了。

6. 跨域&& 跨域的解决方案和原理

什么是跨域,怎么解决?这个问题 ,不知道什么是跨域的掘友希望你么可以自己去了解这个东西,我这就不细说了。

简单理解 客户端服务端 协议 端口 主机 其中有一个不一致则就称为 跨域 ,主要原因是浏览器的同源策略

如何解决呢 ?

我们可以在 webpack 提供的 devServer 这个选项中进行配置 ..

配置

module.exports = {
devServer : {
    host : 'localhost',
    port : 8080 ,
    open : true,
    proxy : {
    // 匹配请求路径 以 /api 开头的 代理到 http://localhost:3000 并且 将 /api 重写成 '' 空字符
      "^/api" : {
        target : 'http://localhost:3000',
        pathRewrite: { '^/api': '' },
      }
    }
  },
}

7.webpack的 HMR

什么是 HMR(Hot Module Replacement) , 我的理解是 热更新替换 ,就是 当你的本地 与webpack开启的服务上的代码 不一致时,HMR将会通过长链接以打补丁的方式 进行替换更新从而使页面内容刷新。

module模块选项中 提供了 hot.accept 方法 ,它能够允许你将指定的模块进行 热更新,当然 官方告诉我们,从webpack-dev-server v4.0.0 开始,热模块替换是默认开启的。所以这里我们简单的对 热更新做一个了解。

原理 : 热更新的原理是 webpack 监测 本地代码 与 webpack开启的服务上的代码 不一致时,HMR将会通过长链接以打补丁的方式 进行替换更新从而使页面内容刷新。

如果我的理解有偏差,可以在评论区中进行参与评论解答,希望能够与大家一起学习

    module.hot.accept('./testHRM.JS' , function () {
    // 当我们 testHRM.js文件更新了 , 则会回调该方法 ,那我们重新引入 一下  则可以获取最新的内容。
     const res = require('./testHRM.JS')
     console.log(res);
    })

结束啦。

到这里 webpack 进阶篇 你已经学会了很多,结合第一篇webpack基础,相信你对webpack 也有了一定的了解 ,如果你觉得对你有帮助,请帮忙点个star ,谢谢 ,我是前端小张同学

原文链接:https://juejin.cn/post/7223358128330096701 作者:前端小张同学

(0)
上一篇 2023年4月19日 上午10:00
下一篇 2023年4月19日 上午10:10

相关推荐

发表回复

登录后才能评论