vue2笔记

Vue笔记
视频: https://www.bilibili.com/video/BV1Zy4y1K7SH?p=1
vue是渐进式JavaScript框架 用到什么功能,只需要引入什么功能模块 ;
vue的特点:易用,灵活,高效;

  1. 组件化 , 一个vue文件包括了(html +css + js)
  2. 声明式编程(不直接操作DOM) ;
  3. 虚拟DOM + diff算法(虚拟dom的对比)

Vue2

vue引入:
引入开发版本的vue, 下载vue Devtools (vue开发者工具) ,

 // 阻止 vue 在启动时生成生产提示。
Vue.config.productionTip = false

创建vue实例:

创建vue实例, 创建一个配置对象; 通过new 关键字调用;

配置项

  • el => element : 即 vue实例管理的元素, 值通常为 id 选择器 的字符串;

  • data 对象或函数且返回一个对象, 用于存储数据, 供el管理的元素使用;

  • vue实例和被管理的容器是一一对应的;且真实开发中只有一个vue实例;

  • data中的数据改变,页面中用到该数据的地方自动更新; (响应式)

const vm = new Vue({el:"#app",data:{msg:"世界"}
})

vue模板语法

{{ }} 插值语法/mustache语法; {{ }}插值语法的内容可以自动读取data中的数据,只能放js表达式;
指令语法,v-开头, 也可以直接使用data中的数据;
在这里插入图片描述


数据绑定:

单向数据绑定(属性绑定) v-bind: 简写为 :

单向数据绑定: <input type="text" :value="msg">

双向数据绑定 v-model , 通常绑定表单元素, v-model: value, 直接简写为 v-model

双向数据绑定:<input type="text" v-model="msg">

双向数据绑定的原理: v-bind 绑定value属性, v-on监听input事件 ;
在这里插入图片描述

el与data的两种写法:

  el: '#app',// 通过实例vm挂载vm.$mount("#app")
data: 
对象形式
data: {msg:"你好 vue"}
函数形式, 返回一个对象
data() {return {msg:"你好 VUE"}
}

vue实例中使用数据, 记得加this;
模板中使用数据,不用加this;
模板中调用方法,加()/也可以不加,但是不传参,默认是event事件;
模板中调用计算属性,不用加(),计算属性是一个属性,get方法简写了;


MVVM框架:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


Vue中的数据代理:

Object.defineProperty的getter()与setter()方法

在这里插入图片描述
在这里插入图片描述


事件处理: v-on

v-on: 事件名 = “事件处理函数”
事件包括 ( 点击事件, 键盘事件, 滚动事件等等)

v-on:事件名  ==> 简写  @事件名

插值语法中调用事件处理函数加 ( ); 也可以不加()

<h4>姓名:{{fullName()}}</h4>

在这里插入图片描述

事件处理函数要写法到 methods (方法) 配置项中去;
事件不传参可以省略();
==不传参,事件处理函数的形参第一个默认是event事件; 传参可以传vm上的数据;
事件传参, 想要保留event 事件,用$event占位;

<button v-on:click="showInfo1">按钮1</button>   //不传参
<button @click="showInfo2(100,$event)">按钮2</button>  //传参methods: {showInfo1(e) { // 调用不加(), 回调函数的形参默认event事件;console.log(e.target); // e.target  拿到触发的对象(元素)console.log(e.target.innerText);  // e.target.innerText  拿到触发对象的内容console.log(this);  // this是Vue实例对象},showInfo2(num, e) {console.log(num, e);},
},

事件修饰符 :

  • .stop 阻止事件冒泡 ==> event.stopPropagation( )
  • prevent 阻止默认行为 ==> event.preventDefault();
  • .once 事件只触发一次
  • .native 触发组件或原生监听事件;
  • .self 只有在 event.target是当前操作的元素时才触发这个事件
  • .capture 事件的捕获阶段
  • .passive
  • 修饰符可以连续使用

键盘修饰符:
键盘事件:

  • keydown 键盘按下触发事件

  • keyup 按下去的键盘松开触发事件

  • 键盘keyCode码
    在这里插入图片描述

键盘修饰符: <input type="text" @keydown.enter=key>

计算属性 computed

配置项: computed: { }

计算属性本质是一个函数,可以实时监听data中数据的变化,并return一个计算后的值,供组件渲染dom时使用;
调用的时候不需要加()。
计算属性依靠的是他的返回值 return, 必需要有;
计算属性会缓存计算的结果,只有计算属性的依赖项发生变化时,才会重新进行运算,所以计算属性性能更好。

计算属性: <span>{{fullName}}</span> -->
      1.定义:要用的属性不存在,要通过已有属性计算得来。2.原理:底层借助了Objcet.defineproperty方法提供的getter和setter。3.get函数什么时候执行?(1).初次读取时会执行一次。(2).当依赖的数据发生改变时会被再次调用。4.优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。5.备注:1.计算属性最终会出现在vm上,直接读取使用即可。2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。

在这里插入图片描述

计算属性的简写: 当只读取(get),不设置(set) 的时候,可以简写;

