webpack4打包nodejs项目进阶版——多页应用模板

时间:2023-01-06 23:39:54

前段时间我写了个打包nodejs项目的文章,点击前往

但是,问题很多。因为之前的项目是个历史遗留项目,重构起来可能会爆炸,当时又比较急所以就写个的适用范围很小的webpack的打包方法。

最近稍微得空,便动了重构的心思,重构第一步当然要把架子搭起来webpack4打包nodejs项目进阶版——多页应用模板

而搭架子的过程也是十分地艰辛啊,终于大概搞定了前端的部分,这一次就分享一下使用最新的webpack4怎么打包nodejs的多页应用

欢迎大佬留言交流,想要源码的点此前往github

工程目录

走个流程先上个项目结构图

webpack4打包nodejs项目进阶版——多页应用模板

这里先说明一下,为什么除了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 来判断当前的环境(即生产环境、开发环境)

用法如下:

webpack4打包nodejs项目进阶版——多页应用模板

webpack4打包nodejs项目进阶版——多页应用模板

在package.json中设置启动命令

将 NODE_ENV 设置为不同的值

根据该值来判断当前的环境

Webpack.config.js

通常来说该文件就是webpack 的核心配置文件

但为降低不同环境的耦合度,使代码逻辑更加清晰

我使用这个文件作为一个“路由” 根据之前的 NODE_ENV 去请求不同的webpack配置文件

代码如下:

webpack4打包nodejs项目进阶版——多页应用模板

为了兼容VUE等框架所以我的ESlint 设为不以分号结尾

config文件夹

我所有的webpack配置文件夹都存放在该文件夹下

上方要获取的配置文件都在这里

webpack4打包nodejs项目进阶版——多页应用模板

我的想法是在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
}
}

关于上述代码

首先我们要配置的是入口:

webpack4打包nodejs项目进阶版——多页应用模板

我这里使用一个函数来遍历page文件夹中的所有html文件

webpack4打包nodejs项目进阶版——多页应用模板

这里我们约定对应html的js与html同名以便我们自动化生成入口对象

如下图所示:

webpack4打包nodejs项目进阶版——多页应用模板

这样我们就能使用html的名字来设置入口了,获取的入口对象如下:

webpack4打包nodejs项目进阶版——多页应用模板

该函数的另一个功能就是,根据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),若还是不懂参考我上一篇初级版的webpackwebpack4打包nodejs项目进阶版——多页应用模板

webpack4打包nodejs项目进阶版——多页应用模板

4 从另一个方面来说将ejs等文件改为html文件有利于搜索引擎优化(小声哔哔)

关于入口和html的问题就解答到这

下一步我们就应该配置出口了

话不多说先上代码:

webpack4打包nodejs项目进阶版——多页应用模板

如果是搞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

代码如下

webpack4打包nodejs项目进阶版——多页应用模板

webpack4打包nodejs项目进阶版——多页应用模板

说完了jq的问题然后就是配置不同文件的loader了

webpack4打包nodejs项目进阶版——多页应用模板

webpack4打包nodejs项目进阶版——多页应用模板

详细代码在我的github上,帮到你们的小伙伴,跪求星星

基础配置中还有一件事

那就是performance

webpack默认入口点文件不能超过300k

超过后webpack会报warning

没有强迫症的小伙伴可以跳过了

有两个解决办法:

1.关掉webpack的警告(一看就不能选)

2.设置performance

设置如下:

webpack4打包nodejs项目进阶版——多页应用模板

好了基本配置就完成了

接下来要针对,不同环境进行独立的配置

开发环境配置

我先讲开发环境的配置,生产环境的坑有点多放到最后讲

对于开发环境来说,代码会经常修改而且,我们需要频繁地查看样式,所以我们并不需要对文件进行压缩等处理

并且要让它能够热更新即可,这里我们使用webpack-server

配置代码如下:

webpack4打包nodejs项目进阶版——多页应用模板

这里没啥要注意的,直接按着配,run就行运行出来像下面这样

webpack4打包nodejs项目进阶版——多页应用模板

页面如下:

webpack4打包nodejs项目进阶版——多页应用模板

具体的我就不演示了

还是那句话github见

接下来开始重头戏生产环境的配置

生产环境

为啥是重头戏呢?生产环境那就是线上环境啊,效率、大小就是钱啊

另外呢,主要是webpack4 和 min-css的配合有点问题,我这搭架子的时候搞的我头皮发麻

我不太清楚这是bug还是我的操作有啥问题

好了,进入正题

关于生产环境,主要的配置是:

1.要能够删除之前的过期文件,手动删多low啊

2.要压缩代码,用webpack的目的是啥,除了构建自动化的前端工作流之外,最主要的目的无非是压缩代码嘛

压缩代码的好处我这里就不说了,网上一搜一堆

好了开搞

首先清理过期代码:

webpack4打包nodejs项目进阶版——多页应用模板

这一步就完成了

下一步抽离css样式

这里要说一下,webpack4中抽离css要使用

mini-css-extract-plugin

原来的那个在webpack4不能使用

这里我要吐槽一下官网给的示例,坑了我一下

webpack4打包nodejs项目进阶版——多页应用模板

这里的两个属性是类似域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 打包 nodejs 项目的架子,如果帮到你的小伙伴可以,关注我、收藏走一波

需要代码的小伙伴请移步github,原创不易,望支持

github链接

