扔掉 cli,webpack工程轻量化配置实战

更新日期: 2019-11-18阅读: 1.7k标签: cli

前言

之前有用 webpack4与babel7改造基于vue-cli2生成的工程模板,介绍文章在此。之后通过一些实践,除去了cli工具相对复杂的配置结构,提供轻量化版本的配置方案。之所以说是轻量化,是相对于Vue、react框架提供的官方cli工具而言的。并不是说这些cli工具不好,它们本身提供了开箱即用的良好特性,又集成了很多提升开发体验的插件,确实能降低框架使用的门槛。但也正是因为工具高度集成,配置高度抽象,导致生成的webpack配置文件结构略显复杂。对于一个技术选型已趋稳定的前端团队来说,比如一般只会使用一种css预处理方案,以及相对固定的插件集成,所以完全可以固定某些配置选项,从而输出一个轻量的工程配置方案。这也有利于灵活更改或升级某些依赖,从而进一步提升开发体验及输出性能。


明确需求

在开始配置之前,还是要明确自身的需求。对于前端应用来说,使用webpack作为工程化工具,我们希望除了静态文件server,babel编译等基本需求外,还能满足以下这些体验及要求:

开发阶段

  • 利用HMR实现模块热加载,包括css模块
  • 尽可能加快首次全量编译及rebuild速度
  • 在编译结果中能清晰看到生成的bundle包及chunk文件

生产输出

  • 压缩优化输出的js、css包,以及其他静态资源
  • 合理配置hash,减少非必要更新
  • 合理配置分包策略,控制输出包的大小及数量


配置方案

本文是以Vue框架应用为例,应用示例用的就是vue-cli生成的工程模板,可以clone 代码仓库 到本地运行。React技术栈的同学也请留步,webpack的配置方案基本是一致的,无非就是babel配置有差异。简化后的方案只在build目录下有3个配置文件:webpack.base.js, webpack.dev.js, webpack.prod.js。其中 dev与prod 分别是开发和生产构建的webpack config文件,在package.json中可以配置如下npm scripts用于开发和生产构建:

"scripts": {
    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.js",
    "build": "cross-env NODE_ENV=production webpack --config build/webpack.prod.js"
}

两个文件都继承了webpack.base.js,此文件包含了主要配置项,下面就方案的关键配置做下注解。

webpack.base.js

环境变量、Helper方法

  • devMode 变量用于区分开发与生产构建
  • resolve 方法用于拼接绝对路径,base目录为工程根目录
const devMode = process.env.NODE_ENV !== 'production';

function resolve (dir) {
  return path.join(__dirname, '..', dir)
}

入口和上下文

  • 设定上下文为工程根目录
  • 入口为 src 目录下的 main.js,此处路径相对于上下文。可以视项目实际情况配置多个入口。
context: resolve(''),
entry: {
    app: './src/main.js'
}

输出 output

  • path指定输出目录为dist,所有构建生成的资源都放入此目录
  • filename 命名输出的bundle文件,这里根据devMode区分,生产构建会加上对应bundle的chunkhash ,这里不用 hash,是需要精确控制每个bundle的缓存,以满足我们对于生产环境加载优化的需求。尤其是多入口的情况下,如果只更新其中一个业务bundle,其他bundle的hash不变,从而长效利用缓存,减少不必要的资源下载。 [name] 会被入口的命名替换,该项目即为app。
  • chunkFilename 命名输出的chunk文件,上面的filename是用来控制入口对应的bundle文件,而这个主要是用于分包(splitChunks)以及懒加载等产生的chunk文件,也使用chunkhash。 [name] 会被分包定义的名称替换。
  • 此处输出的bundle文件前面加上了目录js,后面的 css、图片等资源也对应加上了目录,这可以根据项目规范来定,没有强制约定。不管是否有前置目录,都是相对于 output.path目录的。
  • publicPath也是一个关键配置,会影响到按需加载或外部资源加载,特别是css中引用的图片、字体等资源,如果设置不正确,有可能导致无法加载,受限篇幅与主题,就不展开讲了。
output: {
  path: resolve('dist'),
  filename: devMode ? 'js/[name].js' : 'js/[name].[chunkhash].js',
  chunkFilename: devMode ? 'js/[name].js' : 'js/[name].[chunkhash].js',
  publicPath: '/',
}

解析 resolve

主要设置模块如何被解析,包括别名配置,可以提升一定的构建效率,具体看配置,不做展开。

optimization: 分包配置 splitChunks