不简写: computed: {fullName: {get() {return this.xing + "-" + this.ming;},},
}调用的时候:   <h4>姓名:{{fullName}}</h4>
简写: computed: {fullName() { //对象中函数的简写return this.xing + "-" + this.ming;},
},调用的时候:   <h4>姓名:{{fullName}}</h4>

监视属性(侦听属性) watch :

  • 监视data中数据的变化;

  • 组件在初次加载完毕后不会调用 watch 侦听器,因为immediate默认false。

  • watch监视的是个整个对象的变化,只想监听对象中某个属性的变化, 加引号; " xx.xxx " : { } ;

  • 配置项: watch: { 监听对象: { } }

       1.当被监视的属性变化时, 回调函数自动调用, 进行相关操作2.监视的属性必须存在,才能进行监视!!3.监视的两种写法:(1)  new Vue时传入watch配置(2)  通过vm.$watch("监视对象",{配置项} ) 监视 ; ( 监视的属性,记得加引号)
    
watch监视的属性的配置项: 
/ immediate: false(默认),为true时,实例初始化时会调用一下handler函数; /
handler(newValue,oldValue){} 函数; 监视的属性发生修改时,触发handler()函数;
deep:true; 默认不开启(效率问题), 开启深度监视: 监视属性的多层级的数据的改变, 监听对象内的数据改变;a.b.c
deep深度监视:(1) Vue中的watch默认不监测对象内部值的改变(一层)。(2) 配置deep:true可以监测对象内部值改变(多层)。(3)  监视多级结构中属性所有数据的变化

监视属性的简写:

// 不简写watch: {hot:{handler(newValue,oldValue){console.log('hot改变了', newValue,oldValue);},immediate:true,deep:true,}},

当监视属性只有handler()函数时,可以简写

简写, / 当监视属性只存在handler()函数时/,可以简写watch: {hot(newValue,oldValue){console.log('hot改变了',newValue,oldValue);}
},
// 简写 第二种方法, 在vue实例上监视vm.$watch("hot", function (newValue, oldValue) {console.log("hot改变了", newValue, oldValue);});

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


computed、watch、方法的区别:

1.computed能完成的功能,watch都可以完成。
2.watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。
在这里插入图片描述

两个重要的小原则:
1.所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象vc。
2.所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,
这样this的指向才是vm 或 组件实例对象vc。


样式绑定:
固定的样式写死, 不固定的写成动态的;

  • 绑定class样式
  • 绑定style样式

绑定class样式 :

通过v-bind: 属性绑定, v-bind: 指令可以使用data中的数据 ; 
  • 绑定字符串
<div class="box1" :class="color"></div>
  • 绑定数组
<div class="box1" :class="colorArr"></div>
  • 绑定对象 , 当对象的 value值为真时, 才绑定上
<div class="box1" :class="colorObj"></div>
  • 绑定style样式

行内样式 style绑定; 注意: 短横线命名法与驼峰命名法的切换;
v-bin: style 通常写成对象的形式 ;

对象写法
<div class="box1" :style="{fontSize: font_size + 'px'}">字体大小</div>
<div class="box1" :style="styleObj">字体大小</div>
数组写法
<div class="box1" :style="styleArr">字体大小</div>
     data: {font_size: 40,styleObj:{fontSize:40 + "px"},// 数组里面写样式对象styleArr:[{fontSize:40 + "px"}]},

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


条件渲染 v-if 与 v-show

在这里插入图片描述

v-if与v-show指令后面要跟表达式, 只有当表达式结果为真才显示;
v-if 是真正的节点的创建与销毁;
v-if 与 v-else-if , v-else配合使用, v-else后面不用在跟表达式,中间不能断开; v-if可以配合 template标签使用,
不会渲染template标签; tempalte标签无法添加样式;
v-show只是行内样式: display属性的显示与隐藏;


列表渲染 v-for

在这里插入图片描述

v-for 遍历, 要加 :key, key要属性绑定, 且要有唯一性;
v-for/ v-of v-for 遍历数组, (item,index) 每一项, 下标 ;
v-for 遍历对象 (value , key, index) 属性值, 属性名 , 下标;

key的作用:

虚拟dom对比, diff算法;
文本节点对比, 标签节点对比, 具有差异的节点,新的节点更新旧的节点;
:key 要有唯一性 ;
用index作为key, 打乱顺序容易错乱

1. 虚拟DOM中key的作用:key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM, 
随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下				
2.对比规则:(1).旧虚拟DOM中找到了与新虚拟DOM相同的key:①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真(2).旧虚拟DOM中未找到与新虚拟DOM相同的key
, 创建新的真实DOM,随后渲染到到页面。3. 用index作为key可能会引发的问题:1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。2. 如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题。4. 开发中如何选择key?:1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。

在这里插入图片描述
在这里插入图片描述

Vue.set()方法

Vue.set(目标, key , val) ;V要大写
Vue.set(目标, "key'", "val") 等价与 vm.$set(目标,"key","val")
添加具有响应式的数据到vm实例上, 但不能直接给vm实例 或 根数据对象(data) 添加 ;
响应式的数据, 具有 set()和get()方法;
数据代理
把vm._data.person 代理成 vm.person ;
Vue.set()方法

 vm配置对象中, this 就是 vm Vue.set(this.person,"address","曹县") 等价与Vue.set(this._data.person,"address","曹县") 

vm.$set()方法

  this.$set(this.person,"school","港湾学院") 等价与 this.$set(this._data.person,"school","港湾学院") 

在vue实例的代码中, vm实例的配置对象中, this 指向vm的,使用data中的数据,或者methods中的方法加this ;
在插值语法中, 可以直接使用 vm实例中的data中的数据,不用加this;


数组更新检测的方法
变更方法:
在vue中不能直接通过数组的下标直接赋值的方法修改数组(arr[0]=123), 因为视图不会更新;
Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。
以下方法会改变原数组;

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()
  • Vue.set()

vue监测数据
Vue监视数据的原理:

  1. vue会监视data中所有层次的数据。
  2. 如何监测对象中的数据?
    通过setter实现监视,且要在new Vue时就传入要监测的数据。
    (1).对象中后追加的属性,Vue默认不做响应式处理
    (2).如需给后添加的属性做响应式,请使用如下API:
    Vue.set(target,propertyName/index,value) 或
    this.$set(target,propertyName/index,value)
  3. 如何监测数组中的数据?
    通过包裹数组更新元素的方法实现,本质就是做了两件事:
    (1).调用原生对应的方法对数组进行更新。
    (2).重新解析模板,进而更新页面。
    4.在Vue修改数组中的某个元素一定要用如下方法:
    1.使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
    2.Vue.set() 或 vm.$set() 特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性!!!

v-model 双向数据绑定

v-model绑定的就是value值; v-model是v-bind和v-on的语法糖;
v-bind绑定value属性,v-on监听input事件;

v-model:value  简写==>  v-model
<input type="text" :value="msg" @input="msg = $event.target.value" />

收集表单数据:
若:,则v-model收集的是value值,用户输入的就是value值。
若:,则v-model收集的是value值,且要给标签配置value值。使用v-model后,可以不用再写name属性;
若:
1.没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值true/false)
2.配置input的value属性:
(1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值true/false)
(2)v-model的初始值是数组,那么收集的的就是value组成的数组
若: input的类型是 select 或 textarea , v-model 收集的也是value值;
v-model的三个修饰符:
.lazy:当前表单元素失去焦点时再收集数据;
.number:输入字符串转为有效的数字, 通常配合 number 类型的input框使用 ; (输入框通常输入的是字符串类型);
.trim:输入首尾空格过滤;

v-model原理:
在这里插入图片描述

v-model拆解:
在这里插入图片描述

v-model简化:
在这里插入图片描述

.sync修饰符: 实现子组件与父组件的双向绑定,并且可以实现子组件同步修改父组件的值。

在这里插入图片描述
在这里插入图片描述


过滤器

本质: 函数 , 依靠返回值 , 对 | 前的数据进行处理;
过滤器本质是一个函数, 可以加() 或者 不加 ; 加()后, ()内的传的实参;
过滤器的处理函数的默认实参默认就是要处理的数据, 即 | 前的数据;
过滤器可以传参, 传的参数就是额外参数, 形参默认值(rest参数);
过滤器可以串联使用, 上一个过滤器的返回值作为下一个过滤器的参数;
过滤器依靠返回值对数据进行处理,通过管道符 | 对数据进行处理 ;
要处理的数据会作为一个实参,过滤器函数处理完, 把返回值 替换掉整个 {{ }} 中的数据;

{{ 数据名  |  过滤器名 }} 
  定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。 		

语法:
1.注册过滤器:Vue.filter(name,callback) 或 new Vue{ filters:{} }
2.使用过滤器:{{ xxx | 过滤器名}} 或 v-bind:属性 = “xxx | 过滤器名”, 插值语法中使用,或者v-bind属性绑定定时用;

  备注:1.过滤器也可以接收额外参数、多个过滤器也可以串联2.并没有改变原本的数据, 是产生新的对应的数据;
全局的过滤器 Vue.filter("过滤器名", 处理函数) , 全局过滤器要写在new Vue() 之前; <h4>全局过滤器: {{msg | quanju_Filter}}</h4>Vue.filter("quanju_Filter", function(val){return val.slice(0,7)
})
局部过滤器: new Vue配置项中的  filters:{ }<h4>过滤器的时间是: {{time | filterTime}}</h4><h4>过滤器传参: {{time | filterTime("YYYY&&MM&&DD")}}</h4><h4>过滤器串联: {{time | filterTime("YYYY&&MM&&DD") | mySlice()}}</h4>filters:{// 过滤器传参, 形参默认值:  如果有传了参数就用&&, 如果没有参数就用##filterTime(val,str="YYYY##MM##DD"){return  dayjs(val). format(str)},// 过滤器串联使用mySlice(val){return val.slice(0,4)}}

内置指令

v-指令名, 可以直接使用data中的数据;

v-on:  简写 @   事件绑定
v-bind:  简写 :   属性绑定, 可以使用vue实例data中的数据, 单向的数据绑定;
v-model:value  简写 v-model   input元素双向数据绑定
v-if,  v-else-if , v-else (这个不用跟数据) ; 条件渲染   条件为真才会创建这个节点;
v-show, 条件渲染	条件为真,会把这个节点的css属性的dispaly:none和block切换;
v-for, 列表渲染; :key = "something"  ; 当v-for和 v-if 一起使用时,v-for 的优先级比    v-if 更高
v-text指令; 不解析标签, 会替代节点的原有的内容;
<h2 v-text="msg"></h2> {{ }} 是v-text的另一种写法
v-html指令; 解析标签, 会替代节点原有的内容;
<h2 v-html="msg"></h2>
v-cloak指令,不用跟值; 用于解决网速慢时页面展示出{{xxx}}的问题,先隐藏起来,
通常配合属性选择器使用, [v-cloak]{display:none;}; 当vue解析到v-cloak指令时,移除这个指令;
<h4 v-cloak>{{msg}}</h4>
v-once指令,不用跟值;只渲染一次,
1.v-once所在节点在初次动态渲染后,就视为静态内容了。
2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。
<h4 v-once>计数器:{{n}}</h4>
 v-pre指令, 不用跟值; 不解析节点的{{}}内容;
1.跳过其所在节点的编译过程。
2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。
<h4 v-pre>{{msg}}</h4>

自定义指令 direactive

v-自定义指令名 = "数据"
对底层DOM元素的操作; 自定义指令的this指向window, 方便操作 DOM 元素 (不是自定义指令中的回调函数的this指向)。
自定义指令中的函数何时调用: 1. 指令与元素成功绑定时(一上来)。2.指令所在的模板被重新解析时。

局部自定义指令
directives:{指令名 : 配置对象 }
directives:{指令名 : 回调函数 }
对象的key可以加引号也可以不加,
对象中函数的增强写法, 省略 : function

全局自定义指令 Vue.directive(“指令名”,配置对象)
Vue.directive(“指令名”,回调函数)

配置对象中的钩子函数;
bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置;
inserted:被绑定元素插入父节点时调用 ;
update:指令所在模板结构被重新解析时;
unbind:只调用一次,指令与元素解绑时调用;
*/
bind:绑定时调用。
inserted:插入到父节点时调用。
update:组件更新时调用。
componentUpdated:组件更新完成时调用。
unbind:解绑时调用。钩子函数的参数; 
el:指令所绑定的元素,可以用来直接操作 DOM; 
binding:一个对象; 主要使用binding.value:自定义指令所依赖的值; 例如: v-big='num'binding 对象具有以下常见属性:
value: 指令绑定的值。
oldValue: 指令前一个绑定的值,只在update钩子函数中可用。
expression: 绑定值的原始表达式字符串。
arg: 指令的参数。
modifiers: 一个包含修饰符键值对的对象。
name: 指令的名称。
vnode: 虚拟节点对应的 VNode 实例。
instance: 当前组件实例。
el: 指令所绑定的元素,可以通过binding.el访问。
/通过访问 binding.value 属性,可以获取指令的绑定值,例如: v-big='num'/
在很多时候,你可能想在 bind 和 update 时触发相同行为,而不关心其它的钩子。比如这样写:
Vue.directive('big', function (el, binding) {el.innerText = binding.value*10
})
自定义指令总结:
一、定义语法:(1).局部指令:directives:{指令名:配置对象}   或 directives{指令名:回调函数}(2).全局指令:Vue.directive(指令名,配置对象) 或   Vue.directive(指令名,回调函数)
二、配置对象中常用的3个回调:(1) bind:指令与元素成功绑定时调用。(2) inserted:指令所在元素被插入页面时调用。(3) update:指令所在模板结构被重新解析时
三、备注:1.指令定义时不加v-,但使用时要加v-2.指令名如果是多个单词,要使用kebab-case(短横线)命名方式,不要用camelCase(驼峰)命名。 例如: v-custom-directive3.指令的回调函数名是短横线命名的, 可以用引号引起来,解决报错;directives:{'big-number'(element,binding){ element.innerText = binding.value * 10; }}
<input type="text" v-fbig="num">Vue.directive("fbig",{bind(el,binding){el.value = binding.value*10},inserted(el,binding){el.focus()},update(el,binding){el.focus()el.value = binding.value*10}
})

生命周期

生命周期:一些特殊的回调函数;
1.又名:生命周期回调函数、生命周期函数、生命周期钩子。
2.是什么:Vue在关键时刻调用的一些特殊名称的函数。
3.生命周期函数的名字不可更改。
4.生命周期函数中的this指向是vm 或 组件实例对象。

Vue的生命周期
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


vm.$destroy() 方法 ;完全销毁一个实例。清理它与其它实例的连接,解绑它的全部指令及自定义事件监听器; 触发beforeDestroy 和 destroyed 的钩子。
初始化页面(第一次进入页面)会触发前四个钩子;

常用的生命周期钩子:1.created : 请求数据; (调用methdos中的函数, methods中定义函数,请求数据);2.mounted: 操作DOM;发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。3.beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。
关于销毁Vue实例1.销毁后借助Vue开发者工具看不到任何信息。2.销毁后自定义事件会失效,但原生DOM事件依然有效。 3.一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了。(解绑的是自定义事件, 原生DOM事件不影响, 该钩子函数可以改变数据,但不会在视图更新了)

函数作用域之间有作用域, 无法访问到其他函数内的变量;
解决方法: 追加vm的属性; this.XXX = “变量” ; 这里的this是vm实例对象;


组件 component

在这里插入图片描述

组件 : 为了复用;
非单文件组件 : 包含多个组件。
单文件组件: 只包含一个组件(.vue文件的组件)。
全局组件 Vue.component(“标签名”, 组件构造器)
局部组件 components: { } 配置项 Vue.extend()
组件构造器,配置项内容和vm一样, 但是不要写el配置项,data要写成一个函数,返回一个对象;
template模板内要有一个根元素包裹, 换行可以使用模板字符串 ;

Vue中使用组件的三大步骤:一、创建组件(定义组件)二、注册组件 (components配置项中注册)三、使用组件(写组件标签)一、如何创建一个组件? 	使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别; 	区别如下:1.el不要写,为什么? ——— 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器。2.data必须写成函数,返回一个对象; 为什么? ———— 避免组件被复用时,数据存在引用关系, 组件各自维护独立的数据拷贝。 	备注:使用template可以配置组件结构, 要有根元素包裹, 换行可以使用模板字符串; 二、如何注册组件?1.局部注册:靠new Vue的时候传入components配置项, components:{"组件标签名": 组件构造器名};  //对象的key与value ; 2.全局注册:靠Vue.component('组件标签名',组件构造器名)三、编写组件标签: 		<组件标签名></组件标签名>  //单词首字母大写
组件名注意点: 
几个注意点:1.关于组件名:一个单词组成:第一种写法(首字母小写):school第二种写法(首字母大写):School多个单词组成:第一种写法(kebab-case命名):全小写; my-school , 对象的key值加引号; 第二种写法(CamelCase命名):单词首字母大写;  MySchool (需要Vue脚手架支持)备注:(1).组件名尽可能回避HTML中已有的元素名称,例如:h2都不行。(2).组件构造器配置项中, 可以使用name配置项,指定组件在开发者工具中呈现的名字。2.关于组件标签使用:第一种写法:<school></school>第二种写法:闭合标签 <school/>(需要Vue脚手架支持, 不用使用脚手架时,<school/>会导致后续组件不能渲染。)3.组件构造器的简写方式:const school = Vue.extend(options) 可简写为:const school = options ; 省略了 Vue.extend() ,直接{ }

配置项中, name 属性 , 可以指定组件在开发者工具中呈现的名字;
vm是根组件; vm管理app组件, app组件管理所有的组件;
main.js文件是入口文件,挂载App组件;
创建App标签,render函数的 createElement(“标签名”,“内容”)函数

组件的嵌套: 父子组件关系;
子组件定义之后, 子组件在父组件components:{ }配置项中注册;
在父组件的template模板中使用子组件标签;
子组件的定义要在父组件之前; 否则父子间读取不到;
定义 => 引入 => 注册 => 使用

VueComponent 组件实例对象

组件的本质: 一个构造函数 ==》VueComponent的构造函数,调用Vue.extend()方法生成的。
每次调用Vue.extend,返回的都是一个全新的VueComponent!!!!

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

组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods以及生命周期钩子等。
el除外,因为最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器。
一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝; 防止数据的污染。

Vue与Vuecomponent的关系

在这里插入图片描述

  1. 一个重要的内置关系:VueComponent.prototype.proto === Vue.prototype
  2. 为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。 (组件可以访问vue上的数据,方法。。。)
  3. vm对象和组件对象的关系: vm的原型对象 === 组件对象的原型对象的原型对象。

**单文件组件: 以.vue结尾的文件; **

包含html,js,css;一个文件就是一个组件;
export default {组件的定义} 暴露出去;Vue.extend()省略; 但仍调用Vue.extend()方法,生成一个全新的组件实例对象;
组件的name属性, vue开发者工具查看到的组件名,没有配置name,则是组件发文件名;
单文件组件文件的命名, 推荐大驼峰命名;同组件标签命名一致;
template标签内要有一个根组件div包含;
mian.js 入口文件,import App from ‘./App.vue’,创建vue实例;
index.html 首页;
App.vue文件, 汇总组件, 管理组件,引入汇总组件,在配置项componets中注册,并在模板中使用, 由vm管理 App.vue文件;一人之上,万人之下;
impiort 组件名 from “XXX路径” ;
.vue .js 文件名后缀名可以省略;

组件分类: 页面组件和复用组件。

  • 页面组件存放在 src/views文件夹=>配合路由, 展示页面;
  • 复用组件存放在src/components文件夹=>封装复用, 展示数据;

省略
.vue文件/.js文件 引入 可以省略文件名的后缀 ;
引入index.js文件可以省略, 直接引用到文件夹路径 ;


脚手架 Vue-cli

文档: https://cli.vuejs.org/zh/guide/

第一次全局安装@vue/cli。 npm install -g @vue/cli
切换到你要创建项目的目录,然后使用命令创建项目 。 vue create 项目文件名
进入所在的项目文件夹, 然后启动项目 npm run serve/dev

切换淘宝镜像

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

Vue 脚手架隐藏了所有 webpack 相关的配置,若想查看具体的 webpakc 配置,
vue inspect > output.js

脚手架文件结构├── 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.config.js配置文件  (项目的根目录下)

使用vue.config.js可以对脚手架进行个性化定制,详情见:https://cli.vuejs.org/zh/config

配置项 :   lintOnSave: false; // 关闭语法检查

render函数 渲染函数
关于不同版本的Vue:

	1.vue.js与vue.runtime.xxx.js的区别:(1).vue.js是完整版的Vue,包含:核心功能+模板解析器。(2).vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。2.因为vue.runtime.xxx.js没有模板解析器,

所以不能使用template配置项,需要使用
render函数接收到的createElement函数去指定具体内容。

  render: h => h(App) 函数 ; render 函数跟template配置项 一样都是创建 html 模板的; 必须要有返回值, vue自动调用; 参数是createElement函数, 这个函数第一个参数是创建的标签名, 第二个参数是标签的内容; render:function(createElement){return createElement("标签名","内容")} ;h代替了createElement函数;render:function(h) {return h(App)  //App组件, App是一个变量,已将引入了, App组件内有标签内容; },箭头函数的简写 :render: h => h(App) 
小计简写:
箭头函数; ()=>{}
对象的简写; 属性的简写,方法的简写;对象中方法通常是匿名函数;

ref属性

作用: 打标记 ref = "xxx"  
this.$refs.xxx 应用在html标签上获取的是真实DOM元素; 应用在组件标签上是该组件实例对象(vc)
1. 被用来给元素或子组件注册引用信息(id的替代者)
2. 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
3. 使用方式:1. 打标识:<h1 ref="xxx">.....</h1><School ref="xxx"></School>2. 获取:this.$refs.xxx

props 自定义属性:

功能:让组件接收外部传过来的数据 ;
组件标签传值, props配置项接收, 然后使用传过来的值;
同过v-bind 属性绑定,可以使用组件内的数据; 可以传数据,也可以传递函数;

 <Student :id="name"></Student>一个等号, 赋值操作, 等号右边赋值给左边; 把右边name的值赋值给左边的id, id属性绑定可以使用组件data中的数据了;在子组件中props配置项中接收id通常左右两边都是一样的;  <Student :name="name" />

props ==>property 属性 ;
v-bind 属性绑定; v-bind:xxx = “表达式” ;
props: [ ] 或 { } ;
props 只可读,不能修改,数据单向流;
type 限制传入的数据的类型;
default 不传值, 会显示默认值 ;
required 和 default 冲突 ;
如果传入的数据是对象或数组类型;
default必须是一个函数; 返回 默认对象或数组 return { } 或 [ ] ;

 default() {return { message: 'hello' }// orreturn  ["hello"]}

1. 功能:让组件接收外部传过来的数据2. 传递数据: 在组件标签内写属性 <Demo name="xxx"/>3. 接收数据: props 配置项接受数据1. 第一种方式:数组形式(只接收):props:['name']  // 加引号接受, 接收到是传过来的数据2. 第二种方式:对象形式(限制类型):props:{name:String}3. 第三种方式:对象形式(限制类型、限制必要性、指定默认值):props:{name:{type:String, //类型required:true, //必传项default:'张三' //默认值}}> 备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。 data() {return {msg: this.message, // props中的message; myAge:this.age     // props中的age ; };},
组件中的this是组件实例对象; vc

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


通过props实现子父通信 :

通过自定义属性传递方法
通信: 数据/函数等在各个组件实例对象(vc)上的传递;
父组件先在methods中定义一个函数 ;
把这个函数通过v-bind 在 子组件标签上传递给子组件;
子组件在props配置项中接收;
然后子组件调用父组件传递过来的函数,把要传递的数据作为该函数的实参传递过去(也可以不传数据,仅调用父组件传递过来的函数);
父组件接收传递过来的参数,然后使用;

父组件  //App组件methods: {
// 父组件先在methods中定义一个函数 ;addTodo(xxx){ //形参
// 父组件接受子组件传递过来的数据作为该函数的参数, 然后使用this.todos.unshift(xxx)}},<MyHearder :addTodo="addTodo"/>  // 在子组件标签上通过v-bind把这个函数传给子组件;  //不加()子组件   // Myhearder组件props: ["addTodo"],  //接受传递过来的函数  // [ ] 中加引号; this.addTodo(xxx);  // 子组件调用这个函数, 把数据作为参数传递给父组件

在这里插入图片描述

v-bind: ==> : 属性绑定, 绑定之后可以使用组件/vm上的数据,方法…
v-bind 可以传数据, 也可以传函数, 等等;
函数的形参和实参是一一对应的,函数的调用是实参,函数定义时的参数是形参,函数内部使用的是形参,实参是要确定传过去的值;
实参和形参可以名称不一致, 但一定要一一对应;
模板中,可以使用方法(), 显示方法的最终处理结果return.


mixin 混入

功能:可以把多个组件共用的配置项提取成一个混入对象(单独的js文件,暴露出去), 然后引入使用; (复用相同的配置项)
局部混入 引入混入文件, 组件中 mixins:[ ];
全局混入 main.js中 引入混入文件, Vue.mixin( ),vm和所有的vc都可以使用;
组件和混入的配置一样时, 以组件的为主;

 mixin(混入)1. 功能:可以把多个组件共用的配置提取成一个混入对象, 然后复用2. 使用方式:第一步: 定义混合, 抽离成单独的js文件,暴露出去:{data(){....},methods:{....}....}第二步: 引入混入文件, 使用混入:    全局混入:Vue.mixin(xxx)​	局部混入:mixins:['xxx']

插件(plugins) 与 Vue.use() 方法

Vue.use()的原理: 调⽤插件的 install()⽅法 ;
Vue 插件是一个包含 install 方法的对象
通过 install 方法给 Vue 或 Vue 实例添加方法, 定义全局指令等
引入插件, 使用插件

1. 功能:用于增强Vue2. 本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。3. 定义插件:抽离成一个单独的js文件, plugins.js 暴露出去 ; 对象.install = function (Vue, options) {// 1. 添加全局过滤器Vue.filter(....)// 2. 添加全局指令Vue.directive(....)// 3. 配置全局混入(合)Vue.mixin(....)// 4. 添加实例方法Vue.prototype.$myMethod = function () {...}Vue.prototype.$myProperty = xxxx}4. main.js文件 引入插件 import {xxx}  from  "路径"  5. main.js文件 使用插件:Vue.use( )

scope

App.vue文件不加scoped, 可以给组件添加统一的样式; 
scoped作用
lang
/deep/
添加 scoped  
<style scoped>
作用:让样式只在当前组件生效,使组件的样式不相互污染。
原理: 自动给标签添加一个唯一标识(hash值), data-v-xxx的属性,配合属性选择器给元素添加样式; 
<div data-v-3375b0b8 data-v-7ba5bd90 class="school">
.school[data-v-3375b0b8] {XXX:XXX;}
<style lang="">  
编译语言,less/scss/css; 不加lang=""默认css ;
深度穿透;深度作用选择器; 
>>>   /deep/  ::v-deep   :deep()

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


封装组件:

组件化编码流程:(1).拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。(2).实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:1).一个组件在用:放在组件自身即可。2). 一些组件在用:放在他们共同的父组件上(状态提升)。(3).实现交互:从绑定事件开始。
props适用于:(1).父组件 ==> 子组件 通信(2).子组件 ==> 父组件 通信(要求父先给子一个函数,通过调用函数传参的方法,进行通信)
使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的!
props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做。

webStorage 浏览器端的本地存储

存的都是JSON格式的字符串对象;
JSON.stringify()
JSON.parse()

存储内容大小一般支持5MB左右(不同浏览器可能还不一样) 
浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。 
相关API:xxxxxStorage.setItem('key', 'value');  该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。 xxxxxStorage.getItem('key'); 该方法接受一个键名作为参数,返回键名对应的值。xxxxxStorage.removeItem('key');  该方法接受一个键名作为参数,并把该键名从存储中删除。xxxxxStorage.clear();该方法会清空存储中的所有数据。  备注: SessionStorage存储的内容会随着浏览器窗口关闭而消失。 LocalStorage存储的内容,需要手动清除才会消失。 xxxxxStorage.getItem(xxx)如果xxx对应的value获取不到,那么getItem的返回值是nullJSON.parse(null)的结果依然是null

在这里插入图片描述

/ 深拷贝 /
JSON.stringify()方法  // stringify 字符串化; 将对象转成JSON对象,JSON对象的key和value都是字符串; {"a":"hello","b":"你好"};对象的key加不加引号都可以的;
JSON.parse()方法把JSON字符串解析成对象;
toString()方法转成字符串的方法

$emit 自定义事件

在子组件标签上绑定自定义事件;
给子组件的实例对象vc绑定一个自定义的事件, 通过触发这个自定义事件, 传数据来进行通信;
事件处理函数(是一个回调函数) ,写在父组件的methods中;
@自定义事件 = “事件处理函数”
事件处理函数写在父组件中;
自定义事件写在父组件中的子组件标签上;
自定义事件在子组件中触发 this.$emit;
自定义事件被触发, 对应的事件处理函数就会执行;
自定义事件可以和事件处理函数名成可以一致;
事件处理函数在父组件中定义, 来处理子组件传递过来的值 ;
在子组件中通过this.$emit() 来触发自定义事件, 并传递值过去;
this.$emit("自定义事件", 数据); // 自定义事件名要加引号;
自定义事件也可以使用事件修饰符;
解绑自定义事件, 解绑指定的 this.$off("自定义函数名");
解绑多个放一个数组中 this.$off(["自定义1","自定义2",.....]); 解绑所有 this.$off() ;
组件也可以绑定原生事件, 通过native修饰符 ; < Demo @click.native = “事件处理函数” / >

1. 一种组件间通信的方式,适用于:子组件 ===> 父组件
2. 使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件, 事件的回调函数(事件处理函数)A的methods中。
3. 绑定自定义事件:
(1)第一种方式,在父组件中:<Demo @atguigu="test"/><Demo v-on:atguigu="test"/>
(2)第二种方式,在父组件中:通过ref绑定自定义事件; 
this.$refs.xxx 获得的是该组件的实例对象vc, 然后给他绑定自定义事件,通过$on("自定义事件",回调函数) 
// 这个回调函数要么写成箭头函数, 要不提前在methods中写好,然后调用这个函数 this.函数名; 
因为写成普通函数, this会指向子组件的实例对象上;
<Demo ref="demo"/>
......
mounted(){this.$refs.demo.$on('atguigu',this.test)
}
(3)父组件绑定: 若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法绑定事件。
<Demo @atguigu.once="test"/>this.$refs.demo.$once('atguigu',this.test)
(4)子组件触发: 触发自定义事件:this.$emit('atguigu',数据)	
(5)父组件: 解绑自定义事件this.$off('atguigu') ; 
(6)要在beforeDestroy函数中解绑;
(7)组件上也可以绑定原生DOM事件(click,等等),需要使用native修饰符。
(8)注意:通过this.$refs.xxx.$on('atguigu',回调)绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题!

在这里插入图片描述

绑定自定义事件 $on("事件名",事件处理函数) ;
解绑自定义事件$off();
绑定一次性的自定义事件 $once("事件名",处理函数);
子组件触发父组件绑定的自定义事件, $emit("自定义事件名",参数)
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

emits节点:
用于声明由组件触发的自定义事件。
vue3中配置象, emits: [ ‘xxx’ ], 字符串数组;
可以理解为父组件把自定义事件传递给子组件, 子组件通过emits来接收;
vue2中emits节点可以省略, 直接通过$emit来触发自定义事件;


全局事件总线 (GlobalEventBus)

一种组件间通信的方式,适用于任意组件间通信。

this.$bus.$on()
this.$bus.$emit()
this.$bus.$off()
全局事件总线说到底就是个对象,我们通常就是用vm对象作为全局事件总线使用,把vm对象添加到Vue原型对象, 就形成全局事件总线(vm);
Vue.prototype.$bus = this 

想要成为事件总线的条件:

1、所有的组件对象必须都能看得到这个总线对象,因此我们把这个对象放在了Vue原型; 
2、这个事件总线对象必须能调用$on,$emit,$off方法; 
3、总线对象必须是Vue的实例化对象或者是组件对象;

安装全局事件总线:

位置: 在入口文件mian.js中的创建vm实例的beforCreated钩子中 ;
new Vue({......beforeCreate() {Vue.prototype.$bus = this, / this就是vm实例,挂载到vue原型上, 每个组件通过this.$bus都也以使用 ///安装全局事件总线,$bus就是当前应用的vm ; 给Vue原型上添加了一个$bus公共项, 都去通过这个公共项进行通信;},......
}) 
使用事件总线:
1.接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。
methods(){demo(){......} // 事件的处理函数
}
......
mounted() {this.$bus.$on('xxxx',this.demo) // this.$bus.$on('xxx',()=>{}) 处理函数为箭头函数
}
2.传递数据的组件 : 
通过触发 this.$bus.$emit('xxxx',数据)
3.最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。
beforeDestroy() {this.$bus.$off('xxxx')
}
要数据的组件 ==> 绑定事件   this.$bus.$on("自定义事件名",处理函数); 箭头函数或者提前定义好 this调用; 
传数据的组件 ==> 触发事件   this.$bus.$emit("自定义事件名",参数) ; 
要数据的组件 ==> 解绑事件   this.$bus.$off("自定义事件名")

在这里插入图片描述

使用的时候引入Bus; import Bus from ‘@/utils/EventBus.js’
在这里插入图片描述


消息订阅与发布 (pubsub)

一种组件间通信的方式,适用于任意组件间通信。 依靠 pubsub-js包,pubsub对象; 
subscribe订阅消息; publish 发布消息; unsubscribe 取消订阅; 使用步骤:
安装pubsub:npm i pubsub-js -S
引入: import pubsub from 'pubsub-js'
接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。订阅消息的组件定义事件,来接收数据
methods(){// 这个处理函数,两个参数,第一个是自定义事件的名称, 第二个才是传递过来的数据;  demo(name,data){......}
}
......
mounted() {//订阅消息 this.pid = pubsub.subscribe('事件名',this.demo) //或箭头函数   // this.pid 给vc上追加属性, 要取消订阅this.pid; 类似于定时器的取消;
}
提供数据的组件发布数据;
提供数据:pubsub.publish('事件名',数据)  // 发布消息
在订阅消息的组件取消订阅; 
最好在beforeDestroy钩子中,用PubSub.unsubscribe(this.pid) 去取消订阅。

provide&inject 依赖注入

provide/inject 提供/注入
适用于多层 祖先/父组件 向子/孙组件通信;
在这里插入图片描述

provide写法(提供):1. 使用函数的形式,返回一个对象,可以访问到 this,可以使用data中的数据;provide(){return{xxx:'xxx', // 基本数据类型=>非响应式的;xxx:{xxx..} // 引用数据类型=>响应式的;} }
2. 对象形式,不能使用this,无法传递data中的数据;
provide:{xxx:'xxx", 
}
基本数据类型响应式方法:
import {computed} from 'vue'provide() {return {// c:this.count , count是基本数据类型,不是响应式的;c: computed(()=>this.count)/ vue3中 通过computed()方法, 通过匿名函数,return出去 /},* / 后代组件使用该值时, .value 使用, 因为该数据变成响应式的了; / {{c.value}}
inject(注入)写法:
1. 数组写法
inject:['xx','xxx']
2. 对象写法
inject:{//注入别名, localName是在本组件的属性名;  provideName是父组件提供的属性;localName:{from: 'provideName'}  简写: localName: 'provideName',//默认值,防止父组件没有提供值过来;meg:{ default:'默认值xxx' },
}

Vue.nextTick

  • 在下一次 DOM 更新循环结束之后执行延迟回调。
  • 在修改数据之后立即使用这个方法,获取更新后的DOM。
  • 想要立即拿到更新后的DOM,要在nextTick的回调函数中去获取更新后的Dom的值。
  • 语法: this.$nextTick(箭头函数)
  • 作用:在DOM更新完成后,在这个方法中做某件事;
  • 原因:data改变是响应式的,但是更新dom是异步的;
  • 是一个微任务;
    vue 异步更新队列: vue是依靠数据驱动视图更新的,该更新的过程是异步的。即:当侦听到你的数据发生变化时,Vue将开启一个异步更新队列,等这个队列中所有数据变化完成之后,再统一进行更新视图(DOM)。
    vue在修改数据后,视图(DOM)不会立刻更新,要等到所有数据变化完成之后,再统一进行视图更新。
    所以,在修改数据更新立马读取DOM是获取不到新数据的,
    可以在修改数据之后, 使用这个nextTick方法, 在指定的函数里获取更新后的DOM。
  • 等待当前 DOM 更新队列中的所有同步和异步更新完成后执行回调函数。
  • 用于确保在 DOM 更新之后执行操作,例如获取更新后的 DOM 元素。

nextTick 是 Vue 提供的一个方法,用于在 DOM 更新之后执行回调函数。
它的作用是等待当前 DOM更新队列中的所有同步和异步更新完成后,再执行传入的回调函数。
在 Vue 中,当我们修改了组件的数据或者调用了一些可能会触发 DOM更新的方法时,这些更新不是立即生效的,而是会被加入到一个队列中,在下一次事件循环中进行处理。
也就是说,如果我们需要在某个 DOM 更新完成后执行一些操作(例如获取更新后的 DOM 元素),直接在更新代码之后立即执行是不准确的。
这时候就可以使用 nextTick方法来确保在 DOM 更新完成后再执行相应的操作。
需要注意的是,nextTick() 方法是异步执行的,也就是说回调函数会在下一个事件循环才被调用,因此应该将相应的操作放在回调函数中以确保在正确的时机执行。


插槽 slot

封装一个组件会多次复用,但是不同场景下又有少部分结构数据会发生变化,(当然可以用不同的子组件)
那么就得通过父组件告诉子组件结构的变化的内容是什么,此时就要用到这个插槽slot;

子组件当中<slot></sloat> 其实就是占位用的,让父组件标签内给它填充内容,可以带标签; 
父组件标签内写内容, 子组件对应的位置显示内容;  
作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式; 
插槽通常与template标签配合使用,  emplate不会被解析;  
插槽就是预留的一个位置, 把组件标签内的内容解析完之后放到指定位置;
  • 匿名(默认)插槽
  • 具名插槽
  • 作用域插槽

匿名插槽

v-slot:default <==> #default
没有名字的插槽

  1. 父组件标签内有内容, 子组件对应的内没有默认值, 就会显示该内容;
  2. 父组件标签内没有内容, 子组件默认值 会显示默认值;
  3. 父组件标签内有内容, 子组件默认值, 插槽的默认值会被覆盖;

父组件中:

<Category><div>html结构</div>
</Category>

子组件中:

<template><div>               <slot>插槽默认内容...</slot>  // 会显示 <div>html结构</div>, (后备内容)</div>
</template>

具名插槽

有名字的插槽, 父组件标签内容放到子组件<slot name="xxx">的name相对应的位置; 
语法: 	<p  v-slot:xxx> hello vue </p><slot  name="xxx"> </slot>
插槽的简写: v-slot:xxx  <==> #xxx  <==> slot="xxx" 只能添加在 <template>标签上;
插槽的默认值: v-slot:default  <==> #default ;<slot></slot> <==> <slot  name="default"></slot> template标签的v-slot:xxx == #xxx ; v-slot:xxx 只能配合template标签使用;
父组件中:<Category><template v-slot:header> <div>header位置</div></template>    <template #center><div>center位置</div></template><template slot="footer"><div>footer位置</div></template></Category>子组件中:<template><div><!-- 定义插槽 --><slot name="header"></slot>     // <div>header位置</div><slot name="center">默认</slot> // <div>center位置</div><slot name="footer">默认</slot> // <div>footer位置</div></div></template>

作用域插槽
在这里插入图片描述

作用域插槽:类似于子传父通信, 把子组件的数据通过属性,包装成一个对象(通常命名scope),传递给父组件使用;
默认不能有其它插槽,如果要有其它插槽,必须设置为具名插槽;

理解:数据在子组件,但根据数据生成的结构需要父组件来决定。
子组件slot标签, 通过添加属性的方式传值;
所有添加的属性,会收集到一个对象中(通常命名scope), 传递给父组件;
父组件标签中的tempalate通过 #插槽名="scope"接收, 匿名插槽名为default;

子组件:<template><div><slot msg='四大名著' :shuName='books'></slot> // 默认插槽<slot name="game" id='手游' :games='games'></slot> // 具名插槽</div>
</template>
<script>
export default {name: 'slotComponent'  ,props: {},data () {return {books:['西游记','水浒传','三国演义','红楼梦'],games:['原神','王者荣耀','穿越火线']}}
}
</script>
父组件:<MySlot><template v-slot:default="scope"> // 匿名插槽 #default, 传递过来的对象通常命名为scope;<div>{{scope}}</div>  //  { "msg": "四大名著", "shuName": [ "西游记", "水浒传", "三国演义", "红楼梦" ] } <div>{{scope.msg}}</div> // 四大名著</template><template #game="{games:youxi}"> // v-slot简写, scope对象的解构并重命名<div>{{youxi}}</div> // [ "原神", "王者荣耀", "穿越火线" ]</template></MySlot>

slot属性弃用,具名插槽通过指令参数v-slot:插槽名的形式传入,可以简化为#插槽名;
slot-scope属性弃用,作用域插槽通过v-slot:xxx="slotProps"的slotProps来获取子组件传出的属性对象;
v-slot属性只能在template上使用


Proxy 代理服务器

同源策略: 协议,主机名(域名,IP), 端口号;三者相同; 跨域: 违反同源策略, 三者有一个不相同就会跨域,
不是同源的脚本不能操作其他源下面的对象;

解决跨域的方法:

  • cros+后端配置;
  • JSONP, 利用了script标签的src属性不受同源策略影响的特性, 但是只能get请求;
  • proxy, 配置代理服务器, (ngix服务器配置, vue-cli配置) ;
    在这里插入图片描述
对脚手架进行定制, 在vue.config.js中配置; 
方法一: 在vue.config.js中添加如下配置:
devServer:{proxy:"http://localhost:5000"
}
说明:1.优点:配置简单,请求资源时直接发给前端(8080)即可。2.缺点:不能配置多个代理,不能灵活的控制请求是否走代理。3.工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端本地的资源)方法二: 编写vue.config.js配置具体代理规则:
module.exports = {devServer: {proxy: {'/api1': {// 匹配所有以 '/api1'开头的请求路径,  请求前缀,在端口号后面;target: 'http://localhost:5000',// 代理服务器的请求地址changeOrigin: true,pathRewrite: {'^/api1': ''}   // 键值对形式,正则表达式; 代理服务器对请求路径进行重定向以匹配到正确的请求地址},'/api2': {// 匹配所有以 '/api2'开头的请求路径;target: 'http://localhost:5001',// 代理目标的基础路径changeOrigin: true,	// 伪装pathRewrite: {'^/api2': ''}  // 重新匹配请求路径}}}
}changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080changeOrigin默认值为true;说明:1.优点:可以配置多个代理,且可以灵活的控制请求是否走代理。2.缺点:配置略微繁琐,请求资源时必须加前缀。

Vuex

集中式状态(数据)管理工具,管理共享的数据,这个数据是响应式的,多组件共享;
vuex中的数据储存在内存中,刷新消失,单向数据流;
在Vue中实现集中式状态(数据)管理的一个Vue插件;
对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。
核心在于创建的store对象, 全局都能访问到store对象; $store对象管理数据, 不能直接修改$store中的state数据,vue开发者工具检测不到;

-----------------------------------

state 存储数据 ==> data ;
actions 处理异步任务: 调用commit方法 ; 可以获取context上下文对象,mini版的$store;
mutations 处理state中的数据, 处理同步任务:修改state中的数据必须通过mutations, 可以获取state对象;
getters ==>计算属性,依靠返回值:对state中的数据进行处理,然后使用,不会修改state中的数据, $store.getters.计算属性名; 可以获取state对象;
moduls 模块化的vuex:可以让每一个模块拥有自己的state、mutations、actions、getters,使得结构非常清晰,方便管理。

搭建vuex环境
npm  i  Vuex -S ;
创建文件:src/store/index.js  导出store对象;  // 引入文件时, 引入index.js文件, 可以省略/index.js     //  import store from "./store"
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)   // vue-cli 解析时,会先解析import 引入的东西; //准备actions对象——响应组件中用户的动作
const actions = {}
//准备mutations对象——修改state中的数据
const mutations = {}
//准备state对象——保存具体的数据
const state = {}
//准备getters对象, 列斯与计算属性, 对state中的数据进行加工然后使用;
const getters = {}//创建并暴露store
export default new Vuex.Store({    // 通过new Vux.Store对象创建的storeactions,mutations,state,getters
})
在main.js入口文件中创建vm时传入store配置项  // 全局都可以访问store ,有了$store对象;
//引入store
import store from './store'
......//创建vm
new Vue({el:'#app',render: h => h(App),store
})

基本使用
组件读取vux中的数据 : $store.state.数据名;
组件修改vuex中的数据: $store.dispatch(‘actions中的方法名’,数据) 或 $store.commit(‘mutations中的方法名’,数据);

备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写dispatch,直接编写commit;
mutation中方法(函数)和commit的方法, 方法名通常大写;
context上下文对象, 迷你版的$store,actions中用到 ;
actions 中的方法,接收到两个参数:
第一个是context对象,这是一个min版的$store对象, 第二个参数是,传递过来的数据;
mutations 中的的方法,修改state中的数据, 接收到两个参数:
第一个是 state 对象,可以拿到state中的数据 ;第二个参数是 传递过来的数据;
getters 中的方法中的参数可以接收到state对象:对state中的数据经过加工后再使用, this.$store.getters.getter中的数据
state, 保存的数据; 读取this.$store.state.数据名

// 处理异步任务,业务逻辑,按顺序处理
const actions = {jia(context,value){context.commit('JIA',value)  // commit的方法名,通常大写},
}// 处理同步任务,可以直接修改state中的数据 , mutations中的方法名,通常大写
const mutations = {JIA(state,value){state.sum += value}
}//vux中的数据
const state = {sum:0
}
// 对state中的数据进一步加工
const getters = {bigSum(state){return state.sum * 10}
}

vuex的四个辅助函数函数;

// 引入vuex的辅助函数
import { mapActions, mapGetters, mapState, mapMutations } from "vuex";
...mapState和...mapGetters在computed中映射并解构出来值,然后使用;
...mapMutations和...mapActions在methods中映射并解构出来方法,然后调用方法;
  • …展开运算符
  • 数组写法,对象写法,普通调用方法;
  • 配合模块化,开启命名空间后,需要加模块化的名字;
  • 配合模块化,普通调用的方法;

mapState方法:用于帮助我们映射state中的数据为计算属性; 在computed配置项中;

 <p>学校名:{{ xuexiaoName }}</p>
computed: { // 依靠return返回值;//借助mapState生成计算属性:schoolName、subject(对象写法)...mapState({xuexiaoName:'schoolName',subject:'subject'}), // 对象解构的重命名, 把schoolName重命名为xuexiaoName.//借助mapState生成计算属性:sum、school、subject(数组写法)...mapState(['sum','schoolName','subject']),//等价与   xuexiaoName(){return this.$store.state.schoolName }, ....                          
},

mapGetters方法:用于帮助我们映射getters中的数据为计算属性; 在computed配置项中;

<h4>计算属性:{{ bigSum }}</h4>computed: { // 计算属性依靠返回值;//借助mapGetters生成计算属性:bigSum(对象写法)...mapGetters({bigSum:'bigSum'}),//借助mapGetters生成计算属性:bigSum(数组写法)...mapGetters(['bigSum'])//等价与bigSum() {return this.$store.getters.bigSum},...
},

mapActions方法:用于映射生成与actions对话的方法,即:包含$store.dispatch(xxx)的函数 ; 在methods配置项中;

1. 在methods中解构出来action中方法,在模板中直接调用这个方法并传参; <button @click="incrementOdd(n)">++</button><button @click="jiaOdd(n)">++</button>
methods:{ //methods配置项;//靠mapActions生成:incrementOdd、incrementWait(对象形式)...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})//对象的解构并命名; methods配置项中的方法名=>incrementOdd;  action中的方法名=>jiaOdd//靠mapActions生成:incrementOdd、incrementWait(数组形式)...mapActions(['jiaOdd','jiaWait'])}
2. 绑定事件时不传参, 在事件的处理函数中,调用映射过来的函数时传参;
<button @click="evenJia">++</button>
methods:{// 解构出来action中的方法, 调用传参...mapActions(['jiaOdd'])evenJia(){this.jiaOdd(this.n)},
}3.//等价与
methods:{incrementOdd() {// 通过dispatch调用action中的方法this.$store.dispatch("jiaOdd", this.n);},  
}

mapMutations方法:通过导入的mapMutations函数,将需要的mutations方法,映射为当前组件的methods的方法,然后调用这个方法,即:包含$store.commit(xxx)的函数, 在methods配置项中;

1.	<button @click="increment(n)">++</button>
<button @click="increment2">++</button>
methods:{ // methods配置项中//靠mapActions生成:increment、decrement(对象形式)...mapMutations({increment:'JIA',decrement:'JIAN'}), //increment: method配置项中的方法名 ; JIA: mutation中的方法名  //靠mapMutations生成:JIA、JIAN(对象形式)...mapMutations(['JIA','JIAN']),// 调用JIA方法并传参increment2(){this.JIA(n)    }}- <button @click="evenJia">++</button>
methods:{// 解构出来, 调用传参...mapActions(['increase'])evenJia(){this.increase(this.n)}
}- methods:{//等价与increment() {this.$store.commit("JIA", this.n);},
}

获取state中的数据: 1. this.$store.state.数据名 2. …mapState
获取getters中的数据: 1. this.$store.getters.计算属性名 2. …mapGetters
触发actions中的方法: 1. this.$store.dispatch("方法名", 参数) ; 2. …mapActions
触发mutations中的方法: 1. this.$store.commit("方法名",参数) 2. …mapMutations
mapActions与mapMutations使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象evnet。

模块化 + 命名空间

模块化vuex,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。
作用:让代码更好维护,让多种数据分类更加明确,每个组件各维护对应的数据;
namespaced:true; 开启命名空间
…mapXXX(“模块化名” , [ “xxx”] )
…mapXXX( “模块化名”, { “xxx”: “xxx” } )

创建关于count组件的模块
// counAbout
export default{namespaced:true,//开启命名空间state:{...},mutations: { ... },actions: { ... },getters: {
...}
}
创建关于person组件的模块
// personAbout 
export default{namespaced:true,//开启命名空间state:{...},mutations: { ... },actions: { ... },getters: {
...}
}
src/store/index.js文件
// 创建store对象
import Vue from "vue";
import Vuex from "vuex"
// 使用vuex
Vue.use(Vuex)
// 引入模块化的vuex
import countAbout from "./module/count.js";
import personAbout from "./module/person";
const store = new Vuex.Store({// 模块化modules: {countAbout,personAbout}
})
开启命名空间后,组件中读取state数据:
this.$store.state.moduleName.value
//方式一:自己直接读取; 从对应的模块中读取state中的数据this.$store.state.personAbout.xxx;
//方式二:借助mapState读取:
computed: {...mapState('countAbout',['sum','school','subject']), // 在computed中从对应的模块中读取state中的数据
}
开启命名空间后,组件中读取getters数据:
this.$store.getters['moduleName/getterName']
//方式一:自己直接读取 ; 从对应的模块读取getters中的数据
this.$store.getters['personAbout/xxx']
//方式二:借助mapGetters读取:
computed: {...mapGetters('countAbout',['bigSum']) // 在computed中从对应的模块中读取getters中的数据
}
开启命名空间后,组件中调用dispatch触发actions中的方法: this.$store.dispatch('moduleName/actionName',payload)
//方式一:自己直接dispatch; 明确从对应的模块中读取actions中方法;
this.$store.dispatch('personAbout/addPersonWang',person)
//方式二:借助mapActions:methods: {...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'}) // 在methods中从对应的模块中读取actions中方法;...mapActions('moduleName', ['actionName'])
}
开启命名空间后,组件中调用commit中的方法 : 
this.$store.commit('moduleName/mutationName')
//方式一:自己直接commit; 明确从对应的模块中读取mutations中方法;
this.$store.commit('personAbout/ADD_PERSON',person)
//方式二:借助mapMutations:methods: {...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}), //在methods中从对应的模块中读取mutations中方法;...mapMutations('moduleName', ['mutationName'])}

vuex的缺点: 刷新浏览器,vuex中的state会重新变为初始状态; 因为vuex存储的数据在内存中,刷新消失;
解决方案:
1.使用插件vuex-persistedstate 让vuex的数据持久化;
2. 将vuex的数据进行本地存储,将vuex中的数据直接保存到浏览器缓存中(sessionStorage、localStorage、cookie);


SPA单页面应用

在 Web 页面初始化时加载相应的 html结构、JavaScript交互 和CSS样式。一旦页面加载完成,根据url地址栏的不同,来实现对应的路由组件的切换,整个页⾯是没有进⾏刷新的,只是组件与组件之间的卸载与装载的过程。

  • 原理:根据url地址栏的不同,来实现对应的路由组件的切换,整个页⾯是没有进⾏刷新的,只是组件与组件之间的卸载与挂载的过程。
  • 整个应用只有一个完整的页面, 所用的功能在一个html上实现。
  • 点击页面中的导航链接不会刷新页面,只会做页面的局部更新。
  • 数据需要通过 ajax 请求获取。
    在这里插入图片描述

VueRouter 路由

  • 路径和组件的映射关系;

  • hash地址与组件之间的对应关系;

  • hash地址是#后面的内容(包括#);

一个路由(route)就是一组映射关系(key - value),路径和组件的展示关系;多个路由需要路由器(router)进行管理。
只有一个路由器router,每个组件都有的路由route; 全局有$router对象;且只有一个。

vue-router是基于路由和组件的;

路由用于设定访问路径,将路径和组件映射起来;

在vue-router的单页面应用中,页面的路径的改变就是组件的切换;

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

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


vueRouter的使用:

在这里插入图片描述
在这里插入图片描述


路由模块封装的基本使用:

安装vue-router,命令:npm i vue-router
编写router/index.js配置项: 创建router对象;

// 创建并暴露router对象;
//引入VueRouter
import VueRouter from 'vue-router' //使用VueRouterVue.use(VueRouter)
//引入路由的组件
import About from '../components/About'
import Home from '../components/Home'//创建router实例对象,去管理一组一组的路由规则
const router = new VueRouter({mode: 'hash', // 'history' 路由的工作模式;routes:[ //路由规则是 routes ,一个数组; {path:'/about', // path路径,带/component:About	 //引入的组件},{path:'/home',component:Home}]
})//暴露router对象
export default router
在main.js引入,并挂载到vm上; 全局就有了$router
import Vue from 'vue'
import App from './App.vue'
import router from './router' // 引入创建的router对象
Vue.config.productionTip = false
new Vue({render: h => h(App),router, // 挂载router
}).$mount('#app')

实现切换(active-class可配置高亮样式)

active-class属性可以控制路径切换的时候对应的router-link渲染的dom添加的类名;
router-link自带激活时的高亮类名;

<router-link active-class="active" to="/about">About</router-link>
active-class 激活时的样式active样式, 点击后高亮; 

router-link的自带两个高亮类名:
在这里插入图片描述

路由的自定义匹配类名:
在这里插入图片描述

router-link:

进行跳转, 自带激活时的高亮类名;
该标签是一个vue-router中已经内置的组件,它会被渲染成一个<a>标签; tag属性渲染成其他标签;	
replace属性:
作用: 1.replace属性可以控制router-link的跳转不被记录;2.浏览器的历史记录有两种写入方式:分别为push和replace,push是追加历史记录,replace是替换当前记录。路由跳转时候默认为push;3.开启replace模式: < router-link  replace >  ==> < router-link  :replace="true" >

router-view

路由组件的展示位置;
<router-view></router-view> :指定组件展示的位置; 没有name, 默认default;
<router-view  name="componentsName">:该标签会根据当前的命名视图name,动态渲染出不同的组件;
切换路径时,切换的是<router-view>挂载的组件;

多级路由(路由的嵌套)配置:

在上级路由配置项中添加 children:[ { } ], 在children中配置自己的路由规则;
子级路由的path不要加 / ;
子级路由也要在父级路由组件中配置路由出口,
默认子路由, 子路由的path: " " ;

routes:[{	 // 一级路由path:'/home',component:Home,redirect: '/home/mews', / 重定向到News路由组件 /children:[ //通过children配置子级路由{	// 二级路由path:'news', // 子级路由不要加 /, => 渲染后/home/news ;component:News},/ 默认子路由 /{path:'', // 子路由的path为空, 默认展示该子路由; => /home时就展示Title子路由组件;component:Title},}
]
跳转(要写完整路径): 从父子到子级具体的路径;   路由的路径,/ 开始 ; 
<router-link to="/home/news">News</router-link>
router-link 解析成a标签; to == href ;  tag解析成其他标签, tag="div',通过配置 tag 属性生成别的标签; 

路由的路径

单独的路由规则// 默认路由{path:"/",     / 根路径; /path:"*",    / 任意路径, 用作404页面,前面路径都不匹配就用这个,配置在路由的最后面; /redirect:"/home" , / 匹配到path后, 强制跳转到指定的path; /alias: "/about", / 别名 /},

命名路由

作用:可以简化路由的跳转。 给路由组件的配置项添加name属性 ;

routes:[{	 // 一级路由path:'/home',name:'shouye',component:Home,children:[ //通过children配置子级路由{	// 二级路由path:'news', //此处一定不要写/news; 子级路由不要加 / name: "xinwen", // 命名路由component:News},}
]
<!--简化前,需要写完整的路径 -->
<router-link to="/home/news">新闻</router-link>
<!--简化后,直接通过名字跳转 -->
<router-link :to="{name:'xinwen'}">新闻</router-link>   // name命名路由相当与path的路径; 
等价与
this.$router.push({name:"xinwen"})

命名视图

在components内配置;
如果 router-view 没有设置名字,那么默认为 default ;

<router-view></router-view>
<router-view name="a"></router-view>
<router-view name="b"></router-view>// 一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件。确保正确使用 components 配置;
const router = new VueRouter({routes: [{path: '/',components: {  //是componentsdefault: Foo, // 默认a: Bar,b: Baz}}]
})

路由传参

路由传参:嵌套路由时父路由向子路由传递参数;
路由传参:父路由把数据传递给子路由;

query传参   ? key1=value1&key2=value2
类似于get请求
http://localhost:8080/#/home/?id=1&title=消息1
params传参    /xxx/xxx/xxx
类似于post请求, 将参数放在请求体中传递;
params传参需要配置path, 在path中用  : 占位 ; (动态路由)
url地址栏不显示参数名, 只会显示参数值;
params 传参是将参数直接嵌入到路由路径中,而不是作为查询字符串追加到 URL 上。
{path: '/home/:id/:title', // : 动态路由参数:占位;name:'home' // 命名路由
}
<router-link :to="{ name: 'home', params: { id: 1, title:'消息1' } }">home组件</router-link>
http://localhost:8080/#/home/1/消息1

在这里插入图片描述

query传参

声明式<router-link :to=`/home?id=${'hello'}&title=${'你好'}`>字符串写法</router-link>  //模板字符串写法 
<router-link 
:to="{ 
path:'/home', 
query:{ 
id:'hello',
title:'你好'
}
}"
>对象写法</router-link> // path写法
<router-link 
:to="{ 
name:'name', 
query:{ 
id:'hello',
title:'你好'
}
}"
>对象写法</router-link> // name命名路由写法
url地址栏解析为: /home?id=hello&title=你好
编程式
this.$router.push( `/home?id=${1}&title=${'hello'}`) // 模板字符串写法
this.$router.push({ path:"/home", query:{ id:1, title:"hello"} }) // path写法
this.$router.push({ name:"home", query:{ id:1, title:"hello"} }) // name命名路由写法
url地址栏解析为: /home?id=1&title=你好
读取参数, 借助组件的$route;
this.$route.query.xxx

params传参

通过路由属性中的name来确定匹配的路由,通过params来传递参数。
params传参, 需要占位符, 跳转路由,只能使用name命名路由, 不能使用path;
配置路由,声明接收params参数, : 占位符占位; (动态路由)
如果通过path跳转,params 会被忽略, paramas传参需要通过name命名路由来跳转;

接收参数的组件,配置路由
params传参,先要配置路由; path: " :占位符 "
const router = new VueRouter({routes: [{name:'news',path:'news/:id/:title', //使用占位符声明接收params参数component:News}]
})
声明式
<router-link 
:to="{
name:'news, 
params:{
id:666,
title:'你好'
} 
}"
>跳转</router-link> // 通过name命名路由跳转
// 特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!
<router-link 
:to=`/news/&{666}/${'你好'}}}`
>跳转</router-link> // 通过模板字符串跳转
url地址栏解析为: /news/666/你好
编程式
this.$router.push({name:'news',params:{id:123,title:'hello'} })
this.$router.push(`/news/${123}/${'hello'}`)
url地址栏解析为: /news/123/helloparams读取参数, 借助$route;
this.$route.params.xxx

动态路由参数可选符:

路由有时候需要参数,有时候不需要参数,也希望匹配, 使用 ?
{path:'home/:id?',  / :参数? /component:()=>import('@/views/home'),name:'Home'
}

路由的props配置项

作用:让路由组件更方便的收到参数;
接收动态参数, 配置 path:’ /xxx/:xxx’, 例如: path: ‘/home/:id’;
谁接收参数,谁去配置props配置项;
在路由配置中配置props; 在组件中, 配置props,接收参数; prosp:[‘xxx’,‘xxx’]
路由关系中: prosp:{ } 对象形式;
props:true, 只能接收params参数, 所以要声明接收的是params参数, path要是用:占位符;
props:function($route){ return { } } , $route为参数, 依靠返回值;

接收参数的组件配置props:props:[ "id","a"]
//接收参数的组件==>路由配置关系
//第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给组件props:{a:900}//第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给组件props:true; //只能使params传参;//第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给路由组件props($route){ // props为函数时,可以接收到$route参数, 也可以解构赋值,连续解构赋值;return {id:$route.query.id,title:$route.params.id // 需要提前声明接收的是params参数;}}

声明式导航,编程式导航
实现路由的跳转

声明式导航

to, tag, replace

编程式导航
this.$router.push() //传参相当于router-link的to属性;
this.$router.replce()  //传参相当于router-link的to属性,跳转不被记录;
this.$router.back()  //后退,不用传参
this.$router.forward()  //前进,不用传参
this.$router.go()	 //传参数,正数前进,负数后退,0刷新;
编程式导航跳转
1.this.$router.push('路径')
2.this.$router.push({path:'路径'
})
3.this.$router.push({name:'路由名'
})
编程式导航跳转+传参-query
this.$router.push('/路径?参数名1=参数值1&参数名2=属性值2')
this.$router.push({path:'路径',或 name:'路由名',query:{参数名1:参数值1,参数名2:参数值2,}
})
编程式导航跳转+传参-params
首先需要配置动态路由 {path:'路径/:参数值', component:()=>import('@/文件路径'), name:'路由名'}
this.$router.push('/路径/参数值1/参数值2')
this.$router.push({name:'路由名', / 编程式导航跳转+params传参只能通过name跳转 /params:{参数名1:参数值1,参数名2:参数值2    }
})
<button @click="pushShow(n)">push</button>methods: { //methods中pushShow(n){ // 使用数据,调用传参的形式传递过来;this.$router.push({   //调用push方法, 传参相当于router-link的to属性;name:'xw',params:{id:n.id,news:n.new}})}},<button @click="replaceShow(m)">replace</button>methods: {replaceShow(m){this.$router.replace({  //调用replace方法name: 'msg',query: {id: m.id,title: m.title,}})}},

keep-alive 内置组件

缓存组件,不被销毁;
但被缓存的组件, 再次进入或离开不再执行组件的生命周期;
路由的跳转就是组件的销毁与加载;
作用:让不展示的组件保持挂载,不被销毁。
组件名 => 组件的name配置项;
缓存一个组件 include = “组件名”;
缓存多个组件 include = “组件名1, 组件名2,…” ; //逗号隔开
缓存多个组件 :include = “[ ‘xxx’, ‘xxx’]” // 数组名数组
不缓存某个组件,其他组件都缓存; exclude= “组件名”
不加include,默认包裹的组件及其子组件都被缓存;

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

keep-alive的生命周期钩子

作用:路由组件所独有的两个钩子,用于捕获路由组件的激活和失活状态。
原因: 被缓存的组件,再次进入或离开不再执行生命周期函数;

  activated() { }, 路由组件被激活时触发。deactivated() { }, 路由组件销毁时触发。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

compnent 动态组件-内置组件

组件的占位, 渲染成指定的组件;
通过is属性指定要渲染的组件;
is属性的值是components配置项中的组件注册名;

<keep-alive><component v-bind:is="componentName"></component></keep-alive>
1.搭配<keep-alive>来实现动态组件的缓存;
2.可以通过v-bind:is动态指定显示那个组件;
3.'componentName'是components配置项中的注册组件名;

meta路由元信息

路由的配置项: 路由元信息,是一个对象;
在路由规则中配置这个路由组件独有的信息; meta:{title:“首页” } ;
是一个对象,获取: this.$route.meta.xxx

路由懒加载

当路由被访问时,才加载对应的组件,提高了首屏加载效率;
一打开就有的页面不需要路由懒加载;
在router文件的index.js文件中配置;
写成箭头函数的形式,按需加载;
import Film from './views/Film'{path: '/film',component: Film,
}
/ 替换成路由懒加载 /
*1.
const Film = () => import('../views/Film.vue'){path: '/film',component: Film,
}
*2.{path: '/film',component: () => import('../views/Film.vue'), // 箭头函数形式,按需加载
}

路由守卫

控制路由的访问权限;
路由钩子函数, 进入之前调用,离开之前调用;进行拦截或者其他操作;
作用:对路由进行权限控制;
当路由跳转前或跳转后、进入、离开某一个路由组件前、后,需要做某些操作,就可以使用路由钩子来监听路由的变化;
分类:全局守卫、独享守卫、组件内守卫;
参数: 是一个箭头函数, 这个箭头函数的参数, to from next;

  • to 将要访问的路由对象;
  • from 离开的路由对象;
  • next 调用next()方法放行;
 - next() 直接放行,正常跳转;- next('/路径')next({path: '/路径'}), 强制跳转到指定页面;- next(false), 不跳转,强制停留在当前页;- 不声明next形参,默认允许访问每一个路由;- 声明了next形参,必须调用next()函数,否则不允许访问任何一个路由;

全局路由守卫 : 在路由配置文件 index.js中配置;

router.beforeEach( ) 全局前置路由守卫; 页面初始化、路由切换之前调用;
router.afterEach( ) 全局后置路由守卫( 没有next方法); 页面初始化、路由切换之后调用;
router.beforeResolve( ) 全局解析守卫;

独享路由守卫: 在路由规则中配置; to参数的对象就是要配置的路由组件;

beforeEnter( ) ; 切换对应的组件之前调用;

组件内路由守卫: 配置在路由组件文件中的钩子;

beforeRouteEnter( ) ; 通过路由规则,进入该组件时被调用;
beforeRouteLeave( ) ; 通过路由规则,离开该组件时被调用;
beforeRouteUpdate( ); 在当前路由改变,同时该组件被复用时调用;

全局路由守卫:

//全局前置守卫:初始化时执行、每次路由切换前执行router.beforeEach((to,from,next)=>{
if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则next() //放行}else{alert('暂无权限查看')}
}else{next() //放行}
})//全局后置守卫:初始化时执行、每次路由切换后执行,没有next()
router.afterEach((to,from)=>{document.title = 'to.meta.title' || "vue"	
})

访问权限:
在这里插入图片描述
在这里插入图片描述

独享路由守卫 :

// 切换对应的组件之前调用;
routes: [{path: '/foo',component: Foo,//进入到Foo组件之前调用!beforeEnter: (to, from, next) => {next()//放行}}]

组件内路由守卫:

路由组件文件中的函数钩子,类似于生命周期函数;
//进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter (to, from, next) {/不能获取组件实例 `this`, 因为当守卫执行前,组件实例还没被创建; /
},//离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {/ 离开该组件的对应路由时调用,可以访问组件实例 `this`; /},beforeRouteUpdate(to, from, next) {/ 在当前路由改变,但是该组件被复用时调用, 可以访问组件实例 `this`; /// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。}

路由模式

创建router时; mode: ‘hash’ 或 ‘history’

hash模式(前端路由):默认hash模式;
hash 改变会触发 hashchange 事件;
hash模式原理: 调用了window.onhashchange方法监听 hash值的切换;
hash模式背后的原理是onhashchange事件,可以在window对象上监听这个事件,由于 hash值变化不会导致浏览器向服务器发出请求,而且 hash 改变会触发 hashchange 事件(hashchange只能改变 # 后面的url片段);
更关键的一点是,因为hash发生变化的url都会被浏览器记录下来;

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

history模式的原理: 本质使用H5的histroy.pushState方法来更改url,不会引起刷新。

mode: 'history'
地址干净,美观 ,不带#号。
兼容性和hash模式相比略差。
上线服务器后,刷新页面服务端404的问题,应用部署上线时需要后端人员配置,将所有访问都指向index.html;

对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
hash模式下,仅hash符号之前的内容会被包含在请求中。
history模式下,前端的url必须和实际向后端发起请求的url 一致。
hash值
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/124388.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# 基础面试题(万字)

1.选择题 1. 简述下面选项能够捕获运算溢出的异常类型的有 &#xff1f; A)Exception B)SystemException C)ArithmeticException D)OverflowException 试题回答&#xff1a;AD 2. 程序员可使用&#xff08;&#xff09;语句以程序方式引发异常 &#xff1f; A)run B)try C)th…

LAMP搭建WordPress

L linux A apache hhtpd M mysql/maridb P PHP1、 安装php yum -y install php php-fpm php-server php-mysql1.1、 启动php-fpm并自启 systemctl enable php-fpm --now[rootecs-1cee ~]# systemctl status php-fpm ● php-fpm.service - The PHP FastCGI Process ManagerLoa…

VR农学虚拟仿真情景实训教学演示

首先&#xff0c;VR农学虚拟仿真情景实训教学提供了更为真实的实践环境。传统的农学实训往往受制于时间、空间和资源的限制&#xff0c;学生只能通过观察或简单的模拟来学习农业知识和技能。而借助虚拟现实技术&#xff0c;学生可以进入虚拟农场&#xff0c;与各种农作物、工具…

【运维日常】infiniband网络架构,容器间跨机器不同网段通信

本站以分享各种运维经验和运维所需要的技能为主 《python零基础入门》&#xff1a;python零基础入门学习 《python运维脚本》&#xff1a; python运维脚本实践 《shell》&#xff1a;shell学习 《terraform》持续更新中&#xff1a;terraform_Aws学习零基础入门到最佳实战 《k8…

PY32F003F18按键输入

一、PY32F003F18的GPIO介绍 1、PY32F003F18的18个I/O&#xff0c;均可作为外部中断&#xff1b; 2、每个GPIO都可以由软件配置为输出&#xff1a; 1)、推挽输出(push-pull) 2)、开漏极输出(open drain) 注意:驱动电流为8mA; 3、每个GPIO都可以由软件配置为输入&#xff1a; 1)、…

c语言初阶指针

目录 何为指针 地址大小 野指针 成因 如何规避 有效性 指针计算 -整数 ​编辑 指针比较运算 指针-指针 ​编辑 数组与指针关系 二级指针 指针数组 应用 何为指针 指针就是指针变量&#xff0c;用来存放内存空间的一个编号&#xff0c;将指针比作我们宾馆的客人&a…

前端 JS 经典:上传文件

重点&#xff1a;multipart/form-data 后端识别上传类型必填 1. form 表单上传 <!-- enctype"multipart/form-data" 这个必填 --> <form action"http://127.0.0.1:8080/users/avatar" method"post" enctype"multipart/form-data…

软件工程课件

软件工程 考点概述软件工程概述能力成度模型能力成熟度模型集成软件过程模型逆向工程![ ](https://img-blog.csdnimg.cn/425cea8190fb4c5ab2bf7be5e2ad990e.png) 考点概述 重点章节 软件工程概述 之前老版教程的&#xff0c;之前考过 能力成度模型 记忆 能力等级 和 特点 能力…

最强的AI视频去码图片修复模型:CodeFormer

目录 1 CodeFormer介绍 1.1 CodeFormer解决的问题 1.2 人脸复原的挑战 1.3 方法动机 1.4 模型实现 1.5 实验结果 2 CodeFormer部署与运行 2.1 conda环境安装 2.2 运行环境构建 2.3 模型下载 2.4 运行 2.4.1 人脸复原 ​编辑​编辑 2.4.2 全图片增强 2.4.3 人脸颜色…

linux-进程-execl族函数

exec函数的作用&#xff1a; 我们用fork函数创建新进程后&#xff0c;经常会在新进程中调用exec函数去执行另外一个程序。当进程调用exec函数时&#xff0c;该进程被完全替换为新程序。因为调用exec函数并不创建新进程&#xff0c;所以前后进程的ID并没有改变。 简单来说就是&…

ESP32用作经典蓝牙串口透传模块与手机进行串口通信

ESP32用作经典蓝牙串口透传模块与手机进行串口通信 简介ESP32开发板Arduino程序手机与ESP32开发板进行蓝牙串口透传通信总结 简介 ESP32-WROOM-32模组集成了双模蓝牙包括传统蓝牙&#xff08;BR/EDR&#xff09;、低功耗蓝牙&#xff08;BLE&#xff09;和 Wi-Fi&#xff0c;具…

使用Caffeine实现帖子的缓存来优化网站的运行速度

导入依赖 <!-- https://mvnrepository.com/artifact/com.github.ben-manes.caffeine/caffeine --><dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId><version>3.1.7</version>…

stride与padding对输出尺寸的计算

公式&#xff1a; 练习&#xff1a; 图1&#xff1a; input4&#xff0c;filter3&#xff0c;padding0&#xff0c;stride1 output2 图2&#xff1a; input5&#xff0c;filter3&#xff0c;padding0&#xff0c;stride2 output2 图3&#xff1a; input6&#xff0c;filter3&am…

二、创建个人首页页面

简介 改造 App.vue 创建一个展示页面,实现一个可以轮播的功能效果。欢迎访问个人的简历网站预览效果 本章涉及修改与新增的文件:style.css、App.vue、assets 一、 自定义全局样式 将 style.css 中的文件样式内容替换为如下代码 /* 初始化样式 --------------------------…

Postern配置HTTP和HTTPS的步骤

Postern是一款强大的Android代理工具&#xff0c;它允许您在设备上配置全局代理来实现安全、隐私保护和自由上网。本文将详细介绍如何使用Postern在Android设备上配置HTTP和HTTPS代理&#xff0c;为您提供更便捷的上网体验。 步骤1&#xff1a;下载和安装Postern应用 首先&am…

原生Js Canvas去除视频绿幕背景

Js去除视频背景 注&#xff1a; 这里的去除视频背景并不是对视频文件进行操作去除背景 如果需要对视频扣除背景并导出可以使用ffmpeg等库&#xff0c;这里仅作播放用所以采用这种方法 由于uniapp中的canvas经过封装&#xff0c;且 uniapp 的 drawImage 无法绘制视频帧画面&…

《基于区块链的数据资产评估实施指南》技术研讨会成功召开

2023年9月1日&#xff0c;《基于区块链的数据资产评估实施指南》&#xff08;以下简称《指南》&#xff09;技术研讨会在深圳召开&#xff0c;竹云科技作为主要参编单位出席此次研讨会。 中国科协决策咨询首席专家王春晖&#xff0c;中国社会科学院博士于小丽&#xff0c;中国…

无swing,高级javaSE毕业之贪吃蛇游戏(含模块构建,多线程监听服务)

JavaSE&#xff0c;无框架实现贪吃蛇 文章目录 JavaSE&#xff0c;无框架实现贪吃蛇1.整体思考2.可能的难点思考2.1 如何表示游戏界面2.2 如何渲染游戏界面2.3 如何让游戏动起来2.4 蛇如何移动 3.流程图制作4.模块划分5.模块完善5.0常量优化5.1监听键盘服务i.输入存储ii.键盘监…

Lumion 和 Enscape 应该选择怎样的笔记本电脑?

Lumion 和 Enscape实时渲染对配置要求高&#xff0c;本地配置不够&#xff0c;如何快速解决&#xff1a; 本地普通电脑可一键申请高性能工作站&#xff0c;资产安全保障&#xff0c;供软件中心&#xff0c;各种软件插件一键获取&#xff0c;且即开即用&#xff0c;使用灵活&am…

时序预测 | MATLAB实现CNN-LSTM卷积长短期记忆神经网络时间序列预测(风电功率预测)

时序预测 | MATLAB实现CNN-LSTM卷积长短期记忆神经网络时间序列预测&#xff08;风电功率预测&#xff09; 目录 时序预测 | MATLAB实现CNN-LSTM卷积长短期记忆神经网络时间序列预测&#xff08;风电功率预测&#xff09;预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1…