vue 多页面项目开发指南

vue 多页面项目开发指南

单页应用和多页应用

单页应用

SPA(single page application): 单页面应用,即一个web项目就只有一个页面(即一个HTML文件)。

就是把整个项目的所有页面的所有内容分成了很多的小块(就是组件),可以重复利用的,可以任意调整的组件,每个组件就是一个独立的部分(包括htmlcssjavascript代码)。再做一个html(基本上啥也没有),这个html就是一个页面容器,需要放哪个组件时,直接引入就行。跳转时,直接跳转组件就行。当需要加载某个组件时,js会动态创建这些组件里的HTMLCSS

这类项目通常都需要router来进行页面跳转.

一开始只需要加载一次js、css的相关资源。所有内容都包含在主页面,对每一个功能模块组件化。单页应用跳转,就是切换相关组件,仅仅刷新局部资源。

打包后的页面dist 目录结构

dist
├── static
│   ├── css
│   ├── js
│   ├── img
│   ├── dll
│   └── ...
└── index.html
└── ...
└── ...
复制代码

多页应用

MPA(multipage application): 多页面应用,即一个web项目就有多个页面(即多个HTML文件)。

指有多个独立页面的应用(多个html页面),每个页面必须重复加载js、css等相关资源。多页应用跳转,需要整页资源刷新。

项目是由多个完整的页面组成。多页面跳转刷新所有资源,每个公共资源(js、css等)需选择性重新加载。

打包后的页面dist 目录结构

dist
├── page(这里名字打包出哪个文件夹自己配置)
│   ├── css
│   ├── js
│   ├── img
│   ├── index.html
│   └── user.html
│   └── setting.html
│   └── ....html
│   └── ....html
.   ....
.   ....
.   ....
复制代码

优缺点

单页应用的优缺点

优点
有良好的交互体验。能提升页面切换体验,用户在访问应用页面是不会频繁的去切换浏览页面,从而避免了页面的重新加载。
单页面是一次性把web应用的所有代码(HTML,JavaScript和CSS)全部请求过来,有时候考虑到首屏加载太慢会按需加载。这样一来,以后用户的每一个动作都不会重新加载页面(即不用再问服务器要页面的HTML慢,css和js代码),取而代之的是利用 JavaScript 动态的变换HTML的内容(这不需要和服务器交互,除非数据是动态,那么只需要问服务器要数据即可)。

缺点
SEO难度较高。
首屏加载(初次加载)耗时多。为实现单页Web应用功能及显示效果,需要在加载页面的时候将JavaScript、CSS统一加载,部分页面可以在需要的时候加载。所以必须对JavaScript及CSS代码进行合并压缩处理;

多页应用的优缺点
优点
有利于seo。
首屏加载加载快。

缺点
页面切换慢。资源共用(html、css,js)不共享,不共用,每个页面都需要加载。
页面重复代码多。

复制代码

配置多页应用

1. 修改vue.config.js

官网配置入口

在 multi-page 模式下构建应用。每个“page”应该有一个对应的 JavaScript 入口文件。其值应该是一个对象,对象的 key 是入口的名字,value 是:

  • 一个指定了 entry, template, filename, titlechunks 的对象 (除了 entry 之外都是可选的);
  • 或一个指定其 entry 的字符串。
module.exports = {
  pages: {
    index: {
      // page 的入口
      entry: 'src/index/main.js',
      // 模板来源
      template: 'public/index.html',
      // 在 dist/index.html 的输出
      filename: 'index.html',
      // 当使用 title 选项时,
      // template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>
      title: 'Index Page',
      // 在这个页面中包含的块,默认情况下会包含
      // 提取出来的通用 chunk 和 vendor chunk。
      chunks: ['chunk-vendors', 'chunk-common', 'index']
    },
    // 当使用只有入口的字符串格式时,
    // 模板会被推导为 `public/subpage.html`
    // 并且如果找不到的话,就回退到 `public/index.html`。
    // 输出文件名会被推导为 `subpage.html`。
    subpage: 'src/subpage/main.js'
  }
}
复制代码

