Vue基础学习
一、概念
中小型企业适合用Vue
大型公司一般用react
1、框架和类库的区别:
举例:jQuery好处:抹平了各个浏览器之间的差异 链式操作dom
类似于一个套餐
框架:是一个整体 类似于全家桶 性能的优化,方法的实现
UI框架:bootstrap
2、概念:
Vue是MVVM框架
是渐进式的JavaScript框架
渐进式:只包含核心语法,如果需要其他功能需要单独安装例如 路由vuex 前后端数据交互的方式
好处:便捷不繁重(有需要才去安装)
什么是mvvm
mvc:model view controller
mvvm:
M层:model层 数据层 数据的增删改查
V: view层 视图层 类十余html一样的模板 主要用来作展示
VM: viewModel层 model层与view层之间的一个映射 联系model层和view层
例如:数据层发生改变 视图层也会跟着发生改变
数据驱动视图
二、vue基本使用
1、安装
vue不支持IE8及以下的版本
cdn引入
npm install vue下载
2、创建实例
<div id="app"> <h2> {{message}} </h2> </div> <script src="./vue.js"></script> <script> let vm = new Vue({ //作用:vue起作用的范围 el:"#app",//document.querySelector // 当前组件所需要的状态(vue所需要的属性) data:{ message:“xa-1901” } }) </script>
3、指令
v-text 简写:{{}}
底层原理:innertext 作用:渲染数据 v-text的值:任何js表达式
<div id="app"> <h2 v-text="message"></h2> <h2 v-text="arr[0]"></h2> //当vue没有引入时就是标签添加一个属性 //简写 //当vue没有引入时就是不能识别{{}},页面会显示{{}} <h2 >{{message}}</h2> <h2>{{ arr[0]}}</h2> </div> <script src="vue.js"></script> <script> var vm = new Vue({ el:"#app", data:{ message:"xz1901", arr:[10,20] } }) </script>
v-html
v-html:html
作用:渲染数据
底层原理 innerHTML
v-html的值:任何js表达式
能不用最好不用, 不安全
<div id="app"> <h2 v-html="message"></h2> <h2 v-html="arr[0]"></h2> </div> <script src="vue.js"></script> <script> var vm = new Vue({ el:"#app", data:{ message:"<h4>xz1901</h4>", arr:[10,20] } }) </script>
v-if
v-if:显示隐藏 值:布尔值 true:显示 false:隐藏 原理:操作dom节点
v-show
v-show:显示隐藏 值:布尔值 true:显示 false:隐藏 原理:display属性
v-if v-else v-else-if
限制:v-else v-else-if前面必须有v-if 作用:链式调用
<div id="app"> <h2 v-html="message"></h2> <h2 v-html="arr[0]"></h2> <hr> <h2 v-show="flag">{{message}}</h2> <h2 v-if="flag">{{message}}</h2> <hr> <h2 v-if="cases == 1">周一</h2> <h2 v-else-if="cases == 2">周二</h2> <h2 v-else-if="cases ==3">周三</h2> <h2 v-else-if="cases == 4">周四</h2> <h2 v-else="cases == 4">周四</h2> </div> <script src="vue.js"></script> <script> var vm = new Vue({ el:"#app", data:{ message:"xz1901", arr:[10,20], flag:false } }) </script>
v-for
语法:v-for="(数组中的值,数组中的下标) in 需要遍历的数据"
<div id="app"> <p v-for="(item,index) in data.arr">{{item}}----{{index}}</p> <p v-for="(item,key) in obj">{{item}}----{{key}}</p> <div id="box"> <div v-for="(item,index) in res"> <p>{{item.name}}</p> <p>{{item.age}}</p> </div> </div> </div> <script src="vue.js"></script> <script> var vm = new Vue({ el:"#app", data:{ arr:[10,20] } obj:{ name:“张三”, age:19, sex:“nv” }, res:[ {name:"lisi", age:20, sex:"nv", }, {name:"wangwu", age:19, sex:"nv",} ] }) </script>
v-bind
v-bind:绑定属性 id class style src title href 简写 :简写名称
<div id="app"> <div v-bind:id="id"> </div> <div :class="myclass"> </div> <div :class="myclassArr"> </div> <div :class="myclassObj"> </div> <div :style="mystyle"> </div> <img :src="path" :title="title"> </div> <script src="vue.js"></script> <script> var vm = new Vue({ el:"#app", data:{ id:"box", myclass:"box1 box2", myclassArr:["box1","box2"], myclassObj:{ "content1":true, "content2":false, }, myStyle:{ color:"red", }, path:"baidu.com", title:"一张图片" } }) </script>
v-on
v-on:事件绑定 v-on:click v-on:dblclick v-on:mouseover 简写方式 v-on ---->@ 用法: v-on:事件mingc.修饰符.修饰符 一个事件可以有多个修饰符
修饰符:
stop 阻止冒泡 prevent 阻止浏览器默认行为 once 只会触发一次 keydown 按下某个键出发 keydown.enter keydown.enter 按下enter键
如果在时间中用到事件对象,需要在事件中传入$event
<div id="app"> <div id="box" v-on:click="handle(abc)"><div> </div> <script src="vue.js"></script> <script> var vm = new Vue({ el:"#app", data:{ }, //当前vue所需的一些事件函数必须放在methods methods:{ handle(ddd){ console.log(1234567) } }) </script>
v-model
实现双数据绑定 使用的元素:form表单的元素可以使用v-model vue3.0中废除了 object.defineProperty() 换成es6的Proxy
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> </style> </head> <body> <div id="app"> <form action="" > <label for="">男:<input type="radio" v-model="radioVal" value="男"></label> <label for="">女:<input type="radio" v-model="radioVal" value="女"></label> </form> <div>您的性别是:{{radioVal}}</div> <hr> <form action=""> <label for="">篮球 <input type="checkbox" value="篮球" v-model="checkVal"></label> <label for="">足球 <input type="checkbox" value="足球"v-model="checkVal"></label> <label for="">排球 <input type="checkbox" value="拍球" v-model="checkVal"></label> <label for="">乒乓球 <input type="checkbox" value="乒乓球" v-model="checkVal"></label> <h3>您选的爱好是:</h3> <div v-for="item in checkVal">{{item}}</div> </form> </div> </body> </html> <script src="vue.js"></script> <script> var vm = new Vue({ el : "#app", data : { radioVal:"男", checkVal:[], }, methods:{ }, }) </script>
v-pre
v-pre:不解析{{}}中的数据包括{{}}
v-once
只会渲染一次数据
当数据发生改变的时候加v-once指令的标签数据不会发生改变
v-cloak
防止第一次渲染的时候{{}}的出现
<html> <body> <div id="app"> <div>{{box}}</div> <!-- 只改变一次 --> <div v-once>{{box}}</div> <!-- 原样输出 --> <div v-pre>{{box}}</div> <button @click=handle1>按钮</button> </div> </body> </html> <script src="vue.js"></script> <script> var vm = new Vue({ el:"#app", data:{ box:"1901", }, methods:{ handle1(){ this.box="1902"; }, } }); </script>
创建自定义指令
a、定义:
1、全局自定义指令 directive
2、局部自定义指令 directives
作用范围不同 写的位置不同
全局指令:
b、语法:
Vue.directive(参数1,参数2)
参数1,指令的名称
参数2、指令实现的方法
参数1:当前元素作用在哪个元素上
参数2:是一个对象
modifiers:修饰符
value:指令的表达式
expression:表达式
c、注意:
Vue.directive必须写在实例化之前
会有生命周期
Vue.directive('ccc',(el,{modifiers,value}))=>{
console.log("我是自定义制定")
}
d、拖拽案例
<style> #box{ width:200px; height:200px; background:red; position:absolute; } </style> <div id="app"> <div id="box" v-drag></div> </div> <script src="vue.js"></script> <script> Vue.directive("drag",(el,{modifiers,value})=>{ // console.log(rest) var ofX,ofY el.addEventListener("mousedown",downCallback); function downCallback(e){ //鼠标在当前元素内部的位置 ofX = e.offsetX; ofY = e.offsetY; document.addEventListener("mousemove",moveCallback); document.addEventListener("mouseup",upCallback); }; function moveCallback(e){ var x=e.clientX-ofX; var y=e.clientY-ofY; el.style.left=x+"px"; el.style.top=y+"px"; }; function upCallback(){ document.removeEventListener("mousemove",moveCallback); document.removeEventListener("mouseup",upCallback) } }) var vm = new Vue({ el:"#app", data:{}, methods:{}, computed:{} }) </script>
三、计算属性
1、定义
computed 计算属性 通过计算得来的属性
1、computed 里面的函数建议有返回值,不建议改变data中的属性
2、调用computed中的方法时,不需要加括号
3、基于vue依赖,当出发属性时会触发相对应的方法
4、当computed中函数执行完毕后,会执行缓存,当下次所依赖的函数没有发生改变时,会从缓存中读取数据
2、特点
一个属性受多个属性影响
自己触发不需要调用 缓存存数据
3、computed与methods 区别
computed是属性调用,调用不需要加括号,当下次所依赖的函数没有发生改变时,再次调用时,会从缓存中读取数据
methods是函数调用, 没有缓存存数据,每次调用每次执行
使用场景:一个属性受多个属性影响时会使用computed例如购物车的结算
<div id="app"> <input type="text"v-model.number="a"> <input type="text"v-model.number="b"> <p v-cloak>结果:{{handle()}}</p> <p v-cloak>结果:{{handleCom}}</p> </div> <script src="vue.js"></script> <script> var vm = new Vue({ el:"#app", data:{ a:"", b:"", sum:"", }, methods:{ handle(){ console.log("handle()被调用了") return this.a+this.b; } }, computed:{ handleCom(){ console.log("handleCom被调用了") return this.a+this.b; } } }) </script>
四、过滤器
1、定义
过滤器:用来过滤数据的一个接口
2、语法
全局: Vue.filter("name",(val,n,sign)=>{
执行的操作
return 值
})
参数1:过滤器名称
参数2:过滤器实现的方法
参数2:有三个参数:
1、val 内容
2、n 长度
3、sign 后缀
如何使用过滤器
通过管道符 | 管道符左边是渲染的数据,右边是过滤器的名称
局部: filters:{}
<div id="app"> <h2>{{message | reverse(3,'')}}</h2> </div> <script src="vue.js"></script> <script> Vue.filter("reverse",(val,n,sign)=>{ return "$"+val.substring(0,n).split("").reverse().join(sign) }); var vm = new Vue({ el:"#app", data:{ message:"xa1901" } }) </script
>
五.Vue实例方法
1、$on
$on 绑定事件
参数1:事件名称
canshu2:绑定的事件函数
2、 $emit
$emit 触发事件
参数1:名称
参数2:需要传递给事件函数的参数
3、$off
$off:解绑事件
参数1:事件名称 如果只写一个参数的话会解绑所有的事件
参数2:需要解绑的事件函数
4、$once
$once: 绑定一次事件
参数和on一样
<script> var vm = new Vue({ el:"#app", data:{ }, methods:{ } }); vm.$on("test",func1) //绑定 vm.$emit("test","345")//触发 vm.$off("test",func1)//解绑 function func1(val){ console.log(111,val) } function func1(val){ console.log(111,val) } vm.$once("test",func2)//绑定一次 vm.$emit("test","444")//触发 function func2(val){ console.log(222,val) } vm.$emit("test","666")//再次触发,不会执行 function func2(val){ console.log(222,val) } </script>
5、$mount
$mount() 挂载 外部挂载
vm.$mount("#app")//和el一样
6、$destroy
$destroy() 卸载 外部卸载
vm.$destroy("#app")
7、$forceUpdate
$forceUpdate 强制更新
迫使 Vue 实例重新渲染。
methods:{
changeA(){
this.obj.age = 19;
this.$forceUpdate();//强制更新
}
}
//将非响应式属性变为响应式属性
在实例外部去访问data中的属性
vm
vm.message
vm.$data.message
六、watch监听
1、定义
watch:做属性的监听
1、watch中的函数的名称必须是data中的属性
2、watch中参数会接受两个值,一个是新值,一个是旧值
3、watch中的函数时不需要调用的,但凡所依赖的属性发生了改变,那么就会调用相关的函数
4、在watch的属性监听当中尽量不要去修改data中的属性
5、 watch监听属性的时候,只能监听对象的引用是否发生改变,而具体的属性值发生改变是不会监听的
有两种方式可以监听到值
a、对象.属性
b、深度监听 handler(newVal,oldVal){} , deep:true
6、 watch做属性监听的时候,如果属性是一个对象,那么需要做深度监听,利用handler和deep
7、 watch初始化的时候是不会执行的 immediate:true
解决方法:添加immediate:true属性
8、watch不会对数组的修改(特殊情况:this.arr[0]=100)进行监听
解决方法:
1、用$set
2、数组的方法splice
2、 特点:
一个属性影响多个属性
3、适用场景:
模糊查询
网页自适应
发起后端请求
监听窗口的变化
computed场景:
商品总价结算
商品的筛选
$set:
给一个响应式对象添加一个属性,并且这个属性也是响应式的
对象的用法:
this.set(target,key,value);
Shuzu:
this.set((target,index,value);)
4、案例
<body> <div id="app"> <input type="text"v-model="name"> <h2>{{name}}</h2> <input type="text"v-model="obj.name"> <h2>{{obj.name}}</h2> <p v-for="item in arr">{{item}}</p> <button @click="haldleChange">按钮</button> </div> </body> <script src="vue.js"></script> <script> var vm = new Vue({ el:"#app", data:{ name:"zhangsan", obj:{ name:"moria", }, arr:[1,2,3,4,5] }, methods:{ haldleChange(){ this.arr[0]=100;//不能通过索引的方式直接操作数组 // this.arr.splice(0,1,199); // this.$set(this.arr,0,100); } }, watch:{ name(newVal,oldVal){ console.log(newVal+"+"+oldVal) }, // obj:{ // handler(newVal,oldVal){ // console.log("我变了") // }, // deep:true, // immediate:true //} "obj.name":{//对象打点要加引号 handler(newVal,oldVal){ console.log("我变成"+newVal) }, deep:true, immediate:true }, arr(newVal){ console.log("数组变了"); } } })
七、组件
1、概念:
由HTML css js 等 封装好的一个功能,便于二次维护和复用
2、 特点
复用性
页面上任何一个部分都是组件
3、与模块比较
模块:
一个大的功能,每一个模块可分为多个组件 首页模块分为头部组件,banner组件
可以理解为模块包含组件
4、组件的创建
全局组件
Vue.component()
参数1:组件名称 最好用大写用于区分组件名称和HTML标签
参数2:组件的配置项 -》对象
组件配置项与vm中的配置项一模一样
有两点不一样
1、多一个template
2、data不是属性,是一个函数
局部组件
components:{}
八、脚手架
脚手架:帮助快速搭建一个项目的骨架
1、安装
a、全局安装脚手架 cnpm install -g @vue/cli
b、当前项目下构建项目 vue create 项目名称
c、选择你需要的类目
2、目录介绍
src:开发环境目录 开发所需文件
assets 静态资源文件 可放置css,js文件
components 公共组件编写的地方
static:静态资源文件 图片 json数据等
test:单元测试、代码测试
.gitignore: git上传需要忽略的文件格式
package.json 项目的基本信息(项目的名称、项目所需模块、版本等)
bable:用来做代码转义
.vue文件:template写结构
script写徐建逻辑
style写组件样式
一个。vue文件相当于一个组件
render是一个配置项
是一个函数
用来渲染虚拟dom 渲染组件
九、生命周期
1、概念
一个组件从创建到销毁的过程
2、分类
创建 挂载 更新 销毁
beforeCreate:创建前
当组件实例化的时候,做的一个初始化操作,注意当前生命周期是组件还没有实例化完毕,
因此,你是访问不到当前组件的data或者methods的属性和方法及其他生命周期函数(this可以访问到)
场景:初始化的loading
Created :创建后
1、Created执行完毕之后,会将data以及methods身上的所有属性和方法都添加到vm的实例身上
内部操作:遍历 挂载 所以可以直接使用data和methods中的属性和方法
2、Created生命周期函数会将data身上所有的属性都添加上一个getter和setter方法,使得data的属性具有响应式特征
应用场景:ajax请求
beforeMount:挂载前
1、数据和模板还未进行结合,还可以对数据进行最后的一次处理
2、在这个生命周期函数中,是访问不到真实的DOM结构
mounted:挂载后
1、数据与模板进行相结合生成真是的dom
2、在这个生命周期函数中我们可以通过this.$refs.属性名称,获取真实的dom结构,并对dom进行处理
3、一般情况下我们都会在当前生命周期函数中做方法的实例化
beforeUpdate:更新前
1、更新的数据和模板还未进行结合,更新的最后一次修改
2、在这个生命周期函数中也可以获取到真实的dom结构
updated:更新后
1、更新的数据和模板进行相结合,在这个生命周期函数中我们可以获取到数据更新后的dom结构,以及对数据的做一个监听
类似于watch
beforeDesyroy:销毁前
1、在当前生命周期函数中仍然能访问到dom结构
使用场景:做一些事件监听的解绑 事件的移除
destroyed:销毁后
1、在这个生命周期函数中访问不到真实的dom结构了
2、在这个生命周期中会将vm与dom之间的关联全部都断开
十、组件传值
1、概念
组件之间的通讯
2.分类
a、父传子
传递:当父组件给子组件进行传值的时候,给子组件的标签上加一个自定义属性 值为需要传递的数据
接收:在子组件内部通过props进行接受,props进行接收的方式有2种 一种是数组 一种是对象
数组接收: props:[自定义属性] 对象接收: props:{ 自定义属性:{ type:类型 default:"默认值", required:必须传递 } }
b、子传父
接收:当子组件给父组件进行传值的时候,首先给子组件标签绑定一个自定义方法,值为一个函数,
注意 这个函数不需要加() 传递过来的值通过这个函数就可以接收到
传递:在子组件内部通过 this.$emit("自定义方法名称",需要传递的参数) 来进行传递数据
c、非父子组件传值
①、公共的Vue实例
1、首先需要让所有的子组件都拥有共同的一个事件订阅的方法
Vue.prototype.Observer = new Vue();
2、给需要传值的组件调用this.Observer.$emit("事件名称",传递的值)
3、给需要接受的组件调用this.Observer.$on("事件名称",回调函数)
②、EventBus(自己了解)
③、手动封装$on $emit $off 事件订阅
1、首先需要让所有的子组件都拥有共同的一个事件订阅的方法
手动封装的Observer import Observer from "./Observer"; Vue.prototype.Observer = Observer;
2、给需要传值的组件调用this.Observer.$emit("事件名称",传递的值)
3、给需要接受的组件调用this.Observer.$on("事件名称",回调函数)
// 手动封装$on $emit $off 事件订阅
let evenList={
};
const $on = (evenName,callback)=>{
if(!evenList[evenName]){
evenList[evenName]=[]
};
evenList[evenName].push(callback)
};
const $emit = (evenName,params)=>{
if(evenList[evenName]){
evenList[evenName].forEach((cb)=>{
cb(params)
})
}
}
const $off = (eventName,callback)=>{
if(EventList[eventName]){
if(callback){
var index = EventList[eventName].indexOf(callback);
EventList[eventName].splice(index,1);
}else{
EventList[eventName].length = 0;
}
}
}
export default {
$on,
$emit,
$off,
}
面试题
谈谈你对单项数据流的理解
当父组件给子组件传递数据时,子组件只能接受不能修改,因为数据是单项流动的
如果自组件修改父组件的信息后,有可能会导致其他组件数据也会发生错误,错误无法捕获
为什么组件中的data是一个函数,而不是对象?
data是当前组件所需要的状态,模块中很多个组件
每一个组件都会返回独立的对象,如果data是对象的情况下,可能进行复用,因为对象是引用数据类型;但是如果是一个函数的情况下,那么就会每次返回一个新的对象,保证每个组件都有一个独立的对象(状态 )
十一、动态组件
1、概念
多个组件通过同一个挂载点进行切换展示,通过is属性动态来改变组件
通过一个内置组件<component></component> 以及这个组件的属性is来进行切换的
keep-alive
keep-alive:用来去包裹动态切换的页面或者组件 (内置组件)
作用:凡是用keep-alive包裹的组件,不会被二次创建和销毁,而是进行了一个缓存
keep-alive组件身上会有以下这几个常用的属性
include:
值:字符串 正则表达式
作用:指定哪些组件被缓存
exclude:
值:字符串 正则表达式
作用:排除哪些组件被缓存
max:数字
作用:最大可以缓存多少个组件
注意:
凡是使用了keep-alive包裹的组件都会多2个生命周期函数
deactivated:当组件被切换隐藏的时候会进行缓存状态的生命周期
activated:当组件被切换显示的时候会进行进入活跃状态的生命周期
十二、slot插槽
1、概念
正常情况下组件当做标签使用的时候,组件标签内部嵌套的元素是不会显示的 如果需要进行显示,则需要用到插槽
2、分类
a、 基本使用 (匿名插槽) default
1、在组件标签内部书写一个template标签 标签添加一个指令 v-slot
2、将需要做嵌套的内部放在template标签内部
3、在组件内部通过<slot></slot>进行接受
b、 具名插槽:
有名字的插槽
1、在组件标签内部书写一个template标签 标签添加一个指令 v-slot:插槽的名称
2、将需要做嵌套的内部放在template标签内部
3、在组件内部通过<slot name='插槽的名称'></slot>进行接受
c、作用域插槽:
带参数的插槽 ------数据的子传父
1、在组件内部通过<slot name='插槽的名称' :自定义属性="需要传递的值"></slot>绑定一个自定义属性 值为需要传递的数据
2、在组件标签内部书写一个template标签 标签添加一个指令 v-slot:插槽的名称="需要接收的这个对象"
2、在template内部的标签上进行数据的渲染 {{需要接收的这个对象.自定义属性}}
十三、路由
在vue中使用插件的步骤
1、引入Vue
import Vue from 'vue'
2、引入路由插件
import Router from 'vue-router'
3、使用插件
Vue.use(Router)
4、实例化
export default new Router({
mode:"hash",
//每一个路由页面的配置项
routes: [
{
path: '/movie',
name: 'movie',
component: Movie
}
]
})
当路由配置成功以后Vue中就会多了两个内置组件
<router-view></router-view> //当路径匹配成功以后 router-view用来显示相对应的组件
<router-link></router-link>
1、做路由的跳转 必须要添加一个属性 to:跳转的路径
2、除此之外 router-link 身上还会有一个tag属性 指定router-link 渲染成指定的标签
5、 路由的配置
mode:路由的形式 hash路由 history路由
routes:[] 每一个路由页面的配置项
routes中的配置项
path:"路径匹配的路径"
component:当路径匹配成功需要渲染的组件
redirect:重定向
6、路由传值
a、动态路由
流程
1、在定义路由的时候设置传递数据的key值 path:"/路由地址/:key1/:key2" key1 key2代表的数据的键值
2、在做路由跳转的时候定义传递的数据 to="/路由地址/参数1/参数2"
3、在需要接受信息的组件内部通过this.$route.params进行接收
b、query传值
流程:
1、通过query的方式进行数据的传参 key=val&key=val
所谓的query传值其实就是我们常说的get传值的方式 通过?后面进行拼接
2、接收的时候通过this.$route.query进行接收
c、路由解耦(只适合动态路由传值)
1、在定义路由的时候添加一个属性props:true
2、接收的时候只需要通过props进行接收即可
面试题
query传值 和 params 传值的区别?
前者的参数可传可不传
后者的参数是必须要传递的
7、编程式导航
路由跳转的方式
1、a标签跳转 影院
2、组件 router-link
3、编程式导航 通过js进行路由的跳转
this.$router.push 跳转
this.$router.back 后退
this.$router.forward 前进
this.$router.replace 替换 children:路由嵌套的配置项 这个属性和routes一样
路由嵌套中path只需要写路径的名称即可
十四、vuex
1、npm install vuex - S
2、Vue.use(Vuex)
3、 创建store模块,定义状态管理的规则
state :共享的状态
mutations :更改状态
actions:接受组件的事件(dispath),通过异步获取数据提交mutations(commit)
getters:获取最新状态,响应式的
moudle:分模块进行状态的管理
4、把store模块注册到viewmodel
5、this.$store 来访问这个store
mapGetters 把store中的getters方法的结果映射到组件中的computed属性
会主动订阅仓库中的状态变化,响应式的
mapMutations
mapState
dispatch("事件名")
定义一个vue插件
//插件中 export default { install(Vue,options){ Vue.prototype.$show = function(){ alert(1) } } } main.js中 import MyPlugin from '' Vue.use(MyPlugin)