Vue2.0+Vue3.0(复习笔记)

Vue是一套用于 构建用户界面 的 渐进式 JavaScript框架。
渐进式:vue可以自底向上逐层的应用(简单应用:只需一个轻量小巧的核心库;复杂应用:可以引入各种Vue插件库)

一、Vue基础

1.1. v-bind与v-model

v-bind:单项数据绑定,model => view view !=> medel
v-model:双向数据绑定,model <=> view (仅用于表单元素的value)

image.png

简写:
image.png

1.2.回顾Object.defineProperty

Object.defineProperty:对对象的属性进行操作(vue2.0中的数据劫持、数据代理...底层都有用到)


image.png

1.3. vue中的数据代理

数据代理:通过一个对象(vm)对另一个对象(data)的属性进行操作(Object.defineProperty)


image.png

image.png

1.4. Vue中的事件

a.事件传参问题

@click="fn" 与@click="fn($event,x)" 都可调用事件,后者可直接传递参数

b.事件修饰符(6种)

image.png

c.键盘事件

image.png

注:tab及系统修饰键(ctrl、alt、shift、meta)建议搭配@keydown.键名 使用

1.5. 计算属性computed 与 监视/侦听属性watch

计算属性:通过已有属性计算得来的新属性(getter/setter——defineProperty)
优点:计算属性computed有缓存,当创建时/所依赖的属性发生变化时才会刷新
监视属性:当监视的属性发生变换时被调用(handler(newVal,oldVal){})


深度监视:监视引用类型数据时

计算属性与监视属性的区别:


image.png

当计算属性和监视属性都能实现效果时优先选择计算属性,但涉及到一些异步(比如:定时器setTimeout...)操作的就选监视属性。因为,computed依靠函数的返回值,不能开启异步任务,而watch依靠自身计算不依靠返回结果,可以开启异步任务。

1.6. 样式绑定

image.png

a.绑定class样式
字符串写法:适用于样式的类名不确定,需要动态绑定
:class="variable"
数组写法:适用于要绑定的样式个数不确定,名字也不确定
:class="classArr"
对象写法:适用于要绑定的样式个数确定,名字也确定,但要动态决定是否使用
:class="classObject"
b.绑定style样式
对象写法:
:style="styleObj"
注意:styleObj里要使用小驼峰命名
对象写法(不常用):
:style="[styleObj1,styleObj2]" :style="styleArr"

1.7. 条件渲染(v-if v-else v-show)

image.png

v-show="true/false" ======底层实现就是控制display
v-if="true/false" =======为false时直接对元素进行删除操作
注:切换频率高,使用v-show,频率低,使用v-if
v-if、v-else-if、v-else配合使用,当某条语句的判断条件成立时,紧接在该语句后的其他判断语句就不执行。
这几个条件配合使用时,这几条语句中间不可以加入其他语句,否则该判断结构会被打断,会失效。
注:template标签不会破坏DOM结构,但template只能配合v-if使用

1.8. 列表渲染(v-for)

image.png
//person=[
  {id:'001',name:'张三'},
  {id:'002',name:'李四'},
  {id:'003',name:'王五'}
]
<li v-for="(p,index) in person" :key="p.id">{{p.name}}</li>
补充内容

v-for中key的原理与作用:
真实dom没有key,虚拟dom上有,用户操作是在真实dom上发生,用户操作真实dom后,虚拟dom也随之发生改变,在进行页面渲染之前,新旧虚拟dom会进行对比(diff算法),根据key进行对比,key相同的元素,数据也相同的话就直接复用,否则就重新生成。使用index作为key的值,当打乱遍历的数据顺序时(如:unshift添加数据),数据会发生错乱,所以,开发中最好使用数据的唯一标识作为key的值。
key是每个节点的唯一标识,在vue中不写不会报错,但在react中会报错。
扩展:for-in与for-of的区别:
for-in主要用于遍历对象(val,key,inedx),也可以遍历数组(val,index)
for-of(es6新特性)主要用于遍历数组,也可以遍历字符串、伪数组,一般不用于遍历对象(需内建Object.keys())
使用总结:对象遍历使用for-in,数组遍历使用for-of
列表过滤:
方法一:在watch中使用filter(需要新建一个data属性作为遍历的对象,原数据对象不能直接修改)

image.png

方法二:在computed中使用filter
image.png

注意:data中的原数据尽量不要直接修改
列表排序:
在computed中利用filter+sort实现:
image.png

1.9. Vue监测数据改变的原理:

Vue监测数据改变的原理

当data中的数据发生改变时,Observer方法(观察者)会收集数据,触发该数据对应的set方法(setter),setter会重新解析模板(界面),形成新的虚拟dom树,新旧虚拟dom树进行对比,相同的的元素直接复用,不一样的元素进行生成创建,达到监听数据改变形成响应式的功能。
Vue.set的使用:
①.在向data中的 对象 追加数据时,不可以直接添加(无法实现响应式),需要使用Vue.set()。
Vue.set(需要添加数据的对象,key,value)
或者 this.$set(需要添加数据的对象,key,value)
缺陷:不能直接给data追加数据,只能给data中的对象添加
②.在Vue中,没有为数组提供getter和setter,根据索引值直接赋值修改data中的数组数据,页面数据不会响应,Vue对数组的常用方法进行了封装,所以只有调用数组方法修改数组时,响应才会生效。
image.png

