通过参考网上的一些构建方法,当然也在开发过程中进行了一番实践,最终搭建了一套适用于当前多页应用的构建方案,当然该方案还处于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. lo口环路问题分析

    流程如下,collecter抓取网卡lo和wlan0数据,其中lo口无数据,wlan0是笔记本上网网口,然后按自定义协议把数据包通过lo口发给后端dispatch进行分发! 这种模式下,抓包程序每经过 ...

  2. 构建Docker镜像两种方式的比较-Dockerfile方式和S2I方式

    前言 写Dockerfile是构建Docker镜像最通常的方式,接触过Docker的童鞋多少了解一些.前段时间研究OpenShift(paas的一种),发现了另外一种构建Docker镜像的方式:S2I ...

  3. lseek 与 ioctl

    lseek : 每个打开的文件都记录着当前读写位置,打开文件时读写位置是0,表示文件开头,通常读写多少个字节就会将读写位置往后移多少个字节.但是有一个例外,如果以O_APPEND方式打开,每次写操作都 ...

  4. getprop 获取android系统属性

    Android属性系统 property_get/property_set  (很透彻)http://www.blogjava.net/MEYE/articles/359773.html getpro ...

  5. linxu安装方式

    安装Linux操作系统的5种方法以及心得这几天没有调别的东西,想起自己还不太会在没有安装光盘的时候安装Linux,于是试了一下Linux的五种安装方法,下面是我的一些 篇一:安装Linux操作系统的5 ...

  6. linux和shell关系

    坚持知识分享,该文章由Alopex编著, 转载请注明源地址: http://www.cnblogs.com/alopex/   索引: 什么是shell shell的分类 shell脚本的执行方式   ...

  7. [ CodeVS冲杯之路 ] P3038

    不充钱,你怎么AC? 题目:http://codevs.cn/problem/3038/ 按照题目给定的方法,一步步推下去,直到推到1就输出次数 至于-1的话,一开始想直接用数组判重,但是怕T掉,于是 ...

  8. 渗透协作工具 dradis centos安装

    https://dradisframework.com/ce/documentation/install_centos.html yum install rubygems 安装的bundle在drad ...

  9. XAudio2播放PCM

    XAudio2 是一个跨平台的API,在Xbox 360及Windows中得到支持.在Xbox 360上, XAudio2作为一个静态库编译到游戏可执行文件中.在Windows上,XAudio2提供一 ...

  10. 3.Centos-Docker-rancher

    1.安装mysql,设置密码 docker run -d --name mysqldb -e MYSQL_ROOT_PASSWORD=密码 mysql:latest --character-set-s ...