# Vue-demo
**Repository Path**: CandyPop/vue-demo
## Basic Information
- **Project Name**: Vue-demo
- **Description**: 对vue2-3的学习笔记
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2022-03-20
- **Last Updated**: 2022-12-11
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
#### Vue 学习笔记
https://cn.vuejs.org/
安装devTools,可以使用谷歌应用商店,下载,也可以在本`asset`文件夹下下载
这里以第一种安装的方式,也就是直接往html界面引入`Vue.js`文件
`Vue.js`和`Vue.min.js`前者用于开发模式,会有比较多的错误提示,因此体积也比较大,后者用于生产环境,体积压缩过。

去除使用`Vue.js`引入后,产生的浏览器的提示

也就是去除这个提示。

```javascript
```
##### 初识Vue
* 想让Vue工作,必须创建一个Vue,需要创建一个配置对象
* root容器里的代码已然符合html规范,只不过混入了一些混合的Vue语法
* root容器里面的代码被称为【Vue模版】
* Vue的渲染格式`{{}}`也被称为**插值语法**,里面可以写表达式,可以是数字循环,也可以写方法调用。
##### 模版语法
```javascript
v-bind 可以指定属性绑定,将后面的值看做是表达式,从data从取值
```
##### 双向数据绑定
```javascript
v-bind只能影响单方面绑定,即data->元素的绑定
v-model可以支持双向数据绑定,即在支持data->元素的绑定,元素的值改变,也就是value改变也会影响data
单向数据绑定
双向数据绑定
```
但是`v-model`只能用于**表单元素**,其他使用都会报错。
由于v-model只会收集value元素,所以以下还可以这样简写
```
v-model:value ===> v-model
单向数据绑定
双向数据绑定
```
##### data 与 el 的2种写法
* el有2种写法
* new Vue的时候配置el属性
* 县创建Vue实例,随后再通过实例对象.$mount('#root')来挂载容器
* data有2种写法
* 函数式
* 对象式
* 两种写法都可以,当运用组件的时候,data必须使用函数式,不然会报错
* 一个重要的选择
* 由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue的实例了。
##### mvvm
* 模型model对应data中的数据
* 视图View,模版,也就是html页面
* vm,视图模型viewmodel,vue实例对象
观察发现:
* data中的所有属性,最后都出现在了vm身上
* vm身上所有属性,以及vue原型上的所有属性,在Vue模版中都可以直接使用

```javascript
所以也意味着你可以用这样的插值表达
{{$attrs}} {{$el}}
```
##### Object.defineProperty()
```javascript
//平常我们给对象新增属性的话,可以直接在对象上通过.来追加属性
let number = 18;
let person = {
name:'pop',
sex:'man',
// age:18 可以通过这个方法增加
}
// person.age = 18;
//也可以通过这种方法增加
但其实,还有一种高级的用法
Object.defineProperty(person,'age',{
value:18,
enumerable:true,//控制属性是否可以枚举,默认是false
writable:true,//控制属性是否可以被修改,默认是false
configurable:true,//控制属性是否可以被删除,默认是false
get(){
// 当有人读取person的age属性的时候,get函数(getter)就会被调用
console.log('调用get方法');
return number;
},
set(value){
// 当有人修改person的age属性的时候,set函数(setter)就会被调用
console.log('有人调用set方法');
number = value''
}
});
/*想额外说一下enumerable这个属性的影响
在控制台上,如果直接在对象上添加,会显示亮色
但是使用defineProperty会变暗、
由于这个属性默认是false的
也就是不可枚举的,则意味着
你在遍历的对象属性的时候,这个属性会被跳过。
*/
```

