Vue学习之路

vue.js的引入
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js">

浏览器会显示应用vue.min.js的提示信息,我们可以用下列指令来关闭这个警告

 Vue.config.productionTip=false;
Vue中的事件修饰符
  1. prevent:阻止默认事件的发生(常用)
  2. stop:阻止事件冒泡(常用)
  3. once:事件只触发一次(常用)
  4. capture: 使用事件的捕获模式
  5. self: 只有event.target是当前操作元素时才触发事件
  6. passive: 事件的默认行为立即执行,无需等待事件回调函数进行完毕

ps: 修饰符可以连着写,如 @click.stop.prevent

Vue常用按键别名
  1. vue常见按键别名
    • 回车 => enter
    • 删除 => delete (捕获"删除"或"退格键")
    • 退出 => esc
    • 空格 => space
    • 换行 => tab (特殊,必须配合keydown使用)
    • 上 => up
    • 下 => down
    • 左 => left
    • 右 => right
  2. 其他无别名的,用按键原来的名字即可。注有两个单词构成的名称,要将其小写,并加上连字符进行连接,如 caps-lock
  3. 系统修饰键(用法特殊): ctrl,alt,shift,meta
    • 配合keyup: 按下系统修饰键,然后按下其他按键再释放,才能触发事件
    • 配合keydown: 正常触发事件
  4. keyup,keydown 也可以配合KeyCode来使用(尽量不要使用)
  5. 可以添加别名,如 Vue.config.huiche = 13 (不推荐)
  6. 系统修饰符可以连着写,表示需要先后按下这些按键。如 @keydown.ctrl.y
计算属性
  1. 定义:要用的属性不存在,要通过已有的属性计算得来
  2. 原理:底层借助Object.defineproperty方法提供的getter和setter
  3. get函数什么时候执行?
    • 初次读取时会执行一次
    • 当依赖的数据发生变化时会被再次调用
  4. 优势: 与methon方法的实现相比,内部有缓存机制,效率更高,调试方便
  5. 备注
    • 计算属性最终会出现在vm上,直接读取使用即可
    • 如果计算属性要被修改,那必须要写set函数去响应修改,且set中引起计算时所依赖的数据要发生变化
监视属性
  • 监视属性watch

    1. 当被监视的属性变化时,回调函数自动调用,进行相关操作
    2. 监视属性必须存在,才能进行监视
    3. 监视的两种写法:
      • new Vue时引入watch配置
      • 通过vm.$watch监视
  • 深度监视:

    1. Vue中的watch默认不监测对象内部的值的改变(一层)
    2. 配置deep:true 可以监测对象内部值的改变(多层)

    PS:
    - Vue 自身可以监测对象内部值的改变,但Vue提供的watch默认不可以
    - 使用watch时根据数据的具体结构,决定是否采用深度监视

  • 内部属性

    • immediate:true,//初始化时让handler调用一下
    • handler(newValue,oldValue){}
    • deep
computed 和 watch 之间的区别
  1. computed能完成的功能,watch可以完成
  2. watch 能完成的功能,computed不一定能完成,如 watch可以进行异步操作
    两个重要小原则:
  3. 所有vue管理的函数最好写成普通函数,这样this指向的才是vm
  4. 不被vue管理的函数(定时器的回调函数,Ajax的回调函数,Promise的回调函数),
    最好写成箭头函数,这样this指向的才是vm
绑定样式写法
  1. class样式
    写法:class=“xxx” xxx可以是字符串,对象,数组。
    • 字符串写法适用于:类名不确定,要动态获取
    • 对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。
    • 数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用
  2. style样式
  • :style="{fontSize: xxx}" 其中xxx是动态值
  • :style="[a,b]",其中a,b是样式对象
条件渲染
  1. v-if
    写法:
  • v-if = “表达式”
  • v-else-if = “表达式”
  • v-else = “表达式”
    适用于:切换频率较低的场景
    特点:不展示的DOM元素直接被移除
    注意:v-if可以和v-else-if、v-else一起使用,但要求结构不能被打断
  1. v-show
    写法:v-show = ‘表达式’
    适用于:切换频率较高的场景
  2. 使用v-if时,元素可能无法获取到,而使用v-show时一定需要获取到
