Vue单页应用的配置

前面通过几篇文章了解并掌握了 Vue 项目构建及运行的前期工作 。接下来我们可以走进 Vue 项目的内部,一探其内部配置的基本构成。

1. 路由配置

由于 Vue 这类型的框架都是以一个或多个单页构成,在单页内部跳转并不会重新渲染 HTML 文件,其路由可以由前端进行控制,因此我们需要在项目内部编写相应的路由文件,Vue 会解析这些文件中的配置并进行对应的跳转渲染。

我们来看一下 CLI 给我们生成的 router.js 文件的配置:

/* router.js */import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue' // 引入 Home 组件
import About from './views/About.vue' // 引入 About 组件Vue.use(Router) // 注册路由export default new Router({routes: [{path: '/',name: 'home',component: Home}, {path: '/about',name: 'about',component: About}]
})

这份配置可以算是最基础的路由配置,有以下几点需要进行优化:

  • 如果路由存在二级目录,需要添加 base 属性,否则默认为 "/"

  • 默认路由模式是 hash 模式,会携带 # 标记,与真实 url 不符,可以改为 history 模式

  • 页面组件没有进行按需加载,可以使用 require.ensure() 来进行优化

下面是我们优化结束的代码:

/* router.js */import Vue from 'vue'
import Router from 'vue-router'// 引入 Home 组件
// 使用 require.ensure 实现代码分割,只有在访问该路由时才会加载 Home.vue 组件,实现懒加载
const Home = resolve => {require.ensure(['./views/Home.vue'], () => {resolve(require('./views/Home.vue'))})
}// 引入 About 组件
const About = resolve => {require.ensure(['./views/About.vue'], () => {resolve(require('./views/About.vue'))})
}Vue.use(Router)//获取基础路径.适应不同的部署环境
let base = `${process.env.BASE_URL}` export default new Router({mode: 'history',base: base,routes: [{path: '/',name: 'home',component: Home}, {path: '/about',name: 'about',component: About}]
})

改为 history 后我们 url 的路径就变成了 http://127.0.0.1:8080/vue/about,而不是原来的 http://127.0.0.1:8080/vue/#/about,但是需要注意页面渲染 404 的问题,具体可查阅:HTML5 History 模式。

而在异步加载的优化上,我们使用了 webpack 提供的 require.ensure() 进行了代码拆分,主要区别在于没有优化前,访问 Home 页面会一起加载 About 组件的资源,因为它们打包进了一个 app.js 中:

但是优化过后,它们分别被拆分成了 2.js 和 3.js:

如此,只有当用户点击了某页面,才会加载对应页面的 js 文件,实现了按需加载的功能。

webpack 在编译时,会静态地解析代码中的 require.ensure(),同时将模块添加到一个分开的 chunk 当中。这个新的 chunk 会被 webpack 通过 jsonp 来按需加载。 关于 require.ensure() 的知识点可以参考官方文档:require.ensure。

当然,除了使用 require.ensure 来拆分代码,Vue Router 官方文档还推荐使用动态 import 语法来进行代码分块,比如上述 require.ensure 代码可以修改为

// 引入 Home 组件
const Home = () => import('./views/Home.vue');// 引入 About 组件
const About = () => import('./views/About.vue');

其余代码可以保持不变,仍然可以实现同样的功能。如果你想给拆分出的文件命名,可以尝试一下 webpack 提供的 Magic Comments(魔法注释):

const Home = () => import(/* webpackChunkName:'home'*/ './views/Home.vue');

2. Vuex 配置

除了 vue-router,如果你的项目需要用到 Vuex ,那么你应该对它有一定的了解,Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。这里我们先来看一下使用 CLI 生成的配置文件 store.js 中的内容:

import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({state: {},mutations: {},actions: {}
})