根据项目规模和资源加载性能要求进行合理的分包配置,其中涉及到的参数与原理都会相对复杂,本文也不做展开。本方案里给到的是可以满足中小型应用的通用策略:分出 common包与vendor包。

  • common包是针对于多入口应用的,本示例工程其实不起效。对于多入口应用,提取共同依赖模块到common包,可以减小每个业务bundle的size,同时也能利用缓存的优势,优化加载性能。还可以通过minChunks等参数进一步控制分包粒度。
  • vendor包是将所有在node_modules中的第三方模块全部打包成一个chunk。这样做的一个好处是,相对于业务bundle,第三方依赖的变化频度较低,chunkhash可以稳定较长时间。但如果你的项目持续集成是每次重新全量安装构建,那此策略的效果不会很好,因为npm依赖更新频度太高了(主要是minor和patch版本升级,除非是锁定版本)。如果单个包过大,也可以通过maxSize等参数进一步拆分。
optimization: {
  splitChunks: {
    chunks: 'async',
    name: true,
    cacheGroups: {
      common: {
        name: 'common',
        chunks: 'initial',
        minChunks: 2
      },
      vendor: {
        name: 'vendor',
        test: /[\\/]node_modules[\\/]/,
        chunks: 'all'
      }
    }
  }
}

module.rules

模块处理规则配置,使用恰当的loader处理各种模块。

  • 因为是vue应用,所以需要使用vue-loader处理.vue文件 。loader的配置基本取自vue-cli,具体看源码,需要详细了解的可以查看官方文档
  • js 模块使用babel-loader进行处理,babel的选项可以在.babelrc或package.json中的babel节点单独配置。注意一下 userBuiltIns及corejs的配置,由于babel编译配置不是本文重点,也不再展开。
"presets": [
  [
    "@babel/preset-env",
    {
      "modules": false,
      "targets": {
        "browsers": [
          "> 1%",
          "last 2 versions",
          "not ie <= 8"
        ]
      },
      "useBuiltIns": "usage",
      "corejs": 3
    }
  ]
],
  • css 模块,根据项目使用的预处理方案配置 test及预处理 loader,然后是css-loader,最后是用MiniCssExtractPlugin.loader将代码提取到css文件中,需配合下面的plugins配置。生成的css文件命名规则与output配置类似,注意生产构建用的是该插件提供的contenthash,也是利用缓存优化。mini-css-extract-plugin已经支持了内置 HMR,开发阶段启用hmr option即可,在这之前还需要使用css-hot-loader来配合实现css的 HMR。其原理也比较简单, 就是通过重新加载生成的css文件进行样式覆盖。另外,如果是纯前端应用(见下节解释),开发阶段也可以考虑使用style-loader来实现 HMR。
{
  test: /\.(sa|sc|c)ss$/,
  use: [
    {
      loader: MiniCssExtractPlugin.loader,
      options: {
        hmr: devMode,
      },
    },
    'css-loader',
    // 'postcss-loader',
    // 'sass-loader',
  ],
},
  • 图片、字体、其他媒体资源等直接用url-loader处理,配置limit选项可优化请求数,另外命名直接带上hash。

插件 plugins

  • vue应用需引入 vue-loader/lib/plugin。
  • 上文提到的MiniCssExtractPlugin配置
MiniCssExtractPlugin({
  filename: devMode ? 'css/[name].css' : 'css/[name].[contenthash].css',
  chunkFilename: devMode ? 'css/[id].css' : 'css/[id].[contenthash].css',
}),
  • 由于本示例应用是纯前端应用(引用资源的入口是html文件),所以还要使用HtmlWebpackPlugin插件来处理入口html文件的资源引用注入修改,我们无需关心不同环境构建输出的资源包路径命名差异,插件会自动注入资源引用代码。但如果不是纯前端应用,比如使用了 Node应用框架或其他后端语言框架的模板,则需要考虑如何引入输出后的资源路径了,但具体的实践可能需要另开话题分享了。

webpack.dev.js

webpack.dev.js 是开发环境的webpack config 文件,使用webpack-merge继承 webpack.base.js ,内容主要是开发阶段所需的一些特定配置。

  • mode设为development ,会默认使用DefinePlugin设置process.env.NODE_ENV值为development,可在源码中获取区分环境用;另外还会默认启用几个开发阶段所需的插件。
  • devtool 用于指定source map格式,此示例值为cheap-module-eval-source-map,首次构建与重新构建速度相对较快,以及支持行级别的源码映射品质,是开发阶段的推荐格式。
  • devServer主要是webpack-dev-server的配置,包括host,端口,启用hmr、gzip压缩等配置。这里使用了portfinder解决开发环境可能的端口冲突,可以根据实际情况决定是否使用。另外使用copy-webpack-plugin插件替代contentBase,可以用于非 webpack 编译处理的静态资源伺服访问。
  • devServer.stats 可以配置终端中编译输出的显示信息,以下配置的搭配可以看到每次编译后的bundle、chunk包列表,又不会有大串的编译过程信息干扰,具体可以根据实际需求进行调整。
  devServer: {
    stats: {
      colors: true,
      builtAt: true,
      cached: true,
      cachedAssets: true,
      modules: false,
      children: false
    }
  }