age的值是未知的,需要调用对应的方法来获得,同时,由于number是外部的,所以当number有所改变,这边也会同步改变。
```javascript
// value和 get 是同一个作用,只能同时用一个。
// writable和set是同一个作用,用一个。
// 完整实例:
Object.defineProperties(obj, {
'address': {
//value: true,
// writable: true,
get:()=>{console.log("get110");return obj._address;},
set:(v)=>{console.log("get110");return obj._address=v;}
}
// etc. etc.
});
```
##### 数据的代理
```javascript
/**
* 假设有这样一个需求,obj1里有x属性,obj2有y属性
* 我希望从obj2访问obj1的属性
* 相当于obj2做了obj1的代理
*/
let obj1 = {x:100};
let obj2 = {y:200};
//你可以这样定义
Object.defineProperty(obj2,'x',{
get(){
return obj1.x;
}
,
set(value){
obj1.x = value;
}
});
```
##### Vue中的数据代理
* Vue中的数据代理
* 通过vm对象来代理data对象中的属性的操作(读/写)
* Vue中的数据代理的好处
* 更加方便的操作data中的数据
* 基本原理
* 通过Object.defineProperty来把data对象中所有的属性添加到vm上。为每一个添加到vm上的属性,都制定一个getter/settter。在getter/setter内部去操作(读/写) data中对应的数据
```javascript
const x = new Vue({
el:'#root',//选择容器
data:{
name:'一个Vue',
url:'https://www.baidu.com'
}
});
// 在你通过data属性进行model的数据绑定的时候,vue也会将这个数据源保存起来,他的保存的地方叫做_data的属性
```

