webpack基础篇 – Webpack 与 Webpack-CLI 入门指南

webpack 介绍

Webpack 可以直接执行 webpack 命令进行打包,也可以通过手动配置实现更复杂的打包需求。配置文件为 webpack.config.js,可以导出一个对象或函数,函数接受两个参数:env(环境对象)和 argv(描述传递给 webpack 选项的 map 对象)。

来看一下这两个参数:

  // 在 webpack.config.js 中输入以下代码
  module.exports = function(env, argv) {
    console.log(env, argv)
  }
  // 在控制台执行
  webpack --env=chengyuming

参数

  • webpack正常启动,至少需要两个npm包,webpack、webpack-cli

一、entry

Webpack 采用模块化思想,所有文件和配置都是模块。entry 是顶层入口,Webpack 从入口开始,遍历和递归解析所有入口依赖的模块。

  • entry用法如下

entry

  • 当然也可以采用动态配置entry, 采用箭头函数动态返回。

动态entry

  • 关于entry, 我们要记住, 他有多种配置类型, 而且可以动态配置, 还可以为入口设置别名呐。
  • 也可以引用定义三方模块的打包,配置如下
  entry: {
    app: path.resolve('./src/main.js'),
    // 配置公共文件
    vender: ['./src/jquery.js']
  },

二、output

  • output是一个对象,里面包含一些输出配置项
  • 常用的语法如下:
  output: {
    // 打包后的位置,必须是绝对地址
    path: path.resolve('./dist'),
    // 打包后的文件名,[name] 对应 entry 的名字
    filename: 'js/[name].[chunkHash].js',
    // 打包后静态资源的位置,cdn 的配置
    publicPath: '.'
  },

三、mode

webpack有三种modedevelopmentproductionnone

  • development:会将process.env.NODE_ENV的值设置为development,启用NamedChunksPluginNamedModulesPlugin。代码不压缩
  • production:会将DefinePluginprocess.env.NODE_ENV的值设置为production。启用FlagDependencyUsagePlugin,FlagIncludedChunksPlugin,ModuleConcatenationPlugin,NoEmitOnErrorsPlugin,OccurrenceOrderPlugin,SideEffectsFlagPluginTerserPlugin
  • none:不使用任何默认优化。
  • 如果没有设置,webpack会把mode默认设置为production,并发出警告。
  • 我们可以通过控制台中输入--mode=development来设置webpack的模式,也可以在webpack.config.js中设置mode: development
  • 设置NODE_ENV并不会自动地设置mode
  • 如下,一个简单的 web 打包机就配置成功了,编译、优化就是modulespluginsoptimization配置的事情,配置规则在基础篇已经介绍过了
const path = require('path');
module.exports = function(env, argv) {
  return {
    mode: argv.mode || 'development',
    entry: path.resolve('./src/main'),
    output: {
      path: path.resolve('./dist'),
      filename: '[name].[hash].js',
    },
  };
};

四、module

配置 loader

配置loader就是配置一堆规则,所以要放在module.rules中,先看一段代码

  module: {
    rules: [{
      test: /\.js$/,
      include: path.resolve(__dirname, 'src'),
      // use可以是普通字符串数组,也可以是对象数组
      use: ['babel-loader?cacheDirectory'],
      use: [{
        loader: 'babel-loader',
        options: {
            cacheDirectory: true, //
        },
        enforce: 'post'
      }]
    },
    {
      test: /\.scss$/,
      use: ['style-loader', 'css-loader', 'sass-loader'],
      exclude: path.resolve(__dirname, 'node_modules')
    },
    {
      // 对非文本文件采用 file-loader 加载
      test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
      use: ['file-loader'],
    },
      //配置更多的其他loader
    ]
  }

属性说明

  1. test/include/exclude:表示匹配到loader的文件或者文件范围;
  2. use: 表示使用什么loader, 它可以是一个字符串数组, 也可以是对象数组, 多个loader时, 执行顺序是从右向左, 当然, 也可以使用enforce强制让某个loader的执行顺序放到最前面或者最后面;
  3. cacheDirectory:表示传给babel-loader的参数, 用于缓存babel的编译结果, 加快编译速度。
  4. enforcepost表示将该 loader的执行顺序放到最前面,pre则相反.
  5. 多个loader时处理顺序:从后到前,即先交给sass-loader处理, 再将结果交给css-loader, 最后交给style-loader:::

配置 noParse

