vue学习笔记

todolist案例 利用localStorage实现本地存储数据

localStorage和sessionStorage的区别:

  1. 在存储时间上:localStorage除非手动删除否则会一直存放到浏览器本地;sessionStorage在网页关闭时自动删除;
  2. 在存储大小上:两者存储空间大小都为5M
  3. 在生效空间上:localStorage限制在同一页面下才能获取;sessionStorage则没有这个要求;

localStorage基本使用语法

  1. 存放数据:对于不是基本数据类型的值,要通过JSON.stringify()转为JSON
    localStorage.setItem(属性名,属性值);
  2. 获取数据:
    localStorage.getItem(属性名);
    实现逻辑:
    mounted生命周期中初始化本地存储和获取todolist数据,将本地数据赋值给todolist;在获取本地数据前判断是否有该属性,若没有,就添加默认本地数据
    之后每当修改todolist都重新给本地数据赋值

todolist案例 利用自定义事件实现子组件到父组件的数据传递

自定义事件准则:给谁绑定就找谁触发
绑定自定义事件的方法:

  1. 通过v-on指定绑定
    <my-header @addTodo=“handleAddTodo”>
    addTodo是自定义事件名称,handleAddTodo是事件回调函数;
    在my-header组件中触发该事件,并为回调传递参数,参数在app组件中的回调函数可以获取;
    this.$emit(‘addTodo’,x,y,z);
  2. 通过ref打标识,当模板载入页面后(mounted)通过$on(name,callback)绑定自定义事件;
  3. 以上两个方法的区别:
    • 通过ref打标识的方法可以更加灵活的绑定自定义事件,例如延迟几秒后自定义事件才生效;
    • 自定义事件只触发一次:通过$once(name,callback)绑定自定义事件
  4. 解绑自定义事件
    • this.$off(event);//解绑指定事件
    • this.$off([event1,event2]);//解绑多个自定义事件
    • this.$off();//解绑所有的自定义事件

todolist案例 全局事件总线实现两两组件间数据传递

让每个组件实例对象和Vue实例对象都拥有一个公共对象,该对象具有$on()和$emit();vue中有vueComponent实例和vue实例满足条件;

  1. 在Vue的原型链上添加该对象:因为vueComponent的原型对象是Vue
  2. 通过添加一个vueComponent实例对象,这个实例对象通过Vue.extend({})返回一个组件构造函数,再通过new 获取实例
  3. 添加vm对象;在beforeCreate生命周期回调中为Vue添加原型属性,当中的this就是Vue实例对象

todolist案例 消息订阅与发布实现组件间数据传递

第三方库:pubsub.js
应用方法:

  1. pubsub.subscribe(name,callback);消息订阅
  2. pubsub.unsubscribe(name);取消订阅
  3. pubsub.publish(name,attribute);发布消息

需要注意的是:回调函数不仅接收publish传递的参数还有消息名,可以使用下划线占位

谁订阅谁发布:每回消息发布时,订阅了该消息的组件都要执行回调;

todolist案例中编辑功能

当点击编辑按钮时为当前item对象添加isEdit属性来保存编辑状态,事件回调传参获取当前item对象添加属性通过this.$set(obj,key,value)才会被vue认可,来让vue进行数据代理和数据监听;注意添加时判断对象有没有该属性(hasOwnProperty方法);利用该属性控制输入框隐藏显示,当输入框失去焦点时isEdit值为false隐藏输入框,修改对象值;

因为本次案例使用本地存储所以需要对todolist数据进行深度侦听(deep:true);

github搜索案例

想要在请求未回来时显示加载,添加一个loading状态,初始值为false,在请求时修改该值;

技巧:多个状态需要在请求时修改,整合为一个对象,通过es6语法扩展运算符快速修改;
例如需要传四个值修改,但有个值不常修改的,想只传三个值;

this.info={…this.info,…att}

插槽

有三种插槽:

  1. 默认插槽:适合传入一个元素作为组件内容

    使用:在组件中通过<slot></slot>插入元素
  2. 具名插槽:可传入多个元素,以name属性区分

    传入:特殊写法<template v-slot:ownName></template>常用写法slot="ownName"做名称

    使用:<slot name=“ownName”></slot>
  3. 作用域插槽:数据在组件内,使用者决定数据的显示方式;可以理解为使用同一个数据以不同样式展示

    传入:必须要用template包裹,<template scope=“slotDataObj”><ul>…</ul></template>scope接收的是一个对象,包含有多个key-value;


    使用:<slot :data=“data”></slot>