v-for指令
  1. 用于展示列表数据
  2. 语法:v-for="(item,index) in xxx" :key=“yyy”
  3. 可遍历:数组,对象,字符串(用得少),指定次数(用得少)
react,vue中的key的作用?(key的内部原理)
  1. 虚拟DOM中key的作用:

    key时虚拟DOM对象的标识,但状态中数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较。

  2. 对比规则

    1. 旧虚拟DOM找到与新虚拟DOM相同的key:
      • 若虚拟DOM的内容没变,直接使用之前真实的DOM
      • 若内容变了,则生成真实的DOM,随后替换掉页面中之前的真实DOM
    2. 若虚拟DOM未找到与新虚拟DOM相同的keys,则创建新的虚拟DOM
  3. 用index作为key会引发的问题

    • 若对数据进行逆序添加,逆序删除等破坏顺序的操作,会产生没有必要的DOM更新,效率低
    • 如果结构中含有输入类的DOM,会产生错误的DOM更新,界面有问题
  4. 开发中如何选择key?

    • 最好选择每条数据唯一的标识作为key,比如id,手机号、身份证号
    • 如果不存在对数据的逆序添加、逆序删除等破坏顺序的操作,仅渲染列表用于展示,使用index作为key是没问题的
Vue监测数据的原理
  1. vue会监测data所有层次的数据

  2. 如何监测对象中的数据
    通过setter实现监视,且在new Vue 时就传入要监测的数据。

    • 对象后追加的属性,Vue默认不做相应式处理
    • 如需给添加的属性做响应式,使用下列API:
    	Vue.set(target,propertyName/index,value)
    	vm.$set(target,propertyName,value)
    
  3. 如何监测数组中的数据
    通过包裹数组更新元素的方法来实现,本质上是做了两件事:

    • 调用原生对应的方法对数组进行更新
    • 重新解析模板,进而更新页面
  4. 在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”/>,则
    1. 没有配置input的value属性,则收集到的就是checked(勾选或者没勾选,是布尔值)
    2. 配置了input的value属性
      • v-model的初始值是非数组,那么收集到的就是checked(是布尔值)
      • v-model的初始值是数组,那么收集的就是value组成的数组
  • PS:
    v-model的三个属性值:
    • lazy:失去焦点收集数据
    • number:输入字符串转为有效的数字
    • trim:输入首尾空格过滤
过滤器(Vue3已移除)
  • 定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑处理)
  • 语法
    1. 注册过滤器:Vue.filter(name,callback) =>全局或 new Vue(filters{}) =>局部
    2. 使用过滤器:{{ xxx | 过滤器名 }} 或 v-bind: 属性 = “xxx | 过滤器名”
  • 备注:
    1. 过滤器也可以接受额外参数,多个过滤器可以串联
    2. 并没有该改变原来的数据,是产生新的对应数据
内置指令
  • v-text指令
    1. 作用:向其所在的节点中渲染文本内容
    2. 与插值语法的区别:v-text会替换节点中的内容,而{{x}}不会
  • v-html
    1. 作用:向指定节点中渲染包含html结构的内容
    2. 与插值语法的区别:
      • v-html会替换节点中的所有内容,{{x}}不会
      • v-html可以识别html结构
    3. 严重注意:v-html有严重安全性问题
      - 在网站上动态渲染任意HTML是非常危险的,容易受到xss攻击
      - 一定要在可信的内容上使用html,永远不要用在用户提交的内容上
  • v-cloak 指令(没有值)
    1. 本质上是一个特殊值,Vue实例创建完毕并接管容器后,会删除v-cloak属性
    2. 使用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命名
