vue2 学习笔记

发布时间 2023-03-28 21:51:19作者: 悠悠江水

Date: 2023-03-12 17:28:46
尚硅谷Vue2.0+Vue3.0全套教程丨vuejs从入门到精通
记录遵循费曼学习法,让外行也能看懂。

Vue 总体大纲

MarkDown 引用快速查找

  • 模板语法
  • 内置指令
  • computed 和 watch

P1 Vue介绍

P2 Vue简介

P3 Vue官方使用指南

P4 搭建 Vue 开发环境

P5 Hello 小案例

P6 分析 Hello 小案例

P7 模板语法

插值语法(双大括号表达式)

<p>{{xxx}}</p>xxxx 作为 js 表达式解析

指令语法(以 v-开头)

使用场景:用于解析标签属性(属性、内容、绑定事件...)

<a>v-bind:href = 'xxxx'</a>xxxx 会作为 js 表达式被解析,不能出现 this,因为可以直接访问到 vm 对象

注:模板语法的 this 为 vm,并未浏览器对象

P8 数据绑定

单向数据绑定 data => DOM
v-bind:href ="xxx" 或简写为 :href

双向数据绑定 data <=> DOM
v-mode:value="xxx" 或简写为 v-model="xxx"

P9 el 与 data 的两种语法

const v= new Vue({
  // el:'#root'第一种写法
})
v.$mount('#root') //第二种写法

new Vue({
  // data:{
  //   name: 'AAA' //data 第一种写法
  // }
  data: function(){ //data 第二种写法
    return {
      name: 'AAA'
    }
  }
}).$mount('#root')

el写方法能灵活一些,data两种方式的对比

为什么不能用data第一种写法
两者共用了同一个内存地址,componentA 修改的内容,同样对 componentB 产生了影响
如果我们采用函数的形式,则不会出现这种情况(函数返回的对象内存地址并不相同)

P10 理解 MVVM

MVVM 模型

  • M:模型(Model) :对应 data 中的数据
  • V:视图(View) :DOM 模板
  • VM:视图模型(ViewModel) : Vue 实例对象

P11 Object.defintProperty

vue2 实现响应式数据的方式

P12 理解数据代理

P13 Vue中的数据代理

P14 事件处理

绑定监听

  1. v-on:xxx="fun"
    1. @xxx="fun"
    1. @xxx="fun(参数)"
    1. 默认事件形参: event
  2. 隐含属性对象:

P15 事件修饰符

  • .prevent : 阻止事件的默认行为 event.preventDefault()
  • .stop : 停止事件冒泡 event.stopPropagation()

P16 按键修饰符

  • keycode : 操作的是某个 keycode 值的键
  • .keyName : 操作的某个按键名的键(少部分)

P17 事件总结

P18 姓名案例

计算属性 与 监视属性 P18~25

P19~21 计算属性 computed

使用场景 要显示的数据不存在,要通过依赖 data 计算得来。

如何使用

computed 对象中定义计算属性。在页面中使用{{方法名}}来显示计算的结果

data:{        // vm._data
  firstName:'张',
  lastName:'三'
},
computed:{    // 使用时用插值语法 {{fullName}} 即可
  fullName:{
    // vue 管理的函数不能使用箭头函数
    get(){ // 何时调用,1.初次读取时,2. 所依赖的数据发生变化时
      return this.firstName+'-'+this.lastName //this 为 vm
    }
    // 当 fullName 被修改时触发
    set(){ 
      const arr =value.split('-')
      this.firstName=arr[0]
      this.lastName=arr[1]
    }
  }
}

计算属性的数据会被复用,效率更高

计算属性简写方式

只适用 get() 时计算属性可简写

computed:{
  fullName(){
    return this.firstName+'-'+this.lastName
  }
}

P22~24 监视属性-watch

使用场景

datacomputed 发生变化时,要进行一些操作。
来监视指定的属性计算属性,属性变化时, 回调函数自动调用, 在函数内部进行计算

如何使用

通过watch 配置

data:{
  isHot:true,
},
watch:{
  isHot:{
    immediate: true, //初始化时就开始监听
    handler(newValue,oldValue){ //数据改变 handler 开始调用
      console.log('info被修改了',newValue,oldValue)
    }
  }
}