问题:

  1. 当未传入插槽时,slot是空着的
  2. 具名插槽只传入一个,另一个是不是空着的
  3. 针对未传入插槽时,可以在slot内写入内容默认显示
  4. 同name的slot是可以追加的,但是建议使用一个父元素直接包裹,不用一个一个添加name属性
  5. 结构在哪,样式就写在哪里

Vuex插件 状态管理

由vue团队开发的状态管理插件;vue2对应使用vuex3,vue3使用vuex4;

创建store是通过Vuex.Store();需要传入配置对象;再将创建的store赋值给Vue实例对象

配置vuex运行环境

//store.js
import Vue from 'vue'
import Vuex from 'vuex'
//应用vuex插件
Vue.use(vuex)

//配置项对象

//用于存储数据
const state = {}
//用于响应组件中的动作
const actions = {}
//用于操作state数据
const mutations = {}
//用来对state加工,与computed计算属性类似,返回加工后的值
const getters = {}

export default new Vuex.Store({
   state,
   actions,
   mutations,
   getters
})

//main.js
import store from './store/store'

new Vue({
   el:'#app',
   render:h=>h('App'),
   components:{App},
   store
})

使用vuex操作数据

//App.vue
export default {
   data(){
      return {num:1}
   },
   methods:{
      //对总数加一
      increment(){
         //引起increment动作
         this.$store.dispatch('increment',this.num);
      }
   }
}

//store.js

const actions={
   //函数第一个参数接收到的context上下文对象,即store对象;之后的参数是dispatch传递的参数
   increment(context,num){
      context.commit('increment',num);
      //这里可以直接修改state的数据,但是开发工具是不记录这次的修改
      //context.state.count += num;
      //而且在这里可以做一些判断,网络请求等操作,再进行数据的操作
   }
}
const mutations={
   //函数第一个参数接收到的是state
   increment(state,num){
      state.count += num;
   }
}

mapState状态映射 和 mapGetters

在模板中书写太多的store.state.xxx,想通过computed计算属性简写,而在计算属性又有许多重复的代码。借助vuex中的mapState状态映射自动生成计算属性;

import {mapState,mapGetters} from 'vuex'

computed:{
   //dizhi是计算属性名称,address是指定获取的state属性名
   //这里的sum不能触发对象的简写方式,简写方式是当值为变量且名字与属性名相同
   //...mapState({sum:'sum',dizhi:'address'})
   //当生成的计算属性名和获取属性名相同时,可以传入数组
   ...mapState(['sum','address']),
   //从getters获取数据,生成计算属性
   ...mapGetters(['bigSum'])
}

mapActions 和 mapMutations

//App.vue
//需要注意的是,在调用时要传递参数,要不然默认传递的是事件对象
<button @click="increment(num)">+</button>

import {mapActions} from 'vuex'
export default {
   methods:{
      ...mapActions({increment:'increment'})
      //数组写法
      //...mapAction(['increment'])
   }
}

//mapMutations同mapActions相同

vuex 模块化管理

当vuex存放许多数据后,数据是可以分类的,连同操作数据的actions,mutations,getters;

//分为多个配置对象,每个对象都有自己的actions等操作数据动作
const personOption = {
   //在mapState用到配置对象名称,需要给配置对象添加namespaced:true
   namesapced:true,
   state:{},
   actions:{},
   ...
}
const countOption = {
   state:{},
   ...
}
export default new Vuex.Store({
   modules:{
      personOption,
      countOption
   }
})

//获取对应的state
//$store.personOption.state.xxx

//mapState,mapGetters,mapMutations,mapActions自动生成代码
//...mapState('countOption',['count'])
//...mapGetters('countOption',['bigCount'])
//传递的第一个参数指定配置对象

//自己写methods方法和computed计算属性,在属性名前带着配置名称/
/**
methods:{
   increment(){
      this.$store.dispatch('countOption/increment',this.num)
   }
},
computed:{
   bigCount(){
      return this.$store.getters['countOption/bigCount'];
   },
   count(){
      return this.$store.state.countOption.count;
   }
}
*/

模块化引起的注意事项

  1. 原先在里面直接使用this.state.xxx获取数据也是可行的,每个配置项里面的this是store对象;

    但经过模块化区分后就不建议这样使用了,这时的this是总的store对象,包含了personOption
