vue 2&3 笔记
Date: 2023-03-12 17:28:46
视频链接:vuejs从入门到精通
ps:我第一遍笔记不太满意,重写一遍 😗
练习项目地址
前置知识
- HTML5
- CSS3
- JS(ES6及以上)
JS 是最重要的。直接关系到你是否真正理解学透,虽然开发不用撸源代码,但更懂一些,对于排除错误极有益处。
不建议直接脚手架学习项目,我看第一遍的时候,是直接找脚手架安装了,导致我对 vue 的一些基本知识就不知道,就像创建 vue 实例的函数式方法,一直没看太懂,直到第二遍从头看,我才知道可以传个配置对象。再实现vue实例对象
这篇课程学习的版本为 vue@2.7 之前的版本,vue@2 将在 2023/12/31 后不再维护,建议学习 vue@3 版本
vue2
P1 课程介绍
讲的挺好的,b站最好的 vue 教程,值得一看
P2~4 Vue简介
vue官网,只不过现在更新到 vue3 了,vue2 要在文档里面找
Vue官方使用指
不会在官网里查就行
搭建 Vue 开发环境
vue.js \\ 开发版本
vue.min.js \\ 生产版本,已经压缩
在 chrome 商店搜索 vue 插件安装即可
关闭开发版本提示
❗ 开始学习时,先不用脚手架接触框架 🌠
P5~10 vue 核心
Hello 小案例 ---P5🎈
// html代码
<div id="root">
<h1>{{name}}</h1>
</div>
//创建Vue实例对象,里面传个对象
new Vue({
el: "#root", //el用于指定当前Vue实例为哪个容器服务,值通常为css选择器字符串。
data: {
//data中用于存储数据,数据供el所指定的容器去使用,值我们暂时先写成一个对象。
name: "hello,world!",
},
});
将 vue 实例对象
挂载在DOM节点上
上次更新: 2023-03-31 19:55:14
分析 Hello 小案例 ---P6
JS表达式 和 JS语句 的区别? 表达式能接到 val,语句不能
Vue 创建实例对象后,才能工作
,里面需要传一个配置对象
- DOM 模板语法符合
HTML 规范
,但增加了一些特殊语法 - DOM 模板里的代码被称为
Vue模板
- 容器和vue实例要一一对应
{{xxx}}
里的 xxx 为 js表达式,xxx
的this
为vue 实例对象
- 一旦
data
改变,模板中用到的地方也会自动更新
模板语法 ---P7
- 插值语法
- 功能: 用于解析标签体内容。
- 写法:
{{xxx}}
,xxx
是js表达式,且可以直接读取到data中
的所有属性。 - 注:模板语法的
this
为 vm,并未浏览器对象BOM
- 指令语法
- 功能: 用于解析标签(包括:标签属性、标签体内- 容、绑定事件.....)。
- 举例:
v-bind:href="xxx"
或 简写为:href="xxx"
,xxx 同样要写 js表达式,且可以直接读取到data中的所有属性。 - 备注: Vue中有很多的指令,且形式都是:
v-?
,此处我们只是拿v-bind
举个例子。
<div id="root">
<h3>hello,{{name}}</h3>
<a :href="profile.href" :fun="profile.note.toUpperCase()">点我跳转{{profile.xxx}}</a>
</div>
new Vue({
el: "#root",
data: {
name: "gavin",
profile: {
href: "https://www.cnblogs.com/isgavin/",
xxx: "技术博客",
note:"justNote"
}
},
});
数据绑定 ---P8
单向数据绑定,data => DOM
,
v-bind:href ="xxx" 或简写为 :href
xxx 会被作为js表达式解析
双向数据绑定 data <=> DOM
,
v-mode:value="xxx" 或简写为 v-model="xxx"
el 与 data 的两种语法 ---P9
const v= new Vue({
// el:'#root' el第一种写法
})
v.$mount('#root') //el第二种写法
new Vue({
// data:{
// name: 'AAA' //data 第一种写法
// }
data: function(){ //data 第二种写法
return {
name: 'AAA'
}
}
}).$mount('#root')
el函数方法
的写法能灵活一些,都一样,就赋值挂载而已。
为什么不能用 data 的第一种写法?
组件化后,不同组件共用了同一个内存地址,componentA 修改的内容,同样对 componentB 产生了影响
如果我们采用函数的形式,则不会出现这种情况(函数返回的对象内存地址并不相同)
理解 MVVM ---P10
MVVM 模型