webpack.prod.js

webpack.prodjs 是生产环境的webpack config 文件,也使用webpack-merge继承了 webpack.base.js 的主要配置。在这里设置mode值为 production,同样使用DefinePlugin设置process.env.NODE_ENV值为production,使用TerserPlugin等插件对生产构建输出包进行压缩优化。另外引入optimize-css-assets-webpack-plugin插件,对输出的css bundle也做优化处理。
也可以使用 babel-minify-webpack-plugin插件进一步压缩经babel编译的代码,但从实际使用来看,此插件带来的压缩效果并不明显,编译耗时倒是增加不少,可以看具体情况来决定是否使用。

小结

至此,轻量版本的webpack工程配置已经完成。可以拉取仓库代码在本地运行体验,工程效果基本是与 vue-cli 生成版本一致的,不过对比vue-cli 的整个应用工程配置, 去除了单测、e2e测试,以及编译错误桌面提醒等插件的集成,可以根据实际需求再行配置。


配置工具化

对于需要维护多个项目的团队,为了能让webpack工程配置能尽可能通用,可以考虑将配置方案封装成一个npm包,抽象部分配置项作为可变参数,比如publicPath,一般是不同应用不同值。在应用里可以使用webpack-merge,对一些配置进行覆盖或增加,会有更好的灵活性。就如文章开头所提,一个团队内的模块处理方案基本是统一的,所以无需抽象过多配置项,否则又走回 cli 工具的老路了。


结语

本文从实战出发,提供了一个相对普适的轻量化webpack工程配置方案。受篇幅所限,没有对一些配置项做过多解释,如果需要了解某个配置细节,可以查询相关文档或文章。有兴趣的同学也可以阅读webpack源码作深入了解。

原文:https://segmentfault.com/a/1190000021047381

链接: https://www.fly63.com/article/detial/6492

采用vue-cli搭建一个Vue.js项目工程

Vue很优雅,没太多废话和周折,代码漂亮,思路清晰,大赞!上手比较快。Vue.js 因其性能、通用、易用、体积、学习成本低等特点已经成为了广大前端们的新宠。

使用nodejs编写命令行工具_编写自己的cli工具

编写自己的cli工具,一行命令,3秒钟进入coding状态!看完本文,你将学会如何从零开发一个cli项目,如何上传到github库,以及如何使用npm发布自己的包。

vue-cli e2e测试_运行 npm run e2e报错解决

vue init webpack 项目名字创建项目时,就可以选择单元测试,运行npm run e2e进行e2e单元测试了,结果发现出现很多错误,下面就总结下如何解决这些问题?

vue-cli3.0脚手架的使用_vue-cli3.0搭建与配置(vue.config.js)

vue-cli致力于将 Vue 生态中的工具基础标准化。它确保了各种构建工具能够基于智能的默认配置即可平稳衔接,这样你可以专注在撰写应用上,而不必花好几天去纠结配置的问题。与此同时,它也为每个工具提供了调整配置的灵活性,无需 eject

Vue-cli 3.0配置反向代理

vue-cli 3.0版本,配置代理Proxy,在项目根目录下新建vue.config.js,它是一个可选的配置文件,新建该文件,存放在项目根目录(将自动加载)中。配置代理如下:

改造vue-cli,使用mockjs搭建mock server

最近准备开发一款web应用,考虑到可能会有前后端并行开发的场景,所以决定使用mockjs做mock server。浏览官网文档时发现没有跑在webpack上的例子,索性自己找方法解决。当前端工程师需要独立于后端并行开发时,后端接口还没有完成,那么前端怎么获取数据?

在vue-lic脚手架中安装mockjs,实现前后端分离开发

在项目开发前期,前端开发中,页面布局基本开发完毕,但是后台还接口还没有开发完,等待后台开发完接口,在进行接口联调,浪费了等待时间,也压缩的测试的时间

vue-cli3 DllPlugin 提取公用库

vue 开发过程中,保存一次就会编译一次,如果能够减少编译的时间,哪怕是一丁点,也能节省不少时间。开发过程中个人编写的源文件才会频繁变动,而一些库文件我们一般是不会去改动的。如果能把这些库文件提取出来,就能减少打包体积,加快编译速度。本文主要讲述在 vue-cli3 中利用 DllPlugin 来进行预编译。

Angular CLI 使用教程指南参考

要安装Angular CLI你需要先安装node和npm,然后运行以下命令来安装最新的Angular CLI:注意:Angular CLI 需要Node 4.X 和 NPM 3.X 以上的版本支持。

vue-cli中使用jquery

在webpack.base.conf.js里加入(新版的可能找不到这个文件,你可以npm install webpack --save-dev进行手动安装),在module.exports的最后加入,在main.js 引入,新版直接在main.js 引入

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!