webpack通俗易懂(三)

webpack通俗易懂(三)

  • 1,管理资源(承接上篇文章)
  • 2,使用 babel-loader
  • 3, 代码分离
  • 4,缓存
  • 5,拆分开发环境和生产环境配置

1,管理资源(承接上篇文章)

1.1, 加载 fonts 字体

1,修改配置文件:

module: {
  rules: [
    {
      test: /\.(woff|woff2|eot|ttf|otf)$/i,
      type: 'asset/resource',
    }
  ] 
}

2,在项目中添加一些字体文件

webpack通俗易懂(三)_第1张图片
3,在style.less里加载字体

// style.less
// 使用 css3 的 font-face 定义字体
@font-face {
  font-family: iconfont; // 我们定义的字体的名字
  src: url('./fonts/iconfont.ttf') format('truetype'); // 加载资源 format格式
}
// 在项目中使用这个字体文件
// 定义一个 icon 类
.icon {
  font-family: 'iconfont';
  font-size: 30px;
  color: pink;
}
// 然后就可以在页面里使用了

4, 使用字体

const span = document.createElement('span')
span.classList.add('icon')
span.innerHTML = ''
document.body.appendChild(span)

执行编译npx webpack-dev-server

webpack通俗易懂(三)_第2张图片

1.2, 加载数据

此外,可以加载的有用资源还有数据,如 JSON 文件,CSV、TSV 和 XML。类似于 NodeJS,JSON 支持实际上是内置的,也就是说
默认将正常运行。要导入 CSV、TSV 和 XML,我们可以使用 csv-loader 和 xml-loader

首先安装:

npm install --save-dev csv-loader xml-loader

添加配置文件:

module: {
  rules: [
    {
      test: /\.(csv|tsv)$/i,
      use: ['csv-loader'],
    }, {
      test: /\.xml$/i,
      use: ['xml-loader'],
    }
  ]
}
// data.xml


  xiaowang
  Jerry
  heading
  Hello, webpack ~ 

// data.csv
触发频率不稳定,受很多因素影响。比如当我们的浏览器切换tab后,之前tab注册的requestIdleCallback触发的频率会变得很低
基于以上原因,React实现了功能更完备的requestIdleCallbackpolyfill,这就是Scheduler。除了在空闲时触发回调的功能外,Scheduler还提供了多种调度优先级供任务设置。

入口文件里加载数据模块,并在控制台上打印导出内容

// index.js
import DataXml from './data.xml'
import DataCsv from './data.csv'
console.log(DataXml)
console.log(DataCsv)

webpack通俗易懂(三)_第3张图片

1.3, 自定义 JSON 模块 parser(解析器)

通过使用自定义 parser 替代特定的 webpack loader,可以将任何 toml 、 yaml 或 json5 文件作为 JSON 模块导入

在src下创建文件:data.toml 、data.yaml、data.json5

// data.toml
title = 'TOML'
[owner]
name = 'xiaowang'
age = 18

// data.yaml
title: 'Yaml'
owner:
name: 'xiaowang'
age: 18

// data.json5
{
  title: 'JSON5',
  owner: {
    name: 'xiaowang',
    age: 18
  }
}

首先安装 toml、yamljs 和 json5 的 packages:

npm install toml yamljs json5 --save-dev

并配置webpack.config.js

const toml = require('toml')
const yaml = require('yamljs')
const json5 = require('json5')

rules: [
  {
    test: /\.toml$/i,
    type: 'json',
    parser: {
      parse: toml.parse,
    },
  }, 
  {
    test: /\.yaml$/i,
    type: 'json',
    parser: {
      parse: yaml.parse,
    },
  }, 
  {
    test: /\.json5$/i,
    type: 'json',
    parser: {
      parse: json5.parse,
    },
  }
]

在主文件中引入模块,并打印:

// index.js
import toml from './data.toml'
import yaml from './data.yaml'
import json5 from './data.json5'
console.log(toml)
console.log(yaml)
console.log(json5)

2, 使用 babel-loader

应用 less-loader 编译过 less 文件,应用 xml-loader 编译过 xml 文件,那 js 文件需要编译吗

