详解使用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 相关文章推荐
Array, Array Constructor, for in loop, typeof, instanceOf
Sep 13 Javascript
JS中typeof与instanceof之间的区别总结
Nov 14 Javascript
JavaScript中的数值范围介绍
Dec 29 Javascript
实现无刷新联动例子汇总
May 20 Javascript
jQuery实现立体式数字动态增加(animate方法)
Dec 21 Javascript
Angular.js去除页面中显示的空行方法示例
Mar 30 Javascript
vue-cli中的webpack配置详解
Sep 25 Javascript
在Vue项目中用fullcalendar制作日程表的示例代码
Aug 04 Javascript
vue+elementui 对话框取消 表单验证重置示例
Oct 29 Javascript
重置Redux的状态数据的方法实现
Nov 18 Javascript
微信小程序实现打卡签到页面
Sep 21 Javascript
vue实现简易计算器功能
Jan 20 Vue.js
使用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和ACCESS写聊天室(九)
2006/10/09 PHP
PHP 数字左侧自动补0
2008/03/31 PHP
php addslashes及其他清除空格的方法是不安全的
2012/01/25 PHP
一个php生成16位随机数的代码(两种方法)
2014/09/16 PHP
PHP通过反射动态加载第三方类和获得类源码的实例
2015/11/27 PHP
php文档工具PHP Documentor安装与使用方法
2016/01/25 PHP
php模拟post提交请求调用接口示例解析
2020/08/07 PHP
PHP实现简易图形计算器
2020/08/28 PHP
Javascript实例教程(19) 使用HoTMetal(6)
2006/12/23 Javascript
jQuery插件开发全解析
2012/10/10 Javascript
JavaScript通过正则表达式实现表单验证电话号码
2014/03/07 Javascript
node.js中的fs.lstatSync方法使用说明
2014/12/16 Javascript
node.js中的fs.futimesSync方法使用说明
2014/12/17 Javascript
javascript html5移动端轻松实现文件上传
2020/03/27 Javascript
AngularJS控制器之间的数据共享及通信详解
2016/08/01 Javascript
Javascript6中字符串的四个新用法分享
2016/09/11 Javascript
JavaScript实现Java中Map容器的方法
2016/10/09 Javascript
jQuery dateRangePicker插件使用方法详解
2017/07/28 jQuery
Vue + Vue-router 同名路由切换数据不更新的方法
2017/11/20 Javascript
解决vue 按钮多次点击重复提交数据问题
2018/05/10 Javascript
Vue.js项目实战之多语种网站的功能实现(租车)
2019/08/07 Javascript
[01:07:13]TNC vs Pain 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/20 DOTA
Python运行报错UnicodeDecodeError的解决方法
2016/06/07 Python
使用Python读取二进制文件的实例讲解
2018/07/09 Python
python版飞机大战代码分享
2018/11/20 Python
python3 BeautifulSoup模块使用字典的方法抓取a标签内的数据示例
2019/11/28 Python
Python logging模块原理解析及应用
2020/08/13 Python
Django contrib auth authenticate函数源码解析
2020/11/12 Python
详解canvas绘制网络字体几种方法
2019/08/27 HTML / CSS
介绍一下ICMP(Internet Control Message Protocol)Internet控制信息协议
2016/11/26 面试题
介绍一下Java中的static关键字
2012/05/12 面试题
镇班子对照检查材料思想汇报
2014/09/24 职场文书
2019年个人工作总结范文(3篇)
2019/08/27 职场文书
Node-Red实现MySQL数据库连接的方法
2021/08/07 MySQL
MySQL 1130异常,无法远程登录解决方案详解
2021/08/23 MySQL
在MySQL中你成功的避开了所有索引
2022/04/20 MySQL