生命周期函数
  • 基础概念
    1. 又名:生命周期回调函数,生命周期函数,生命周期钩
    2. Vue在关键时期帮我们调用的一些特殊名称的函数
    3. 生命周期函数的名字不可更改,但函数的具体内容可以根据程序员的具体需求编写的
    4. 生命周期函数的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使用组件的三大步骤
  1. 定义组件
    使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但区别如下:
    • el不用写——所有的组件都要经过vm的管理,由vm决定服务那个容器
    • data必须写成函数——避免组件被复用时,数据存在引用关系
    • 使用template可以配置组件结构
  2. 如何注册组件
    • 局部注册:靠new Vue的时候传入component选项
    • 全局注册:靠Vue.component(‘组件名’,组件)
    • 简写:const school = Vue.extend(options)可以简写为 const school = options
  3. 编写组件标签:
    如 <school></school>
组件名
  • 一个单词组成:
    • 第一种写法(首字母小写):school
    • 第二种写法(首字母大写):School
  • 多个单词组成:
    • kebab-case命名:my-school
    • CamelCase命名:MySchool(需要Vue脚手架支持)
  • 备注
    • 组件名尽可能回避HTML已有的元素名称
    • 可以使用name配置指定组件在开发者工具中呈现的名字
组件标签

基本写法:

  • <school></school>
  • <school/> 不使用脚手架时, <school/> 会导致后续组件不能渲染
一个重要的内置关系
  • VueComponent.prototype === Vue.proptotype
  • 有了这个关系,让组件实例对象(vc)可以访问到Vue原型上的属性、方法
ref 属性
  1. 被用来给元素或者子组件注册引用信息(id的替代者)
  2. 应用在html标签上获取的是真实Dom元素,应用在组件标签上是组件实例对象
  3. 使用方法:
    • 打标识: <h1 ref=“xxx”>…</h1> 或者 <School ref=“xxx”></School>
    • 获取: this.$refs.xxx
配置项props

功能:让组件接收外部传回来的数据

  1. 传递数据
    <Demo name=“xxx”/>
  2. 接收数据
    • 第一种方式(只接收):
      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案例
  1. 组件化编码流程
    • 拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突
    • 实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用
      • 一个组件在用:放在组件自身即可
      • 一些组件在用:放在它们共同的父组件上(状态提升)
    • 实现交互:从绑定事件开始
  2. props适用于
    1. 父组件 ===> 子组件 通信
    2. 子组件 ===> 父组件 通信 (要求父先给子一个函数)
  3. 使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的
  4. props传过来的若是对象类型的值,修改对象时Vue不会报错,但不推荐这样做
webStorage
  1. 储存的大小支持5MB左右
  2. 浏览器通过Window.sessionStorage和Window.localStorage属性来实现本地储存机制
  3. 相关API
    1. xxxxStorage.setItem(‘key’,‘value’)
      该方法接受一个键和值作为参数,会把键值对加入在存储中,如果键名存在,则更新其对应地值
    2. xxxxStorage.getItem(‘key’)
      该方法接受一个键名作为参数,返回键名对应的值
    3. xxxxStorage.removeItem(‘key’)
      该方法接受一个键名作为参数,放回键名对应的值
    4. xxxxStorage.clear()
      该方法会清空储存中的所有数据
  4. 备注
    1. SessionStorage储存的内容会随着浏览器的关闭而消失
    2. LocalStorage储存的内容,需要手动清除才会消失
    3. xxxxStorage.getItem(‘key’) 如果key获取不到,那么返回值为null
    4. JSON.parse(null)的结果还是null
组件自定义事件
  1. 一种组件间的通信方式,适用于 子组件 ===> 父组件
  2. 使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)
  3. 绑定自定义事件:
    • 第一种方式:在父组件中,
      <Demo  @a="test"/>
      
    • 第二种方式,在父组件中
      <Demo ref="demo">
      .....
      mounted(){
      	this.$refs.xxx.$on('a',this.test)
      }
      
    • 若想让自定义事件只触发一次,可以使用once修饰符,或者$once()方法
  4. 触发自定义事件:this.$emit(‘a’,数据)
  5. 解绑自定义事件: this.$off(‘a’)
  6. 组件上也可以绑定原生的DOM事件,需要加native修饰符
  7. 注意:通过 this.$refs.xxx.$on(‘a’,回调)绑定自定义事件,回调要么只能配置在methods中,要么用箭头函数,否则this指向会出问题