所以实际上我们的插值属性
```
{{name}}也可以等同于{{_data.name}}
```
当很明显,这样太麻烦了,所以数据代理,让我们直接使用`{{name}}`这种数据代理的方式,快速的访问到变量,实际上是代理我们创建vm的时候,传入的data参数。这里也是使用object.defineProperty的方式来实现的,十分的方便。
##### 事件绑定
* 使用v-on:xxx 或 @xxx 绑定事件,其中xxx就是事件名
* 事件的回调需要配置在`methods`对象中,最终会在vm上
* 
* methods中配置的函数,**不要**使用箭头函数函数,否则this就不是vm了
* methods中配置的函数,都是被Vue所管理的函数,this的指向是vm或组件实例对象
* @click=“demo” 和 @click="demo($event)" 效果一致,但后者可以传参
```javascript
欢迎{{name}}
```
##### 事件描述符
Vue 中的事件修饰符
* prevent:阻止默认事件(常用)
* stop:阻止事件冒泡()常用
* once:事件只触发一次(常用)
* capture:使用事件的捕获模式
* self:只有event.target是当前的元素时才会触发事件
* passive:事件的默认行为立即执行,无需等待时间回调执行完成
```javascript
欢迎{{name}}
点击阻止跳转
```
##### 键盘事件
* Vue中常用的按键别名
* 回车=>enter
* 删除=>delete
* 退出=>esc
* 空格=>space
* 换行=>tab
* 上=>up
* 下=>down
* 左=>left
* 右=>right
* ```javascript
@keyup.enter="eventName"
这样的绑定,如果这里没有定义的按钮,你也想使用,可以查看e.key来查看,也是同样可以使用的,但是有几个例外的按键,请看第四点。
```
* Vue 未提供别名的按键,可以使用按键原始的key值去绑定,但要注意kebab-case(短横线命名)
* ```
按键的名字你可以通过 e.key(按键名称)e.keyCode (按键编码)来查找,但是keyCode在不同的键盘也许code码也许不一样,未来这个属性可能会废弃,所以还是以按键名字为主
如键盘上有一个按键叫做CasLock,这个时候有点像是驼峰的命名,你在绑定的时候不能这样写
@keydown.CasLock 而是要 @keydown.cas-lock才可以
```
* 系统修饰符(用法特殊):ctrl、alt、shift、meta(win/command)、tab
* tab在网页上有失去焦点的意思,所以tab的事件是发生在按键按下的时候,当抬起的时候已经失去了事件对象了,所以对tab而言,需要使用keydown。
* 配置keyup使用:按下修饰键的同事,再按下其它键,随后释放其它键,事件才被触发
* 配合keydown使用:正常触发事件
* 也可以使用keyCode指定具体按键,(不推荐)
```html
```
* `Vue.config.keyCodes.自定义键名 = 键盘码`,可以去定制按键别名
```javascript
Vue.config.keyCodes.huiche = 13;
```
##### 事件的总结
###### 事件的组合使用
```html
```
###### 按键的组合使用
```html
```
##### 计算属性
* 定义:要用的属性不存在,要通过已有的**属性**计算得来
```javascript
vue中,属性一词泛指data中定义的属性,例如当你希望data.name="sdfasdf123"的这个属性,用在页面上的我是全部字母大写,且都截取3位置的时候,也许你会写成这样
{{name.slice(0,3).toUpperCase()}},
当你希望去名字拼接其它属性的时候,你也许也写成这样
{{name.slice(0,3).toUpperCase()}}-{{age.slice(0,3).toUpperCase()}}
或者
{{name.slice(0,3).toUpperCase()-age.slice(0,3).toUpperCase()}}
很明显的是,这样可读性很差,如果有一个可以动态按照我们要求计算出属性的“属性”那就好了
```
* 原理:底层借助了`Object.defineProperty`方法提供的getter和setter。
* get函数什么时候执行?
* 初次读取时会执行一次
* 当依赖的数据发生变化的时候会再次调用
* 对比methods
* 同样是定义方法,计算属性内部有缓存机制,可以复用,效率更高,调试方便
* 备注
* 计算属性最终会出现在vm上,直接读取使用即可。
* 如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算属性的变化
```html
姓:
名:
姓名:
```
##### 监视属性
* 监视属性watch
* 当被监视的属性变化时,回调函数自动调用,进行相关操作
* 监视的属性必须**存在**,才能进行监视
```
不存在也可以监视,但是没什么意义
```
* 监视的两种写法
* new Vue时传入watch配置
* 通过`vm.$watch`监视
```javascript
今天天气{{info}}
```
##### 深度监视
* `Vue`中的watch默认不检测对象内部的改变(一层)
* 配置的deep:true可以检测对象内部值的改变(多层)
* `Vue`自身可以检测对象内部值的改变,但`Vue`提供的watch**默认不可以**
* 使用`watch`时,根据数据的具体结构,决定是否采用深度监视。
```javascript
```
##### computed和watch之间的区别
* computed能完成的任务,watch都可以完成
```
computed可以用于直接计算出属性,watch如果希望用到data中的某个属性,但是那个属性不存在的情况下,是需要提前声明的。
```
* watch能完成的功能,computed不一定能完成,例如watch可以异步操作
```
你希望在某个值改变后,延迟一秒再执行,这一点computed无法做到,但是watch可以
```
* 两个重要的原则
* 所被`Vue`管理的函数,最好写成普通函数,这样的this才是vm,或者组件实例对象
* 所有不被Vue所管理的函数(定时器的会回调函数,ajax的回调函数,Promise的回调函数),最好写成箭头函数,这样的this指向的才是vm,而不是组件实例对象。
##### 绑定class样式
```html
{{name}}
{{name}}
{{name}}
```
##### 绑定style
你也在元素上定义内联样式
* class样式
* :class="xxx" xxx 可是是字符串,对象,数组。
* 字符串写法适用于:类名不确定,要动态获取。
* 对象写法适用于:要绑定多个样式,个数不确定,名字也不确定
* 数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用
* style样式
* :style=“{fontSize:xxx}”其中是xxx是动态值
* :style="[a,b]"其中a、b是**样式对象**
```html
{{name}}
{{name}}
{{name}}
```
##### 条件指令
* v-if
* v-if=“表达式”
* v-else-if="表达式"
* v-else="表达式"
* 适用于切换评率脚底的场景
* 不展示DOM元素直接被移除
* v-if可以和v-else-if、v-else一起使用,但要求结构不能被“打断”
* v-show
* 写法:v-show=“表达式”
* 适用于:切换评率较高的场景
* 特点:不展示dom元素未被移除,仅仅是使用样式隐藏掉
* 备注:
* **使用v-if的时候,元素可能无法获取到,而使用v-show一定可以获取到**
```html
{{name}}
{{name}}
{{name}}
{{name}}
{{name}}
{{name}}
{{name}}
123
456
789
```
##### 列表渲染
* 用于展示列表数据
* 语法: v-for="(item,index)" in xxx :key="yyy"
* 可遍历:数组,对象,字符串(用得少),指定次数(用得少)
```html
人员列表
-
{{p.name}}-{{p.age}}
-
{{p.name}}-{{p.age}}
-
{{key}}-{{value}}
-
{{char}}-{{index}}
-
{{number}}-{{index}}
```
##### key的作用和原理

