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 事件处理
绑定监听
- v-on:xxx="fun"
-
- @xxx="fun"
-
- @xxx="fun(参数)"
-
- 默认事件形参: event
- 隐含属性对象:
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
使用场景
当 data 或 computed 发生变化时,要进行一些操作。
来监视指定的属性计算属性,属性变化时, 回调函数自动调用, 在函数内部进行计算
如何使用
通过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:{
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
- 如果需要频繁切换 v-show 较好
- 当条件不成立时, v-if 的所有子节点不会解析(项目中使用)
列表显示指令
遍历数组: v-for / index
遍历对象: v-for / ke
filter 过滤器
对要显示的数据进行特定格式化后再显示
2. 注意: 并没有改变原本的数据, 是产生新的对应的数据
内置指令与自定义指令
常用内置指令
- v-text : 更新元素的 textContent
- v-html : 更新元素的 innerHTML
- v-if : 如果为 true, 当前标签才会输出到页面
- v-else: 如果为 false, 当前标签才会输出到页面
- v-show : 通过控制 display 样式来控制显示/隐藏
- v-for : 遍历数组/对象
- v-on : 绑定事件监听, 一般简写为@
- v-bind : 绑定解析表达式, 可以省略 v-bind
- v-model : 双向数据绑定
- 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还在内存中,没有变成真实 DOMmounted()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>
基本使用
- 引入组件
import xxx from '/src/components/xxx' - 映射成标签 ``
- 使用组件标签
<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
vue.js与vue.runtime.xxx.js的区别:
vue.js是完整版的 Vue,包含:核心功能 + 模板解析器。
vue.runtime.xxx.js是运行版的 Vue,只包含:核心功能;没有模板解析器。- 因为
vue.runtime.xxx.js没有模板解析器,所以不能使用 template 这个配置项,需要使用 render 函数接收到的 createElement 函数去指定具体内容。
P65~66 ref 与 props 属性
- ref
使用场景:用于给节点打标识
读取方式:this.$refs.xxxxxx
- props
使用场景: 用于父组件 => 子组件传递数据
传递数据
<Demo name="xxx"/>
接收数据
-
只接收名称
props: ['name', 'age', 'setName'] -
限制类型
props: { name: String, age: Number, setNmae: Function } -
指定名称/类型/必要性/默认值
props: { name: {type: String, required: true, default:xxx}, }
注❗: props 是只读的,Vue 底层会监测你对 props 的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制 props 的内容到 data 中一份,然后去修改 data 中的数据
P67 mixin 混入
使用场景: 复用 vm 里所写的配置
如何使用
-
in
src/createmixin.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 组件化编码开发流程(通用思路)
- 实现静态组件:抽取组件,使用组件实现静态页面效果
- 展示动态数据:
数据的类型、名称是什么
数据保存在哪个组件?- 一个组件在用:放在组件自身即可。
- 父子组件在用:放在他们共同的父组件上(状态提升)
- 交互——从绑定事件监听开始,处理交互逻辑
P78 webStorage
- 存储内容大小一般支持 5MB 左右(不同浏览器可能还不一样)
- 浏览器端通过
Window.sessionStorage和Window.localStorage属性来实现本地存储机制。
相关 API
xxxxxStorage.setItem('key', 'value');该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。xxxxxStorage.getItem('person');该方法接受一个键名作为参数,返回键名对应的值。xxxxxStorage.removeItem('key');该方法接受一个键名作为参数,并把该键名从存储中删除。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) 取消消息订阅 --解绑事件监听
使用流程
-
安装 pubsub:
npm i pubsub-js -
引入:
import pubsub from 'pubsub-js' -
接收数据:A 组件想接收数据,则在 A 组件中订阅消息,订阅的回调留在 A 组件自身。
methods(){ demo(data){......} } ...... mounted() { this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息 } -
提供数据:
pubsub.publish('xxx',数据) -
最好在 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
-
准备好样式:
元素进入的样式:
- 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`值。
面试题:如何解决开发环境的跨域问题?
根本原因是浏览器同源策略的限制,实际上请求的接口数据已经收到,只不过被浏览器拦截了,解决方式有很多,主要是挂代理,因为 服务器 \(\Rightarrow\) 服务器没有此限制。
在 vue 项目中,官方脚手架已经内置了解决方案,主要是使用 nodejs开个本地服务器代理进行转发,稍微设置一下即可
vue 项目中常用的 Ajax 库, axios
axios 对 原生hdr请求 进行了一次封装,是我们能够更好的发送请求。
在实际开发过程中,一般会对 axios 进行二次封装,扔到 utils 文件夹里,目的是统一进行请求,响应拦截处理,简化请求发送代码
P102~104 slot 插槽
使用场景: 父组件 => 子组件传递带数据结构不定的标签
插槽内容是在父组件中编译后, 再传递给子组件的。
分类
- 默认插槽
- 命名插槽
- 作用域插槽
使用方式
<!-- 父组件中: -->
<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- {{g}}
{{g}}
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
- 包含多个 module
- 一个 module 是一个 store 的配置对象
- 与一个组件(包含有共享数据)对应
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 步
- 定义路由组件
在views文件夹里定义xxx.vue组件,这个文件夹里都是要展现的页面。 - 注册路由
在 vueRouter 里统一注册,编写好路径 - 使用路由
在页面里引入使用。
路由的基本使用
适用于 vue@3
- 安装
vue-router,命令:yarn add vue-router@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 几个注意点
- 路由组件通常存放在
views文件夹,一般组件通常存放在components文件夹。 - 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
- 每个组件都有自己的
$route属性,里面存储着自己的路由信息。 - 整个应用只有一个
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 参数
- 传递参数
<!-- 跳转并携带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 属性
使用场景:控制路由跳转 浏览器历史记录的模式为 替换
浏览器的历史记录有两种写入方式:
分别为push和replace,push是追加历史记录,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 模式
- 地址中永远带着#号,不美观 。
- 若以后将地址通过第三方手机 app 分享,若 app 校验严格,则地址会被标记为不合法
- 兼容性较好。
history 模式
- 地址干净,美观。
- 兼容性和 hash 模式相比略差。
- 应用部署上线时需要后端人员支持,解决刷新页面服务端 404 的问题。
Vue UI 组件库
移动端常用 UI 组件库
- Vant https://youzan.github.io/vant
- Cube UI https://didi.github.io/cube-ui
- Mint UI http://mint-ui.github.io
PC 端常用 UI 组件库 - Element UI https://element.eleme.cn
- IView UI https://www.iviewui.co
混入
Vue 插件是一个包含 install 方法的对象
通过 install 方法给 Vue 或 Vue实例 添加方法, 定义全局指令等