webpack笔记-webpack基础用法(二)

webpack 本质上是一个打包工具,它会根据代码的内容解析模块依赖,帮助我们把多个模块的代码打包。

一切文件:JavaScript、CSS、SCSS、图片、模板,在 Webpack 眼中都是一个个模块,这样的好处是能清晰的描述出各个模块之间的依赖关系,以方便 Webpack 对模块进行组合和打包。 经过 Webpack 的处理,最终会输出浏览器能使用的静态资源。

 entry

webpack 构建的入口entry,webpack 会读取这个文件,并从它开始解析依赖,在内部构件一个依赖图,这个依赖图会引用项目中使用到的各个模块,然后进行打包,生成一个或者多个 bundle 文件。

我们常见的项目中,如果是单页面应用,那么入口只有一个;如果是多个页面的项目,那么通常是一个页面会对应一个构建入口。

单⼊⼝:entry 是⼀个字符串,如下代码:

module.exports = {
entry: './src/index.js'
};

上述代码等价于:

// 上述配置等同于
module.exports = {
  entry: {
    main: './src/index.js'
  }
}

多⼊口:entry 是⼀个对象,如下代码:

module.exports = {
  entry: {
    app: './src/app.js',
    home: './src/home.js'
  }
};

还有一种场景比较少用到,即是多个文件作为一个入口来配置,webpack 会解析多个文件的依赖然后打包到一起:

// 使用数组来对多个文件进行打包
module.exports = {
  entry: {
    main: [
      './src/foo.js',
      './src/bar.js'
    ]
  }
}

动态 entry

const path = require('path');
const fs = require('fs');

// src/pages 目录为页面入口的根目录
const pagesRoot = path.resolve(__dirname, './src/pages');
// fs 读取 pages 下的所有文件夹来作为入口,使用 entries 对象记录下来
const entries = fs.readdirSync(pagesRoot).reduce((entries, page) => {
  // 文件夹名称作为入口名称,值为对应的路径,可以省略 `index.js`,webpack 默认会寻找目录下的 index.js 文件
  entries[page] = path.resolve(pagesRoot, page);
  return entries;
}, {});

module.exports = {
  // 将 entries 对象作为入口配置
  entry: entries,

  // ...
};

output

output用来告诉 webpack 如何将编译后的文件输出到磁盘。webpack 构建生成的文件名、路径等都是可以配置的,在配置文件中使用 output 字段来进行设置:

module.exports = {
  // ...
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
  },
}

// 或者使用 entry 的名称
module.exports = {
  entry: {
    main: './src/index.js' // main 为 entry 的名称
  },
  output: {
    filename: '[name].js', // 使用 [name] 来引用 entry 名称,在这里即为 main
    path: path.join(__dirname, '/dist/[hash]'),
    // 路径中使用 hash,每次构建时会有一个不同 hash 值,可以用于避免发布新版本时浏览器缓存导致代码没有更新
    // 文件名中也可以使用 hash
  },
}

 loader

我们在前端构建中会遇见需要使用各式各样的文件,例如 css 代码,图片,模板代码等。webpack 中提供一种处理多种文件格式的机制,便是使用 loader。我们可以把 loader 理解为是一个转换器,负责把某种文件格式的内容转换成 webpack 可以支持打包的模块。例如我们需要 css-loader 来处理 .css 文件(这里其实还需要 style-loader),最终把不同格式的文件都解析成 js 代码,以便打包后在浏览器中运行。

webpack 开箱即用只支持 JS 和 JSON 两种文件类型,通过 Loaders 去支持其它文件类型并且把它们转化成有效的模块,并且可以添加到依赖图中。

本身是一个函数,接受源文件作为参数,返回转换的结果。

 常见的 Loaders 有哪些?

  •  babel-loader:转换ES6、ES7等JS新特性语法
  • css-loader:支持.css文件的加载和解析
  • less-loader:将less文件转换成css
  • ts-loader:将TS转换成JS
  • file-loader:进行图片、字体等的打包
  • raw-loader:将文件以字符串的形式导入
  • thread-loader:多线程打包JS和CSS

下面我们来配置下处理css的loader以及ES6转ES5的loader,首先npm安装如下loader,

  • css-loader:npm i style-loader css-loader -D
  • babel-loader:npm install babel-loader @babel/core @babel/preset-env

babel的一系列工具链说明:

@babel/core:

@babel/core是babel的核心库,所有的核心Api都在这个库里,这些Api供babel-loader调用;

@babel/preset-env:

这是一个预设的插件集合,包含了一组相关的插件,Bable中是通过各种插件来指导如何进行代码转换。该插件包含所有es6转化为es5的翻译规则。

babel官网对此进行的如下说明:

Transformations come in the form of plugins, which are small JavaScript programs that instruct Babel on how to carry out transformations to the code. You can even write your own plugins to apply any transformations you want to your code. To transform ES2015+ syntax into ES5 we can rely on official plugins like@babel/plugin-transform-arrow-functions