以上是官网的例子,这里我们改写一下,统一配置多页

前提条件在src 下新建一个pages文件夹

pages新建如下三个文件

pages
└── index-skeleton.html
└── indexApp.html
└── app.js
复制代码
const glob = require('glob')
const fs = require('fs');
let titleObj = {};
// 统一配置多页

// 这里是遍历src下面的pages 下面每个文件夹(例如index)下以xxxApp.vue 命名的vue页面
glob.sync('./src/pages/**/*App.vue').forEach((path) => {
    // 遍历path
    console.log(path,'path')
   //./src/pages/index/indexApp.vue path
  
  // 找到文件名
  const fileName = path.split('/')[path.split('/').length - 1];
   console.log(fileName,'fileName')
   // indexApp.vue fileName
  
  // 去掉App 后缀
  const chunk = path.substring(12, path.indexOf('/' + fileName));
    console.log(chunk,'chunk')
   // index chunk
 
  // 这里是给每个页面设置标题,需要在indexApp.vue设置一个变量pageTitle
  let fileContent = fs
    .readFileSync(path, { encoding: 'utf-8' })
    .toString()
    .replace(/\r\n/g, '');
  fileContent = fileContent.substr(fileContent.indexOf('pageTitle:'));
  fileContent = fileContent.substr(0, fileContent.indexOf(','));

  fileContent =
    fileContent.indexOf('"') > 0
      ? fileContent.substr(fileContent.indexOf('"') + 1)
      : fileContent.substr(fileContent.indexOf("'") + 1);

  fileContent =
    fileContent.indexOf('"') > 0
      ? fileContent.substr(0, fileContent.indexOf('"'))
      : fileContent.substr(0, fileContent.indexOf("'"));
  titleObj[chunk] = fileContent ? fileContent : '标题';
});


// 这里是遍历src下面的pages 下面每个文件夹(例如index)下以app.js
glob.sync('./src/pages/**/app.js').forEach((path) => {
  //打包js
  const chunk = path.split('./src/pages/')[1].split('/app.js')[0];
  const tmp = chunk.split('/');
  // 模版html,如果都是一样的,可以直接使用public下index.html,如果要设置标题的话,需要每个页面都有一个html模版,如果不需要,就可以使用同一个,看个人习惯
  let templateUrl =
    'src/pages/' + chunk + '/' + tmp[tmp.length - 1] + '-skeleton.html';

  pages[chunk] = {
    entry: path,//入口文件
    template: templateUrl,//模版html
    title: titleObj[chunk] ? titleObj[chunk] : '标题',//标题
    filename: chunk.replace(/\//g, '-') + '.html',//打包出来的html名字
    chunks: ['chunk-vendors', 'chunk-common', chunk],//依赖包
  };
});
module.exports = {
  // 选项...
  publicPath: process.env.NODE_ENV === 'production'
    ? '/dist/'
    : '/',
  pages,
}
复制代码

2. 修改title

其实是用插件替换的

很简单,就是把html模版中的title使用模版语法就行

例如index-skeleton.html 这里每个页面html都是一样的,复制即可

<title><%= htmlWebpackPlugin.options.title %></title>
复制代码

3. 合并第三方库

如果不设置分包,所有node_modules 里面的第三方资源库,例如Echarts,Axios,ali-oss,等等都会被打进chunk-vendors,至于为什么会打进去,我们看下vue.config.js默认的分包规则

官网分包splitChunks入口

官网默认的配置

module.exports = {
  //...
 //...
  optimization: {
    splitChunks: {
      chunks: 'async', // 代码分割时对异步代码生效,all:所有代码有效,inital:同步代码有效
      minSize: 30000, // 代码分割最小的模块大小,引入的模块大于 30000B 才做代码分割
      maxSize: 0, // 代码分割最大的模块大小,大于这个值要进行代码分割,一般使用默认值
      minChunks: 1, // 引入的次数大于等于1时才进行代码分割
      maxAsyncRequests: 6, // 最大的异步请求数量,也就是同时加载的模块最大模块数量
      maxInitialRequests: 4, // 入口文件做代码分割最多分成 4 个 js 文件
      automaticNameDelimiter: '~', // 文件生成时的连接符
      automaticNameMaxLength: 30, // 自动生成的文件名的最大长度
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/, // 位于node_modules中的模块做代码分割
          priority: -10 // 根据优先级决定打包到哪个组里,例如一个 node_modules 中的模块进行代码
        }, // 分割,,既满足 vendors,又满足 default,那么根据优先级会打包到 vendors 组中。
        default: { // 没有 test 表明所有的模块都能进入 default 组,但是注意它的优先级较低。
          priority: -20, //  根据优先级决定打包到哪个组里,打包到优先级高的组里。
          reuseExistingChunk: true // //如果一个模块已经被打包过了,那么再打包时就忽略这个上模块
        }
      }
    }
  }
};
复制代码