M:模型(Model):对应 data 中的数据
V:视图(View) :DOM 模板
VM:视图模型(ViewModel) : Vue 实例对象
P11 Object.defintProperty
vue2 实现响应式数据的方式
P12 理解数据代理
P13 Vue中的数据代理
有时间再看
P14~17 事件处理
事件的基本使用 P14
- 使用
v-on:xxx
或@xxx
绑定事件,其中xxx
是事件名; - 事件的回调需要配置在
methods对象
中,最终会在vm
上; - methods 中配置的函数,不要用箭头函数!否则this就不是 vm 了;
- methods 中配置的函数,都是被 Vue 所管理的函数,this的指向是 vm 或 vc
@click="demo"
和@click="demo($event)"
效果一致,但后者可以传参并可以将event对象
带过去进行处理;
上次更新: 2023-04-01 12:00:37
事件修饰符 P15
Vue中的事件修饰符:@click.事件修饰符="handleClick"
prevent
:阻止默认事件(常用);event.preventDefault()
stop
:阻止事件冒泡(常用);event.stopPropagation()
once
:事件只触发一次(常用);capture
:使用事件的捕获模式;self
:只有event.target
是当前操作的元素时才触发事件;也可以阻止冒泡
passive
:事件的默认行为立即执行,无需等待事件回调执行完毕;
@scroll 滚动条滚动,@wheel 鼠标滚动移动,有必要复习一下浏览器事件
什么是事件冒泡?
<ul @click="handleClick">
<li @click="handleClick">AAA</li>
</ul>
点击子元素,同样会触发父级元素的事件方法。被称为事件冒泡
按键修饰符 P16
-
Vue中常用的按键别名:
@keyup.enter="handleKeycode"
- 回车 => enter
- 删除 => delete (捕获“删除”和“退格”键)
- 退出 => esc
- 空格 => space
- 换行 => tab (特殊,必须配合keydown去使用)
- 上 => up
- 下 => down
- 左 => left
- 右 => right
-
Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名)
按空格@keyup.caps-lock="handleCick"
-
系统修饰键(用法特殊):ctrl、alt、shift、meta
- (1) 配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
- (2) 配合keydown使用:正常触发事件。
@keydown.ctrl="handleKeycode"
-
也可以使用keyCode去指定具体的按键(不推荐)(因为不同键盘的编码不一致,web 标准可能废弃)
-
Vue.config.keyCodes.自定义键名 = 键码
,可以去定制按键别名(没啥区别,常用够用)
事件总结 P17
- 事件修饰符可以连着写
@click.once.prevent="handleClick"
- 系统修饰符也可以连着写
@keyup.ctrl.y="handleClick"
只有ctrl+y 同时按可以触发事件
P18~25 计算属性 与 监视属性
姓名案例 P18🎈
引出计算属性
计算属性 computed P19
使用场景 要显示的数据不存在,要通过依赖 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]
}
}
}
计算属性的数据会被复用,效率更高
计算属性简写方式 P20
只适用 get()
时计算属性可简写
computed:{
fullName(){
return this.firstName+'-'+this.lastName
}
}
天气案例 P21🎈
引出监视属性的使用
监视属性 P22
使用场景
当 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)
}
})
深度监视 P23
根据数据结构和需求,选择是否添加深度监视
data:{
numbers:{
a:1,
b:2
}
},
watch:{
numbers:{
deep:true, //深度监视 numbers 和其子属性 改变
handler(newValue,oldValue){
console.log('info被修改了',newValue,oldValue)
}
}
}
监视的简写形式 P24
配置项中只使用 handler
时使用,开发时能用简写用简写
// 简写形式
watch:{
isHot(newValue,oldValue){
console.log('info被修改了',newValue,oldValue)
}
}
vm.$watch('isHot',function(newValue,oldValue){ //不能写成箭头函数,会造成 this 指向问题
console.log('info被修改了',newValue,oldValue)
}
)
watch 对比 computed P25
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",
};
},
// HTML
<button class="basic" :class="xxx" @click="handleClass">切换 class</button>
元素上添加 :class='xxx'
data 上为
- 字符串:
xxx:'classA'
- 对象:
xxx:{classA:isA, classB: isB}
- 数组:
xxx:['classA','classB']
实现 style 绑定
data() {
return {
styleObj: {
color: "blue",
fontSize: "50px",
backgroundColor: "yellow",
}
};
},
// HTML
<div :style="styleObj">style 修改</div>
P28~32 条件渲染和列表渲染
条件渲染 ---P28
// 这一堆不能打断
<h3 v-if="n===1">AAA</h3>
<h3 v-else-if="n===2">BBB</h3>
<h3 v-else-if="n===3">CCC</h3>
<h3 v-else>noneValue</h3>
<template v-if="val"></template>
只能配合 v-if
使用
面试题:比较 v-if
与 v-show
的区别?
面试题链接,我写的还行
上次更新: 2023-04-02 11:02:32
列表渲染 ---P29
v-for
指令:
- 用于展示列表数据
- 语法:
v-for="(item, index) in xxx" :key="yyy"
- 可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
key 作用和原理 ---P30
key 是 diff 算法标识符,用于提高DOM效率
列表过滤 ---P31
对要显示的数据进行特定格式化后再显示
注意: 并没有改变原本的数据, 是产生新的对应的数据
列表排序 ---P32
列表排序功能
33~38 Vue监视数据的原理
更新的一个问题 ---P33
更新后发现数据没有更新,原因是 difineProperty 的特性
P34 Vue监视数据的原理-对象
P35 Vue.set() 方法
P36 Vue监视数据的原理-数组
P37 vue 监视数据-总结
P38 收集表单数据 🎈
写一份原生的
,写一份 element-ui
的。
收集表单数据:
- 若
<input type="text"/>
,则v-model收集的是value值,用户输入的就是value值。 - 若
<input type="radio"/>
,则v-model收集的是value值,且要给标签配置value值。 - 若
<input type="checkbox"/>
- 没有配置 input 的 value 属性,那么收集的就是 checked(勾选 or 未勾选,是布尔值)
- 配置 input 的 value 属性:
(1) v-model的初始值是非数组,那么收集的就是 checked(勾选 or 未勾选,是布尔值)
(2) v-model 的初始值是数组,那么收集的的就是 value 组成的数组
- 备注:v-model 的三个修饰符:
lazy
:失去焦点再收集数据number
:输入字符串转为有效的数字trim
:输入首尾空格过滤
v-module 配合不同的标签元素
P39 过滤器
vue@3 中已经移除过滤器
P40~47 vue 指令系统
vue 的内置指令和自定义指令
v-text 指令 ---P40
更新元素的 文本内容
,不常用
v-html 指令 ---P41
介绍 cookie
cookie 是一种本地存储技术,实现持久化登录的一种方式。
工作原理:服务器发送到用户浏览器并保存在本地的一小段文本信息,每次浏览器向同一服务器再发送请求时,都会携带这个 cookie 信息。
有可能被盗取,添加 httponly 属性,防止 XSS 攻击
。
v-html指令:
作用:向指定节点中渲染包含html结构的内容。
与插值语法的区别:
(1).v-html 会替换掉节点中所有的内容,{{xx}}
则不会。
(2).v-html 可以识别 html结构。 <div v-html="HtmlData"></div>
严重注意:v-html 有安全性问题!!!!
(1).在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。
(2).一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!
v-cloak 指令 ---P42
解决 js 阻塞渲染,防止闪现, 与 css 配合使用:
- 先在 元素上设置
<h3 v-cloak>{{data}}</h3>
- 元素标签设置
[v-cloak]{display: none}
当 vue 加载完毕后,会自动移除该属性。
v-once 指令 ---P43
v-once
所在节点在初次动态渲染后,就视为静态内容了 <h3 v-once>{{data}}</h3>
以后数据的改变不会引起 v-once所在结构
的更新,可以用于优化性能。data 变化不会在页面上显示了
v-pre 指令 ---P44
跳过其所在节点的编译过程,可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译速度
自定义指令-函数式 ---P45
将 操作 DOM 的代码
封装成一个函数指令
注册自定义指令
export default {
name: 'app',
directives: {
upcase(el, binding) {
el.innerHTML = binding.value.toupperCase()
}
}
}
- el 为真实的 DOM 元素,
- binding 为当前DOM的配置对象
使用指令 v-upcase='xxx
执行时机
指令与元素成功绑定时 bind
,页面模板重新解析时再次执行 update
自定义指令-对象式 ---P46
export default {
name: 'app',
directives: {
upcase: {
bind (el, binding) {
el.innerHTML = binding.value.toupperCase()
},
inserted (el, binding) {
el.focus()
},
update (el, binding) {
el.innerHTML = binding.value.toupperCase()
}
}
}
}
自定义指令-总结 ---P47
- 书写的时候,如果指令名较长,可以用 - 连接单词。
- 指令函数中的 this 为 BOM 对象
- 定义全局指令:所有组件都可使用
Vue.directive(指令名,配置对象)
或Vue.directive(指令名,回调函数)
,使用插件安装全局指令
P48~52 Vue 生命周期
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已经生成,所以可能会造成闪屏,用户体验不佳。
P53~60 关于单文件组件
对组件的理解 ---P53
一种新的网页开发方式,将相关的 HTML,js,css 部分放在一起组成的单文件组件。
Vue中使用组件的三大步骤:
>一、 定义组件(创建组件)
- 使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别;
- 区别如下:
- el不要写,为什么?
最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器。 - data必须写成函数,为什么?
避免组件被复用时,数据存在引用关系。 - 备注:使用 template 可以配置组件结构。
- el不要写,为什么?
>二、 注册组件
- 局部注册:靠new Vue的时候传入components选项
- 全局注册:靠Vue.component('组件名',组件)
>三、 使用组件(写组件标签) <school></school>
非单文件组件 ---P54
传统 HTML 网页
组件的几个注意点 ---P55
-
关于组件名:
- 一个单词组成:首字母大写
第一种写法(首字母小写):school
第二种写法(首字母大写):School - 多个单词组成:大驼峰命名
第一种写法(kebab-case命名):my-school
第二种写法(CamelCase命名):MySchool (需要Vue脚手架支持) - 备注:
(1).组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行。所以首字母大写
(2).可以使用name配置项
指定组件在开发者工具中呈现的名字。
- 一个单词组成:首字母大写
-
关于组件标签:
第一种写法:<school></school>
第二种写法:<school/>
备注:不用使用脚手架时,<school/>
会导致后续组件不能渲染。 -
一个简写方式:
const school = Vue.extend(options) 可简写为:const school = options
组件的嵌套 ---P56
引入其他组件
import Home from '../components/Home.vue'
- 在配置对象里注册
export default {
component: {Home}
}
- 在 template 中使用
<Home/>
VueComponent 构造函数 ---P57
组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend
生成
Vue解析到自定义组件标签Demo/>
时,会帮我们创建组件的实例对象,
即Vue帮我们执行的:new VueComponent(options)
。
特别注意:每次调用Vue.extend
,返回的都是一个全新的VueComponent
!
Vue实例与组件实例 ---P58
关于this
指向:
- 组件配置中:
vc
data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【VueComponent实例对象】。 - new Vue(options)配置中:
vm
data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】。
VueComponent 的实例对象,以后简称vc(也可称之为:组件实例对象)。Vue的实例对象,以后简称vm。
一个重要的内置关系 P59
一个重要的内置关系:VueComponent.prototype.__proto__ === Vue.prototype
为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。
P60 单文件组件
一个.vue 文件的组成(3 个部分)
<template> // 页面模板
</template>
<script> // 模块对象
export default {
data() {
return {}
},
methods: {},
computed: {},
components: {}
}
</script>
<style> // 样式定义
</style>
P61~64 Vue 脚手架
创建Vue脚手架 ---P61
使用 vue 脚手架工具 Vue CLI
Vue 脚手架隐藏了所有 webpack 相关的配置,若想查看具体的 webpakc 配置,
请执行:vue inspect > output.js
,之所以隐藏,就是不想让你动里面的东西,修改在vue.config.js
中设置就可以了
Date:2023-04-06 11:14:10
以上已过时,现在的脚手架工具为 vite
脚手架文件结构 ---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:包版本控制文件
关于不同版本的Vue ---P63
vue.js
与vue.runtime.xxx.js
的区别:
vue.js
是完整版的 Vue,包含:核心功能 + 模板解析器。
vue.runtime.xxx.js
是运行版的 Vue,只包含:核心功能;没有模板解析器。- 因为
vue.runtime.xxx.js
没有模板解析器,所以不能使用 template 这个配置项,需要使用 render 函数接收到的 createElement 函数去指定具体内容。
修改默认配置 ---P64
P65 ref 🎆
使用场景:用于给节点打标识
<h3 v-text="text" ref="title"></h3>
读取 this.$refs.xxxxxx
跟 react 的 string ref 写法一样。
也可以 写在组件标签上拿到组件的实例对象
P66 props 属性 🎆
用于父组件 => 子组件传递数据
传递数据
<Demo name="xxx" :age="18+1"/>
接收数据
在子组件中
props: ["name", "age", "sex"],
// 限制类型
props:{
name:String,
age:Number,
sex:String
}
// 限制类型和其他配置
props: {
name: {
type: String,
required: true,
},
age: {
type: Number,
},
sex: {
type: String,
default: "男",
},
},
注意 ❗
props
是只读的
,Vue 底层会监测你对 props 的修改,如果进行了修改,就会发出警告。
若业务需求确实需要修改,那么请复制 props 的内容到 data 中一份,然后去修改 data 中的数据
P67~69 可复用性 & 组合
mixin 混入 ---P67
使用场景: 复用 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)
scoped 样式 ---P69
使用场景:让样式在局部生效,防止冲突。
写法:<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()
该方法会清空存储中的所有数据。
TodoList-实现本地存储 ---P79🎈
P80~82 组件的自定义事件
使用场景: 一种 子组件 ===> 父组件
通信的方式,react 里也有
子组件 ===> 父组件 通信的方式 ---P80
> 一、 向子组件 传一个 props 的方法
,然后子组件触发此方法,将数据带到父组件
- 父组件中
<Demo :getSonData="xxx"
- 子组件中 接收props后
@click="getSonData(data)"
> 二、 在子组件标签上绑定一个方法,然后在子组件里触发自定义方法,将数据带回来,在组件销毁前解绑
- 父组件中
<Demo @getSonData="xxx">
- 子组件中
this.$emit("getSonData", this.data);
> 三、 通过 ref 直接拿到子组件的实例对象
- 父组件中
<Demo ref="vc"\>
- 直接拿到子组件的实例对象
this.$refs.vc
> 四、 通过 ref 在生命周期里使用 $on方法钩子
绑定自定义事件
- 父组件中
<Demo ref="vc">
然后拿到子组件绑定自定义事件this.$refs.vc.$on("getSonData", this.xxx);
- 子组件中
this.$emit("getSonData", this.data);
自定义事件-解绑 ---P81
解绑一个 this.$off('111')
解绑多个 this.$off(['111','222'])
解绑全部 this.$off()
一般在 beforeDestroy()
生命周期钩子里进行解绑
自定义事件-总结 ---P82
在方法四中
this.$refs.vc.$on("getSonData", console.log(this));
谁触发,this 在谁手里,此时 this 为 子组件实例对象
<Demo @click="show"></Demo>
click
此时为自定义事件,将其 + @click.native
变为原生事件
自定义事件-TodoList案例 ---P83🎈
P84~85 全局事件总线
理解 vm 与 vc 的关系 ---P84