大致即es6到es5的语法转换是以插件的形式实现的,可以是自己的插件也可以是官方提供的插件如箭头函数转换插件@babel/plugin-transform-arrow-functions。

由此我们可以看出,我们需要转换哪些新的语法,都可以将相关的插件一一列出,但是这其实非常复杂,因为我们往往需要根据兼容的浏览器的不同版本来确定需要引入哪些插件,为了解决这个问题,babel给我们提供了一个预设插件组,即@babel/preset-env,可以根据选项参数来灵活地决定提供哪些插件。为此,我们往往做如下配置: 

@babel/polyfill:

@babel/preset-env只是提供了语法转换的规则,但是它并不能弥补浏览器缺失的一些新的功能,如一些内置的方法和对象,如Promise,Array.from等,此时就需要polyfill来做js得垫片,弥补低版本浏览器缺失的这些新功能。

我们需要注意的是,polyfill的体积是很大的,如果我们不做特殊说明,它会把你目标浏览器中缺失的所有的es6的新的功能都做垫片处理。但是我们没有用到的那部分功能的转换其实是无意义的,造成打包后的体积无谓的增大,所以通常,我们会在presets的选项里,配置"useBuiltIns": "usage",这样一方面只对使用的新功能做垫片,另一方面,也不需要我们单独引入import '@babel/polyfill'了,它会在使用的地方自动注入。

babel-loader:

以上@babel/core、@babel/preset-env 、@babel/polyfill其实都是在做es6的语法转换和弥补缺失的功能,但是当我们在使用webpack打包js时,webpack并不知道应该怎么去调用这些规则去编译js。这时就需要babel-loader了,它作为一个中间桥梁,通过调用babel/core中的api来告诉webpack要如何处理js。

@babel/plugin-transform-runtime:

polyfill的垫片是在全局变量上挂载目标浏览器缺失的功能,因此在开发类库,第三方模块或者组件库时,就不能再使用babel-polyfill了,否则可能会造成全局污染,此时应该使用transform-runtime。transform-runtime的转换是非侵入性的,也就是它不会污染你的原有的方法。遇到需要转换的方法它会另起一个名字,否则会直接影响使用库的业务代码,

故开发类库,第三方模块或者组件库时使用transform-runtime,平常的项目使用babel-polyfill即可.

详细可参考文章:

rules的配置如下:

module: {
        rules:[
            {
                test: /\.css$/,
                use: ['style-loader','css-loader']
            },
            {
                test: /\.jsx?$/,  // 匹配文件路径的正则表达式,通常我们都是匹配文件类型后缀
                exclude: /node_modules/, // 排除掉node_modules这个文件夹的js文件
                include: [
                    path.resolve(__dirname,'src') // 指定哪些路径下的文件需要经过 loader 处理
                ],
                use: { // 指定使用的 loader
                    loader: 'babel-loader', // babel-loader 可以使用 babel 来将 ES6 代码转译为浏览器可以执行的的 ES5 代码
                    options: {
                        presets: ['@babel/preset-env']
                    }
                }
            }
        ]
    }

 plugin

plugin 在 webpack 中的配置只是把实例添加到 plugins 字段的数组中。不过由于需要提供不同的功能,不同的 plugin 本身的配置比较多样化。

常用的plugin:

  • CommonsChunkPlugin:将chunks相同的模块代码提取成公共js,webpack4.x版本使用optimization.SplitChunks
  • cleanWebpackPlugin:清理构建目录
  • ExtractTextWebpackPlugin:将CSS从bundle文件里提取成一个独立的CSS文件(webpack3.x版本),webpack4.x版本使用mini-css-extract-plugin
  • CopyWebpackPlugin:将文件或者文件夹拷贝到构建的输出目录
  • HtmlWebpackPlugin:创建html文件去承载输出的bundle
  • UglifyjsWebpackPlugin:压缩JS
  • ZipWebpackPlugin:将打包出的资源生成一个zip包
const path = require('path');
module.exports = {
    output: {
        filename: 'bundle.js'
    },
    plugins: [
        new HtmlWebpackPlugin({   // 放到plugins数组里
            template: './src/index.html'
        })
    ]
};

 

在 webpack 的构建流程中,plugin 用于处理更多其他的一些构建任务。可以这么理解,模块代码转换的工作由 loader 来处理,除此之外的其他任何工作都可以交由 plugin 来完成。

例如,使用 copy-webpack-plugin 来复制其他不需要 loader 处理的文件,只需在配置中通过 plugins 字段添加新的 plugin 即可:

npm install copy-webpack-plugin -D 
# 插件通常为第三方的 npm package,都需要安装后才能使用
const CopyPlugin = require('copy-webpack-plugin');

module.exports = {
  // ...

  plugins: [
    new CopyPlugin([
      { from: 'src/public', to: 'public' },
    ]),
  ],
};

mode

