详解使用webpack构建多页面应用


Posted in Javascript onDecember 21, 2017

关于webpack的配置和使用,网上已经有许多文章了,大多是在讲单页应用,当我们需要打包多个html时,事情就变得麻烦起来。怎么在webpack-dev-server里使用路由?怎么打包多个html和js chunk并自动更新md5?本文讲的就是如何解决这些问题。

这里假设你对Webpack已经有最基础的了解

需求

来看下我们的需求:

  1. 使用webpack-dev-server做开发时的服务器
  2. 在webpack-dev-server里使用路由,访问/a时候显示a.html,/b显示b.html
  3. 打包成多个html,给其中引用到资源加md5戳

主要目录结构

├── src            
│  └── views         # 每一个文件夹对应一个页面
│    └── a         
│      └── index.js
│    └── b         
│      └── index.js
├── output          # 打包输出的目录
|  └── ...
└── template.html       # 将根据这个模版,生成各个页面的html
└── webpack.config.js
└── dev-server.js       # webpack-dev-server + express

只列出了主要的目录,这里我们根据一个template.html来生成多个页面的html,他们之间只有引用的资源路径不同。当然,你也可以每个页面单独使用一个html模版。

Webpack 配置

这里主要解决两个小问题。

1. 打包多个页面的js文件

读取src/views下的目录,约定每一个目录当成一个页面,打包成一个js chunk。

2. 打包多个html

循环生成多个HtmlWebpackPlugin插件,把每一个插件的chunks各自指向上面打包的js chunk。

// webpack.config.js
var glob = require('glob');

var webpackConfig = {
  /* 一些webpack基础配置 */  
};

// 获取指定路径下的入口文件
function getEntries(globPath) {
   var files = glob.sync(globPath),
    entries = {};

   files.forEach(function(filepath) {
     // 取倒数第二层(view下面的文件夹)做包名
     var split = filepath.split('/');
     var name = split[split.length - 2];

     entries[name] = './' + filepath;
   });

   return entries;
}
    
var entries = getEntries('src/view/**/index.js');

Object.keys(entries).forEach(function(name) {
  // 每个页面生成一个entry,如果需要HotUpdate,在这里修改entry
  webpackConfig.entry[name] = entries[name];
  
  // 每个页面生成一个html
  var plugin = new HtmlWebpackPlugin({
    // 生成出来的html文件名
    filename: name + '.html',
    // 每个html的模版,这里多个页面使用同一个模版
    template: './template.html',
    // 自动将引用插入html
    inject: true,
    // 每个html引用的js模块,也可以在这里加上vendor等公用模块
    chunks: [name]
  });
  webpackConfig.plugins.push(plugin);
})

路由配置

在多页应用下,我们希望访问的是localhost:8080/a,而不是localhost:8080/a.html。

由于webpack-dev-server只是将文件打包在内存里,所以你没法在express里直接sendfile('output/views/a.html'),因为这个文件实际上还不存在。还好webpack提供了一个outputFileStream,用来输出其内存里的文件,我们可以利用它来做路由。

注意,为了自定义路由,你可能需要引进express或koa之类的库,然后将webpack-dev-server作为中间件处理。

// dev-server.js
var express = require('express')
var webpack = require('webpack')
var webpackConfig = require('./webpack.config')

var app = express();

// webpack编译器
var compiler = webpack(webpackConfig);

// webpack-dev-server中间件
var devMiddleware = require('webpack-dev-middleware')(compiler, {
  publicPath: webpackConfig.output.publicPath,
  stats: {
    colors: true,
    chunks: false
  }
});

app.use(devMiddleware)

// 路由
app.get('/:viewname?', function(req, res, next) {
  
  var viewname = req.params.viewname 
    ? req.params.viewname + '.html' 
    : 'index.html';
    
  var filepath = path.join(compiler.outputPath, viewname);
  
  // 使用webpack提供的outputFileSystem
  compiler.outputFileSystem.readFile(filepath, function(err, result) {
    if (err) {
      // something error
      return next(err);
    }
    res.set('content-type', 'text/html');
    res.send(result);
    res.end();
  });
});

module.exports = app.listen(8080, function(err) {
  if (err) {
    // do something
    return;
  }
  
  console.log('Listening at http://localhost:' + port + '\n')
})

最后,在package.json里定义下启动命令:

// package.json
{
  scripts: {
    "dev": "node ./dev-server.js"  
  }
}

运行 npm run dev,然后在浏览器访问localhost:8080/各个页面,你应该可以看到想要的结果。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
AJAX使用了UpdatePanel后无法使用alert弹出脚本
Apr 02 Javascript
javascript 文本框水印/占位符(watermark/placeholder)实现方法
Jan 15 Javascript
javascript中的previousSibling和nextSibling的正确用法
Sep 16 Javascript
JavaScript 闭包详细介绍
Sep 28 Javascript
Jquery UI实现一次拖拽多个选中的元素操作
Dec 01 Javascript
zTree jQuery 树插件的使用(实例讲解)
Sep 25 jQuery
详解使用create-react-app添加css modules、sasss和antd
Jul 31 Javascript
Javascript三种字符串连接方式及性能比较
May 28 Javascript
vue 实现websocket发送消息并实时接收消息
Dec 09 Javascript
Vue移动端实现图片上传及超过1M压缩上传
Dec 23 Javascript
vue element-ui实现input输入框金额数字添加千分位
Dec 29 Javascript
浅析JavaScript预编译和暗示全局变量
Sep 03 Javascript
使用ajax的post同步执行(实现方法)
Dec 21 #Javascript
jQuery Validate插件ajax方式验证输入值的实例
Dec 21 #jQuery
原生js+cookie实现购物车功能的方法分析
Dec 21 #Javascript
JS实现去除数组中重复json的方法示例
Dec 21 #Javascript
解析vue中的$mount
Dec 21 #Javascript
vue中使用refs定位dom出现undefined的解决方法
Dec 21 #Javascript
js中bool值的转换及“&&”、“||”、 “!!”详解
Dec 21 #Javascript
You might like
PHP动态页生成静态页的3种常用方法
2014/11/13 PHP
php实现的农历算法实例
2015/08/11 PHP
PHP实现的装箱算法示例
2018/06/23 PHP
用js生产批量批处理执行命令
2008/07/28 Javascript
jquery select(列表)的操作(取值/赋值)
2011/03/16 Javascript
javascript中注册和移除事件的4种方式
2013/03/20 Javascript
js使用DOM操作实现简单留言板的方法
2015/04/10 Javascript
JS使用遮罩实现点击某区域以外时弹窗的弹出与关闭功能示例
2018/07/31 Javascript
vuejs选中当前样式active的实例
2018/08/22 Javascript
Vue中Axios从远程/后台读取数据
2019/01/21 Javascript
vue生命周期与钩子函数简单示例
2019/03/13 Javascript
微信小程序登录session的使用
2019/03/17 Javascript
vue实现标签云效果的方法详解
2019/08/28 Javascript
Vue 的双向绑定原理与用法揭秘
2020/05/06 Javascript
vue cli4.0项目引入typescript的方法
2020/07/17 Javascript
再谈Python中的字符串与字符编码(推荐)
2016/12/14 Python
用Python写王者荣耀刷金币脚本
2017/12/21 Python
python绘制散点图并标记序号的方法
2018/12/11 Python
pandas 数据结构之Series的使用方法
2019/06/21 Python
python解析多层json操作示例
2019/12/30 Python
Python object类中的特殊方法代码讲解
2020/03/06 Python
selenium WebDriverWait类等待机制的实现
2020/03/18 Python
Python发送邮件封装实现过程详解
2020/05/09 Python
django 连接数据库出现1045错误的解决方式
2020/05/14 Python
8款使用 CSS3 实现超炫的 Loading(加载)的动画效果
2015/03/17 HTML / CSS
网页切图的CSS和布局经验与要点
2015/04/09 HTML / CSS
逻辑链路控制协议
2016/10/01 面试题
中学教师师德师风演讲稿
2014/08/22 职场文书
2014年项目工作总结
2014/11/24 职场文书
饭店服务员岗位职责
2015/02/09 职场文书
2015年禁毒宣传活动总结
2015/03/25 职场文书
大学生团支书竞选稿
2015/11/21 职场文书
2016年10月份红领巾广播稿
2015/12/21 职场文书
Java集成swagger文档组件
2021/06/28 Java/Android
MySQL选择合适的备份策略和备份工具
2022/06/01 MySQL
Windows server 2012 NTP时间同步的实现
2022/06/25 Servers