初始Vue
- 想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象
- root容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法
- root容器里的代码被称为vue模板
- Vue实例和容器是一一对应的
- 真实开发中只有一个Vue实例,并且会配合组件一起使用
- {{xxx}}中的xxx要写js表达式,并且xxx可以自动读取到data中的所有属性,一旦data中的数据发生改变,那么模板中用到该数据的地方也会自动更新
- js表达式和js语句的区别:表达式:一个表达式会产生一个值,并且可以放在任何一个需要值的地方,如:a,q+b,function(1),x===y?a:b;js语句:if(),for()
hello,{{name}}
模板语法
- 插值语法:
功能:用于解析标签体内容
写法:{{xxx}},xxx是js表达式,并且可以直接读取到data中的所有属性 - 指令语法:
功能:用于解析标签(包括:标签属性,标签体内容,绑定事件...)
举例:v-bind:href="xxx" 或 简写成 :href="xxx",xxx同样要写成js表达式,并且可以直接读取data值的所有值
数据绑定
vue中有两种数据绑定的方式
- 单向绑定(v-bind):数据只能从data流向页面
- 双向数据流(v-model):数据不仅能从data流向页面,还能从页面流向data
1.双向绑定一般懂用于表单类元素上(如:input,select等),2.v-model:value 可以简写成v-model,因为v-model默认的是value的值
el与data的两种写法
- el的两种写法:a:new Vue()的时候配置el属性,b:先创建Vue实例,随后通过vm.$mount("#root")指定el的值
- data的两种写法:a:对象式,b:函数式.如何选择:在组件中写的时候必须用函数式,否则会报错.
一个重要的原则:由Vue管理的函数,一定不要写成箭头函数,一旦写成箭头函数,this的指向就不再是Vue实例了
Object.defineProperty
数据代理
- 通过一个对象代理对另一个对象中属性的操作(读/写)
let obj = {x:100}
let obj2 = {y:200}
Object.defineProperties(obj2,'x',{
get(){
return obj.x
},
set(value){
obj.x = value
}
})
console.log(obj2.x) //输出结果为100
obj2.x = 300,
console.log(obj.x) //输出值为300 直接打印obj也可以看出
//上述代码通过数据代理,通过obj2就可以操作obj中x的值
Vue中的数据代理
- 1.Vue中的数据代理:通过vm对象来代理data对象中属性的操作(读/写)
- 2.Vue中数据代理的好处:更加方便的操作data中的数据
- 3.基本原理:通过Object.defineProperties()把data中的所有属性添加到vm身上.为每一个添加vm身上的属性都指定一个对应的setter和getter.在getter和setter内部去操作(读/写)data中对应的属性
常见指令
- 事件处理v-on:xxx
事件的基本使用:1.使用v-on:xxx 或 @xxx 绑定事件,其中xxx是事件名.2.事件的回调需要配置在methods对象中,最终会在vm上.3.mehods中配置的函数,不要用箭头函数,否则this就不是vm了.4.methods中配置的函数,都是被Vue所管理的函数,this的指向是vm或组件实例对象.5.@click="demo"和@click=demo($event)效果一样,但后者可以传参
vue中的事件修饰符
- prevent 阻止默认事件(常用)
- stop 阻止冒泡事件(常用)
- once 事件只触发一次(常用)
- capture 使用事件的捕获模式
- self 只有event.target是当前操作的元素时才触发事件
- passive 事件的默认行为立即执行,无需等待事件回调执行完毕
键盘事件
- enter 会车键
- delete 删除
- esc 退出
- tab 换行(特殊,必须配合keydown使用)
计算属性与监视
{{fullName}}
深度监视
- vue中的watch默认不监测对象内部值的改变(一层)
- 配置deep:true可以检测对象内部的改变(多层)
今天天气很{{info}}
computed和watch的区别
1.computed能完成的功能,watch都可以完成,2.watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作
两个重要小原则
1.所被vue管理的函数,最好写成普通函数,这样this的指向才是vm或组件实例对象
2.所有不被vue所管理的函数(定时器的回调函数,ajax的回调函数,promise的回调函数)
,最好写成箭头函数,这样this的指向才是vm或组件实例对象
绑定样式
- class样式
:class="xxx", xxx可以是字符串,对象,数组.字符串写法用于:类型不确定,需要动态获取.对象写法适用于:要绑定多个样式,个数不确定,名字也不确定.数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用 - style样式
:style = "{fontSize:xxx}" 其中xxx是动态值.:style='[a,b]' 其中a,b是样式对象
{{name}}
{{anme}}
{{anme}}
{{anme}}
{{anme}}
{{anme}}
{{anme}}
条件渲染 v-show v-if
v-show是在样式上隐藏disply:none,v-if直接不创建这个组件.在开发中如果切换频率高的显示和隐藏用v-show
列表渲染 v-for
可以遍历数组,字典,字符串,指定次数
人员列表
{{p.name}}---{{p.age}}
Vue中key的作用
key是虚拟Dom对象的标识,当数据发生变化时,Vue会根据新数据生成新的虚拟Dom,随后Vue进行新虚拟Dom与旧虚拟Dom的差异比较,比较规则如下:
- 旧虚拟Dom中找到了与新虚拟Dom相同的key:1.若虚拟Dom中内容没有变化,直接使用之前的真实Dom.2.若虚拟Dom中内容变化了,则生成新的真实Dom,随后替换掉页面中之前变化的真实Dom
- 旧虚拟Dom中未找到与新虚拟Dom相同的key:创建新的真实Dom,随后渲染到页面
Vue监视数据的原理
1.vue会检测data中的所有层次的数据
2.如何检测对象中的数据
通过setter实现监视,且要在new Vue的时就传入检测的数据
- 对象中后裔追加的属性,Vue默认不做响应式处理
- 如需给后添加的属性做响应式,请使用如下API:
Vue.set(target,propertyName/index,value)或者Vue.$set(target,propertyName/index,value)
3.如何检测数组中的数据?
通过包装数组更新元素方法的实现,本质上就是做了两件事
- 1.调用原生对应的方法对数组进行更新
- 2.重新解析模板,进而更新页面
4.在Vue中修改数组中的某个元素一定要用如下方法(不要用下标赋值的方法去做):
1.使用这些API:push(),pop(),shift(),splice(),sort(),reverse()
2.Vue.set() 或 Vue.$set()
特别注意:Vue.set()和Vue.$set()不能给vm或者vm的根元素(data)添加属性
input表单有关
- 若:,则v-model收集的是vlaue值,用户输入的就是value
- 若:,则v-model收集的是value值,且要给标签配置value值
- 若:
1.没有配置input的value值,那么收集的就是checked(勾选 或 未勾选,是布尔值)
2.配置input的value值:
(1)v-model的初始值是非数组,那么收集的就是checked(勾选 或 未勾选,是布尔值)
(2)v-model的初始值是数组,那么收集的就是value值组成的数组
v-model的三个修饰符:lazy:失去焦点再收集数据.number:输入字符串转为有效的数组.trim:输入收尾空格过滤
过滤器
定义:对现实的数据进行特定格式化后再显示(适用于一些简单的逻辑处理)
语法:
1.注册过滤器:Vue.filter(name,callback) 或 new Vue{ filter:{} }
2.使用过滤器:{{xxx | 过滤器名}} 或v-bind:"xxx | 过滤器名"
备注: 1.过滤器也可以接收额外的参数,多个过滤器也可以串联.2.并没有改变原本的数据,是产生新的对应的数据.(过滤器的值是挨个传递的,第一个过滤器返回的值作为第二个返回值的参数使用值)
过滤器,显示格式化之后的时间
现在的时间是:{{time}}
现在的时间是):{{time | timeForamter}}
现在的时间是:{{time | timeForamter('YYYY_MM_DD') | mySlice}}
内置指令
- v-text:向其所在的节点中渲染文本内容,与插值语法的区别:v-text会替换掉节点中的内容,{{xxx}}则不会
- v-html:像指定节点渲染包含html结构的内容.ps:v-html有安全性问题,容易导致xss攻击(获取到网站上的cookie)
- v-cloak:可以通过设置属性形式[v-cloak]的display:none在数据未加载之前隐藏节点,避免出现插值语法
- v-once:只渲染一次
- v-pre:预编译指令,跳过其所在的节点编译过程(不会解析节点所在的插值语法,指令语法)
自定义指令
{{name}}
当前的值是:
增加10倍的值是:
生命周期
常用的生命周期钩子:
1.mounted:发送ajax请求,启动定时器,绑定自定义事件,订阅消息[等初始化操作]
2.beforeDestroy:清除定时器,解绑自定义事件,取消订阅消息等[收尾工作]
关于销毁Vue实例:
1.销毁后借助Vue开发者工具看不到任何消息
2.销毁后自定义事件会失效,但原生的Dom事件依然有效
3.一般不会在beforeFestory操作数据,因为即便操作数据,也不会再触发更新流程
//无法通过vm访问data中的数据,methods中的方法
beforeCreate() {
},
//可以通过vm访问到data中的数据,methods中配置的方法
created() {
},
// 此时1.页面呈现的是未经Vue编译的Dom结构.2.所有对Dom的操作,最终都不奏效
beforeMount() {
},
//Vue完成模板的解析,并把初始的真实DOM元素放入页面后(挂载完毕)调用mounted.此时1.页面中呈现的是经过Vue编译的Dom.2.对Dom的操作均有效(尽可能避免).
//至此初始化过程结束,一般在此进行:开启定时器,发送网络请求,订阅消息,绑定自定义事件等初始化操作
mounted() {
},
//此时:数据是新的,但页面是旧的,即:页面尚未和数据保持同步
beforeUpdate() {
},
// 此时:数据是新的,页面也是新的.即:页面和数据保持同步
updated() {
},
// 此时:vm中所有的:data,methods,指令等等.都处于可用状态,马上要执行销毁过程.一般在此阶段:关闭定时器,取消订阅消息,解绑自定义事案件等收尾操作
beforeDestroy() {
},
// 销毁完毕
destroyed() {
},
组件
实现应用中局部功能代码和资源的集合.分为非单文件组件和单文件组件.非单文件一般是在html中包含很多组件.单文件组件一般是值一个.vue文件就是一个组件
使用(非单文件组价)组件的步骤:
- 1.创建组件
//Vue.extend()可以省略
const school = Vue.extend({
// 注意Vue2只允许也有个根标签
template:`
{{schoolname}}
{{schooladdress}}
`,
// 组件中的data必须用函数式,不然不能保证数据修改不互相影响
data() {
return {
schoolname:'上海高级中学',
schooladdress:'青浦区十二河西路45号'
}
},
})
- 2.注册组件
const vm = new Vue({
dta:{
name:"世界你好",
n:1
},
// 此处为局部注册
components:{
school:school
}
})
//全局注册
vue.component('school',school)
- 3.使用组件
- 关于组件名的注意点:
一个单词组成的:school,School.多个单词组成的my-school,MyScool(需要脚手架支持)
关于VueComponent
- 1.school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的
- 2.我们只需要写
或 ,Vue解析时会帮助我们创school的实例对象,即帮助我们执行:new VueComponent(optios) - 3.特别注意:每次调用Vue.extend({}),返回的都是一个全新的VueComponent
- 4.关于this的指向
(1).组件配置中:
data函数.methods函数,watch中的函数,computed中的函数,他们的this均是(VueComponent实例对象)
(2).new Vue()配置中:
data函数.methods函数,watch中的函数,computed中的函数,他们的this均是(Vue实例对象)
- 5.Vuecomponent的实例对象,以后简称VC(也可成为组件实例对象).vue的实例对象简称VM
Vue中重要的内置关系
- 1.VueComponent.prototype.proto === Vue.prototype
- 2.为什么有这个内置关系:让组件的实例对象(vc)可以访问到Vue原型上的属性和方法
//参考原型
//实例的隐式原型属性永远指向自己缔造者的原型对象
//定义一个构造函数
function Demo(){
this.a = 1
this.b = 2
}
//创建一个demo的实例对象
const d = new Demo()
console.log(Demo.prototype) //显式原型属性(只有函数才有的)
console.log(d.__proto__) //隐式原型属性(对象中有的)
//指向同一个原型对象
console.log(Demo.prototype === d.__proto__) //输出true
//程序员通过显式原型属性操作原型对象,追加一个x属性,值为99
Demo.prototype.x = 99
// 下面两种写法一样
console.log(d.__proto__.x)
console.log(d.x)
单文件组件(xxx.vue)
//快捷键
学校名字:{{name}}
学校地址:{{address}}
ref属性
- 被用来给元素或子组件注册引用信息(通过id获取元素的替代者)
- 应用在html标签上获取的是真实的DOM元素,应用在组件标签上是组件实例对象(vc)
- 使用方式:
打标识:...
或者
获取:this.$refs.xxx
略略略
props属性
让组件接收外部传过来的数据
- 1.数据传递
- 2.接收数据
第一种方式(只接受):
props:['name']
第二种方式(限制类型):
props:{
name:String
}
第三种方式(限制类型,限制必要性,指定默认值):
props:{
name:{
type:String, //类型
require:true, //必要性
default:'老王' //默认值
}
}
props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告.若业务需求确实要修改,那么请复制props的内容到data中一份,然后去修改data中的数据
mixin 混入
// mixin.js把公共的抽离出来
export const hunhe = {
data:{
x:10,
y:100
},
//methods中的方法如果和被混入对象中的方法一样时,以被混入对象的为准
methods:{
click(){
console.log('点击了')
}
},
//和被混入对象中的钩子函数都调用
mounted(){
console.log('这是钩子函数')
}
}
//-------------------------
//组件vc
// 1.导入 2.使用
import { hunhe } from '../mixin.js'
{
data:{
name:小明
},
//以数组形式加入混合
mixins:[ hunhe ]
methods:{
},
mounted(){
}
}
插件
功能:用于增强Vue
本质:傲寒install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据.
定义插件:
//plugis.js
export default {
inastall (Vue,options){
//1.添加全局过滤器
Vue.filter(...)
//2.添加全局指令
Vue.directive(...)
//3.配置混入对象
Vue.mixin(...)
//添加实例方法
Vue.prototype.$myMethod = function(){...}
Vue.prototype.$myProperty = xxx
}
}
使用插件:
导入 import plugin from '../plugins.js'
//用Vue.use(插件名)
Vue.use(plugin)
scoped样式
作用:让样式在局部生效,防止冲突.
写法:
浏览器的本地存储(localStorage,sessionStorage)
//存储
localStorage.setItem(key,value) //key和value必须都是字符串
//读取
localStorage.getItem(key)
//删除
localStorage.removeItem(key)
//清空
localStorage.clear()
//ps:sessionStorage和localStorage的用法一样,只不过浏览器关闭sessionStorage会被清除,而localStorage不会被清除
组件的自定义事件
- 一种组件间通信的方式,适用于:子组件 ===>父组件
- 使用场景:A是父组件,B是子组件,B想给A传数据,那么就在A中给B绑定自定义事件(事件的回调在A中)
//通过父组件子组件传递函数类型的props事件(函数回调传值),子给父传递数据
//通过父组件给子组件定义一个自定义事件实现:子给父传递数据(使用V-on或者@)
//子组件通过this.$emit('myClcik',"666","888"),后面参数也可以包装成一个对象
//通过父组件给子组件绑定一个自定义事件:子给父传递数据(使用ref)
// this.$refs.hell.$on('监听的事件名',function(){
//注意:如果函数直接写在这里,以普通函数的写法去写,那这里的this是HelloWoreld的VueComponent(vc).解决办法:第一种箭头函数,第二种可以把这个函数拆分到methods中去
})
//解绑自定义事件
this.$off('clickMthods') //解绑一个
this.$off(['clickMthods','schoolNameMthod']) //解绑多个
this.$off() //解绑所有的自定义事件
组件绑定原生事件,如@click,系统会判定会这是一个自定义事件,想要原生事件生效,应该用native修饰.@click.native='xxx'
全局事件总线(GlobalEventbus)
- 1.一种组件间通信的方式,适用于任意组件间通信
- 2.安装全局事件总线:
new Vue({
.....
beforeCreate(){
Vue.prototype.$bus = this //暗转全局事件总线,$bus就是当前应用的vm
}
})
- 3.使用事件总线
1.接收数据:A组件想接受数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身
methods(){
demo(prams){
.....
}
....
mounted(){
this.$bus.$on('xxx',this.demo)
}
}
2.提供数据:this.emit('xxx',传递的数据)
- 4.最好在beforeDestory钩子中,用$off去解绑当前组件所用到的事件
消息的订阅与发布
1.一种组件间通信的方式,适用于任意组件
-
2.使用步骤:
1.安装pubsub:npm i pubsub-js
2.引入:import pubsub from 'pubsub-js'
3.接收数据:A组件想接受数据,则在A组件中订阅消息,订阅的回调留在A组件自身.methods(){ demo(parmas){ .... } .... mounted(){ this.pid = pubsub.subscribe('xxx',this.demo) }, deforeDestory(){ Pubsub.unsubscribe(this.pid) } }
4.提供数据:pubsub.publish('xxx',数据)
5.最好在deforeDestory钩子中,用Pubsub.unsubscribe(pid)去取消订阅nextTick
- 1.语法:this.$nextTick(回调函数)
- 2.作用:在下一次DOM更新结束后执行其指定的回调
- 3.什么时候调用:当改变数据后,要基于更新后的新DOM进行某些操作时(如输入框的失去焦点),要在nextTick所指定的回调函数中执行
Vue封装的过渡和动画
作用:咋插入和移除DOM元素时,在合适的时候给元素添加样式类名
写法(-).准备好样式: 元素的进入样式 1.v-enter:进入的起点 2.v-enter-active:进入过程中 3.v-enter-to:进入的终点 元素离开的样式: 1.v-leave:离开的起点 2.v-leave-active:离开过程中 3.v-leave-to:离开的终点 (二)使用
包裹要过渡的元素,并配置name属性 你好啊
(三)若有多个元素需要过渡,则需要使用:
> 项目中可以用第三方动画库,如:animate.css
#### 配置代理(解决跨域问题)
什么是跨域:当一个请求url的协议,域名.端口号三者之间任意一个与当前页面url不同即为跨域
- 方法1:在Vue.config.js中添加如下配置
devServer:{
proxy:'http://localhost:5000'
}
//说明:
1.优点:配置简单,请求资源时直接发给前端(8080)即可
2.缺点:不能配置多个代理,不能灵活的控制请求是否走代理
3.工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器
- 方法2:编写vue.config.js配置具体代理规则
module.exports= {
devServer:{
proxy:{
'/api1':{//匹配所有以'/aopi1'开头的请求
target:"http://localhost:5000",//代理目标的基础路径(不要写后面的路径了)
ws: true,// 是否启用websockets
changeOrigin: true,//是否开启代理
pathRewrite:{ 'api1':'' }//匹配以api开头的路径替换成空字符串
}
}
}
}
#### 插槽slot
1.作用:让父组件可以让子组件指定位置插入html结构,也是一种组件间通信的方式,适用于父组件===>子组件
2.分类:默认插槽,具名插槽,作用域插槽
3.使用方式:
-.默认插槽:
父组件:
子组件:
二.具名插槽
父组件:
子组件:
三.作用域插槽
//理解:数据在组件自身,但根据数据生成的结构需要组件的使用者来决定.
父组件:
{{ g }}
{{ g }}
子组件:
#### vuex
专门在vue中实现集中式状态,数据管理的一个vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间的通信方式,且适用于任意组件间的通信
1.//创建文件:src/store/index.js
//引入vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用插件(需要在stroe实例创建前使用,不然会报错)
Vue.use(Vuex)
//准备actios对象----响应组件中用户的动作
const actions = {}
//准备mutatios对象---修改state中的数据
const mutatios = {}
//准备state对象---保存具体的数据
const state = {}
//准备getters---用于将state中的数据进行加工
const getters = {}
//并暴露store
export default new Vuex.store({
actions,
mutations,
state,
getters
})
2.在main.js中创建vm时传入store配置项
//引入store
import store from './store'
...
//创建vm
new Vue({
el:'#app'
render:h=>h(App)
store
})
#### 路由
1.理解:一个路由就是一组映射关系(key-vlaue),多个路由需要路由器(router)进行管理
2.前端路由:key是路径,vlaue是组件
##### 基本使用
1.安装vue-router,命令: npm i vue-router
2.应用插件:Vue.use(VueRouter)
3.编写router配置项
//引入VueRouter
import VueRouter from 'vue-router'
//引入组件
import About from '../components/about'
import Home from '../components/home'
//创建router实例对象,去管理一组一组的理由规则
const router = new vueRouter({
routes:[
{
path:"/about",
component:About
},
{
path:"/home",
component:Home
},
]
})
//暴露router
export default router
4.实现切换(active-class可配合样式进行高亮显示等)
5.指定展示位置