资讯专栏INFORMATION COLUMN

Webpack 配置详解(含 4)——关注细节

jsbintask / 999人阅读

摘要:由于这种差异我们将对预处理器文件的配置封装为函数,由参数生成对应配置,将文件放入文件内,将配置放在文件内。

前言

源代码

熟悉 webpack 与 webpack4 配置。

webpack4 相对于 3 的最主要的区别是所谓的零配置,但是为了满足我们的项目需求还是要自己进行配置,不过我们可以使用一些 webpack 的预设值。同时 webpack 也拆成了两部分,webpack 和 webpack-cli,都需要本地安装。

我们通过实现一个 vue 的开发模板(vue init webpack 模板,其实跟 vue 关系不太大)来进行一次体验。在配置过程中会尽量使用 webpack4 的相关内容。

本文不做 webpack 配置的完整介绍,着重介绍配置过程中需要注意的地方。查看代码注释阅读效果更佳,完整配置与详细注释可见源代码。配置位于 build 文件夹下。

与版本 4 相关的章节会添加符号 ④

需要注意的一点是,我们的 webpack 代码是运行在node环境下的,这部分代码可以使用 node api,但是我们的业务代码(src下)是无法使用 node api 的。

基本公用配置

由于 webpack 配置中的如 context,entry(chunk入口),output(输出)和 module.rules 中 loaders 的配置在开发模式和生产模式基本都是公用的,所以我们提取到 webpack.base.js 文件内,供复用。其中 output 部分如下:

output: {
  
  path: path.resolve(__dirname, "../dist/"), // 资源文件输出时写入的路径
  filename: "static/js/[name].[chunkhash].js", // 使用 chunkhash 加入文件名做文件更新和缓存处理
  chunkFilename: "static/js/[name].[chunkhash].js"
}

需要注意的有:

文件名 hash

hash 是用在文件输出的名字中的,如 [name].[hash].js,总的来说,webpack 提供了三种 hash:

[hash]:此次打包的所有内容的 hash。

[chunkhash]:每一个 chunk 都根据自身的内容计算而来。

[contenthash]:由 css 提取插件提供,根据自身内容计算得来。

三种 hash 的使用,我们在优化部分再讲,先优先使用 [chunkhash]

loader 优先级

loader 优先级需要注意两点,

同 test 配置内优先级:在同一个 test 下配置多个 loader ,优先处理的 loader 放在配置数组的后面,如对 less 处理,则:

{
  test: /.less$/,
  use: [
    "style-loader", 
    "css-loader", 
    "postcss-loader", 
    "less-loader"
  ]
}

不同 test 内优先级:如对 js 文件的处理需要两个 test 分别配置,使用 eslint-loaderbabel-loader ,但是又不能配置在一个配置对象内,可使用 enforce: "pre" 强调优先级,由 eslint-loader 优先处理。

{
  test: /.(js|vue)$/,
  loader: "eslint-loader",
  enforce: "pre",
},
{
  test: /.js$/,
  loader: "babel-loader"
}

css 预处理器配置

我们以 less 文件的 loader 配置 ["vue-style-loader", "css-loader", "postcss-loader", "less-loader"],使用 @import url(demo.less)为例:

less-loader 先处理 less 语法

postcss-loader 进行前缀添加等其他处理

css-loader 将内容引入 @import 所在的 css 文件内

vue-style-loader 将生成 style 标签,将 css 内容插入 HTML

vue-style-loader 功能类似 style-loader

但是由于 vue 中的单文件组件,又分为两种情况:

.vue 文件内的 style:
vue-loader 会对 .vue 单文件组件进行处理,对 .vue 单文件组件内的各种 lang="type" 我们可以在 vue-loader 的 options 配置不同的 loader,由于 vue-loader 内置了 postcss 对 css 进行处理,所以此处我们不需要再配置 postcss-loader

{
  test: /.vue$/,
  loader: "vue-loader",
  options: {
    loaders: {
      less: ["// xxx-loaders"],
      scss: ["// xxx-loaders"],
    }
  }
}

js 直接引入中引入样式文件:
如 main.js 中 import "demo.less",这种方式引入的样式文件,在 vue-loader 处理范围置之外,所以仍然需要配置 postcss-loader

由于这种差异我们将 对 css 预处理器文件的配置封装为函数,由 usePostCss 参数生成对应配置,将文件放入 utils.js 文件内,将 vue-loader 配置放在 vue-loader.js 文件内。

也就是对 css 预处理器的配置我们需要在 vue-loader 内和 webpack 内配置两遍。

写这篇 README.md 期间 vue-loader 发布了 v15 版,需要配合插件使用,不用再进行两遍配置

postcss-loader

