Webpack是什么

官方解释: webpack is a static module bundler for modern JavaScript applications. webpack是现代JavaScript应用程序的静态模块绑定器。

打包bundler:webpack可以将帮助我们进行打包,所以它是一个打包工具
静态的static:这样表述的原因是我们最终可以将代码打包成最终的静态资源(部署到静态服务器);
模块化module:webpack默认支持各种模块化开发,ES Module、CommonJS、AMD等;
现代的modern:正是因为现代前端开发面临各种各样的问题,才催生了webpack的出现和发展;
image.png

Vue项目加载的文件有哪些呢?

  • JavaScript的打包:
    • 将ES6转换成ES5的语法;
    • TypeScript的处理,将其转换成JavaScript;
  • Css的处理:
    • CSS文件模块的加载、提取;
    • Less、Sass等预处理器的处理;
  • 资源文件img、font:
    • 图片img文件的加载;
    • 字体font文件的加载;
  • HTML资源的处理:
    • 打包HTML资源文件;
  • 处理vue项目的SFC文件.vue文件;

Webpack的运行是依赖Node环境的,所以我们电脑上必须有Node环境

Webpack的安装

  • webpack的安装目前分为两个:webpack、webpack-cli
  • 那么它们是什么关系呢?
    • 执行webpack命令,会执行node_modules下的.bin目录下的webpack;
    • webpack在执行时是依赖webpack-cli的,如果没有安装就会报错;
    • 而webpack-cli中代码执行时,才是真正利用webpack进行编译和打包的过程;
    • 所以在安装webpack时,我们需要同时安装webpack-cli(第三方的脚手架事实上是没有使用webpack-cli的,而是类似于自己的vue-service-cli的东西)

image.png

Webpack的默认打包

  • 直接执行webpack 会调用全局的webpack,如果没有安装会报错
  • 可以使用npx webpack 来调用, 默认会找 src/index.js 作为入口进行打包,
    • 如果当前项目中没有存在src/index.js文件,会报错
    • 生成一个dist文件夹,里面的main.js文件,就是打包之后的文件:该文件的代码被压缩和丑化了;
  • 如果找不到这个index.js文件就会报错,可以再命令中这么写
    • npx webpack —entry ./src/main.js —output-path ./build
    • 也可以在package.json script标签中指定,但是不推荐这么做因为我们webpack会有很多项配置,
  • 在脚本中会默认使用局部的webpack,不需要指定npx

Webpack配置文件

在根目录下创建一个webpack.config.js文件,来作为webpack的配置文件:

  1. const path = require('path');
  2. module.exports = {
  3. entry: './src/main.js',
  4. output: {
  5. path: path.resolve(__dirname, './build'), // 这里必须写绝对路径
  6. filename: 'bundle.js',
  7. },
  8. };