key在遍历的时候,作为更新元素的唯一凭证,vue会拿到对应的key所指定的元素进行对比渲染,当key所关联的元素完全一致,将会复用以前已经渲染完成的,当不一致时,将会重新渲染。
* 虚拟dom中的key的作用
* key是虚拟DOM对象的标识,当数据发生变化时,vue会根据【新规则】生成【新的虚拟DOM】,随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异对比,比较规则如下
* 对比规则
* 旧虚拟DOM找到了与新虚拟DOM相同的key
* 若虚拟DOM中内容没变,直接使用之前的真实DOM
* 若虚拟DOM中内容变了,则生成新的真实DOM,**随后替换掉页面中之前的真实DOM**
* 旧虚拟DOM中未找到与新虚拟DOM相同的key,创建新的真实DOM,随后渲染到页面
* 用index作为key可能会引发的问题
* 若对数据进行:逆序添加,逆序删除等破坏顺序操作,会产生没有必要的真实DOM更新,界面看起来没有问题,但是效率低。
* 如果结构中还包含输入类的DOM,会产生错误的DOM更新,界面有问题,因为如果输入控件没有变化,将会被直接替换,造成数据错位。
* 开发中如何选择key
* 最好使用每天数据的唯一标识
* 如果你一定要用index来作为标识,请保证不要打乱前面已经渲染好的数据
##### 关于更新数据时候一个问题
调试vue代码的时候,会安装一个Vue的开发者工具在自己电脑上,如果你在更新一个列表的时候,是这样写的
```javascript
let vm = new Vue({
data:{
persons:[
{name:'123',age:18,id:1}
]
},
methods:{
changeProfile(){
this.persons[0].age=16;//奏效
this.persons[0]={name:'546',age:30,id:2};//不奏效
}
}
});
```
可以看到我们写了两种方式,第一种方式是可以在页面中体现的,但是第二种却不行,不过第二种的不行并不是说数据没有修改成功,而是界面上没有显示,如果你在点了changeProfile方法后,再打开浏览器的开发者工具,你会其实数据有体现,不过你在点击了changePrile之前点开,会发现数据并没有变化,无论哪种方式,界面上的数据都不会有变化。
##### Vue 监测数据的原理
之前我们提及过数据代理,也就是我们在data属性中定义过的数据,最终会在Vue实例vm身上找到,并且最后还伴随着许多关于他们的set,get方法,当我们尝试去修改,就会触发vue对模版的重新解析,那么
```javascript
this.persons[0].age=16;//奏效
this.persons[0]={name:'546',age:30,id:2};//不奏效
```
persons的修改为什么只有第一种可以,而第二种不可以呢,主要是因为,vue只会对对象的**属性**进行检测,并不会对对象本身进行检测。如果要解释Vue生成的代理原理进行解释,这里有一段简单的代码。
```javascript
let data = {
name:'pop',
age:26
};
const obs = new Observer(data);
//假设这是一个vm对象
let vm = {};
vm._data = data=obs;
//Vue实例中的再生成实例之前,也就是对_data之前还做了另一个操作
function Observer(data){//传入需要被代理的对象
//获得这个对象所有可以获得的属性名
let keys = Object.keys(data);
keys.forEach((k)=>{
//为每个属性属性进行代理
//此时的this就是Observer对象
Object.defineProperty(this,k,{
get(){
return obj[k];
},
set(val){
obj[k] = val;
}
});
});
}
```
所以直接修改对象本身vm是无法检测的,所以修改并不会触发vue重新解析模版,即便数据真的已经修改了。
##### Vue中的Set与$set
如果你希望在后续的开发中,追加属性的话,可以使用这个api
```javascript
//希望在student属性上追加性别属性
Vue.set(vm._data.student,'sex','男')
//数据代理也是一样的
Vue.set(vm.student,'sex','男')
// 这样使用也是可以的
vm.$set(vm.student,'sex','男')
```
但是有一个问题,这个set方法只能用于Vue实例身上已经存在的属性,意思是
```javascript
let vm = new Vue({
data:{
student:{
name:"pop"
}
}
});
//这样追加可以
Vue.set(vm._data.student,'sex','男')
Vue.set(vm._data,'sex','男')
//一个新的属性,这样添加一个全新的响应式对象是不可以的
```

