监听的属性发生改变就会执行回调函数
注意:Vue所管理的函数,都不能写成箭头函数,
Vue管理的函数:methods中定义的函数,计算属性,监听器中的函数
目录结构:
node_modules:第三方依赖
public:公共资源目录
api接口资源
imgs -->public中的图片资源在当前项目下通过/img直接可以访问到
src:源码
network 文件夹用于项目数据的请求的发送,网络请求模块
assets:静态资源,css、img、js、font等
compoments:一般组件存放的目录
router:路由配置,导入自定义的组件,创建路由,一般将views中的组件绑定上路由,
index.js--里面用于配置所有的路由信息,配置路由与组件的映射关系
pages:这个文件夹需要自己手动创建,整个文件夹存放路由组件(受路由管理的组件)
mixin:写一些vue混入的数据或者方法,混入就是给vue实例添加的方法或者属性,通过this直接可以调用到混入对象中的一些方法和属性
utils:存放自定义插件
myPlugin.js
store:Vuex的文件夹
index.js -->写store state 然后导入下面的文件
mutations.js -->写 mutations的代码
getters.js -->写getters的代码
actions.js --》写actions的代码
modules 存放modules模块的文件夹
modulesA.js 存放模块a的代码
...
App.vue:首页组件(默认组件)一般导入components中的组件,汇总所有组件
main.js:入口文件
.browserslistrc:配置使用CSS兼容性插件的浏览器的使用范围
.editorconfig:统一和规范代码书写,【vue-cli3中没有,一般来说项目中需要这个文件】
.eslintrc.js:配置ESLint
.gitignore:配置git忽略的文件或者文件夹
babel.config.js:使用一些预设
package.json:项目描述既依赖 可以根据这个文件下载整个项目所依赖的包,npm install
package-lock.json:版本管理使用的文件
vue.config.js 自定义webpack配置【在vue-cli3中使用,这个文件最终会和默认的配置进行合并】
README.md:项目描述
MVVM:Vue没有完全遵循MVVM模型
M:模型数据 Model:对应的是data中的数据
V:视图 View(模板页面):DOM
VM:视图模型(View):Vue缔造的实例对象【作用:1.数据绑定,2.数据监听】
数据绑定:将模型数据绑定到【展示到】页面中的指定位置
数据监听:监听DOM的改变
事件修饰符
vue中的事件修饰符:
1.prevent:阻止默认事件(常用);原生阻止默认行为:event事件对象.preventDefault()
2.stop:阻止事件冒泡(常用);原生阻止冒泡:event事件对象.stopPropagation()
3.once:事件只触发一次(常用)之后就不会触发绑定的事件函数;
4.capture:使用事件的捕获模式:意思是再事件捕获阶段就执行绑定的事件处理函数
默认事件触发有三个阶段:
一,事件捕获阶段,由外向内进行事件的捕获,此阶段不会执行事件处理函数,
二,目标阶段,事件捕获到目标元素,之后触发事件的冒泡阶段开始执行事件
二,事件冒泡阶段,由内向外进行事件冒泡,此阶段会指向绑定的事件处理函数,先触发内部绑定的事件
5.self:只有event.target是当前操作的元素时才触发事件;触发事件函数执行的元素是绑定事件的元素
6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕;
默认是:事件一被触发,先执行事件处理函数,等到事件处理函数执行完毕后,然后才会执行事件的默认行为
7.native 组件标签绑定的事件的修饰符,添加上这个修饰符才会把内置事件绑定给组件内部的根元素
计算属性详解
计算属性:在vue中放在data中的数据都是属性,计算属性是对data中的数据进行处理
计算属性的原始写法
computed:{
计算属性名:{
get(){
return 计算属性的值
},
set(value){
get中依赖的数据=value
}
}
}
计算属性中的get调用时机:
(1).页面中初次使用计算属性时会执行一次。
(2).当计算属性依赖的数据(return返回的数据就是依赖的数据)发生改变时会被再次调用。
计算属性中set调用时机
(1).当计算属性被修改时,只有重写了set方法,计算属性才能被修改
使用计算属性获取值的方式是调用get函数,获取get函数的返回值
计算属性在第一次执行后,vue会将计算属性就缓存下来,之后使用到的同一个计算属性就不会再次去执行get获取,而是去找缓存
默认的计算属性中的值是不能被修改的
如果需要直接修改计算属性(如果不需要修改计算属性的值,那么就直接使用计算属性的简写)
那么就需要在计算属性中添加上set方法,set方法接收需要修改的值,然后将需要修改的值赋值给get函数中所依赖的数据
计算属性不能与data中的属性重名
如果计算属性不需要被修改,那么直接写计算属性的简写方式即可
简写方式:
computed:{
计算属性名(){
return 计算属性的值
}
}
监听器详解
监听器监听的属性是data中的数据,或者是计算属性,或者是props中的数据
监听器有两种写法:
1。Vue监听器:
watch:{ //完整写法:需要使用immediate,deep时,使用完整写法
"需要监听的数据":{
immediate:true/false(默认值) 是否初始化完毕watch后直接调用一次监听的属性所绑定的函数
deep:true//是否开启深度监视,
handler(新的值,旧的值){//handler回调函数名是固定写法
...
}
}
}
第一种方法简写:
watch:{
需要监听的数据(newValue,oldValue){
...
}
}
2.Vue实例对象的实例方法的监听器
完整写法
Vue实例.$watch("监听的属性",{
。。一些配置,
immedate:false,
deep:false,
handler(newValue,oldValue){
}
})
简写
Vue实例.$watch("监听的属性",function(newValue,oldValue){
方法体
})
监听的属性发生改变就会执行回调函数
注意:Vue所管理的函数,都不能写成箭头函数,
Vue管理的函数:methods中定义的函数,计算属性,监听器中的函数、
深度监视
开启深度监视,开启后一旦一个对象中的某一个属性值发生变化,就会调用handler函数
写法:
watch: {
// 检测对象中的某个属性的改变
"number.a": {
deep: true
handler() {
console.log("a的值发生变化");
}
},
// 监听对象中所有的属性值变化,需要开启deep=true深度监视
"number": {
deep: true,//开启深度监视,开启后一旦一个对象中的某一个属性值发生变化,就会调用handler函数
handler() {
console.log("number里面的属性值发生变化");
}
}
}
:key的作用与原理
面试题:react、vue中的key有什么作用?(key的内部原理)
key会绑定在虚拟DOM上
1. 虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,
随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
2.Diff算法对比规则:
diff算法大致做法是比较old VDOM树和new VDOM树之间的差异,根据差异更新虚拟DOM树。
(1).旧虚拟DOM中找到了与新虚拟DOM相同的key:
①.若虚拟DOM中内容没变, 直接使用之前的真实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是没有问题的。
5. key的作用:
1.主要用在 Vue 的虚拟 DOM 算法,在新旧 DOM 对比时辨识 新的VDOM,相当于唯一标识ID。
2.使 Vue 会尽可能高效的更新虚拟dom,Vue可以根据key来更精准的判断两个元素是否相同。key值相同的vue会复用已有元素而不是从头开始渲染,
同理,改变某一元素的key值会使该元素重新被渲染。
6.如果不设置key,vue会默认的将index设置为key
Vue数据响应式
给vue添加响应式的数据:数据一添加,页面就会发生变化
响应式的给vue中的data中的 对象 添加属性
Vue.set(vm.字段,key,value)==》只有这样添加的才是响应式的数据
Vue实例对象.$set(vm.字段,key,value)==》只有这样添加的才是响应式的数据
直接通过 Vue实例对象.对象.属性="值"这种方式添加的数据是不具有响应式的
Vue.delete()
Vue收集表单数据
v-model默认收集的是表单元素的value值
但是有一些输入类的元素没有明显的value值,如单选,多选框
收集表单数据:
若:<input type="text"/>,则v-model收集的是value值,用户输入的就是value值。
若:<input type="radio"/>,则v-model收集的是value值,且要给标签配置value值。
若:<input type="checkbox"/>
1.没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)
2.配置input的value属性:
(1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
(2)v-model的初始值是数组,那么收集的的就是value组成的数组
备注:v-model的三个修饰符:
lazy:失去焦点再收集数据
number:输入字符串转为有效的数字
trim:输入首尾空格过滤
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攻击。
(2).一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!
v-cloak指令(没有值):
1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。
2.使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题。
v-once指令:
1.v-once所在节点在初次动态渲染后,就视为静态内容了。
2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。
v-pre指令:
1.跳过其所在节点的编译过程。
2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译
3.元素加上v-pre指令后,不会参与vue的编译,直接会被放到页面上进行展示
混入
混入:https://www.cnblogs.com/dengyao-blogs/p/11589962.html
定义混入
在当前项目scr目录下,创建minix目录,然后传递混入的js文件,js文件中定义所有
混入的作用:
将多个组件中公共的部分,抽取出来,成为一个混入对象,从而简化代码,代码复用
混入对象的数据解构与组件基本一致
mixin混入对象的变量是不会共享的
注意:
如果组件中定义了与混入对象中data字段相同的属性data或者定义了与混入对象相同的methods方法,组件中的data和methods就会覆盖掉混入中的data和methods方法,否则就会进行合并
但是混入中的生命周期函数,组件对象是覆盖不掉的,所以如果组件和混入中都定义的生命周期函数,那么这个生命周期函数就会执行两遍
混入对象中可以写哪些内容?
只要组件.vue中的所有的属性和方法都可以写道混入对象中
局部混入:在组件中引入混入,只有引入了mixin的混入对象才可以使用,并且只有在引入了mixin混入对象的组件中才生效;
在需要的组件中引入mixin
import xxx from "./mixin/globalMixin"
然后注册和使用:
//这里注意:属性名为mixins,值为数组类型
mixins: [myMixin], //相当于注册,然后就可以直接使用混入对象中的数据
全局混入:全局混入我们只需要把mixin.js引入到main.js中,然后将mixin放入到Vue.mixin()方法中即可;
全局混入更为便捷,我们将不用在子组件声明,全局混入将会影响每一个组件的实例,
使用的时候需要小心谨慎;这样全局混入之后,我们可以直接在组件中通过this.变量/方法来调用mixin混入对象的变量/方法;
// 引入全局的混入 main.js
import { globalMixin } from "./mixin/globalMixin"
全局注册混入
Vue.mixin(globalMixin)
然后直接可以再所有的组件中使用到混入对象中的数据,通过this.的方式,所有的数据都直接混入到所有的组件中
mixin混入对象和Vuex的区别:
Vuex是状态共享管理,所以Vuex中的所有变量和方法都是可以读取和更改并相互影响的;
mixin可以定义公用的变量或方法,但是mixin中的数据是不共享的,也就是每个组件中的mixin实例都是不一样的,都是单独存在的个体,不存在相互影响的;
mixin混入对象值为函数的同名函数选项将会进行递归合并为数组,两个函数都会执行,只不过先执行mixin中的同名函数;
mixin混入对象值为对象的同名对象将会进行替换,都优先执行组件内的同名对象,也就是组件内的同名对象将mixin混入对象的同名对象进行覆盖;
插件
插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制——一般有下面几种:
添加全局方法或者 property。如:vue-custom-element
添加全局资源:指令/过滤器/过渡等。如 vue-touch
通过全局混入来添加一些组件选项。如 vue-router
添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。
一个库,提供自己的 API,同时提供上面提到的一个或多个功能。如 vue-router
使用插件步骤:
1.先创建插件文件
在src目录下创建 plugin/xxx.js 文件
然后再js文件中,暴露一个对象,对象必须要有install方法
plugin.js
const pluginObj = {
// 插件必须要有install方法,这个方法由Vue来进行调用
install(Vue,【参数列表】) {//install默认接收参数Vue构造函数
console.log("install", Vue);
在install方法内部可以既然用Vue进行一些全局的操作
定义全局过滤器
Vue.filter("过滤器名称",function(value){
})
定义全局混入
Vue.mixin({
....
})
给Vue原型上添加上一个方法或者属性
Vue.prototype.xxxx=function(){}
Vue.prototype.xxx="xxx"
定义全局指令
Vue.directive("指令名称",{
})
},
}
export default pluginObj
2.在main.js入口文件中使用插件
1.先引入插件
import xxx from "./插件路径"
2.使用插件
Vue.use(xxx,【参数列表】)
组件的自定义事件:
vue中的内置事件(click,keyup...)是给html元素使用的
组件的自定义事件是给组件使用的,会绑定到子组件VueComponent对象身上
组件实例对象.$on()=>绑定自定义事件
组件实例对象.$emit()=>触发自定义事件
组件实例对象.$off()=>解绑自定义事件
组件实例对象.$once()=>触发一次自定义事件
绑定事件:
方式1:
在父组件中,引入了子组件
<子组件 @自定义事件名称="父组件中的对应的事件处理函数"></子组件>
注意:这个自定义事件是绑定在子组件对象上
在子组件中,触发自定义事件,此时就可以向父组件传递数据
通过this.$emit("自定义事件名称",[参数列表]) ===>从而去父组件中触发自定义事件绑定的函数
方式2: 这种方式更加灵活
父组件中
<子组件 ref="xxx"></子组件> 通过ref来获取到子组件实例对象this.$refs.xxx=>获取的是子组件的实例对象
绑定事件:
子组件实例对象this.$refs.xxx.$on("自定义事件名称",父组件的事件处理函数)
子组件中:
this.$emit(自定义事件名称,"数据")
注意点:
如果事件处理函数是普通函数,那么谁触发自定义事件,那么父组件的事件处理函数中的this指向的就是谁
如果事件处理函数是箭头函数,那么事件处理函数中this就是父组件中的this
如果给组件标签直接绑定vue内置事件,那么vue会将内置事件,当成自定义事件
<组件 @click="事件处理函数"></组件> ==>组件会吧click当成自定义事件
<组件 @click.native="事件处理函数"></组件> ==>此时才会把click内置事件绑定给组件内部的根元素
解绑事件:
解绑组件上的一个自定义事件
组件实例对象.$off("自定义事件名称")
解绑组件上的多个自定义事件
组件实例对象.$off(["自定义事件名称1","自定义事件名称2",...])
解绑组件上的所有的自定义事件
组件实例对象.$off()
全局事件总线:可以实现任意组件间的通信34
可以设置为全局事件总线的对象需要满足:
1、能使全局所有的组件都能够访问的到
2、这个对象具有$emit() $on() $off() 方法
满足这个两个要求的只有是Vue对象
全局事件组件可以实现任意两个组件之间进行数据的交互
创建全局实例对象:方式1
在main.js中
import Vue from "vue"
Vue.prototype.$EventBus=new Vue()
创建全局实例对象:方式2
在main.js中
new Vue({
render:h=>h(App),
绑定方式2:
beforeCreate(){
Vue.prototype.$EventBus=this
}
})
使用全局事件总线
组件2---》传递数据给--》组件1
组件1:绑定事件总线
mounted(){
this.$EventBus.$on("自定义事件名称",(【参数】)=>{
<!-- 回调函数 -->
})
},
beforeDestroy(){
this.$EventBus.$off(“自定义事件名称”)解绑
}
组件2:触发自定义事件
methods:{
xxxClick(){
this.$EventBus.$emit("自定义事件名称",【参数数据】)
}
}
消息订阅与发布
也可以实现任意组件间的通信
1.订阅消息:需要消息名 --》需要数据的组件进行订阅消息
2.发布消息:传递消息内容 --》传递数据的组件进行发布消息
npm i pubsub-js --> 消息订阅与发布所需库
使用步骤
1.npm i pubsub-js -S
2.在需要使用消息订阅与发布的地方使用
订阅消息的组件(接收数据的组件)
import pubsub from "pubsub-js"
mounted(){
const 唯一id=pubsub.subscribe("消息名",(【自定义参数名称,数据】)=>{
//回调函数:回调函数执行时机:是在发布消息后,进行自动回调
})
this.唯一id=唯一id
},
beforeDestory(){
<!-- 组件销毁之前,取消订阅 根据id进行销毁 -->
pubsub.unsubscribe(this.唯一id)
}
发布消息的组件(传递数据的组件)
import pubsub from "pubsub-js"
methods:{
xxxClick(){
<!-- 只要触发publish,就会回调subscribe中的回调函数 -->
pubsub.publish("消息名",【数据】)
}
}
this.$nextTick():调用时机,当修改了data数据,等待vue重新把虚拟DOM重新挂载到页面之后,再调用
this.$nextTick(回调函数)
$nextTick所绑定的回调函数会在模板渲染完毕后再执行
Vue默认是不会一边修改data中的模型数据一边去渲染模板的,这样就效率太差了,
Vue会等待所有的代码执行完毕都然后再去渲染模板(除异步操作外)
所以有些时候,当我们立刻把模型数据修改后,页面也不一定会直接就跟着渲染了,而是等待代码都在执行完毕后,然后开始渲染模板
但是有些数据操作,是要等待模板重新渲染完毕后,操作新的dom,这样就体现出vm.$nextTick(回调函数)价值链
this.$nextTick(回调函数)会等待页面重新渲染完毕后,然后再来调用回调函数
js运行机制:单线程
同步程序执行完毕后再去执行异步程序
js是单线程的,一个任务完成之后才会执行另外一个任务,
'''js
for (let i = 0; i < 2000; i++) {
console.log("a");//先输出2000遍
}
setTimeout((params) => {
console.log("bbb");//输出c后,等待5s后执行,此时for循环导致计时器不准时了
}, 5000)
console.log("c");//输出两千遍a之后执行c
'''
process.nextTick(回调函数)与setImmediate(回调函数)
process.nextTick(回调函数)
process.nextTick(回调函数)需要借助于node环境运行
process.nextTick(回调函数)执行时机:同步代码执行完毕后,异步代码执行之前,process.nextTick(回调函数)绑定的回调函数会自动执行
setImmediate(回调函数)执行时机:第一个异步代码执行完毕后执行
js事件循环
运行栈:同步代码直接放到运行栈中执行
任务队列:异步代码到了阻塞时间后,就会被放到任务队列中执行
事件循环:js引擎不断检测任务队列中,是否有任务和运行栈中是否还有待执行的任务,如果没有任务,就去任务队列中将待执行的任务放到主线程的执行栈中执行
任务:同步任务,异步任务,异步任务分为微任务,宏任务
# 宏任务与微任务
异步程序分为 宏任务与微任务
宏任务:1、计时器 2、ajax 3、读取文件
微任务:Promise对象.then()方法
js代码执行顺序:
同步程序->process.nextTick()-> 微任务异步程序->宏任务异步程序(第一个异步程序)-> setImmediate()->其余的宏异步程序
Promise对象 es6
const promiseObj = new Promise((resolve,reject)=>{
if(){
resolve(data)
}else{
reject(err)
}
//内部只能一次调用 resolve(data) reject(err)其中的一个
})
<!-- 只有再Promise内部调用resolve()才会执行then中的第一个回调函数
只有再Promise内部调用reject()才会执行then第二个回调函数
-->
promise.then(
(data)=>{
data是resolve(data)中的data
},
(err)=>{
err是reject(err)中的err
},
)
或者
promise.then((data)=>{
data是resolve(data)中的data
}).catch(//失败的回调
(err)=>{
err是reject(err)中的err
})
async与await
async用于修饰函数
async修饰的函数的返回值是一个Promise对象
await用于修饰Promise对象,将Promise对象中resolve和reject的参数值取出
Vue动画
想要给谁加动画效果,就给谁包裹transition标签
transition 默认会动态给被包裹元素添class样式:
v-enter:元素进入动画之前,元素显示/插入前的效果,元素初始的位置 例如:transform: translateX(-200%);
v-active-enter:元素执行动画的过程中,元素显示/插入的过渡时间和函数,初始前到最终的一个时间段 例如:transition: all 0.3s ease;
v-enter-to:元素动画执行完毕,元素元素显示/插入后的效果,元素最终的位置 例如:transform: translateX(-100%);
v-leave:元素动画离开之前,元素隐藏/移除前的效果,例如:transform: translateX(0);
v-active-leave:元素动画离开的过程中,元素隐藏/移除的过渡时间和函数,例如:transition: all 0.3s ease;
v-leave-to:元素动画执行完毕。元素隐藏/移除后的效果,例如:transform: translateX(100%);
v-enter-to与 v-leave 这个两个样式规定动画开始时元素的位置状态
v-leave-to与v-enter 这个两个样式规定动画结束后元素的位置状态
v-active-enter与 v-active-leave 这个两个样式代表执行动画的时间段 一般在这个阶段设置元素的过渡效果
给transition添加上name="xxx" 那么默认的样式就得是.xxx-enter-active name改变v
transition添加上:appear='true' 意思是组件已挂载,就会执行动画
transition标签不会渲染到页面上
'''js
<transition>
<div class="box" v-show="isShow"></div>
</transition>
'''
transition只能包裹一个根元素,只能给一个元素添加动画
给多个元素添加动画
需要使用transition-group包裹
并且transition-group包裹的元素需要绑定key
Vue集成第三方动画库
animate.css官网链接:https://animate.style/
安装:
npm install animate.css --save
在项目种引入
import "animate.css"
使用animate.css
给transition标签添加上固定的name属性
按需求添加上
enter-active-class="官网上自己找动画值" =》进入动画的过程
leave-acitve-class="官网上自己找动画值" =》离开动画的过程
<transition name="animate__animated animate__bounc" enter-active-class="" leave-acitve-class="">
插槽
让父组件可以向子组件指定位置插入指定的模板结果,也是一种组件间的通信方式
定义一个插槽,插槽内可以包含任何模板代码
## 默认插槽:
在组件内部:
<div>
<slot>[xxxx默认的摹本内容]</slot> 如果这个插槽中没有传递任何内容,就会展示默认的模板内容
</div>
<组件标签>
xxxxx //此时的xxxx就填充到组件内部的<slot></slot>上,进行替换,就相当于在组件中挖了个坑
</组件标签>
## 具名插槽:具有名称的插槽,用于却分不同的插槽
定义具名插槽:
<slot name="名称"><slot/>
老板使用插槽:
给填充的到插槽的标签添加上对应的slot="名称" 此时就会填充到对应的插槽上
<a slot="名称"> //这种填充方式已经过时了
新版使用插槽:template不会渲染成标签
<template v-slot:名称>//v-slot:名称 只能使用在template标签上
填充模板内容
</template>
## 作用域插槽
数据在定义插槽的地方,根据数据生成的结构由使用插槽的人自己定义
作用域插槽用于:数据只有一份,存储在定义插槽的组件中时,此时就可以使用作用域插槽
作用域插槽:携带数据的插槽
<slot :data="data" :sdada="sasa"></slot> 通过slot绑定数据的方式向外界传递插槽的数据
数据:
data(){
return {
data:["aaa","bbb","ccc"]
}
}
使用作用域插槽
<template scope="自定义接收数据的名称">
{{自定义接收数据的名称.slot指定的数据字段}} ==>得到作用域插槽种的数据
然后就可以进行数据渲染了
</template>
Vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式的插件
Vuex:把需要多个组件共享的变量(状态)全部存储在一个对象里面
这个共享的变量是响应式的,一个变,全局变
Vuex一般管理什么样的状态?
一般管理用户的状态
多个组件需要使用到用户的头像
多个组件需要共享的东西,都会放到Vuex中
安装Vuex: npm install vuex -S
在src目录下创建store文件夹,里面创建index.js文件用于注册vuex
使用过程
在index.js文件中,
1.安装插件
Vue.use(Vuex) ==>在底层执行了Vuex.install
2,创建Vuex对象----在src下新建一个store文件夹/index.js
const store =new Vuex.Store({
})
3.导出Vuex对象
export default store
4.在入口main.js文件中注册vuex
import store from "store/index.js"
new Vue({
store ==>给vue的原型增加了一个属性 vue.prototype.$store
})
Vuex与Vue.prototype全局对象 存放全局状态的区别:
1.Vuex store存放的状态是响应式的。当 Vue 组件从中检索状态时,如果 store 的状态发生变化,它们将响应式有效地更新。
2.您不能直接改变store的状态。改变 store 状态的唯一方法是显式提交 mutations。这确保了每个状态更改都会留下可跟踪的记录,并启用帮助我们更好地了解我们的应用程序的工具。
确保每一次修改store的状态都要经过mutations
vuex的五大核心概念:
1.State:在 store 中的 state 对象,可以理解为 Vue 实例中的 data 对象,它用来保存最基本的数据。
State 提供唯一的公共数据源,所有共享的数据都要统一放到 Store 的 State 中进行存储
state 单一状态树:在一个项目中只需要创建一个Store对象,对整个项目的状态信息进行管理,
state里面存放的数据都是响应式的
2. Mutation:定义方法用于操作 state中 的数据。
只能通过 mutation 变更 Store 数据,不可以直接操作 Store 中的数据。
通过这种方式虽然操作起来稍微繁琐一些,但是可以集中监控所有数据的变化。
Vuex的store状态的更新的唯一方式:提交Mutation
Mutation对象里面需要写同步函数,不能有异步的操作,如果有异步的操作,devtools工具监听不到state的变化
1.Mutation包含两部分
1.事件类型
2.回调函数
事件类型(参数){
回调函数
}
2.Mutation的使用
1.在Mutation中定义方法:
Mutation:{
//接收一个参数
事件类型(state,【接收参数】){
操作state状态变量的代码
},
//接收多个参数
方法(state,payload){
payload.参数==》取出参数
}
}
2. 提交Mutation
传递一个参数给Mutation中的方法 :传递给Mutation的参数称为:payload负载
1.this.$store.commit("mutations中定义的事件类型",【传递参数】)
传递多个参数给Mutation中的方法
2.this.$store.commit("mutations中定义的事件类型",{键1:值1,...})
3.通过Mutation修改state中的状态信息是响应式的 mutations中的方法必须是同步方法,只有是同步方法,dectools插件才能捕捉到数据的变化
但是在vue-cli2中,通过Mutation向state中添加数据,删除数据不是响应式的
就不能通过简单的修改数据,删除数据的方式进行操作
就需要使用
state.对象变量["新的属性"]="值" 这种方式不是响应式的
Vue.$set(要修改的对象,key值,"修改的值")可以添加元素,也可以修改元素
delete state.对象变量.属性 这种方式删除对象的属性也不是响应式的
Vue.$delete(要删除的对象,删除的key值)是删除元素
在vue-cli3中是响应式的
4.类型常量
3.Getter:Getter 可以对 Store 中已有的数据进行加工处理,然后返回给state 类似 Vue 的计算属性。
Store 中数据发生变化,Getter 的数据也会跟着变化
在getters中定义方法:
方法(state,getters){参数:state是存储状态的对象,getters:是当前的getters对象,可以根据该对象,使用其他的方法返回值
return 处理state的代码,以满足需求
}
使用getters中定义的方法:
this.$store.getters.方法 不需要加()就会得到getter中方法的返回值
4.Action:Action 用于处理异步任务。可以提交mutation
如果通过异步操作变更数据,必须通过 Action,而不能使用 Mutation,
但是在 Action 中还是要通过触发Mutation 的方式间接变更数据。
在actions中编写异步操作,通过context.commit("Mutation中的函数名称")触发执行在Mutation
1.使用:
actions: {
aupdateInfo(context,【参数】) {
context上下文对象:context.commit("mutations中的函数") 用于将异步操作中调用mutations中的方法进行处理
setTimeout(() => {
context.commit("Mutation中的函数名称",传递参数给mutation)
}, 2000)
},
},
2.页面中通过this.$store.dispath(actions中的函数名称,【参数】)触发执行actions中的异步代码
参数可以是一个变量,也可以是一个对象
5.Module:
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。
当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。
每个模块拥有自己的 state、mutation、action、getter
1.使用:
moudules:{
a模块:{
state:{},
getters:{},
mutations:{},
actions:{},
modules:{}
},
b模块:{
state:{},
getters:{
方法(state,【getter,rootState】){
参数:state是当前模块中的state,
getter是当前getters对象 可以通过getter.方法 来调用其他的方法的返回值数据
rootState:是根State
return 当前方法的返回值
return function(params){
return 当前方法的返回值+params
}
}
},
mutations:{
},
actions:{
方法(context){//context暂时理解为vuex store对象
context类似于模块的对象,里面有好多属性和方法
异步操作
context.commit("当前模块中的mutations中的方法")
}
},
modules:{}
}
...
}
2.使用modules中的不同模块的内容:
2.1 访问modules中的state的内容
this.$store.state.模块名.属性
2.2 提交Mutation(执行Mutaion中的方法)
this.$store.commit("Mutation定义的方法")
2.3 执行Action中的方法
this.$store.dispatch("action中的异步方法",参数)
原理是:vuex会将moudules中的定义的state、mutation、action、getter一个模块以对象的形式,
统一添加到外面的state,mutation、action、getter中
vuex
目录结构:
└── store
├── index.js # 我们组装模块并导出 store 的地方
├── actions.js # 根级别的 action
├── mutations.js # 根级别的 mutation
├── getters.js
└── modules
├── cart.js # 购物车模块
└── products.js # 产品模块
Vuex五大核心:
1.state:在 store 中的 state 对象,可以理解为 Vue 实例中的 data 对象,它用来保存最基本的数据。
2.Mutations:定义方法用于操作 state中 的数据。 Vuex的store状态的更新的唯一方式:提交Mutation mutation只能处理同步操作
3.Getters:Getters 可以对 Store 中已有的数据进行加工处理,然后返回给state 类似 Vue 的计算属性。Store 中数据发生变化,Getter 的数据也会跟着变化
4.Actions:Actions 用于处理异步任务。可以提交mutation
5.Module 每个模块拥有自己的 state、mutation、action、getter
## 其中五大核心都是对象
Vuex的使用步骤:
1.安装 vuex:npm i vuex -S
2.初始化Vuex: 在store/index.js中
improt Vue from "vue"
import Vuex from "vuex"
import acrions from "./actions"
import getters from "./getters"
import mutations from "./mutations"
Vue.use(Vuex)//此时vue实例上就有了$store用于管理vuex
const state = {
...数据
}
Vuex.Store({
state,
acrions,
getters,
mutations,
})
3.在main.js中进行注册
import store from "./store/index"
new Vue({
render: h => h(App), //h是一个函数createElement,将App这个组件替换首页面中的#app所控制的区域
beforeCreate() {
// 创建全局事件总线 方式1:使用当前的Vue作为全局事件总线
Vue.prototype.$EventBus = this
},
store,//此时就可以通过Vue实例对象.$store访问到vuex
}).$mount('#app')
## Vuex五大核心的基本使用
1.state:
state定义在index.js中,用于存储数据
const state={
数据
}
在外界访问state中的数据
this.$store.state.数据
2.actions:
外界通过Vue实例.$store.dispatch("actions中的方法",参数/{参数})
export default{
方法(context,value){
if(){}逻辑操作,延时操作,异步操作都写在actions中
context上下文对象.commit("mutations中的方法",参数/{参数}) //在actions中提交mutation
context上下文对象中包含:
context.dispatch()可以调用actions中的方法
context.commit()可以调用mutations中的方法
context.state可以访问state
}
}
3.mutations
外界通过Vue实例.$store.commit("mutaion中定义的方法",{参数}/参数)、
在mutaion内部:
export default {
方法(state,payload){
如果通过commit传递过来的参数是一个对象那么payload的结构就是:{参数:数据} 通过payload.参数 访问到数据
如果通过commit传递过来的参数是一个单纯的数据值,那么payload就是单纯的这个数据
对state进行修改、增加、删除等等。
mutation中只执行很单纯的操作,就是负责操作state
}
}
4.getters
Getters可以对 state 中已有的数据进行加工处理, 类似 Vue 的计算属性。Store 中数据发生变化,Getter 的数据也会跟着变化
在getters中定义方法:
方法(state,getters){
参数:state是存储状态的对象,getters:是当前的getters对象,可以根据该对象,使用其他的方法返回值
return 返回加工后的state数据
}
使用getters中定义的方法:
this.$store.getters.方法 不需要加()就会得到getter中方法的返回值
## Vuex模块化:modules
5.Module:
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。
当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。
每个模块拥有自己的 state、mutation、action、getter,
每个分类都属于一个模块
Vuex模块后的目录结构:
└── store
├── index.js # 我们组装模块并导出 store 的地方
├── actions.js # 根级别的 action
├── mutations.js # 根级别的 mutation
└── modules
├── cart.js # 购物车模块
└── products.js # 产品模块
export default new Vuex.Store({
state,
actions,
getters,
mutations,
// 下面是模块化
modules: {
模块名:"moduleA",
moduleB
}
})
### 使用模块化后,访问不同模块中的内容:
普通方式访问(不借助辅助函数):
1.访问模块中的state:
this.$store.state.模块名.xxx
2.访问actions,getters,mutaions
this.$store.getters["模块名/getters中的方法"];
this.$store.commit("模块名名/Mutation定义的方法",参数)
this.$store.dispatch("模块名名/actions定义的方法",参数)
但是在actions文件中,通过context.commit("直接使用mutations中的方法")
模块化结合vuex辅助函数:
方式一:
...mapXxxx(["模块名1","模块名2"....]) ==》取出不同的模块
通过 模块名.方法 进行访问对应的方法
方式二:
...mapXxxx("模块名",["模块中对应的方法名1",....]) ==》取出对应的模块中的方法
...mapXxxx("模块名",{
别名:"模块中对应的方法名1",
})
通过 模块中对应的方法名1 直接可以访问到
注意:使用方式二的前提是:需要给模块开启命名空间:namespaced:true
模块名需要提前指定
模块A:
export default {
namespaced: true,
actions:{},
state:{},
...
}
给...mapActions和...mapMutation指定的方法传递参数:只能通过绑定事件时,通过onXxxx="方法名(传递参数)"
1.使用:
moudules:{
a模块:{
state:{},
getters:{},
mutations:{},
actions:{},
modules:{}
},
b模块:{
state:{},
getters:{
},
mutations:{
},
actions:{
},
modules:{}
}
...
}
2.使用modules中的不同模块的内容:
2.1 访问modules中的state的内容
this.$store.state.模块名.属性
2.2 提交Mutation(执行Mutaion中的方法)
this.$store.commit("Mutation定义的方法")
2.3 执行Action中的方法
this.$store.dispatch("action中的异步方法",参数)
原理是:vuex会将moudules中的定义的state、mutation、action、getter一个模块以对象的形式,
统一添加到外面的state,mutation、action、getter中
## vuex辅助函数
1.mapState:将state数据映射到组件的计算属性中
在组件中访问vuex中存储的状态(数据)访问比较麻烦,this.$store.state.xxx
通过mapState可以快速的访问到state中的数据
使用步骤:
在组件文件中:
import {mapState} from "vuex"
<!-- mapState第一种写法(对象写法) -->
computed(){
<!-- 将state混入到计算属性中 -->
...mapState({
别名:"state中需要被暴漏的数据字段", //在模板中通过别名就可以直接访问到state中的数据
别名:"state中需要被暴漏的数据字段",
。。。。
})
}
<!--mapState第二种写法(数组写法) -->
computed(){
<!-- 将state混入到计算属性中 -->
...mapState(["state的字段1","state的字段2",...])//数组写法:1.state字段要在state中有定义过,2.在模板中访问state,也必须使用state的字段1
}
2.mapGetters:将getters中的方法映射到计算属性中
在组件中访问vuex中的getters时,访问代码比较长,this.$store.getters.xxx
使用mapGetters可以快速访问getters
使用步骤:
import {mapGetters} from "vuex"
computed(){
<!-- 将getters混入到计算属性中 -->
...mapState({
别名:"getters中的方法", //在模板中通过别名就可以直接访问到getters的数据
别名:"getters中的方法",
。。。。
})
<!-- 写法二:数组写法 -->
...mapState(["getters方法名1",...])//getters方法名1必须在getters中定义过,在模板中使用getters方法名1进行访问
}
3.mapMutations:将mutations方法映射到methods中
在组件中访问vuex中的mutaions中的方法时,访问代码比较长,this.$store.commit("mutaions中的方法",数据)
使用mapMutations可以快速访问mutaions
使用步骤:
import {mapMutations} from "vuex"
methods:{
<!-- 将mutations混入到methods中 -->
...mapMutations({
别名:"mutations中的方法",//但是这种方式没有好的办法传递参数给mutations中的方法,只有一个方法那就是通过事件绑定的时候,@click="别名(传递参数给mutaions中的方法)"
})
<!-- 写法二:数组写法 -->
...mapMutations(["mutations方法名1",...])//mutations方法名1必须在mutations中定义过,在模板中使用mutations方法名1进行调用方法,传递参数通过onCick="mutations方法名1(参数)"
}
4.mapActions:将actions中的方法映射到methods中
在组件中访问vuex中的actions中的方法时,访问代码比较长,this.$store.dispatch("actions中的方法",数据)
使用mapActions可以快速访问actions
使用步骤:
import {mapActions} from "vuex"
methods:{
<!-- 将actions混入到methods中 -->
...mapActions({
别名:"actions中的方法",//但是这种方式没有好的办法传递参数给actions中的方法,只有一个方法那就是通过事件绑定的时候,@click="别名(传递参数给actions中的方法)"
})
<!-- 写法二:数组写法 -->
...mapActions(["actions方法名1",...])//actions方法名1必须在actions中定义过,在模板中使用actions方法名1进行调用方法,传递参数通过onCick="actions方法名1(参数)"
}
生成唯一字符串:
import {nanoid} from "nanoid"
使用:const str=nanoid() 生成唯一字符串
rander函数
const vm = new Vue({
render: h => h(App), //h是一个函数createElement,将App这个组件替换首页面中的#app所控制的区域
}).$mount('#app')
render是一个函数,且必须要有返回值,vue负责调用
render(createElement){//createElement也是一个函数,接收一个模板为参数,创建模块渲染到页面
return createElement("h1")
}
简写:
render:h=>h(模板组件)
vue由:vue核心+模板解析器组成(模板解析器负责解析template标签)
模板解析器体积过大
vue文件分为:精简版和完整版
默认使用的是精简版的vue(vue.runtime.xxx.js)。体积小,但是无法解析模板,需要借助render函数或者模板解析器,但是模板解析器vue精简版中没有
完成的vue(vue.js)中有模板解析器,直接可以对template模板进行解析,但是体积过大
vue配置
在根目录下;创建vue.config.js
详细配置:https://cli.vuejs.org/zh/config/#vue-config-js
关闭eslint语法检查:lintOnSave: false
路由
路由:为了实现单页面应用(SPA应用)index.html,一个路由就是一组映射关系:key是路径,value是组件
SPA应用:整个应用只有一个完整的页面,点击页面导航,不会重新刷新页面,只会进行组件间的跳转,实现局部刷新,数据需要进行ajax进行请求
前端路由:一个路由对应对应一个组件,
后端路由:一个路由对应一个响应
vue-router 是vue的一个插件库
安装:
npm i vue-router -S
引入:
在src/router/index.js中
improt VueRouter from "vue-router"
import Vue from "vue"
<!-- 引入路由展示的指定的组件 -->
import ComA from "../components/ComA"
安装
Vue.use(VueRouter) //此时就可以在vue中配置router
创建对象
const router = new VueRouter({
routes:[
<!-- 路由是一组{key:value} -->
{
path:"/aaaa",
component:ComA //在访问 当前/aaa路径时,就展示omA组件
}
]
})
<!-- 导出 -->
export default router
在main.js中进行引入
improt router "router/index.js"
const vm = new Vue({
render: h => h(App), //h是一个函数createElement,将App这个组件替换首页面中的#app所控制的区域
router
}).$mount('#app')
组件提供的内置组件
1. <router-link to="/路径" tag="a"></router-link> 实现路由跳转
to="/路径" ==》指定路径跳转到哪
tag="a" ==> tag指定router-link渲染成什么标签 默认是标签
当某个路由处于活跃(当前正在访问的路由时),router-link会动态的增加一个active-class类样式
当某个路由切换走时,这个路由对应的组件就会被销毁
当访问某个路由时,这个路由对应的组件就会被挂载
路由组件实例对象会有:$router与$route两个属性
$router:当前的整个路由器对象,整个应用只有一个$router
$route: 当前路由的配置对象(里面有路由参数等信息),每个路由组件都有属于自己的$route
2.<router-view></router-view> 给当前访问的路由的对应的组件,占位置用的,(指定路由组件呈现的位置,实现路由组件的切换)
一般组件与路由组件:
一般组件不受路由的管理,需要手动引入组件到页面,然后写组件标签
路由组件受路由管理的组件,不需要手动将组件引入到页面中,路由组件是根据前端路由的路由来确定渲染组件的
一般组件放在:components文件夹中
路由组件放在:pages文件夹中
## 路由嵌套
1.子路由编写规则
const router = new VueRouter({
routes:[
{
path:"/aaaa",
component:ComA //在访问 当前/aaa路径时,就展示omA组件
children:[//path:"/aaaa"子路由存放位置
{
path:"bbb"//子路由路径前不要加/
component:ComASon
}
]
}
]
})
2.访问规则
<router-link to="/父路由路径/子路由" tag="a"></router-link> 实现路由跳转
## 路由传参
1.query参数: /home?name=XXX&age=YYY
携带参数方式一:<router-link to="/父路由路径/子路由?name=XXX&age=YYY" tag="a"></router-link> 实现路由跳转
获取参数方式一:this.$route.query==>{name:XXX,age:YYY}
携带参数方式二:<router-link :to="{path:'/父路由路径/子路由',qurey:{name:XXX,age:YYY}}" tag="a"></router-link> 实现路由跳转
获取参数方式一:this.$route.query==>{name:XXX,age:YYY}
2.params参数:/home/id值/name值
携带参数方式一:<router-link to="/父路由路径/子路由/id值/name值" tag="a"></router-link> 实现路由跳转
const router = new VueRouter({
routes:[
{
name:"自定义路由名称"
path:"/aaaa/:id/:name", //配置params参数
component:ComA //在访问 当前/aaa路径时,就展示omA组件
children:[ //path:"/aaaa"子路由存放位置
{
path:"bbb" //子路由路径前不要加/
component:ComASon
}
]
}
]
})
获取参数方式一:this.$route.params==> {id:XXX,name:YYY}
携带参数方式二:<router-link :to="{name:'命名路由name',params:{name:XXX,age:YYY}}" tag="a"></router-link> 实现路由跳转
获取参数方式一:this.$route.params==> {id:XXX,name:YYY}
## 获取参数的简便方式:porps配置
路由的porps配置:那个路由需要简化接收参数的方式,就给那个路由组件进行props配置
在route/index.js中:
const router = new VueRouter({
routes:[
{
name:"自定义路由名称" //命名路由
path:"/aaaa/:name/:age",
component:ComA //在访问 当前/aaa路径时,就展示omA组件
props:{a:1,b:"hello"} //props第一种配置:对象形式,该对象的种的所有的key-value都会以props的形式传递给当前路由组件ComA
props:true //props第二种配置:布尔形式,如果props:true,那么就会把该路由组件接收的所有的 params参数,以props形式传递给当前路由组件ComA
props:($route)=> {
return {name:$route.query.name,age:$route.query.age}
return {name:$route.params.name,age:$route.params.age}
} //props第三种配置: 函数形式,,函数会接受到一个参数:$route对象,对象中具有params参数对象和query参数对象,然后进过一些操作将参数数据传递props
函数返回值必须是{}对象,对象中的key-value都会以props的形式传递给当前路由组件ComA
}
]
})
在对应要接收路由参数的路由组件ComA中
接收props第一种配置路由参数:props:["a","b"]
接收props第二,三种配置路由参数:props:["name","age"]
## 命名路由:给路由起名称,可以简化路由跳转
const router = new VueRouter({
routes:[
{
name:"自定义路由名称" //命名路由
path:"/aaaa",
component:ComA //在访问 当前/aaa路径时,就展示omA组件
children:[ //path:"/aaaa"子路由存放位置
{
path:"bbb" //子路由路径前不要加/
component:ComASon
}
]
}
]
})
设置命名路由后可以通过name进行切换
<router-link :to="{name:'路由的name',qurey:{name:XXX,age:YYY}}" tag="a"></router-link> 实现路由跳转
## 路由跳转:两种方式:push,replace
$router.push({path:'home'});本质是向history栈中添加一个路由,在我们看来是切换路由,但本质是在添加一个history记录
$router.replace({path:'home'});替换上一条路由,没有历史记录,点击返回,会跳转到上上一个页面
<router-link :to="{name:'路由的name',qurey:{name:XXX,age:YYY}}" tag="a"></router-link> 实现路由跳转:默认的跳转方式是:push
<router-link :replace="true" :to="{name:'路由的name',qurey:{name:XXX,age:YYY}}" tag="a"></router-link> :replace="true"开启replace跳转模式
## 编程式导航: 不借助router-link
$router.push({name:"命名路由"/path:"路由路径",query/params:{参数}});本质是向history栈中添加一个路由,在我们看来是切换路由,但本质是在添加一个history记录
$router.replace({name:"命名路由"/path:"路由路径",query/params:{参数}});替换上一条路由,没有历史记录,点击返回,会跳转到上上一个页面
$router.go(n) n为正数:表示前进n,n为负数:表示回退n
$router.forward() 前进1
$router.back() 回退1
## 路由缓存
缓存指定的一个组件
<keep-alive include="指定要缓存的组件(写组件的name)">
<router-view></router-view> //指定路由组件呈现的位置,实现路由组件的切换
</keep-alive>
缓存多个指定的路由
<keep-alive include="[指定要缓存的组件1(写组件的name),指定要缓存的组件2(写组件的name),...]">
<router-view></router-view> //指定路由组件呈现的位置,实现路由组件的切换
</keep-alive>
缓存所有的组件
<keep-alive>
<router-view></router-view> //指定路由组件呈现的位置,实现路由组件的切换
</keep-alive>
## 路由懒加载
路由的懒加载:用到时,再加载;
默认情况下,所有的代码最终都会打包成一个js文件,
将不同的路由打包成不同的js文件,当路由被访问到时,就加载路由对应的js文件=》路由懒加载
默认:页面会将所有的组件及资源都加载到main.js入口文件中,统一进行加载,
这样就导致刚进页面时就会发生卡顿,缓慢,
就像:在饭店点了一百个菜,饭店是等到菜全部做好后,统一将100个菜一起上上来
路由懒加载解决方法:路由懒加载,需要啥路由就加载那个路由,它多把每个路由单独的打包成一个js文件,访问到哪个路由组件,就加载这个js文件
在路由与组件绑定的时候,设置为:就可以实现路由的懒加载
new VueRouter({
routes:[
{
path:"/",
//component:Home
component:()=>import ("对应的组件的路劲")
}
]
})
import ('../view/Home.vue') //路由懒加载
但是路由懒加载还是有一个问题:这样会导致js文件过多,100道菜太多了,桌子放不下,
视图组件一般要与路由进行绑定
解决方法:就是将相同的类型的菜,放到一起,10个菜一组。。。
组件按组分块的加载方式:多个js文件打包成一个js文件,多个组件打包一起
有时候我们想把某个路由下的所有组件都打包在同个异步块 (chunk) 中。只需要使用 命名 chunk (opens new window),一个特殊的注释语法来提供 chunk name (需要 Webpack > 2.4)。
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')
new VueRouter({
routes:[
{
path:"/",
//component:Home
component:()=>Foo
}
]
})
## 路由组件独有的生命周期函数
activated(){} 调用时机:路由未激活->路由激活 时,调用
deactivated(){} 调用时机:路由激活->路由未激活 时,调用
路由激活:正访问的路由,就是被激活的路由
路由未激活:正访问的路由,然后切走了,就是未被激活的路由
## 路由守卫
路由守卫钩子函数一般有接收三个参数:
to:跳转到哪个路由对象(这个路由对象中有一部分的数据是routes中配置的)
from:来自于哪个路由对象
next:管理是否允许路由发送跳转
next()表示允许发生路由跳转
next(false)或 不写next:表示不允许发生路由跳转
const router = new VueRouter({
routes:[
{
name:"命名参数"
path: "/home",
component: HomeComp
meta:{ //可以存放当前路由携带的说明信息 可以通过to,from等对象访问到
isAuth: true, //是否需要进行权限验证
title:"首页"
}
},
{
path: "/about",
component: () => import("../pages/About.vue")
}
]
})
### 全局前置路由守卫:每一次切换路由(每一个路由切换)之前都会调用beforeEach所绑定的回调函数,还有路由初始化时也调用
router.beforeEach((to, from, next)=>{ //一般再前置路由守卫中进行用户权限的验证
if(to.meta.isAuth){//是否进行权限认证
if(to.fullpath==="/xxx"){
next()
}
}
})
### 全局后置路由守卫:每一次切换路由(每一个路由切换)之后都会调用afterEach所绑定的回调函数,还有路由初始化时也调用
router.afterEach((to, from)=>{ //一般在后置路由守卫中进行修改页面的title
document.title=to.meta.title
})
## 路由独享守卫:某一个路由独享的
const router = new VueRouter({
routes:[
{
name:"命名参数"
path: "/home",
component: HomeComp
meta:{ //可以存放当前路由携带的说明信息 可以通过to,from等对象访问到
isAuth: true, //是否需要进行权限验证
title:"首页"
},
//路由独享守卫:在访问当前路由之前会调用当前beforeEnter
beforeEnter:(to, from, next)=>{
...
}
},
{
path: "/about",
component: () => import("../pages/About.vue")
}
]
})
## 组件内导航守卫:类似于什么周期函数的写法
beforeRouteEnter(to,from,next){//通过路由规则,进入组件之前被调用
next() //放行
next(vm=>{ //放行的同时,也获取到vm就是当前的组件对象
})
}
beforeRouteLeave(to,from,next){//通过路由规则,离开组件之后被调用
next() //放行
}
## 路由的两种工作模式:history模式与hash模式
1 hash 模式下,仅 hash #符号之前的内容会被包含在请求中,如 http://www.abc.com/#/sasasxda/xsaxaa,#井号之后的内容不会发送给后端服务器,因此对于后端来说,请求地址发生错误,也不会报错,即使没有做到对路由的全覆盖,也不会返回 404 错误。
url地址中#号之后的内容就是hash值
hash模式是默认的工作模式
2 history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.abc.com/book/id 如果后端缺少对 /book/id 的路由处理,将返回 404 错误。Vue-Router 官网里如此描述:“不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”
设置history模式:
在路由配置中,与routes添加:mode:"history"
3 结合自身例子,对于一般的 Vue + Vue-Router + Webpack + XXX 形式的 Web 开发场景,用 history 模式即可,只需在后端(Apache 或 Nginx)进行简单的路由配置,同时搭配前端路由的 404 页面支持。
区别:
hash值:url中#号后面的都是hash值,hash值不会被包含在http请求中发送给后端服务器,
history 模式 url路径好看
hash模式兼容性好一些
hash模式:
1.地址中永远带着#号,不美观。
2.若以后将地址通过第三方手机app分享,若app校验严格, 则地址会被标记为不合法。
3.兼容性较好。
history模式:
1.地址干净,美观。
2.兼容性和hash模式相比略差。
3.应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。
异步组件【组件的懒加载】
原始引入组件:
import ComA from "./ComA"
components:{
ComA
}
原始引入组件的方式:组件会在页面已加载的时候就会加载下来
异步组件 实现 组件的懒加载
components:{
ComA:()=>import("./ComA")
}
异步组件的工厂函数【加工异步组件】
import Loading from "./components/asynccomp/Loading.vue";
import Error from "./components/asynccomp/Error.vue";
/* 异步组件的工厂函数 */
const AsyncComp = () => ({
// 需要异步加载的组件
component: import("./components/asynccomp/AsyncComA.vue"),
loading: Loading,
error: Error,
delay: 200, //loading组件展示的时间
timeout: 3000, //异步组件3s没有显示,就展示Error组件
});
vue3
创建vue3.0工程
1.使用vue-cli创建vue3项目 --> vue-cli基于webpack搭建
1.确保vue-cli > 4.5
2.查看vue-cli版本 vue --version
3.创建项目:vue create 项目名称
2.使用vite创建vue3项目
1.创建项目:npm init vite-app 项目名称
2.进入到项目目录:yarn install
vite相比于传统的项目项目构建工具优势:
开发环境中,无需打包操作,可快速的冷启动。
轻量快速的热重载(HMR)
真正的按需编译,不再等待整个应用编译完成。
vue3 main.js入口文件
import { createApp } from 'vue' //引入的不是vue构造函数,引入的是createApp的工厂函数
import App from './App.vue'
createApp(App).mount('#app') 创建和挂在
1.vue3不能像vue2那样创建vue实例,会报错
new Vue({
render:h=>h(App)
).$mount("#app")
2. vue3中的组件文件中template标签可以包含多个根元素
3.vue3不支持vue2的浏览器插件
常用的组合式API(Composition API)
1.setup:Vue3.0中一个新的配置项,值为一个函数。与data...methods。。一样的配置像
setup(props){
let 数据 = 值 //定义数据
function 方法 (){} //定义方法
//返回值1:返回一个对象,对象中放回的方法直接可以在插值表达式中访问到
return {
数据,
方法
}
//返回值2:若返回一个渲染函数:则可以自定义渲染内容。(了解)
import {h} from "vue"
return ()=>h("标签","标签内容") //会将当前组件中的所有内容都覆盖掉
}
2.ref函数
import {ref} from "vue"
setup(props) {
<!-- 定义p普通数据 【不是响应式数据】-->
let name = "张三"
let age = 12
<!-- 定义响应式数据:需要借助ref函数 -->
let namne=ref("zhangsan") //ref函数返回一个引用对象,借助引用对象,将普通数据转换为响应式数据
funtion func(){
<!-- 修改响应数据 -->
name.value='xxx' //此时就将name数据修改为xxx,并且页面也会跟着刷新
<!-- 在插值表达式中直接通过name就可以访问到数据 -->
}
}
组件懒加载:
组件懒加载 :使用异步组件的方式实现
components:{
自定义组件名称:()=>import(“./组件”)
}
异步组件的工厂函数
const AsyncComp = () => ({
// 需要异步加载的组件
component: import("./components/asynccomp/AsyncComA.vue"),
loading: Loading,
error: Error,
delay: 200, //loading组件展示的时间
timeout: 3000, //异步组件3s没有显示,就展示Error组件
});
components:{
AsyncComp
}
Vue组件生命周期:
父子组件生命周期执行顺序
挂载阶段: 父组件先被创建->子组件再被创建->子组件挂载到页面->父组件再挂载到页面 ==>[先有父组件然后才有子组件,子组件挂载完毕后,父组件才算挂载完毕]
更新阶段: 父组件数据发生变化->props=>子组件数据也发生变化->子组件更新完毕->父组件数据才算更新完毕==>[视图导致页面数据发生变化,父组件将发生变化的数据通过props传递给子组件,子组件数据发生变化,子组件跟新完毕,父组件组件才算跟新完毕]
销毁阶段: 父组件即将销毁->子组件即将销毁->子组件销毁完毕->父组件销毁完毕
如果父组件没有向子组件传递props数据,那么父组件发生更新不会影响到子组件重新渲染
页面刚已加载组件的生命周期:会从beforeCreate执行到updated
Vue生命周期函数
生命周期函数:在某个时刻会自动执行的函数
生命周期的过程:
1.Vue({}).$mount("#app") 实例化Vue对象,并挂载到指定的容器
2.初始化事件和生命周期函数
3.beforeCreate() { // Vue实例初始化创建完成之前调用 [vue初始化事件和生命周期函数完成之后执行的函数] 此时最早可以访问到Vue组件实例
console.log("fatherComp beforeCreate");
},
4.初始化数据和响应式,
5. created() { // Vue实例创建完成 methods方法,data数据都可以访问到
console.log("fatherComp created");
},
6.判断有没有template的选项,来看是否编译模板到render函数
7.beforeMount() { // 模板挂载到页面之前执行 此时不能访问DOM元素
console.log("fatherComp beforeMount");
},
8.mounted() { // 模板挂载到页面完毕后执行 此时能访问DOM元素
console.log("fatherComp mounted");
},
9.beforeUpdate() { // 更新完成之前 数据发生更新,但是还没有同步到页面上,DOM还没有更新
console.log("fatherComp beforeUpdate");
},
updated() { // 更新完毕 页面与data中的数据同步
console.log("fatherComp updated");
},
beforeDestroy() {// 组件卸载之前,此时还可以访问到vue实例,等一些数据,还没有移出完毕
console.log("fatherComp beforeDestroy");
},
destroyed() { //组件卸载完毕之后
console.log("fatherComp destroyed");
},
// 路由对应的两个钩子
activated() {
console.log("进入当前路由组件");
},
deactivated() {
console.log("离开当前路由组件");
},
// 组件内独享守卫
beforeRouteEnter(to, from, next) {},
data() {
return {
count: 0,
};
},