通过 vm 对象$watch()

vm.$watch('isHot':{ //一样,就是外层结构改一下
  immediate: true, //初始化时就开始监听
  handler(newValue,oldValue){ //数据改变 handler 开始调用
    console.log('info被修改了',newValue,oldValue)
  }
})

深度监视

根据数据结构和需求,选择是否添加深度监视

data:{
  numbers:{
    a:1,
    b:2
  }
},
watch:{
  numbers:{
    deep:true, //深度监视 numbers 和其子属性 改变
    handler(newValue,oldValue){
      console.log('info被修改了',newValue,oldValue)
    }
  }
}

监视的简写形式

配置项中只使用 handler 时使用,开发时能用简写用简写

// 简写形式
watch:{
  isHot(newValue,oldValue){
    console.log('info被修改了',newValue,oldValue)
  }
}
vm.$watch('isHot',function(newValue,oldValue){ //不能写成箭头函数,会造成 this 指向问题
  console.log('info被修改了',newValue,oldValue)
}
)

P25 watch 对比 computed 的区别

watch 对比 computed 的区别

watch:{
  isHot(val){
    setTimeout((val)=>{ //this 指向是什么?
      console.log('info被修改了',val)
    },1000)
  }
}

两者的使用场景非常相近,computed 能做到的 watch 也能做到,但 computed 性能更高,所以优先使用 computed ,

computed 不能开启异步任务,因为其依靠 return 返回值

P26-27. class 与 style 绑定

应用场景: 实现动态样式效果

不管怎么写,就是vue的单向数据绑定,根据业务需求修改

实现 class 绑定

data() {
  return {
    xxx: "b2",
  };
},

<button class="basic" :class="xxx" @click="handleClass">切换 class</button>

元素上添加 :class='xxx'

表达式是字符串: 'classA'
表达式是对象: {classA:isA, classB: isB}
表达式是数组: ['classA',]

实现 style 绑定

  data() {
    return {
      styleObj: {
        color: "blue",
        fontSize: "50px",
        backgroundColor: "yellow",
      }
    };
  },

<div :style="styleObj">style 修改</div>

P28 条件渲染

P29 列表渲染

P30 key 作用和原理

P31 列表过滤

P32 列表排序

P33 更新的一个问题

P34 Vue监视数据的原理

1.9.2. 比较 v-if 与 v-show

  1. 如果需要频繁切换 v-show 较好
  2. 当条件不成立时, v-if 的所有子节点不会解析(项目中使用)

列表显示指令

遍历数组: v-for / index
遍历对象: v-for / ke

filter 过滤器

对要显示的数据进行特定格式化后再显示
2. 注意: 并没有改变原本的数据, 是产生新的对应的数据

内置指令与自定义指令