mode,构建模式是 webpack v4 引入的新概念,用于方便快捷地指定一些常用的默认优化配置,mode 可以是 development,production,none 其中的一个,我们之前已经在配置文件中添加了相应的配置:

module.exports = {
  mode: 'development', // 指定构建模式为 development
  // ...
}

顾名思义,development 模式用于开发时使用,production 模式用于线上生产时使用,none 则是不需要任何默认优化配置时使用。

development 和 production 模式的区别:

  • 这两个模式会使用 DefinePlugin 来将 process.env.NODE_ENV 的值分别设置为 development 和 production,方便开发者在项目业务代码中判断当前构建模式。
  • production 模式会启用 TerserPlugin来压缩JS代码,让生成的代码文件更小。
  • development 模式会启用 devtools: 'eval' 配置,提升构建和再构建的速度。

前端构建基础配置

关联HTML

怎样将HTML引用路径和我们的构建结果关联起来,这个时候我们可以使用 html-webpack-plugin。

html-webpack-plugin 是一个独立的 package,在使用之前我们需要先安装它:

npm install html-webpack-plugin -D 

然后在 webpack 配置中,将 html-webpack-plugin 添加到 plugins 列表中:

const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  // ...
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html', // 配置文件模板
    }),
  ],
}

这样,通过 html-webpack-plugin 就可以将我们的页面和构建 JS 关联起来,回归日常,从页面开始开发。如果需要添加多个页面关联,那么实例化多个 html-webpack-plugin, 并将它们都放到 plugins 字段数组中就可以了。

构建 CSS

我们编写 CSS,并且希望使用 webpack 来进行构建,为此,需要在配置中引入 loader 来解析和处理 CSS 文件:

module.exports = {
  module: {
    rules: {
      // ...
      {
        test: /\.css/,
        include: [
          path.resolve(__dirname, 'src'),
        ],
        use: [
          'style-loader',
          'css-loader',
        ],
      },
    },
  }
}

上面配置的两个loader的作用:

  • css-loader 负责解析 CSS 代码,主要是为了处理 CSS 中的依赖,例如 @import 和 url() 等引用外部文件的声明;
  • style-loader 会将 css-loader 解析的结果转变成 JS 代码,运行时动态插入 style 标签来让 CSS 代码生效。

如果需要单独把 CSS 文件分离出来,我们需要使用 mini-css-extract-plugin 插件。

v4 版本之后才开始使用 mini-css-extract-plugin,之前的版本是使用 extract-text-webpack-plugin。

如下例子:

const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.css/i,
        use: [
          // 因为这个插件需要干涉模块转换的内容,所以需要使用它对应的 loader
          MiniCssExtractPlugin.loader,
          'css-loader',
        ],
      },
    ],
  },

  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].css' // 这里也可以使用 [hash]
    }), // 将 css 文件单独抽离的 plugin
  ]
};

在上述使用 CSS 的基础上,通常我们会使用 Less/Sass 等 CSS 预处理器,webpack 可以通过添加对应的 loader 来支持,以使用 Less 为例

less-loader 只是 webpack 的转换器,启动 Less 你还需要安装 less 自身,同样地,sass-loader 也是这般。

在 webpack 配置中,添加一个配置来支持解析后缀为 .less 的文件:

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.(less|css)$/,
        use: [
          // 因为这个插件需要干涉模块转换的内容,所以需要使用它对应的 loader
          MiniCssExtractPlugin.loader,
          'css-loader',
          'less-loader',
        ],
      },
    ],
  },
  // ...
}

处理图片文件

在前端项目的样式中总会使用到图片,虽然我们已经提到 css-loader 会解析样式中用 url() 引用的文件路径,但是图片对应的 jpg/png/gif 等文件格式,webpack 处理不了。是的,我们只要添加一个处理图片的 loader 配置就可以了,现有的 file-loader 就是个不错的选择。

file-loader 可以用于处理很多类型的文件,它的主要作用是直接输出文件,把构建后的文件路径返回。配置很简单,在 rules中添加一个字段,增加图片类型文件的解析配置:

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.(png|jpg|gif)$/,
        use: [
          {
            loader: 'file-loader',
            options: {},
          },
        ],
      },
    ],
  },
}

使用babel

Babel 是一个让我们能够使用 ES 新特性的 JS 编译工具,我们可以在 webpack 中配置 Babel,以便使用 ES6、ES7 标准来编写 JS 代码。

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.jsx?/, // 支持 js 和 jsx 文件,使用 react 时需要
        include: [
          path.resolve(__dirname, 'src'), // 指定哪些路径下的文件需要经过 loader 处理
        ],
        use: {
          loader: 'babel-loader', // 指定使用的 loader
          options: {
            presets: ['@babel/preset-env'],
          },
        },
      },
    ],
  },
}

 

posted @ 2020-06-27 11:46  风雨后见彩虹  阅读(221)  评论(0编辑  收藏