// 新建test.js
function getString() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('Hello world~')
    }, 2000)
  })
}

async function helloWorld() {
  let string = await getString()
  console.log('test.js =====>', string)
}
// 导出函数模块
export default helloWorld

// index.js
import testHelloWorld from './test'
testHelloWorld()

npx webpack执行编译,启动服务: npx webpack serve

浏览器会 2s 后输出test.js =====> Hello world~, 但是如果浏览器版本过低,就很难保证代码正常运行了

2.1,为什么需要 babel-loader

webpack 自身可以自动加载JS文件,就像加载JSON文件一样,无需任何 loader。可是,加载的JS文件会原样输出,即使JS文件里包含ES6+的代码,也不会做任何的转化。那么我们就需要Babel帮忙了,Babel 是一个 JavaScript 编译器,可以将 ES6+ 转化成 ES5。在Webpack里使用Babel,需要使用 babel-loader

2.2,使用 babel-loader

npm install -D babel-loader @babel/core @babel/preset-env
  • babel-loader: 在webpack里应用babel解析ES6的桥梁
  • @babel/core: babel核心模块
  • @babel/preset-env: babel预设,一组babel插件的集合

在 webpack 配置中,需要将 babel-loader 添加到 module 列表中:

rules: [
  {
    test: /\.js$/,
    exclude: /node_modules/,
    use: {
      loader: 'babel-loader',
      options: {
        presets: ['@babel/preset-env']
      }
    } 
  }
]

编译后可以看出,async/await 的 ES6 语法被 babel 编译了。

webpack通俗易懂(三)_第4张图片

2.3,regeneratorRuntime 插件

此时执行编译,在浏览器里打开项目发现报了一个错误:

regeneratorRuntime 是webpack打包生成的全局辅助函数,由babel生成,用于兼 容async/await的语法

# 这个包中包含了regeneratorRuntime,运行时需要 
npm install --save @babel/runtime
# 这个插件会在需要regeneratorRuntime的地方自动require导包,编译时需要 
npm install --save-dev @babel/plugin-transform-runtime
# 更多参考这里 https://babeljs.io/docs/en/babel-plugin-transform-runtime

修改配置文件:

rules: [
  {
    test: /\.js$/,
    exclude: /node_modules/,
    use: {
      loader: 'babel-loader',
      options: {
        presets: ['@babel/preset-env'],
        plugins: [['@babel/plugin-transform-runtime']]
      } 
    }
  }
]

npx webpack-dev-server启动服务:

webpack通俗易懂(三)_第5张图片

3,代码分离

代码分离是 webpack 中最引人注目的特性之一。此特性能够把代码分离到不同的 bundle 中,然后可以按需加载或并行加载这些文件。代码分离可以用于获取更小的 bundle,以及控制资源加载优先级,如果使用合理,会极大影响加载时间

常用的代码分离方法有三种:

  • 入口起点:使用 entry 配置手动地分离代码。
  • 防止重复:使用 Entry dependencies 或者 SplitChunksPlugin 去重和分离 chunk。
  • 动态导入:通过模块的内联函数调用来分离代码。

3.1,入口起点

这是迄今为止最简单直观的分离代码的方式。不过,这种方式手动配置较多,并有一些隐患,我们将会解决这些问题。先来看看如何从 main bundle 中分离 another module(另一个模块):

// 新建another-module.js文件
import _ from 'lodash';
console.log(_.join(['apple', 'orange', 'banana']))
// 安装 lodash
npm install lodash --save-dev

修改配置文件:

entry: { // 入口
  index: './src/index.js',
  another: './src/another-module.js'
},
output: {  // 出口
  filename: '[name].bundle.js', // 文件名
  // ...
},

npx webpack, npx webpack-dev-server:

webpack通俗易懂(三)_第6张图片

webpack通俗易懂(三)_第7张图片

// 再修改下 index.js
import _ from 'lodash'
console.log('index.js===>', _.join(['index', 'module'], ''))