修改配置文件

  • 如果配置文件名字不是webpack.config.js,可以通过 —config 来指定对应的配置文件:
    • webpack --config wk.config.js
  • 但是每次这样执行命令来对源码进行编译,会非常繁琐,所以我们可以在package.json中增加一个新的脚本: ```json “scripts”: { “build”: “webpack —config cos.config.js” },
  1. <a name="CMtYv"></a>
  2. ### webpack是如何对项目进行打包的?
  3. - webpack在处理应用程序时,会根据命令或者配置文件找到入口文件;
  4. - 从入口开始,会生成一个依赖关系图,这个依赖关系图会包含应用程序中所需的所有模
  5. - 比如.js文件、css文件、图片、字体等
  6. - 然后遍历图结构,打包一个个模块(根据文件的不同使用不同的loader来解析);
  7. <a name="StqmH"></a>
  8. ## css-loader的使用
  9. webpack默认只能打包js文件,所以如果需要打包css文件,就需要配置css-loader:
  10. - **loader 可以用于对模块的源代码进行转换**;
  11. - 我们可以将css文件也看成是一个模块,通过import来加载这个模块的;
  12. - 在加载这个模块时,webpack其实并不知道如何对其进行加载,我们必须制定对应的loader来完成这个功能;
  13. - 对于加载css文件来说,需要一个可以读取css文件的loader;
  14. - 这个loader最常用的是css-loader;
  15. - css-loader的安装:
  16. - `npm install css-loader -D`
  17. <a name="NPnD2"></a>
  18. ### css-loader的使用方案
  19. 如何使用这个loader来加载css文件呢?有三种方式:
  20. <a name="CgEYy"></a>
  21. #### 内联方式
  22. - 内联方式使用较少,因为不方便管理;
  23. - 在引入的样式前加上使用的loader,并且使用!分割;
  24. - `import 'css-loader!../css/style.css';`
  25. <a name="iJ9IJ"></a>
  26. #### CLI方式(webpack5中不再使用)
  27. - 在webpack5的文档中已经没有了--module-bind;
  28. - 实际应用中也比较少使用,因为不方便管理;
  29. <a name="T47dt"></a>
  30. #### 配置方式
  31. <a name="Pkaaf"></a>
  32. ##### 在`webpack.config.js`文件中写明配置信息:
  33. - module.rules中允许我们配置多个loader(因为我们也会继续使用其他的loader,来完成其他文件的加载);
  34. - 这种方式可以更好的表示loader的配置,也方便后期的维护,同时也让你对各个Loader有一个全局的概览;
  35. <a name="He0C2"></a>
  36. ##### module.rules的配置如下:
  37. - rules属性对应的值是一个数组:[Rule]
  38. - 数组中存放的是Rule,Rule是一个数组对象,对象中可以设置多个属性:
  39. - test属性:用于对 resource(资源)进行匹配的,通常会设置成正则表达式;
  40. - use属性:对应的值是一个数组:[UseEntry]
  41. - UseEntry是一个对象,可以通过对象的属性来设置一些其他属性
  42. - loader:必须有一个 loader属性,对应的值是一个字符串;
  43. - options:可选的属性,值是一个字符串或者对象,值会被传入到loader中;
  44. - query:目前已经使用options来替代;
  45. - 传递字符串(如:use: [ 'style-loader' ])是 loader 属性的简写方式(如:use: [ { loader: 'style-loader'} ]);
  46. - loader属性: Rule.use: [ { loader } ] 的简写。
  47. ```javascript
  48. module.exports = {
  49. ...
  50. module: {
  51. rules: [
  52. {
  53. test: /\.css$/, // 正则表达式 \. 做转义
  54. // 1. loader的写法-语法糖
  55. // loader: 'css-loader',
  56. // 2. 完整写法
  57. // use: 'css-loader',
  58. // use: ['css-loader'], // 一般我们都会在use之后跟一个数组,因为有时候一个loader是搞不定的,需要多个loader
  59. use: [
  60. // 这是完整的写法,因为可以再里面配置其他的参数
  61. // 这里loader的执行顺序是,从后往前执行的。
  62. {
  63. loader: 'style-loader',
  64. },
  65. {
  66. loader: 'css-loader',
  67. },
  68. ],
  69. },
  70. ],
  71. },
  72. };

style-loader

  • 通过css-loader可以加载css文件
  • 但css在代码中并没有生效(页面没有效果)。
  • 这是为什么呢?
    • 因为css-loader只是负责将.css文件进行解析,并不会将解析之后的css插入到页面中;
    • 如果希望再完成插入style的操作,还需要另外一个loader,就是style-loader;
  • 安装style-loader:

    • npm install style-loader -D

      配置style-loader

  • 在配置文件中,添加style-loader;

    • 因为loader的执行顺序是从右向左(或者说从下到上,或者说从后到前的),所以我们需要将styleloader写到css-loader的前面;
  • 重新执行编译npm run build,可以发现打包后的css已经生效了:
  • 当前的css是通过页内样式的方式添加进来的;
  • 后续学习如何将css抽取到单独的文件中,并且进行压缩等操作;

Less

  • 在开发中我们能会使用less、sass、stylus的预处理器来编写css样式,效率会更高。
    • 但是,less、sass等编写的css需要通过工具转换成普通的css;
    • 安装less:npm install less -D
  • 我们通过 lessc (less源码编译器)把编写好的.less文件生成.css文件:
    • npx lessc ./src/css/title.less title.css