##### Vue中的数组监控
vue中根数据(data)中如果去修改数组,你不能直接根据索引去修改元素,这样vue无法监测到数组里元素的改变,也就是这样。
```javascript
this.persons[0]={name:'546',age:30,id:2};//不奏效
```

Vue的作者,将常用的用于修改数组的方法进行了封装,你可以使用
```
this.persons.push({....})
```
的方法来更新数组,这样Vue可以被监听到,从而触发重新解析模版。当然除了官网的方法,你也可以用这种,但是不多。
```javascript
// 属性名,可以是数组的索引值
Vue.set(vm.student,1,'男')
```
* Vue 会监视data中所有层次的数据
* 如何监视对象中的数据?
* 通过setter实现监视,且要求new Vue时转入要检测的数据
* 对象中后追加的属性,Vue默认不做响应式处理
* 如需要给后添加的熟悉做响应式,请使用如下API
```
Vue.set(target,propertyName/index,value)
或者
vm.$set(target,propertyName/index,value)
```
* 如何检测数组中的数据?
* 通过包装数组更新元素的方法实现,本质就是做了两件事
* 重新解析模板,从而更新数据
* 在Vue修改数组中某个元素,一定要如下方式
* ```
push pop shift unshift splice sort reverse
```
* ```
Vue.set()或vm.$set()
```
**特别注意:**Vue.set()和vm.$set() 不能给vm或vm的根数据(_data)对象,追加属性。
##### Vue收集表单数据
* ```
,则 v-model收集的是value值,用户输入的就是value值。
,则 v-model收集的是value值,且要给标签配置value,否则由于没有value值,收集的信息将一直是空。
1.没有配置input的value属性,那么收集的就是checked(勾选or未勾选,是布尔值)
2.配置input的value属性:
1.v-model的初始值是非数组,那么收集的就是checked(勾选or未勾选,是布尔值)
2.v-model的初始值是数组,那么收集的就是value组成的数组
备注:v-model的三个修饰符
如。v-model.lazy
lazy:失去焦点再收集数组
number:输入字符串转为有效数字
trim:输入首尾空格过滤
```
如果你希望表单提交前,进行某个方法的提交
```html
```
##### Vue中的时间戳
https://www.bootcdn.cn/
有一些好用的js组件,可以上这里使用。`dayjs.min.js`


