使用Webpack打包时的"多页"实践

Webpack在”多页“开发中遇到的问题

在开发时我们经常使用Webpack官方提供的webpack-dev-server插件。我们只需要通过一个入口main.js和入口页面HTML,用webpack-dev-server就能够提供热更新“Live Reload”以及热替换“Hot Module Replacement”(即HMR) ,这很方便,但是在实际项目中我们遇到了更复杂的场景。

举例来说,我们现在的一个项目中不仅仅只是一个SPA应用了,它可能是由多个SPA应用构成的一个项目,每个SPA应用可能会由不同的人维护。因此它会有多个入口JS和入口HTML。由于没有提供唯一的入口js和html,仅仅使用webpack-dev-server的方案就行不通了。对于这种”多页应用“的项目,最好的方案是在开发时能通过路由切换到对应的SPA应用下,即对应的入口JS和HTML下,为此我们需要一些小技巧。

解决方案

其实在使用Webpack以前其实我们不会有这种烦恼,也许这就是“螺旋式上升”的必经之路。总之,我一开始能想到的方案有以下三种。

  1. 在开发模式下通过Gulp监听文件变化,然后直接使用Webpack打包出文件,用Gulp-Server处理路由。
  2. 任然使用webpack-dev-server,通过proxy代理另一个Server处理路由。
  3. 只起一个Server + WebpackMiddleware 在保留热更新和热替换的基础上,增加多路由。

三种方法各有利弊。在此我们选择第三种方式,通过独立Server我们能够更方便的处理Mock中Post请求以及Prox等问题,自由度更大。

server.png
server.png

图片中的multientry-dev-server就是接下来我们要创造的server。

Webpack Dev\Hot Middleware

官方提供的webpack-dev-server也只是一个用Express起的Server而已,其中使用了webpack-dev-middlewarewebpack-hot-middleware作为中间件提供Hot Module Replacement/Hot Reloading能力。Webpack Hot Middleware 必须要搭配Webpack Dev Middleware才能使用,因此同样的我们也可以用Express启动一个有热替换功能的服务器,webpack-dev-server只是做了一个简单的封装而已。

var http = require('http');
var express = require('express');
var path = require('path');
var webpackMiddleware = require('webpack-dev-middleware');
var webpackHotMiddleware = require('webpack-hot-middleware');

通过下面的代码建立Webpack的实例以及使用中间件

var app = express();
var compiler = webpack(webpackConfig);
var middleware = webpackMiddleware(compiler, {
  publicPath: webpackConfig.output.publicPath
});
app.use(middleware);
app.use(webpackHotMiddleware(compiler));

通过路由获取内存中的Webapck打包文件

使用webpack中间件打包并不会真正的生成文件,它会把文件载入到内存中。
为了能通过路由指定跳转到对应的入口JS和HTML,我们在需在项目中做一些约定。假设项目入口为apps目录,该目录下的每一个子目录对应一个SPA应用,在子目录中需要通过一个package.json指定该SPA应用的入口JS和这个SPA应用的其他信息,比如名字和子应用负责人等。

基础模板1.png
基础模板1.png

package.json文件格式类似如下:

{
  "name": "app1",
  "main": "./main.js",
  "author": "左伦"
}

middleware提供了middleware.fileSystem.readFileSync方法读取内存中的文件,文件的地址就是在webpack中配置的输出地址。

app.get('/:appName', function (req, res) {
  var result = '';
  var htmlPath = path.join(__dirname, webpackConfig.output.path + req.params.appName + '/index.html');
  console.log(htmlPath);
  try {
    result = middleware.fileSystem
      .readFileSync(htmlPath);
  } catch (err) {
    result = err.toString();
  }
  res.write(result);
  res.end();
});

OK, 至此便可以通过路由指定到对应App的入口。

Mock数据以及“首页”

由于是独立启动的Server,我们可以很方便的在Server中指定任意目录作为我们的静态目录,同时处理好对应的Post请求。