1.10. 收集表单数据(form)

总结

表单type属性:
text:文本(v-model.trim收集数据时去除空格)
password:密码
number:数值(input默认输入的是字符串,所以,可以v-model.numder)
radio:单选(多选一时,固定name属性)===>配置value值,被选中时读取value
checkbox:多选 (多选v-model要绑定数组)===>配置value值,被选中时读取value
select>option:下拉选项
textarea:多行文本输入(有时不需要打一个字就收集,可以v-model.lazy,改标签失去焦点时才收集)
单个checkbox:勾选框(直接指定v-model,无需配置value值)
image.png

image.png

1.11. 过滤器

数据 | 过滤器的名称
过滤器的本质就是一个函数,将数据作为参数传入过滤器,然后将返回结果替换整个过滤器。多个过滤器可以串联使用。过滤器不改变原数据。
过滤器一般用于插值语法{{}}和v-bind,不能用于v-model
局部过滤器:和methods、methods一样,直接在Vm下filters:{}

//局部过滤器
filters:{
  过滤器名(数据,参数='设置参数默认值'){
        代码片段;
    return 返回结果
    }       
}           

全局过滤器:Vue.filter('过滤器名称',function(){})

//全局过滤器
Vue.filter('mySlice',function(value){
    return value.slice(0,4)
})

总结:


总结

1.12. 内置指令(Vue提供的但不常用的指令)

复习:
v-bind: 单向绑定解析表达式, 可简写为 :xxx
v-model : 双向数据绑定
v-for : 遍历数组/对象/字符串
v-on : 绑定事件监听, 可简写为@
v-if : 条件渲染(动态控制节点是否存存在)
v-else : 条件渲染(动态控制节点是否存存在)
v-show : 条件渲染 (动态控制节点是否展示)
v-text指令:
1.作用:向其所在的标签中插入文本内容。
2.与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会。
v-html指令:
1.作用:向指定节点中渲染包含html结构的内容。
2.与插值语法的区别:
(1).v-html会替换掉节点中所有的内容,{{xx}}则不会。
(2).v-html可以识别html结构。
3.严重注意:v-html有安全性问题!!!!
(1).在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。
(XSS攻击:冒充用户攻击,盗用cookie)
(2).一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!
扩展:cookie的工作原理:

image.png

v-cloak指令(没有值):
1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。
2.使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题。
扩展:JS阻塞:JS具有阻塞特性,当浏览器在执行js代码时,不能同时做其它事情,即<script>每次出现都会让页面等待脚本的解析和执行(不论JS是内嵌的还是外链的),JS代码执行完成后,才继续渲染页面。在引入成功之前页面不会渲染或显示未处理的内容。
v-once指令(没有值):
1.v-once所在节点在初次动态渲染后,就视为静态内容了。
2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。
v-pre指令:
1.跳过其所在节点的编译过程。(vue不解析带有该指令的标签)
2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。

1.13. 自定义指令

自定义指令:将原生操作dom的操作进行封装
在Vue的配置对象directives中进行封装(编写时不加" v- ",使用时采用" v-*** ")。
全写方式:
**
指令名:{
//element:指令所在的元素 bingding:绑定元素的信息
bind(element,binding){
//指令与元素成功绑定时调用
},
inserted(element,binding){
//指令所在元素被插入页面时调用
},
update(element,binding){
//指令所在模板被重新解析时调用
},
}
**
**简写方式:
指令名(指令所在的元素,绑定元素的信息){
代码片段(注意:没有返回值)
}
**
注意:directives中的函数的this不指向Vm,指向window
例子:

//界面使用::<span v-big="n"></span>   <input type="text" v-fbind:value="n">
directives:{
//函数的调用:自定义指令与元素成功绑定时被调用;指令所在的模板被重新解析时
    big(element,binding){//绑定元素的信息的 value*10
        element.innerText = binding.value * 10
    } ,
        /*这样写是无法实现的:需要用到inserted函数,不能使用简写方式
        fbind(element,binding){//获取焦点
               element.value=binding.value;
                element.focus();
        },
        */
        fbind:{
                bind(element,binding){
                      //指令与元素成功绑定时调用
                       element.value=binding.value;
                    
                },
                inserted(element,binding){
                        //指令所在元素被插入页面时调用
                        element.focus();  
                },
                update(element,binding){
                         //指令所在模板被重新解析时调用
                          element.value=binding.value;
                },
        }

总结:


自定义指令

1.14. Vue生命周期(11个钩子函数)

通常情况(8个):
beforeCreate()==[数据监测、数据代理]==>created()====>beforeMount()====>mounted()====>beforeUpdate()====>updated()====>beforeDestroy(关闭定时器、取消订阅消息、解绑自定义事件)====>destroyed()
常用的两个生命周期钩子:
①.mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】
②.beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】

Vue生命周期

总结:
image.png

二、Vue组件化

组件定义:实现应用中局部功能的代码(html、css、js)和资源(img/mp3/ttf/zip/gif...)的集合
组件作用:复用代码,简化项目代码,提高运行效率。

2.1. 非单文件组件

Vue中使用组件的三大步骤:定义组件(创建组件)、注册组件、使用组件(写组件标签)
1、创建组件:
使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别;
注意:组件定义时,一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器。
区别如下:
①.el不要写,为什么? ——— 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器。
②.data必须写成函数,为什么? ———— 避免组件被复用时,数据存在引用关系。
备注:使用template可以配置组件结构。
2、注册组件:
1.局部注册:靠new Vue的时候传入components选项
2.全局注册:靠Vue.component('组件名',组件)
3、使用组件:
<school></school>
组件注意点:
1.关于组件名:
一个单词组成:
第一种写法(首字母小写):school
第二种写法(首字母大写):School
多个单词组成:
第一种写法(kebab-case命名):my-school
第二种写法(CamelCase命名):MySchool (需要Vue脚手架支持)
备注:
(1).组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行。
(2).可以使用name配置项指定组件在开发者工具中呈现的名字。
2.关于组件标签:
第一种写法:<school></school>
第二种写法:<school/>
备注:不用使用脚手架时,<school/>会导致后续组件不能渲染。
3.一个简写方式:
const school = Vue.extend(options) 可简写为:const school = options
关于VueComponent:

     const school = Vue.extend({... })//定义一个组件

1.school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的。
2.我们只需要写<school/>或<school></school>,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:new VueComponent(options)。
3.特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent!!!!
4.关于this指向:
(1).组件配置中:
data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【VueComponent实例对象】。
(2).new Vue(options)配置中:
data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】。
5.VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)。
Vue的实例对象,以后简称vm。
注意:Vm和vc的区别:vc没有el配置项;vc的data配置项只能使用函数的返回值。

Vue与VueComponent的关系

1.一个重要的内置关系:VueComponent.prototype.proto === Vue.prototype
2.为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。
补充:原型链相关知识
原型链相关知识

实例的隐式原型属性(proto)永远指向自己缔造者的原型对象!

2.2. 单文件组件

组件:实现局部功能代码和资源的集合。
单文件组件(.vue)的结构组成:

<template>
      <!--组件的结构-->
</template>
<script>
      //组件交互相关的代码(数据/方法等)
</script>
<style>
       /*组件的样式*/
</style>

单文件组件的结构组成

文件暴露/导出的三种方式:
1、分别暴露:export const 组件名 = Vue.extend({})
2、统一暴露:export {组件名} ---------引入时:import {***} from ****
3、默认暴露:export default 组件名---------引入时:import *** from ****
统一暴露与默认暴露的区别
App.vue 汇总所有的组件
main.js 入口文件(new Vue({}))====引入App.vue
index.html 入口文件的容器====引入main.js

关系

三、Vue脚手架 Vue CLI(command line interface 命令行接口工具)

Vue脚手架是Vue官方提供的标准化开发工具/开发平台。

3.1.Vue脚手架

使用步骤:
①全局安装@vue/cli(仅一次使用时,记得配置淘宝镜像)

    npm config set registry https://registry.npm.taobao.org

切换到要创建项目的目录,然后使用命令行创建项目 vue create ***
③启动项目 npm run serve
注:Vue脚手架隐藏了所有webpack相关的配置,若想查看相关配置:vue inspect > output.js
可参考文章使用Vue-CLI搭建项目
项目结构解析:

Vue cli项目结构解析

总结:执行npm run serve=>main.js=>App.js=>执行其下的组价=>汇总到App.js=>找到容器所在文件index.html,将执行的内容放入容器,浏览器进行渲染。
针对main.js中的render方法进行分析:
完整版的vue:vue核心+模板解析器,其中模板解析器占1/3左右,如果使用完整版的vue,使用webpack打包后模板解析器占了很大内存且无用。为了使打包完成的项目体积小一些,脚手架中引入的vue是残缺的,没有模板解析器(无法解析template标签),就需要使用render函数来进行渲染。
render其实是一个函数:

render(createElement){
    return createElement('h1','你好啊!')
}
//vue调用,且该函数中没有用到this,可以简写:
render:createElement=>createElement('h1','你好啊!')

image.png

脚手架默认配置:public、favicon.icn、index.html、src、main.js
默认配置一般是不可更改的,若要自定义个性化配置,可在项目的package.json的同级目录下创建vue.config.js文件(若修改该文件,一定要有重启项目)。
关闭语法检查的方法:在vue.config.js文件下添加 lintOnSave:false(与pages平级)
关闭语法检查

Vue脚手架总结:
项目结构

Vue版本说明

vue.config.js

3.2. ref属性