```javascript
new Vue({
el:"#root",
data:{
time:Date.now()//一个时间戳
},
computed:{
fmtTime(){
//不传,默认是当前时间
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss');
}
}
});
```
##### Vue中的过滤器
```java
//全局过滤器配置
Vue.filter('mySlice',function(value){
return value.slice(0,4);
});
//单独vue实例配置
new Vue({
el:"#root",
data:{},
filters:{
mySlice(value){
return value.slice(0,4)
}
}
});
```
* 对要显示的数据进行特定格式化再显示(**适用于一些简单的逻辑处理,如果逻辑过于复杂,推荐使用计算属性和方法**)
* 语法
* 注册过滤器:`Vue.filter(name,callback)`或者 `new Vue(filters:{})`
* 使用过滤器:使用插值语法,可以利用`管道符|进行连接`,达到链式调用,上一个过滤器的返回值将会作用于下一个过滤器的第一个参数。
```
{{ xxx | 过滤器名1 | 过滤器2 }} 或 v-bind:属性 = "xxx | 过滤器名"
xxx 定义的变量总是会作用于 过滤器的第一个参数,当然,你的过滤本身也是一个方法,如果你在过滤器上又定义其它参数,他们总是按定义的顺序排在管道顶端第一个变量后面。
```
* 备注:
* 过滤器也可以接受额外参数,多个过滤器也可以串联
* 并没有改变原本的数据,是产生新的对应数据。
##### Vue 中的一些标签
* v-bind
* 单向绑定解析表达式,可简写为 :xxx
* v-model
* 双向数据绑定
* v-for
* 遍历数组/对象/字符串
* v-on
* 绑定事件监听,可简写为@
* v-if
* 条件渲染,动态控制节点是否存在
* v-else v-else-if
* 条件渲染,动态控制节点是否存在,和v-if配合使用,**中间不可以断开,插入其他标签**
* v-show
* 条件渲染,动态控制节点是否展示
* v-text
* 作用:向其所在的节点中渲染的文本内容
* 与插值语法的区别,v-text会替换掉节点中的内容,{{xx}}则不会。
* v-html
* 作用:向指定节点中渲染包含html结构的内容
* 与插值语法的区别
* v-html会替换节点中所有的内容,{{xx}}则不会
* v-html可以识别html结构
* 严重注意,v-html有安全问题
* 在网站上动态渲染任意的html是非常危险的,容易导致xss攻击
* 一定要在可信的内容上使用v-html,永远不要用在用户提交的内容上
* v-cloak
* 是一个特殊的属性,Vue实例创建完毕并接管容器后,会删除掉v-cloak属性
* 使用css配合v-cloak可以解决网速慢时,页面展示出{{xxx}}的问题
* ```html
{{name}}
```
* v-once
* v-once所在节点再初次动态渲染后,就视为静态内容
* 以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能
```html
初始化的n值是:{{n}}
当前的n值是:{{n}}
```
* v-pre
* 跳过其它所在节点的编译过程
* 可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译
```html
一段不需要vue解析的模板,使用v-pre,vue可以不再解析,加快渲染速度
当前的n值是:{{n}}
```
##### Vue中自定义指令函数
Vue中有一个v-show函数,可以控制所绑定dom元素的显示隐藏,本质上是操作dom元素,在行内样式上增加了display:none,我们是否也可以定义自己的指令函数呢,答案是可以的
```html
{{name}}
当前的n值是:
当前放大10倍的n值是:
```


##### Vue 中的生命周期
看源码



##### Vue中的组件


###### 定义组件的几种方式
* 非单文件组件
* 一个文件中包含了n个组件
* 单文件组件
* 一个文件中只包含1个组件



一个重要的内置关系
```
VueComponent.prototype.__proto__ === Vue.prototype
让组件实例对象vc,可以访问到Vue原型上的属性、方法
```

##### Vue 的脚手架
安装脚手架
```
npm install -g @vue/cli
切换到你要创建项目的目录,然后使用命令创建
vue create xxxx
启动项目
npm run serve
```
这个操作需要用管理员来操作,但是有的时候下载好的vue-cli没办法使用,你需要用
```
get-ExecutionPolicy
查看当前PowerShell的权限是什么,如果是Restricted,说明是限制的
接着我们更改一下
Set-ExecutionPolicy -Scope CurrentUser
该模式会变成,RemoteSigned (受限移除)
```

一个正确的流程是,`main.js`作为整个项目的入口文件,他创建了这个项目的唯一VM对象,也就是Vue实例对象,`main.js`中需要引入vue,和一个所有容器的父级,就是App.vue他管理着所有的容器,然后接着就是App.vue的定义,他里面注册了所有的容器。
然后就是作为网页的展示,是在public文件下的index.html文件。

脚手架的默认配置无法修改,虽然vue的脚手架依靠webpack,但是我们没看到webpack的配置文件,主要是因为这个配置被隐藏了,我们可以使用
```
vue inspect > output.js
```
来获得参数,不过,在这个js里修改后,也是不生效的,这个只是展示作用。
如果你希望修改脚手架的默认配置,可以自己创建一个`vue.config.js`文件,这里使用的语法是commonJS语法,也就是module.exports的暴露方式而不是es6,原因在于这个文件会被webpack解析,将参数和默认参数合并,而webpack是使用Nodejs,nodejs打包用的是commonjs。
关于vue.config.js的语法这里可以查到
https://cli.vuejs.org/zh/config/#vue-config-js
###### ref
看源码
###### props
看源码
###### mixin
看源码
###### plugin
插件

###### scoped
看源码
##### 自定义一个Todo List


##### LocalStorage 和 SessionStorage

只会存储属于同一域名下的存储内容

##### Vue 中的组件自定义事件
给组件定义的事件,不是针对html元素的

