# idooy-vue-study **Repository Path**: antirust/idooy-vue-study ## Basic Information - **Project Name**: idooy-vue-study - **Description**: vue2学习过程中的练习代码和笔记 - **Primary Language**: JavaScript - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-04-17 - **Last Updated**: 2023-06-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: vue2 ## README - [观看的视频教程资源(尚硅谷)](https://www.bilibili.com/video/BV1Zy4y1K7SH);无意间翻看了几集之后,如获至宝;有种相见恨晚的感觉,立即决定重新学习。 - 本人借此机会由衷的感谢尚硅谷提供的众多优秀教学视频;愿其越来越辉煌。 - 当前页所记录的知识点都来自于视频教程;[本次学习过程练习的代码地址](https://gitee.com/antirust/vue-study/tree/master/repeat-study) # 散落的知识点 - 同时满足以下三个条件会出现跨域问题 - 浏览器的安全策略,同源策略 - 请求是XMLHttpRequest对象发出,即务必是xhr,比如Ajax请求 - 请求发出的“源”和目标服务器不同源 - 两个重要原则 - 被Vue所管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象 - 所有不被Vue所管理的函数(定时器的回调、ajax的回调函数、Promise的回调函数等),最好写成箭头函数,这样this的指向才是vm 或 组件实例对象 - VueComponent的实例对象,简称`vc`,即组件实例对象;`Vue.extend(options)`; - 每次调用Vue.extend,返回的都是一个全新的VueComponent - Vue的实例对象,简称`vm`:`new Vue(options)` - this指向: - 组件配置中,data函数、methods中的函数、watch中的函数、computed中的函数,它们的this均是【VueComponent实例对象】 - vm配置中,data函数、methods中的函数、watch中的函数、computed中的函数,它们的this均是【Vue实例对象】 - ref标签属性: - 被用来给元素或者子组件注册引用信息(id的代替者) - 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc) - 使用方式:`