全局事件总线
  1. 一种组件间的通信方式,适用于任意组件间的通信
  2. 安装全局事件总线:
    new Vue({
    ......
    beforeCreate(){
    	Vue.proptotype.$bus = this;//安装全局事件总线,$bus就是当前应用的vm
    }
    ......
    })
    
  3. 使用事件总线:
    1. 接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件本身
    methods(){
    	demo(data){...}
    }
    ....
    mounted(){
    	this.$bus.$on('xxxx',this.demo)
    }
    
    1. 提供数据
      this.$bus.$on('xxxx',this.demo)
      
  4. 最好在beforeDestroy钩子里,用$off解绑当前组件所用到的事件
消息订阅与发布(pubsub)
  1. 一种组件间通信的方式,适用于任意组件间的通信
  2. 使用步骤
    1. 安装pubsub:npm i pubsub-js
    2. 引入: import pubsub from ‘pubsub-js’
    3. 接收数据:A组件想接收数据,则在A组件订阅消息,订阅的是回调留在A组件自身
    methods(){
    	demo(data){...}
    }
    ......
    mounted(){
    	this.pid = pubsubcribe('xxx',this.demo)
    }
    
  3. 提供数据: pubsub.publish(‘xxx’,this.demo)
  4. 最好在beforeDestroy钩子里,用pubsub.unsubscribe(pid)去取消订阅
nextTick
  1. 语法:this.$nextTick(回调函数)
  2. 作用:在下一次DOM更新结束后执行其指定的回调
  3. 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作,要在nextTick所在函数中进行
关于Object原型方法调用出现的bug

解决方法
为了避免这种细微的 bug,最好总是从 Object.prototype 调用这些方法。例如,foo.hasOwnProperty(“bar”) 应该替换为 Object.prototype.hasOwnProperty.call(foo, “bar”)。

Vue封装的过度和动画
  1. 作用:在插入、更新或移除DOM元素时,在合适的位置给元素添加样式别名
  2. 写法:
  • 准备好样式:
    • 元素进入的样式:
      1. v-enter:进入的起点
      2. v-enter-active:进入的过程中
      3. v-enter-to:进入的终点
    • 元素离开的样式:
      1. v-leave: 离开的起点
      2. v-leave-active: 离开的过程中
      3. v-leave-to: 离开的终点
  1. 使用<transition>包裹要过度的元素,并配置name属性
    <transition name="hello">
    	<h1 v-show="isShow">你好啊</h1>
    </transition>
    
  2. 若有多个元素需要过度,则需要使用:<transition-group>,且每个元素都要指定key值
  3. 可以使用第三方库,如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团队的一款插件

  1. 导入库
    import vueResource from 'vue-resource';
    
  2. 使用该插件
    Vue.use(vueResource);
    
  3. 发送请求(和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:[]});
                }
            )
    
插槽
  1. 作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件===>子组件
  2. 分类:默认插槽,具名插槽,作用域插槽
  3. 使用方法:
    • 默认插槽
      父组件中:
      	<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>
      
  4. 作用域插槽
    1. 理解:数据在组件的自身,但根据数据生成的结构需要组件的结构者来决定
    2. 举例:
      父组件
      <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
  1. 概念:
    在Vuex中实现集中式状态(数据)管理的一个Vue插件,对vue应用的多个组件共享状态进行集中式的管理(读/写),也是一种组件间的通信方式,且适用于任意组件间的通信
  2. 何时使用
    多个组件需要共享数据时
  3. 搭建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')
      
  4. 基本使用
    1. 初始化数据,配置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
      })
      
    2. 组件读取vuex的数据:$store.state.sum
    3. 组件修改vuex的数据:$store.dispatch(‘action的方法名’,数据)或者$store.commit(‘mutation中的方法名’,数据)
    4. 若没有网络请求或其他业务逻辑,组件也可以越过actions,直接编写commit