postcss-loader 是一个强大的 css 处理工具,我们将 postcss 的配置拆分出去,新建 postcss.config.js 配置文件

module.exports = {
  plugins: {
    // 处理 @import
    "postcss-import": {},
    // 处理 css 中 url
    "postcss-url": {},
    // 自动前缀
    "autoprefixer": {
      "browsers": [
        "> 1%",
        "last 2 versions"
      ]
    }
  }
}

除了注释中列出的需要的功能插件,我们还可能会用到 nextcss(新的css语法的处理),px2rem/px-to-viewport 移动端适配相关的插件。

babel-loader

我们使用 babel 编译浏览器不能识别的 js、类 js 语法,如转义 ES6+、JSX等。同样将 babel-loader 的配置拆分出去,需要创建 .babelrc 并配置:

{
  "presets": [
    [
      /* *
       *  babel-preset-env
       *  可以根据配置的目标运行环境自动启用需要的 babel 插件。
       */
      "env", {
        "modules": false, // 关闭 babel 对 es module 的处理
        "targets": { // 目标运行环境
          "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
        }
      }
    ]
  ],
  "plugins": [
    "syntax-dynamic-import" // 异步加载语法编译插件
  ]
}
媒体资源 loader

我们还需要对图片、视频、字体等文件进行 loader 配置,以字体文件为例子,主要用到的是 url-loader:

{
  /**
   * 末尾 ?.* 匹配带 ? 资源路径
   * 我们引入的第三方 css 字体样式对字体的引用路径中可能带查询字符串的版本信息
   */
  test: /.(woff2|woff|eot|ttf|otf)(?.*)?$/,
  /**
   * url-loader
   * 会配合 webpack 对资源引入路径进行复写,如将 css 提取成独立文件,可能出现 404 错误可查看 提取 js 中的 css 部分解决
   * 会以 webpack 的输出路径为基本路径,以 name 配置进行具体输出
   * limit 单位为 byte,小于这个大小的文件会编译为 base64 写进 js 或 html
   */
  loader: "url-loader",
  options: {
    limit: 10000,
    name: "static/fonts/[name].[hash:7].[ext]",
  }
}
静态文件拷贝

直接引用(绝对路径)和代码执行时确定的资源路径应该是以静态文件存在的,这些资源文件不会经过 webpack 编译处理,所以我们将它们放在独立的文件夹(如 static)中,并在代码打包后拷贝到我们的输出目录,我们使用 copy-webpack-plugin 自动完成这个工作:

const CopyWebpackPlugin = require("copy-webpack-plugin")

// 在开发模式下,会将文件写入内存
new CopyWebpackPlugin([
  {
    from: path.resolve(__dirname, "../static"),
    to: "static",
    ignore: [".*"]
  }
])

此插件在拷贝文件过多时会崩溃,不知道解决了没有。

生产模式 production

我们先进行生产模式的配置。

添加 script 脚本命令

在 package.json 下添加