普通标签:在原生javascript中,我们获取一个html元素通常使用id属性(getElementById),但在vue中,我们使用ref属性给元素做标识,然后this.$refs.获取相应元素。
组件标签:当ref使用到组件标签上时,this.$refs.
获取到得就是该组件的实例对象VueComponent。(可用于组件间通信)

ref属性

3.3. props配置

<组件标签 key1="value1" key2="value2" ...>
组件:props:["key1","key2",...]

但这样接收到的是字符串,所以,可以使用v-bind解决,②即:<组件标签 :key1="value1" :key2="value2" ...>,为了避免上述情况,接收时也可进行类型限制,即:props:{key1:String,key2:Number,...}。③也可设置默认值指定和必要性限制:props:{key1:{type:String,required:true},key2:{type:Number,default:77},...}
注意:接收到的props是不可以更改的,若想修改,可以赋值给其他变量,然后修改。扫描优先级:props>data
e.g.

注意事项

props总结:
props总结

3.4. mixin混入(混合)

mixin:多个组件使用相同的配置时使用。

混合mixin的使用

mixin总结:
mixin总结

3.5. 插件plugin

在src文件夹下创建文件plugins.js文件,创建一个对象(插件的本质是一个对象),在该对象的install方法(一定要有install方法)中进行插件编写,记得暴露/导出,然后在main.js中引入,然后使用(Vue.use(插件名,参数列表))。

image.png

插件

3.6. scoped样式

在Vue中,各个组件的样式最终是会汇总到一起的,在各个组件中可能会出现类名相同的情况,造成类名冲突。为了解决上述问题,可以给各个组件的样式标签style加一个作用域限制(scoped)。
注意:App组件不适用scoped
<style scoped>... </style>
总结:让样式在局部生效,防止冲突
组件间通信:
原始方法:
父子间传参
兄弟间传参:借助父组件
1、自定义事件
2、全局事件总线
3、消息订阅与发布
4、Vuex
5、路由传参

3.7. 本地存储(控制台=>Application)

1、localStorage:借助浏览器的本地存储,将数据存在硬盘上。关闭浏览器,数据不会消失。
(主动删除或者删除浏览器缓存时就会被清除)
注:localStorage解决页面更新,数据丢失问题

解决页面更新,数据丢失问题

localStorage.setItem("key":"value");//存储,key和value都要使用字符串
localStorage.getItem("key");//读取
localStorage.removeItem("key");//移除
localStorage.clear();//清空

JSON.stringify();//数组对象转JSON字符串
JSON.parse();//

2、sessionStorage:关闭浏览器,数据消失

webStorage

3.8. 组件自定义事件

子组件给父组件传递数据的方式(3种):
1、通过父组件给子组件传递函数类型的props实现:子组件通过props接收,然后通过函数使用触发将数据传递给父组件。
<School :getSchoolName="getSchoolName"/>
2、通过父组件给子组件绑定一个自定义事件实现:给谁绑的自定义事件找谁触发,使用this.$emit("事件名",参数)触发(第一种写法,使用@或v-on
<Student @atguigu="getStudentName" @demo="m1"/>
3、通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第二种写法,使用ref)
<Student ref="student" @click.native="show"/>

image.png

解绑自定义事件(给谁绑的自定义事件找谁解绑):
1、this.$off('自定义事件名'):解绑一个
2、this.$off(['自定义事件名','自定义事件名']):解绑多个
3、this.$off():解绑所有自定义事件
注意:销毁当前组件的VueComponent实例,销毁后该组件上的所有自定义事件都失效。
自定义事件总结

3.9. 全局事件总线:任意组件间通信

全局事件总线:在A组件中给x绑定自定义事件demo,回调函数留在A中,B组件触发x中的事件demo,并传递参数p,A组件就可接收到B组件传递的参数p。
x应满足的条件:所有组件都可访问;可调用到$on/$off/$emit...

全局事件总线图解

条件①:所有组件都可访问
一个重要的内置关系:VueComponent.prototype.prop===Vue.prototype(让组件实例对象vc可以访问到Vue原型上的属性和方法),所以,可以把X放到Vue的原型对象Vue.prototype上(在main.js文件中)。
条件②:可调用到$on/$off/$emit...
在数据监测、数据代理之前(beforeCreate)直接安装(main.js)

new Vue({
    el:'#app',
    render: h => h(App),
    beforeCreate() {
                //Vue.prototype.x= this
        Vue.prototype.$bus = this //安装全局事件总线
    }
})

