Webpack核心概念

什么是 Loader

Loader 可以简单的理解为一个处理函数,对于不同类型的文件(js文件、图片文件、字体文件),我们使用不同的 Loader 进行处理。Loader 是不包含在 Webpack 中的,如果我们要处理各种文件,就要安装不同的 Loader,因为 Webpack 本质上只能根据exportj和import这样的导出引入方法,建立起各个模块之间的依赖,如果要对js文件执行一系列操作,比如说要对es6或更高级的语法实现对低版本浏览器的适配,就需要特定的loader来做这件事。

Loader的配置方法

module.exports = {
  entry: '',
  // 定义一系列处理规则的模块
  module: {
    // 规则列表
    rules: [
      {
        // 匹配文件
        test: /\.js$/,
        /**
         * 使用什么样的 loader
         * 如果使用的 loader是一个数组,每一个 loader 都是一个对象,如果这个 loader 没有任何配置项,直接以字符串的形式写入名称即可
         */
        use: [
          {
            // loader 名称
            loader: 'babel-loader',
            // 配置项
            options: {}
          }
        ],
        /**
         * 每一个 Loader 都有一个打包范围的选项,可以限制打包范围,或者打包时忽略某些文件
         */
        // 打包路径
        include: 
        // 忽略的文件夹
        exclude:
      }
    ]
  }
}

Loader 就是一个打包方案。

使用 Loader 打包图片文件

对于图片资源,我们可以使用 file-loader 或者 url-loader。

file-loader:默认情况下,生成的文件的文件名就是文件内容的 MD5 哈希值并会保留所引用资源的原始扩展名。

module: {
  rules: [
    {
      test: /\.(png|jpg|gif)$/,
      use: [
        {
          loader: 'file-loader',
          options: {
            /**
             * 默认打包后的文件名为 MD5 哈希值
             * 通过 name 属性配置打包过后的文件名
             */
            name: '[name].[ext]',
            /**
             * 输出之后放入哪个路径,默认放在 dist 跟目录下
             * 设置的路径是相对于 dist 目录的,'images/' -- 即在 dist 目录下新建一个文件夹,打包完成的图片放入里面
             */
            outputPath: 'images/'
          }
        }
      ]
    }
  ]
}

url-loader: 作用跟 file-loader 类似,但可以通过配置选项里的 limit 属性限制文件小于某个值时直接返回一个 DataUrl,嵌入到html文件中,这样的话使用该图片资源就不用额外请求资源了。

// url-loader 配置项基本与 file-loader 一致
module: {
  rules: [
    {
      loader: 'url-loader',
      options: {
        // 如果打包的图片小于此大小,就会直接打包到入口文件里,页面使用时不需要再发送 http 请求
        limit: 8192
      }
    }
  ]
}

使用 Loader 打包样式

处理css文件

  • postcss-loader -- 添加厂商前缀
  • css-loader -- 用来处理css文件中的url路径(解析@import 和 url()语法)
  • style-loader -- 可以把css文件变成style标签的形式插入到head中

具体配置

module: {
  rules: [
    {
      test: /\.css$/,
      // 多个loader有执行顺序的要求,从右向左,从下往上,对于处理css文件不能搞混loader执行的先后顺序
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            /**
             * 当在一个css文件中如果通过 import 的方式引入了其他css文件,解析这个css文件默认从css-loader开始
             * 如果配置了 importLoaders,就等于告诉了 Webpack在执行该loader之前还有两个loader需要执行
             * Webpack 会自动往前寻找指定数量的loader执行,就避免了引入的css文件处理不完整的问题
             */
            importLoaders: 1
          } 
        },
        'postcss-loader'
      ]
    }
  ]
}

如果打包sass、less类型的文件,只需在其他css-loader执行之前加载相应的loader即可

处理字体文件

file-loader

module: {
  rules: [
    {
      test: /\.(eot|woff|svg|ttf|woff2)$/,
      use: [
        {
          loader: 'file-loader',
          options: {
            name: '[name].[ext]',
            outputPath: 'fonts/'
          }
        }
      ]
    }
  ]
}

使用 Plugins 使打包更加便捷

可以在 Webpack 运行到某个时刻的时候,做一些特定的事情(类似生命周期函数);
Plugin 使用时需要引入,然后实例化

常用的 Plugins

  • html-webpack-plugin -- 会在每次打包结束后,自动生成一个html文件,并把打包生成的js自动引入到这个hmtl文件中,可以设置模板文件,使用该模板作为这个html文件
// 引入插件,命名以大写字母开头,因为后面要实例化
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 使用插件
module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
      // 设置模板文件
      template: ''
    })
  ]
}
  • clean-webpack-plugin -- 每次打包之前都清除 dist 目录下的所有文件,可配置清除目录

Entry 与 Output 配置

打包入口文件和输入文件的路径设置
打包单个入口文件

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

打包多个入口文件

module.exports = {
  entry: {
    main: './src/main.js',
    index: './src/index.js'
  },
  output: {
    // 为js文件增加前缀
    publicPath: 'https://cdn.com.cn',
    // 使用 name 占位符,会根据入口文件的配置自动生成多个文件
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist')
  }
}

SourceMap 的配置

