vue多页面项目开发
孤山渐青 人气:0单页应用和多页应用
单页应用
SPA(single page application): 单页面应用,即一个web项目就只有一个页面(即一个HTML文件)。
就是把整个项目的所有页面的所有内容分成了很多的小块(就是组件),可以重复利用的,可以任意调整的组件,每个组件就是一个独立的部分(包括html,css和javascript代码)。再做一个html(基本上啥也没有),这个html就是一个页面容器,需要放哪个组件时,直接引入就行。跳转时,直接跳转组件就行。当需要加载某个组件时,js会动态创建这些组件里的HTML,CSS。
这类项目通常都需要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, title 和 chunks 的对象 (除了 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默认的分包规则
官网默认的配置
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': '', }, }, }, }, };
总结
加载全部内容