使用:
this.$bus.$on('自定义事件名',(data)=>{data:接收到的数据})
this.$bus.$emit(('事件名',传递的数据)
this.$off('事件名');解绑
注:
1、收数据的组件:在mounted中绑定事件总线中自定义事件:this.$bus.on('绑定的自定义事件',绑定时触发的函数);
传数据的组件:触发this.$bus.$emit('绑定的自定义事件',参数);
2、在组件销毁前,可以关闭该组件使用到的自定义事件;
e.s. beforDestroy(){this.$bus.$off('hello')}

3.10. 消息订阅与发布:任意组件间通信

消息订阅与发布(mounted中):
(推荐库pubsub-js)安装:npm i pubsub-js
引入:import pubsub- from ' pubsub-js'
1.订阅消息:消息名(接收数据的组件:需要/买):

    this.pubId=pubsub.subscribe('消息名',(消息名msgName,数据data)=>{}) 
    //第二个参数使用箭头函数,this才能指向VC(组件实例对象)

2.发布消息:消息内容(传递数据的组件:提供/卖)pubsub.publish('消息名',function(){ return ...})
注:组件销毁前(beforeDeatory)需要把订阅消息取消pubsub.unsubscribe(this.pubId);
消息订阅与发布原生不可实现,需要借助第三方库pubsub-js(其他库也可以)

消息订阅与发布

$nextTick

语法:this.$nextTick(function(){})
作用:在下一次DOM更新结束后执行其指定的回调函数。
什么时候用:当改变数据后,要基于更新后的DOM进行某些操作,要在nextTick所指定的回调函数中执行。

3.11. 过渡与动画

1、单个元素:将产生动画效果的这个元素使用标签<transition>包裹起来,然后编写进入/离开的动画,进入.v-enter-active 离开:v-leave-active
要使元素一开始就有进入动画,给transition标签加appear属性。
当有多个transition时,可以给transition标签加name属性(name="x1")进行区分,但在写选择器时,选择器名得与之对应,进入.x1-enter-active 离开:x1-leave-active
2、多个元素使用相同的动画效果:使用<transition-group>包裹元素,注意,其中的元素要使用key属性进行区分。

<template>
    <div>
        <button @click="isShow = !isShow">显示/隐藏</button>
        <transition name="hello" appear>
            <h1 v-show="isShow">你好啊!</h1>
        </transition>
    </div>
</template>
<script>
    export default {
        name:'Test',
        data() {
            return {
                isShow:true
            }
        },
    }
</script>

使用动画:

<style scoped>
        h1{
        background-color: orange;
    }
    .hello-enter-active{
        animation: atguigu 0.5s linear;
    }
    .hello-leave-active{
        animation: atguigu 0.5s linear reverse;
    }
    @keyframes atguigu {
        from{
            transform: translateX(-100%);
        }
        to{
            transform: translateX(0px);
        }
    }
</style>

使用过渡:

<style scoped>
        h1{
        background-color: orange;
    }
    /* 进入的起点、离开的终点 */
    .hello-enter,.hello-leave-to{
        transform: translateX(-100%);
    }
    
    /* 进入的终点、离开的起点 */
    .hello-enter-to,.hello-leave{
        transform: translateX(0);
    }
        /* 进入的过程、离开的过程 */
        .hello-enter-active,.hello-leave-active{
        transition: 0.5s linear;
    }
</style>

推荐:动画库animate
多个元素使用同一个动画效果(这里使用了动画库):

         <transition-group 
            appear
            name="animate__animated animate__bounce" 
            enter-active-class="animate__swing"
            leave-active-class="animate__backOutUp"
        >
            <h1 v-show="!isShow" key="1">你好啊!</h1>
            <h1 v-show="isShow" key="2">尚硅谷!</h1>
        </transition-group>
总结

3.12. 配置代理

axios:promis风格,支持请求拦截和响应拦截,体积小
fetch:会把返回的数据包装两层promise,兼容性差
1、axios的使用:(使用广泛,推荐)
下载:npm i axios
引入:import axios from 'axios'(App.vue)
使用:axios.get('url').then(response=>{},err=>{})


跨域报错

解决跨域(协议名+主机名+端口号):
1、cors:后端配置特殊的响应头
2、jsonp:借助script的src属性在引入外部资源时不受同源策略限制(只能解决get产生的跨域问题)
3、代理服务器(开发中常用)


image.png

使用脚手架中的代理服务器:
方式一:
image.png

在vue.config.js配置devServer:{ proxy:'http://localhonst:5000}
缺点:只能配置一台服务器代理;无法灵活控制请求是否走代理
方式二:

image.png

devServer:{ 
  proxy:{
    '/请求前缀':{
        target:'url',//要发送到的服务器
        pathRewirite:{'^/请求前缀',''},//重写路径(一定要写!!)
        ws:true,//用于支持websocket
        changeOrigin:false//用于控制请求头中的host值
    }
  }
}

axios.get('http://host:端口号/前缀/路径')
注意:
1、所谓“前缀”在请求时是放在端口号之后
2、引入第三方文件时,①src-assets-...-在App.vue中引入 import '文件' ②在public中添加,然后在index.html文件中使用link引入
3、Vue的插件库vue-resource(维护频率低)
安装:npm i vue-resource
引入(main.js):import vueResource from 'vue-resource'
使用:Vue.use(vueResource)
e.g. this.\$http.get('url').then()

3.13. 插槽slot

插槽:让父组件可以向子组件指定的位置插入html结构,也是一种组件间通信的方式,适用于父子组件。[子组件挖坑等父组件来填(父组件不填时使用默认值)]
插槽/匿名插槽/具名插槽/作用域插槽/v-slot指令

四、Vuex

4.1. Vuex简介

Vuex:专门在Vue中实现集中式状态(数据)管理的一个Vue插件,对Vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。
Vuex
使用场景:[共享数据]①多个组件依赖同一状态/数据 ②来自不同组件的行为需要变更同一状态

4.2. Vuex工作原理

Vuex=Actions+Mutations+State
Actions:动作、行为 Mutations:修改、加工、维护 State:状态、数据


Vuex工作原理图解

所有的组件实例对象VueCompontent都要能使用store(import+use)
注意:vue2只能使用vuex的3版本,vue3只能使用vuex的4版本

4.3. 搭建Vuex环境

1、安装:npm i vuex@指定版本号
2、引入(main.js):import Vuex from 'vuex'
Vue.use(Vuex)
3、store
4、VueComponent都能使用store
创建文件:src=>store=>index.js

//该文件用于创建Vuex中最为核心的stroe
//引入Vuex
import Vuex from 'vuex'
//准备actions——用于响应组件中的动作(服务员)
const actions = {}
//准备mutations——用于操作数据(state)
const mutations = {}
//准备state——用于存储数据
const state = {}
//创建store
//const store = new Vuex.Store({
export default  new Vuex.Store({
  acrions,
  mutations,
  state
})
//导出/暴露文件
//export default store

然后在main.js中引入 import store from './store/index.js',在vm中使用
注意:因为store是基于Vuex的,所以store必须得Vuex生效后才能使用,但import的执行顺序总是大于Vue.use(),所以,在./store/index.js中引入Vuex,然后将Vue.use(Vuex)放到store的创建之前,记得删除main.js中Vuex的引入和使用。

搭建Vuex环境

总结:
1获取state中的数据:this.$store.state.数据名
2修改state中的共享数据: 1
①将共享数据放到state中;
②在组件中调用this.$store.dispatch("数据名",变量)
③在actions中编写对应的数据 数据名:function(context,value){context.commit('数据名',value)},(actions可以进行业务逻辑的操作,尤其是对各组件共同使用到的操作,需要的数据去content中找);
④在mutations中** 数据名:function(state,value){}**,mutations尽量不要进行业务逻辑的操作,也不要发送请求。
无业务逻辑处理时,可忽略③步骤,①步骤中直接调用④步骤,即this.$store.commit("数据名",变量)
image.png

4.4. getters配置项

getters配置项:用于将state中的数据进行加工


getters的使用

4.5. 4种map方法的使用(读取Vuex中的数据的方法)

1、this.$store.state.数据名
2、借助mapState生成计算属性,从state中读取数据(对象写法)
...mapState({计算属性名:需获取的state中的变量,...})
3、借助mapState生成计算属性,从state中读取数据(数组写法):计算属性名与需获取的state中的变量同名时
...mapState(['需获取的state中的变量',...])
同理:还有mapGetters方法、mapActions方法、mapMutations方法(加上mapState方法,总的4个,mapActions和mapMutations在调用时,记得把变化量作为参数传入的)

mapActions和mapMutations

map**方法的使用总结

多组件共享数据:

4.5. Vuex模块化namespace

Vuex模块化:按功能分类集中处理
配置项处理:

Vuex模块化配置

注意:每一个模块一定要开启命名空间'namespace:true'
组件获取数据时:
自己写:this.$store.state.模块名.数据名 this.$store.commit('模块名/mutation名',变化量名)
this.$store.state.getters[模块名/数据名] this.$store.state.dispatch('模块名/mutation名',变化量名)
image.png

借助map**方法:...mapState('模块名',{'方法名':数据,...})/...mapState('模块名',['数据列表']),同理,mapGetters方法、mapActions方法、mapMutations方法也一样
image.png

总结:
image.png

image.png

五、路由

路由:一个路由(route)就是一组映射关系【key-value(路径-组件)】,将路径与组件配对使用,多个路由需要路由器(router)进行管理。

5.1. 路由的基本使用

1、安装vue-router:npm i vue-router(2022.7月之后,默认版本为4,且4版本只能在vue3中使用,若是vue2项目,使用路由安装时需要指定版本3:npm i vue-router@3)
2、在mian.js中引入使用:import VueRouter from 'vue-router' Vue.use(VueRouter)
3、创建路由器:在src下创建router文件夹>index.js

// 创建整个项目的路由器
// 引入vue-router
import VueRouter from 'vue-router'
// 引入组件
import Login from '../components/Login'
import Index from '../components/Index'
// 创建路由器并导出
export default new VueRouter({
    routes:[
        {
            path:'/login',
            component:Login
        },
        {
            path:'/index',
            component:Index
        }
    ]
})

4、在main.js中引入路由器:

import router from './router/index'
new Vue({
  el:'#app',
  render:h=>{app},
  router:router
})

5、跳转:<router-link to='/**'>跳转</router-link> 出口/视图:<router-view></router-view>
注:
①标签属性active-calss:该路由被激活时的样式
②一般组件与路由组件的区别:开发中为了一般组件与路由组件,会将它们发到不同的文件夹之下进行管理,pages:路由组件
③不用的路由组件会被销毁,待使用时再挂载
components:一般组件
动态路由

5.2. 嵌套路由

路由嵌套

注:子级路由在配置对应组件时,不需要以'/'开头;子级路由在进行路由跳转时,需要把父级带上。(<router-link to='/父级路由/子级路由'></router-link>)

5.3. 路由传参

1、跳转路由并携带query参数:
①to的字符串写法:
在进行路由跳转时:<router-link to='/...?key1=value1&key2=value2'></router-link>
接收参数的组件使用$route(路由信息):this.$route.query.key1
<router-link :to='`/...?**key1=${value1}&key2=${value2}**`'></router-link>
②to的对象写法:

<router-link :to='{
            path:"/...",
            query:{
                key1:value1,
                key2:value2
            }
        }
        '>
    </router-link>

路由的query参数

2、跳转路由并携带params参数:
在进行路由跳转时:<router-link to='/.../value1/value2**'></router-link>
在路由配置时,在path属性中设置占位符(指明对应数据对应的对象):{path:"/路由/:key1/:key2",component:组件}
接收参数的组件使用$route(路由信息):this.\$route.params.key1

5.4. 命名路由

命名路由:在配置路由时增加name属性。
在路由跳转时就不用使用path属性编写多级路由,直接使用name属性即可。
注意:路由使用to的对象写法时,路由命名和params参数不可共存。

<router-link :to='{
            <!--path:"/...",    这样备注是错的,但理解这里的用意就好-->
             name:"...",
            query:{
                key1:value1,
                key2:value2
            }
        }
        '>
    </router-link>