sourceMap 是一个映射关系,它知道dist目录下main.js文件中代码的每一行对应打包之前文件的行数。
如果有一行代码出错了,通过设置不同的 devtool 显示不同的报错信息。
通过配置devtool设置souceMap,不同的值会明显影响到构建(build)和重新构建(rebuild)的速度。
比如我们在src目录下的某一行代码出错了,默认情况下控制台报错显示的是打包过后的文件报错的行数,但是我们期望定位到原始出错位置。
webpack 文档中有关于各种模式的 devtool 的打包效率对比。

配置

module.exports = {
  // development 模式下
  devtool: 'cheap-module-eval-source-map'
  // production 模式下
  devtool: 'cheap-module-source-map'
}

WebpackDevServer的使用

它为我们提供了一个简单的 web 服务器,并且能够实时重新加载,有利于提高开发效率。
开发模式下使用。

配置

module: {
  // 默认在 localhost:8080 下建立服务
  devServer: {
    // 将 dist 目录下的文件,作为可访问文件。
    contentBase: './dist',
    // 为了不与其他端口号冲突,可配置端口
    port: '8000'
  }
}

添加脚本命令运行devServer

// package.json 文件中
scripts: {
  // --open 表示自动打开默认浏览器
  "dev": "webpack-dev-server --open"
}

现在,命令行运行 npm run dev 即可开启 webpack-dev-server 开启服务器辅助开发。
通过 webpack-dev-server 打包命令打包后,不会显示的出现dist目录,这是被放入了电脑内存中,有利于提高性能

Hot Moudle Replacement -- 模块热替换

当我们更改项目代码的时候,webpack-dev-server 会感知到变化,然后自动刷新页面,然后并不是每一次改动我们都希望它自动刷新页面。
当更改样式时,为了代码的调试方便,我们只希望更改页面的样式,而不希望刷新整个页面,就需要启用模块热替换。

配置

const webpack = require('webpack');
module.exports = {
  module: {
    devServer: {
      contentBase: './dist',
      port: 8000,
      hot: true,
      hotOnly: true
    },
    plugins: [
      new webpack.hotModuleReplacementPlugin()
    ]
  }
}

使用 Babel 处理 ES6 语法

我们在开发的过程中会使用大量的 ECMAScript 2015+ 版本的语法,但是某些浏览器中较低的版本没有实现该功能语法,这个时候我们就需要借助 Babel 编译 ES6 语法成浏览器能够识别的代码。

定义

Babel 是一个工具链,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。

功能

  • 语法转换
  • 通过 Polyfill 方式在目标环境中添加缺失的特性 (通过 @babel/polyfill 模块)
  • 源码转换 (codemods)

在 Webpack 中使用 Babel

进入 Babel 官网,进入设置,然后选择 Webapck,按照提示进行相关包的安装和配置。

安装模块

  • babel-loader
    Webpack 与 Babel 之间通信的桥梁,本身不认识 ES6 语法。
  • @babel/core
    Babel 中的一个核心库,让 Babel 识别代码中的内容,转化为抽象语法树。
  • @babel/preset-env
    Babel 中核心模块,包含一系列语法转换的规则,把 ECMAScript 2015+ 的语法转化为我们希望它能用的浏览器或环境(通过 targets 配置)。
  • @babel/polyfill
    Babel包含一个polyfill,它包含一个自定义的再生器运行时和core-js。
    这将模拟完整的ES2015 +环境,引入该模块,可以自动添加在项目中使用了但运行环境中还没有实现的功能,比如(Primise),默认引入所有功能,可通过设置按需引入。
    该模块需要安装在生产环境中。

配置

// webpack.config.js
module: {
  rules: [
    {
      test: /\.js$/,
      // 忽略第三方模块,不需要进行打包
      exclude: /node_modules/,
      use: 'babel-loader'
    }
  ]
}

// .babelrc
// preset -- 预置
{
  "preset": ["@babel/preset-env"]
  /**
   * 如果使用多个预置,使用数组隔开
   * 如果使用的 preset 还有其他配置,则数组的第一位写名称,第二位写配置对象
   */
  "presets": [
    [
      "@babel/preset-env",
      {
        targets: {
          // 目标设置最低浏览器运行的版本,如果该浏览器已经实现了 ES2015 + 的相关功能,则不需要再次引入
          chrome: 67
        },
        // 按需引入,使用了哪些 ES2015 + 语法,就引入哪些
        "useBuiltIns": "usage"
      }
    ]
  ]
}

// main.js -- 入口文件顶部引入
// 如果在 Babel 配置文件中配置了 useBuiltIns,则不需要在额外引入 @babel/polyfill
import '@babel/polyfill';

以上的配置是在打包业务逻辑代码时使用,如果是打包一个库类的文件,这些配置就有些不合适了,因为在引入 @babel/polyfill 时会污染全局。

打包库类文件的配置

  • 模块:
  • @babel/plugin-transform-runtime
  • @babel/runtime
  • @babel/runtime-corejs2
// .babelrc
{
  "presets": [
    [
      "@babel/plugin-transform-runtime",
      {
        "absoluteRuntime": false,
        // 默认 false,修改为 2
        "corejs": 2,
        "helpers": true,
        "regenerator": true,
        "useESModules": false
      }
    ]
  ]
}
最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容