前段时间我写了个打包 Node.JS 项目的文章, 点击前往
但是, 问题很多. 因为之前的项目是个历史遗留项目, 重构起来可能会爆炸, 当时又比较急所以就写个的适用范围很小的 webpack 的打包方法.
最近稍微得空, 便动了重构的心思, 重构第一步当然要把架子搭起来
而搭架子的过程也是十分地艰辛啊, 终于大概搞定了前端的部分, 这一次就分享一下使用最新的 webpack4 怎么打包 Node.JS 的多页应用
欢迎大佬留言交流, 想要源码的点此前往 GitHub https://github.com/lhy2813419591/webpackTemplate
工程目录
走个流程先上个项目结构图
这里先说明一下, 为什么除了 webpack.config.JS 这个配置文件之外还有一个 config 文件夹存放相关配置文件
因为 webpack 分为了开发环境和生产环境, 两者在配置和表现形式上有所区别, 放在一个文件中不利于维护
这也算是一种解耦吧.
至于其他的一些文件我这里就大概提一下:
1.babelrc 配置 babel-loader 用于将 ES6 + 的 JS 代码转为 ES5 的通用 JS
2.eslint 主要用于代码的在线纠错, 以及一些语法错误的查找
3. 用于 Git 的配置配置哪些文件需要上传到 Git
4. package.JSON 就是用于设置项目信息, 以及项目的依赖
5.postCSS 用于配置 postcss 主要用于修复浏览器兼容的问题
6, yarn 就是一个进阶版的 NPM 可以并行下载 缓存等(由 Facebook 研发)
以上就是整个架子的大概模板
接下来进入主题 --webpack 的相关配置
cross-env 跨平台设置环境变量
通过 cross-env 来判断当前的环境(即生产环境, 开发环境)
用法如下:
在 package.JSON 中设置启动命令
将 NODE_ENV 设置为不同的值
根据该值来判断当前的环境
Webpack.config.JS
通常来说该文件就是 webpack 的核心配置文件
但为降低不同环境的耦合度, 使代码逻辑更加清晰
我使用这个文件作为一个 "路由" 根据之前的 NODE_ENV 去请求不同的 webpack 配置文件
代码如下:
为了兼容 vue 等框架所以我的 ESlint 设为不以分号结尾
config 文件夹
我所有的 webpack 配置文件夹都存放在该文件夹下
上方要获取的配置文件都在这里
我的想法是在 base.JS 中存放两种环境的公共代码
dev.JS,prod.JS 存放对应环境的特殊配置代码
最后输出的文件只能有一个 webpack 的配置文件
所以使用
webpack-merge
来合并两个 webpack 配置文件
webpack 基础配置
下面我们来一 一分析每个配置文件
首先就是 base.JS
代码如下:
- /**
- * webpack 基础配置
- */
- const webpack = require('webpack')
- const path = require('path')
- const fs = require('fs')
- const Entries = {} // 保存文件入口
- const pages = []// 存放 html-webpack-plugin 实例
- const env = process.env.NODE_ENV !== 'prod' // 判断运行环境
- const MiniCssExtractPlugin = require('mini-css-extract-plugin') // 引入 mini-CSS-extract-plugin
- const HtmlWebpackPlugin = require('html-webpack-plugin');
- // 获取 HTML-webpack-plugin 实例集合
- (function () {
- let pagePath = path.join(__dirname, '../src/page')// 定义存放 HTML 页面的文件夹路径
- let paths = fs.readdirSync(pagePath) // 获取 pagePath 路径下的所有文件
- paths.forEach(page => {
- page = page.split('.')[0]// 获取文件名(不带后缀)
- pages.push(new HtmlWebpackPlugin({
- filename: `views/${page}.html`, // 生成的 HTML 文件的路径(基于出口配置里的 path)
- template: path.resolve(__dirname, `../src/page/${page}.html`), // 参考的 HTML 模板文件
- chunks: [page, '[name]', 'commons', 'vendors', 'manifest'], // 配置生成的 HTML 引入的公共代码块 引入顺序从右至左
- favicon: path.resolve(__dirname, '../src/img/favicon.ico'), // 配置每个 HTML 页面的 favicon
- minify: {// 配置生成的 HTML 文件的压缩配置
- collapseWhitespace: true,
- collapseInlineTagWhitespace: true,
- conservativeCollapse: true,
- minifyCSS: true,
- minifyJS: true,
- removeComments: true,
- trimCustomFragments: true
- }
- }))
- Entries[page] = path.resolve(__dirname, `../src/js/${page}.js`)// 入口 JS 文件
- })
- })()
- module.exports = {
- // 配置入口文件
- entry: Entries,
- // 启用 sourceMap
- devtool: 'cheap-module-source-map',
- // mode 为 none 表示这是默认配置
- mode: 'none',
- // 配置文件出口
- output: {
- // 将打包好的 JS 输出到 public(静态资源目录)下的 JS 文件夹中
- filename: 'public/js/[name].bundle.[hash].js',
- path: path.resolve(__dirname, '../dist'), // 输出目录, 所有文件的输出路径都基于此路径之上(需要绝对路径)
- publicPath: '../'
- },
- // 省略文件后缀
- resolve: {
- extensions: ['.js'] // 配置过后, 书写该类文件路径的时候可以省略文件后缀
- },
- // loader
- module: {
- rules: [
- // 使用 expose 处理 jQuery(JQ 使用 NPM 安装)配置了这一条后就不要使用 external(主要用于 cdn 引入)
- {
- test: require.resolve('jquery'), // 此 loader 配置项的目标是 NPM 中的 jQuery
- loader: 'expose-loader?$!expose-loader?jQuery' // 先把 jQuery 对象声明成为全局变量 `jQuery`, 再通过管道进一步又声明成为全局变量 `$`
- },
- // 处理 HTML 中的图片, 考虑到 node 使用模板的情况所以不能使用 HTML-loader
- {
- test: /\.HTML$/,
- use: [{
- loader: 'html-withimg-loader' // 处理 img 标签中的图片
- }]
- },
- // 处理样式表
- {
- test: /\.(sa|sc|c)ss$/,
- use: [
- env ? 'style-loader' : MiniCssExtractPlugin.loader,
- 'css-loader',
- 'postcss-loader',
- 'sass-loader'
- ]
- },
- {
- test: /\.(Less)$/,
- use: [
- env ? 'style-loader' : MiniCssExtractPlugin.loader,
- 'css-loader',
- 'postcss-loader',
- 'less-loader'
- ]
- },
- // 使用 babel 处理 JS 文件
- {
- test: /\.m?JS$/,
- exclude: /(node_modules|bower_components)/,
- use: 'babel-loader'
- },
- // 处理图片
- {
- test: /\.(PNG|jpg|gif|svg)$/,
- use: [{
- loader: 'url-loader',
- options: {
- limit: 10000, // 设置图像大小超过多少转存为单独图片
- name: 'public/img/[name].[hash].[ext]' // 转存的图片目录
- }
- }]
- },
- // 处理字体
- {
- test: /\.(woff|woff2|eot|ttf|otf)$/,
- use: ['url-loader']
- }
- ]
- },
- // 配置插件
- plugins: [
- // 分离 tml-webpack-plugin 实例数组, 引入 jq
- ...pages, new webpack.ProvidePlugin({
- $: 'jquery',
- jQuery: 'jquery',
- 'window.$': 'jquery',
- 'window.jQuery': 'jquery'
- })
- ],
- // 配置 webpack 执行相关
- performance: {
- maxEntrypointSize: 1000000, // 最大入口文件大小 1M
- maxAssetSize: 1000000 // 最大资源文件大小 1M
- }
- }
关于上述代码
首先我们要配置的是入口:
我这里使用一个函数来遍历 page 文件夹中的所有 HTML 文件
这里我们约定对应 HTML 的 JS 与 HTML 同名以便我们自动化生成入口对象
如下图所示:
这样我们就能使用 HTML 的名字来设置入口了, 获取的入口对象如下:
该函数的另一个功能就是, 根据 HTML 文件使用
HTML-webpack-plugin
来自动生成我们的 HTML 页面
看到这里或许有的小伙伴会有疑问, 为啥不用 HTML-loader 来解析 HTML 文件然后打包进去?
正好我也解答一下一些, node 项目中使用 ejs 等模板的小伙伴的疑问, 不是 HTML 怎么办?
原因如下:
1. 我这个架子主要考虑的是 node 项目, 通常来说 node 不管是做中间层, 还是做全栈都有可能会使用模板引擎
而 HTML-loader 无法解析 ejs 等模板语法
2. 以 ejs 来举例, 如果我使用 ejs-loader 来解析呢? 如果使用 ejs-loader 那么只能适用于用 ejs 做组件化开发的
情况, 而不能适用于使用 ejs 做数据渲染 (中间层) 的情况
3. 那么在已经是 ejs 等模板的情况下的 node 项目怎么使用我的架子呢?
答案很简单, 在 App.JS 中加入以下代码(express), 若还是不懂参考我上一篇初级版的 webpack
4 从另一个方面来说将 ejs 等文件改为 HTML 文件有利于搜索引擎优化(小声哔哔)
关于入口和 HTML 的问题就解答到这
下一步我们就应该配置出口了
话不多说先上代码:
如果是搞 node 的小伙伴应该知道 public(静态资源目录)
所以我将 webpack 打包后的文件输出到该目录下
关于 publicpath 我这里用的相对路径, 就是让 webpack-server 的项目根路径和我的静态资源文件一致 不然 run dev 的时候会 404
在这里提一下 jQuery 的问题, 目前来说 jq 有三种引入方式
1.cdn 引入
2.import 本地文件
3.expose-loader 暴露出 NPM 安装的 jQuery
这里我采用的是第三种方法
有几个好处
1. 在页面中不用显式地引入 jq 了 (懒是人类进步的第一生产力)
2. 使 jq 也纳入了 NPM 模块化管理的范畴
3. 前面两点足够了, emm
代码如下
说完了 jq 的问题然后就是配置不同文件的 loader 了
详细代码在我的 GitHub 上, 帮到你们的小伙伴, 跪求星星 https://github.com/lhy2813419591/webpackTemplate
基础配置中还有一件事
那就是 performance
webpack 默认入口点文件不能超过 300k
超过后 webpack 会报 warning
没有强迫症的小伙伴可以跳过了
有两个解决办法:
1. 关掉 webpack 的警告(一看就不能选)
2. 设置 performance
设置如下:
好了基本配置就完成了
接下来要针对, 不同环境进行独立的配置
开发环境配置
我先讲开发环境的配置, 生产环境的坑有点多放到最后讲
对于开发环境来说, 代码会经常修改而且, 我们需要频繁地查看样式, 所以我们并不需要对文件进行压缩等处理
并且要让它能够热更新即可, 这里我们使用 webpack-server
配置代码如下:
这里没啥要注意的, 直接按着配, run 就行运行出来像下面这样
页面如下:
具体的我就不演示了
还是那句话 GitHub 见 https://github.com/lhy2813419591/webpackTemplate
接下来开始重头戏生产环境的配置
生产环境
为啥是重头戏呢? 生产环境那就是线上环境啊, 效率, 大小就是钱啊
另外呢, 主要是 webpack4 和 min-CSS 的配合有点问题, 我这搭架子的时候搞的我头皮发麻
我不太清楚这是 bug 还是我的操作有啥问题
好了, 进入正题
关于生产环境, 主要的配置是:
1. 要能够删除之前的过期文件, 手动删多 low 啊
2. 要压缩代码, 用 webpack 的目的是啥, 除了构建自动化的前端工作流之外, 最主要的目的无非是压缩代码嘛
压缩代码的好处我这里就不说了, 网上一搜一堆
好了开搞
首先清理过期代码:
这一步就完成了
下一步抽离 CSS 样式
这里要说一下, webpack4 中抽离 CSS 要使用
mini-CSS-extract-plugin
原来的那个在 webpack4 不能使用
这里我要吐槽一下官网给的示例, 坑了我一下
这里的两个属性是类似域 output 中的同名属性的, 一般来说只用配置一个就行
另外可能就是这个插件有点 bug
我先说一下我希望达到的效果
我希望将每个 HTML 的所有 CSS 作为一个单独文件
最好再将 CSS 的重复代码提取一下
如果不将 CSS 提取成一个单独的文件就没法 CDN 加速了啊
但是问题来了没法提取公共 CSS 代码, 网上有的说用 Extractcss 那个插件的 @next 可以搞, 我试了一下只能不重复打包, 不能提取公共代码
我觉得人家既然专门为 webpack4 新出了一个, 应该是有过人之处的, 所以我就没有用这个方法
我就自己开始折腾, 我试着用那个提取 JS 重复代码的
splitChunks
我试了一下竟然可以处理 CSS, 但是有个问题, 生成的公共 CSS 没法自动引入 HTML 页面
因为 splitChunks 是处理 JS 的没法自动引入 CSS
如果实在有提取公共 CSS 需求的小伙伴, 页面又不多的情况(指你愿意手动引入)
不妨试试这种方法
主要步骤如下
在 spplitChunks 中创建缓存组过滤掉所有的 JS 文件
然后再建一个优先级很低的缓存组, 将剩下的文件中后缀为 CSS 的文件都强制提取到该组
用 enforce:true 就可以提取出来, 由于不是本文主题, 也不知道是不是个 bug, 感兴趣的小伙伴可以留言我私聊, 这里就不过多去讲了
继续来说, 我这提不提取公共 CSS 影响不大
所以我的代码如下:
- /**
- * 生产环境配置
- */
- const webpackBase = require('./webpack.config.base') // 引入基础配置
- const path = require('path')
- const MiniCssExtractPlugin = require('mini-css-extract-plugin') // 提取 CSS
- const webpackMerge = require('webpack-merge') // 引入 webpack-merge 插件
- const CleanWebpackPlugin = require('clean-webpack-plugin') // 清理 dist 文件夹
- // 合并配置文件
- module.exports = webpackMerge(webpackBase, {
- plugins: [
- new MiniCssExtractPlugin({// 提取出的 CSS 的相关配置
- filename: 'public/css/[name].[hash].css' // 文件存放路径
- }),
- new CleanWebpackPlugin(['dist'], {// 自动清理 dist 文件夹
- root: path.resolve(__dirname, '../'), // 根目录
- verbose: true, // 开启在控制台输出信息
- dry: false // 启用删除文件
- })
- ],
- optimization: {
- minimize: true,
- splitChunks: {// 配置提取公共代码
- chunks: 'all',
- minSize: 30000, // 配置提取块的最小大小(即不同页面之间公用代码的大小)
- minChunks: 3, // 最小共享块数, 即公共代码最少的重复次数一般设为 3
- automaticNameDelimiter: '.', // 生成的名称指定要使用的分隔符
- cacheGroups: {// 设置缓存组
- vendors: {
- name: 'vendors',
- test (module) {
- let path = module.resource
- return /[\\/]node_modules[\\/]/.test(path) || /[\\/]lib[\\/]/.test(path)
- },
- priority: 30
- },
- commons: {
- name: 'commons',
- test: /\.JS$/,
- enforce: true,
- priority: 20
- }
- }
- },
- runtimeChunk: {
- name: 'manifest' // 打包运行文件
- }
- }
- })
这里我为 JS 设置了两个缓存组, 并提取出了运行时的 manifest
一个是依赖的插件等 JS(满足 3 个页面引用)生成 vender.JS
不满足 3 个或自己写的 JS 提取到 commons.JS 中
结语
以上就是 webpack4 打包 Node.JS 项目的架子, 如果帮到你的小伙伴可以, 关注我, 收藏走一波
需要代码的小伙伴请移步 GitHub, 原创不易, 望支持
GitHub 链接 https://github.com/lhy2813419591/webpackTemplate
顺便再给我的 JS 高编读书笔记系列文章打个广告
有什么问题, 欢迎留言, 也欢迎大佬指正, 共同进步.
来源: https://www.cnblogs.com/lhyxq/p/10111096.html