我的回答

entry中增加多个html入口, 在html-webpack-plugin中设置对应的多个html模板和配置

参考回答

Vue-cli 多页面配置

单页应用(SPA)往往只含有一个主入口文件与index.html,页面间切换通过局部刷新资源来完成。而在多页应用中,我们会为每个html文档文件都指定好一个JS入口,这样一来当页面跳转时用户会获得一个新的html文档,整个页面会重新加载。

vue-cli可以配置vue.config.js的pages选项,实现多页面应用开发

  1. module.exports = {
  2. pages: {
  3. index: {
  4. // page 的入口(必选项,除此之外就是可选项)
  5. entry: "src/index/main.js",
  6. // 模板来源
  7. template: "public/index.html",
  8. // 在 dist/index.html 的输出
  9. filename: "index.html",
  10. // 当使用 title 选项时,
  11. // template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>
  12. title: "Index Page",
  13. // 在这个页面中包含的块,默认情况下会包含
  14. // 提取出来的通用 chunk 和 vendor chunk。
  15. chunks: ["chunk-vendors", "chunk-common", "index"],
  16. },
  17. // 当使用只有入口的字符串格式时,
  18. // 模板会被推导为 `public/subpage.html`
  19. // 并且如果找不到的话,就回退到 `public/index.html`。
  20. // 输出文件名会被推导为 `subpage.html`。
  21. subpage: "src/subpage/main.js",
  22. },
  23. };

1.1 初始化项目

通过vue-cli脚手架初始化一个默认工程,修改项目目录

  1. ├── assets
  2. └── logo.png
  3. ├── components
  4. ├── About.vue
  5. ├── HelloWorld.vue
  6. └── Home.vue
  7. ├── pages
  8. ├── page1
  9. ├── page1.html
  10. ├── page1.js
  11. └── page1.vue
  12. └── page2
  13. ├── page2.html
  14. ├── page2.js
  15. └── page2.vue
  16. └── style
  17. ├── common.css
  18. └── common.less

vue.config.js是一个可选文件,用户需要自行创建,他会被@vue/cli-service读取。当正确添加配置后,重启一下项目,测试一下项目在改变目录结构后是否正常运行。

1.2 vue.config.js配置

  1. let path = require("path");
  2. let glob = require("glob");
  3. //配置pages多页面获取当前文件夹下的html和js
  4. function getEntry(globPath) {
  5. let entries = {},
  6. basename,
  7. tmp,
  8. pathname,
  9. appname;
  10. glob.sync(globPath).forEach(function (entry) {
  11. basename = path.basename(entry, path.extname(entry));
  12. // console.log(entry)
  13. tmp = entry.split("/").splice(-3);
  14. console.log(tmp);
  15. pathname = basename; // 正确输出js和html的路径
  16. // console.log(pathname)
  17. entries[pathname] = {
  18. entry: "src/" + tmp[0] + "/" + tmp[1] + "/" + tmp[1] + ".js",
  19. template: "src/" + tmp[0] + "/" + tmp[1] + "/" + tmp[2],
  20. title: tmp[2],
  21. filename: tmp[2],
  22. };
  23. });
  24. return entries;
  25. }
  26. let pages = getEntry("./src/pages/**?/*.html");
  27. console.log(pages);
  28. //配置end
  29. module.exports = {
  30. pages,
  31. };

1.3 dist打包目录

  1. ├── css
  2. ├── page1.9951d5a1.css
  3. └── page2.009d0d6f.css
  4. ├── img
  5. └── logo.82b9c7a5.png
  6. ├── js
  7. ├── chunk-vendors.f061f10e.js
  8. ├── page1.5a5322e0.js
  9. └── page2.db57562b.js
  10. ├── page1.html
  11. └── page2.html

1.4 源码部分

@vue/cli-service通过判断是否传入pages参数来生成对应 webpack 配置文件
如果配置了pages选项,则会执行下面步骤:

  1. 清除原有entry 对pages字段的每个key做循环
  2. 解析每个入口对象的参数entry(必选)、title、template、filename、chunks
  3. 通过entry字段生成 webpack 的entry入口

    1. const multiPageConfig = options.pages
    2. if (!multiPageConfig) {
    3. // default, single page setup.
    4. htmlOptions.template = fs.existsSync(htmlPath)
    5. ? htmlPath
    6. : defaultHtmlPath
    7. webpackConfig
    8. .plugin('html')
    9. .use(HTMLPlugin, [htmlOptions])
    10. if (!isLegacyBundle) {
    11. // inject preload/prefetch to HTML
    12. ...
    13. }
    14. }else{
    15. // multi-page setup
    16. webpackConfig.entryPoints.clear()
    17. const pages = Object.keys(multiPageConfig)
    18. const normalizePageConfig = c => typeof c === 'string' ? { entry: c } : c
    19. pages.forEach(name => {
    20. const pageConfig = normalizePageConfig(multiPageConfig[name])
    21. const {
    22. entry,
    23. template = `public/${name}.html`,
    24. filename = `${name}.html`,
    25. chunks = ['chunk-vendors', 'chunk-common', name]
    26. } = pageConfig
    27. const customHtmlOptions = {}
    28. for (const key in pageConfig) {
    29. if (
    30. !['entry', 'template', 'filename', 'chunks'].includes(key)
    31. ) {
    32. customHtmlOptions[key] = pageConfig[key]
    33. }
    34. }
    35. // inject entry
    36. const entries = Array.isArray(entry) ? entry : [entry]
    37. webpackConfig.entry(name).merge(entries.map(e => api.resolve(e)))
    38. // resolve page index template
    39. const hasDedicatedTemplate = fs.existsSync(api.resolve(template))
    40. const templatePath = hasDedicatedTemplate
    41. ? template
    42. : fs.existsSync(htmlPath)
    43. ? htmlPath
    44. : defaultHtmlPath
    45. publicCopyIgnore.push(api.resolve(templatePath).replace(/\\/g, '/'))
    46. // inject html plugin for the page
    47. const pageHtmlOptions = Object.assign(
    48. {},
    49. htmlOptions,
    50. {
    51. chunks,
    52. template: templatePath,
    53. filename: ensureRelative(outputDir, filename)
    54. },
    55. customHtmlOptions
    56. )
    57. webpackConfig
    58. .plugin(`html-${name}`)
    59. .use(HTMLPlugin, [pageHtmlOptions])
    60. })
    61. }