什么是Vue
Vue是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型。
初始Vue
使用步骤
1.引入Vue.js文件。
2.创建一个Vue实例,且要传入一个配置对象; new Vue( { } );
3.还要准备一个容器接收Vue实例传来的数据,容器里的代码称为Vue模板
4.Vue实例和容器是一 一对应的,不能一对多 多对一和多对多。
5.真实开发中只有一个Vue实例,并且配合组件一起使用。
6.{{xxx}}中的xxx要写js表达式,xxx也可以写Vue原型对象上的属性,xxx可以自动读取到data中的所有属性。
7.一旦data中的数据发生改变,页面中用到该数据的地方也会自动更新
<div id="root"> <h1>姓名:{{uname}}</h1> <h1>年龄:{{age}}</h1> <!-- {{}}也可以是Vue原型对象上的属性 --> <h1>{{$options}}</h1> </div> <script> const vm = new Vue({ el:'#root', data:{ uname:'张三', age:18 } }) console.log(vm); </script>
Vue模板语法
插值语法 {{ }}
插值语法就是 {{}}
功能:用于解析标签体内容
写法:{{xxx}} xxx是js表达式,且可以直接读取到data中的所有属性
<div id="root"> <h1>姓名:{{uname}}</h1> <h1>年龄:{{age}}</h1> </div> <script> new Vue({ el:'#root', data:{ uname:'张三', age:18 } }) </script>
指令语法 v-开头
Vue中有很多指令,形式都是:v-???
功能:用于解析标签(包括:标签属性、标签内容、绑定事件。。。)
举例:v-bind 可以简写为 : v-model:value可以简写为 v-model
<div id="root"> <a v-bind:href="url">点我去百度</a> <a :href="url">点我也去百度</a> </div> <script> new Vue({ el:'#root', data:{ url:'https://www.baidu.com' } }) </script>
Vue数据绑定
单向数据绑定 v-bind / :
数据只能从data流向页面
<div id="root"> 姓: <input type="text" v-bind:value="firstName"> <br><br> 名: <input type="text" v-bind:value="lastName"> <br><br> <span>全名{{firstName}}-{{lastName}}</span> </div> <script> new Vue({ el:'#root', data:{ firstName:'张', lastName:'三' } }) </script>
双向数据绑定 v-model:value / v-model
数据不仅能从data流向页面,还可以从页面流向data。
双向数据绑定一般都应有在表单类元素上(input、select等)
<div id="root"> 姓: <input type="text" v-model:value="firstName"> <br><br> 名: <input type="text" v-model:value="lastName"> <br><br> <span>全名{{firstName}}-{{lastName}}</span> </div> <script> new Vue({ el:'#root', data:{ firstName:'张', lastName:'三' } }) </script>
Vue中的MVVM模型
M:模型(model):data中的数据
V:视图(view):模板代码
VM:视图模型(ViewModel):Vue实例
总结:1.data中所有的属性,最后都会在Vue实例对象身上。2.Vue实例对象身上所有的属性以及Vue原型上所有属性,在Vue模板中都可以直接使用
<div id="root"> <h1>姓名:{{uname}}</h1> <h1>年龄:{{age}}</h1> <h1>测试一下1:{{1+1}}</h1> <h1>测试一下2:{{$options}}</h1> <h1>测试一下3:{{$emit}}</h1> <h1>测试一下4:{{_c}}</h1> </div> <script> const vm = new Vue({ el: '#root', data() { return { uname: '张三', age: 20 } }, }) console.log(vm); </script>
Vue数据代理
什么是数据代理:通过一个对象代理对另一个对象中属性的操作(读/写
Object.defineProperty
语法:Object.defineProperty(对象名, 属性名,{ 配置对象 });
定义:该方法允许精确地添加或修改对象的属性。通过赋值操作添加的普通属性是可枚举的。对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。数据描述符是具有值的属性,该值可以是可写的,也可以是不可写的。存取描述符是由 getter 函数和 setter 函数所描述的属性。
configurable:值为
true
时,属性才能被改变,同时该属性也能从对应的对象上被删除。 默认为false
。enumerable:值为
true
时,该属性才会出现在对象的枚举属性中。 默认为false
。writable:值为
true
时,属性的值,也就是value
,才能被赋值运算符 改变。 默认为false。
value:该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。 默认为 undefined。
get:属性的 getter 函数,当访问该属性时,会调用此函数。该函数的返回值会被用作属性的值。 默认为 undefined。
set:属性的 setter 函数,当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被修改后的值)
<script> let number = 18 let obj = { name:'张三', sex:'男', } Object.defineProperty(obj,'age',{ // value:18, // enumerable:true, //控制属性是否可以枚举,默认值是false // writable:true, //控制属性是否可以被修改,默认值是false // configurable:true //控制属性是否可以被删除,默认值是false //当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值 get(){ console.log('有人读取age属性了') return number }, //当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值 set(value){ console.log('有人修改了age属性,且值是',value) number = value } }) </script>
Vue中的数据代理
定义:通过Vue实例对象来代理data对象中属性的操作(读/写)
好处:更加方便的操作data中的数据
基本原理:通过Object.defineProperty()把data对象中所有属性添加到Vue实例对象身上。为每一个添加到Vue实例对象上的属性,都指定getter/setter。在getter/setter内部去操作(读/写)data中对应的属性。
Vue事件处理 v-on/@ methods:{ }
事件的基本使用
1.使用 v-on:事件类型 或者 @事件类型
2.事件的回调需要配置在methods对象中,最终会在Vue实例对象身上。
3.methods中配置的函数,不要用箭头函数!this会指向window而不是Vue实例对象了。
4.methods中配置的函数,都是被Vue所管理的,this指向Vue实例对象或者组件实例对象。
5.@click=”fn“ 和 @click=”demo()“ 效果是一致的,但后者可以传参。
<div id="root"> <button v-on:click="showInfo">点我试试</button> <button @click="showInfo1">点我在试试</button> <button @click="showInfo2(666)">来试试</button> </div> <script> new Vue({ el:'#root', methods: { showInfo(){ alert('试试就试试'); }, showInfo1(){ alert('我就不试试'); }, showInfo2(num){ console.log(num); // 666 alert('来吗来吗'); } }, }) </script>
事件对象 $event
<a href="https://www.baidu.com" @click="one($event)">阻止去百度</a> methods: { one(e){ e.preventDefault(); } }
事件修饰符
1.prevent:阻止默认行为(常用)
2.stop:阻止事件冒泡(常用)
3.once:事件只触发一次(常用)
4.capture:使用事件的捕获模式
5.self:只有e.target是当前操作的元素时才触发事件
6.passive:事件的默认行为立即指向,无需等待事件回调执行完毕
<div id="root"> <!-- 1.prevent:阻止默认行为(常用)只弹窗 不跳转 --> <a href="https://www.baidu.com" @click.prevent="showInfo"> 点我去百度 </a> <!-- 2.stop:阻止事件冒泡(常用)只会弹一次窗 --> <div @click="showInfo"> <button @click.stop="showInfo">阻止事件冒泡</button> </div> <!-- 3.once:事件只触发一次(常用)能点一次 在点无效果 --> <button @click.once="showInfo">事件只触发一次</button> </div> <script> new Vue({ el:'#root', methods: { showInfo(){ alert('待在这吧') } }, }) </script>
键盘事件
Vue中常用按键别名:
回车 --- enter
删除 --- delete
退出 --- esc
空格 --- space
换行 --- tab(特殊,必须配合keydown去使用)
上 --- up
下 --- down
左 --- left
右 --- right
系统修饰键(用法特殊):ctrl、alt、shift、meta(徽标键)
(1).配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
(2).配合keydown使用:正常触发事件。
Vue计算属性 computed:{ }
定义:要用的属性不存在,要通过已有的属性计算得来。
原理:底层借助了Object.defineproperty方法提供的getter和setter。
get函数什么时候执行?
1.初次读取数据时候会执行一次。
2.当依赖的数据发生改变时会被再次调用。
3.return的返回值就是计算属性的值
优点:与methods对象相比,内部有缓存机制(复用),初次加载时只会调用一次,效率更高。
注意:
1.计算属性最终会出现在vm(Vue实例对象)上,直接读取使用即可。不用加小括号!!
2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
<div id="root"> 姓: <input type="text" v-model="firstName"> <br><br> 名: <input type="text" v-model="lastName"> <br><br> <span>全名:{{fullName}}</span> </div> <script> const vm = new Vue({ el:'#root', data() { return { firstName:'张', lastName:'三', } }, computed:{ fullName:{ // get函数初次加载数据时候会执行一次 当依赖的发生改变会被调用一次 get(){ return `${this.firstName}-${this.lastName}` }, // set函数 修改计算属性时去响应修改 set(value){ this.firstName = value.split('-')[0]; this.lastName = value.split('-')[1]; } } } }) </script>
Vue监视属性 watch:{ }
1.当被监视的属性变化时,回调函数自动调用,进行相关操作
2.监视的属性必须存在,才能进行监视!!!
3.监视的两种写法:
(1)new Vue时在配置对象里面写watch配置
(2)通过vm.$watch监视
4.handler函数什么时候调用?
(1)当被监视的属性发生改变时
(2)会返回两个参数,一个是修改后的值,一个是修改前的值。
<div id="root"> <h3>今天天气很{{info}}</h3> <button @click="change">点我切换天气</button> </div> <script> const vm = new Vue({ el:'#root', data() { return { isHot:true } }, methods: { change(){ this.isHot = !this.isHot } }, computed:{ info(){ return this.isHot ? '炎热' : '凉爽' } }, watch:{ isHot:{ handler(newValue,oldValue){ console.log(`修改后的值${newValue},修改前的值${oldValue}`); } } } }) </script>
深度监视 deep
以下没代码没有深度监视,触发a和b的修改,但不会触发numbers的修改。
加上深度监视 deep,触发a或b都会触发numbers修改。
Vue中计算属性和监视属性的区别
有无属性的区别:
1.计算属性是要用的属性不存在,通过已有的属性计算获得。
2.监视属性是被监视的属性必须存在
功能区别:
1.计算属性能完成的功能,监视属性都可以完成。
2.监视属性能完成的功能,计算属性不一定能。例如:监视属性可以进行异步操作,计算属性不可以。
主要函数调用方式的区别:
1.计算属性里的get()函数,初次加载会调用一次,然后依赖的数据发生改变时会调用。
2.监视属性里的handler()函数,初次加载不会调用,只有被监视的属性发生改变时会调用。
组件通信方式
父传子 props
父组件:一般是App.vue
子组件:引入到App.vue的组件
实现步骤:
1.在子组件内使用 props:['变量名1','变量名2',......] 定义变量,准备接收父组件传来的值,然后使用变量
<template> <div> <h1> {{title}} </h1> <h1> {{price}} </h1> </div> </template> <script> export default { props: ['title','price'] } </script>
2.在父组件内,引入子组件,注册子组件,使用组件,传值进去
<template> <div> // 3.使用子组件 传值进去 <Panel title="可口可不可乐" price="2.88"></Panel> </div> </template> <script> // 1.引入子组件 import Panel from './components/Panel.vue' export default { // 2.注册子组件 components:{ Panel } } </script>
总结:
子组件内,props定义变量,在子组件使用变量
父组件内,使用子组件,以属性的方式给props变量传值
单向数据流
从父到子的数据流向,叫单向数据流。
单向数据流引起的问题:
1.如果子组件修改父组件传来的值,不通知父级,会造成数据不一致性
2.Vue规定props里的变量,本身是只读的
子传父 子->this.$emit() 父->自定义事件名
实现步骤:
1.父组件内,绑定自定义事件和事件处理函数
语法:@自定义事件名="父methods里函数名"
<template> <div> <Panel title="可口可不可乐" price="2.88" ***自定义事件名和事件处理函数*** @subprice="fn" ></Panel> </div> </template> <script> import Panel from './components/Panel.vue' export default { components:{ Panel }, methods:{ ***title,price是子组件传来的值作为参数*** fn(title,price){ ....... } }, } </script>
2.子组件内,恰当的时机(例如触发事件),触发父绑定的自定义事件,导致父methods里的事件处理函数指向,使用this.$emit('父绑定的自定义事件',参数1,参数2,....)传值
<template> <div> <h1> {{title}} </h1> <h1> {{price}} </h1> <button @click="btn">恰当的时机</button> </div> </template> <script> export default { props: ['title','price'], methods:{ btn(){ this.$emit('subprice',this.title,this.price) } } } </script>
总结:
父组件内,给组件@自定义事件="父methods函数"
子组件内,恰当的时机this.$emit('自定义事件名',值)
兄弟组件传值 EventBus.$emit EventBus.$on
如何实现没有任何引入关系的组件,互相通信?
答:使用EventBus跨组件通信,传值的组件使用EventBus.$emit('事件名',值);接收值的组件使用EventBus.$on('事件名',函数体),且得写在create()钩子函数里!
实现步骤:
1.在src创建一个EventBus空文件夹里面创建一个index.js文件放一个空的Vue实例对象,作为一个中转站
2.需要传值的组件引入EventBus文件夹下的index.js,使用EventBus.$emit('事件名',值) 传值。
3.需要接收值的组件引入EventBus文件夹下的index.js,使用EventBus.$on('事件名',函数体) 接收值。写在create()钩子函数里
总结:
EventBus的本质:空白Vue对象,只负责$emit和$on
Vue生命周期
过程:从new Vue() 实例对象开始一直到被销毁
分为4个阶段,每个阶段对应两个钩子函数。初次加载会触发初始化阶段和挂载阶段
初始化阶段:beforecreate created
befocreate:new Vue()以后, vue内部给实例对象添加了一些属性和方法, data和methods初始化"之前"。
created:data和methods初始化以后 常用于网络请求, 注册全局事件
挂载阶段:beforeMount mounted
beforeMount:真实DOM挂载之前 常用于预处理data,但不会触发update钩子函数
mounted:真实DOM挂载之后,在这个阶段第一次获取真实DOM 常用于挂载真实DOM
更新阶段:beforeupdate updated
触发该阶段的前提是data数据发生改变
beforeupdete:数据更新之前
updated:数据更新之后 常用于获取更新后的真实DOM
销毁阶段:beforedestroy destroyed
触发该阶段需要通过v-if=‘false’ 销毁vue实例
该阶段会移除全局事件,移除当前组件,计时器,定时器,eventbus移除事件$on
ref的使用
作用:在mounted钩子函数里用于获取原生DOM或者组件对象
ref属性获取原生DOM元素
<h1 ref="myH">获取原生dom</h1>
通过this.$refs.myH获取DOM元素
mounted(){ //mounted 生命周期内部 console.log(this.$refs.myH); // h1 },
ref属性获取组件对象
1.创建一个Demo组件,写一个方法 并在App.vue中使用
<template> <div> <p>我是Demo组件</p> </div> </template> <script> export default { methods: { fn(){ console.log("demo组件内的方法被调用了"); } } } </script>
2.给Demo组件绑定ref属性
<template> <div> <Demo ref="de"></Demo> </div> </template>
3.通过this.$refs.de 获取组件对象并可以调用组件里的方法和属性
mounted(){ this.$refs.de.fn(); // demo组件内的方法被调用了 }
nextTick的使用
作用:因为Vue更新DOM是异步,马上获取DOM里的内容,获取的仍然是更新之前的数据。使用this.$nextTick(函数体)可以等DOM更新后,触发该方法里的函数体执行相应的操作
<template> <div> <p ref="myP">{{ count }}</p> <button @click="btn">点击count+1, 马上提取count</button> </div> </template> <script> export default { data(){ return { count: 0 } }, methods: { async btn(){ this.count++; // vue监测数据更新, 开启一个DOM更新队列(异步任务) console.log(this.$refs.myP.innerHTML); // 0 // 原因: Vue更新DOM异步 // 解决: this.$nextTick() // 过程: DOM更新完会挨个触发$nextTick里的函数体 //this.$nextTick(() => { // console.log(this.$refs.myP.innerHTML); // 1 //}) // 简写 通过使用await取代回调函数 需要配合async使用 // $nextTick()原地返回Promise对象 await this.$nextTick() this.$refs.myP.innerHTML } } } </script>
组件进阶
动态组件
多个组件使用同一个挂载点,并动态切换,这就是动态组件。
步骤:
1.准备被切换的 组件1/组件2 两个组件
2.把被切换的组件引入到另一个组件这注册
3.准备一个变量来承载要显示的"组件名"
4.设置挂载点 <component :is="变量名"></component> 用is属性来设置要显示哪个组件
<template> <div> <button @click="comName = 'UserName'">账号密码填写</button> <button @click="comName = 'UserInfo'">个人信息填写</button> <p>下面显示注册组件-动态切换:</p> <div style="border: 1px solid red;"> <component :is="comName"></component> </div> </div> </template> <script> // 目标: 动态组件 - 切换组件显示 // 场景: 同一个挂载点要切换 不同组件 显示 // 1. 创建要被切换的组件 - 标签+样式 // 2. 引入到要展示的vue文件内, 注册 // 3. 变量-承载要显示的组件名 // 4. 设置挂载点<component :is="变量"></component> // 5. 点击按钮-切换comName的值为要显示的组件名 import UserName from '../components/01/UserName' import UserInfo from '../components/01/UserInfo' export default { data(){ return { comName: "UserName" } }, components: { UserName, UserInfo } } </script>
组件缓存 keep-alive
作用:频繁的切换组件会导致组件频繁的创建和销毁,降低了性能。使用Vue内置的keep-alive组件,可以让包裹的组件保存在内存中不被销毁。被组件缓存的组件会触发两个钩子函数 activated(激活)和deactivated(失去激活状态)
总结: keep-alive可以提高组件的性能, 内部包裹的标签不会被销毁和重新创建, 触发激活和非激活的生命周期方法。
激活和非激活生命周期钩子函数
activated – 激活时触发
deactivated – 失去激活状态触发
作用:可以知道缓存的组件是出现了还是消失了
keep-alive里的两个属性值 include和exclude
<keep-alive include="组件A"></keep-alive> 组件A会被缓存,其他组件不缓存
<keep-alive exclude="组件A"></keep-alive> 除了组件A,其他组件都被缓存
匿名插槽
组件插槽使用 为了让封装的组件显示不同的标签结构
使用步骤:
1.组件内<slot></slot>占位
<div> <!-- 如果不传入任何内容 默认显示slot里的内容 --> <slot>默认显示的内容</slot> </div>
2.使用组件,传入具体的标签替换到slot位置上
<Panel> 我是内容:<img src="./assets/logo.png" alt=""> </Panel>
具名插槽 v-slot:name属性值
一个组件内有2个以上位置需要显示不同标签
使用步骤:
1.给slot一个name属性
<div> <slot name="content">默认显示的内容</slot> </div>
2.使用组件 用template标签相互关联 对应slot里name的属性值 v-slot:name属性值
<Panel> <template v-slot:content> <p>啊啊啊啊啊啊啊</p> <p>大大大大大大的</p> <p>啊啊啊啊啊啊啊</p> <p>水水水水水水是</p> </template> </Panel>
作用域插槽 v-slot="变量名"
使用插槽,使用组件内的变量
使用步骤:
1.slot标签,自定义属性和内变量关联
<div class="container" v-show="isShow"> <slot :changeName="defaultObj">{{defaultObj.defaultOne}}</slot> </div>
2.使用组件, template配合v-slot="变量名" 变量名会收集slot身上属性和值形成对象
v-slot="scoped" === scope:{changeName} changeName===defaultObj
<template v-slot="scoped"> <p>{{scoped.changeName.defaultTwo}}</p> <p>{{scoped}}</p> </template>
VueRouter路由
配置一级路由
1.安装路由第三方模块 Vue2中:npm i vue-router@3 Vue3中:npm i vue-router@4
2.在main.js文件中引入路由第三方模块 impot VueRouter from ‘vue-router’
3.通过Vue.use(VueRouter)注册成为全局组件
4.通过 new VueRouter({}) 配置路由规则 并返回一个实例对象接收一下 const router = new VueRouter({})
5.在Vue实例对象上挂载router:router
配置多级路由
在一级路由下使用children属性 以此类推 套娃
路由里的<router-link></router-link>和<route-view></route-view>组件
Vue中借助router-link标签实现路由的切换 ---- 浏览器解析出来是a标签
Vue中借助router-view指定组件的呈现位置
query和params参数
query参数传参
跳转路由并携带query参数,to的字符串写法
跳转路由并携带query参数,to的对象写法
接收参数的组件使用 this.$route.query.属性名
params参数传参
跳转路由并携带params参数,to的字符串写法
1.先在路由配置规则里的path路径上定义属性名
跳转路由并携带params参数,to的对象写法
接收参数的组件 使用 this.$route.params.属性名
总结:query传参和params传参的区别
query传参:参数会显示在地址栏中 页面刷新参数仍然存在
params传参:参数不会显示在地址栏中 页面刷新参数是underfind