我们重点看下 minChunks 这个配置,默认大于1次就会进行分包操作,以前是一个单页面,所以分包没有问题,只会在index.html引入,现在是多页面,每个页面都会引入这个chunk-vendors,有些包其实只有两三个页面用到,因此,最好是不分包,或者达到一定次数才有分包意义

我们这里设置8次,才分包,几乎没有分包,根据各位需求可以自己设置

    optimization: {
      minimize: false,
      splitChunks: {
        cacheGroups: {// 缓存分组
          common: {// 公共的模块
            name: 'chunk-common',//命名要和上面chunks定义的一致
            chunks: 'initial',
            minSize: 1,// 大小限制
            priority: 0,
            minChunks: 8,// 最少复用过几次
          },
          // 打包第三方库的文件
          vendor: {
            name: 'chunk-vendors',//命名要和上面chunks定义的一致
            test: /[\\/]node_modules[\\/]/,
            chunks: 'initial',
            priority: 10,// 权限更高,优先抽离,重要!!!
            minChunks: 8,
          },
        },
      },
    },
复制代码

4. 打包第三方scss

有一些自己写的公共scss,比如common.scss ,不想在页面引入,因为每个页面都要引入,其实也有很简单处理的方法,这和以前没什么变化,话不多说,直接上代码

let scssVariables = require('./src/scss/variables.scss.js');

css: {
    loaderOptions: {
      scss: {
        prependData:
          Object.keys(scssVariables)
            .map((k) => `${k.replace('_', '-')}: ${scssVariables[k]};`)
            .join('\n') + '\n',
      },
    },
  },
复制代码

5. 其它常见设置

就是一些常见设置,看个人设置 这里重点推荐一下filenameHashing,多页面应用不带hash的设置,因为没有使用路由,也就用不到了.

module.exports = {
  publicPath: './',
  //输出目录
  outputDir: 'fund',
  assetsDir: '',
  // 配置别名
  chainWebpack: (config) => {
    config.resolve.alias.set('@', resolve('src'));
    config.resolve.alias.set('@@', resolve('src/components'));
    config.resolve.alias.set('@assets', resolve('src/assets'));
    config.resolve.alias.set('scss', resolve('src/scss'));
  },
  // 关闭eslint校验
  lintOnSave: false,
  // 不生成map文件
  productionSourceMap: false,
  //文件名称不带hash值
  filenameHashing: false,
  devServer: {
    publicPath: '/fund/',
    proxy: {// 本地调试转发
      '/api': {
        target: 'http://127.0.0.1:8080',
        changeOrigin: true,
        pathRewrite: {
          '^/api': '',
        },
      },
    },
  },
};
    
    
复制代码

Guess you like

Origin juejin.im/post/7054439846927925256