执行编译npx webpack后,我们可以发现index.bundle.js文件变大了,这种方式存在一些隐患:

  • 如果入口 chunk 之间包含一些重复的模块,那些重复模块都会被引入到各个 bundle 中
  • 这种方法不够灵活,并且不能动态地将核心应用程序逻辑中的代码拆分出来

以上两点中,第一点对我们的示例来说无疑是个问题,因为之前我们在
./src/index.js 中也引入过 lodash ,这样就在两个 bundle 中造成重复引用

3.2, 防止重复

  • 入口依赖

    配置 dependOn option 选项,这样可以在多个 chunk 之间共享模块:

    entry: {
      index: {
        imports: './src/index.js',
        dependOn: 'shared'
      },
      another: {
        import: './src/another-module.js',
        dependOn: 'shared'
      },
      shared: 'lodash'
    },
    

执行编译npx webpack发现:
index.bundle.js 与 another.bundle.js 共享的模块 lodash.js 被打包到一 个单独的文件 shared.bundle.js 中

  • SplitChunksPlugin

SplitChunksPlugin 插件可以将公共的依赖模块提取到已有的入口 chunk 中,或者提取到一个新生成的 chunk。让我们使用这个插件,将之前的示例中重复的 lodash 模块去除:

entry: {
  // ....
},
optimization: {
  splitChunks: {
    chunks: 'all'
  }
}

执行编译npx webpack可以看出:

使用 optimization.splitChunks 配置选项后,index.bundle.js 和 another.bundle.js 中已经移除了重复的依赖模块。需要注意的是,插件将 lodash 分离到单独的 chunk,并且将其从 main bundle 中移除,减轻了大小

3.3,动态导入

涉及到动态代码拆分时,webpack 提供了两个类似的技术。第一种使用符合 ECMAScript 提案的 import() 语法来实现动态导入(推荐使用)。第二种,是 webpack 的遗留功能,使用 webpack 特定的 require.ensure

// 第一种
// 创建 async-module.js 文件:
function getComponent() {
  return import('lodash')
  .then(({
    default: _
  }) => {
    const element = document.createElement('div')
    element.innerHTML = _.join(['async', 'module'], '')
    return element
  })
  .catch((error) => 'error')
}
getComponent().then((component) => {
  document.body.appendChild(component)
})

// 在入口文件导入:
import './async-module'

执行编译npx webpack后可以看出,除了公共的 lodash 代码被单独打包到一个文件外,还生成了一个vendors-node_modules_babel_runtime_regenerator_index_js-node_modules_babel_runtime_helpers_es-58772e.bundle.js文件。

webpack通俗易懂(三)_第8张图片

3.4,懒加载

懒加载或者按需加载,是一种很好的优化网页或应用的方式。这种方式实际上是先把
我们的代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立即引用
或即将引用另外一些新的代码块。这样加快了应用的初始加载速度,减轻了它的总体
体积,因为某些代码块可能永远不会被加载。

// 创建一个 math.js 文件,在主页面中通过点击按钮调用其中的函数:
export const add = (x, y) => {
    return x + y
}
export const minus = (x, y) => {
    return x - y
}

// index.js
const button = document.createElement('button') button.textContent = '点击执行加法运算' button.addEventListener('click', () => {
  import(/* webpackChunkName: 'math' */ './math.js').then(({ add
}) => {
    console.log(add(4, 5))
  })
})
document.body.appendChild(button)

// 以上代码中的注释称为 webpack 魔法注释: webpackChunkName: 'math' , 告诉webpack打包生成的文件名为 math

启动服务,我们可以看到第一次加载完页面,math.bundle.js 不会加载,当点击按钮后,才加载 math.bundle.js 文件

3.5, 预获取/预加载模块

Webpack v4.6.0+ 增加了对预获取和预加载的支持。
在声明 import 时,使用下面这些内置指令,可以让 webpack 输出 “resource hint(资源提示)”,来告知浏览器:

  • prefetch(预获取):将来某些导航下可能需要的资源
  • preload(预加载):当前导航下可能需要资源

下面这个 prefetch 的简单示例中,编辑 index.js 文件:

const button = document.createElement('button') button.textContent = '点击执行加法运算' button.addEventListener('click', () => {
  import(/* webpackChunkName: 'math', webpackPrefetch: true */
'./math.js').then(({ add }) => {
    console.log(add(4, 5))
  })
})
document.body.appendChild(button)
// 添加第二句魔法注释:webpackPrefetch: true

告诉 webpack 执行预获取。这会生成
并追加到页面头部,指示着浏览器在闲置时间预取 math.js 文件

npx webpack-dev-server:
会发现,在还没有点击按钮时, math.bundle.js 就已经下载下来了。同时,在 index.html 里webpack自动添加了一句:

webpack通俗易懂(三)_第9张图片
点击按钮,会立即调用已经下载好的math.bundle.js文件中的add方法

webpack通俗易懂(三)_第10张图片

与 prefetch 指令相比,preload 指令有许多不同之处:

  • preload chunk 会在父 chunk 加载时,以并行方式开始加载。prefetch chunk 会在父 chunk 加载结束后开始加载。
  • preload chunk 具有中等优先级,并立即下载。prefetch chunk 在浏览器闲置 时下载。
  • preload chunk 会在父 chunk 中立即请求,用于当下时刻。prefetch chunk 会 用于未来的某个时刻。
  • 浏览器支持程度不同。
// 创建一个 print.js 文件:
export const print = () => {
  console.log('preload chunk.')
}

// index.js
const button2 = document.createElement('button') 
button2.textContent = '点击执行字符串打印' 
button2.addEventListener('click', () => {
  import(/* webpackChunkName: 'print', webpackPreload: true */
'./print.js').then(({ print }) => {
    print(40, 50)
  })
})
document.body.appendChild(button2)

启动服务,会发现print.bundle.js 未被下载,因为我们配置的是
webpackPreload , 是在父 chunk 加载时,以并行方式开始加载。点击按钮才加载的
模块不会事先加载的。

再修改下:

// index.js
import(/* webpackChunkName: 'print', webpackPrefetch: true */
'./print.js').then(({ print }) => {
    print(40, 50)
  })
// 推荐使用webpackPrefetch预获取,print.bundle.js会在网络空闲的时候把将来使用的代码加载出来,这样既不影响首屏的加载速度,又省去了将来模块加载的延迟。

也可以使用webpackPreload实现页面模块的并行下载

print.bundle.js 被加载下来,是和当前 index.bundle.js 并行加载的。

4,缓存

以上,我们使用 webpack 来打包我们的模块化后的应用程序,webpack 会生成一个可部署的 /dist 目录,然后把打包后的内容放置在此目录中。只要 /dist 目录中的内容部署到 server 上,client(通常是浏览器)就能够访问此 server 的网站及其资源。而最后一步获取资源是比较耗费时间的,这就是为什么浏览器使用一种名为缓存的技术。可以通过命中缓存,以降低网络流量,使网站加载速度更快,然而,如果我们在部署新版本时不更改资源的文件名,浏览器可能会认为它没有被更新,就会使用它的缓存版本。由于缓存的存在,当你需要获取新的代码时,就会显得很棘手。
本节通过必要的配置,以确保 webpack 编译生成的文件能够被客户端缓存,而在文件内容变化后,能够请求到新的文件。

4.1,输出文件的文件名

我们可以通过替换 output.filename 中的 substitutions 设置,来定义输出文件的 名称。webpack 提供了一种使用称为 substitution(可替换模板字符串) 的方式,通过带括号字符串来模板化文件名。其中, [contenthash] substitution 将根据资源内容创建出唯一hash。当资源内容发生变化时, [contenthash] 也会发生变化。

// 修改配置文件
module.exports = {
  output: {
    filename: '[name].[contenthash].js',
  },
};

webpack通俗易懂(三)_第11张图片

可以看到bundle 的名称是它内容(通过 hash)的映射。如果我们不做修改,然后再次运行构建,文件名会保持不变

4.2,缓存第三方库