常用内置指令

  1. v-text : 更新元素的 textContent
  2. v-html : 更新元素的 innerHTML
  3. v-if : 如果为 true, 当前标签才会输出到页面
  4. v-else: 如果为 false, 当前标签才会输出到页面
  5. v-show : 通过控制 display 样式来控制显示/隐藏
  6. v-for : 遍历数组/对象
  7. v-on : 绑定事件监听, 一般简写为@
  8. v-bind : 绑定解析表达式, 可以省略 v-bind
  9. v-model : 双向数据绑定
  10. v-cloak : 防止闪现, 与 css 配合: [v-cloak] { display: none

自定义指令

注册全局指令

directives : {
  'my-directive' : {
    bind (el, binding) {
      el.innerHTML = binding.value.toupperCase()
    }
  }
}

使用指令
v-my-directive='xxx

Vue 生命周期 P48~52

vue 生命周期钩子函数

> 挂载流程 初始化显示

  • beforeCreate() 数据监测、数据代理创建前
  • created() 可以通过 vm 访问到 data 中的数据、methods中配置的方法。
  • beforeMount() 虚拟DOM还在内存中,没有变成真实 DOM
  • mounted() Vue完成模板解析,初始 DOM 已生成

> 更新流程
更新状态: this.xxx = value

  • beforeUpdate() 数据是新的,页面是旧的
  • updated() 数据是新的,页面也是新的

> 销毁流程
销毁 vue 实例: vm.$destory()

  • beforeDestory() 对 data 做的所有操作都不会触发更新了
  • destoryed()

在各个生命周期一般要做的事情

created() 发送 Api 请求
mounted(): 启动定时器等异步任务、绑定自定义事件、订阅消息、发送 Api 请求等
beforeDestory(): 做收尾工作, 如: 清除定时器、清楚订阅、解绑自定义事件等

面试题:在created() 和在 mounted() 里写接口请求有什么区别?

在 mounted() 里写接口请求,因为DOM已经生成,所以可能会造成闪屏,用户体验不佳。

单文件组件

一个.vue 文件的组成(3 个部分)

<template>  // 页面模板
</template>
<script>    // 模块对象
export default {
  data() {
    return {}
  }, 
  methods: {}, 
  computed: {}, 
  components: {}
}
</script>
<style>     // 样式定义
</style>

基本使用

  1. 引入组件 import xxx from '/src/components/xxx'
  2. 映射成标签 ``
  3. 使用组件标签 <xxx></xxx>

使用 vue 脚手架工具 Vue CLI

Vue 脚手架隐藏了所有 webpack 相关的配置,若想查看具体的 webpakc 配置,
请执行:vue inspect > output.js
之所以隐藏,就是不想让你动里面的东西,修改在vue.config.js中设置就可以了

P62 脚手架文件结构

├── node_modules
├── public (文件不会被脚手架压缩处理)
│ ├── favicon.ico: 页签图标
│ └── index.html: 主页面
├── src
│ ├── assets: 存放静态资源,(文件会被脚手架压缩处理)
│ │ └── logo.png
│ │── component: 存放组件
│ │ └── HelloWorld.vue
│ │── App.vue: 汇总所有组件
│ │── main.js: 入口文件
├── .gitignore: git 版本管制忽略的配置
├── babel.config.js: babel 的配置文件
├── package.json: 应用包配置文件
├── README.md: 应用描述文件
├── package-lock.json:包版本控制文件

P63 关于不同版本的Vue

  1. vue.jsvue.runtime.xxx.js 的区别:
    vue.js 是完整版的 Vue,包含:核心功能 + 模板解析器。
    vue.runtime.xxx.js 是运行版的 Vue,只包含:核心功能;没有模板解析器。
  2. 因为 vue.runtime.xxx.js 没有模板解析器,所以不能使用 template 这个配置项,需要使用 render 函数接收到的 createElement 函数去指定具体内容。

P65~66 ref 与 props 属性

  • ref

使用场景:用于给节点打标识
读取方式:this.$refs.xxxxxx

  • props

使用场景: 用于父组件 => 子组件传递数据

传递数据

<Demo name="xxx"/>

接收数据

  1. 只接收名称
    props: ['name', 'age', 'setName']

  2. 限制类型

    props: {
    name: String, age: Number, setNmae: Function
    }
    
  3. 指定名称/类型/必要性/默认值

    props: {
    name: {type: String, required: true, default:xxx}, 
    }
    

注❗: props只读的,Vue 底层会监测你对 props 的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制 props 的内容到 data 中一份,然后去修改 data 中的数据

P67 mixin 混入

使用场景: 复用 vm 里所写的配置

如何使用

  • in src/ create mixin.js 文件

  • mixin.js 中写通用配置

    export const toMix = {
      methods:{
        show(){
          alert("这是混入内容")
        }
      }
    }
    export const AAA = {
      methods:{
        show(){
          alert("这是AAA")
        }
      }
    }
    
  • 在目标组件里
    局部混合

    import {toMix,AAA} from "@/mixin.js"
    export default {
      mixins: [toMix,AAA]
    }
    

    全局混入 会污染所有 vm/vc
    在 main.js 中

    import {toMix,AAA} from "@/mixin.js"
    Vue.use(toMix)
    Vue.use(AAA)
    

注:发生冲突以组件为主,生命周期钩子全部生效

P68 插件

使用场景

用于增强 Vue 功能,里面可以写 过滤器、全局指令、混入等等,无需一个个在 main.js 中引入使用。一次引入即可解决

其本质:包含 install 方法的一个对象,install 的第一个参数是 Vue,第二个以后的参数是插件使用者传递的数据。

如何使用

  • 定义插件,create plugins.js

  • plugins.js 写内容

    export default{
    install = function (Vue, options) {
        // 1. 添加全局过滤器
        Vue.filter(....)
        // 2. 添加全局指令
        Vue.directive(....)
        // 3. 配置全局混入(合)
        Vue.mixin(....)
        // 4. 原型添加实例方法
        Vue.prototype.$myMethod = function () {...}
        Vue.prototype.$myProperty = xxxx
      }
    }
    
  • main.js中,引入使用

    import plugins from './plugins' 
    Vue.use(plugins)                
    

P69 scoped 样式

使用场景:让样式在局部生效,防止冲突。
写法:<style scoped>
使用插件:Vue.use()

P70~77 组件化编码开发流程(通用思路)

  1. 实现静态组件:抽取组件,使用组件实现静态页面效果
  2. 展示动态数据:
    数据的类型、名称是什么
    数据保存在哪个组件?
    • 一个组件在用:放在组件自身即可。
    • 父子组件在用:放在他们共同的父组件上(状态提升)
  3. 交互——从绑定事件监听开始,处理交互逻辑

P78 webStorage

  • 存储内容大小一般支持 5MB 左右(不同浏览器可能还不一样)
  • 浏览器端通过 Window.sessionStorageWindow.localStorage 属性来实现本地存储机制。

相关 API

  1. xxxxxStorage.setItem('key', 'value'); 该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。
  2. xxxxxStorage.getItem('person'); 该方法接受一个键名作为参数,返回键名对应的值。
  3. xxxxxStorage.removeItem('key'); 该方法接受一个键名作为参数,并把该键名从存储中删除。
  4. xxxxxStorage.clear() 该方法会清空存储中的所有数据。

P80 组件的自定义事件

使用场景 一种子组件 ===> 父组件通信的方式,react里也有。

如何使用

A 是父组件,B 是子组件,B 想给 A 传数据,那么就要在 A 中给 B 绑定自定义事件(事件的回调也在 A 中

  • 绑定自定义事件

    (第一种方式)在父组件中:<Demo @atguigu="test"/>

    (第二种方式)在父组件中:

    <Demo ref="demo"/>
    ......
    mounted(){
        this.$refs.xxx.$on('atguigu',this.test)
    }
    

    若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法。

触发自定义事件:this.$emit('atguigu',数据)
解绑自定义事件:this.$off('atguigu')

Other

组件上也可以绑定原生 DOM 事件,需要使用native修饰符。
注意:通过this.$refs.xxx.$on('atguigu',回调) 绑定自定义事件时,回调要么配置在 methods 中要么用箭头函数,否则 this 指向会出问题

P84 全局事件总线

使用场景: 不相关组件间的通信方式

实现原理

注:

  • 所有组件实例对象的原型对象的原型对象 就是 Vue 的原型对象
    js继承
  • 所有组件对象都能看到 Vue 原型对象上的属性和方法
  • Vue.prototype.$bus = new Vue(), 所有的组件对象都能看到$bus这个属性对象

(其实就是在Vue原型对象上又挂了一个vue原型对象,俗称套娃)

使用方式

指定事件总线对象

new Vue({
  beforeCreate () { // 尽量早的执行挂载全局事件总线对象的操作
    Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的 vm
  },
})

提供数据:this.$bus.$emit('xxxx',数据)

接收数据:A 组件想接收数据,则在 A 组件中给$bus 绑定自定义事件,事件的回调留在 A 组件自身。

methods(){
  demo(data){......}
}
......
mounted() {
  this.$bus.$on('xxxx',this.demo)
}

P87 消息订阅与发布

使用场景: 不相关组件间的通信方式

Vue 不常用,react 经常使用,这种方式的思想与全局事件总线很相似

它包含以下操作:
(1) 订阅消息 --对应绑定事件监听
(2) 发布消息 --分发事件
(3) 取消消息订阅 --解绑事件监听

使用流程

  1. 安装 pubsub:npm i pubsub-js

  2. 引入: import pubsub from 'pubsub-js'

  3. 接收数据:A 组件想接收数据,则在 A 组件中订阅消息,订阅的回调留在 A 组件自身。

    methods(){
      demo(data){......}
    }
    ......
    mounted() {
      this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
    }
    
  4. 提供数据:pubsub.publish('xxx',数据)

  5. 最好在 beforeDestroy 钩子中,用PubSub.unsubscribe(pid)取消订阅

P90 nextTick

使用场景 当改变数据后,要基于更新后的新 DOM 进行某些操作

要在 nextTick 所指定的回调函数中执行。在下一次 DOM 更新结束后执行其指定的回调

语法:this.$nextTick(回调函数)

Vue 中的自定义事件

使用场景: 子组件 => 父组件传递数据

在父组件中绑定事件监听

<Header @addTodo="addTodo"/>
或者
<Header ref="header"/>
this.$refs.header.$on('addTodo', this.addTodo)

在子组件中触发事件 this.$emit('addTodo', todo)

P91 过渡与动画

使用场景: 在插入、更新或移除 DOM 元素时,在合适的时候给元素添加样式类名

vue 动画的理解:
操作 css 的 trasition 或 animation,vue 会给目标元素添加/移除特定的 class

  • 准备好样式:

    元素进入的样式:

    1. v-enter:进入的起点
    2. v-enter-active:进入过程中
    3. v-enter-to:进入的终点
      元素离开的样式:
    4. v-leave:离开的起点
    5. v-leave-active:离开过程中
    6. v-leave-to:离开的终点
  • 使用<transition> 包裹要过渡的元素,并配置 name 属性

<transition name="hello">
 <h1 v-show="isShow">你好啊!</h1>
</transition>
备注:若有多个元素需要过度,则需要使用:`<transition-group>`,且每个元素都要指定`key`值。

面试题:如何解决开发环境的跨域问题?

根本原因是浏览器同源策略的限制,实际上请求的接口数据已经收到,只不过被浏览器拦截了,解决方式有很多,主要是挂代理,因为 服务器 \(\Rightarrow\) 服务器没有此限制。
在 vue 项目中,官方脚手架已经内置了解决方案,主要是使用 nodejs开个本地服务器代理进行转发,稍微设置一下即可

vue 项目中常用的 Ajax 库, axios

axios 对 原生hdr请求 进行了一次封装,是我们能够更好的发送请求。

在实际开发过程中,一般会对 axios 进行二次封装,扔到 utils 文件夹里,目的是统一进行请求,响应拦截处理,简化请求发送代码

P102~104 slot 插槽

使用场景: 父组件 => 子组件传递带数据结构不定的标签

插槽内容是在父组件中编译后, 再传递给子组件的。

分类

  1. 默认插槽
  2. 命名插槽
  3. 作用域插槽

使用方式

<!-- 父组件中: -->
<Category>
  <div>html结构1</div>
</Category>
<!-- 子组件中: -->
<template>
  <div>
    <!-- 定义插槽 -->
    <slot>插槽默认内容...</slot>
  </div>
</template>

具名插槽

<!-- 父组件中: -->
<Category>
   <template slot="center">
     <div>html结构1</div>
   </template>
   <template v-slot:footer>
      <div>html结构2</div>
   </template>
</Category>
<!-- 子组件中: -->
<template>
  <div>
    <!-- 定义插槽 -->
    <slot name="center">插槽默认内容...</slot>
    <slot name="footer">插槽默认内容...</slot>
  </div>
</template>

作用域插槽

使用场景: 数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。
(games 数据在 Category 组件中,但使用数据所遍历出来的结构由 App 组件决定)

**使用详情** ```js ```

Vuex P105~110

 最新版更名为 pinia,我之后会在后面写 pinia 的使用方式,确实 mutation 数据更新,极为没必要

使用场景官方用来解决 无直接关系组件使用通用数据 的方案

// data 数据存储
const state = {
  xxx: initValue
}
// 处理请求,无所谓的,直接写一起也没什么
const actions = {
  zzz({commit,state},data1){
    commit('yyy',{data1})
  }
}
// 直接修改数据 pinia 直接干掉了,大可不必
const mutations={
  yyy (state, {data1}){
    //更新state的某个属性
  }
}
// vuex 里的 计算属性
const getters = {
  mmm(state){
    return state.msg+'!'
  }
}

modules

  1. 包含多个 module
  2. 一个 module 是一个 store 的配置对象
  3. 与一个组件(包含有共享数据)对应

P107 使用Vuex

搭建 vuex 环境

//引入Vue核心库
import Vue from "vue";
//引入Vuex
import Vuex from "vuex";
//应用Vuex插件,can look store in the vm\vc.use store in the main.js.
Vue.use(Vuex);

//准备actions对象——响应组件中用户的动作
const actions = {};
//准备mutations对象——修改state中的数据
const mutations = {};
//准备state对象——保存具体的数据
const state = {};

//创建并暴露store
export default new Vuex.Store({
  actions,
  mutations,
  state,
});

main.js中创建 vm 时传入store配置项

//引入store
import store from "./store";
//创建vm
new Vue({
  el: "#app",
  render: (h) => h(App),
  store,
});

基本使用

初始化数据、配置actions、配置mutations,操作文件store.js

import Vue from "vue";    //引入Vue核心库
import Vuex from "vuex"; //引入Vuex
Vue.use(Vuex);            //引用Vuex,先import后jscode.
const actions = { //响应组件中加的动作
  jia(context, value) {
    // console.log('actions中的jia被调用了',miniStore,value)
    context.commit("JIA", value);
  },
};
const mutations = { //执行加
  JIA(state, value) {
    // console.log('mutations中的JIA被调用了',state,value)
    state.sum += value;
  },
};
//初始化数据
const state = {
  sum: 0,
};
//创建并暴露store
export default new Vuex.Store({
  actions,
  mutations,
  state,
});

组件中读取 vuex 中的数据:$store.state.sum
组件中修改 vuex 中的数据:$store.dispatch('action中的方法名',数据)$store.commit('mutations中的方法名',数据)

备注:若没有网络请求或其他业务逻辑,组件中也可以越过 actions,即不写dispatch,直接编写commit

原则:只要有一些业务逻辑,都放在actions里。mutation 只负责改变值。大可不必,顾客才不在乎厨师怎么做的饭。

P111 getters 的使用

概念:当 state 中的数据需要经过加工后再使用时,可以使用 getters 加工。像组件里的computed

store.js中追加getters配置

......
  // in store.index.js file,similar component computed attribute.
const getters = {
  bigSum(state){
  return state.sum * 10
  }
}

//创建并暴露store
export default new Vuex.Store({
  ......
  getters
})

组件中读取修改后的数据:$store.getters.bigSum

P112-113. 四个 map 方法的使用

如果此组件里使用vuex api 比较多,可使用这4个方法脱壳,简化书写

  • mapState 方法:映射state中的数据

    computed: {
     //借助mapState生成计算属性:sum、school、subject(对象写法)
      ...mapState({sum:'sum',school:'school',subject:'subject'}),
    
     //借助mapState生成计算属性:sum、school、subject(数组写法)
     ...mapState(['sum','school','subject']),
    },
    
  • mapGetters 方法:用于帮助我们映射getters中的数据为计算属性

    computed: {
        //借助mapGetters生成计算属性:bigSum(对象写法)
        ...mapGetters({bigSum:'bigSum'}),
    
        //借助mapGetters生成计算属性:bigSum(数组写法)
        ...mapGetters(['bigSum'])
    },
    
  • mapActions 方法:用于帮助我们生成与actions对话的方法,即:包含$store.dispatch(xxx)的函数

    methods:{
        //靠mapActions生成:incrementOdd、incrementWait(对象形式)
        ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
    
        //靠mapActions生成:incrementOdd、incrementWait(数组形式)
        ...mapActions(['jiaOdd','jiaWait'])
    }
    
  • mapMutations 方法 :用于帮助我们生成与mutations对话的方法,即:包含$store.commit(xxx)的函数

    methods:{
        //靠mapActions生成:increment、decrement(对象形式)
        ...mapMutations({increment:'JIA',decrement:'JIAN'}),
    
        //靠mapMutations生成:JIA、JIAN(对象形式)
        ...mapMutations(['JIA','JIAN']),
    }
    

备注:mapActions 与 mapMutations 使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。

P115 模块化+命名空间

使用场景:大型项目通用数据较多,为了让代码更好维护,让多种数据分类更加明确。

修改store.js

const countAbout = {
  namespaced:true,//开启命名空间
  state:{x:1},
  mutations: { ... },
  actions: { ... },
  getters: {
    bigSum(state){
        return state.sum * 10
    }
  }
}
const personAbout = {
  namespaced:true,//开启命名空间
  state:{ ... },
  mutations: { ... },
  actions: { ... }
}
const store = new Vuex.Store({
  modules: {
    countAbout,
    personAbout
  }
})

开启命名空间后,组件中读取 state 数据:

//方式一:自己直接读取
this.$store.state.personAbout.list
//方式二:借助mapState读取:
...mapState('countAbout',['sum','school','subject']),

开启命名空间后,组件中读取 getters 数据:

//方式一:自己直接读取
this.$store.getters['personAbout/firstPersonName']
//方式二:借助mapGetters读取:
...mapGetters('countAbout',['bigSum'])

开启命名空间后,组件中调用 dispatch

//方式一:自己直接dispatch
this.$store.dispatch('personAbout/addPersonWang',person)
//方式二:借助mapActions:
...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})