less-loader处理

在项目中会编写大量的css,可以使用less-loader,来自动使用less工具转换less到css;
npm install less-loader -D

PostCSS

什么是PostCSS?

  • PostCSS是一个通过JavaScript来转换样式的工具;
    • 这个工具可以帮助我们进行一些CSS的转换和适配,比如自动添加浏览器前缀、css样式的重置;
    • 实现这些功能,需要借助于PostCSS对应的插件;

      命令行使用postcss

      我们也可以直接在终端使用PostCSS,但需要单独安装一个工具postcss-cli;
      npm install postcss postcss-cli -D

插件autoprefixer

npm install autoprefixer -D
直接使用使用postcss工具,并且指定使用autoprefixer
npx postcss --use autoprefixer -o end.css ./src/css/style.css
image.png

postcss-loader

  • 在webpack中使用postcss就是使用postcss-loader来处理的;
    • npm install postcss-loader -D
  • 修改加载css的loader(配置文件已经过多,给出一部分了)
    • 注意:因为postcss需要有对应的插件才会起效果,所以我们需要配置它的plugin;
      1. module: {
      2. rules: [
      3. {
      4. test: /\.css$/,
      5. use: [
      6. 'style-loader',
      7. 'css-loader',
      8. {
      9. loader: 'postcss-loader',
      10. options: {
      11. postcssOptions: {
      12. plugins: [require('autoprefixer')],
      13. },
      14. },
      15. },
      16. ],
      17. },
      18. ],
      19. },
      从上面的代码可以发现其实这样的配置方法是非常繁琐的,所以我们可以把postcss的配置提取出来,放在根目录下的 postcss.config.js文件下:
      1. module.exports = {
      2. plugins: [require('autoprefixer')],
      3. };
      在webpack.config.js中,我们就可以去掉对应的配置信息了。

postcss-preset-env

  • 实际开发中,postcss-loader插件一般不使用autoprefixer。
    • 而是使用另外一个插件:postcss-preset-env
  • postcss-preset-env也是一个postcss的插件:
    • 它可以帮助我们将一些现代的CSS特性,转成大多数浏览器认识的CSS,
    • 并且会根据目标浏览器或者运行时环境添加所需的polyfill
    • 也包括会自动添加autoprefixer(所以相当于已经内置了autoprefixer);
  • npm install postcss-preset-env -D
  • 在使用某些postcss插件时,也可以直接传入字符串
    1. module.exports = {
    2. // plugins: [require('autoprefixer')],
    3. plugins: ['postcss-preset-env'],
    4. };

打包图片

  • 加载图片资源的方式
    • img元素,设置src属性;
    • 其他元素(比如div),设置background-image的css属性;
  • 但是,这时候直接打包会报错,webpack5当中,其实在css属性中引入背景图片是不会报错的,并且如果使用file-loader处理后会生成一张多余的图片,导致背景图片访问出现问题。

    file-loader

  • 要处理jpg、png等格式的图片,也需要有对应的loader:file-loader

  • file-loader的作用就是帮助我们处理import/require()方式引入的一个文件资源,并且会将它放到我们输出的文件夹中;
  • npm install file-loader -D
  1. {
  2. test: /\.(jpe?g|png|svg|gif)$/i,
  3. use: {
  4. loader: 'file-loader',
  5. options: {
  6. // outputPath: 'img',
  7. // name: '[name]_[hash:4].[ext]',
  8. // 可以省略outputPath,把文件夹直接写在name路径中
  9. name: 'image/[name]-[hash:8].[ext]',
  10. },
  11. },
  12. },