将第三方库(library)(例如 lodash )提取到单独的 vendor chunk 文件中,是比较推荐的做法,因为它们很少像本地的源代码那样频繁修改。因此通过实现以上步骤,利用 client 的长效缓存机制,命中缓存来消除请求,并减少向 server 获取资源,同时还能保证 client 代码和 server 代码版本一致。 我们在
optimization.splitChunks 添加如下 cacheGroups 参数并构建:

optimization: {
  splitChunks: {
    cacheGroups: {
      vendor: {
        test: /[\\/]node_modules[\\/]/,
        name: 'vendors',
        chunks: 'all'
      }
    }
  }
}

npx webpack

webpack通俗易懂(三)_第12张图片

4.3, 将 js 文件放到一个文件夹中

目前,全部 js 文件都在 dist 文件夹根目录下,我们尝试把它们放到一个文件夹中,
这个其实也简单,修改配置文件:

output: {  // 出口
    filename: 'scripts/[name].[contenthash].js'
  },

webpack通俗易懂(三)_第13张图片

5, 拆分开发环境和生产环境配置

现在,我们只能手工的来调整 mode 选项,实现生产环境和开发环境的切换,且很多配置在生产环境和开发环境中存在不一致的情况,比如开发环境没有必要设置缓存,生产环境还需要设置公共路径等等

接下来我们看怎么拆分开发和生产环境,让打包更灵活

5.1,公共路径

publicPath 配置选项在各种场景中都非常有用。你可以通过它来指定应用程序中所
有资源的基础路径。

  • 基于环境设置
    在开发环境中,我们通常有一个 assets/ 文件夹,它与索引页面位于同一级。这没太大问题,但是,如果我们将所有静态资源托管至 CDN,然后想在生产环境中使用呢?
    想要解决这个问题,可以直接使用一个 environment variable(环境变量)。假设我们有一个变量 ASSET_PATH:
// 尝试使用环境变量,否则使用根路径
const ASSET_PATH = process.env.ASSET_PATH || '/'
output: {  // 出口
  publicPath: ASSET_PATH,
},
plugins: [ // 可以配置多个插件
  // 这可以帮助我们在代码中安全地使用环境变量
  new webpack.DefinePlugin({
    'process.env.ASSET_PATH': JSON.stringify(ASSET_PATH)
  })
],
  • Automatic publicPath
    有可能我们事先不知道 publicPath 是什么,webpack 会自动根据 import.meta.url、document.currentScript、script.src 或者 self.location 变量来设置 publicPath。我们需要做的是将 output.publicPath
    设为 ‘auto’ :
output: {  // 出口
  publicPath: 'auto',
},

请注意在某些情况下不支持 document.currentScript ,例如:IE 浏览器,我们不得不引入一个 polyfill,例如 currentScript Polyfill

5.2,环境变量

想要消除 webpack.config.js 在开发环境和生产环境之间的差异,我们可能需要
环境变量(environment variable)

webpack 命令行环境配置的 --env 参数,可以允许我们传入任意数量的环境变量。而在 webpack.config.js 中可以访问到这些环境变量。例如,–env production 或
–env goal=local

npx webpack --env goal=local --env production --progress

对于我们的 webpack 配置,有一个必须要修改之处。通常,module.exports 指向配置对象。要使用 env 变量,你必须将 module.exports 转换成一个函数:

module.exports = (env) => {
  return {
    // 根据命令行参数 env 来设置不同环境的 mode
    mode: env.production ? 'production' : 'development', //...
  }
}

5.3, 拆分配置文件

目前,生产环境和开发环境使用的是一个配置文件,我们需要将这两个文件单独放到不同的配置文件中。如 webpack.config.dev.js (开发环境配置)和
webpack.config.prod.js (生产环境配置)。在项目根目录下创建一个配置文件夹 config 来存放他们。