noParse可以用于让webpack忽略哪些没有采用模块化的文件, 不对这些文件进行编译处理, 这样做可以提高构建性能, 因为例如一些库:如jquery本身是没有采用模块化标注的, 让webpack去解析这些文件即耗时, 也没什么意义。

  module: {
    rules: [],
    noParse: /jquery/,
    noParse: content => {
      return /jquery/.test(content);
    }
  }
  • noParse的值可以是正则表达式, 也可以是一个函数;
  • 被忽略的文件里不应该包含import、require、define等模块化语句, 不然会导致在构建出的代码中包含无法在浏览器环境下执行的模块化语句

配置 parser

因为Webpack是以模块化的JavaScript文件为入口的,所以内置了对模块化JavaScript的解析功能,支持AMD,CommonJSSystemJSES6parser属性可以更细粒度地配置哪些模块语法被解析、哪些不被解析。同noParse配置项的区别在于,parser可以精确到语法层面,而noParse只能控制哪些文件不被解析。

  module: {
    rules: [
      test: /\.js$/,
      use: ['babel-loader'],
      parse: [
        amd: false,                  // 禁用AMD
        commonjs: false,              // 禁用 CommonJS
        system: false,                // 禁用 SystemJS
        harmony: false,              // 禁用 ES6 import/export
        requireinclude: false,        // 禁用require.include
        requireEnsure: false,         // 禁用require.ensure
        requireContext: false,        // 禁用require.context
        browserify: false,            // 禁用
        browserify requireJs: false,  // 禁用 requirejs: false, //禁用requirejs
      ]
    ]
  }
  • parsenoParse同级的属性,当然也可以嵌套到rules,表示针对于某个loader应用到该属性的规则。
  • 目前只要明白parse属性,是用于声明哪些模块语法被解析,哪些不被解析即可。

单个规则配置多个 loader, 语法需要使用 use, 如图

  {
    test: /\.(sa|sc|c)ss$/,
    use: [
      {
        loader: MiniCssExtractPlugin.loader,
        options: {
          // publicPath: '../',
          reloadAll: true
        }
      },
      'css-loader',
      'postcss-loader',
      'sass-loader'
    ]
  },

oneOf

每个文件对于 rules 中所有规则都会遍历一遍,使用 oneOf 就可以解决该问题,只要匹配一个即可退出(在 oneOf 中不能两个配置处理同一种类型文件)

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        // 优先执行
        enforce: 'pre',
        loader: 'eslint-loader',
        options: {
          fix: true
        }
      },
      {
        // 以下 loader 只会匹配一个
        oneOf: [
          ...,
          {},
          {}
        ]
      }
    ]
  }
}

五、resolve

resolve配置webpack去寻找模块对应的文件,我们平常通过import导入的模块,resolve可以告诉webpack如何去解析导入的模块

1. alias:配置路径别名

  resolve: {
    alias: {
      '@': path.join(__dirname, '..', 'src')
    }
  }

2. extensions:用于配置模块文件的后缀列表

用来配置文件可以不写后缀名

resolve: {
  extension: ['.js', '.json', '.vue', '.jsx'];
}

3. modules

resolve.modules配置的webpack默认会去node_modules目录下寻找,假如项目中遇到一些模块大量依赖和导入由于 其他模块 的位置不定, 针对不同的文件都要计算被导入的模块文件的相对路径,这个路径 有时会很长,例如:就像import ······ '../../../components/module-a.js',这时可以利用modules配置项优化。假如那些被大量导入的模块都在./src/components目录下,则将modules配置成这样

resolve: {
  modules: ['./src/components', 'node_modules'];
}
// 然后引入模块就会去这两个目录中寻找
import moduleA from 'module-a'; // module-a.js 在 ./src/components 目录下

此时我们就可以简单的通过import ······ 'module-a'导入
注意:modulesalisa的区别:modules是用来配置一些公共模块,这些公共模块和node_modules类似,配置以后,我们就可以直接引用模块,前面不需要再加路径,而alias作用是配置路径别名,目的是可以让路径简化。两者是不一样的。
除此之外,还有:

  1. descriptionFiles:配置描述第三方模块的文件名称:默认是package.json
  2. enforceExtension:配置后缀名是否必须加上

六、plugin

  • plugins其实包括webpack本身自带的插件,也有开源的其他插件,都可以使用,它的作用就是解决loader之外的其他任何相关构建的事情。
  • plugin的值是一个数组,可以传入多个插件实例,用法是直接new一个插件然后传入相应的配置即可
  • plugin如何配置并不是难点,难点是我们需要清楚常用的一些插件分别解决了什么样的问题,以及这些插件的配置项