文件的命名规则

  • 如果要将处理后的文件名称,按照一定的规则进行显示:
  • 比如保留原来的文件名、扩展名,同时为了防止重复,包含一个hash值等;
  • 可以使用PlaceHolders来完成,webpack提供了大量的PlaceHolders显示不同的内容:
  • 常用的placeholder:

    • [ext]: 处理文件的扩展名;
    • [name]:处理文件的名称;
    • [hash]:文件的内容使用MD4的散列函数处理,生成的一个128位的hash值(32个十六进制);
    • [contentHash]:在file-loader中和[hash]结果是一致的(在webpack的一些其他地方不一样);
    • [hash:]:截图hash的长度,默认32个字符太长了;
    • [path]:文件相对于webpack配置文件的路径;

      设置文件的存放路径

  • 我们通过 img/ 已经设置了文件夹,这是vue、react脚手架中常见的设置方式:

  • 按照这种设置方式就可以了;
  • 也可以通过outputPath来设置输出的文件夹;

    url-loader

  • url-loaderfile-loader的工作方式是相似的,但是可以将较小的文件,转成base64的URI。

  • npm install url-loader -D
  • 使用url-loader打包后,图片同样可以正常显示;
  • 但是在dist文件夹中,没有图片文件:

    • 默认情况下url-loader会将所有的图片文件转成base64编码

      url-loader的limit

  • 开发中往往是小的图片需要转换,但是大的图片直接使用图片即可

    • 因为小的图片转换base64之后可以和页面一起被请求,减少不必要的请求过程;
    • 而大的图片也进行转换,反而会影响页面的请求速度;
  • 如何限制哪些大小的图片转换和不转换呢?

    • url-loader有一个options属性limit,可以用于设置转换的限制;
    • limit默认的单位是字节Byte,所以如果设置100kb以下的图片打包成base64则为:limit: 100 * 1024
      1. {
      2. test: /\.(jpe?g|png|svg|gif)$/,
      3. use: {
      4. loader: 'url-loader',
      5. options: {
      6. name: 'image/[name]-[hash:8].[ext]',
      7. limit: 100 * 1024,
      8. },
      9. },
      10. },

      asset module type

  • 在webpack5之前,加载这些资源需要使用一些loader,比如raw-loader 、url-loader、file-loader;

  • 在webpack5开始,可以直接使用资源模块类型(asset module type),来替代上面的这些loader;
  • 资源模块类型(asset module type),通过添加 4 种新的模块类型,来替换所有这些 loader:
    • asset/resource 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现;
    • asset/inline 导出一个资源的 data URI。之前通过使用 url-loader 实现;
    • asset/source 导出资源的源代码。之前通过使用 raw-loader 实现;
    • asset 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源体积限制实现;
  • 通过将type修改为asset,然后添加一个parser属性,并且制定dataUrl的条件,添加maxSize属性,类似limit效果

如何可以自定义文件的输出路径和文件名呢?
方式一:修改output,添加assetModuleFilename属性;

  1. output: {
  2. path: path.resolve(__dirname, './build'), // 这里必须写绝对路径
  3. filename: 'bundle.js',
  4. assetModuleFilename: 'image/[name][hash:6][ext]',
  5. },

方式二:在Rule中,添加一个generator属性,并且设置filename;

  1. {
  2. test: /\.(jpe?g|png|svg|gif)$/,
  3. type: 'asset',
  4. generator: {
  5. filename: 'img/[name]-[hash:6][ext]',
  6. },
  7. parser: {
  8. dataUrlCondition: {
  9. maxSize: 100 * 1024,
  10. },
  11. },
  12. },

打包字体文件

file-loader处理字体文件

在webpack5中这种方法会出现文件重复的问题

  1. {
  2. test: /\.(eot|ttf|woff2?)$/,
  3. use: {
  4. loader: 'file-loader',
  5. options: {
  6. name: 'fonts/[name]-[hash:6].[ext]',
  7. },
  8. },
  9. },

使用webpack5的资源模块处理

  1. {
  2. test: /\.(eot|ttf|woff2?)$/,
  3. type: 'asset/resource',
  4. generator: {
  5. filename: 'fonts/[name]-[hash:6][ext]',
  6. },
  7. },

其实到目前位置,我们已经配置了常见的资源文件的打包策略,但是还存在一些小问题,比如:

  • 1- dist目录需要手动删除
  • 2- 打包的文件内没有暴露index.html这样的入口