webpack.config.dev.js 配置如下:

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const toml = require('toml')
const yaml = require('yaml')
const json5 = require('json5')
module.exports = {
  entry: {
    index: './src/index.js',
    another: './src/another-module.js'
  },
  output: {
    filename: 'scripts/[name].js',
    path: path.resolve(__dirname, '../dist'), // src目录下
    clean: true,
    assetModuleFilename: 'images/[contenthash][ext]'
  },
  mode: 'development',
  devtool: 'inline-source-map',
  plugins: [
    new HtmlWebpackPlugin({
      template: './index.html',
      filename: 'app.html',
      inject: 'body'
    }),
    new MiniCssExtractPlugin({
      filename: 'styles/[contenthash].css'
    })],
  devServer: {
    static: './dist'
  },
  module: {
    rules: [
      {
        test: /\.png$/,
        type: 'asset/resource',
        generator: {
          filename: 'images/[contenthash][ext]'
        }
      }, {
        test: /\.svg$/,
        type: 'asset/inline'
      },
      {
        test: /\.txt$/,
        type: 'asset/source'
      }, {
        test: /\.jpg$/,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 4 * 1024 * 1024
          }
        }
      },
      {
        test: /\.(css|less)$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
      },
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        type: 'asset/resource'
      }, {
        test: /\.(csv|tsv)$/,
        use: 'csv-loader'
      },
      {
        test: /\.xml$/,
        use: 'xml-loader'
      }, {
        test: /\.toml$/,
        type: 'json',
        parser: {
          parse: toml.parse
        }
      }, {
        test: /\.yaml$/,
        type: 'json',
        parser: {
          parse: yaml.parse
        }
      }, {
        test: /\.json5$/,
        type: 'json',
        parser: {
          parse: json5.parse
        }
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
            plugins: [
              [
              ]]
          }
        }
      }]
  },
  optimization: {
    splitChunks: {
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all'
        }
      }
    }
  }
}

webpack.config.prod.js配置如下:

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const toml = require('toml')
const yaml = require('yaml')
const json5 = require('json5')
module.exports = {
  entry: {
    index: './src/index.js',
    another: './src/another-module.js'
  },
  output: {
    filename: 'scripts/[name].[contenthash].js',
    // 打包的dist文件夹要放到上一层目录
    path: path.resolve(__dirname, '../dist'),
    clean: true,
    assetModuleFilename: 'images/[contenthash][ext]', publicPath: 'http://localhost:8080/'
  },
  mode: 'production',
  plugins: [
    new HtmlWebpackPlugin({
      template: './index.html',
      filename: 'app.html',
      inject: 'body'
    }),
    new MiniCssExtractPlugin({
      filename: 'styles/[contenthash].css'
    })],
  module: {
    rules: [
      {
        test: /\.png$/,
        type: 'asset/resource',
        generator: {
          filename: 'images/[contenthash][ext]'
        }
      }, {
        test: /\.svg$/,
        type: 'asset/inline'
      },
      {
        test: /\.txt$/,
        type: 'asset/source'
      }, {
        test: /\.jpg$/,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 4 * 1024 * 1024
          }
        }
      }, {
        test: /\.(css|less)$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
      }, {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        type: 'asset/resource'
      },
      {
        test: /\.(csv|tsv)$/,
        use: 'csv-loader'
      }, {
        test: /\.xml$/,
        use: 'xml-loader'
      },
      {
        test: /\.toml$/,
        type: 'json',
        parser: {
          parse: toml.parse
        }
      }, {
        test: /\.yaml$/,
        type: 'json',
        parser: {
          parse: yaml.parse
        }
      }, {
        test: /\.json5$/,
        type: 'json',
        parser: {
          parse: json5.parse
        }
      }, {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
            plugins: [
              [
              ]]
          }
        }
      }]
  },
  optimization: {
    minimizer: [
      new CssMinimizerPlugin()
    ],
    splitChunks: {
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all'
        }
      }
    }
  },
  //关闭 webpack 的性能提示 performance: { hints:false}
}

拆分成两个配置文件后,分别运行这两个文件:

开发环境:

npx webpack serve -c ./config/webpack.config.dev.js // -c --config的简写

生产环境:

npx webpack serve -c ./config/webpack.config.prod.js

我们也可以启动不同环境的服务:

// 开发环境:
npx webpack serve -c ./config/webpack.config.dev.js
// 生产环境
npx webpack serve -c ./config/webpack.config.prod.js