plugins: [
  // 处理vue
  new VueLoaderPlugin(),
  // 分离css,只需要传入相应的配置即可
  new MiniCssExtractPlugin({
    filename: 'css/[name].[hash].css',
    chunkFilename: 'css/[id].[hash].css',
  }),
];

七、optimization

optimization很明显是优化代码的,比如说我们的压缩代码,就是在这里面配置,从webpack4开始,如果modeproduction模式会默认压缩代码,具体可查看官网文档

  optimization: {
    // 简单配置压缩代码
    minimize: true,
    // 分离代码
    splitChunks: {
      chunk: 'all',
      cacheGroups: {
        libs: {
          name: 'chunk-libs',
          test: /[\\/]node_modules[\\/]/,
          priority: 10,
          chunks: 'initial' // 只打包初始时依赖的第三方
        },
        vue: {
          name: 'vue',
          test: /[\\/]node_modules[\\/]vue[\\/]/,
          priority: 11,
        },
        vueRouter: {
          name: 'vue-router',
          test: /[\\/]node_modules[\\/]vue-router[\\/]/,
          priority: 12,
        }
      }
    }
  }

八、devServer

devServer主要用于本地开发的时候,配置本地服务的各种特性,常用的配置如下

  • hot:是否开启模块热替换
  • inline:  是否开启实时刷新, 即代码更改以后, 浏览器自动刷新
  • contentBase: 用于配置本地服务的文件根目录
  • header: 设置请求头
  • host: 设置域名
  • port:  设置端口
  • allowedHosts: 只有请求的域名在该属性所配置的范围内, 才可以访问。
  • https:  使用使用 https 服务, 默认为 false
  • compress:  是否启用 Gzip 压缩, 默认为 false.
  • open: // 是否开启新窗口
  • devtool: 配置 webpack 是否生成 source Map, 以方便调试。
  • watch: 默认为 true, 表示是否监听文件修改以后, 自动编译。

附上一个简单的配置

const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = function(env, argv) {
  return {
    mode: argv.mode || 'development',
    entry: path.resolve('./src/main'),
    output: {
      path: path.resolve('./dist'),
      filename: 'js/[name].[chunkHash].js',
      publicPath: '.',
    },
    module: {
      rules: [
        // 使用MiniCssExtractPlugin插件后的配置
        {
          test: /\.(sa|sc|c)ss$/,
          use: [
            {
              loader: MiniCssExtractPlugin.loader,
              options: {
                reloadAll: true,
              },
            },
            'css-loader',
          ],
        },
      ],
    },
    plugins: [
      // 配置HTML
      new HTMLWebpackPlugin({
        // 源文件模板
        template: path.resolve('./public/index.html'),
        // 输出文件
        filename: 'index.html',
        title: 'fecym',
        inject: true,
        hash: true,
        showErrors: true,
      }),
      // 分离css
      new MiniCssExtractPlugin({
        filename: 'css/[name].[hash].css',
        chunkFilename: 'css/[id].[hash].css',
      }),
    ],
    optimization: {
      minimize: true,
      runtimeChunk: {
        name: entryPoint => `runtime~${entryPoint.name}`,
      },
    },
  };
};

webpack 一些概念

一、module,chunk 和 bundle

  1. 对于同逻辑代码,当我们手写下一个个文件,他们都是module
  2. 当我们写的module源文件传到webpack进行打包时,webpack会根据文件引用关系生成chunk文件,webpack会对这个chunk文件进行一些操作;
  3. webpack处理好chunk文件后,最后会输出bundle文件,这个bundle文件包含了经过加载和编译的最终源文件,所以它可以直接在浏览器中运行。

总结:module、chunk、bundle其实就是同一份逻辑代码在不同转换场景下取了不同的名字:我们直接写出来的是modulewebpack处理时是chunk,最后在浏览器中可以直接运行的是bundle

  • module:我们手写的文件。
  • chunk:Webpack 根据文件引用关系生成的中间文件。
  • bundle:Webpack 处理后输出的最终文件,可直接在浏览器中运行。

二、filename 和 chunkFilename

  1. filename是一个很常见的配置,就是对应于entry里面的输入文件,经过webpack打包后输出文件的文件名;
  2. chunkFilename指未被列在entry中,却又需要被打包出来的chunk文件的名称。一般来说:这个chunk文件指的是要懒加载的代码(可以在output里面配置)
  3. 总结:filename指列在entry中,打包后输出的文件的名称;chunkFilename指未被列在entry中,却又需要被打包出来的文件的名称