getters的使用
  1. 概念:当state的数据需要加工后再使用,可以用getters加工
  2. 在store.js追加getters配置
    	//准备getters——用于将state中的数据进行加工 
    const getters = {
        bigSum(state){
            return state.sum*10
        }
    }
    //创建并暴露store
    export default new Vuex.Store({
    	....
    	getter
    })
    
  3. 组件读取数据 $store.getters.bigSum
4个map写法
  1. mapState方法:用于帮我们映射state中的数据为计算属性
    computed:{
    //借助mapState生成计算属性,从state中读取数据(对象写法)
    ...mapState({sum:'sum',school:'school',subject:'subject'})
    
    //借助mapState生成计算属性,从state中读取数据(数组写法)
    ...mapState(['sum','school','subject']),
    }
    
  2. mapGetters方法:用于帮我们映射getters中的数据为计算属性
    computer:{
    	 //借助mapGetters生成计算属性,从getters中读取数据(对象写法)
        ...mapGetters({bigSum:'bigSum'})
        //借助mapGetters生成计算属性,从getters中读取数据(数组写法)
        ...mapGetters(['bigSum'])
    }
    
  3. mapActions方法:用于帮助我们生成actions对话的方法,即包含$store.dispatch(xxx)的函数.数组写法和上面类似,这里就不写了
    methods:{
     	...mapActions({incrementOdd:'addOdd'}),
    }
    
  4. mapMutations方法:用于帮助我们生成mutations对话的方法,即包含$store.commit(xxx)的函数
    methods:{
      //借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)
        ...mapMutations({increment:'ADD',decrement:'SUBTRACT'})
      }
    
  5. 备注:mapAction与mapMutations使用时,若需要传递参数需要,在模板中绑定事件时传递好参数,否则参数是事件对象
生成唯一的id —— nanoid
  1. 安装
    npm i nanoid
    
  2. 导入
    import {nanoid} from 'nanoid';
    
  3. 使用
    id = nanoid()
    
模块化+命名空间
  1. 目的:让代码更好维护,让多种数据分类更加明确
  2. 修改store.js
    const count = {
    	namespace:true,
    	state:{...},
    	action:{...},
    	getters{...}
    },
    const count = {
    	namespace:true,
    	state:{...},
    	action:{...},
    	getters{...}
    },
    export default new Vuex.Store({
       modules:{
           count,
           person
       }
    })
    
  3. 开启命名空间后,组件读取state数据
    //方式一 自己直接读取
    this.$store.state.person.list
    //方式二 借助mapState读取
    ...mapState('person',['list'])
    
  4. 开启命名空间后,组件读取getters的数据
    //方式一 自己直接读取
    this.$store.getters['person/list']
    //方式二 借助mapState读取
    ...mapState('person',['list'])
    
  5. 开启命名空间后,组件调用dispatch
    //方式一 自己直接dispatch
    this.$store.dispatch('person/addPerson',person)
    //方式二 借助mapState读取
    ...mapActions('person',{add:'addPerson'})
    
  6. 开启命名空间后,组件调用commit
    //方式一 自己直接commit
    this.$store.commit('person/addPerson',person)
    //方式二 借助mapMutaions读取
    ...mapMutations('person',{add:'addPerson'})
    
路由
  1. 理解:一个路由(router)就是一组映射关系(key-value),多个路由需要路由器(router)来管理
  2. 前端路由:key是路径,value是组件
  3. 基本使用
    1. 安装vue-router,npm i vue-router
    2. 应用插件:Vue.use(VueRoute)
    3. 编写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
          }
        ]
      })
      
  4. 实现切换(active-class可配置高亮样式)
    <router-link 
            class="list-group-item" 
            to="/about" 
            active-class="active"
     >
        About
     </router-link >
    
  5. 指定展示位置
    <router-view></router-view>
    
  6. 注意事项
    1. 路由组件经常存在pages文件夹,一般组件通常存放在components文件夹
    2. 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载
    3. 每个组件都有自己的$route属性,里面储存着自己的路由信息
    4. 整个应用只有一个router,可以通过$router属性获取
