各位靓仔靓女们,晚上好!我是你们的老朋友,今天咱们聊聊 Vue CLI 多页应用(MPA)的那些事儿。
先声明,我不是什么“编程专家”,只是个在代码堆里摸爬滚打多年的老兵。咱们今天就是唠嗑,把 MPA 这玩意儿扒个底朝天,争取让大家听完能上手就干!
开场白:单页应用(SPA)虽好,但 MPA 也有春天!
现在 SPA 好像成了主流,各种框架都在推。SPA 确实爽,用户体验好,切换流畅。但是!它也有缺点,比如 SEO 不友好,首屏加载慢等等。这时候,MPA 就有了用武之地。
MPA 简单粗暴,每个页面都是独立的,SEO 友好,首屏加载也快。当然,它也有缺点,比如页面切换可能会有白屏,公共资源不好管理等等。所以,选择 SPA 还是 MPA,要根据你的项目实际情况来决定。
第一章:Vue CLI 搭建 MPA 项目:手把手教你填坑!
Vue CLI 搭建 MPA 项目,其实很简单,但是一不小心就会掉坑里。咱们一步一步来,保证大家不迷路。
-
安装 Vue CLI:
如果你还没装 Vue CLI,先把它装上:
npm install -g @vue/cli # OR yarn global add @vue/cli
-
创建项目:
vue create my-mpa-project
选择
Manually select features
,然后选择Babel
、Router
、Vuex
、CSS Pre-processors
、Linter / Formatter
这些常用的功能(当然,你可以根据自己的需求选择)。然后选择
Use history mode for router?
,这里我们选择No
,因为 MPA 不需要 history 模式。CSS 预处理器可以选一个你喜欢的,比如
Sass/SCSS (with dart-sass)
。Linter / Formatter 可以选择
ESLint + Standard config
或者ESLint + Prettier
。最后选择
Lint on save
和Commit
。 -
配置
vue.config.js
:这是 MPA 的关键!在项目的根目录下,创建一个
vue.config.js
文件,然后写入以下配置:module.exports = { pages: { index: { entry: 'src/pages/index/main.js', template: 'public/index.html', filename: 'index.html', title: 'Index Page', chunks: ['chunk-vendors', 'chunk-common', 'index'] }, about: { entry: 'src/pages/about/main.js', template: 'public/about.html', filename: 'about.html', title: 'About Page', chunks: ['chunk-vendors', 'chunk-common', 'about'] } }, // 其他配置... };
这个
pages
对象就是 MPA 的核心配置。每个 key 代表一个页面,比如index
和about
。每个页面都有以下属性:entry
:入口文件,每个页面都有一个独立的入口文件。template
:HTML 模板文件,每个页面都可以有自己的模板。filename
:输出的文件名,比如index.html
和about.html
。title
:页面的标题,会设置<title>
标签。chunks
:指定要包含的 chunk,比如chunk-vendors
、chunk-common
和index
。
-
创建页面目录和文件:
按照
vue.config.js
里的配置,创建对应的目录和文件:src/ pages/ index/ main.js App.vue about/ main.js App.vue public/ index.html about.html
main.js
负责初始化 Vue 实例,App.vue
是页面的根组件。 -
修改
public/index.html
和public/about.html
:在 HTML 文件里,修改
<title>
标签,并添加<div id="app"></div>
:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Index Page</title> </head> <body> <div id="app"></div> <!-- built files will be auto injected --> </body> </html>
about.html
类似,只需要修改<title>
标签即可。 -
启动项目:
npm run serve # OR yarn serve
现在,你应该能看到两个页面了:
http://localhost:8080/index.html
和http://localhost:8080/about.html
。
第二章:MPA 项目优化:让你的应用飞起来!
MPA 项目搭建起来了,但是性能优化也很重要。咱们来聊聊 MPA 项目的优化策略。
-
公共资源提取:
MPA 项目里,很多资源都是公共的,比如 Vue、Vue Router、Vuex、Element UI 等等。如果每个页面都引入这些资源,会造成资源浪费。所以,我们需要把这些公共资源提取出来,放到一个单独的 chunk 里。
在
vue.config.js
里,配置chainWebpack
:module.exports = { pages: { // ... }, chainWebpack: config => { config.optimization.splitChunks({ cacheGroups: { vendors: { name: 'chunk-vendors', test: /[\/]node_modules[\/]/, priority: -10, chunks: 'initial' }, common: { name: 'chunk-common', minChunks: 2, priority: -20, chunks: 'initial', reuseExistingChunk: true } } }); } };
这个配置会把
node_modules
里的代码提取到chunk-vendors.js
里,把被多个页面引用的代码提取到chunk-common.js
里。然后在
pages
配置里,指定每个页面都要包含chunk-vendors
和chunk-common
:module.exports = { pages: { index: { // ... chunks: ['chunk-vendors', 'chunk-common', 'index'] }, about: { // ... chunks: ['chunk-vendors', 'chunk-common', 'about'] } }, // ... };
-
资源预加载:
在 MPA 项目里,页面切换的时候,浏览器会重新加载资源。如果资源比较大,会造成白屏。所以,我们需要对资源进行预加载,让浏览器提前下载资源。
在
vue.config.js
里,配置chainWebpack
:module.exports = { pages: { // ... }, chainWebpack: config => { config.plugin('preload-index') .use(require('@vue/preload-webpack-plugin'), [{ rel: 'preload', include: 'initial', fileBlacklist: [/.map$/, /chunk-vendors..*/, /chunk-common..*/] }]); config.plugin('preload-about') .use(require('@vue/preload-webpack-plugin'), [{ rel: 'preload', include: 'initial', fileBlacklist: [/.map$/, /chunk-vendors..*/, /chunk-common..*/] }]); config.plugin('prefetch-index') .use(require('@vue/preload-webpack-plugin'), [{ rel: 'prefetch', include: 'asyncChunks', fileBlacklist: [/.map$/] }]); config.plugin('prefetch-about') .use(require('@vue/preload-webpack-plugin'), [{ rel: 'prefetch', include: 'asyncChunks', fileBlacklist: [/.map$/] }]); } };
这个配置会使用
@vue/preload-webpack-plugin
插件,对初始加载的资源进行preload
,对异步加载的资源进行prefetch
。preload
:告诉浏览器尽快加载资源,优先级比较高。prefetch
:告诉浏览器在空闲的时候加载资源,优先级比较低。
-
图片优化:
图片是网页里最占体积的资源之一。所以,我们需要对图片进行优化,减小图片的大小。
- 选择合适的图片格式: PNG 适合存储颜色较少的图片,JPEG 适合存储颜色较多的图片,WebP 适合存储各种类型的图片。
- 压缩图片: 可以使用各种图片压缩工具,比如 TinyPNG、ImageOptim 等等。
- 使用 CDN: 把图片放到 CDN 上,可以加快图片的加载速度。
- 懒加载: 对于不在首屏显示的图片,可以使用懒加载,只有当图片进入可视区域的时候才加载。
-
Gzip 压缩:
Gzip 是一种常用的压缩算法,可以减小网页的大小,加快网页的加载速度。
在
vue.config.js
里,配置configureWebpack
:const CompressionWebpackPlugin = require('compression-webpack-plugin'); module.exports = { pages: { // ... }, configureWebpack: config => { config.plugins.push( new CompressionWebpackPlugin({ filename: '[path][base].gz', algorithm: 'gzip', test: /.(js|css|html|svg)$/, threshold: 10240, minRatio: 0.8, deleteOriginalAssets: false }) ); } };
这个配置会使用
compression-webpack-plugin
插件,对js
、css
、html
、svg
等文件进行 Gzip 压缩。需要注意的是,你需要配置服务器,让服务器支持 Gzip 解压。
-
代码分割:
除了提取公共资源,我们还可以对代码进行更细粒度的分割。比如,可以把每个页面的组件都分割成单独的 chunk。
Vue CLI 默认会对异步组件进行代码分割。如果你的组件不是异步组件,可以使用
import()
函数进行动态导入:// 异步组件 const MyComponent = () => import('./MyComponent.vue'); // 动态导入 import('./MyComponent.vue').then(module => { const MyComponent = module.default; });
使用动态导入的组件,会被分割成单独的 chunk。
-
路由懒加载:
如果你的 MPA 项目里,某个页面比较大,可以使用路由懒加载,只有当用户访问这个页面的时候才加载。
在 Vue Router 里,使用
import()
函数进行动态导入:const routes = [ { path: '/about', component: () => import('./pages/about/App.vue') } ];
这样,只有当用户访问
/about
页面的时候,才会加载about
页面的组件。 -
缓存:
利用浏览器缓存可以有效减少资源加载时间。可以设置 HTTP 缓存头,例如
Cache-Control
和Expires
,来控制浏览器缓存行为。 对于静态资源,可以设置较长的缓存时间,对于动态资源,可以设置较短的缓存时间或者使用ETag
和Last-Modified
来进行缓存验证。此外,还可以使用 Service Worker 来实现更高级的缓存策略,例如离线访问。
第三章:MPA 项目的坑:小心别踩雷!
MPA 项目虽然简单,但是也有一些坑需要注意。
-
publicPath
配置:如果你的 MPA 项目部署在子目录下,比如
https://example.com/my-app/
,你需要配置publicPath
,否则资源路径会出错。在
vue.config.js
里,配置publicPath
:module.exports = { publicPath: '/my-app/' };
-
跨域问题:
如果你的 MPA 项目需要访问其他域名的 API,可能会遇到跨域问题。
解决跨域问题的方法有很多,比如 JSONP、CORS、代理等等。
在 Vue CLI 里,可以使用
devServer.proxy
配置代理:module.exports = { devServer: { proxy: { '/api': { target: 'http://api.example.com', changeOrigin: true, pathRewrite: { '^/api': '' } } } } };
这样,所有以
/api
开头的请求都会被代理到http://api.example.com
。 -
页面间通信:
MPA 项目里,每个页面都是独立的,页面间通信比较麻烦。
常用的页面间通信方法有:
- URL 参数: 通过 URL 参数传递数据。
localStorage
: 使用localStorage
存储数据。postMessage
: 使用postMessage
API 进行跨域通信。- Vuex: 如果你的项目比较复杂,可以使用 Vuex 进行状态管理。
第四章:总结:MPA 项目的优势与劣势
咱们来总结一下 MPA 项目的优势和劣势:
优势 | 劣势 |
---|---|
SEO 友好 | 页面切换可能会有白屏 |
首屏加载快 | 公共资源不好管理 |
每个页面都是独立的,易于维护 | 页面间通信比较麻烦 |
适合简单的、静态的网站 | 不适合复杂的、交互性强的网站 |
可以使用不同的技术栈来构建不同的页面 | 构建过程可能比 SPA 复杂,需要更多配置 |
尾声:选择适合你的才是最好的!
好了,今天的讲座就到这里了。希望大家对 Vue CLI MPA 项目有了更深入的了解。记住,没有最好的技术,只有最适合你的技术。选择 SPA 还是 MPA,要根据你的项目实际情况来决定。
如果大家还有什么问题,欢迎随时提问。祝大家编程愉快! Bye Bye!