5.4,npm 脚本

每次打包或启动服务时,都需要在命令行里输入一长串的命令,我们可以在package.json里配置npm脚本来简化命令行的输入:

webpack通俗易懂(三)_第14张图片

// package.json
{
  "scripts": {
    "start": "webpack serve -c ./config/webpack.config.dev.js",
    "build": "webpack -c ./config/webpack.config.prod.js"
  }
}

开发环境运行脚本:

npm run start
npm run build

5.5,提取公共配置

这时,我们发现这两个配置文件里存在大量的重复代码,可以手动的将这些重复的代
码单独提取到一个文件里,
创建 webpack.config.common.js ,配置公共的内容:

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const toml = require('toml')
const yaml = require('yaml')
const json5 = require('json5')
module.exports = {
  entry: {
    index: './src/index.js',
    another: './src/another-module.js'
  },
  output: {
    // 注意这个dist的路径设置成上一级
    path: path.resolve(__dirname, '../dist'),
    clean: true,
    assetModuleFilename: 'images/[contenthash][ext]',
  },
  plugins: [new HtmlWebpackPlugin({
    template: './index.html',
    filename: 'app.html',
    inject: 'body'
  }),
  new MiniCssExtractPlugin({
    filename: 'styles/[contenthash].css'
  })],
  module: {
    rules: [
      {
        test: /\.png$/,
        type: 'asset/resource',
        generator: {
          filename: 'images/[contenthash][ext]'
        }
      }, {
        test: /\.svg$/,
        type: 'asset/inline'
      },
      {
        test: /\.txt$/,
        type: 'asset/source'
      }, {
        test: /\.jpg$/,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 4 * 1024
          }
        }
      }, {
        test: /\.(css|less)$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
      }, {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        type: 'asset/resource'
      },
      {
        test: /\.(csv|tsv)$/,
        use: 'csv-loader'
      }, {
        test: /\.xml$/,
        use: 'xml-loader'
      },
      {
        test: /\.toml$/,
        type: 'json',
        parser: {
          parse: toml.parse
        }
      }, {
        test: /\.yaml$/,
        type: 'json',
        parser: {
          parse: yaml.parse
        }
      }, {
        test: /\.json5$/,
        type: 'json',
        parser: {
          parse: json5.parse
        }
      }, {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
            plugins: [
              [
              ]]
          }
        }
      }]
  },
  optimization: {
    splitChunks: {
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all'
        }
      }
    }
  },
  //关闭 webpack 的性能提示 performance: { hints:false}
}

改写 webpack.config.dev.js :

module.exports = {
  // 开发环境不需要配置缓存
  output: {
    filename: 'scripts/[name].js',
  },
  // 开发模式
  mode: 'development',// 配置 source-map
  devtool: 'inline-source-map',
  // 本地服务配置 devServer: { static: './dist' }
}

改写webpack.config.prod.js

const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
module.exports = {
  // 生产环境需要缓存
  output: {
    filename: 'scripts/[name].[contenthash].js', publicPath: 'http://localhost:8080/'
  },
  // 生产环境模式
  mode: 'production',
  // 生产环境 css 压缩 
  optimization: {
    minimizer: [
      new CssMinimizerPlugin()
    ]
  }
}

5.6, 合并配置文件

配置文件拆分好后,新的问题来了,如何保证配置合并没用问题呢?webpack-merge 这个工具可以完美解决这个问题。

安装 webpack-merge :

npm install webpack-merge -D // --save-dev: -D

创建webpack.config.js,合并代码:

const { merge } = require('webpack-merge')
const commonConfig = require('./webpack.config.common.js')
const productionConfig = require('./webpack.config.prod.js')
const developmentConfig = require('./webpack.config.dev')
module.exports = (env) => {
  switch (true) {
    case env.development:
      return merge(commonConfig, developmentConfig)
    case env.production:
      return merge(commonConfig, productionConfig)
    default:
      throw new Error('No matching configuration was found!');
  }
}

你可能感兴趣的:(webpack,webpack,前端,node.js)