From 1194560bee91dcc96467baf7d1e34a1e57b27b61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=97=E6=98=A5=E7=BF=94=EF=BC=88=E7=A9=B7=E5=91=90-?= =?UTF-8?q?=EF=BC=89?= <907079131@qq.com> Date: Sun, 19 May 2024 20:21:05 +0800 Subject: [PATCH] =?UTF-8?q?=E9=98=BF=E5=B7=B4=E9=98=BF=E5=B7=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...04\344\273\266\351\200\232\344\277\241.md" | 2 +- .../20240509_Vue\350\267\257\347\224\261.md" | 106 +++++++ ...4\345\245\227&\345\221\275\345\220\215.md" | 59 ++++ ...13\345\274\217\345\257\274\350\210\252.md" | 298 ++++++++++++++++++ ...66\346\200\201\347\256\241\347\220\206.md" | 187 +++++++++++ ...57\347\224\261\345\272\224\347\224\250.md" | 34 ++ 6 files changed, 685 insertions(+), 1 deletion(-) create mode 100644 "\346\236\227\346\230\245\347\277\224/20240509_Vue\350\267\257\347\224\261.md" create mode 100644 "\346\236\227\346\230\245\347\277\224/20240510_\345\265\214\345\245\227&\345\221\275\345\220\215.md" create mode 100644 "\346\236\227\346\230\245\347\277\224/20240511_\347\274\226\347\250\213\345\274\217\345\257\274\350\210\252.md" create mode 100644 "\346\236\227\346\230\245\347\277\224/20240513_\347\212\266\346\200\201\347\256\241\347\220\206.md" create mode 100644 "\346\236\227\346\230\245\347\277\224/20240516_\350\267\257\347\224\261\345\272\224\347\224\250.md" diff --git "a/\346\236\227\346\230\245\347\277\224/20240428_\345\255\220\347\273\204\344\273\266\345\220\221\347\210\266\347\273\204\344\273\266\351\200\232\344\277\241.md" "b/\346\236\227\346\230\245\347\277\224/20240428_\345\255\220\347\273\204\344\273\266\345\220\221\347\210\266\347\273\204\344\273\266\351\200\232\344\277\241.md" index 077477a..7efd27a 100644 --- "a/\346\236\227\346\230\245\347\277\224/20240428_\345\255\220\347\273\204\344\273\266\345\220\221\347\210\266\347\273\204\344\273\266\351\200\232\344\277\241.md" +++ "b/\346\236\227\346\230\245\347\277\224/20240428_\345\255\220\347\273\204\344\273\266\345\220\221\347\210\266\347\273\204\344\273\266\351\200\232\344\277\241.md" @@ -2,7 +2,7 @@ 子组件向父组件通信可以理解成: - 子组件向父组件传值 - - 子组件调用子父组件的方法 + - **子组件调用子父组件的方法** 与父组件向子组件通信不同的是,子组件调用父组件方法的同时就可以向父组件传值,使用$emit方法和自定义事件。 diff --git "a/\346\236\227\346\230\245\347\277\224/20240509_Vue\350\267\257\347\224\261.md" "b/\346\236\227\346\230\245\347\277\224/20240509_Vue\350\267\257\347\224\261.md" new file mode 100644 index 0000000..c2443c0 --- /dev/null +++ "b/\346\236\227\346\230\245\347\277\224/20240509_Vue\350\267\257\347\224\261.md" @@ -0,0 +1,106 @@ +### 一、单页应用 +#### 1.概念 +- 单页应用是基于移动web的应用或者网站,大多数是由一个完整的HTML页面组成 +- 单页页面之间的切换是通过不断替换HTML内容或者隐藏/显示所需要的内容 +- 多页应用是由多个HTML页面组成,页面切换通过a标签完成,每次打开都是新的HTML页面 +#### 2.单页面特点 +1. 页面加载时会将整个应用资源都下载下来,时间稍长 +2. 页面内容由前端js逻辑生成,在初始化时由一个空的div占位,不利于搜索引擎优化 +3. 页面切换一般通过修改浏览器的哈希来记录和标识 + +### 二、Vue Router概述 +#### 1.router的安装/引入 +##### CDN引入 +~~~html +https://unpkg.com/vue-router@4.0.15/dist/vue-router.global.js +~~~ +##### npm安装 +~~~js +npm install vue-router@4 +// 引入 +import { createRouter } from 'vue-router' +~~~ +#### 2.实现一个简单的路由 +1. 在入口文件main.js中引入 +~~~js +// 导入组件 +import Home from './components/Home.vue'; +import About from './components/About.vue'; +import Index from './components/Index.vue' +import HelloWorld from './components/HelloWorld.vue'; +// 在这里将HelloWorld作为根组件 +let app = createApp(HelloWorld); +// 创建路由实例 +const router = createRouter({ + // 使用浏览器历史记录的路由模式 + history:createWebHistory(), + routes:[ + {path:'/about',component:About}, + {path:'/index',component:Index}, + {path:'/',component:Home} + ] +}); +// 将路由器添加到应用程序实例 +app.use(router); +app.mount('#app'); +~~~ +2. 在组件中编写代码 +~~~html + + +~~~ +###### 关于【router-link】和【router-view】 +分别用于导航链接和路由视图的渲染,可以理解为路由的入口和出口 +- router-link:路由链接组件,也可以使用RouterLink写法(这种写法需要引入) +- router-view:路由视图组件,可以不和router-link写在同一个组件文件中 +#### 3.动态路由 +动态路由是指路由的路径中包含参数,并且这些参数的值是根据用户的输入或应用状态动态确定的,例如/user/:userId 中的 :userId 就是一个动态参数 +~~~js +// main.js +const router = createRouter({ + history:createWebHistory(), + routes:[ + {path:'/index/:id',component:Index}, + // 通过:id方式可以指定路由的路径参数 + {path:'/',component:Home} + ] +}); +~~~ +~~~html + + + + +~~~ +###### 拓展: +~~~js + {path:'/about/:data*',component:About} +// 当使用参数*的方式来匹配,about后面所有字符都会被当做data参数 +~~~ +#### 4.监听路由 +可以使用监听属性来响应路由变化,但是要注意两种情况: +- 当路由切换的是同一个组件时,比如同样是Index,只是参数不一样,监听方法可以写在子组件Index中 +- 当路由切换的是不同组件,例如Index和About,监听方法需要写在根组件中才能接收到变化 diff --git "a/\346\236\227\346\230\245\347\277\224/20240510_\345\265\214\345\245\227&\345\221\275\345\220\215.md" "b/\346\236\227\346\230\245\347\277\224/20240510_\345\265\214\345\245\227&\345\221\275\345\220\215.md" new file mode 100644 index 0000000..2875166 --- /dev/null +++ "b/\346\236\227\346\230\245\347\277\224/20240510_\345\265\214\345\245\227&\345\221\275\345\220\215.md" @@ -0,0 +1,59 @@ +### 一、嵌套路由 +- 路由里面可以包含子路由 +- 主要特点: + - 层次结构 + - 模块化 + - 嵌套视图 +~~~html +~~~ + +### 二、命名路由 +可以为配置路由中的每个path提供name,同时提供params参数可以让路由之间切换时,传递更复杂的数据 +~~~js +const router = createRouter({ + history: createWebHistory(), + routes: [ + { + path:'/data/:user_id', + name:'user', + component:User + }, + ] +}); +~~~ +~~~html +// 路由切换时可以传递id +用户 +~~~ + +### 三、命名视图 +给不同的router-view设置name,实现不同的router-view显示不同的内容,呈现多个视图 +~~~js +const router = createRouter({ + history: createWebHistory(), + routes: [ + { + path:'/', + components:{ + top:Home, + middle:User, + buttom:About + } + } + ] +}); +~~~ +~~~html + +~~~ \ No newline at end of file diff --git "a/\346\236\227\346\230\245\347\277\224/20240511_\347\274\226\347\250\213\345\274\217\345\257\274\350\210\252.md" "b/\346\236\227\346\230\245\347\277\224/20240511_\347\274\226\347\250\213\345\274\217\345\257\274\350\210\252.md" new file mode 100644 index 0000000..cae1e35 --- /dev/null +++ "b/\346\236\227\346\230\245\347\277\224/20240511_\347\274\226\347\250\213\345\274\217\345\257\274\350\210\252.md" @@ -0,0 +1,298 @@ +### 一、编程式导航 +#### 1.router.push导航到不同的位置 +- 想要导航到不同的 URL,可以使用 router.push 方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,会回到之前的 URL +- 所以点击 【router-link :to="..."】(声明式) 相当于调用 router.push(...) (编程式) +- router.push的参数可以是: +~~~js +// 先解构 +import { useRouter , useRoute } from 'vue-router' +// 定义路由实例 +const router=useRouter() +// 1定义当前路由对象 +const route=useRoute() + +// 字符串路径 +router.push('/users/1'); +// 带有路径的对象 +router.push({ path: '/users/eduardo' }); +// 命名的路由,并加上参数,让路由建立 url +router.push({ name: 'user', params: { username: 'eduardo' } }); +// 带查询参数,结果是 /register?plan=private +router.push({ path: '/register', query: { plan: 'private' } }) +// 带 hash,结果是 /about#team +router.push({ path: '/about', hash: '#team' }) + +/* 注意: + 1.如果提供了path,params会被忽略 + 2.使用params时,可以提供string或number参数,任何其他类型(如对象、布尔等)都将被自动字符串化;对于可选参数,可以提供一个空字符串("")或 null 来移除它 + 3.query会在浏览器地址栏中显示参数,params不显示 + */ +~~~ +#### 2.router.replace替换当前位置 +- 作用类似于 router.push,唯一不同的是,它在导航时不会向 history 添加新记录,它取代了当前的条目 +- 两种写法: +~~~js +// 声明式写法 + +// 编程式写法 +router.replace({ path: '/home'}) +// 相当于 +router.push({ path: '/home', replace: true }) +~~~ +#### 3.router.go横跨历史记录 +该方法采用一个整数作为参数,表示在history记录中向前或者后退多少步,类似于 window.history.go(nnumber) +~~~js +// 向前移动一条记录,与 router.forward() 相同 +router.go(1) +// 返回一条记录,与 router.back() 相同 +router.go(-1) +// 前进 3 条记录 +router.go(3) +// 如果没有那么多记录,静默失败 +router.go(-100) +router.go(100) +~~~ +#### 4.router.resolve解析路由 +- 有时候,我们可能需要在代码中动态地获取路由信息,这时就可以使用router.resolve方法,返回一个包含路由解析结果的 Promise 对象 +- 参数是一个包含以下属性的对象: + - name:要解析的路由名称 + - params:路由参数 + - query:查询参数 + - hash:路由的哈希值 + - meta:元信息 + ~~~js + const router = createRouter({ + history: createWebHistory(), + routes: [ + { path: '/', name: 'home' }, + { path: '/about/:id', name: 'about' }, + ], + }); + const route = router.resolve({ + name: 'about', + params: { id: 123 }, + query: { foo: 'bar' }, + }); + ~~~ + +### 二、路由组件传参 +#### 1.props传参 +采用props将参数直接赋值给组件,将$router和组件进行解耦 +~~~js + let props = defineProps(['id']); + const User = { + // 请确保添加一个与路由参数完全相同的 prop 名 + props: ['id'], + template: '
User {{ id }}
' + } + const routes = [{ path: '/user/:id', component: User, props: true }]; + // 当 props 设置为 true 时,route.params 将被设置为组件的 props + +// 对于命名视图 + const routes = [ + { + path: '/user/:id', + components: { default: User, sidebar: Sidebar }, + props: { default: true, sidebar: false } + } + ] +// 对于对象 + const routes = [ + { + path: '/promotion/from-newsletter', + component: Promotion, + props: { newsletterPopup: false } + } + ] +// 对于函数 + const routes = [ + { + path: '/search', + component: SearchUser, + props: route => ({ query: route.query.q }) + } + ] +~~~ +#### 2.RouterView +还可以通过RouterView插槽传递任意参数 +~~~html + + + + + +~~~ + +### 三、路由重定向 +当用户访问某个路由时,自动跳转到另一个指定的路由 +~~~js +const router = createRouter({ + ... + routes:[ + {path:'/home',redirect:'/h'}, //直接从/home重定向到/h + {path:'/about',redirect:{name:'a'}}, //从/about重定向到命名路由a + {path:'/index',redirect:(to)=>{ + // 接受目标路由作为传入参数 + // return返回重定向的字符串路径或者路由对象 + }} + ] +}) +/* redirect可以接收: + 路径字符串 + 路由对象 + 返回路径或路由对象的方法 +*/ +~~~ + +### 四、路由别名 +为路由路径设置一个额外的名称,以便在导航中引用它,例如:/a的别名是/b,当我们访问/b,URL会保持为/b,但是匹配路由为/a +~~~js +const router = createRouter({ + history: createWebHistory(), + routes: [ + { path: '/', component: Home }, + // 定义带别名的路由 + { + path: '/home', + component: Home, + name: 'home', + alias: '/admin' } + ] +}); +~~~ + +### 五、路由元信息 +希望将任意信息附加到路由上,如过渡名称、谁可以访问路由等,可以通过接收属性对象的meta属性来实现,并且它可以在路由地址和导航守卫上都被访问到,通过$route.meta.xx获取数据 +~~~js +const routes = [ + { + path: '/home', + component:Home, + children: [ + { + path: 'new', + component: PostsNew, + // 只有经过身份验证的用户才能创建帖子 + meta: { requiresAuth: true }, + }, + { + path: ':id', + component: PostsDetail + // 任何人都可以阅读文章 + meta: { requiresAuth: false }, + }, + { + path:'/', + component:Index, + meta:{ title:'首页' } + } + ] + } +] +~~~ + +### 六、滚动行为 +可以自定义路由切换时页面如何滚动,注意的是这个功能只在支持 history.pushState 的浏览器中可用 +~~~js +// 当创建一个 Router 实例,提供一个 scrollBehavior 方法 +const router = createRouter({ + history: createWebHashHistory(), + routes: [...], + scrollBehavior (to, from, savedPosition) { + // return 期望滚动到哪个的位置 + // 配置跳转到原先滚动的位置 + if (savedPosition) { + return savedPosition + } else { + return { top: 0 } + } + } +}) +/* 参数savedPosition: + 结构是{left:num,top:num},例如:始终滚动到顶部 return { top: 0 } + 如果页面不可滚动,就是默认值{left:0,top:0} + */ +// 滚动到锚点 +const router = createRouter({ + scrollBehavior(to, from, savedPosition) { + if (to.hash) { + return { + el: to.hash, + } + } + }, +}) +// 延迟滚动 +onst router = createRouter({ + scrollBehavior(to, from, savedPosition) { + return new Promise((resolve, reject) => { + setTimeout(() => { + resolve({ left: 0, top: 0 }) + }, 500) + }) + }, +}) +~~~ + +### 七、keep-alive缓存状态 +#### 1.基本使用 +将组件缓存在内存当中,防止重复渲染DOM,属于消耗内存获取速度 +~~~js +// main.js +const routes = [ + { + path: '/home', + component:Home, + //利用元数据meta控制页面是否需要使用缓存 + meta: { + keepAlive: true + }, + component: () => import("@/views/keepAliveTest/index.vue") + }, + { + path:'/about', + component:About + } +] +~~~ +~~~html + +home +about + + + + + + + +~~~ +#### 2.动态设置路由keep-alive属性 +有些时候我们用完了keepalive缓存之后,想让页面不再保持缓存,或者设置下一个页面keepalive,也这个时候我们可以改变meta的keepAlive值来去除页面缓存,使用beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave,使用方式如下: +~~~js +// to为即将跳转的路由,from为上一个页面路由 +beforeRouteLeave(to, from,+ next) { + // 设置下一个路由的 meta + to.meta.keepAlive = false; + next(); +} +~~~ +#### 3.生命周期方法 +- activated:当vue-router的页面被打开时,会触发这个函数 +- deactivated:当被关闭,会触发这个函数 +~~~js +activated() { + // 页面每次进入将手机动态验证码置为空 + $refs.mobPwdCode.inputValue = ''; +}, +deactiveted(){ + alert('确认关闭?') +} +~~~ \ No newline at end of file diff --git "a/\346\236\227\346\230\245\347\277\224/20240513_\347\212\266\346\200\201\347\256\241\347\220\206.md" "b/\346\236\227\346\230\245\347\277\224/20240513_\347\212\266\346\200\201\347\256\241\347\220\206.md" new file mode 100644 index 0000000..6df3115 --- /dev/null +++ "b/\346\236\227\346\230\245\347\277\224/20240513_\347\212\266\346\200\201\347\256\241\347\220\206.md" @@ -0,0 +1,187 @@ +### 一、什么是状态管理 +- 对于大项目来说,组件多,状态零散分布在许多组件和组件之间的交互操作中,这时候就需要状态管理,理论上来说,每一个 Vue 组件实例都已经在“管理”它自己的响应式状态了 +- 示例如下: +~~~html + + + + + +~~~ +- 上面代码中,当add方法不断被调用时,count的值就会不断增加显示在页面上,这样叫做“状态自管理”,由以下几个部分组成: + - 状态(state):驱动整个应用的数据源 + - 视图(view):对状态的一种声明式映射 + - 交互(actions):状态根据用户在视图中的输入而作出相应变更的可能方式 + + 但是以上只是一个单向数据流,即从view上出发action改变state,state的改变最终回到view上,然而,当我们有多个组件共享一个共同的状态时,就会出现以下问题: + - 多个组件依赖于同一状态 + - 来自不同视图的交互也可能需要更改同一份状态 + +### 二、使用Pinia实现状态管理 +#### 1.安装 +~~~js +npm install pinia +~~~ +#### 2.创建pinia实例 +~~~js +// main.js +import {createPinia} from 'pinia'; +let app = createApp(App); +const pinia = createPinia(); +app.use(pinia); +~~~ +#### 3.pinia的基本用法 +##### 1.创建store实例 +~~~js +// stores/counter.js +import { defineStore } from 'pinia'; + +// Option对象写法 +export const useCountStore = defineStore('count',{ + state:()=>{ + return {count:0} + }, + // 也可以写为 + // state: () => ({ count: 0 }),这里用大括号是为了让他以对象的形式返回出来 + actions:{ + onClick(){ + this.count++ + } + } +}) + +// Setup函数写法 +export const useCountStore = defineStore('count',()=>{ + let count = ref(0); + function onClick(){ + count.value++; + }; + return{count,onClick} +}) +~~~ +store的参数: + - 第一个参数要求不可重复 + - 第二个参数可以接受两种类型的值:Setup函数或Option对象 + - Setup: + - ref() 就是 state 属性,computed() 就是 getters,computed() 就是 getters + - 在 setup store 中返回 state 的所有属性 + - Option:state 是 store 的数据 (data),getters 是 store 的计算属性 (computed),而 actions 则是方法 (methods) +##### 2.在组件中使用store +~~~html + + +~~~ +从store解构 +为了从 store 中提取属性时保持其响应性,需要使用 storeToRefs(),但是可以直接从 store 中解构 action +~~~js +import {storeToRefs} from 'pinia'; +const counter = useCountStore(); +// 并且会跳过所有的 action 或非响应式 (不是 ref 或 reactive) 的属性 +const {count} = storeToRefs(counter); +// 作为 action 的 onClick 可以直接解构 +const {onClick} = counter +~~~ + +### 三、State +#### 1.概念 +state是 store 的核心,被定义为一个返回初始状态的函数 +#### 2.访问 +~~~js +const counter = useCountStore(); +// 访问的是state的初始状态 +// 也可以直接对其读写 +const data = counter.count; +~~~ +#### 3.重置 +通过调用 store 的 $reset() 方法将 state 重置为初始值 +~~~js +// counter.js +export const useCountStore = defineStore('count',()=>{ + let count = ref(0); + function onClick(){ + count.value++; + }; + // 创建自己的 $reset() 方法 + function $reset(){ + count.value=0; + } + return{count,onClick,$reset} +}) +~~~ +~~~html + + + + +~~~ +#### 4.变更 +用 store.count++ 直接改变 store,还可以调用 $patch 方法 +#### 5.替换 +不能完全替换掉 store 的 state,因为那样会破坏其响应性,还是使用patch + +### 四、Getter +Getter 完全等同于 store 的 state 的计算属性 +~~~js +export const useCountStore = defineStore('count',{ + state:()=>{ + return {count:1} + }, + actions:{ + onClick(){ + this.count++ + } + }, + getters: { + doubleCount: (state) => state.count * 2, + }, +}) +~~~ +- 大多数时候,getter 仅依赖 state,不过,有时它们也可能会使用其他 getter,通过 this,你可以访问到其他任何 getter +- getter 只是幕后的计算属性,所以不可以向它们传递任何参数。不过,你可以从 getter 返回一个函数,该函数可以接受任意参数 +~~~js +export const useUsersStore = defineStore('users', { + getters: { + getUserById: (state) => { + // 可以返回函数,这个返回的函数可以接受容易参数 + return (userId) => state.users.find((user) => user.id === userId) + }, + }, +}) + // 调用 + +~~~ + +### 五、Action +Action 相当于组件中的方法,也可通过 this 访问整个 store 实例,而且是可以异步的 \ No newline at end of file diff --git "a/\346\236\227\346\230\245\347\277\224/20240516_\350\267\257\347\224\261\345\272\224\347\224\250.md" "b/\346\236\227\346\230\245\347\277\224/20240516_\350\267\257\347\224\261\345\272\224\347\224\250.md" new file mode 100644 index 0000000..4da751f --- /dev/null +++ "b/\346\236\227\346\230\245\347\277\224/20240516_\350\267\257\347\224\261\345\272\224\347\224\250.md" @@ -0,0 +1,34 @@ +### 通过路由方式实现增删改查 +#### 思路: +1. 配置路由: +~~~js +// src/router/router.js + import { createRouter, createWebHistory } from 'vue-router'; + import Home from './components/Home.vue'; + import ManageItem from './components/Edit.vue'; + const router = createRouter({ + history: createWebHistory(), + routes:[ + {path:'/home',component:Home}, + { path: '/edit/:id?', + name: 'Edit', + component: Edit, + }, + {path:'/',component:App}, + // 这里也可以加上redirect:'/home' 表示一打开就跳转到列表页,但是不推荐 + ] + } ); + export default router; +~~~ +2. 配置入口文件: +~~~js + import router from '.Router/router'; + const app = createApp(App); + app.use(router); +~~~ +3. 创建视图组件:Home.vue(列表页面)、Edit.vue(新增编辑页)、 + - 列表页部分代码如下: + - ![code-2024-5-1916:59:14.png](https://gitee.com/huangxuefang0929/xiu_img/raw/master/code-2024-5-1916:59:14.png) + + - 编辑页部分代码如下: + - ![edit-2024-5-1916:59:02.png](https://gitee.com/huangxuefang0929/xiu_img/raw/master/edit-2024-5-1916:59:02.png) \ No newline at end of file -- Gitee