`或者`<组件名 ref='xxx'>` - 获取:`this.$refs.xxx` - ` ``` ​ 接下来将之前非单文件组件中的`School`组件改写成单文件组件的`School.vue`;下图是各个部分的比对详情 ![](./repeat-study-img/vueFile.png) ## 脚手架作用 ​ 下面将一步步的抽取组件往脚手架的方向靠、有助于我们更好的理解脚手架的作用;之前聊到过在Vue的组件开发中,VM关联管理所有的组件实例对象;但是在开发中这其中还有一个叫`App`的组件介于VM和所有组件实例对象之间;是一人之下万人之上的存在;这样VM只关心和管理App组件;其余的组件全部由App组件进行统一的组装。所以VM通过App间接的对所有的组件进行了管理。下图就是将`School和Student`组件导入`App` ![](./repeat-study-img/appvue.png) ​ App组件将所有的组件都组装起来不假,可是组件必须要在Vue实例中才有用呀;App得把搜集起来的组件交给VM对象才可以。那么这个Vue实例在哪里呢?这个专门创建Vue实例的地方就是`main.js有的脚手架也叫index.js`文件;以及模板容器怎么配置?vue.js在何时引入? ![](./repeat-study-img/vuecopeapp.png) 现在代码的这几个文件是没有办法运行起来的,第一、浏览器压根就不认识.vue文件;第二、浏览器也不识别ES6模块化导入导出的语法。目前暴露的这两个问题,使用vue的脚手架就可以帮我们解决; ## Vue脚手架 Vue脚手架是Vue官方提供的标椎化开发工具(开发平台); **npm下载镜像** ```tex npm config set registry https://registry.npm.taobao.org ``` ### Vue-cli安装 **环境依赖**: vue-cli依赖Node.js和全局的webpack 安装命令(全局安装,只需要执行一次):`npm install -g @vue/cli` 构建脚手架项目(来到项目的目标目录执行):`vue create 自定义项目名称`;命令执行后控制台如图所示 ![](./repeat-study-img/es4.png) ### vue-cli3目录结构 ![](./repeat-study-img/vuecliinit.png) ### Vue的不同版本 ​ 熟悉了脚手架的目录结构,接着就从`main.js`文件开始,如下图所示;其中`render`函数目前还不知道是什么(后面会专门学习),所以就替换成了另一种写法;但是启动之后报错了;大概的意思就是我们目前运行的vue版本不能解析模板;提示我们需要使用`render`函数或者`vue的其他版本` ![](./repeat-study-img/renderreplaceerror.png) vue是怎么引入运行时的依赖版本的,以及为什么运行时的版本就不能正常的解析模板呢?下图有描述 ![](./repeat-study-img/vueversion.png) 因为`render`我们本身就不熟悉,因此就替换版本,使用最全乎的`vue.js`,替换之后就成功了。 ![](./repeat-study-img/vueversionreplace.png) **总结:关于不同版本的Vue** - vue.js与vue.runtime.xxx.js的区别 - vue.js是完整版的vue,包含:核心功能+模板解析器 - vue.runtime.xxx.js是运行版的vue,只包含:核心功能;没有模板解析器 - 因为vue.runtime.xxx.js没有模板解析器,所有不能使用template配置项,需要使用render函数接受到的createElement函数去指定具体内容 ​ ### 验证单文件组件 ​ 之前学习单文件组件时,编写的`School.vue`和`Student.vue`文件因为需要借助脚手架的运行时环境就没有验证写的对不对,现在有了脚手架,就验证一下是否正确 ![](./repeat-study-img/singlesuccess.png) ```html <%= htmlWebpackPlugin.options.title %>
``` ### render函数 ​ 通过上面知道`render: h=>h(App)`和`template:'',components:{App}`是一样的效果;但是使用运行时的vue版本时,又必须使用render函数 ```javascript new Vue({ render: h=>h(App) }).$mount('#app') ``` 上面的render函数写法,使用下面的代码过渡一下 ![](./repeat-study-img/renderguodu.png) ```javascript render:(createElement)=>{ console.log(typeof createElement) // function // 参数1:标签元素;参数2:标签体内容 return createElement(App) } // 最终简写成 render:h => h(App) ``` ## vue.config.js - 脚手架3的默认配置是隐藏起来的。使用`vue inspect > output.js`可以查看脚手架3的默认配置 - 在脚手架项目的`package.json`同级目录下创建`vue.config.js`文件可以对脚手架进行个性化的配置,[具体能配置哪些,Vue官方已经整理好了](https://cli.vuejs.org/zh/config/) ### 代理服务 **这里只是针对开发环境;生产环境谁会这么干呢?后台解决跨域才是真正的解决跨域** ​ 这里的`代理服务`特指前端Vue提供的服务代理解决方案。Vue提供了两种方式,第一种相对简单但是不够灵活、代理的服务单一;第二种方式更加全面。要很明确Vue服务代理解决的是浏览器同源策略下不同源的前后服务跨域请求所带来的问题。 ![](./repeat-study-img/vueserveproxy.png) 如果前端应用和后端 API不同源,需要在开发环境下将 API 请求代理到 API 服务器。这个问题可以通过 `vue.config.js` 中的 `devServer.proxy` 选项来配置。 ```js module.exports = { devServer: { proxy: 'http://localhost:8080' } } ``` ![](./repeat-study-img/cros.png) 上面的配置是vue脚手架中提供的最简单的方式,当然简单就意味着局限 - 局限一:在”没有匹配到静态文件的请求“这种情况下,才会 代理到`http://localhost:8080`。即脚手架的根目录下存在请求的资源就不会发出代理请求,也就不会去找后台服务API >![](./repeat-study-img/staticreq.png) - 局限二:只能代理一个地址,在微服务的大环境下,这种配置肯定不行 **接下来的配置会解决以上所有的问题:** ```js // 方式二、更加全面的代理配置 module.exports = { devServer: { // proxy对象属性'/api'和'/foo',简单说就是请求的前缀,用来标识不同的请求应该走不同的代理服务 proxy: { // student打头的请求走该代理 // 假设前端服务端口是8080;那么http://localhost:8080/student/getInfo请求就走代理 '/student': { target: 'http://localhost:8081', // 代理的服务地址 ws: true, // 是否支持WebScoket请求,默认是true changeOrigin: true //是否对后台服务暴露真实的请求地址,默认是true }, // /school打头的请求走该代理 '/school': { target: 'http://localhost:8082' } } } } ``` 对第二种配置方式的特殊情况进行一个说明: ​ 描述:`http://localhost:8080/student/getInfo`走第一个代理;`http://localhost:8080/school/getName`走第二个代理。这个基于上面的配置是无可厚非的;我们分别那不同的请求前缀来匹配不同的代理服务。 ​ 但有这么一种情况;后台应用服务器没有配置应用上下文,那它提供的`getInfo`API的绝对请求路径就应该是`http://localhost:8081/getinfo`, 可是前端为了区分不同的请求走对应的代理服务不可避免的需要添加了`/student`前缀;这样前端请求`http://localhost:8080/student/getInfo`确实会找到代理服务发送请求`http://localhost:8081/student/getInfo`。可是代理发送`http://localhost:8081/student/getInfo`请求给后端API时出现404了。 这样的情况怎么解决呢?一个`pathRewrite:{'^/student':''}`配置就可以解决,如下所示 ```js // 方式二、更加全面的代理配置 module.exports = { devServer: { // proxy对象属性'/api'和'/foo',简单说就是请求的前缀,用来标识不同的请求应该走不同的代理服务 proxy: { // student打头的请求走该代理 // 假设前端服务端口是8080;那么http://localhost:8080/student/getInfo请求就走代理 '/student': { target: 'http://localhost:8081', // 代理的服务地址 // 代理服务发送真实请求时,去掉'/student'前缀 pathRewrite:{'^/student':''} // ws: true, // 是否支持WebScoket请求,默认是true // changeOrigin: true //是否对后台服务暴露真实的请求地址,默认是true }, // /school打头的请求走该代理 '/school': { target: 'http://localhost:8082' } } } } ``` ## .env环境变量 [官方文档](https://cli.vuejs.org/zh/guide/mode-and-env.html#%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F) 下图就开发环境的环境变量如果定义和获取进行简单的描述 ![](./repeat-study-img/vueenv-1.png) # 第四章:组件开发 以下组件相关学习都是基于脚手架进行的; ## ref标签属性 ref标签属性: - 被用来给元素或者子组件注册引用信息(id的代替者) - 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc) - 使用方式:`

`或者`<组件名 ref='xxx'>` - 获取:`this.$refs.xxx` ![](./repeat-study-img/ref.png) ## props配置项 **父组件 ====》子组件** ​ 组件实例对象的`props`配置项,可以把数据传递给组件,反过来说就是让组件自己可以接受来自其他组件的数据,比如下图中;App.vue组件可以传递数据给`Student.vue以及School.vue`这两个组件;这两个组件可以通过`props`配置项来接受数据;并在接受的时候可以配置一些规则。 ​ 注意:1、props中的属性是只读的,为了避免开发人员修改props中的属性;Vue底层对其进行监测,如果开发人员错误的对其进行了修改,就会发出警告;2、若确实需要修改,那么请通过data中的数据对其进行修改。 | props属性只读;不能修改 | 确实需要修改,可使用data或computed | | --------------------------------------- | ------------------------------------- | | ![](./repeat-study-img/modifyprops.png) | ![](./repeat-study-img/dataprops.png) | **使用案例** - 数据传递:**`<组件标签 自定义属性名=“属性值”/>`** - 接受数据;有三种不同的方式 1、方式一;`props:['name','sex','age']` 2、方式二;可以限制类型 >```json > props:{ > name:String, > sex:String, > age:Number > } >``` > >![](./repeat-study-img/propserror.png) 3、方式三;限制类型、限制必要性、默认值 > ```json > // 第三种、最严格:类型 + 必传项 + 默认值 > props:{ > name:{ > type:String, > required:true > }, > sex:{ > type:String, > default:'男' > }, > age:{ > type:Number > } > } > ``` > > ![](./repeat-study-img/mulprops.png) ## 组件自定义事件 **1、这是一种组件间通信的方式;是子组件给父组件传递数据。子组件 ===》父组件** 给谁自定义事件?是组件,准确点就是子组件标签;之前学习的内置事件可都是针对元素标签的 **2、两种自定义事件的实现方式** **实现方式一** ![](./repeat-study-img/customevent.png) **实现方式二、**还可以使用`ref`属性进行实现;因为触发自定义事件的是子组件实例对象;通过ref也是可以拿到子组件的实例对象的 ![](./repeat-study-img/customrefevent.png) **3、自定义事件只触发一次,可以使用`once`修饰符或$once方法** ```html ``` ```javascript this.$refs.student.$once('sendStudentInfo',this.getStudentInfo) ``` **4、触发自定义事件:`this.$emit('自定义事件名',参数)`** **5、解绑自定义事件:`this.$off('自定义事件名')`、`this.$off(['自定义事件名1','自定义事件名2'])`或者`this.$off()`直接解绑所有自定义事件** **6、组件标签上也可以绑定原生的DOM事件,只是需要使用`native`修饰符;否则还当时自定义事件处理** **7、注意;通过`this.$refs.student.$on('sendStudentInfo',回调函数)`绑定自定义事件时,回调要么配置在`methods`中,要么用箭头函数,否则this的指向会出现问题。** ![](./repeat-study-img/jiantouzhxiang.png) ## 全局事件总线 **总结**: - GlobalEventBus ;一种组件通信的方式; 可以实现任意组件间通信 - 安装全局事件总线,比较标准的写法 - ```js new Vue({ render: h => h(App), // 安装全局事件总线,$bus就是当前应用的VM beforeCreate(){ Vue.prototype.$bus = this } }).$mount('#app') ``` - 使用全局总线 - 接受数据:A组件想接受数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身 - ```js mounted(){ // 向总线中注册一个自定义事件‘studentName’,并指定回调函数 // 待到其他组件触发事件就回调函数 this.$bus.$on('studentName',(arg)=>{ this.name = arg }) } ``` - 提供数据: ```js methods:{ sendSchoolName(){ // 通过总线触发自定义事件 studentName this.$bus.$emit('studentName',this.name) } } ``` - 最好在beforeDestroy钩子中,用$off去解绑当前组件所用的事件 **图解**,[对应的源码笔记](https://gitee.com/antirust/vue-study/tree/master/repeat-study/20_%E8%84%9A%E6%89%8B%E6%9E%B6HelloWorld/05_src_%E5%85%A8%E5%B1%80%E4%BA%8B%E4%BB%B6%E6%80%BB%E7%BA%BF) **![](./repeat-study-img/bus.png)** 那么这个总线谁能满足要求呢?这里直接给出结论;视频中老师一步步的引导讲的非常好,此处不做过多的阐述。下图就是总线的定义 ![](./repeat-study-img/buswho.png) 基于上面的配置,下面进行一个简单的小练习 ![](./repeat-study-img/register.png) ![](./repeat-study-img/trigger.png) ## 消息订阅与发布 1、一种组件间通信的方式,使用于任意组件间通信 2、使用步骤:需要借助第三方库 ​ 2.1、安装`pubsub-js;`; `npm i pubsub-js` ​ 2.2、引入:`import pubsub from 'pubsub-js'` 3、接受数据:A组件想接受数据,则在A组件中订阅消息,订阅的回调留在A组件自身 ```js mehotds(){ demo(data){........} } ......... mounted(){ this.pid = pubsub.subscribe('xxx',this.demo)//订阅消息 } ``` 4、提供数据:`pubsub.publish('xxx',数据)` 5、最好在beforeDestroy钩子中,用`pubsub.unsubscribe(pid)取消订阅` ## nextTick 语法:`this.$nextTick(回调函数)` 作用:在下一次DOM更新结束后执行其指定的回调 使用场景:当数据改变后,**要基于更新后的新DOM进行某些操作时**,要在nextTick所指定的回调函数中执行 ## 插槽 **作用**:让父组件可以向子组件的指定位置插入html结构,也是一种组件间通信的方式。适用于父组件====>子组件 **分类**:默认插槽、具名插槽、作用域插槽 **描述**:组件标签体间编写了html结构以后,往子组件的哪个地方放就成了一个问题;slot插槽就是用来解决这个问题的。 #### 默认插槽 一对一的情况;组件中的所有html元素都将渲染在组件中的同一个地方;那么使用一个``足够; ![](./repeat-study-img/defaultSlot.png) #### 具名插槽 ​ 但是组件中的html元素都将渲染在组件中的不同地方;那么需要多个``才行,当然为了区分需要进行标识加以区分 ![](./repeat-study-img/nameSlot-1.png) ![](./repeat-study-img/nameSlotOver.png) #### template标签 template标签可以将需要的html结构包起来,当成一个整体使用指定的插槽 ![](./repeat-study-img/templateSlot.png) #### 作用域插槽 使用下图简单的回顾一下默认插槽和具名插槽,帮助我们理解作用域插槽 ![](./repeat-study-img/scopeslot-1.png) 理解:数据在子组件的自身,但父组件需要根据子组件的数据来生成对应的html结构 ![](./repeat-study-img/scopeslot-2.png) ```html ``` # 第五章:网络请求 ## axios安装使用 安装一个命令就搞定:`npm i axios`, 引入`import axios from 'axios'` ```js import axios from 'axios' export default { name:'Student', methods:{ getStudent(){ axios.get('http://localhost:8080/student').then( response => { console.log('获取学生信息请求成功!',response.data) }, error => { console.log('获取学生信息请求失败!',error.message) } ) }, getSchool(){ axios.get('http://localhost:8080/school').then( response => { console.log('获取学校信息请求成功!',response.data) }, error => { console.log('获取学校信息请求失败!',error.message) } ) }, } } ``` ## vue服务代理 [gitee锚点链接](https://gitee.com/antirust/vue-study#%E4%BB%A3%E7%90%86%E6%9C%8D%E5%8A%A1) [本地编辑器锚点链接](###代理服务) # 第六章:vuex [vuex官方网址](https://vuex.vuejs.org/zh/) ## 概念 ​ 专门在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读、写),也是一种组件间通信的方式,且适用于任意组件间的通信。 ​ 上一次学习到的可以在任意组件间通信的还是“全局事件总线”和“消息订阅与发布”; ![](./repeat-study-img/eventbus.png) 如果使用vuex的话 ![](./repeat-study-img/vuexfirst.png) ## vuex原理 ![](./repeat-study-img/vuexPrinciple.png) ## vuex的安装 ​ 在2022年2月7日,vue3成为了默认版本;即`npm i vue`安装的直接是就是vue3了;vue3成为默认版本的同时,vuex也更新到了4版本,`npm i vuex`,安装的就是vuex4。而vuex4只能在vue3中使用;如果非要在vue2中使用vuex4就会出现下图的错误 ![](./repeat-study-img/vuexinstall.png) 总结: - **vue2中,要用vuex3** - **vue3中,要用vuex4** 目前使用就是vue2,所以就要安装vuex3版本,要怎么样安装呢? ```node npm i vuex@3 ``` ## vuex搭建-引导 **该部分的笔记打算啰嗦些,将视频中老师的引导都适当的记录下来**;前期引导部分的编码可能是不对。最后呈现的才是最终的正确使用方式 ### vm的store配置项 vuex是插件,插件就应该使用`Vue.use(Vuex)`;插件配置完成,vm的配置项中的`store`属性就起作用了;即vm和所有的vc都可见了,他们的实例对象中都会有个`$store`属性 ![](./repeat-study-img/Vuexuse.png) ### 构建Store实例 Vue配置项`store`最终需要的是一个`Store`实例,而不是什么’hello store‘。所以就构建store实例并配置;下图是引入顺序错误而导致的问题 ![](./repeat-study-img/storeCreateBefore.png) ### 完整的Vuex搭建 ![](./repeat-study-img/vuexBuildSuccess.png) **src/store/index.js** ```js // 该文件用于创建Vuex中最为核心的store import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) // store对象管理着Actions\Mutations\State三个对象 const actions = { } const mutations = { } const state = { // 用于存储数据 } // 创建并暴露store export default new Vuex.Store({ actions, mutations, state }) ``` **main.js** ```js import Vue from 'vue' import App from './App' Vue.config.productionTip= false import store from './store' const vm = new Vue({ render: h => h(App), store }).$mount('#app') console.log(vm) ``` ## vuex的使用 ### 初体验 上面已经搭建好了vuex运行时的一个环境,那么如何使用Vuex进行共享数据集中式的管理呢?两张图对比着体会一下; ![](./repeat-study-img/vuex.png) ![](./repeat-study-img/vuexinituse.png) ### 基本使用 基于一个求和的案例,将vuex进行一个基本的学习和使用;后期都是围绕该案例的代码进行扩展学习和优化,比如: 1. `this.$store.state.sum`的简写 2. 模块化拆分 3. ........... ![](./repeat-study-img/culsum.png) ```js // 该文件用于创建Vuex中最为核心的store import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) // store对象管理着Actions\Mutations\State三个对象 const actions = { jia(context,value){ console.log("actions中的jia被调用了",context,value) context.commit('JIA',value) }, jian(context,value){ context.commit('JIAN',value) }, jiaOdd(context,value){ if(context.state.sum%2){ context.commit('JIA_ODD',value) } }, jiaWait(context,value){ setTimeout(() => { context.commit('JIA_WAIT',value) }, 1000); } } const mutations = { JIA(state,value){ console.log("mutations中的JIA被调用了",state) state.sum += value }, JIAN(state,value){ state.sum -= value }, JIA_ODD(state,value){ state.sum += value }, JIA_WAIT(state,value){ state.sum += value } } const state = { sum: 0 } // 创建并暴露store export default new Vuex.Store({ actions, mutations, state }) ``` ```js ``` ### 组件直接commit 之前学习vuex的原理时,图中的组件可以绕开Actions直接commit数据给Mutations;如果在Actions中没有什么逻辑需要处理的话,组件就可以直接commit数据给Mutations ![](./repeat-study-img/vuexPrinciple.png) ![](./repeat-study-img/dispatch-1.png) ![](./repeat-study-img/dispatch-2.png) ### 参数说明 对vuex原理图中提到的API方法的参数作出说明: **实参**: - dispatch('参数1',‘参数2’) - 参数1:自定义名称(约定是小写);为了对应和找到Actions中的函数 - 参数2:需要传递给Actions的值,可以是组件中的任意值 - commit('参数1',‘参数2’) - 参数1:自定义名称(约定是大写);为了对应和找到Mutations中的函数 - 参数2:需要传递给Mutations的值 **形参**: - actions中函数(参数1,参数2) - 参数1:context,上下文对象,可以看成是一个迷你版的store对象实例,如下图 - 参数2:value,传过来的值 - mutations中函数(参数1,参数2) - 参数1:state,就是state对象,可以拿到共享变量,如下图 - 参数2:value,传进来的值 ![](./repeat-study-img/vuexparam.png) ## getters的使用 - 当多个组件需要对state中的数据经过相同的逻辑处理后再使用,就可以使用getters; - 在Vuex的配置项中添加`getters`属性 - 组件中获取数据`$store.getters.函数名` 比如需要对求和后的sum进行10倍运算的逻辑: ![](./repeat-study-img/mulsumdemo.png) ![](./repeat-study-img/gettersvuex.png) ## mapState 组件从vuex中获取共享变量的简洁方式: ```js computed:{ sum(){ return this.$store.state.sum }, school(){ return this.$store.state.school }, subject(){ return this.$store.state.subject } }, ``` ```js import {mapState} from 'vuex' ``` ![](./repeat-study-img/mapState-1.png) **如何优化?** ![](./repeat-study-img/mapState-2.png) ## mapGetters 组件从store的getters中获取值的简洁写法; ```js import {mapState,mapGetters} from 'vuex' ``` ![](./repeat-study-img/gettersSimple.png) ```html ``` ## mapActions mapActions优化的是这坨代码;dispatch向Actions中提交数据;所以也不难理解vuex将它命名为`mapActions` ![](./repeat-study-img/mapActions-1.png) 使用mapActions实现 ![](./repeat-study-img/mapActions-2.png) ## mapMutations 组件绕开Actions直接commit数据给Mutations的这写法也是有简写方式的 ![](./repeat-study-img/mapMutations.png) ## namespaced 借助`nameSpace`可以将src/store/index.js中的代码进行模块化的拆分 ### 模块化拆分为对象 ![](./repeat-study-img/buyiyang.png) ![](./repeat-study-img/nameSpace-1.png) ```js // 该文件用于创建Vuex中最为核心的store import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) const countOperations = { actions:{ jiaOdd(context,value){ if(context.state.sum%2){ context.commit('JIA_ODD',value) } }, jiaWait(context,value){ setTimeout(() => { context.commit('JIA_WAIT',value) }, 1000); } }, mutations:{ JIA(state,value){ console.log("mutations中的JIA被调用了",state) state.sum += value }, JIAN(state,value){ state.sum -= value }, JIA_ODD(state,value){ state.sum += value }, JIA_WAIT(state,value){ state.sum += value } }, getters:{ mulSum(state){ return state.sum * 10 } }, state:{ sum: 0, school:'尚硅谷', subject:'java', } } const personOperations={ actions:{ addPerson(context,value){ // console.log(value) context.commit('ADD_PERSON',value) } }, mutations:{ ADD_PERSON(state,value){ state.personList.unshift(value) } }, getters:{}, state:{ personList:[ {id:'kZluDlz2dAVGbdLv9sKbA',name:'普安'} ] } } // 创建并暴露store export default new Vuex.Store({ modules:{ count:countOperations, person:personOperations } }) ``` 经过上面的模块化拆分以后和配置以后,store属性的state存储的属性值如下: ![](./repeat-study-img/console--1.png) ### 组件mapXXX调用 #### mapState ![](./repeat-study-img/modulesstate.png) #### mapGetters ![](./repeat-study-img/getterssss.png) #### mapMutations ![](./repeat-study-img/mapMustationsss.png) #### mapActions ![](./repeat-study-img/mapac.png) ### 传统调用方式的改变 #### 拿state共享数据 ![](./repeat-study-img/stateperson.png) #### getters拿共享数据 因为getters中存储的属性名比较特殊,所以相对于state来说差异还是挺大 ![](./repeat-study-img/getterstesu.png) #### dispatch修改数据 ![](./repeat-study-img/dispatch-3.png) #### commit修改数据 ![](./repeat-study-img/mutationsssss.png) ### 模块化拆分成文件 ![](./repeat-study-img/拆分.png) # 第七章:vue-router route:路由(key-value) router:路由器(一组路由) ## VueRouter安装 ​ 2022年2月7日,vue-router的默认版本为vue-router4;vue-router4只能在vue3中使用;vue2只能使用vue-router3;因为当前使用的vue2.所以就安装vue-router3 ```npm npm i vue-router@3 ``` ## VueRouter基本使用 1. 安装vue-router 2. 导入vue-router;并使用Vue.use(VueRouter)配置插件 3. 创建router对象;配置路由 4. 在Vue的配置项中添加router对象实例 5. 使用``实现路由的切换 6. `` 指定组件的呈现位置 ![](./repeat-study-img/initrouter.png) ## 几个注意点 1、路由组件通常存放在`pages`文件夹,一般组件通常就存放在`components`文件夹 2、路由组件切换,被”隐藏“的组件默认是被销毁的,用到时再重新进行挂载;可以在路由组件中使用`mounted()和beforeDistroy()进行验证` 3、每个路由组件都有自己的`$route`属性,里边存储自身的路由信息 4、整个应用只有一个`router`,每一个路由组件都可以通过`$router`属性获得 ## 嵌套路由 [嵌套路由案例效果视频下载](https://gitee.com/antirust/vue-study/raw/master/repeat-study-video/%E5%B5%8C%E5%A5%97%E8%B7%AF%E7%94%B1.wmv) 嵌套路由,即多级路由 - 路由规则配置 ```js export default new VueRouter({ routes:[ { path:'/about', component:About }, { path:'/home', component:Home, children:[ { path:'news', //注意除了一级路由,其他路由的path不要带‘/’ component:News }, { path:'messages', component:Messages }, ] }, ] }) ``` - 路由跳转时,要写将父级路由的path都带上;`to="/home/news"` ```html 新闻 ``` ![](./repeat-study-img/qtluyou.png) ## 路由传参 在路由跳转的时候携带参数给路由组件 ### query传参 [案例效果演示视频下载地址](https://gitee.com/antirust/vue-study/raw/master/repeat-study-video/%E8%B7%AF%E7%94%B1query%E4%BC%A0%E5%8F%82.wmv) [练习的代码](https://gitee.com/antirust/vue-study/tree/master/repeat-study/20_%E8%84%9A%E6%89%8B%E6%9E%B6HelloWorld/16_src_%E8%B7%AF%E7%94%B1%E4%BC%A0%E5%8F%82Query) 在路由跳转的时候携带上参数;有两种不同的写法 ```html
  • {{detail.title}} {{detail.title}}
  • ``` ![](./repeat-study-img/queryobject.png) ### params传参 一、字符串拼接的方式 ![](./repeat-study-img/paramrouter.png) 二、对象的形式 ![](./repeat-study-img/param222.png) > **注意:这里有个坑:使用params进行传值并且使用的是对象的配置方式,一定要注意不能使用`path`;要使用`name`** ## 路由跳转写法 > **注意:params传参;不管什么方式;都需要在路径上进行占位** > > ![](./repeat-study-img/routeee.png) - 直接使用路由配置的`path`的属性值 ```html 新闻 ``` - 跳转还需携带query参数,字符串拼接 ```html {{detail.title}} ``` - `对象+路由配置的path属性+query`的方式;对于Query传参特别友好; ```html {{detail.title}} ``` - `对象+路由配置的name属性+params`的方式;记得在路径上占位 ```html {{detail.title}} ``` - `params`的字符串形式;记得占位 ```html {{detail.title}} ``` - `对象+路由配置的name属性+query`的方式 ```html {{detail.title}} ``` ## 路由命名 在路由规则配置中给每一个路由对象起个名字进行唯一的标识 作用:如果子组件路径层级太深可以用来简化跳转的路径 ![](./repeat-study-img/nameroute.png) ## 路由的props配置 作用:让路由组件更方便的接受到参数 这里的props指的是路由对象的props属性;即下图的所示位置 ![](./repeat-study-img/props-2.png) 如图所示;目前路由组件是这样接受路由参数的,看上去比较繁琐;如果使用路由的props配置路由组件接受参数将变得更加的简单。 ![](./repeat-study-img/props-1.png) ### 对象 这种的好像没有办法把query参数或者params参数获取到放进props中;只能传递某些固定的值 ![](./repeat-study-img/props-3.png) ### 布尔值 > **注意:这种方式只能把params参数传递给路由组件;query参数形式就行不通** ![](./repeat-study-img/props-4.png) ### 函数 >**这种方式是最强大的;自定义的固定属性值、params参数、query参数都不在话下** ![](./repeat-study-img/props-5.png) ## router-link的replace属性 [视频效果演示下载](https://gitee.com/antirust/vue-study/raw/master/repeat-study-video/replace.wmv) 浏览器的历史记录有两种写入方式:分别是`push`和`replace` | push(默认) | replace | | ---------------------- | ------------------------------------------------------------ | | 将所有的记录都压入栈中 | 如果某个路由跳转是replace方式;那么该条记录在进栈的时候会将栈顶的数据(记录)给替换掉 | 基于上面的理解;学习一下`router-link的replace属性` ![](./repeat-study-img/replace-1.png) ## 编程时路由导航 ​ 路由的跳转一直借助`新闻`实现;即路由的跳转可以不借助``标签,可以使用对应的API进行调整;更加的灵活 - 路由调整 ```js this.$router.push({ name:'detail', params:{ content:detail.content } }) this.$router.replace({ name:'detail', params:{ content:detail.content } }) ``` ![](./repeat-study-img/apirouter.png) - 前进、后退 ```js this.$router.forward() // 前进 this.$router.back() // 后退 this.$router.go(正负数) // 可前进也可后退 ``` ![](./repeat-study-img/routerapi.png) ## 缓存路由组件 路由跳转时;组件会被销毁。标签``可以让被隐藏的组件保持挂载,不被销毁 ```html ``` ![](./repeat-study-img/keepactive.png) 从生命周期函数的角度看现象: ![](./repeat-study-img/keepa.png) ## 新的生命周期函数 路由组件所独有的两个生命周期函数;用于捕获路由组件的激活状态。 ` keep-alive`可以让路由组件在切换的时候不被销毁并缓存起来;也就是说路由组件如果使用keep-alive;那么它不会调用生命周期函数`beforeDestroy();mounted()也只会初始化一次`;所以;如果在这种情况下想在组件“激活”和“失活”的时候做点事情有点困难。于是乎就有了下面的这两个新的钩子函数 ```html ``` `activated`:路由组件被激活时触发。 `deactivated`:路由组件失活的时候触发。 ## 路由守卫 ### 全局路由守卫 - **前置路由守卫:**全局前置路由守卫;初始化执行、每个路由组件跳转前执行 - **后置路由守卫:**全局后置路由守卫;初始化执行、每个路由组件成功跳转后执行 ![](./repeat-study-img/routerforeach.png) **前置路由守卫应用**:进行权限认证 ```js // 全局前置路由守卫;初始化执行、每个路由组件跳转前执行 router.beforeEach((to,from,next)=>{ // console.log("前置路由守卫",to,from) if(to.meta.isAuth){ if(localStorage.getItem('username')==='tom'){ next() }else{ alert('没有访问权限') } }else{ next() } }) ``` **后置路由守卫应用:**标签的title切换 ```js // 全局后置路由守卫;初始化执行、每个路由组件成功跳转后执行 router.afterEach((to)=>{ // console.log("后置路由守卫",to,from) // 每个路由的页签title展示 document.title = to.meta.title || 'vue学习练习' }) ``` ![](./repeat-study-img/title.png) ```js import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter) import About from '../pages/About.vue' import Home from '../pages/Home.vue' import Messages from '../pages/Messages.vue' import News from '../pages/News.vue' import Detail from '../pages/Detail.vue' const router = new VueRouter({ routes:[ { path:'/about', component:About, meta:{title:'有关信息展示'} }, { name:'home', path:'/home', component:Home, meta:{title:'系统欢迎页'}, children:[ { path:'news', component:News, // isAuth:true用于在全局前置路由守卫进行权限验证 meta:{isAuth:false,title:'新闻报道'} }, { path:'messages', component:Messages, // isAuth:true用于在全局前置路由守卫进行权限验证 meta:{isAuth:false,title:'小道消息'}, children:[ { name:'detail', path:'detail/:text', component:Detail, meta:{title:'消息详细信息'}, props(route){ return{ content:route.params.text } } } ] }, ] }, ] }) // 全局前置路由守卫;初始化执行、每个路由组件跳转前执行 router.beforeEach((to,from,next)=>{ // console.log("前置路由守卫",to,from) if(to.meta.isAuth){ if(localStorage.getItem('username')==='tom'){ next() }else{ alert('没有访问权限') } }else{ next() } }) // 全局后置路由守卫;初始化执行、每个路由组件成功跳转后执行 router.afterEach((to)=>{ // console.log("后置路由守卫",to,from) // 每个路由的页签title展示 document.title = to.meta.title || 'vue学习练习' }) export default router ``` ### 独享路由守卫 - 路由配置规则中的`beforeEnter`属性进行配置 - 独享路由守卫就仅仅只是一个`beforeEnter`,再无其他 ![](./repeat-study-img/duxiang.png) ### 组件内路由守卫 在组件内配置路由守卫 ![](./repeat-study-img/componentsroute.png) ![](./repeat-study-img/routeorder.png) ## 路由器工作模式 ### hash模式 - 对于一个url来说;什么是hash值? `#`以后的内容就是hash值。 ```java http://localhost:8080/#/home/messages/detail/1 ``` - hash值不会包含在HTTP请求中,即;hash值不会带给服务器 - 特点:url地址中永远带着`#` - 若以后将地址通过第三方手机APP分享,若APP校验严格,则地址会被标记为不合法 - 对浏览器的兼容性较好 ### history模式 - 地址干净美观 - 兼容性和hash模式相比略差 - 应用部署上线时需要后端人员支持;解决刷新页面服务端404的问题(刷新时将前端路由地址发送给了服务器) # Vue3-常用Composition Api setup是所有**Composition Api的表演舞台** ## 拉开序幕的setup 1. setup是vue3中一个新的配置项,值为一个函数 2. 组件中所用的;数据、方法等,均要配置在setup中。(vue2,数据要写在data中,方法要写在methods中)vue3不需要了 3. setup函数的两种返回值: 1. 若返回一个对象,则对象中的属性、方法、在模板中均可以直接使用 2. 若返回一个渲染函数,则可以自定义渲染内容 ## ref函数 数据响应式