文章目录
- vue.js的引入
- Vue中的事件修饰符
- Vue常用按键别名
- 计算属性
- 监视属性
- computed 和 watch 之间的区别
- 绑定样式写法
- 条件渲染
- v-for指令
- react,vue中的key的作用?(key的内部原理)
- Vue监测数据的原理
- 收集表单数据
- 过滤器(Vue3已移除)
- 内置指令
- 自定义指令
- 生命周期函数
- Vue使用组件的三大步骤
- 组件名
- 组件标签
- 一个重要的内置关系
- ref 属性
- 配置项props
- mixin(混入)
- 插件
- scoped样式
- 总结TodoList案例
- webStorage
- 组件自定义事件
- 全局事件总线
- 消息订阅与发布(pubsub)
- nextTick
- 关于Object原型方法调用出现的bug
- Vue封装的过度和动画
- vue脚手架配置代理
- vue-resource (不推荐使用)
- 插槽
- Vuex
- getters的使用
- 4个map写法
- 生成唯一的id —— nanoid
- 模块化+命名空间
- 路由
- 多级路由
- 路由的query参数
- 命名路由
- 路由的params参数
- 路由的props配置
- router-link的replace属性
- 编程式路由导航
- 缓存路由组件
- 两个新的生命周期钩子——activated,disactivated
- 路由守卫
- 独享路由守卫
- 组件内守卫
- 路由器的两种工作模式
vue.js的引入
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js">
浏览器会显示应用vue.min.js的提示信息,我们可以用下列指令来关闭这个警告
Vue.config.productionTip=false;
Vue中的事件修饰符
- prevent:阻止默认事件的发生(常用)
- stop:阻止事件冒泡(常用)
- once:事件只触发一次(常用)
- capture: 使用事件的捕获模式
- self: 只有event.target是当前操作元素时才触发事件
- passive: 事件的默认行为立即执行,无需等待事件回调函数进行完毕
ps: 修饰符可以连着写,如 @click.stop.prevent
Vue常用按键别名
- vue常见按键别名
- 回车 => enter
- 删除 => delete (捕获"删除"或"退格键")
- 退出 => esc
- 空格 => space
- 换行 => tab (特殊,必须配合keydown使用)
- 上 => up
- 下 => down
- 左 => left
- 右 => right
- 其他无别名的,用按键原来的名字即可。注有两个单词构成的名称,要将其小写,并加上连字符进行连接,如 caps-lock
- 系统修饰键(用法特殊): ctrl,alt,shift,meta
- 配合keyup: 按下系统修饰键,然后按下其他按键再释放,才能触发事件
- 配合keydown: 正常触发事件
- keyup,keydown 也可以配合KeyCode来使用(尽量不要使用)
- 可以添加别名,如 Vue.config.huiche = 13 (不推荐)
- 系统修饰符可以连着写,表示需要先后按下这些按键。如 @keydown.ctrl.y
计算属性
- 定义:要用的属性不存在,要通过已有的属性计算得来
- 原理:底层借助Object.defineproperty方法提供的getter和setter
- get函数什么时候执行?
- 初次读取时会执行一次
- 当依赖的数据发生变化时会被再次调用
- 优势: 与methon方法的实现相比,内部有缓存机制,效率更高,调试方便
- 备注
- 计算属性最终会出现在vm上,直接读取使用即可
- 如果计算属性要被修改,那必须要写set函数去响应修改,且set中引起计算时所依赖的数据要发生变化
监视属性
-
监视属性watch
- 当被监视的属性变化时,回调函数自动调用,进行相关操作
- 监视属性必须存在,才能进行监视
- 监视的两种写法:
- new Vue时引入watch配置
- 通过vm.$watch监视
-
深度监视:
- Vue中的watch默认不监测对象内部的值的改变(一层)
- 配置deep:true 可以监测对象内部值的改变(多层)
PS:
- Vue 自身可以监测对象内部值的改变,但Vue提供的watch默认不可以
- 使用watch时根据数据的具体结构,决定是否采用深度监视 -
内部属性
- immediate:true,//初始化时让handler调用一下
- handler(newValue,oldValue){}
- deep
computed 和 watch 之间的区别
- computed能完成的功能,watch可以完成
- watch 能完成的功能,computed不一定能完成,如 watch可以进行异步操作
两个重要小原则: - 所有vue管理的函数最好写成普通函数,这样this指向的才是vm
- 不被vue管理的函数(定时器的回调函数,Ajax的回调函数,Promise的回调函数),
最好写成箭头函数,这样this指向的才是vm
绑定样式写法
- class样式
写法:class=“xxx” xxx可以是字符串,对象,数组。- 字符串写法适用于:类名不确定,要动态获取
- 对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。
- 数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用
- style样式
- :style="{fontSize: xxx}" 其中xxx是动态值
- :style="[a,b]",其中a,b是样式对象
条件渲染
- v-if
写法:
- v-if = “表达式”
- v-else-if = “表达式”
- v-else = “表达式”
适用于:切换频率较低的场景
特点:不展示的DOM元素直接被移除
注意:v-if可以和v-else-if、v-else一起使用,但要求结构不能被打断
- v-show
写法:v-show = ‘表达式’
适用于:切换频率较高的场景 - 使用v-if时,元素可能无法获取到,而使用v-show时一定需要获取到
v-for指令
- 用于展示列表数据
- 语法:v-for="(item,index) in xxx" :key=“yyy”
- 可遍历:数组,对象,字符串(用得少),指定次数(用得少)
react,vue中的key的作用?(key的内部原理)
-
虚拟DOM中key的作用:
key时虚拟DOM对象的标识,但状态中数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较。
-
对比规则
- 旧虚拟DOM找到与新虚拟DOM相同的key:
- 若虚拟DOM的内容没变,直接使用之前真实的DOM
- 若内容变了,则生成真实的DOM,随后替换掉页面中之前的真实DOM
- 若虚拟DOM未找到与新虚拟DOM相同的keys,则创建新的虚拟DOM
- 旧虚拟DOM找到与新虚拟DOM相同的key:
-
用index作为key会引发的问题
- 若对数据进行逆序添加,逆序删除等破坏顺序的操作,会产生没有必要的DOM更新,效率低
- 如果结构中含有输入类的DOM,会产生错误的DOM更新,界面有问题
-
开发中如何选择key?
- 最好选择每条数据唯一的标识作为key,比如id,手机号、身份证号
- 如果不存在对数据的逆序添加、逆序删除等破坏顺序的操作,仅渲染列表用于展示,使用index作为key是没问题的
Vue监测数据的原理
-
vue会监测data所有层次的数据
-
如何监测对象中的数据
通过setter实现监视,且在new Vue 时就传入要监测的数据。- 对象后追加的属性,Vue默认不做相应式处理
- 如需给添加的属性做响应式,使用下列API:
Vue.set(target,propertyName/index,value) vm.$set(target,propertyName,value)
-
如何监测数组中的数据
通过包裹数组更新元素的方法来实现,本质上是做了两件事:- 调用原生对应的方法对数组进行更新
- 重新解析模板,进而更新页面
-
在Vue中修改数组中的某个元素一定要用如下方法:
- 使用这些api,push(),pop(),shift(),unshift(),splice(),sort(),reverse()
- Vue.set() 或 vm.$set()
PS: Vue.set() 和 vm.$set() 不能给 vm 或 vm的根数据对象 添加属性
收集表单数据
- 若<input type=“text”/>,则v-model收集的是value值,用户输入的也是value值
- 若<input type=“radio”/>,则v-model收集的是value值,且要给标签配value值
- 若<input type=“checkbox”/>,则
- 没有配置input的value属性,则收集到的就是checked(勾选或者没勾选,是布尔值)
- 配置了input的value属性
- v-model的初始值是非数组,那么收集到的就是checked(是布尔值)
- v-model的初始值是数组,那么收集的就是value组成的数组
- PS:
v-model的三个属性值:- lazy:失去焦点收集数据
- number:输入字符串转为有效的数字
- trim:输入首尾空格过滤
过滤器(Vue3已移除)
- 定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑处理)
- 语法
- 注册过滤器:Vue.filter(name,callback) =>全局或 new Vue(filters{}) =>局部
- 使用过滤器:{{ xxx | 过滤器名 }} 或 v-bind: 属性 = “xxx | 过滤器名”
- 备注:
- 过滤器也可以接受额外参数,多个过滤器可以串联
- 并没有该改变原来的数据,是产生新的对应数据
内置指令
- v-text指令
- 作用:向其所在的节点中渲染文本内容
- 与插值语法的区别:v-text会替换节点中的内容,而{{x}}不会
- v-html
- 作用:向指定节点中渲染包含html结构的内容
- 与插值语法的区别:
- v-html会替换节点中的所有内容,{{x}}不会
- v-html可以识别html结构
- 严重注意:v-html有严重安全性问题
- 在网站上动态渲染任意HTML是非常危险的,容易受到xss攻击
- 一定要在可信的内容上使用html,永远不要用在用户提交的内容上
- v-cloak 指令(没有值)
- 本质上是一个特殊值,Vue实例创建完毕并接管容器后,会删除v-cloak属性
- 使用css配合v-cloak可以解决网速慢时页面加载{{x}}的问题,如
[v-cloak]{ display:none; }
- v-once 指令:
- v-once指令所在节点初次动态渲染后,就视为静态内容
- 以后数据的改变不会引起v-once所在结构的更新,用于优化性能
- v-pre 指令:
- 跳过其所在节点的编译过程
- 可利用它跳过:没有指令语法,没有用到插值语法的节点,会加快编译
自定义指令
- 定义语法:
- 局部指令:
new Vue({ directives:{指令名:配置对象} }) \\或者 new Vue({ directives:{指令名:回调函数} })
- 局部指令:
- 配置对象常见的3个回调
- bind 指令与元素成功绑定时调用
- inserted:指令所在元素被插入页面时调用
- update: 指令所在模板结构被重新解析时调用
- 备注:
- 指令定义时不加v-,但使用时要加v-;
- 指令如果是多个单词,要使用kebab-case命名方式,不要使用camelCase命名
生命周期函数
- 基础概念
- 又名:生命周期回调函数,生命周期函数,生命周期钩
- Vue在关键时期帮我们调用的一些特殊名称的函数
- 生命周期函数的名字不可更改,但函数的具体内容可以根据程序员的具体需求编写的
- 生命周期函数的this指向vm或组件实例对象
- 生命周期钩子
生命周期钩子共有8个
//无法通过vm访问到data中的数据,methods中的方法
//初始化:生命周期、事件、但数据代理还未开始
beforeCreate() {
console.log('beforeCreate');
},
//初始化数据监测、数据代理
//可以通过vm访问到data中的数据。methods中配置的方法
created() {
console.log('created');
},
//此时 页面呈现的是未经Vue编译的DOM结构,所有对DOM的操作都不奏效
beforeMount() {
console.log('beforeMount')
},
//页面中呈现的是经过Vue编译的DOM,对DOM的操作均有效,至初始化结束,
//一般在此进行:开启定时器,发送网络请求,订阅信息,绑定自定义事件等初始化操作
mounted() {
console.log('mounted')
},
//此时数据是新的,但页面是旧的,即:页面尚未和数据保持同步
beforeUpdate() {
console.log('beforeUpdate')
},
//数据是新的,页面也是新的,即页面和数据保持同步
updated() {
console.log('update');
},
//vm中所有的:data,methods,指令等等,都处于可用状态,马上要执行销毁过程
//一般在此阶段:关闭定时器、取消订阅消息、解绑自定义事件等收尾工作
beforeDestroy() {
console.log('beforeDestroy')
},
destroyed() {
console.log('destoryed');
},
常用的生命函数钩子:
- mounted: 发送ajax请求,启动定时器、绑定自定义事件、订阅消息等【初始化操作】
- beforeDestroy:清除定时器,解绑自定义事件,取消订阅消息等收尾工作
关于销毁Vue实例
- 销毁后借助vue开发者工具看不到任何消息
- 销毁后自定义事件会失效,但原生的DOM依然有效
- 一般不会在beforeDestroy操作数据,因为即使操作数据,也不会触发更新流程
Vue使用组件的三大步骤
- 定义组件
使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但区别如下:- el不用写——所有的组件都要经过vm的管理,由vm决定服务那个容器
- data必须写成函数——避免组件被复用时,数据存在引用关系
- 使用template可以配置组件结构
- 如何注册组件
- 局部注册:靠new Vue的时候传入component选项
- 全局注册:靠Vue.component(‘组件名’,组件)
- 简写:const school = Vue.extend(options)可以简写为 const school = options
- 编写组件标签:
如 <school></school>
组件名
- 一个单词组成:
- 第一种写法(首字母小写):school
- 第二种写法(首字母大写):School
- 多个单词组成:
- kebab-case命名:my-school
- CamelCase命名:MySchool(需要Vue脚手架支持)
- 备注
- 组件名尽可能回避HTML已有的元素名称
- 可以使用name配置指定组件在开发者工具中呈现的名字
组件标签
基本写法:
- <school></school>
- <school/> 不使用脚手架时, <school/> 会导致后续组件不能渲染
一个重要的内置关系
- VueComponent.prototype === Vue.proptotype
- 有了这个关系,让组件实例对象(vc)可以访问到Vue原型上的属性、方法
ref 属性
- 被用来给元素或者子组件注册引用信息(id的替代者)
- 应用在html标签上获取的是真实Dom元素,应用在组件标签上是组件实例对象
- 使用方法:
- 打标识: <h1 ref=“xxx”>…</h1> 或者 <School ref=“xxx”></School>
- 获取: this.$refs.xxx
配置项props
功能:让组件接收外部传回来的数据
- 传递数据
<Demo name=“xxx”/> - 接收数据
- 第一种方式(只接收):
props:[‘name’] - 第二种方式(限制类型)
props:{
name:String
} - 第三种方式(限制类型、限制必要性、指定默认值):
props:{ name:{ type:String,//类型 required:true,//必要性 default:'老王'//默认值 } }
- 第一种方式(只接收):
- 备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data种一份,然后修改data中的数据
mixin(混入)
功能:可以把多个组件共用的配置提取成一个混入对象
使用方式:
- 第一步定义混合,例如
{ data(){...}, methods:{...}, ... }
- 第二步使用混合,例如:
- 全局混入 Vue.mixin(xxx)
- 局部混入:minxins: [‘xxx’]
插件
功能: 用于增强Vue
本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。
- 定义插件:
对象.install = function (Vue,options){
//1.增加全局过滤器
Vue.filter(...)
//2. 添加全局指令
Vue.directive(...)
//3. 配置全局混入
Vue.mixin(...)
//4.添加实例对象
Vue.proptype.$myMethod = function(){...}
Vue.prototype.$myProperty = xxx
}
- 使用插件
Vue.use()
scoped样式
作用:让样式在局部生效,防止冲突
写法: <style scoped>
总结TodoList案例
- 组件化编码流程
- 拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突
- 实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用
- 一个组件在用:放在组件自身即可
- 一些组件在用:放在它们共同的父组件上(状态提升)
- 实现交互:从绑定事件开始
- props适用于
- 父组件 ===> 子组件 通信
- 子组件 ===> 父组件 通信 (要求父先给子一个函数)
- 使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的
- props传过来的若是对象类型的值,修改对象时Vue不会报错,但不推荐这样做
webStorage
- 储存的大小支持5MB左右
- 浏览器通过Window.sessionStorage和Window.localStorage属性来实现本地储存机制
- 相关API
- xxxxStorage.setItem(‘key’,‘value’)
该方法接受一个键和值作为参数,会把键值对加入在存储中,如果键名存在,则更新其对应地值 - xxxxStorage.getItem(‘key’)
该方法接受一个键名作为参数,返回键名对应的值 - xxxxStorage.removeItem(‘key’)
该方法接受一个键名作为参数,放回键名对应的值 - xxxxStorage.clear()
该方法会清空储存中的所有数据
- xxxxStorage.setItem(‘key’,‘value’)
- 备注
- SessionStorage储存的内容会随着浏览器的关闭而消失
- LocalStorage储存的内容,需要手动清除才会消失
- xxxxStorage.getItem(‘key’) 如果key获取不到,那么返回值为null
- JSON.parse(null)的结果还是null
组件自定义事件
- 一种组件间的通信方式,适用于 子组件 ===> 父组件
- 使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)
- 绑定自定义事件:
- 第一种方式:在父组件中,
<Demo @a="test"/>
- 第二种方式,在父组件中
<Demo ref="demo"> ..... mounted(){ this.$refs.xxx.$on('a',this.test) }
- 若想让自定义事件只触发一次,可以使用once修饰符,或者$once()方法
- 第一种方式:在父组件中,
- 触发自定义事件:this.$emit(‘a’,数据)
- 解绑自定义事件: this.$off(‘a’)
- 组件上也可以绑定原生的DOM事件,需要加native修饰符
- 注意:通过 this.$refs.xxx.$on(‘a’,回调)绑定自定义事件,回调要么只能配置在methods中,要么用箭头函数,否则this指向会出问题
全局事件总线
- 一种组件间的通信方式,适用于任意组件间的通信
- 安装全局事件总线:
new Vue({ ...... beforeCreate(){ Vue.proptotype.$bus = this;//安装全局事件总线,$bus就是当前应用的vm } ...... })
- 使用事件总线:
- 接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件本身
methods(){ demo(data){...} } .... mounted(){ this.$bus.$on('xxxx',this.demo) }
- 提供数据
this.$bus.$on('xxxx',this.demo)
- 最好在beforeDestroy钩子里,用$off解绑当前组件所用到的事件
消息订阅与发布(pubsub)
- 一种组件间通信的方式,适用于任意组件间的通信
- 使用步骤
- 安装pubsub:npm i pubsub-js
- 引入: import pubsub from ‘pubsub-js’
- 接收数据:A组件想接收数据,则在A组件订阅消息,订阅的是回调留在A组件自身
methods(){ demo(data){...} } ...... mounted(){ this.pid = pubsubcribe('xxx',this.demo) }
- 提供数据: pubsub.publish(‘xxx’,this.demo)
- 最好在beforeDestroy钩子里,用pubsub.unsubscribe(pid)去取消订阅
nextTick
- 语法:this.$nextTick(回调函数)
- 作用:在下一次DOM更新结束后执行其指定的回调
- 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作,要在nextTick所在函数中进行
关于Object原型方法调用出现的bug
解决方法
为了避免这种细微的 bug,最好总是从 Object.prototype 调用这些方法。例如,foo.hasOwnProperty(“bar”) 应该替换为 Object.prototype.hasOwnProperty.call(foo, “bar”)。
Vue封装的过度和动画
- 作用:在插入、更新或移除DOM元素时,在合适的位置给元素添加样式别名
- 写法:
- 准备好样式:
- 元素进入的样式:
- v-enter:进入的起点
- v-enter-active:进入的过程中
- v-enter-to:进入的终点
- 元素离开的样式:
- v-leave: 离开的起点
- v-leave-active: 离开的过程中
- v-leave-to: 离开的终点
- 元素进入的样式:
- 使用<transition>包裹要过度的元素,并配置name属性
<transition name="hello"> <h1 v-show="isShow">你好啊</h1> </transition>
- 若有多个元素需要过度,则需要使用:<transition-group>,且每个元素都要指定key值
- 可以使用第三方库,如Animate.css库
vue脚手架配置代理
- 方法一:在vue.config.js中添加如下配置:
说明:devServer:{ proxy:'http://localhost:5000' }
1. 优点:配置简单,请求资源时直接发给前端即可
2. 缺点:不能配置多个代理,不能灵活的控制请求是否走代理
3. 工作方式:若按照上述配置代理,当前端请求了不存在的资源时,那么该请求会转发给服务器(优先配置前端资源) - 方法二:编写vue.config.js代理规则:
devServer: {
proxy: {
'/api': {//匹配所有以'/api'开头的请求路径
target: 'http://localhost:5000',//代理目标的基础路径
pathRewrite:{'^/api':''},
ws: true,//用于支持websocket的
changeOrigin: true
},
'/demo': {
target: 'http://localhost:5001',
pathRewrite:{'^/demo':''},
ws: true,//用于支持websocket的
changeOrigin: true
},
}
}
- changOrigin设置为true时,服务器收到的请求头的host为:localhost:5000
- changOrigin设置为false时,服务器收到的请求头的host为:localhost:8080
- changOrigin默认值为true
vue-resource (不推荐使用)
功能和axios一样,是vue团队的一款插件
- 导入库
import vueResource from 'vue-resource';
- 使用该插件
Vue.use(vueResource);
- 发送请求(和axios类似)
this.$http.get(`https://api.github.com/search/users?q=${this.keyword}`).then( response => { this.$bus.$emit('updateListData',{isLoading:false,users:response.data.items}); }, error => { this.$bus.$emit('updateListData',{isLoading:false,errMsg:error.message,users:[]}); } )
插槽
- 作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件===>子组件
- 分类:默认插槽,具名插槽,作用域插槽
- 使用方法:
- 默认插槽
父组件中: <Category> <div>html结构</div> </Category> 子组件中: <template> <div> <slot>插槽默认内容</slot> </div> </template>
- 具名插槽
父组件中: <Category> <template slot="center"> <div>html结构</div> </template> </Category> <Category> <template v-slot:footer> <div>html结构</div> </template> </Category> 子组件中: <template> <div> <slot name="center">插槽默认内容</slot> <slot name="footer">插槽默认内容</slot> </div> </template>
- 默认插槽
- 作用域插槽
- 理解:数据在组件的自身,但根据数据生成的结构需要组件的结构者来决定
- 举例:
父组件 <Music> <template scope="{music}"> <ul> <li v-for="(item,index) in music" :key="index">{{item}}</li> </ul> </template> </Music> 子组件: <template lang="html"> <div class="category"> <h3>音乐分类</h3> <slot :music="music"></slot> </div> </template>
Vuex
- 概念:
在Vuex中实现集中式状态(数据)管理的一个Vue插件,对vue应用的多个组件共享状态进行集中式的管理(读/写),也是一种组件间的通信方式,且适用于任意组件间的通信 - 何时使用
多个组件需要共享数据时 - 搭建vuex环境
- 创建文件:src/store/index
import Vuex from 'vuex'; import Vue from 'vue'; Vue.use(Vuex); //准备actions————用于响应组件中的动作 const actions = {} //准备mutations————用于操作state const mutations = {} //准备state————用于操作数据(state) const state = {} export default new Vuex.Store({ actions, mutations, state })
- 在main.js创建vm时传入store配置项
import store from "./store" ...... new Vue({ render: h => h(App), store }).$mount('#app')
- 创建文件:src/store/index
- 基本使用
- 初始化数据,配置actions,配置mutations,操作文件store.js
import Vuex from 'vuex'; import Vue from 'vue'; Vue.use(Vuex); //准备actions————用于响应组件中的动作 const actions = { add(context,value){ context.commit('ADD',value); }, subtract(context,value){ context.commit('SUBTRACT',value); }, addOdd(context,value){ if(context.state.sum % 2){ context.commit('ADD',value); } } } //准备mutations————用于操作state const mutations = { ADD(state,value){ state.sum += value; }, SUBTRACT(state,value){ state.sum -= value; } } //准备state————用于操作数据(state) const state = {sum:0} export default new Vuex.Store({ actions, mutations, state })
- 组件读取vuex的数据:$store.state.sum
- 组件修改vuex的数据:$store.dispatch(‘action的方法名’,数据)或者$store.commit(‘mutation中的方法名’,数据)
- 若没有网络请求或其他业务逻辑,组件也可以越过actions,直接编写commit
- 初始化数据,配置actions,配置mutations,操作文件store.js
getters的使用
- 概念:当state的数据需要加工后再使用,可以用getters加工
- 在store.js追加getters配置
//准备getters——用于将state中的数据进行加工 const getters = { bigSum(state){ return state.sum*10 } } //创建并暴露store export default new Vuex.Store({ .... getter })
- 组件读取数据 $store.getters.bigSum
4个map写法
- mapState方法:用于帮我们映射state中的数据为计算属性
computed:{ //借助mapState生成计算属性,从state中读取数据(对象写法) ...mapState({sum:'sum',school:'school',subject:'subject'}) //借助mapState生成计算属性,从state中读取数据(数组写法) ...mapState(['sum','school','subject']), }
- mapGetters方法:用于帮我们映射getters中的数据为计算属性
computer:{ //借助mapGetters生成计算属性,从getters中读取数据(对象写法) ...mapGetters({bigSum:'bigSum'}) //借助mapGetters生成计算属性,从getters中读取数据(数组写法) ...mapGetters(['bigSum']) }
- mapActions方法:用于帮助我们生成actions对话的方法,即包含$store.dispatch(xxx)的函数.数组写法和上面类似,这里就不写了
methods:{ ...mapActions({incrementOdd:'addOdd'}), }
- mapMutations方法:用于帮助我们生成mutations对话的方法,即包含$store.commit(xxx)的函数
methods:{ //借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法) ...mapMutations({increment:'ADD',decrement:'SUBTRACT'}) }
- 备注:mapAction与mapMutations使用时,若需要传递参数需要,在模板中绑定事件时传递好参数,否则参数是事件对象
生成唯一的id —— nanoid
- 安装
npm i nanoid
- 导入
import {nanoid} from 'nanoid';
- 使用
id = nanoid()
模块化+命名空间
- 目的:让代码更好维护,让多种数据分类更加明确
- 修改store.js
const count = { namespace:true, state:{...}, action:{...}, getters{...} }, const count = { namespace:true, state:{...}, action:{...}, getters{...} }, export default new Vuex.Store({ modules:{ count, person } })
- 开启命名空间后,组件读取state数据
//方式一 自己直接读取 this.$store.state.person.list //方式二 借助mapState读取 ...mapState('person',['list'])
- 开启命名空间后,组件读取getters的数据
//方式一 自己直接读取 this.$store.getters['person/list'] //方式二 借助mapState读取 ...mapState('person',['list'])
- 开启命名空间后,组件调用dispatch
//方式一 自己直接dispatch this.$store.dispatch('person/addPerson',person) //方式二 借助mapState读取 ...mapActions('person',{add:'addPerson'})
- 开启命名空间后,组件调用commit
//方式一 自己直接commit this.$store.commit('person/addPerson',person) //方式二 借助mapMutaions读取 ...mapMutations('person',{add:'addPerson'})
路由
- 理解:一个路由(router)就是一组映射关系(key-value),多个路由需要路由器(router)来管理
- 前端路由:key是路径,value是组件
- 基本使用
- 安装vue-router,
npm i vue-router
- 应用插件:
Vue.use(VueRoute)
- 编写router配置项
import VueRouter from "vue-router"; import About from '../components/About.vue'; import Home from '../components/Home.vue' export default new VueRouter({ routes:[ { path:'/about', component:About }, { path:'/home', component:Home } ] })
- 安装vue-router,
- 实现切换(active-class可配置高亮样式)
<router-link class="list-group-item" to="/about" active-class="active" > About </router-link >
- 指定展示位置
<router-view></router-view>
- 注意事项
- 路由组件经常存在pages文件夹,一般组件通常存放在components文件夹
- 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载
- 每个组件都有自己的$route属性,里面储存着自己的路由信息
- 整个应用只有一个router,可以通过$router属性获取
多级路由
- 配置路由规则,使用children配置项
export default new VueRouter({ routes:[ { path:'/about', component:About }, { path:'/home', component:Home, children:[ { path:'news', component:News }, { path:'message', component:Message } ] } ] })
- 跳转(要写完整路径)
<router-link to="/home/news"></router-link>
路由的query参数
- 传递参数
<!--跳转路由并携带query参数,to的字符串写法 --> <router-link :to="`/home/message/detail?id=${item.id}&title=${item.title}`" > {{item.title}} </router-link> <!-- 跳转路由并携带query参数,to的对象写法 --> <router-link :to="{ path:'/home/message/detail', query:{ id:item.id, title:item.title } }" > {{item.title}} </router-link>
- 接收参数
$route.query.id $route.query,title
命名路由
- 作用:可以简化路由的跳转
- 如何使用
- 给路由起名字
export default new VueRouter({ routes:[ { name:'about',//起名为about path:'/about', component:About }, { path:'/home', component:Home, children:[ { path:'news', component:News }, { path:'message', component:Message, children:[ { name:'detail',//起名为detail path:'detail', component:Detail, } ] } ] } ] })
- 给路由起名字
- 简化跳转
//跳转到detail <router-link :to="{ name:'detail', query:{ id:item.id, title:item.title } }" >
路由的params参数
- 配置路由,声明接收params参数
{ path:'message', component:Message, children:[ { name:'detail', path:'detail/:id/:title',//使用占位符声明接收params参数 component:Detail, } ] } ] }
- 传递参数
特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置跳转路由并携带params参数,字符串写法 <router-link :to="`/home/message/detail/${item.id}/${item.title}`">{{item.title}}</router-link> --> 跳转路由并携带params参数,对象写法 <router-link :to="{ name:'detail', params:{ id:item.id, title:item.title } }" >
- 接收参数
$route.query.id $route.query,title
路由的props配置
作用:让路由组件更方便地收到参数
{
name:'detail',
path:'detail/:id',
component:Detail,
//第一种写法,props值为对象,该对象所有的key-value组合最终会通过props传给Detail组件
props:{a:900}
//第二种写法,props值为布尔值,布尔值为true,则把路由收到所有params参数通过props传给Detail组件
props:true
//第三种写法:props为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
props($route){
return{
id:$route.query.id,
title:$route.query.title
}
}
}
router-link的replace属性
- 作用:控制路由跳转时操作浏览器历史记录的模式
- 浏览器的历史记录有两种写入方式,分别是push和replace,push是追加历史记录,replace是替换当前记录。路由跳转时默认为push
- 如何开启replace模式:<route-link>News</route-link>
编程式路由导航
- 作用:不借助
<router-link>
实现路由跳转,让路由跳转更灵活 - push , replace的代码。push相当于堆栈,replace则是替代
pushShow(item){ this.$router.push({ name:'detail', params:{ id:item.id, title:item.title } }) }, replaceShow(item){ this.$router.replace({ name:'detail', params:{ id:item.id, title:item.title } }) }
- 浏览历史的前进与后退
this.$router.back()//后退 this.$router.forward()//前进 this.$router.go(num)//num表明前进几步或者后退几步,正数为前进,负数为后退
缓存路由组件
- 作用:让不展示的路由组件保持挂载,不被销毁
- 具体代码
这里的News是组件名,如果没有include的话将缓存所有要展示的组件。<keep-alive include="News"> <router-view></router-view> </keep-alive>
如果想缓存多个,可以这样写::include="['News','Message']"
两个新的生命周期钩子——activated,disactivated
- 作用:路由组件所独有的钩子,用于捕获路由组件的激活状态
- 具体名字
activated
路由组件被激活时触发disactivated
路由组件失活时触发
路由守卫
- 作用:对路由进行权限控制
- 分类:全局守卫,独享守卫,组件内守卫
- 全局守卫
//全局前置路由守卫————初始化或者每次路由切换之前被调用 router.beforeEach((to,from,next)=>{ if(to.meta.isAuth){ if(localStorage.getItem('school')==='scut'){ next(); } else{ alert('学校名不对,无权限查看!') } } else{ next(); } }) //全局后置路由守卫————初始化或者每次路由切换之后被调用 router.afterEach((to)=>{ document.title = to.meta.title || 'route-learning' }) export default router;
要在组件配置项中加入meta:{isAuth:true,title:'message'},
,meta相当于程序员所操作的容器
独享路由守卫
给单个组件使用
beforeEnter(to,from,enter){
if(to.meta.isAuth){
if(localStorage.getItem('school')==='scut'){
next();
}
else{
alert('学校名不对,无权限查看!')
}
}
}
组件内守卫
//进入守卫,通过路由规则,进入组件时被调用
beforeRouteEnter(to,from,next){
if (to.meta.isAuth) {
console.log('@');
if (localStorage.getItem('school') === 'scut') {
next();
}
else {
alert('学校名不对,无权限查看!')
}
}
}
//离开守卫,通过路由规则,离开该组件被调用
beforeRouteLeave(to,from,next){
next();
}
路由器的两种工作模式
- 对一个url来说,什么是hash —— #及其后面的内容就是hash
- hash值不会包括在HTTP请求中,即hash值不会带给服务器
- hash模式
- 地址中永远带着#号,不美观
- 若以后将地址通过让第三方手机app分享,若app校验严格,则地址会标记为不合法
- 兼容性好
- history模式
- 地址干净美观
- 兼容性和hash值略比稍差
- 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题
- 切换
const router = new VueRouter({ mode:'history',//默认是哈希模式 ..... })