// 静态资源,Mock GET请求
app.use(express.static(path.join(__dirname, '../')));
// Mock POST请求
app.post('/api/*', function (req, res) {
  res.sendFile(path.join(__dirname, '../api', req.params[0]));
});

当越来越多的子App在项目中后,通过手动在浏览器中输入路由再进行跳转会显得十分麻烦。因此可以在Server中新增一个“首页”,列出当前项目下的所有子应用以及开发时的对应路由,具体实现并不难,通过遍历目录下每个应用中的Packge.json即可,最后效果如下:


屏幕快照 2016-12-11 下午7.11.20.png
屏幕快照 2016-12-11 下午7.11.20.png

终于不用每次在浏览器中敲地址了..
到目前为止,已经成功解决了Webpack在“多页“应用下的开发问题,接下来是时候更进一步了。

使用target指定入口应用

之前说到Webpack中间件在构建时会把文件都读取到内存中,但是当我们的项目越来越大的时候,项目下会有越来越多的子应用,这就造成了另一问题。有时我们只是在开发某一个子App下的代码,但是Webpack每次都会把整个项目打包进内存,非常浪费资源。因此这里我们可以在webpack的配置文件中做点文章,想办法只打包我们需要的目录下的JS。

在执行npm start的时候,通过在命令行里用
target=appName1,appName2 npm start
其中appName为你想要启动的应用名称(名称在package.json中定义),此时Webpack配置中的Entry只会包含target指定应用名下的入口JS和HTML,大大缩短了Webpack启动时间并且减少了内存占用。这个想法最初是在团队的另一位师兄的代码中看到的,十分巧妙,关键代码如下:

var targetEntries = process.env.target;
targetEntries = targetEntries ? targetEntries.split(',') : '';
  targetEntries.forEach(function (value) {
    console.log('应用: ', value);
    entryPath = path.join(viewsDir, value);
    entryJson = fse.readJsonSync(path.join(entryPath, '/package.json'));
    entryMap[value] = [path.resolve(path.join(entryPath, entryJson.main))];
    var appName = entryJson.name;
    var tplPath = path.join(entryPath, '/index.html');
    var conf = {
      template: tplPath,
      filename: path.join(appName, 'index.html'),
      inject: 'body',
      chunks: [appName]
    };
    htmlPluginsArr.push(new HtmlWebpackPlugin(conf));
  });
  _.extend(webpackConfig, {
    entry: entryMap,
    output: {
      path: path.join(__dirname, '../build/'),
      filename: '[name]/[name].min.js'
    },
    plugins: [
      new ExtractTextPlugin('[name]/[name].css'),
      new webpack.optimize.UglifyJsPlugin({
        compress: {
          warnings: false,
          drop_console: true
        }
      })
    ].concat(htmlPluginsArr)
  });

通过这样一个简单的服务,我们完全不用再使用Webpack-Dev-Server了,甚至我们也可以封装成一个相似的plugin。整个问题的思路和解决方案到此为止咯,如果大家有更好的想法可以补充。

文章首发于alisec-ued ,个人博客地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335

推荐阅读更多精彩内容

  • 写在开头 先说说为什么要写这篇文章, 最初的原因是组里的小朋友们看了webpack文档后, 表情都是这样的: (摘...
    Lefter阅读 5,260评论 4 31
  • 在现在的前端开发中,前后端分离、模块化开发、版本控制、文件合并与压缩、mock数据等等一些原本后端的思想开始...
    Charlot阅读 5,415评论 1 32
  • 构建一个小项目——FlyBird,学习webpack和react。(本文成文于2017/2/25) 从webpac...
    布蕾布蕾阅读 16,787评论 31 98
  • 特喜秦淮水,生爱江上船。 载君便捷去,打工又经年。 借问东园葵,独爱寂无言。 虽是无枝分,时向太阳偏。 幸嫁商人妇...
    悠游鱼阅读 294评论 3 2
  • 如果有人和你说:胡歌是渣男。你会怎么想? 当然是毫不犹豫一巴掌打过去,狠狠怼回去:连胡歌都敢黑?疯了? 很遗憾,总...
    简浅Jian阅读 460评论 0 5