开启命名空间后,组件中调用 commit

//方式一:自己直接commit
this.$store.commit('personAbout/ADD_PERSON',person)
//方式二:借助mapMutations:
...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),

多组件时使用

vuex的简化封装(uView框架)

Vue 路由管理 P117~133

使用版本为 vue-router@3

P117 vue-router介绍

使用场景:vue 的一个插件库,专门用来实现单页面应用的。

路由分类

  • 后端路由:
    理解:value 是 function, 用于处理客户端提交的请求。
    工作过程:服务器接收到一个请求时, 根据请求路径找到匹配的函数来处理请求, 返回响应数据。
  • 前端路由:
    理解:value 是 component,用于展示页面内容。
    工作过程:当浏览器的路径改变时, 对应的组件就会显示。

P118 基本使用

编写使用路由的 3 步

  1. 定义路由组件
    views 文件夹里定义 xxx.vue 组件,这个文件夹里都是要展现的页面。
  2. 注册路由
    在 vueRouter 里统一注册,编写好路径
  3. 使用路由
    在页面里引入使用。

路由的基本使用

适用于 vue@3

  1. 安装 vue-router,命令:yarn add vue-router@3
  2. 引入并应用插件:
  3. main.js 中的vue配置项编写 router 配置项:

路由示例

import Vue from "vue";
import VueRouter from "vue-router"; //引入VueRouter
Vue.use(VueRouter) //安装路由
//创建router实例对象,去管理一组一组的路由规则
const router = new VueRouter({ //将路由对象实例化
  routes: [
    {
      path: "/about",
      component: () => import("../components/About"),
    },
    {
      path: "/home",
      component: () => import("../components/Home"),
    },
  ],
});
export default router; //暴露router

