前端 Vue3 框架 | 地址 |
---|---|
(一)基础语法 | https://blog.csdn.net/weixin_42771853/article/details/129956268 |
(二)组件化基础 | https://blog.csdn.net/weixin_42771853/article/details/129957080 |
(三)Composition API | https://blog.csdn.net/weixin_42771853/article/details/129957511 |
(四)Vue3 全家桶 router、vuex、pinia、axios | https://blog.csdn.net/weixin_42771853/article/details/129957595 |
发展历程:
hash模式:URL的hash也就是锚点(#), 本质上是改变window.location的href属性
history模式:history接口是HTML5新增的,通过一些模式函数来改变URL但不刷新页面
hash | history |
---|---|
有 # 号 | 没有 # 号 |
能够兼容到IE8 | 只能兼容到IE10 |
实际的url之前使用哈希字符,这部分url不会发送到服务器,不需要在服务器层面上进行任何处理 | 每访问一个页面都需要服务器进行路由匹配生成 html 文件再发送响应给浏览器,消耗服务器大量资源 |
刷新不会存在 404 问题 | 浏览器直接访问嵌套路由时,会报 404 问题。 |
不需要服务器任何配置 | 需要在服务器配置一个回调路由 |
路由用于设定访问路径,将路径和组件映射起来
npm install vue-router
使用vue-router的步骤:
history: createWebHashHistory()
history: createWebHistory()
让路径默认跳到到首页
把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就会更加高效
{ path: "/home", component: () => import(/* webpackChunkName: 'home' */"../Views/Home.vue") }
// webpackChunkName 规定分包后的文件名称
如果需要将给定匹配模式的路由映射到同一个组件:可以在路径中使用一个动态字段来实现,我们称之为路径参数
在router-link中进行如下跳转:
获取动态路由中对应的值:
$route.params
获取值this.$route.param
s获取值没有匹配到的路由,我们通常会匹配到固定的某个页面:编写一个动态路由用于匹配所有页面
通过 $route.params.pathMatch获取到传入的参数:
会出现多层路由嵌套的情况:
希望实现其他元素(按钮、span等)的跳转,可以通过 useRouter 来获取
通过query的方式来传递参数,在界面中通过 $route.query 来获取参数
某些情况下我们可能需要动态的来添加路由:
删除路由有以下三种方式:
路由的其他方法补充:
导航守卫主要用来通过跳转或取消的方式守卫导航
全局的前置守卫beforeEach在导航触发时被回调:
完整的导航解析流程:
在开发中,应用程序需要处理各种各样的数据,这些数据需要保存在应用程序中的某一个位置。对于这些数据的管理我们就 称之为是状态管理。
随着发展,JavaScript需要管理的状态越来越多,越来越复杂
此时我们将组件的内部状态抽离出来,以一个全局单例的方式来管理
npm install vuex
Vuex应用的核心就是store(仓库):包含着应用中大部分的状态(state)
在组件中使用store,有三种方式:
Vuex 使用单一状态树,便于维护:
见 2.2
如果我们有很多个状态都需要获取,可以使用mapState的辅助函数:
分为在options api中使用 & 在setup中使用
<template>
<div class="app">
<button @click="incrementLevel">修改level</button>
<!-- 1.在模板中直接使用多个状态 -->
<!-- <h2>name: {{ $store.state.name }}</h2>
<h2>level: {{ $store.state.level }}</h2>
<h2>avatar: {{ $store.state.avatarURL }}</h2> -->
<!-- 2.计算属性(映射状态: 数组语法) -->
<h2>name: {{ name() }}</h2>
<h2>level: {{ level() }}</h2>
<!-- 3.计算属性(映射状态: 对象语法) -->
<h2>name: {{ sName }}</h2>
<h2>level: {{ sLevel }}</h2>
<!-- 4.setup计算属性(映射状态: 对象语法) -->
<h2>name: {{ cName }}</h2>
<h2>level: {{ cLevel }}</h2>
<!-- 5.setup计算属性(映射状态: 对象语法) -->
<h2>name: {{ name }}</h2>
<h2>level: {{ level }}</h2>
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
computed: {
fullname() {
return "xxx";
},
...mapState(["name", "level", "avatarURL"]),
...mapState({
sName: (state) => state.name,
sLevel: (state) => state.level,
}),
},
};
</script>
<script setup>
import { computed, toRefs } from "vue";
import { mapState, useStore } from "vuex";
import useState from "../hooks/useState";
// 1.一步步完成
// const { name, level } = mapState(["name", "level"])
// const store = useStore()
// const cName = computed(name.bind({ $store: store }))
// const cLevel = computed(level.bind({ $store: store }))
// 2.使用useState
// const { name, level } = useState(["name", "level"])
// 3.直接对store.state进行解构(推荐)
const store = useStore();
const { name, level } = toRefs(store.state);
function incrementLevel() {
store.state.level++;
}
</script>
某些属性我们可能需要经过变化后来使用,这个时候可以使用getters
如果有多个getter中的函数需要获取,可以使用mapGetters的辅助函数:
<template>
<div class="app">
<button @click="changeAge">修改name</button>
<h2>doubleCounter: {{ doubleCounter }}</h2>
<h2>friendsTotalAge: {{ totalAge }}</h2>
<h2>message: {{ message }}</h2>
<!-- 根据id获取某一个朋友的信息 -->
<h2>id-111的朋友信息: {{ getFriendById(111) }}</h2>
<h2>id-112的朋友信息: {{ getFriendById(112) }}</h2>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters(["doubleCounter", "totalAge", "getFriendById"])
}
}
</script>
<script setup>
import { computed, toRefs } from 'vue';
import { mapGetters, useStore } from 'vuex'
const store = useStore()
// 1.使用mapGetters
// const { message: messageFn } = mapGetters(["message"])
// const message = computed(messageFn.bind({ $store: store }))
// 2.直接解构, 并且包裹成ref(会报警告)
// const { message } = toRefs(store.getters)
// 3.针对某一个getters属性使用computed(推荐)
const message = computed(() => store.getters.message)
function changeAge() {
store.state.name = "kobe"
}
</script>
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation
如果有多个mutation中的函数需要获取,可以使用mapMutations的辅助函数:
<template>
<div class="app">
<button @click="changeName('王小波')">修改name</button>
<button @click="incrementLevel">递增level</button>
<button @click="changeInfo({ name: '王二', level: 200 })">修改info</button>
<h2>Store Name: {{ $store.state.name }}</h2>
<h2>Store Level: {{ $store.state.level }}</h2>
</div>
</template>
<script>
import { mapMutations } from "vuex";
import { CHANGE_INFO } from "@/store/mutation_types";
export default {
computed: {},
methods: {
btnClick() {
console.log("btnClick");
},
...mapMutations(["changeName", "incrementLevel", CHANGE_INFO]),
},
};
</script>
<script setup>
import { mapMutations, useStore } from "vuex";
import { CHANGE_INFO } from "@/store/mutation_types";
const store = useStore();
// 1.手动的映射和绑定
const mutations = mapMutations(["changeName", "incrementLevel", CHANGE_INFO]);
const newMutations = {};
Object.keys(mutations).forEach((key) => {
newMutations[key] = mutations[key].bind({ $store: store });
});
const { changeName, incrementLevel, changeInfo } = newMutations;
</script>
一条重要的原则就是要记住 mutation 必须是同步函数
Action类似于mutation,不同在于:
参数context:
实例:渲染页面banner
默认情况下,模块内部的action和mutation仍然是注册在全局的命名空间中的,这样使得多个模块能够对同一个 action 或 mutation 作出响应。此时需要注意,模块中定义的名称不要和根模块进行重复
如果我们希望模块具有更高的封装度和复用性,可以添加 namespaced: true
的方式使其成为带命名空间的模块
npm install pinia
<template>
<div class="home">
<h2>Home View</h2>
<h2>name: {{ name }}</h2>
<h2>age: {{ age }}</h2>
<h2>level: {{ level }}</h2>
<button @click="changeState">修改state</button>
<button @click="resetState">重置state</button>
</div>
</template>
<script setup>
import useUser from "@/stores/user";
import { storeToRefs } from "pinia";
const userStore = useUser();
const { name, age, level } = storeToRefs(userStore);
function changeState() {
// 1.一个个修改状态
userStore.name = "kobe";
userStore.age = 20;
userStore.level = 200;
// 2.一次性修改多个状态
// userStore.$patch({
// name: "james",
// age: 35
// })
// 3.替换state为新的对象
// const oldState = userStore.$state
// userStore.$state = {
// name: "curry",
// level: 200
// }
// console.log(oldState === userStore.$state) // true
}
function resetState() {
userStore.$reset();
}
</script>
// 定义关于counter的store
import { defineStore } from "pinia";
// 引入其他module
import useUser from "./user";
const useCounter = defineStore("counter", {
state: () => ({
count: 99,
friends: [
{ id: 111, name: "why" },
{ id: 112, name: "kobe" },
{ id: 113, name: "james" },
],
}),
getters: {
// 1.基本使用
doubleCount(state) {
return state.count * 2;
},
// 2.一个getter引入另外一个getter
doubleCountAddOne() {
// this是store实例
return this.doubleCount + 1;
},
// 3.getters也支持返回一个函数
getFriendById(state) {
return function (id) {
for (let i = 0; i < state.friends.length; i++) {
const friend = state.friends[i];
if (friend.id === id) {
return friend;
}
}
};
},
// 4.getters中用到别的store中的数据
showMessage(state) {
// 1.获取user信息
const userStore = useUser();
// 2.拼接信息
return `name:${userStore.name}-count:${state.count}`;
},
},
actions: {
increment() {
this.count++;
},
incrementNum(num) {
this.count += num;
},
},
});
export default useCounter;
在action中可以通过this访问整个store实例的所有操作
功能特点:
相比与原生fetch,axios自动适配浏览器和node.js,同时还提供多种附加功能(拦截请求等)
为什么需要创建实例
axios.create()
axios的也可以设置拦截器:拦截每次请求和响应
在项目中,我们会在多个地方使用到axios进行网络请求。但如果axios库发生停止维护的情况时,需要利用别的库进行替代。
此时一一替换非常不方便,因此我们事先对axios进行封装。
在service文件夹中index.js进行封装
使用时引入该文件即可:
对默认CSS样式进行重置:
封装一个工具utils:
// load_assets.js
export const getAssetURL = (image) => {
// 参数一: 相对路径
// 参数二: 当前路径的URL
return new URL(`../assets/img/${image}`, import.meta.url).href
}
<template>
<img :src="getAssetURL(item.image)" alt="" />
</template>
<script setup>
import { getAssetURL } from "@/utils/load_assets.js";
</script>
http://vant-contrib.gitee.io/vant/v3/#/zh-CN/quickstart
封装一个class到common.css中
.top-page {
position: relative;
z-index: 9;
height: 100vh;
background-color: #fff;
overflow-y: auto;
}
.city {
// 布局滚动
.content {
height: calc(100vh - 98px); // 98px为top的高度
overflow-y: auto;
}
}
<!-- city.vue -->
// 从Store中获取数据
const cityStore = useCityStore()
cityStore.fetchAllCitiesData()
const { allCities } = storeToRefs(cityStore)
<!-- store->modules->city.js -->
import { getCityAll } from "@/services";
import { defineStore } from "pinia";
const useCityStore = defineStore("city", {
state: () => ({
allCities: {},
currentCity: {
cityName: "广州"
}
}),
// 通过axios发送数据请求,得到的数据存储在store中
actions: {
async fetchAllCitiesData() {
const res = await getCityAll()
this.allCities = res.data
}
}
})
export default useCityStore
<!-- services->modules->city.js -->
// 通过axios发送数据请求
import hyRequest from '../request' // 对axios组件的再封装
export function getCityAll() {
return hyRequest.get({
url: "/city/all"
})
}
格式化日期:npm install dayjs
防抖/节流函数:npm install underscore