actions={
   increment(context,num){
      //调用countOption的commit
      this.commit('countOption/INCREMENT',num);

      //直接使用context上下文对象,就是当前配置对象
      context.commit('INCREMENT',num);
      //而且在前期也可以使用,后期就算修改为模块化也不需要修改
   }
}
  1. 不通过mapState等方法自动生成代码,配置对象不添加namespaced:true,可以不添加**personOption/**但是会引起被其他配置对象同名属性覆盖问题报错问题,也就是说添加了namespaced:true后,每个配置对象中的属性名可以相同

vue-router 路由

配置

vue2搭配vue-router@3使用,vue3搭配vue-router@4使用;

使用vue-router 实现单页应用

1、创建路由器

//引入vue-router
import VueRouter from 'vue-router'
//引入路由组件
import Home from '../pages/Home'
import About from '../pages/About'

//创建路由器
export default new VueRouter({
   //编写路由规则 是一个数组,以key-value的形式存放多个路由
   routes:[
      {path:'/home',component:Home},
      {path:'/about',component:About}
   ]
})

2、使用路由链接

<router-link to="/home" active-class="active">Home</rouoter-link>
<router-link to="/about" active-class="active">About</rouoter-link>
//active-class="active"指定路由链接选中样式

3、定义路由组件放置位置

<router-view></router-view>

注意点

  1. 当路由不断切换时,路由组件是被不断挂载和加载的
  2. 路由组件比一般组件多了两个属性:$route $router

    $route:与其他路由组件的$route都不相等,是当前路由组件独有的,是当前路由组件的路由规则

    $router:是整个应用的路由器,所有路由组件的$router都相等

多级路由

//router.js
export default new VueRouter({
   route:[
      {
         path:'/home',
         component:Home,
         children:[
            {
               path:'news',
               component:News
            },
            {
               path:'messages',
               component:Messages
            }
         ]
      }
   ]
})

路由传参

query传参

  1. 字符串写法
    //Messages
    <router-link :to="`/home/messages/detail?id=${m.id}&title=${m.title}`">{{m.title}}</router-link>
    
  2. 对象写法
    <router-link :to="{
       path:'/home/messages/detail',
       query:{
          id:m.id,
          title:m.titl
       }
    }">{{m.title}}</router-link>
    
  3. 获取传递的参数
    $route.query.xxx
    

params传参

必须写成对象写法,因为路径是通过命名路由匹配路径,命名路由只能对象写法

//router.js
export default new VueRouter({
   routes:[
      {
         path:'/home',
         component:Home,
         children:[
            {
               path:'messages',
               component:Messages,
               children:[
                  {
                     //命名路由
                     name:'detail',
                     //定义接收参数
                     path:'detail/:id/:title',
                     component:Detail
                  }
               ]
            }
         ]
      }
   ]
}) 

//Messages.vue
<router-link :to="{
   name:'detail',
   params:{
      id:m.id,
      title:m.title
   }
}">{{m.title}}</router-link>

获取参数:

$route.params.xxxx

路由props配置

不在路由组件中通过$route.query.xxx或$route.params.xxx获取参数,直接在组件中props获取参数;

通过路由props配置,将参数传递给组件props:

//router.js
export default new VueRouter({
   routes:[
      {
      ...
         {
            //命名路由
            name:'detail',
            path:'detail/:id/:title',
            component:Detail,
            //第一种写法:props值为对象,将key-value作为props传给组件
            props:{id:666,title:'test'}
            //第二种写法:props值为boolean,将params参数通过props传递给组件,还是要老老实实定义接收的
            props:true
            //第三种写法:props值为函数,返回值作为props传递给组件
            props($route){
               return {id:$route.query.id,title:$route.query.title}
            }
         }
       ...
      }
   ]
}) 

需要注意的是,路径里还是要定义接收params参数的。

router-link的replace属性

  1. 将路由模式转为replace替换模式,替换成当前路径,上一次路径不保留历史记录,无法退回。
  2. 路由模式分为两种:push追加历史记录、replace替换当前记录
  3. 应用:
    <router-link replace to="/home">Home</router-link>
    

编程式路由导航

  1. 定义:不借助router-link跳转路由,让路由调转更加灵活。
  2. 常用api
    • $router.forward()前进
    • $router.back()后退
    • $router.go(n)指定前进或回退到第几步,n>0前进,n<0后退
    • $router.push({path:‘’,query:{…}}),追加模式跳转到指定路由
    • $router.replace({…}),替换模式跳转到指定路由

缓存路由组件

作用:让不展示的路由组件不被销毁(卸载)
实现:

//缓存一个路由
<keep-alive include="News">
   <router-link></router-link>
</keep-alive>

//缓存多个路由
<keep-alive :include="['News','Messages']">
   <router-link></router-link>
</keep-alive>

需要注意的:

  1. include写的是组件名组件名组件名。要是不写那router-link展示的路由组件都会被缓存。
  2. 路由组件要配置name:'News’即组件名,就是在开发工具显示的那个,要不然include不起效

缓存路由引起的两个生命周期钩子

因为路由被缓存后当路由组件不显示时也不会被卸载,所以beforeDestroy()不会被调用,也就没办法通过这个生命周期删除计时器。可以使用以下的生命周期。

  1. deactivated():在路由组件不被显示(失活)时,调用该函数
  2. activated():当路由组件被显示(激活)时,调用该函数

缓存路由需要注意的事项

activated和deactivated是配合keep-alive一起使用的

activated和deactivated没有keep-alive的时候是不会被触发的

路由守卫

全局前置路由守卫

作用:在初始化时、路由切换前调用

//router.js
const router = new VueRouter({
   routes:[
      ...
      {
         path:'/home',
         component:Home,
         meta:{
            isAuth:true //添加路由元,属性名自定义
         }
      }
   ]
})

router.beforeEach((to,from,next)=>{
//to 下一个路径的信息对象,包含path、meta、name...
//from 当前路径的信息对象,包含path、meta、name...
//next() 让路径可以跳转
//首先可以通过meta自定义属性中的isAuth判断下一个路由是否需要路由守卫
if(to.path.meta.isAuth){
   if(localStorage.getItem('xxx') === xxxx){
      next()
   }else{
      alert('sorry,you can\'t go on')
   }
}else{
   next()
}
})

全局后置路由守卫

作用:在初始化时、路由切换后调用;虽然没法拦截,但是可以在路由切换后更改标签或其他操作

const router = new VueRouter({
   routes:[
      {
         path:'/home',
         meta:{title:'首页'},
         component:Home
      },
      {
         path:'/about',
         meta:{title:'相关'},
         component:About
      }
      ...
   ]
})

//没有next
router.afterEach((to,from)=>{
document.title = to.meta.title || '测试'
})

独享路由守卫

作用:单独为一个路由设置守卫,特定路由被切换时调用

export default new VueRouter({
   routes:[
      ...
      children:[
         {
            path:'messages',
            component:Messages,
            beforeEnter(to,from,next){
               if(localStorage.getItem('xxx')==='xxxx'){
                  next()
               }else{
                  alert('you can\'t go on')
               }
            }
         }
      ]
   ]
})

组件内路由守卫

作用:在路由组件中配置路由守卫

//Messages.vue
//通过路由规则,(注意是通过路由规则)进入该路由组件时被调用
beforeRouteEnter(to,from,next){
   if(localStorage.getItem('xxx')==='xxx'){
      next()
   }else{
      alert('you can\'t go on')
   }
},
//通过路由规则,离开该组件时被调用
beforeRouteLeave(to,from,next){
   ....
}

路由模式

  1. hash模式:#后边的值就是hash值,hash值不包含在HTTP请求中,不会带给服务器
  2. history模式
  3. 修改模式:
    //router.js
    export default new VueRouter({
       mode:'hash',
       route:[...]
    })
    
  4. 区别
    • hash带有#不美观
    • hash兼容性比history好
    • history在项目部署时要后端人员支持,解决页面刷新时将路径当作资源请求造成的404问题

      后端解决方法有:使用node.js中间件connect-history-api-fallback

Vue3

改变

  1. 改用proxy实现响应式
  2. 内存更小,更新渲染和初次渲染更快

vue引入方式

比vue2中App更加“轻盈”

import {createApp} from 'vue'
import App from './App'

createApp(App).mount('#app')

template模板可以不在外边使用div包裹全部元素

新增setup配置项

  1. 数据、方法等写在setup中,通过返回值让模板获取
  2. setup是所有Composition API(组合API)“ 表演的舞台 ”。
  3. 可以在配置中写vue2的data、methods,但是vue2配置项中可以获取vue3的配置,vue3获取不到vue2;
  4. vue3与vue2的配置重名时,vue3优先
  5. setup返回值有对象,渲染函数:
    • 返回对象时在模板中可以直接使用
    • 返回一个渲染函数时,可以更改渲染的内容,不根据模板渲染
    //setup返回对象,利用作用域减少this使用
    import {h} from 'vue'
    export default {
       name:'App',
       setup(){
          let name='zhangsan'
          let age=44
          function cslName(){
             console.log(name)
          }
          return {
             name,
             age,
             cslName
          } 
       }
    }
    //setup返回一个渲染函数,这个渲染函数在vue中获取
    ...
    setup(){
       ...
       //页面仅显示h渲染的内容
       return ()=>h('h1','测试')
    }
    ...
    

ref函数

  1. 作用:定义一个响应式的数据
    ...
    setup(){
       //基本数据类型还是通过defineProperty()实现响应式
       let name=ref('zhangsan')
       //引用数据类型通过proxy代理实现响应式
       let obj=ref({name:'zhangsan',age:22})
    }
    ...
    
  2. 操作数据通过xxx.value,模板中直接{{xxx}}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值