"scripts": {
  "build": "node build/build.js"`
}

那么使用 npm run build 命令就可执行 node build/build.js,我们不直接使用 webpack webpack.prod.config.js 命令去执行配置文件,而是在 build.js 中,做一些文件删除的处理,再启动 webpack。

创建 build.js 逻辑

主要是两个工作,引入 rimraf 模块删除 webpack 下之前产生的指定文件,启动 webpack,并在不同阶段给出不同的提示信息。

// 在第一行设置当前为 生产环境
process.env.NODE_ENV = "production"

const webpack = require("webpack")
const rm = require("rimraf")
const webpackConfig = require("./webpack.prod")
// 删除 webpack 输出目录下的内容,也可只删除子文件如 static 等
rm(webpackConfig.output.path, err => {
  // webpack 按照生产模式配置启动
  webpack(webpackConfig, (err, stats) => {
    // 输出一些状态信息
  })
}

更多细节见源代码注释

生产模式配置文件

新建 webpack.prod.js 文件,使用

const merge = require("webpack-merge") // 专用合并 webpack 配置的包
const webpackBaseConfig = require("./webpack.base")
module.exports = merge(webpackBaseConfig, {
  // 生产模式配置
})

合并基本配置和生产模式独有配置,然后我们开始进行生产模式下的 webpack 的配置信息的填写。

④ mode 预设

这是 webpack4 的新 api ,有三个预设值:developmentproductionnone,我们在生产模式选用mode: "production",webpack4在此配置下默认启用了:

插件

FlagDependencyUsagePlugin:应该是删除无用代码的,其他插件依赖

FlagIncludedChunksPlugin:应该是删除无用代码的,其他插件依赖

ModuleConcatenationPlugin:作用域提升 webpack3的scope hosting

NoEmitOnErrorsPlugin:遇到错误代码不跳出

OccurrenceOrderPlugin

SideEffectsFlagPlugin

UglifyJsPlugin:js代码压缩

process.env.NODE_ENV 的值设为 production

所以这些默认启用的内容我们不需要再配置。

最后一点设置 process.env.NODE_ENV 的值设为 production 其实是使用 DefinePlugin 插件:

new webpack.DefinePlugin({
  "process.env.NODE_ENV": JSON.stringify("production") 
})

从而我们可以在业务代码中通过 process.env.NODE_ENV,如进行判断,使用开发接口还是线上接口。如果我们需要在 webpack 中判断当前环境,还需要多带带的设置 process.env.NODE_ENV = "production",这也是我们在 build.js 中第一行做的事情。

添加 webpack 打出的 bundles 到 HTML 文件

我们使用 webpack 配置入口时只能配置 js 文件作为入口,webpack 打出的 bundles 并不能自动与我们项目的 HTML 文件发生关联。

需要我们手动添加(还可能包括后面提取出来的 css 文件)到 HTML 文件。

我们可以使用 html-webpack-plugin 插件自动完成这个工作。

当仅使用 webpack 对 js 进行打包,而没有 HTML文件需求时,不需要这一步。

const HtmlWebpackPlugin = require("html-webpack-plugin")
plugins: [
  new HtmlWebpackPlugin({
    filename: path.join(__dirname, "../dist/index.html"),// 文件写入路径
    template: path.join(__dirname, "../src/index.html"),// 模板文件路径
    inject: true // js 等 bundles 插入 html 的位置 head/body等
  })
]

如果不对 HtmlWebpackPlugin 进行配置,则其会创建一个 HTML 文件,其中 filename 在开发模式下还是比较重要的。

④ 提取 js 中的 css 部分到多带带的文件

使用过 webpack3 的同学应该对 extract-text-webpack-plugin 插件(以旧插件代称)比较熟悉,为了尝试webpack4,我并不想使用这个插件的 @next 版本,所以选择了新的替代插件 mini-css-extract-plugin(以新插件代称)。
与旧插件相同,同样需要在 webpack 的 loader 部分和 plugin 部分都进行配置,不同的是新插件提供了多带带的 loader,在 loader 部分与旧插件的配置方式不太相同。配置如下:

loader 部分

const MiniCssExtractPlugin = require("mini-css-extract-plugin")
// 
[
  {
    loader: MiniCssExtractPlugin.loader,
    options: {
      // (segmentfault 这儿的多行注释渲染有点问题            
               
                                           
                       
                 

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/107725.html

相关文章

  • webpack4配置详解之逐行分析

    摘要:今天就尝试着一起来聊聊吧,旨在帮大家加深理解新手更容易上路,都能从到搭建配置自定属于自己的脚手架,或对已封装好的脚手架有进一步的巩固,接下来苏南会详细讲解中的每一个配置字段的作用部分为新增。 showImg(https://segmentfault.com/img/bVbjmMV?w=1008&h=298); 前言   经常会有群友问起webpack、react、redux、甚至cre...

    dkzwm 评论0 收藏0
  • 支持多框架的组件库KPC 1.0正式发布

    摘要:自从年月份对外公布以来,已经经过了个月的迭代,期间发布了几十个正式版本,但一直没有到,因为我们觉得是个里程碑版本,我们必须做的足够完善才敢称之为。 自从17年11月份对外公布以来,KPC已经经过了8个月的迭代,期间发布了几十个正式版本,但一直没有到1.0,因为我们觉得1.0是个里程碑版本,我们必须做的足够完善才敢称之为1.0。而如今我们有信心对外宣布:KPC 1.0终于来了! 其实距离...

    刘厚水 评论0 收藏0
  • Webpack Loader 高手进阶(三)

    摘要:相关的内容为这样对于一个处理的第二阶段也就结束了,通过去拦截不同类型的,并返回新的,跳过后面的的执行,同时在内部会剔除掉,这样在进入到下一个处理阶段的时候,不在使用的范围之内,因此下一阶段便不会经由来处理。 文章首发于个人github blog: Biu-blog,欢迎大家关注~ Webpack 系列文章: Webpack Loader 高手进阶(一)Webpack Loader 高手...

    ivydom 评论0 收藏0
  • webpack引入eslint详解

    摘要:配置规则在文件中添加代码如下和是中规则的名称。安装,命令如下这个插件将会提醒模块脚本之间模拟浏览器共享全局变量的行为,因为这不适用于模块脚本。而用编译后的代码使用这款解析器可以避免不必要的麻烦。 webpack中eslint使用 首先,要使webpack支持eslint,就要要安装 eslint-loader ,命令如下: npm install --save-dev eslint-l...

    Lin_R 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<