Object 对象原型链

实现全局事件总线 ---P85
使用场景: 不相关组件间的通信方式
实现原理
在Vue原型对象上, 挂了一个vue实例对象
,俗称套娃
使用方式
先创建事件总线对象,注意时机,在创建前在 vm 上挂载一个 vc
new Vue({
beforeCreate () { // 尽量早的执行挂载全局事件总线对象的操作
Vue.prototype.$bus = this //安装全局事件总线,$bus
},
})
A 组件提供数据:this.$bus.$emit('getData',数据)
B 组件接收数据:
this.$bus.$on('getData',方法(数据))
记得解绑当前组件用到的自定义事件 this.$bus.$off('getData')
全局事件总线-TodoList案例 ---P86🎈
P87 消息订阅与发布-pubsub
使用场景: 不相关组件间的通信方式
Vue 不常用,react 经常使用,这种方式的使用和原理与全局事件总线很相似
如何使用
它包含以下操作:
(1) 订阅消息 --对应绑定事件监听
(2) 发布消息 --分发事件
(3) 取消消息订阅 --解绑事件监听
安装包 npm i pubsub-js
在 main.js
全局引入: import PubSub from 'pubsub-js'
具体使用
// A组件提供数据:
PubSub.publish('getData',数据)
// 接收数据:
token = PubSub.subscribe('geData',function(消息名,data))
PubSub.unsubscribe(token)
vue3 ,不能传对象,还是要用 mitt,也没必要,
全局事件总线-TodoList案例 ---P88🎈
编辑-TodoList案例 ---P89🎈
P90 nextTick
使用场景 当改变数据后,要基于更新后的新 DOM 进行某些操作
要在 nextTick
所指定的回调函数中执行。在下一次 DOM 更新结束后执行其指定的回调
watch: {
list () {
this.$nextTick(() => { // 在下一次 DOM 更新结束后执行其指定的回调
console.log("DOM 更新了")
})
}
}
P91~95 Vue 动画
使用场景: 在插入、更新或移除 DOM 元素时,在合适的时候给元素添加样式类名
理解: 操作 css 的 trasition 或 animation,vue 会给目标元素添加/移除特定的 class
过渡与动画 ---P91
<transition name="name" appear> //解析后脱掉了
<h3>这是动画</h3>
</transition>
appear
为初始化渲染
.name-enter-active{animation:ianime 0.5s linear;}
.name-leave-active{animation:ianime 0.5s linear;}
@keyframes ianime{}
过渡效果 ---P92
元素进入的样式:
1. name-enter:进入的起点
2. name-enter-active:进入过程中
3. name-enter-to:进入的终点
元素离开的样式:
1. name-leave:离开的起点
2. name-leave-active:离开过程中
3. name-leave-to:离开的终点
使用<transition>
包裹要过渡的元素,并配置 name
属性
多个元素过渡 ---P93
在多个元素外增加 <transition-group></transition-group>
若有多个元素需要过度,则需要使用:<transition-group>
,且每个元素都要指定key
值。
集成第三方动画库 ---P94
css动画库,Animate.css
代码示例
yarn add animate.css
- 然后在组件中引入
import "animate.css"
- 书写代码
<transition
appear
name="animate__animated animate__bounce"
enter-active-class="动画名"
> //解析后脱掉了
<h3>这是动画</h3>
</transition>
总结过渡与动画 ---P95
根据需求进行使用 官方文档
P96~97 配置代理
面试题:如何解决开发环境的跨域问题?
根本原因是浏览器同源策略的限制,实际上请求的接口数据已经收到,只不过被浏览器拦截了,解决方式有很多,主要是挂代理,因为 服务器 \(\Rightarrow\) 服务器没有此限制。
在 vue 项目中,官方脚手架已经内置了解决方案,主要是使用 nodejs开个本地服务器代理进行转发,稍微设置一下即可
vue 项目中常用的 Ajax 库, axios
axios 对 原生hdr请求 进行了一次封装,是我们能够更好的发送请求。
在实际开发过程中,一般会对 axios 进行二次封装,扔到 utils 文件夹里,目的是统一进行请求,响应拦截处理,简化请求发送代码
P98~100 搜索案例🎈
P101 vue-resource
hdr 请求库,官方推荐使用 axios
P102~104 插槽
slot 插槽 ---P102
使用场景: 父组件 => 子组件传递带数据结构不定的标签
插槽内容是在父组件中编译后, 再传递给子组件的。
分类
- 默认插槽
- 命名插槽
- 作用域插槽
使用方式
在父组件中<Demo>父组件向子组件放的结构</Demo>
在子组件中<slot></slot>
,为父组件中的结构占位
结构样式父组件和子组件都可以定义
具名插槽 ---P103
如果在父组件中写多个插槽,每个插槽必须命名
子组件中
<slot name="center">我是一些默认值,当使用者没有传递具体结构时,我会出现1</slot>
<slot name="footer">我是一些默认值,当使用者没有传递具体结构时,我会出现2</slot>
父组件中
<Demo title="美食" >
<video slot="center" controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video>
<template v-slot:footer>
<div class="foot">
<a href="http://www.atguigu.com">经典</a>
</div>
<h4>欢迎前来观影</h4>
</template>
</Demo>
作用域插槽 ---P104
使用场景
数据在组件的自身
,但根据数据生成的结构
需要组件的使用者
来决定。
slot 可将数据传递给父组件
如何使用
在子组件中:
<template>
<div>
<slot :datas="datas"></slot>
</div>
</template>
<script>
export default {
name: "Demo",
data() {
return {
datas: ["红色警戒", "穿越火线", "劲舞团", "超级玛丽"],
};
},
};
</script>
在父组件中
<Demo>
<template scope="scopeData">
<!-- 生成的是ul列表 -->
<ul>
<li v-for="g in scopeData.games" :key="g">{{g}}</li>
</ul>
</template>
</Demo>
<Demo>
<template scope="scopeData">
<!-- 生成的是ul列表 -->
<ol>
<li v-for="g in scopeData.games" :key="g">{{g}}</li>
</ol>
</template>
</Demo>
P105~116 Vuex
Vuex 简介 ---P105
最新版更名为 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 的配置对象
- 与一个组件(包含有共享数据)对应
求和案例 普通版🎈 ---P106
Vuex 工作原理图 ---P107

