传统的多页面网站(MPA)能否用webpack打包?

对于多页面网站, 我们最多的是用Grunt或Gulp来打包, 因为这种简单的页面对模块化编程的需求不高. 但如果你喜欢上使用import来引入库, 那么我们仍然可以使用webpack来打包.

MPA意味着并没不是一个单一的html入口和js入口, 而是每个页面对应一个html和多个js. 那么我们可以把项目结构设计为:

  1. ├── dist
  2. ├── package.json
  3. ├── node_modules
  4. ├── src
  5. ├── components
  6. ├── libs
  7. | ├── favicon.png
  8. | ├── vendor.js 所有页面公用的第三方库
  9. └── pages 页面放这里
  10. | ├── foo 编译后生成 http://localhost:8100/foo.html
  11. | | ├── index.html
  12. | | ├── index.js
  13. | | ├── style.css
  14. | | └── pic.png
  15. | └── bar http://localhost:8100/bar.html
  16. | ├── index.html
  17. | ├── index.js
  18. | ├── style.css
  19. | └── baz http://localhost:8100/bar/baz.html
  20. | ├── index.html
  21. | ├── index.js
  22. | └── style.css
  23. └── webpack.config.js

这里每个页面的index.html是个完整的从<!DOCTYPE html>开头到</html>结束的页面, 这些文件都要用html-webpack-plugin处理. index.js是每个页面的业务逻辑, 全部作为入口js配置到entry中. 页面公用的第三方库仍然打包进vendor.js. 这里我们需要用glob库来把这些文件都筛选出来批量操作.

  1. npm install glob --save-dev

webpack.config.js修改的地方:

  1. // ...
  2. const glob = require('glob')
  3. module.exports = (options = {}) => {
  4. // ...
  5. const entries = glob.sync('./src/**/index.js')
  6. const entryJsList = {}
  7. const entryHtmlList = []
  8. for (const path of entries) {
  9. const chunkName = path.slice('./src/pages/'.length, -'/index.js'.length)
  10. entryJsList[chunkName] = path
  11. entryHtmlList.push(new HtmlWebpackPlugin({
  12. template: path.replace('index.js', 'index.html'),
  13. filename: chunkName + '.html',
  14. chunks: ['manifest', 'vendor', chunkName]
  15. }))
  16. }
  17. return {
  18. entry: Object.assign({
  19. vendor: './src/vendor'
  20. }, entryJsList),
  21. // ...
  22. plugins: [
  23. ...entryHtmlList,
  24. // ...
  25. ]
  26. }
  27. }

代码在examples/mpa目录.

其他问题

为什么不使用webpack.config.babel.js

部分同学可能知道webpack可以读取webpack.config.babel.js, 它会先调用babel将文件编译后再执行. 但这里有两个坑:

  1. 由于我们的package.json中的babel配置指定了modules: false, 所以babel并不会转码import, 这导致编译后的webpack配置文件仍然无法在node.js中执行, 解决方案是package.json不指定modules: false, 而在babel-loader中的options中配置babel. 这样webpack.config.babel.js会使用package.json的babel配置编译, 而webpack编译的js会使用babel-loader指定的配置编译.
  1. {
  2. test: /\.js$/,
  3. exclude: /node_modules/,
  4. use: [
  5. {
  6. loader: 'babel-loader',
  7. options: {
  8. presets: [
  9. ['env', {
  10. loose: true,
  11. modules: false
  12. }],
  13. 'stage-2'
  14. ]
  15. }
  16. },
  17. 'eslint-loader'
  18. ]
  19. }
  1. postcss的配置不支持先用babel转码, 这导致了我们的配置文件格式的不统一.

综上, 还是只在src目录中的文件使用ES6模块规范会比较方便一点.

总结

通过这篇文章, 我想大家应该学会了webpack的正确打开姿势. 虽然我没有提及如何用webpack来编译Reactvue.js, 但大家可以想到, 无非是安装一些loader和plugin来处理jsxvue格式的文件, 那时难度就不在于webpack了, 而是代码架构组织的问题了. 具体的大家自己去摸索一下. 以后有时间我会把脚手架整理一下放到github上, 供大家参考.

几个脚手架