通过参考网上的一些构建方法,当然也在开发过程中进行了一番实践,最终搭建了一套适用于当前多页应用的构建方案,当然该方案还处于draft版本,会在后续的演进过程中不断的优化。

个人觉得该方案的演进过程相对于成果而言更值得记录。但在此之前,我们先简单介绍下项目的整体架构,这样大家会更明白为什么要采用这样的构建方式。下图列出了用户在浏览器中输入url到页面ready的过程,可以看出这是一个典型的服务端直出架构,其中作为前端工程师的我们主要关注点是放在浏览器端以及Node层。在Node层,我们对koa的进行了封装,并采用了类似于eggjs的MVC架构,同时使用pug作为模板引擎,技术栈其实并不复杂。

V0.0.1:使用webpack对前端资源进行构建

在构建过程中要做什么事呢?相信不同的人有不同的见解:

  • 对静态资源进行压缩,减少传输字节;
  • 为避免浏览器读取了旧的缓存文件,需要为静态资源添加MD5戳;
  • 为CSS属性自动添加vendor prefix;
  • ......

上面所列出的事项也是我们在构建过程中所需要考虑的。在项目早期的构建方案中,我们选择使用webpack作为构建工具,原因其实很简单:功能强大、用的人多,所以一拍脑袋就选择它了。当然,webpack也确实不负众望,通过它,我们可以像写Node一样直接引入其他的文件,在使用前期确实给我们带来了很多的便捷。

但是我们的项目毕竟和webpack主流的使用场景,如React、Vue等项目还是有很大的不同之处,在使用webpack的过程中陆续出现了一些水土不服的地方,虽然都最后都通过一些方式解决了,但是这也促使我们在思考,是否有其他更合适的方案。

问题一:

如何让webpack打包所有资源文件?

解决方法:

webpack会将entry作为入口起点,找到所有依赖项并对其进行构建。由于webpack只认识JavaScript文件,所以对于非JavaScript文件需要使用loader将其转为webpack能够处理的模块。所以说,如果某个资源文件需要被webpack构建,那么这个资源文件必须是从entry可达的。对我们的项目来说,最理想的情况是以pug模板文件作为入口,但是由于pug模板文件需要的数据是从server获取的,而在构建阶段是不可知的,所以,只能退而求其次,使用JavaScript文件作为入口。

对于不同类型的文件,我们采用了不同的策略:

  • 对于图片,我们使用了webpack提供的require.context方法,它可以让我们使用正则的方式来引入相应的模块。所以我们添加了下述文件,并将其置于webpack的entry属性中。
require.context('./public', true, /\.*\.(jpg|png|jpeg|gif|ico)$/i)
  • 对于css文件,当然也可以采用上述的方式,但是这会将所有的css编译到一个文件中,导致生成的文件过大,从而使页面加载耗时较长。所以我们只能选择在相应的JavaScript文件中添加 import './xxxx.less' 来告诉webpack:你需要构建xxxx less/css文件了。这样做功能是实现了,但是却并不优雅,同时也会使JavaScript变得不纯粹,也没办法被其他不用webpack作为构建工具的项目所重用了。

问题二:

在Node从server获取数据后,会将pug模板渲染成html并将其发送回浏览器端,那么在模板中如何保持对静态资源的引用呢?因为构建工具会为所有的资源文件添加MD5戳,所以我们在编码时并不知道确切的文件名是什么。

解决方法:

利用webpack-manifest-plugin插件,它会在webpack构建完成后生成一个manifest.json文件,该文件会列出原始文件名与构建后的文件名之间的匹配关系。通过将manifest.json作为参数传入pug的render方法中,这样在pug模板中就可以通过类似于 img(src=manifest["logo.png"]) 的方式来保持对静态资源的引用。

V1.0.0-beta:使用gulp+webpack对前端资源进行构建