实现切换高亮样式
<router-link active-class="active" to="/about">About</router-link>
指定展示位置
<router-view></router-view>

P119 几个注意点

  1. 路由组件通常存放在views文件夹,一般组件通常存放在components文件夹。
  2. 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
  3. 每个组件都有自己的$route属性,里面存储着自己的路由信息。
  4. 整个应用只有一个 router,可以通过组件的$router属性获取到。

P120 多级路由

多级路由是为了实现子页面部件留存,然后父组件不销毁

配置路由规则,使用 children 配置项:

routes: [
  {
    path: "/about",
    component: About,
  },
  {
    path: "/home",
    component: Home,
    //通过children配置子级路由
    children: [
      {
        path: "news", //此处一定不要写:/news
        component: News,
      },
      {
        path: "message", //此处一定不要写:/message
        component: Message,
      },
    ],
  },
];

跳转(要写完整路径):<router-link to="/home/news">News</router-link>

P121 路由的 query 参数

  1. 传递参数
<!-- 跳转并携带query参数,to的字符串写法 -->
<router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link>

<!-- 跳转并携带query参数,to的对象写法 -->
<router-link
  :to="{
    path: '/home/message/detail',
    query: {
      id: 666,
      title: '你好',
    },
  }">跳转</router-link>

接收参数:$route.query.id, $route.query.title;