##### 全局事件总线
可以实现任意组件通信,这不是一个新的技术,而是总结出来的经验

某个组件X,所有的其它组件都可以找到这个组件X,且可以往X身上注册他们的事件,然后可以通过X来互相传递信息。
x组件需要满足两个条件
* 可以被所有的组件看到
* 支持注册事件,解绑事件,触发事件的方法 $on $off $emit
要实现能被所有的组件看到,我们需要这样一个以前的知识点
```
VueComponent.prototype.__proto__=Vue.prototype
```
所以我们只需要往Vue的显示实例对象上添加就行了,可以保证所有的vc实例都可以看到。


需要注意的是,并不是所有的组件交流都需要使用全局事件总线的,例如,父组件给子组件传递,可以用props属性,子给父也可以用自定义事件来传递。
但是如果涉及关系到达到多层,例如孙子组件和爷爷传递,父亲也只是中间商而已,这个时候就可以用全局事件总线。
##### 消息订阅与发布

谁订阅了消息,然后谁往那个订阅了消息的地方发布了东西,谁就会收到
原生js无法实现消息订阅和方法,所以需要借助第三方库。
这里使用的是`pubsub.js`,你有其他的也可以选择其他的
```
npm i pubsub-js --save
```

但是用的少,我们还是比较常用全局事件总线
##### nextTick

##### 过度动画

vue中,对于动画有几个阶段,一个是进入的动画enter,即表示被定义的元素会以什么样的方式从enter过度到leave,也就是从登场到离场的状态。enter的阶段也有对应的,就是从v-enter到v-enter-to,表示从enter这个大动画的开始到结束可以算作一个完整的动画过程。leave也是同理

##### 代理服务器
看源码
##### 插槽




##### VueX
* 多个组件依赖于同一个状态(数据),
* 来自不同的组件行为需要变更同一状态


Vuex有三个模块需要了解,对于案例-计算器,我们可以选择数字和运算符来对结果进行把控,这个实在一个组件里面就可以完成的,而对于Vuex来说,我们需要三个步骤来进行完成结果。
* Actions
* 以VueComponents举例,我们自己写的也是这个,当你请求发送一个加2的请求的时候,需要使用一个Dispatch的Api。将这个请求发送给Actions,他会将这个请求发送给Mutations,Actions本质是一个对象,有一个加2的回调来响应这个dispatch操作。接着在Actions中,你可以使用Commit操作来提交给Mutations
* Mutations
* 用于操作和修改数据,也就是状态(State)的对象,他同样也是对象,他可以直接使用State中的数据进行更改
* State
* 用于存储数据,他也是一个对象,所有需要操作的数据和都在这个对象中,当他的数据被修改的时候,Vue会重新渲染模版来进行展示
这个里面有一个也许会有一个多余的操作,就是Actions,因为他貌似只是将操作告诉给Mutations,真正修改状态的操作其实是Mutations,其实Actions还是有存在的必要,之前我们是dispatch给Actions一个准确的2的数字,如果当要加的数据不确定的时候,需要向其他的服务器请求的时候,Actions就有了用武之地。所以当你希望在操作数据之前需要额外的逻辑的时候,Actions就可以做到。
但是如果你就是明确了加2,其实也可以直接在VC步骤的时候提交给Mutations。
这所有的对象,都是归Store对象管理,所以当你调用dispatch的时候,其实调用的是store.dispatch
准备工作,你需要安装vuex,这里需要注意的是,由于官方已经将Vue3设置为默认版本,并且Vuex也更新到了4版本,所以你如果现在直接使用vuex安装的就是vue4,有因为vuex4只能用于vue3,目前项目使用的是vue2,所以你直接使用会报错,所以我们这里需要直接指定版本安装,vue2->vuex3,vue3->vuex4
```
npm i vuex@3
```
##### 路由
* 路由就是一组key-value的对应关系
* 多个路由,需要经过路由器的管理
```
npm i vue-router@3
版本和vuex是一个意思,默认是4,用于vue3
```
路由的配置规则 key-> 路径 value -> 组件,当vue-router检测到你的路径发生了变化,就会寻找路径对应的组件,选择暂时,如果没有找到,则不展示。
路由分类
* 后端路由
* 理解value是function,用于处理客户端提交的请求。
* 工作过程:服务器接收到一个请求时,根据请求路径找到匹配的函数来处理请求,返回响应数据。
* 因为使用nodejs我们可以编写一个基于js的服务器,来响应请求,编写方法也是一个路径对应一个函数来响应请求。
* 前端路由
* 理解:value是component,用于展示页面内容。
* 工作过程:当浏览器的路径改变时,对应的组件就会显示。
文件规范
* 普通组件,我们定义的在component文件下的vue组件,由App.vue管理在components项中的组件
* 路由组件,由vue-router管理,且他会匹配跳转的,一般我们会将这些路由组件放在pages文件夹下。
关于路由的几个注意点
* 路由组件通常存放在pages文件夹,一般组件通常存放在componets文件夹
* 通过切换,隐藏了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
* 每个组件都有自己的$route属性,里面存储着自己的路由信息
* 整个应用只有一个router,可以通过组件的$router属性获取到。
##### 路由守卫
决定路由的内容是否展示,与权限绑定。
路由的hash值
如果你的一个请求是这样的
```
http://localhost:8080/student
```
是请求student资源,如果你在这后面增加#号,那么#号包括后面的东西不会包括在请求资源中,而#后面的东西就叫做hash值
```
http://localhost:8080/student/#/123/123/123/123dfsadf
```
hash与history的区别
* hash
* 会在url中存在#,看上去不美观
* hash值不会包含在http请求中,即hash值不会带给服务器
* 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记不合法
* 兼容性好
* history
* 和平时使用的url一直,只是有单纯的资源名,没有#
* 兼容性和hash模式相比略差
* 应用部署上线时需要后端人员支持,解决刷新页面服务端404问题(可使用第三方类库解决)
其实这最主要的问题是会影响项目部署。
我们在router/index.js中配置你的模式
```javascript
const router = new VueRouter({
mode:'history',// 默认hash模式
// ...
})
```
然后打包
```
npm run build
```
打包完成