三、预请求和预加载

  1. preload chunk会在父chunk加载时,以并行方式开始加载。prefetch chunk会在父chunk加载结束后开始加载。preload chunk具有中等优先级,并立即下载。prefetch chunk在浏览器闲置时下载。
  2. preload chunk会在父chunk中立即请求,用于当下时刻。prefetch chunk会用于未来的某个时刻。
  3. 总结:webpackChunkName是为预加载的文件取别名。webpackPrefetch会在浏览器闲置时下载文件,webpackPreload会在父chunk加载时并行下载文件。

四、hash、chunkHash 和 contentHash

哈希一般是结合CDN缓存来使用的。如果文件内容改变的话, 那么对应文件哈希值也会改变, 对应的HTML引用的URL地址也会改变, 触发CDN服务器从源服务器上拉取对应数据, 进而更新本地缓存。

  • hash:计算是跟整个项目的构建相关,所有的文件共享一个hash,一个文件发生改变,其他都会改变,hash值是一个。
  • chunkHash:跟hash差不多。区别在于 chunkHash所有公共库的代码文件都用一个hash,公共库的代码用一个hash,当我们更新普通模块内容的时候,其他模块的hash值发生改变,但是公共模块的hash不会受到影响
  // 简单配置如下
  {
    entry: {
      app: path.resolve('./src/main.js'),
      // 配置公共文件
      vender: ['./src/jquery.js']
    },
    output: {
      path: path.resolve('./love'),
      filename: 'js/[name].[chunkHash].js'
    },
  }
  • contenthash:所有的文件的hash都是不一样的,哪个文件被改变,哪个文件的hash发生改变。index.jsindex.css同为一个chunk,如果index.js内容发生变化,但是index.css没有变化,打包后他们的hash都发生变化,这对css文件来说是一种浪费。contentHash将根据资源内容创建出唯一hash,也就是说内容不变,hash不变
  • 总结:hash要变一起变;chunkhash公共内容hash不发生改变,其他一起变;contenthash哪个文件内容发生变化,那么对应的hash才发生改变

五、webpack 指纹占位符

webpack 中有一些指纹占位符,比如以下配置:

new MiniCssExtractPlugin({
  filename: 'css/[name].[hash].css',
  chunkFilename: 'css/[id].[hash].css',
}),

中括号中[name].[hash],其实就是一些占位符,其中 name 代表文件名称,hash 代码构建时生成的 hash 值,下面是完整的占位符表示

占位符名称含义
name文件名称
ext资源后缀名
path文件的相对路径
folder文件所在的文件夹
hash每次构建时生成一个唯一的 hash 值
chunkhash根据 chunk 生成 hash 值,来源于同一个 chunk,则 hash 值就一样
contenthash根据内容生成 hash 值,文件内容相同 hash 值就相同

六、path、publicPath、contentBase

output.publicPath 和 devServer.publicPath

  • output.publicPath:引用资源的前缀。
    • 就是项目要扔到服务器的哪个地址里面
  • devServer.publicPath:静态文件的位置。
    • 表示的是打包生成的 静态文件 所在的位置
    • 配置了devServer.publicPath之后,打完包的项目会自动在请求地址上加上配置的那个文件夹名字
    • 比如说配置了address_v2,那么此时请求的所有文件资源都是携带这个address_v2,也就是说在你的请求地址最后面要加上address_v2,那么在服务器上也需要放这么一个文件夹,用来放置你打包完的项目
  • devServer.contentBase:本地服务根目录。

七、webpack 处理 css 的一些介绍

css loader (包括前处理器和后处理器)

  • css 基础 loader
    • css-loader 和 style-loader
  • css 前处理 less 两件套
    • less 和 less-loader
  • css 前处理 sass 两件套
    • node-sass 和 sass-loader
  • css 后处理 postcss 两件套
    • postcss-loader 和 autoprefixer
    • postcss 需要配置, 需要一个 postcss.config.js 配置文件
module.exports = {
  plugins: [require('autoprefixer')],
};

本内容基于 Webpack 4.x 版本的配置

本篇到此基本也就告一段落了,下一篇介绍 webpack 常用配置以及可能遇到的坑

声明:来自ENJOY前端,仅代表创作者观点。链接:https://eyangzhen.com/3437.html

ENJOY前端的头像ENJOY前端

相关推荐

关注我们
关注我们
购买服务
购买服务
返回顶部