问题总是有方法解决的,但是这样写起来总有一些别扭,也感觉很不优雅,这致使我们思考webpack是否真的适用于我们的项目。通过一番讨论,最终决定尝试使用gulp+webpack的方式进行构建。

  1. 利用webpack构建JavaScript资源,可以方便的利用webpack模块的思想,使得JavaScript之间相互引用变得简单、便捷;
  2. 利用gulp构建css, images等其他资源。

使用webpack对JavaScript进行构建时,为了不至于每次添加一个新文件,都要修改webpack的配置,所以我们写了一个方法将所有的JavaScript都放入webpack的entry属性中。

function entries(globPath) {
const files = glob.sync(globPath);
let key, name, ext, entries = {}; for (let file of files) {
ext = path.extname(file);
name = path.basename(file, ext);
if (name.startsWith('_')) {
continue;
} entries[name] = path.join(__dirname, file);
}
return entries
}

使用时,只需要在globPath中输入指定的js路径就可以了,如

let webpackConfig = {
entry: entries('./public/**/*.js'),
}

而在实际的开发中我们发现,有一些JavaScript文件存在的目的就是被其他文件引用,例如一些helper方法,它们是不会作为webpack的entry存在的,所以我们在entries方法中只寻找不是以下划线(_)开头的JavaScript文件,因此对于一些只有helper方法的JavaScript文件,只需要将其文件名以_开头,这样就不会被添加到webpack的entry属性中了,这也算是该构建方案中的一个小彩蛋~~~

在使用gulp对其他资源的构建过程中,我们用到了很多成熟的gulp插件,其中我认为比较重要的是gulp-rev和gulp-rev-replace。其中gulp-rev插件用来为css、images等资源文件添加MD5戳并生成相应的manifest.json文件,同时搭配使用gulp-rev-replace插件将pug、css等文件中存在于manifest.json里的文件名进行替换,这样我们在pug模板中引用资源文件就可以直接写 img(src=logo.png) 而不需要像以前那么复(chou)杂(lou)了。

再说一个题外话,其实在使用gulp-rev+gulp-rev-replace插件之前,我们尝试使用过gulp-md5-plus插件。gulp-md5-plus插件使用起来超级方便,它可以添加MD5戳,也可以替换文件名,但是该插件暂不支持添加自定义前缀的功能,而这个功能对我们确实必须的。因为在生产环境中,所有的资源文件会放到CDN上,而测试环境中的资源文件则放在对应的测试服务器上,所以说,同样的一张图片,在测试环境的地址可能是public/images/logo.png,而生产环境却是//cdn.demo.com/images/logo.png,所以我们需要支持自定义前缀功能,并使用类似下述的代码为其添加前缀 var prefix = process.env.NODE_ENV === 'production' ? '//cdn.demo.com/': '/public/' 。

写在最后:

上述构建方案也许不是那么完美,但是对于当前的项目来说,确是一个相对而言较为合适的。当然也不排除后续该方案会被其他更优雅的方案所替代。但是通过这么多次的尝试、重构,才真正体会到了什么叫“适合的才是最好的