5.5. 路由的props配置

路由的props配置:在配置路由时添加props属性
第一种写法:props值为对象。该对象中的所有key-value都会以props的形式传给使用该路由的组件,然后在对应组件中接收props。(用的非常少,死数据)
第二种写法:值为布尔值。若为true,就会把改路由组件收到的所有params参数,以props的形式传给使用该路由的组件,然后在对应组件中接收props。
第三种写法:值为函数。依靠函数的返回值,返回值(对象)以props的形式传给使用该路由的组件。

路由的props配置

5.6. router-link的replace属性

作用:控制路由跳转时操作浏览器历史记录模式。
浏览器的历史记录(栈)有两种写入方式:push和replace,push是追加历史记录,replace是替换当前记录,默认是push.
replace模式:取代栈顶的第一条数据(路由跳转时,当前路由会替换掉上一条路由,最终结果就是页面不能千金不能后退)
使用:<router-link to='/...' replace></router-link>

5.7. 编程式路由导航

有些时候我们无法使用<router-link>(标签)来进行路由或者需要实现跳转延迟,这时就需要使用编程式路由导航(不借助<router-link>的路由导航)

//e.g.
<button @click='goRouter'>编程式路由</button>

methods:{
  goRouter(){
    //this.$router.push({name:'rputeName',query:{}});
    //this.$router.push({name:'rputeName',query:{}});
    //this.$router.go(number);
      this.back();
  }
}