接下来,我们需要将他们部署到一个服务器中,这里我们使用nodejs中的express框架搭建一个简单的服务器。
```
在一个文件夹下使用
npm init 初始化,创建一个package.json
然后用安装express
npm i express
```
```javascript
const express = require('express')
const app = express()
// 指定可以访问的资源路径
app.use(express.static(__dirname+'/static'))
app.get('/person',(req,res)=>{
res.send({
name:'pop',
age:18
})
})
app.listen(5005,(err)=>{
if(!err) console.log('服务器启动了')
})
```
然后到目录下,`node server.js`启动服务器。
启动完成后,我们尝试访问`http://localhost:5005/person`也可以拿到数据表示启动成功。接着我们在将打包好的项目,放到服务器的static文件下,static目录下,默认访问index.html文件。重启服务器
但是,这里存在的是,如果你是history,点击菜单是ok的,但是你刷新页面就会报错,因为你刷新,你的路径是会请求后台的,但是你的后台是没有对应的资源。
例如,你请求的是/home/news,你通过点击菜单只是路由的切换,不会发送网络请求,但是你一旦刷,这个就会去找我们启动的这台服务器,那么我们现在这台服务器只只支持一个person的get请求,所以会404。但是如果你使用hash就不会有这个问题。
如果解决,你可以使用一个第三方的类库
```
npm i connect-history-api-fallback
```
安装完成后,你在加入如下代码
```javascript
const express = require('express')
// 解决history部署的404问题
const history = require('connect-history-api-fallback')
const app = express()
//一定要写在静态资源前面
app.use(history())
// 指定可以访问的资源路径
app.use(express.static(__dirname+'/static'))
app.get('/person',(req,res)=>{
res.send({
name:'pop',
age:18
})
})
app.listen(5005,(err)=>{
if(!err) console.log('服务器启动了')
})
```
这样,他就会将你的资源映射回已经有的资源,而不会请求后台了。
但其实,用nginx也可以解决,因为他只是去请求了不存在的资源而已,我们只要用nginx把请求转发回来就没问题了。nginx来判断他是前端路由还是后端路由。
#### Vue 3