P122 命名路由

使用场景:简化路由的跳转。

<!-- 1. 给路由命名: -->
router 中 children 数组对象的 name 配置项
<!--简化前,需要写完整的路径 -->
<router-link to="/demo/test/welcome">跳转</router-link>
<!--简化后,直接通过名字跳转 -->
<router-link :to="{ name: 'hello' }">跳转</router-link>
<!--简化写法配合传递query参数 -->
<router-link
  :to="{
    name: 'hello',
    query: {
      id: 666,
      title: '你好',
    },
  }">跳转</router-link>

P123 路由的 params 参数

配置路由,声明接收 params 参数

{
 path:'/home',
 component:Home,
 children:[
  {
   path:'news',
   component:News
  },
  {
   component:Message,
   children:[
    {
     name:'xiangqing',
     path:'detail/:id/:title', //使用占位符声明接收params参数
     component:Detail
    }
   ]
  }
 ]
}

传递参数

<!-- 跳转并携带params参数,to的字符串写法 -->
<router-link :to="/home/message/detail/666/你好">跳转</router-link>
<!-- 跳转并携带params参数,to的对象写法 -->
<router-link
  :to="{
    name: 'xiangqing',
    params: {
      id: 666,
      title: '你好',
    },
  }">跳转</router-link>

特别注意:路由携带 params 参数时,若使用 to 的对象写法,则不能使用 path 配置项,必须使用 name 配置!