5.8. 缓存路由keep-alive

作用:让不展示的路由组件保持挂载,不被销毁。

//<keep-alive include='componmentName'>
<keep-alive :include='[cn1,cn2...]'>
  <router-view></router-view>
</keep-alive>

5.9. 两(3)个新的生命钩子函数(activated,deactivated)

由路由缓存<keep-alive>引出的,路由组件独有的两个生命钩子函数。
作用:路由组件独有,用于捕获路由组件的激活/失活状态。
activated:路由组件被激活时触发
deactivated:路由组件失活时被触发
nextTick:在下一次DOM更新结束后执行其指定的回调函数。当改变数据后,要基于更新后的DOM进行某些操作,要在nextTick所指定的回调函数中执行。

5.10. 路由守卫(很重要!!!)

分类:全局守卫、独享守卫、组件内守卫(可搭配使用)
路由守卫:对路由进行权限控制,保护路由的安全。进入某个路由时,进行需求判断,条件为真才可进入该路由,否则跳回指定的其他路由。(防止在不登录的情况下在地址栏输入路径查看页面详情的情况发生)
1、全局前置路由守卫
使用:在路由器中给每个路由添加name属性,先使用变量接收路由器,再默认导出(不可简写),在导出之前进行路由守卫的编写beforeEach函数
beforeEach函数:初始化路由时被调用+每次路由切换之前被调用
to:目标路由(去哪里)
from:来自哪里
next:放行(接着往下走)

router.beforeEach((to,from,next)=>{
    //前提条件,当前去的目标路由是否是我们需要进行守卫的路由,是:进入判断,否:放行
    if(to.path==='/route1' || to.name==='/route1'...){
        //是需要守卫的路由,那么就进行条件判断,满足就往下走,否则就修改目标路由
         //例如,判断token是否有效,无效就跳到登录页面,有效就继续
         if(!token){
              next('/login')
          }else{
              next()
          }
    }else{
        next()
    }
})
export default router