搭建 Vuex 环境 ---P108
-
安装 vuex
npm i vuex
-
main.js
中在创建 vm 时传入store
配置项
//引入store
import store from "./store";
//创建vm
new Vue({
el: "#app",
render: (h) => h(App),
store,
});
- 然后创建
src/store/index.js
文件
//引入Vue核心库
import Vue from "vue";
//引入Vuex
import Vuex from "vuex";
//应用Vuex插件
Vue.use(Vuex);
//准备actions对象——响应组件中用户的动作
const actions = {};
//准备mutations对象——修改state中的数据
const mutations = {};
//准备state对象——保存具体的数据
const state = {};
//创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state,
});
求和案例 vuex版🎈 ---P109
Vuex 开发者工具 ---P110
chrome商店vuex插件
getters 配置项 ---P111
概念:当 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
四个 map 方法的使用 ---P112-113
mapState 与 mapGetters
mapActions 与 mapMutations
如果此组件里使用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 使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。
多组件共享数据 ---P114
基本使用
初始化数据、配置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 只负责改变值。大可不必,顾客才不在乎厨师怎么做的饭。
vuex 模块化 namespace P115-116
使用场景:大型项目通用数据较多,为了让代码更好维护,让多种数据分类更加明确。
修改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框架)
P117~133 Vue 路由管理
🔥🔥🔥 ❗ 学习使用版本为 vue-router@3
2022/2/7 后,vue-router 默认版本为@4
,只能在 vue@3
中使用,vue-router@3
,只能在 vue@2
中使用
vue-router介绍 ---P117
vue 的一个插件库,专门用来实现SPA
的。
路由分类
后端路由:
- 理解:value 是 function, 用于处理客户端提交的请求。
- 工作过程:服务器接收到一个请求时, 根据请求路径找到匹配的函数来处理请求, 返回响应数据。
前端路由:
- 理解:value 是 component,用于展示页面内容。
- 工作过程:当浏览器的路径改变时, 对应的组件就会显示。
路由基本使用 ---P118
路由的基本使用
- 安装
vue-router
,命令:yarn add vue-router@3
- 引入并应用插件:
- 在
main.js
中的vue配置项
可编写router
配置项
- 编写
.active
,设置激活后样式
<router-link to="" active-class="active"></router-link>
制定组件呈现位置 <router-view></router-view>
编写使用路由的 3 步
-
定义路由组件,在
views
文件夹里定义xxx.vue
组件,这个文件夹里都是要展现的页面。在页面中实现切换高亮样式
<router-link active-class="active" to="/about">About</router-link>
-
注册路由,在 vueRouter 里统一注册,编写好路径
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-view></router-view>
几个注意点 ---P119
- 路由组件通常存放在
views
文件夹,
一般组件通常存放在components
文件夹。 - 通过切换,原有的路由组件默认是被销毁掉的,需要的时候再去挂载。
每个组件 vc
都有自己的$route
属性,里面存储着自己的路由信息
。
整个应用
只有一个router
,可以通过组件的$router
属性获取到。
多级路由 ---P120
多级路由是为了实现 子页面部件跳转,父组件不销毁
- 配置路由规则,使用
children 配置项
:
routes: [
{
path: "/home",
component: Home,
//通过children配置子级路由
children: [
{
path: "news", //此处一定不要写:/news,底层已经加了 /
component: News,
},
{
path: "message", //此处一定不要写:/message
component: Message,
},
],
},
];
- 跳转(要写完整路径):
<router-link to="/home/news">News</router-link>
路由的 query 参数 ---P121
可用于 父 => 子
传递参数,可以传递任意类型的数据。
- 在父组件传递参数
<!-- ?id=666&title=你好 为 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>
路由的 params 参数 ---P123
- 配置路由,声明接收
params
参数
{
name: "myname", //给路由命名
component: Home,
path: '/detail/:id/:title', //使用占位符声明接收params参数
}
- 传递参数
<!-- 跳转并携带 params参数,to的字符串写法 -->
<router-link :to="/detail/666/你好">跳转</router-link>
<!-- 跳转并携带params参数,to的对象写法 -->
<router-link
:to="{
name: 'myname',
params: {
id: 666,
title: '你好',
},
}">跳转</router-link>
特别注意: 路由携带 params 参数时,若使用 to 的对象写法,则不能使用 path 配置项,必须使用 name 配置!
- 接收参数:
$route.params.id; $route.params.title;
路由的 props 配置 ---P124
谁接收 data,在谁的路由对象中配置 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
}
}
}
router-link 的 replace属性 ---P125
使用场景
控制路由跳转,浏览器历史记录的模式为 替换
浏览器的历史记录有两种写入方式:分别为push
和replace
,
push
是追加历史记录,replace
是替换当前记录。路由跳转时候默认为push
开启replace
模式
<router-link replace :to="/home/news">toNews</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>
- 不加
include
属性时,所有的路由组件都会被缓存。 include
属性的值为要缓存的路由组件的名称。include=["A","B","C"]
属性的值可以是一个数组,数组中可以写多个要缓存的路由组件的名称。
两个新的生命周期钩子 ---P128
使用场景
路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
export default {
name: "A",
activated() { // 路由组件被激活时触发。
console.log("A组件被激活了");
},
deactivated() { // 路由组件被失活时触发。
console.log("A组件失活了");
},
}
路由守卫 ---P129~132
在 router.js
中配置路由守卫
回调函数中的 to
为跳转的路由对象,from
为当前的路由对象。
- 全局前置-路由守卫 ---P129
//全局前置路由守卫————初始化的时候被调用、每次路由切换之前被调用
router.beforeEach((to,from,next)=>{
console.log('前置路由守卫',to,from)
if(to.meta.isAuth){ //判断是否需要鉴权
if(localStorage.getItem('school')==='atguigu'){
next()
}else{
alert('名不对,无权限查看!')
}
}else{
next()
}
})
- 全局后置-路由守卫 ---P130
// 全局后置路由守卫————初始化的时候被调用、每次路由切换之后被调用
router.afterEach((to,from)=>{
console.log('后置路由守卫',to,from)
document.title = to.meta.title || '硅谷系统'
})
- 独享-路由守卫 ---P131
{
name:'xinwen',
path:'news',
component:News,
meta:{isAuth:true,title:'新闻'},
beforeEnter: (to, from, next) => { //独享路由守卫,就只对某个路由进行限制,无后置路由守卫
console.log('独享路由守卫',to,from)
if(to.meta.isAuth){ //判断是否需要鉴权
if(localStorage.getItem('school')==='atguigu'){
next()
}else{
alert('学校名不对,无权限查看!')
}
}else{
next()
}
}
},
- 组件内-路由守卫 ---P132
通过路由规则进入
通过路由规则离开
export default {
//通过路由规则,进入该组件时被调用
beforeRouteEnter (to, from, next) {
console.log('About--beforeRouteEnter',to,from)
if(to.meta.isAuth){ //判断是否需要鉴权
if(localStorage.getItem('school')==='atguigu'){
next()
}else{
alert('学校名不对,无权限查看!')
}
}else{
next()
}
},
//通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {
console.log('About--beforeRouteLeave',to,from)
next()
}
}
history 模式和 hash 模式 ---P133
使用场景:
总体来说 history 模式
使用较多,因为较为美观。
对于一个 url 来说,什么是 hash 值?
#及其后面的内容
就是 hash值
。hash 值不会包含在 HTTP 请求中,即 hash 值不会带给服务器。
hash 模式 对比 history 模式
hash 模式 | history 模式 |
---|---|
地址中永远带着#号,不美观。 | 地址干净,美观。 |
兼容性较好。 | 兼容性和 hash 模式相比略差 |
hash 若以后将地址通过第三方手机 app 分享,若 app 校验严格,则地址会被标记为不合法。
history 应用部署上线时需要后端人员支持,解决刷新页面服务端 404 的问题。配一下 nginx 即可。前端也要做个 404 界面。
P134~P135 element-ui 组件库
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
终于看完了🎉🎉🎉
vue2.7 支持 compositin api,所以我要将本文中的案例,全部用其实现一遍
vue3
pnia 学习
可以认为 pinia 就是一个特殊的组件,
state => data,
action => function,
getter => computed,
再将其换成组合式 api 写法,就跟写普通组件的感觉是一样的
- 先定义 仓库,在 main.js 中引入
- 创建 stores 文件夹,数据模块分别进行存放,
- 在组件中,引入store文件,通过方法创建 store 对象,然后将 state 和 getter 解构赋值并响应式,action 解构赋值即可
vue-router@4.x
路由传参 props
在路由表中设置参数,在组件 props 中接收
本文作者:Gavin's blog
本文链接:https://www.cnblogs.com/isgavin/p/17266867.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。