接收参数:$route.params.id; $route.params.title;

P124 路由的 props 配置

使用场景:路由组件传递数据的一种方式

{
 name:'xiangqing',
 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
  }
 }
}

P125 的 replace 属性

使用场景:控制路由跳转 浏览器历史记录的模式为 替换

浏览器的历史记录有两种写入方式:
分别为pushreplacepush是追加历史记录,replace是替换当前记录。
路由跳转时候默认为push

开启replace模式:<router-link replace .......>News</router-link>

P126 编程式路由导航

使用场景:用 js代码语法糖 进行路由跳转,让路由跳转更加灵活

相关 API:

this.$router.push({
  name: "detail",
  params: {
    id: xxx,
    title: xxx,
  },
}); //相当于点击路由链接(可以返回到当前路由界面)
this.$router.replace({
  name: "detail",
  params: {
    id: xxx,
    title: xxx,
  },
}); //用新路由替换当前路由(不可以返回到当前路由界面)
this.$router.forward(); //前进
this.$router.back(); //后退
this.$router.go(1); //可前进也可后退,请求下一个记录路由

P127 缓存路由组件

使用场景:让不展示的路由组件保持挂载,不被销毁。
使用方式如下:

<keep-alive include="Detail">
    <router-view></router-view>
</keep-alive>

P128 两个新的生命周期钩子

使用场景:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。

activated路由组件被激活时触发。 deactivated路由组件失活时触发。

P133 路由器的两种工作模式

使用场景:总体来说 history 模式 使用较多,因为较为美观。

对于一个 url 来说,什么是 hash值 ?—— #及其后面的内容就是hash值
hash 值不会包含在 HTTP 请求中,即:hash 值不会带给服务器。

hash 模式

  1. 地址中永远带着#号,不美观 。
  2. 若以后将地址通过第三方手机 app 分享,若 app 校验严格,则地址会被标记为不合法
  3. 兼容性较好。

history 模式

  1. 地址干净,美观。
  2. 兼容性和 hash 模式相比略差。
  3. 应用部署上线时需要后端人员支持,解决刷新页面服务端 404 的问题。

Vue UI 组件库

移动端常用 UI 组件库

  1. Vant https://youzan.github.io/vant
  2. Cube UI https://didi.github.io/cube-ui
  3. Mint UI http://mint-ui.github.io
    PC 端常用 UI 组件库
  4. Element UI https://element.eleme.cn
  5. IView UI https://www.iviewui.co

混入

Vue 插件是一个包含 install 方法的对象
通过 install 方法给 VueVue实例 添加方法, 定义全局指令等