用gulp+webpack构建多页应用——记一次Node多页应用的构建过程的更多相关文章

  1. 做一个gulp+webpack+vue的单页应用开发架子

    1.目标 最近项目上的事情不多,根据我自己的开发习惯,决定开发一些简单的开发架子,方便以后事情多的时候直接套用.本文讲的一个gulp+webpack+vue的单页应用架子,想要达到的目的: 可以通过命 ...

  2. gulp & webpack整合

    为什么需要前端工程化? 前端工程化的意义在于让前端这个行业由野蛮时代进化为正规军时代,近年来很多相关的工具和概念诞生.好奇心日报在进行前端工程化的过程中,主要的挑战在于解决如下问题:✦ 如何管理多个项 ...

  3. nodejs+gulp+webpack基础知识

    nodejs+gulp+webpack基础知识 2019年08月22日 11:49:40 天府云创 阅读数 22   版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文 ...

  4. gulp + webpack + sass 学习

    笔记: new webpack.optimize.CommonsChunkPlugin 核心作用是抽离公共代码,chunks:['index.js','main.js'] 另一个作用就是单独生成一个j ...

  5. gulp+webpack+vue

    gulp+webpack+vue 章节目录 1.目标 2.实现 2.1合并库文件 2.2组织业务代码 2.3打包开发代码 2.4使用webpack-dev-server和热替换插件HotModuleR ...

  6. grunt,gulp,webpack前端打包工具的特性

    1.http://www.cnblogs.com/lovesong/p/6413546.html (gulp与webpack的区别) 2.http://blog.csdn.net/qq_3231263 ...

  7. 万能js实现翻页,动态生成内容自动翻页,兼容各种浏览器(已测试)----神器版!

    转--http://www.2cto.com/kf/201402/277535.html 万能js实现翻页,动态生成内容自动翻页,兼容各种浏览器(已测试)----神器版! 2014-02-11     ...

  8. C# 翻页设计:首页,上一页,下一页,末页 ,跳转

    int pageSize = 0; //每页显示行数 int nMax = 0; //总记录数 int pageCount = 0; //页数=总记录数/每页显示行数 int pageCurrent ...

  9. React + Node 单页应用「二」OAuth 2.0 授权认证 & GitHub 授权实践

    关于项目 项目地址 预览地址 记录最近做的一个 demo,前端使用 React,用 React Router 实现前端路由,Koa 2 搭建 API Server, 最后通过 Nginx 做请求转发. ...

随机推荐

  1. python 由递归的dict构建树的画图代码

    createPlot(mytree)方法实现. 其中myTree是一个字典,调用retrieveTree(0)可以获得一个字典的样式. Last login: Thu Feb 23 19:07:53 ...

  2. 深入探讨ui框架

    深入探讨前端UI框架 1 前言 先说说这篇文章的由来 最近看riot的源码,发现它很像angular的dirty check,每个component ( tag )都保存一个expressions数组 ...

  3. HDU 4455 Substrings ( DP好题 )

    这个……真心看不出来是个DP,我在树状数组的康庄大道上欢快的奔跑了一下午……看了题解才发现错的有多离谱. 参考:http://www.cnblogs.com/kuangbin/archive/2012 ...

  4. sql分页汇总-摘抄自网络

    文章:几种常见SQL分页方式效率比较 个人倾向于:(2005以上版本支持 row_number()) select * from ( select row_number()over(order by ...

  5. 01、dos命令行的常用命令

    cd 进入指定目录cd..  返回上一级目录cd\   退回盘符根目录dir        列出当前目录下的文件以及文件夹md       创建目录rd 删除目录del   删除文件cls       ...

  6. Hexo安装和配置

    Hexo安装和配置   1. Git安装和设置 github brew install git #Mac电脑使用brew安装 sudo apt-get install git #Ubuntu系统使用这 ...

  7. asp.net 条码 一维条码 生成, 一共有32中格式类型

    想用asp.net 实现条码功能,网上找了代码都不全. 终于找到了一个非常全的DLL 是winForm的,原以为不能用在WEB 上结果 可以使用. 首先,引用 DLL 不必说了,下面是 使用设置.还有 ...

  8. EXTJS4.0 显示图片 动态图片

    在网上找了好久没有好的解决方法 都是用 'box' 什么的组件 改写的 autoEl 好麻烦,修改 好的属性都不能用.弄了好久没弄成 最后: 用panel  显示 html 文本 追加 ‘'<i ...

  9. 洛谷 P2197 【模板】nim游戏 解题报告

    P2197 [模板]nim游戏 题目描述 甲,乙两个人玩Nim取石子游戏. nim游戏的规则是这样的:地上有n堆石子(每堆石子数量小于10000),每人每次可从任意一堆石子里取出任意多枚石子扔掉,可以 ...

  10. JAVA项目-嗖嗖移动

    /** * 移动卡类 */ public class MobileCard { private String cardNumber; //卡号 private String userName; //用 ...