!!!!!!如果需要守卫的路由很多的话,这种判断很繁琐,可以在路由配置时加路由源信息meta来控制是否需要守卫:

// 引入vue-router
import VueRouter from 'vue-router'
const router = new VueRputer({
    routes:[
      {
        name:'**',
        path:'/**',
        meta:{isAuth:true},//需要守卫的路由为true
        compontent:()=>import('/**')//按需引入路由组件,有效防止首屏加载慢或白屏问题
      }
    ]
})
router.beforeEach((to,from,next)=>{
    //前提条件:使用配置项进行判断是否路由守卫
    if(to.meta.isAuth){
        //是需要守卫的路由,那么就进行条件判断,满足就往下走,否则就修改目标路由
         //例如,判断token是否有效,无效就跳到登录页面,有效就继续
         if(!token){
              next('/login')
          }else{
              next()
          }
    }else{
        next()
    }
})
export default router

2、全局后置路由守卫
后置路由守卫没有next参数,一般用于路由跳转后修改页面的标题title。
用法:在路由源信息meta中增加title属性,在路由跳转是进行修改:

// 引入vue-router
import VueRouter from 'vue-router'
const router = new VueRputer({
    routes:[
      {
        name:'**',
        path:'/**',
        meta:{isAuth:true,title:'****'},
        compontent:()=>import('/**')
      }
    ]
})
router.beforeEach((to,fromt)=>{
    if(to.meta.isAuth){
         if(!token){
              next('/login')
          }else{
              next()
          }
    }else{
        next()
    }
})
//使用后置路由守卫修改页面标题
router.afterEach((to,from)=>{
    ducument.title = to.meta.title || '默认值'
})
export default router
全局守卫

3、独享路由守卫
独享路由守卫:只给某一个路由设置的守卫。只有前置没有后置!

{
        name:'**',
        path:'/**',
        meta:{
            isAuth:true,
            title:'****',
            beforeEnter:(to,from,next)=>{
                  if(to.meta.isAuth){
                       if(!token){
                            next('/login')
                       }else{
                            next()
                        }
                 }else{
                    next()
                }
            }
          },
        compontent:()=>import('/**')
      }

4、组件内路由守卫
组件内路由守卫:直接在组件内编写beforeRouterEnter和afterRouterLeave(和钩子函数同级)

//beforeRouterEnter:通过路由规则,进入该组件时被调用
beforeRouterEnter(to,from,next){}
//afterRouterLeave:通过路由规则,离开该组件时被调用
afterRouterLeave(to,from,next){}

5.11. history模式和hash模式(仅从前端角度分析)

路由器的两种工作模式:history模式和hash模式
hash模式下,路径中的值称为哈希值,它不会随着http请求发给服务器。
hash模式(默认模式):地址栏带#号。兼容性好。
history模式:无#号。兼容性略差。
项目打包(npm run bind)前可将工作模式改为hash模式(哈希模式在部署发送网络请求时不会因地址栏参数产生404错误):更改工作模式:在路由配置项中 mode:'history或者hash'

路由器的两种工作模式

若使用history模式,404报错的解决方法(使用node的中间件):
npm i connect-history-api-fallback=>在server.js中引入:const history = require('connect-history-api-fallback')=>使用(在使用静态资源之前):app.use(history)

扩展:使用node express搭建一个微型服务器
新建文件夹demo=>vscod打开=>安装express: 【npm init=>取名:test-server,一路回车=>npm i express】=>创建server.js文件:=>服务器下一般有static(public)文件夹用来存放前端的文件(服务器要识别这些文件就需要借助中间件app.use来指定静态资源(express.static(__dirname+'static')))=>停掉重启即可在浏览器查看前端文件
将前端文件放入服务器静态资源文件夹下的过程就称之为部署。

//server.js
//引入express
const express = require('express')
//引入connect-history-api-fallback解决跨域
const history = require('connect-history-api-fallback')
app.use(history)
//创建服务对象
const app = express()
//借助中间件app.use来指定静态资源
express.static(__dirname+'static'))
//配置路由
app.get('/person',(request,response)=>{
      //该路由给前端返回的信息
      request.send({name:'jaja',age:18})
})
//监听端口
app.listen(50005,(err)=>{
      if(!err) {
            //服务器启动成功
      }
})

5.12. Vue UI组件库

移动端常用:
vant:https://youzan.github.io/vant
Cube UI:https://didi.github.io/cube-ui
Mint UI:https://mint-ui.github.io
PC端常用:
Element UI:https://element.eleme.cn
IView UI:https://www.iviewui.com
使用(以Element UI为例):
打开官网=>组件(按着官网来就好了)
npm i element-ui=>在main.js中引入使用(import和use)=>引入样式(最好按需引入)=>使用:<el-**>
按需引入:借助库babel-plugin-component:
安装:npm i babel-plugin-component -D(-D开发依赖)=>在babel.config.js下追加配置=>在main.js下引入需要的组件

六、Vue3

嗷嗷嗷,终于到vue3了!!!!!
Vue3快速上手

注:绝大部分截图来自尚硅谷张天禹老师教学视频

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容