顺便再给我的JS高编读书笔记系列文章打个广告

有什么问题,欢迎留言,也欢迎大佬指正,共同进步。

webpack4打包nodejs项目进阶版——多页应用模板的更多相关文章

  1. webpack打包nodejs项目(前端代码)

    PS.若本文没有帮到你可以看看我的进阶版点此前往 适用情况 首先说明,此情况不具备普遍性.若你的情况与笔者类似那么希望这篇文章能够帮到你. 我的项目情况是这样的:用node.js做后台,ejs做模板引 ...

  2. Flask入门的第一个项目进阶版

    前言: 此次版本增加[一对多]数据库关系和动态路由设置. 一.数据库设计 environments表与variable1表的关系为:一对多.variable1.env_id设置为外键,与environ ...

  3. webpack4 打包优化

    1 参考文章 彻底解决 webpack 打包文件体积过大 webpack4提升180%编译速度 详解webpack4之splitchunksPlugin代码包分拆 webpack v4 中的断舍离 开 ...

  4. nodejs项目mysql使用sequelize支持存储emoji

    nodejs项目mysql使用sequelize支持存储emoji 本篇主要记录nodejs项目阿里云mysql如何支持存储emoji表情. 因由 最近项目遇到用户在文本输入emoji进行存储的时候导 ...

  5. 项目androidAnt编译打包Android项目

    时间紧张,先记一笔,后续优化与完善. Ant编译打包Android项目 在Eclipse中对Android项目停止编译和打包如果项目比较大的话会比较慢,所以改为Ant工具来停止编译和打包 Ant环境配 ...

  6. NodeJS项目迁移兼Ubuntu下NodeJS环境部署

    前言 之前做的几个项目都托管在阿里云服务器,但是最近要到期了.想着到底要不要续期,毕竟100/月.后面看着阿里云有个活动,800/三年.果断买下.环境部署折腾了一天,其中也遇到几个坑. 目录 一.安装 ...

  7. 手机端页面自适应解决方案—rem布局进阶版

    手机端页面自适应解决方案—rem布局进阶版   https://www.jianshu.com/p/985d26b40199 注:本文转载之处:https://www.cnblogs.com/anni ...

  8. 制作nodejs项目镜像,实现docker下的快速部署

    前言 前面的文章<centos7+ docker1.12 实践部署docker及配置direct_lvm>中,已经实践了如何在centos7下安装,配置docker, 所以接下来就打算去制 ...

  9. 手机端页面自适应解决方案—rem布局(进阶版,附源码示例)

    转自:https://segmentfault.com/a/1190000007350680 一年前笔者写了一篇 <手机端页面自适应解决方案—rem布局>,意外受到很多朋友的关注和喜欢.但 ...

随机推荐

  1. Linux安装ftp组件过程

    1   安装vsftpd组件 安装完后,有/etc/vsftpd/vsftpd.conf 文件,是vsftp的配置文件. [root@bogon ~]# yum -y install vsftpd 2 ...

  2. 【转】C&num;多线程示例

    using System; using System.Threading; namespace ConsoleThread { class ThreadApp { static int interva ...

  3. 基于&period;NET C&num;的 sqlite 数据库 ORM 【Easyliter】

    因为工作原因经常用到SQLITE数据库,但又找不到好用的ORM所以自个整理了一个简单好用的轻量极ORM框架:Easyliter 功能介绍: 1.支持SQL语句操作 2.支持 List<T> ...

  4. Genotype&amp&semi;&amp&semi;陨石的秘密

    Genotype: Genotype 是一个有限的基因序列.它是由大写的英文字母A-Z组成,不同的字母表示不同种类的基因.一个基因可以分化成为一对新的基因.这种分化被一个定义的规则集合所控制.每个分化 ...

  5. 如何让FPGA中的SPI与其他模块互动起来

    在上一篇文章<FPGA的SPI从机模块实现>中,已经实现了SPI的从机模块,如何通过SPI总线与FPGA内部其他模块进行通信,是本文的主要讨论内容. 一. 新建FPGA内部DAC控制模块 ...

  6. CentOS 删除自带的OpenJDK 和 安装SunJDK

    [root@WX32 local]# java -version java version "1.6.0" OpenJDK Runtime Environment (build - ...

  7. 如何生成可变表头的excel(转)

    1.实现功能: 传入一个表头和数据,将数据导入到excel中. 为了便于项目的扩展,数据传入通过泛型集合传入,获取数据时,通过反射的方式获取,这样无论你的表头是多少项,我都能很方便的生成.另外为了便于 ...

  8. Castle扩展Ibatis&period;Net

    使用Castle扩展Ibatis.Net,面向接口编程-更优雅的代码 使用Ibatis.Net做项目半年了,甚是喜欢,感觉确实是个简单.轻巧的O/R Mapping框架,特别是将Sql配置在Xml文件 ...

  9. UNIX环境高级编程——epoll函数使用详解

    epoll - I/O event notification facility 在linux的网络编程中,很长的时间都在使用select来做事件触发.在linux新的内核中,有了一种替换它的机制,就是 ...

  10. MySQL &middot&semi;InnoDB 文件系统之文件物理结构

    从上层的角度来看,InnoDB层的文件,除了redo日志外,基本上具有相当统一的结构,都是固定block大小,普遍使用的btree结构来管理数据.只是针对不同的block的应用场景会分配不同的页类型. ...