多级路由
  1. 配置路由规则,使用children配置项
    export default new VueRouter({
      routes:[
        {
          path:'/about',
          component:About
        },
        {
          path:'/home',
          component:Home,
          children:[
            {
              path:'news',
              component:News
            },
            {
              path:'message',
              component:Message
            }
          ]
        }
      ]
    })
    
  2. 跳转(要写完整路径)
    <router-link to="/home/news"></router-link>
    
路由的query参数
  1. 传递参数
    	<!--跳转路由并携带query参数,to的字符串写法 -->
      <router-link 
      	:to="`/home/message/detail?id=${item.id}&title=${item.title}`"
      >
      	{{item.title}}
      </router-link>&nbsp;&nbsp;
      
       <!-- 跳转路由并携带query参数,to的对象写法 -->
    	 <router-link 
    	     :to="{
    	           path:'/home/message/detail',
    	           query:{
    	             id:item.id,
    	             title:item.title
    	            }
    	          }"
    	        >
    	      {{item.title}}
    	  </router-link>&nbsp;&nbsp;
    
  2. 接收参数
    $route.query.id
    $route.query,title
    
命名路由
  1. 作用:可以简化路由的跳转
  2. 如何使用
    1. 给路由起名字
      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,
                  }
                ]
              }
            ]
          }
        ]
      })
      
  3. 简化跳转
    //跳转到detail
    <router-link 
         :to="{
           name:'detail',
           query:{
              id:item.id,
              title:item.title
            }
          }"
        >
    
路由的params参数
  1. 配置路由,声明接收params参数
     {
          path:'message',
          component:Message,
          children:[
            {
              name:'detail',
              path:'detail/:id/:title',//使用占位符声明接收params参数
              component:Detail,
            }
          ]
        }
      ]
    }
    
  2. 传递参数
     跳转路由并携带params参数,字符串写法
        <router-link :to="`/home/message/detail/${item.id}/${item.title}`">{{item.title}}</router-link>&nbsp;&nbsp; -->
     跳转路由并携带params参数,对象写法
        <router-link :to="{
              name:'detail',
              params:{
                id:item.id,
                title:item.title
              }
            }"
        >
    
    特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置
  3. 接收参数
    $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属性
  1. 作用:控制路由跳转时操作浏览器历史记录的模式
  2. 浏览器的历史记录有两种写入方式,分别是push和replace,push是追加历史记录,replace是替换当前记录。路由跳转时默认为push
  3. 如何开启replace模式:<route-link>News</route-link>
编程式路由导航
  1. 作用:不借助<router-link>实现路由跳转,让路由跳转更灵活
  2. 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
        }
      })
    }
    
  3. 浏览历史的前进与后退
    this.$router.back()//后退
    this.$router.forward()//前进
    this.$router.go(num)//num表明前进几步或者后退几步,正数为前进,负数为后退
    
缓存路由组件
  1. 作用:让不展示的路由组件保持挂载,不被销毁
  2. 具体代码
    <keep-alive include="News">
    	<router-view></router-view>
    </keep-alive>
    
    这里的News是组件名,如果没有include的话将缓存所有要展示的组件。
    如果想缓存多个,可以这样写::include="['News','Message']"
两个新的生命周期钩子——activated,disactivated
  1. 作用:路由组件所独有的钩子,用于捕获路由组件的激活状态
  2. 具体名字
    • activated 路由组件被激活时触发
    • disactivated路由组件失活时触发
路由守卫
  1. 作用:对路由进行权限控制
  2. 分类:全局守卫,独享守卫,组件内守卫
  3. 全局守卫
    	//全局前置路由守卫————初始化或者每次路由切换之前被调用
    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();
	}

路由器的两种工作模式
  1. 对一个url来说,什么是hash —— #及其后面的内容就是hash
  2. hash值不会包括在HTTP请求中,即hash值不会带给服务器
  3. hash模式
    1. 地址中永远带着#号,不美观
    2. 若以后将地址通过让第三方手机app分享,若app校验严格,则地址会标记为不合法
    3. 兼容性好
  4. history模式
    1. 地址干净美观
    2. 兼容性和hash值略比稍差
    3. 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题
  5. 切换
    const router = new VueRouter({
    	mode:'history',//默认是哈希模式
    	.....
    })
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值