该配置文件便是 Vuex 的配置文件,主要有 4 个核心点:state、mutations、actions 及 getter,详细的介绍大家可以参考官方文档:核心概念,这里我用一句话介绍它们之间的关系就是:我们可以通过 actions 异步提交 mutations 去 修改 state 的值并通过 getter 获取

需要注意的是不是每一个项目都适合使用 Vuex,如果你的项目是中大型项目,那么使用 Vuex 来管理错综复杂的状态数据是很有帮助的,而为了后期的拓展性和可维护性,这里不建议使用 CLI 生成的一份配置文件来管理所有的状态操作,我们可以把它拆分为以下目录:

└── store├── index.js          # 我们组装模块并导出 store 的地方├── actions.js        # 根级别的 action├── mutations.js      # 根级别的 mutation└── modules├── moduleA.js    # A模块└── moduleB.js    # B模块

与单个 store.js 文件不同的是,我们按模块进行了划分,每个模块中都可以包含自己 4 个核心功能。比如模块 A 中:

/* moduleA.js */const moduleA = {state: { text: 'hello'},mutations: {addText (state, txt) {// 这里的 `state` 对象是模块的局部状态state.text += txt}},actions: {setText ({ commit }) {commit('addText', ' world')}},getters: {getText (state) {return state.text + '!'}}
}export default moduleA

上方我们导出 A 模块,并在 index.js 中引入:

/* index.js */import Vue from 'vue'
import Vuex from 'vuex'
import moduleA from './modules/moduleA'
import moduleB from './modules/moduleB'
import { mutations } from './mutations'
import actions from './actions'Vue.use(Vuex)export default new Vuex.Store({state: {groups: [1]},modules: {moduleA, // 引入 A 模块moduleB, // 引入 B 模块},actions, // 根级别的 actionmutations, // 根级别的 mutations// 根级别的 gettersgetters: {getGroups (state) {return state.groups}}   
})

3. 接口配置

在项目的开发过程中,我们也少不了与后台服务器进行数据的获取和交互,这一般都是通过接口完成的,那么我们如何进行合理的接口配置呢?我们可以在 src 目录下新建 services 文件夹用于存放接口文件:

└── src└── services├── http.js      # 接口封装├── moduleA.js    # A模块接口└── moduleB.js    # B模块接口

为了让接口便于管理,我们同样使用不同的文件来配置不同模块的接口,同时由于接口的调用 ajax 请求代码重复部分较多,我们可以对其进行简单的封装,比如在 http.js 中(fetch为例):

/* http.js */
import 'whatwg-fetch'// HTTP 工具类
export default class Http {static async request(method, url, data) {const param = {method: method,headers: {'Content-Type': 'application/json'}};if (method === 'GET') {url += this.formatQuery(data)} else {param['body'] = JSON.stringify(data)}// Tips.loading(); // 可调用 loading 组件return fetch(url, param).then(response => this.isSuccess(response)).then(response => {return response.json()})}// 判断请求是否成功static isSuccess(res) {if (res.status >= 200 && res.status < 300) {return res} else {this.requestException(res)}}// 处理异常static requestException(res) {const error = new Error(res.statusText)error.response = resthrow error}// url处理static formatQuery(query) {let params = [];if (query) {for (let item in query) {let vals = query[item];if (vals !== undefined) {params.push(item + '=' + query[item])}}}return params.length ? '?' + params.join('&') : '';}// 处理 get 请求static get(url, data) {return this.request('GET', url, data)}// 处理 put 请求static put(url, data) {return this.request('PUT', url, data)}// 处理 post 请求static post(url, data) {return this.request('POST', url, data)}// 处理 patch 请求static patch(url, data) {return this.request('PATCH', url, data)}// 处理 delete 请求static delete(url, data) {return this.request('DELETE', url, data)}
}

封装完毕后我们在 moduleA.js 中配置一个 github 的开放接口:https://api.github.com/repos/octokit/octokit.rb

/* moduleA.js */
import Http from './http'// 获取测试数据
export const getTestData = () => {return Http.get('https://api.github.com/repos/octokit/octokit.rb')
}

然后在项目页面中进行调用,会成功获取 github 返回的数据,但是一般我们在项目中配置接口的时候会直接省略项目 url 部分,比如:

/* moduleA.js */
import Http from './http'// 获取测试数据
export const getTestData = () => {return Http.get('/repos/octokit/octokit.rb')
}

这时候我们再次调用接口的时候会发现其调用地址为本地地址:http://127.0.0.1:8080/repos/octokit/octokit.rb,这便是CORS(跨域资源共享)问题。那么为了让其指向 https://api.github.com,我们需要在 vue.config.js 中进行 devServer 的配置:

/* vue.config.js */module.exports = {...devServer: {// string | Object 代理设置proxy: {// 接口是 '/repos' 开头的才用代理'/repos': {// 目标API地址,所有匹配 /repos 的请求都会转发到这个地址target: 'https://api.github.com',// 允许跨域changeOrigin: true, // 重新写路径,将 '/api' 前缀去掉,这样,实际请求发送到 https://api.github.com 时不会再包含 /api 前缀pathRewrite: { '^/api': '' }, }},}...
}

在 devServer 中 我们配置 proxy 进行接口的代理,将我们本地地址转换为真实的服务器地址,此时我们同样能顺利的获取到数据,不同点在于接口状态变成了 304(重定向):

 拓展1

 304 状态码

1.304 状态码的含义
  • 304 Not Modified: 这个状态码表示自从上次请求以来,资源没有被修改。服务器会在响应中不返回资源的实体内容,而是告诉客户端使用缓存的版本。

为什么会出现 304 状态码?

当你在开发环境中使用代理设置时,devServer 可能会利用浏览器的缓存机制。以下是一些可能导致出现 304 状态码的原因:

  1. 浏览器缓存: 浏览器会根据 Cache-Control ETag 等 HTTP 头信息来缓存资源。如果你之前请求过某个接口,且没有添加任何请求参数或头信息,浏览器可能会直接从缓存中返回 304 状态。

  2. 代理配置的影响: 如果 proxy 的目标服务器支持缓存,并且根据请求头(如 If-None-Match)返回 304,则会在代理过程中看到这一返回状态。

如何处理 304 状态码?

如果你希望确保始终获取最新的数据,而不是使用缓存,可以考虑以下几种方法:

  1. 禁用缓存: 在请求中添加一些请求头,告知服务器和浏览器不使用缓存。

    axios.get('/api/repos/octokit/octokit.rb', {headers: {'Cache-Control': 'no-cache','Pragma': 'no-cache','Expires': '0'}
    });
  2. 使用唯一的请求参数: 在请求 URL 中添加随机参数或时间戳,以确保请求是唯一的。

    axios.get(`/api/repos/octokit/octokit.rb?timestamp=${new Date().getTime()}`);
  3. 检查服务器响应: 确保你的服务器设置了适当的缓存策略。如果你在开发环境中使用的是某种 API,可以检查其文档,确保它按预期工作。

2.什么是重定向

在 HTTP 协议中,重定向是指服务器向客户端发送一个响应,告知其需要访问其他的 URL。这通常发生在请求的资源位置发生变化、需要进行身份验证、或者为了其他目的引导用户访问不同的页面或接口。

常见的重定向状态码

以下是一些常见的 HTTP 重定向状态码及其含义:

  1. 301 Moved Permanently: 永久重定向,表示请求的资源已被永久移至新位置。客户端应使用新 URL 进行后续请求。

  2. 302 Found: 临时重定向,表示请求的资源临时被移至新位置。客户端仍应使用原 URL 进行后续请求。

  3. 303 See Other: 表示请求的响应可以在另一个 URL 上找到,通常用于 POST 请求之后重定向到 GET 请求。

  4. 307 Temporary Redirect: 临时重定向,表示请求的资源临时移至新位置,客户端应使用新 URL 进行后续请求,但仍应使用原请求方法。

  5. 308 Permanent Redirect: 永久重定向,表示请求的资源已被永久移至新位置,客户端应使用新 URL 进行后续请求,且保留原请求方法。

重定向的工作流程

  1. 客户端发送请求到一个特定的 URL。
  2. 服务器处理请求并返回一个重定向状态码(如 301 或 302),通常附带一个新的 URL。
  3. 客户端接收到响应,并根据新的 URL 再次发送请求。
  4. 服务器响应新的请求,返回最终的资源。

开发中重定向的应用

在前端开发中,重定向常用于以下场景:

  • 用户登录: 在用户成功登录后,将其重定向到首页或仪表板。
  • 资源迁移: 当 API 路径发生变化时,可以通过重定向引导用户使用新的路径。
  • 访问控制: 未经授权的用户访问受保护的资源时,可以重定向到登录页面。

示例:使用 Axios 处理重定向

在前端使用 Axios 进行 HTTP 请求时,如果遇到重定向,Axios 会自动处理这些重定向。以下是一个简单的示例:

import axios from 'axios';axios.get('https://example.com/api/resource').then(response => {console.log('最终响应数据:', response.data);}).catch(error => {console.error('请求出错:', error);});

在这个示例中,如果 https://example.com/api/resource 发生重定向,Axios 会自动跟踪重定向并最终返回最终资源的数据。

4. 公共设施配置

最后我们项目开发中肯定需要对一些公共的方法进行封装使用,这里我把它称之为公共设施,那么我们可以在 src 目录下建一个 common 文件夹来存放其配置文件:

/* index.js */
import Validate from './validate'
import Other from './other'export {Validate,Other,
}

这样我们在页面中只需要引入一个 index.js 即可。

本案例代码地址:single-page-project

拓展2

1.devServer 中 proxy 的 key 值代表什么?如果再添加一个 /reposed 的配置会产生什么隐患?

devServer proxy 配置中,key 值(即配置的 URL 路径)是用来匹配请求的 URL 的前缀。当你的开发服务器收到请求时,它会检查请求的 URL 是否以配置的 key 开头。如果匹配,则会将该请求转发到指定的目标服务器。

例如:

javascript
module.exports = {devServer: {proxy: {'/api': {target: 'http://example.com',changeOrigin: true},'/repos': {target: 'http://another-api.com',changeOrigin: true}}}
};

在这个示例中:

  • 所有以 /api 开头的请求将会被代理到 http://example.com
  • 所有以 /repos 开头的请求将会被代理到 http://another-api.com

添加 /repos 配置的隐患

  1. 冲突: 如果 /repos 与其他 URL 路径重叠,可能会导致请求的目标不明确。例如,如果你有一个 /api/repos 的请求,可能会导致混淆,无法确定请求应该被代理到哪个目标。

  2. 优先级: 在 proxy 的配置中,匹配是基于先出现的规则。如果 /api/repos 都存在,并且请求是 /api/repos,则首先匹配到 /api,而不会转发到 /repos。这可能导致一些意外的行为,尤其是当你希望某些请求到达特定的服务器时。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/64154.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CocosCreator-引擎案例-TS:spine

工程1&#xff1a;LoadSpine&#xff1a;简单加载spine资源 建立工程&#xff0c;在层级上建立一个空对象&#xff0c;改名spine 在spine上添加spine组件&#xff1a; 添加组件>渲染组件>spine 在spine上挂上脚本loadspine onLoad () {cc.resources.load(loadSpine/ali…

使用FreeNAS软件部署ISCSI的SAN架构存储(IP-SAN)练习题

一&#xff0c;实验用到工具分别为&#xff1a; VMware虚拟机&#xff0c;安装教程&#xff1a;VMware Workstation Pro 17 安装图文教程 FreeNAS系统&#xff0c;安装教程&#xff1a;FreeNAS-11.2-U4.1安装教程2024&#xff08;图文教程&#xff09; 二&#xff0c;新建虚…

【ANGULAR网站开发】初始环境搭建

1. 初始化angular项目 1.1 创建angular项目 需要安装npm和nodejs&#xff0c;这边不在重新安装 直接安装最新版本的angular npm install -g angular/cli安装指定大版本的angular npm install -g angular/cli181.2 启动angular 使用idea启动 控制台启动 ng serve启动成功…

【再谈设计模式】享元模式~对象共享的优化妙手

一、引言 在软件开发过程中&#xff0c;我们常常面临着创建大量细粒度对象的情况&#xff0c;这可能会导致内存占用过高、性能下降等问题。享元模式&#xff08;Flyweight Pattern&#xff09;就像是一位空间管理大师&#xff0c;它能够在不影响功能的前提下&#xff0c;有效地…

Milvus×EasyAi:如何用java从零搭建人脸识别应用

如何从零搭建一个人脸识别应用&#xff1f;不妨试试原生Java人工智能算法&#xff1a;EasyAi Milvus 的组合拳。 本文将使用到的软件和工具包括&#xff1a; EasyAi&#xff1a;人脸特征向量提取Milvus&#xff1a;向量数据库用于高效存储和检索数据。 01. EasyAi&#xff1a;…

NS3学习——tcpVegas算法代码详解(2)

NS3学习——tcpVegas算法代码详解&#xff08;1&#xff09;-CSDN博客 目录 4.TcpVegas类中成员函数 (5) CongestionStateSet函数 (6) IncreaseWindow函数 1.检查是否启用 Vgas 2.判断是否完成了一个“Vegas 周期” 2.1--if&#xff1a;判断RTT样本数量是否足够 2.2--e…

GitLab 将停止为中国区用户提供服务,60天迁移期如何应对? | LeetTalk Daily

“LeetTalk Daily”&#xff0c;每日科技前沿&#xff0c;由LeetTools AI精心筛选&#xff0c;为您带来最新鲜、最具洞察力的科技新闻。 GitLab作为一个广受欢迎的开源代码托管平台&#xff0c;近期宣布将停止服务中国大陆、澳门和香港地区的用户提供服务。根据官方通知&#x…

华为实训课笔记 2024 1223-1224

华为实训 12/2312/24 12/23 [Huawei]stp enable --开启STP display stp brief --查询STP MSTID Port Role STP State Protection 实例ID 端口 端口角色 端口状态 是否开启保护[Huawei]display stp vlan xxxx --查询制定vlan的生成树计算结…

《Java源力物语》-3.空值猎手

~犬&#x1f4f0;余~ “我欲贱而贵&#xff0c;愚而智&#xff0c;贫而富&#xff0c;可乎&#xff1f; 曰&#xff1a;其唯学乎” \quad 夜色渐深&#xff0c;在一处偏僻小径上&#xff0c;月光透过浓密的源力云层&#xff0c;在地面上投下斑驳的光影。String正独自练习着刚从…

科技云报到:人工智能时代“三大件”:生成式AI、数据、云服务

科技云报到原创。 就像自行车、手表和缝纫机是工业时代的“三大件”。生成式AI、数据、云服务正在成为智能时代的“新三大件”。加之全球人工智能新基建加速建设&#xff0c;成为了人类社会数字化迁徙的助推剂&#xff0c;让新三大件之间的耦合越来越紧密。从物理世界到数字世…

hiprint结合vue2项目实现静默打印详细使用步骤

代码地址是&#xff1a;vue-plugin-hiprint: hiprint for Vue2/Vue3 ⚡打印、打印设计、可视化设计器、报表设计、元素编辑、可视化打印编辑 本地安装包地址&#xff1a;electron-hiprint 发行版 - Gitee.com 1、先安装hipint安装包在本地 2、项目运行npm&#xff08;socket.…

CUDA各种内存和使用方法

文章目录 1、全局内存2、局部内存3、共享内存3.1 静态共享内存3.2 动态共享内存 4、纹理内存5、常量内存6、寄存器内存7、用CUDA运行时API函数查询设备CUDA 错误检测 1、全局内存 特点&#xff1a;容量最大&#xff0c;访问延时最大&#xff0c;所有线程都可以访问。 线性内存…

Chapter 03 复合数据类型-1

1.列表 Python内置的一种有序、可变的序列数据类型&#xff1b; 列表的定义&#xff1a; [ ]括起来的逗号分隔的多个元素组成的序列 列表对象的创建&#xff1a; &#xff08;1&#xff09;直接赋值 >>> list1 []#创建一个空列表赋值给list1 >>> list…

【后端】LNMP环境搭建

长期更新各种好文&#xff0c;建议关注收藏&#xff01; 本文近期更新完毕。 LNMPlinuxnginxmysqlphp 需要的资源 linux服务器 web服务软件nginx 对应的语言编译器代码文件 数据库mysql安装 tar.gz包或者命令行安装 进入root&#xff1a; sodu 或su mkdir path/{server,soft}…

基于PyQt5的UI界面开发——多界面切换

介绍 最初&#xff0c;因为课设的缘故&#xff0c;我只是想做一个通过按键进行切面切换而已&#xff0c;但是我看网上资料里面仅是语焉不详&#xff0c;让我困惑的很&#xff0c;但后面我通过摸索才发现这件事实在是太简单了&#xff0c;因此我想要记录下来。 本博客将介绍如…

操作002:HelloWorld

文章目录 操作002&#xff1a;HelloWorld一、目标二、具体操作1、创建Java工程①消息发送端&#xff08;生产者&#xff09;②消息接收端&#xff08;消费者&#xff09;③添加依赖 2、发送消息①Java代码②查看效果 3、接收消息①Java代码②控制台打印③查看后台管理界面 操作…

机器视觉检测相机基础知识 | 颜色 | 光源 | 镜头 | 分辨率 / 精度 / 公差

注&#xff1a;本文为 “keyence 视觉沙龙中机器视觉检测基础知识” 文章合辑。 机器视觉检测基础知识&#xff08;一&#xff09;颜色篇 视觉检测硬件构成的基本部分包括&#xff1a;处理器、相机、镜头、光源。 其中&#xff0c;和光源相关的最重要的两个参数就是光源颜色和…

【体验官招募】SoFlu - JavaAI 开发助手:开启智能开发新时代

你是否有过这样的经历&#xff1f;在深夜的办公室里&#xff0c;面对紧急的 Java 项目&#xff0c;看着厚厚的需求文档&#xff0c;你是否感到无从下手&#xff1f; 当你尝试理解客户那些复杂又模糊的需求时&#xff0c;是否会因为要和产品经理反复沟通确认每一个细节而感到厌…

自学记录HarmonyOS Next DRM API 13:构建安全的数字内容保护系统

在完成了HarmonyOS Camera API的开发之后&#xff0c;我开始关注更复杂的系统级功能。在浏览HarmonyOS Next文档时&#xff0c;我发现了一个非常有趣的领域&#xff1a;数字版权管理&#xff08;DRM&#xff09;。最新的DRM API 13提供了强大的工具&#xff0c;用于保护数字内容…

【HENU】河南大学计院2024 操作系统 简答题复习

和光同尘_我的个人主页 一直游到海水变蓝。 单项选择 15x2 30 判断 10x1 10 简答 3x10 30 综合 3x10 30 简答题 简述操作系统的四个基本特征。 并发性 共享性 虚拟性 异步性 并发性是最重要特性&#xff0c;其它三种特性以此为前提。 并发 并发(Concurrence)&#…