1.插值语法:
功能:用于解析标签体内容。
写法:{{xxx}},xxx是js表达式,且可以直接读取到data中的所有属性。
2.指令语法:
功能:用于解析标签(包括:标签属性、标签体内容、绑定事件)。
举例:v-bind:href=“xxx” 或 简写为 :href=“xxx”,xxx同样要写js表达式,且可以直接读取 到data中的所有属性。
<h1>插值语法h1>
<h3>你好,{{name}}h3>
<hr/>
<h1>指令语法h1>
<a v-bind:href="school.url.toUpperCase()" x="hello">点我去{{school.name}}学习1a>
<a :href="school.url" x="hello">点我去{{school.name}}学习2a>
1.单向绑定(v-bind):数据只能从data流向页面。
2.双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data。
单向数据绑定:<input type="text" v-bind:value="name"><br/>
双向数据绑定:<input type="text" v-model:value="name"><br/>
单向数据绑定:<input type="text" :value="name"><br/>
双向数据绑定:<input type="text" v-model="name"><br/>
1.el有2种写法
(1).new Vue时候配置el属性。
(2).先创建Vue实例,随后再通过vm.$mount(’#root’)指定el的值。
//el的两种写法
const v = new Vue({
el:'#root', //第一种写法
data:{
name:'尚硅谷'
}
})
v.$mount('#root') //第二种写法
2.data有2种写法
(1).对象式
(2).函数式
//data的两种写法
new Vue({
el:'#root',
//data的第一种写法:对象式
data:{
name:'尚硅谷'
}
//data的第二种写法:函数式
data(){
console.log('@@@',this) //此处的this是Vue实例对象
return{
name:'尚硅谷'
}
}
})
如何选择:目前哪种写法都可以,以后学习到组件时,data必须使用函数式,否则会报错。
3.一个重要的原则:
由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了。
M:模型(Model) :data中的数据
V:视图(View) :模板代码
VM:视图模型(ViewModel):Vue实例
观察发现:
1.data中所有的属性,最后都出现在了vm身上。
2.vm身上所有的属性 及 Vue原型上所有属性,在Vue模板中都可以直接使用。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PDecwpwu-1641102674781)(C:\Users\骑上这只不归鹿\AppData\Roaming\Typora\typora-user-images\image-20211213170007781.png)]
let ageNumber=18
let person={
name:'张三',
sex:'男',
}
Object.defineProperties(person,{
'age':{
value:18,
writable:true, //控制属性是否可以被修改,默认值是false
enumerable:true, //控制属性是否可以枚举,默认值是false
configurable:true //控制属性是否可以被删除,默认值是false
//数据描述符和存取描述符 (value和writable)与 (get和set)不能同时存在
//当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
get(){
return this.value
},
//当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值
set(value){
this.value=value
}
},
'add':{
value:'c'
}
})
对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符**。数据描述符是一个具有值的属性,该值可以是可写的,也可以是不可写的。存取描述符是由 getter 函数和 setter 函数所描述的属性。一个描述符只能是这两者其中之一;不能同时是两者。
Object.defineProperty(obj, prop, descriptor)
定制对象属性const object1 = {};
Object.defineProperty(object1, 'property1', {
value: 42,
writable: false
});
Object.defineProperties(obj, props)
批量定制对象属性var obj = {};
Object.defineProperties(obj, {
'property1': {
value: true,
writable: true
},
'property2': {
value: 'Hello',
writable: false
}
// etc. etc.
});
Object.getOwnPropertyDescriptor(obj, prop)
获取属性描述符const object1 = {
property1: 42
};
const descriptor1 = Object.getOwnPropertyDescriptor(object1, 'property1');
console.log(descriptor1.configurable);
// expected output: true
console.log(descriptor1.value);
// expected output: 42
Object.getOwnPropertyDescriptors(obj)
方法用来获取一个对象的所有自身属性的描述符。
数据代理:通过一个对象代理对另一个对象中属性的操作(读/写)
let obj = {x:100}
let obj2 = {y:200}
Object.defineProperty(obj2,'x',{
get(){
return obj.x
},
set(value){
obj.x = value
}
})
1.Vue中的数据代理:
`通过vm对象来代理data对象中属性的操作(读/写)
2.Vue中数据代理的好处:
更加方便的操作data中的数据
3.基本原理:
通过Object.defineProperty()把data对象中所有属性添加到vm上。
为每一个添加到vm上的属性,都指定一个getter/setter。
在getter/setter内部去操作(读/写)data中对应的属性。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sj8rOp4I-1641102674783)(C:\Users\骑上这只不归鹿\AppData\Roaming\Typora\typora-user-images\image-20211213222403514.png)]
使用v-on:xxx
或 @xxx
绑定事件,其中xxx是事件名;
<button v-on:click="showInfo">点击button><button @click="showInfo">点击button>
事件的回调需要配置在methods对象中,最终会在vm上;
methods中配置的函数,不要用箭头函数!否则this就不是vm了;
methods中配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例对象;
@click="demo"
和 @click="demo($event)"
效果一致,但后者可以传参;
<button @click="showInfo2($event,args)">点击button> //$event为事件参数,args为需要传递的参数
事件传播:捕获阶段(不会触发事件)、目标阶段(开始在目标元素上触发事件)、冒泡阶段(依次触发祖先元素上的事件)
prevent
:阻止默认事件(常用);
<a href="http://www.atguigu.com" @click.prevent="showInfo">点我提示信息a>//阻止页面跳转
stop
:阻止事件冒泡(常用);
<button @click.stop="showInfo">点我提示信息button>
once
:事件只触发一次(常用);
<button @click.once="showInfo">点我提示信息button>
capture
:使用事件的捕获模式;
<div class="box1" @click.capture="showMsg(1)"> div1 <div class="box2" @click="showMsg(2)"> div2 div>div>
self
:只有event.target是当前操作的元素时才触发事件;
<div class="demo1" @click.self="showInfo"> <button @click="showInfo">点我提示信息button>div>
passive
:事件的默认行为立即执行,无需等待事件回调执行完毕;
键盘事件修饰符
<
input type="text" @keydown.Enter="showInfo">
<input type="text" @keydown.enter="showInfo">
<input type="text" @keydown.13="showInfo">
部分按键别名
回车 enter
删除 delete (捕获“删除”和“退格”键)
退出 esc
空格 space
换行 tab (特殊,必须配合keydown去使用)
上 up
下 down
左 left
右 right
Vue未提供别名的按键,可以使用按键原始的key值(键本名)去绑定,多个单词组成的键名注意要转为短横线命名
<input type="text" @keydown.caps-lock="showInfo">
系统修饰键ctrl
、alt
、shift
、meta
keyup
使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。keydown
使用:正常触发事件。<input type="text" @keyup.ctrl.up="showInfo">
Vue.config.keyCodes
.自定义键名 = 键码,可以去定制按键别名
Vue.config.keyCodes.huiche = 13 //定义了一个别名按键,将enter的别名定义为huiche
定义:要用的属性不存在,要通过已有属性计算得来。
原理:底层借助了Objcet.defineproperty方法提供的getter和setter。
get函数什么时候执行?
①初次读取时会执行一次。
②当依赖的数据发生改变时会被再次调用。
优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
备注:
计算属性最终会出现在vm上,直接读取使用即可
如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变
//完整写法
computed:{
fullName:{
//get有什么作用?当有人读取fullName时,get就会被调用,且返回值就作为fullName的值
//get什么时候调用?1.初次读取fullName时。2.所依赖的数据发生变化时。
get(){
console.log('get被调用了')
// console.log(this) //此处的this是vm
return this.firstName + '-' + this.lastName },
//set什么时候调用? 当fullName被修改时。
set(value){
console.log('set',value)
const arr = value.split('-')
this.firstName = arr[0]
this.lastName = arr[1]
}
}}
//简写
computed:{
fullName(){
console.log('get被调用了')
return this.firstName + '-' + this.lastName
}}
被监视的属性变化时, 回调函数自动调用, 进行相关操作。监视的属性必须存在,才能进行监视!!
监视的两种写法:
//1.new Vue时传入watch配置
watch:{
isHot:{
immediate:true, //初始化时让handler调用一下
//handler什么时候调用?当isHot发生改变时。
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
}
}
//2.通过vm.$watch监视
vm.$watch('isHot',{
immediate:true, //初始化时让handler调用一下
//handler什么时候调用?当isHot发生改变时。
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
})
深度监视:
(1).Vue中的watch默认不监测对象内部值的改变,只能监测到对象的改变(一层)。
(2).配置deep:true
可以监测对象内部属性值改变(多层)。
data:{
isHot:false,
stu:{
age:18,
address:{
province:'sichuan',
city:'chengdu'
}
}
},
//深度监视,能监视对象内部属性值的变化,若为false则监测不到
watch:{
'stu.age':{
deep:true,
handler(newValue,oldValue){
console.log('stu.age修改了')
}
},
stu:{
deep:true,
handler(newValue,oldValue){
console.log('num被修改了')
}
},
}
备注:
Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以!
使用watch时根据数据的具体结构,决定是否采用深度监视。
简写:
watch:{
//正常写法
isHot:{
immediate:true, //初始化时让handler调用一下
deep:true,//深度监视
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
},
//简写
isHot(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue,this)
}
}
//正常写法
vm.$watch('isHot',{
immediate:true, //初始化时让handler调用一下
deep:true,//深度监视
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
})
//简写
vm.$watch('isHot',(newValue,oldValue)=>{
console.log('isHot被修改了',newValue,oldValue,this)
})
注意:当变更(不是替换)对象或数组并使用 deep 选项时,旧值将与新值相同,因为它们的引用指向同一个对象/数组。Vue 不会保留变更之前值的副本
computed和watch之间的区别:
watch可以进行异步操作
。两个重要的小原则:
普通函数
,这样this的指向才是vm 或 组件实例对象。定时器的回调函数
、ajax的回调函数等
、Promise的回调函数
),最好写成箭头函数
,这样this的指向才是vm 或 组件实例对象。写法:class=“xxx” xxx可以是字符串、对象、数组。
字符串写法 样式的类名不确定,需要动态指定
<div class="box" :class="boxStyle" >div><button id="btn" @click="change">切换button>
const vm=new Vue({
el:"#app",
data:{
boxStyle:'box1', //去CSS样式中寻找.box1
},
methods: {
change(){
this.boxStyle='box2'
}
},
})
数组写法 要绑定多个样式,个数不确定,名字也不确定
<div class="box" :class="boxStyleArr" >div>
const vm=new Vue({
el:"#app",
data:{
boxStyleArr:['box1','box2','box3'] //去CSS样式中寻找.box1 .box2 .box3
},
})
对象写法 要绑定多个样式,个数确定,名字也确定,但不确定用不用
<div class="box" :class="boxStyleObj" >div>
const vm=new Vue({
el:"#app",
data:{
boxStyleObj:{
box1:false,
box2:true,
box3:true
}
},
})
写法:
:style="{ fontSize: xxx }"其中xxx是动态值
<div class="box" :style="boxStyle" >div>
const vm=new Vue({
el:"#app",
data:{
boxStyle:{
borderRadius:"50%" //短横线转为驼峰
}
},
})
:style="[a,b]"其中a、b是样式对象。
<div class="box" :style="boxStyleArr" >div>
<div class="box" :style="[backgroundStyle,boderStyle]" >div>
//第一种写法
const vm=new Vue({
el:"#app",
data:{
boxStyleArr:[
{
borderRadius:"50%" //短横线转为驼峰
},
{
backgroundColor:"blue"
}
]
},
})
//第二种写法
const vm=new Vue({
el:"#app",
data:{
backgroundStyle:{
borderRadius:"50%"
//短横线转为驼峰
},
boderStyle:{
backgroundColor:"blue"
}
},
})
写法
适用于:切换频率较低的场景。
特点:不展示的DOM元素节点直接被移除。
注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”(中间不能出现其他兄弟节点)
<div v-if="n === 1">Angulardiv><div v-else-if="n === 2">Reactdiv> <div v-else-if="n === 3">Vuediv><div v-else>框架div>
v-if与template的配合使用
<template v-if="n === 1"> <h2>你好h2> <h2>尚硅谷h2> <h2>北京h2>template>
写法:v-show="表达式"
适用于:切换频率较高的场景。
特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉(display:none)
<h2 v-show="false">欢迎来到{{name}}h2><h2 v-show="1 === 1">欢迎来到{{name}}h2>
v-for
语法:v-for="(item, index) in xxx" :key="yyy"
遍历数组
<li v-for="(person, index) of persons" :key="index"> {{person.name}}_{{person.age}}li>
遍历对象
<li v-for="(value, key) of car" :key="key"> {{key}}_{{value}}li>
遍历字符串
<li v-for="(char, index) in str" :key="index"> {{index}}_{{char}}li>
遍历指定次数
<li v-for="(number,index) of 5" :key="index"> {{index}}-{{number}}li>
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】, 随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较
旧虚拟DOM中找到了与新虚拟DOM相同的key
旧虚拟DOM中未找到与新虚拟DOM相同的key
watch实现
new Vue({
el:'#root',
data:{
keyWord:'',
persons:[
{id:'001',name:'马冬梅',age:19,sex:'女'},
{id:'002',name:'周冬雨',age:20,sex:'女'},
{id:'003',name:'周杰伦',age:21,sex:'男'},
{id:'004',name:'温兆伦',age:22,sex:'男'}
],
filPerons:[]
},
watch:{
keyWord:{
immediate:true,
handler(val){
this.filPerons = this.persons.filter((p)=>{
return p.name.indexOf(val) !== -1
})
}
}
}
})
computed实现
new Vue({
el:'#root',
data:{
keyWord:'',
persons:[
{id:'001',name:'马冬梅',age:19,sex:'女'},
{id:'002',name:'周冬雨',age:20,sex:'女'},
{id:'003',name:'周杰伦',age:21,sex:'男'},
{id:'004',name:'温兆伦',age:22,sex:'男'}
]
},
computed:{
filPerons(){
return this.persons.filter((p)=>{
return p.name.indexOf(this.keyWord) !== -1
})
}
}
})
更新时候的小问题
methods: {
updateMei(){
// this.persons[0].name = '马老师' //奏效
// this.persons[0].age = 50 //奏效
// this.persons[0].sex = '男' //奏效
// this.persons[0] = {id:'001',name:'马老师',age:50,sex:'男'} //不奏效
this.persons.splice(0,1,{id:'001',name:'马老师',age:50,sex:'男'})
}
}
<body>
<div id="root">
<h2>人员列表h2>
<input type="text" placeholder="请输入名字" v-model="keyWord">
<button @click="sortType = 2">年龄升序button>
<button @click="sortType = 1">年龄降序button>
<button @click="sortType = 0">原顺序button>
<ul>
<li v-for="(p,index) of filPerons" :key="p.id">
{{p.name}}-{{p.age}}-{{p.sex}}
<input type="text">
li>
ul>
div>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
keyWord:'',
sortType:0, //0原顺序 1降序 2升序
persons:[
{id:'001',name:'马冬梅',age:30,sex:'女'},
{id:'002',name:'周冬雨',age:31,sex:'女'},
{id:'003',name:'周杰伦',age:18,sex:'男'},
{id:'004',name:'温兆伦',age:19,sex:'男'}
]
},
computed:{
filPerons(){
const arr = this.persons.filter((p)=>{
return p.name.indexOf(this.keyWord) !== -1
})
//判断一下是否需要排序
if(this.sortType){
arr.sort((p1,p2)=>{
return this.sortType === 1 ? p2.age-p1.age : p1.age-p2.age
})
}
return arr
}
}
})
script>
body>
通过setter实现监视,且要在new Vue时就传入要监测的数据。
对象中后追加的属性,Vue默认不做响应式处理
如需给后添加的属性做响应式,请使用如下API
Vue.set(obj,key,value) vm.$set(obj,key,value)
Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。因此不能通过索引值修改数组来实现响应式处理
在Vue修改数组中的某个元素一定要用如下方法
Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性!!!
v-model的三个修饰符:
lazy:失去焦点再收集数据
number:输入字符串转为有效的数字
trim:输入首尾空格过滤
v-model收集的是value值,用户输入的就是value值
<label for="userName">账号:label>
<input type="text" id="userName" v-model="userInfo.userName"><br><br>
<label for="userPwd">密码:label>
<input type="password" id="userPwd" v-model="userInfo.userPwd"><br><br>
<label for="userAge">年龄:label>
<input type="number" id="userAge" v-model.number="userInfo.userAge">
其他信息:<textarea name="other" id="other" v-model="userInfo.other">textarea>
v-model收集的是value值,且要给标签配置value值
<label for="male">男label>
<input type="radio" id="male" name="sex" value="male" v-model="userInfo.sex">
<label for="female">女label>
<input type="radio" id="female" name="sex" value="female" v-model="userInfo.sex">
没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)
配置input的value属性
(1)v-model的初始值(配置项data中的值)是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
(2)v-model的初始值(配置项data中的值)是数组,那么收集的的就是value组成的数组(重点)
爱好:<label for="drink">喝酒label>
<input type="checkbox" name="hobby" id="drink" value="drink" v-model="userInfo.hobby">
<label for="smoke">抽烟label>
<input type="checkbox" name="hobby" id="smoke" value="smoke" v-model="userInfo.hobby"><label for="perm">烫头label>
<input type="checkbox" name="hobby" id="perm" value="perm" v-model="userInfo.hobby">
v-model收集的是value值
所属小区<select name="region" id="region" v-model="userInfo.region">
<option value="">请选择校区option>
<option value="chengdu">成都option>
<option value="guangzhou">广州option>
<option value="beijing">北京option>
select>
<body>
<form id="app">
<label for="userName">账号:label>
<input type="text" id="userName" v-model="userInfo.userName">
<br><br>
<label for="userPwd">密码:label>
<input type="password" id="userPwd" v-model="userInfo.userPwd">
<br><br>
<label for="userAge">年龄:label>
<input type="number" id="userAge" v-model.number="userInfo.userAge"> <br><br>
性别:
<label for="male">男label>
<input type="radio" id="male" name="sex" value="male" v-model="userInfo.sex"> <label for="female">女label>
<input type="radio" id="female" name="sex" value="female" v-model="userInfo.sex">
<br><br>
爱好:
<label for="drink">喝酒label>
<input type="checkbox" name="hobby" id="drink" value="drink" v-model="userInfo.hobby">
<label for="smoke">抽烟label>
<input type="checkbox" name="hobby" id="smoke" value="smoke" v-model="userInfo.hobby">
<label for="perm">烫头label>
<input type="checkbox" name="hobby" id="perm" value="perm" v-model="userInfo.hobby">
<br><br>
所属小区
<select name="region" id="region" v-model="userInfo.region">
<option value="">请选择校区option>
<option value="chengdu">成都option>
<option value="guangzhou">广州option>
<option value="beijing">北京option>
select>
<br><br>
其他信息:
<textarea name="other" id="other" v-model="userInfo.other">
textarea>
<br><br>
<input type="checkbox" name="agreement" id="agreement" v-model="userInfo.checked">阅读并接受
<a href="">《用户协议》a>
<br><br>
<input type="submit" value="提交" :disabled="userInfo.disabled">
form>
<script type="text/javascript">
const vue=new Vue({
el:"#app",
data:{
userInfo:{
userName:'',
userPwd:'',
userAge:'',
sex:'',
hobby:[],
region:'',
other:'',
disabled:true,
checked:false
}
},
methods: {
},
watch:{
'userInfo.checked':{
handler(val){
val == true ? this.userInfo.disabled=false : this.userInfo.disabled=true
}
}
}
})
script>
body>
定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)
注册过滤器
//Vue.filter(name,callback) 或 new Vue{filters:{}}
//全局过滤器
Vue.filter('mySlice',function(value){
return value.slice(0,4)
})
//局部过滤器
new Vue({
filters:{
timeFormater(){
return
}
}
})
使用过滤器:
<h3>现在是:{{time | timeFormater}h3>
<h3>现在是:{{time | timeFormater('YYYY_MM_DD')}h3>
<h3 :x="msg | mySlice">尚硅谷h3>
- 过滤器也可以接收额外参数、多个过滤器也可以串联
- 并没有改变原本的数据, 是产生新的对应的数据
v-bind : 单向绑定解析表达式, 可简写为 :xxx
v-model : 双向数据绑定
v-for : 遍历数组/对象/字符串
v-on : 绑定事件监听, 可简写为@
v-if : 条件渲染(动态控制节点是否存存在)
v-else : 条件渲染(动态控制节点是否存存在)
v-show : 条件渲染 (动态控制节点是否展示)
向其所在的节点中渲染文本内容。
v-text会替换掉节点中的内容,插值语法{{xx}}则不会。
<div v-text="name">div> //尚硅谷<div v-text="name">你好div> //尚硅谷
向指定节点中渲染包含html结构的内容 v-html可以识别html结构
v-html会替换掉节点中所有的内容,插值语法{{xx}}则不会
<div v-html="str" style="background-color: blue;">div> //你好啊!
// str:'<h3>你好啊!h3>',
Vue实例创建完毕并接管容器后,会删掉v-cloak属性
使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题
[v-cloak]{
display:none;
}
<h2 v-cloak>{{name}}h2>
<div id="root">
<h2 v-once>初始化的n值是:{{n}}h2>
<h2>当前的n值是:{{n}}h2>
<button @click="n++">点我n+1button>
div>
<div id="root">
<h2 v-pre>Vue其实很简单h2>
<h2 >当前的n值是:{{n}}h2>
<button @click="n++">点我n+1button>
div>
directives:{
big(element,binding){
console.log('big',this) //注意此处的this是window
element.innerText = binding.value * 10
},
}
钩子函数:
directives:{
fbind:{
//指令与元素成功绑定时(一上来)
bind(element,binding){
element.value = binding.value
},
//指令所在元素被插入页面时
inserted(element,binding){
element.focus()
}
//指令所在的模板被重新解析时
update(element,binding){
element.value = binding.value
}
}
}
Vue.directive('fbind',function(element,binding){
element.innerText = binding.value * 10
})
Vue.directive('fbind',{
//指令与元素成功绑定时(一上来)
bind(element,binding){
element.value = binding.value
},
//指令所在元素被插入页面时
inserted(element,binding){
element.focus()
},
//指令所在的模板被重新解析时
update(element,binding){
element.value = binding.value
}
})
1.指令定义时不加v-,但使用时要加v-;
2.指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。
'big-number'(element,binding){
// console.log('big')
element.innerText = binding.value * 10
}
在实例初始化之后,进行数据侦听和事件/侦听器的配置之前同步调用。this指数据监测、数据代理
此时无法
通过vm访问data上中的数据、methods中的方法
在实例创建完成后被立即同步调用。在这一步中,实例已完成对选项的处理,意味着以下内容已被配置完毕:数据侦听、计算属性、方法、事件/侦听器的回调函数。然而,挂载阶段还没开始,且 $el
property 目前尚不可用。this指数据监测、数据代理
此时可以
通过vm访问data上中的数据、methods中配置的方法
未经Vue编译的DOM结构
。不奏效
。Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)
发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】
数据是新的,但页面是旧的,即:页面尚未和数据保持同步。
数据是新的,页面也是新的,即:页面和数据保持同步。
vm中所有的: data、methods、指令等等,都处于可用状态,马上要执行销毁过程,一般在此段:关闭定时器、取消订阅消息、解绑自定义事件等收尾操作
清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。
不会再触发更新
**流程了。实现局部功能的代码和资源的集合
组件本质是一个名为VueComponent的构造函数
,且不是程序员定义的,是Vue.extend生成的
当我们创建组件标签时,Vue解析时会帮我们创建组件的实例对象
每次调用Vue.extend,返回的都是一个全新的VueComponent构造函数
this指向
组件配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【VueComponent实例对象】
new Vue(options)配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】
VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)。
Vue的实例对象,以后简称vm。
一个重要的内置关系:VueComponent.prototype._proto_ === Vue.prototype
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BokKHvmy-1641102674784)(C:\Users\骑上这只不归鹿\AppData\Roaming\Typora\typora-user-images\image-20211223135643387.png)]
一个文件包含多个组件
创建组件
//第一步:创建school组件
const school = Vue.extend({
template:`
学校名称:{{schoolName}}
学校地址:{{address}}
`,
//模板只有一个根节点 将替换组件标签
//组件定义时,一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器。
data(){
return {
schoolName:'尚硅谷',
address:'北京昌平'
}
},
methods: {
showName(){
alert(this.schoolName)
}
},
})
注册组件
new Vue({
el:'#root',
data:{
msg:'你好啊!'
},
//第二步:注册组件(局部注册)只能在root中使用
components:{
school,
student
}
})
//第二步:全局注册组件,在root1 root2都能使用
Vue.component('hello',hello)
使用组件(写组件标签)
<div id="root">
<hello>hello>
<hr>
<h1>{{msg}}h1>
<hr>
<school>school>
<hr>
<student>student>
<hello>hello>
div>
<div id="root2">
<hello>hello>
div>
关于组件名
一个单词组成
第一种写法(首字母小写):school
第二种写法(首字母大写):School
多个单词组成
第一种写法(kebab-case命名):my-school
第二种写法(CamelCase命名):MySchool (需要Vue脚手架支持)
备注
(1).组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行。
(2).可以使用name配置项指定组件在开发者工具中呈现的名字。
const s = Vue.extend({
name:'atguigu', // 指定组件在开发者工具
template:`
学校名称:{{name}}
学校地址:{{address}}
`,
data(){
return {
name:'尚硅谷',
address:'北京'
}
}
})
组件嵌套
子组件在父组件中注册
// 创建学生组件
const student=Vue.extend({
template:`
{{stuName}}
{{stuAge}}
`,
data() {
return {
stuName: '欧泽巍',
stuAge:18,
};
},
})
// 创建学校组件
const school=Vue.extend({
template: `
{{schName}}
`,
components:{
student
},
data() {
return {
schName: '西南石油大学',
};
},
})
// 创建app组件
const app=Vue.extend({
template: `
`,
components:{
school
}
})
// 创建VM实例
const vm=new Vue({
el:"#root",
template:`
`,
components:{
app
}
})
一个文件包含一个组件(.vue)
命名:
一个.vue 文件的组成(3 个部分)
模板页面
模板
JS 模块对象
样式
ES6模块化语法
模块功能主要由两个命令构成:export 和 import。
⚫ export 命令用于规定模块的对外接口
⚫ import 命令用于输入其他模块提供的功能
//1.分别暴露 m1.js
export let school = '尚硅谷';
export function teach() {
console.log("我们可以教给你开发技能");
}
//2.统一暴露 m2.js
let school = '尚硅谷';
function findJob(){
console.log("我们可以帮助你找工作!!");
}
export {school, findJob};
//3.默认暴露 m3.js
//第一种写法
export default {
school: 'ATGUIGU',
change: function(){
console.log("我们可以改变你!!");
}
}
//第二种写法
school: 'ATGUIGU',
change: function(){
console.log("我们可以改变你!!");
}
export default {school,change}
<script type="module">
//1.通用的导入方式
import * as m1 from "./src/js/m1.js";
import * as m2 from "./src/js/m2.js";
import * as m3 from "./src/js/m3.js";
//2. 解构赋值形式
import {school, teach} from "./src/js/m1.js";
import {school as guigu, findJob} from "./src/js/m2.js";//重名使用别名as
import {default as m3} from "./src/js/m3.js"; //default得用别名
//3. 简便形式 针对默认暴露
import m3 from "./src/js/m3.js";
Vue 脚手架是 Vue 官方提供的标准化开发工具(开发平台)
文档: https://cli.vuejs.org/zh/。
第一步(仅第一次执行):全局安装@vue/cli。
npm install -g @vue/cli
第二步:切换到你要创建项目的目录
,然后使用命令创建项目
或者vue ui创建项目
vue create xxxx
第三步:启动项目
npm run serve
如出现下载缓慢请配置 npm 淘宝镜像:
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:包版本控制文件
会报错,解决方案:要么使用render函数,要么引入完整版Vue
import Vue from 'vue/dist/vue.js'
vue.js与vue.runtime.xxx.js的区别
(1).vue.js是完整版的Vue,包含:核心功能+模板解析器。
(2).vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。
因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容。
new Vue({
el:'#app',
render: h => h(App), //箭头函数
})
打标识
获取DOM元素或者组件实例
console.log(this.$refs.title) //真实DOM元素
console.log(this.$refs.btn) //真实DOM元素
console.log(this.$refs.sch) //School组件的实例对象(vc)
功能:vc的配置项,让组件接收外部传过来的数据(父组件传递到子组件)
传递数据
传递数字,布尔值,数组,对象
,我们仍然需要 v-bind
来告诉 Vue这是一个 JavaScript 表达式而不是一个字符串
接收数据
//简单声明接收
props:['name','age','sex']
//接收的同时对数据进行类型限制
props:{
name:String,
age:Number,
sex:String
}
//接收的同时对数据:进行类型限制+默认值的指定+必要性的限制
props:{
name:{
type:String, //name的类型是字符串
required:true, //name是必要的
},
age:{
type:Number,
default:99 //默认值 //若未传递数据则使用默认值
},
sex:{
type:String,
required:true
}
}
备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。(相当于需要用形参去接收实参)
{{msg}}
学生姓名:{{name}}
学生性别:{{sex}}
学生年龄:{{myAge+1}}
功能:可以把多个组件共用的配置提取成一个混入对象(复用配置项)
定义混合
//mixin.js
export const xxx={
data(){....},
methods:{....}
....
},
export const yyy={
data(){....},
methods:{....}
....
}
使用混合
//局部混入
import {xxx,yyy} from 'mixin.js'
mixins:[xxx,yyy]
//全局混入
Vue.mixin(xxx) //所有的vm,vc均混入
混合中数据和组件中数据发生冲突,以组件中的数据为主(若都有钩子函数,则钩子函数合并)
功能:用于增强Vue
本质:包含install方法的一个对象,install的第一个参数是Vue
,第二个及以后的参数是插件使用者传递的数据。
定义插件
//plugins.js
//写法1
const obj.install = function (Vue, options) {
// 1. 添加全局过滤器
Vue.filter(....)
// 2. 添加全局指令
Vue.directive(....)
// 3. 配置全局混入(合)
Vue.mixin(....)
// 4. 添加实例方法
Vue.prototype.$myMethod = function () {...}
Vue.prototype.$myProperty = xxxx
}
//plugins.js
//第二种写法
export default {
install(Vue, options){
// 1. 添加全局过滤器
Vue.filter(....)
// 2. 添加全局指令
Vue.directive(....)
// 3. 配置全局混入(合)
Vue.mixin(....)
// 4. 添加实例方法
Vue.prototype.$myMethod = function () {...}
Vue.prototype.$myProperty = xxxx
}
}
使用插件
//main.js
//引入插件
import plugins from './plugins'
//应用(使用)插件
Vue.use(plugins,1,2,3)
//插件的形参options将收到实参[]
<template>
<div>
<h2>学校名称:{{name | mySlice}}h2>
<h2>学校地址:{{address}}h2>
<button @click="test">点我测试一个hello方法button>
div>template><script>
export default {
name:'School',
data() {
return {
name:'尚硅谷atguigu',
address:'北京',
}
},
methods: {
test(){
//使用插件中给Vue原型对象添加的hello方法
this.hello()
}
},
}
script>
组件中的样式都要汇总
作用:让样式在局部生效,防止命名冲突。
<style scoped>style>
组件化编码流程:
(1).拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。
(2).实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:
1**).一个组件在用:放在组件自身即可**。
2). 一些组件在用:放在他们共同的父组件上(状态提升)。
(3).实现交互:从绑定事件开始。
props适用于:
(1).父组件 ==> 子组件 通信
(2).子组件 ==> 父组件 通信
父组件定义一个函数,参数为要接收的数据,然后将函数通过props传递给子组件,随后子组件调用该函数将数据传递给父组件,在父组件中的函数中对数据进行处理(间接实现兄弟组件数据传递)
//父组件
methods: {
addTodo(todoObj){
this.todos.unshift(todoObj)
}
},
//子组件
methods: {
//包装为todo对象
add(e){
/* if(e.target.value=='')
{
return alert("不能输入为空")
} */
if(this.task.trim()==''){
return alert('不能输入为空')
}
const todoObj={id:nanoid(),name:e.target.value,done:false}
console.log(todoObj)
this.addTodo(todoObj)
// e.target.value=''
this.title = ''
}
},
props:['addTodo']
使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的!
props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做。
存储内容大小一般支持5MB左右(不同浏览器可能还不一样)
浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。
相关API
该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。
sessionStorage.setItem('key', 'value')
localStorage .setItem('key', 'value')
该方法接受一个键名作为参数,返回键名对应的值。
sessionStorage.getItem('key', 'value')
localStorage .getItem('key', 'value')
该方法接受一个键名作为参数,并把该键名从存储中删除。
sessionStorage.removeItem('key')
localStorage .removeItem('key')
该方法会清空存储中的所有数据。
localStorage .clear()
sessionStorage.clear()
备注:
webStorage存储的value是String类型,若需要存储对象、数组类型需要通过JSON.stringify()来转换
SessionStorage存储的内容会随着浏览器窗口关闭而消失。
LocalStorage存储的内容,需要手动清除才会消失。
xxxxxStorage.getItem(xxx)
如果xxx对应的value获取不到,那么getItem的返回值是null。
JSON.parse(null)
的结果依然是null。
父组件
中给子组件
绑定自定义事件(事件的回调在父组件中)。1.绑定自定义事件
或
methods:{
test(数据)
{ }
}
//若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法。
......
mounted(){
this.$refs.demo.$on.$once('atguigu',this.test)}
methods:{
test(数据)
{ }
}
组件上绑定原生DOM事件,需要使用native
修饰符
2.触发自定义事件
//子组件中
this.$emit('atguigu',数据)
注意:通过this.$refs.xxx.$on('atguigu',回调)
绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题!
在哪里处理数据
,事件绑定
就在哪儿,事件的回调函数
就写在哪儿
例如:在父组件中给子组件绑定事件,就在父组件中写回调函数
一种组件间通信的方式,适用于任意组件间通信。
安装全局事件总线:
new Vue({
......
beforeCreate() {
Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
},
......
})
使用事件总线:
绑定事件:
A组件想接收数据
,则在A组件中给$bus绑定自定义事件,事件的回调函数留在A组件自身。
methods(){
demo(data){......}
}
......
mounted() {
this.$bus.$on('xxxx',this.demo)
}
触发事件(发送数据):
this.$bus.$emit('xxxx',数据)
最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。
一种组件间通信的方式,适用于任意组件间通信。
使用步骤:
安装pubsub:npm i pubsub-js
引入: import pubsub from 'pubsub-js'
在订阅者和发布者中引入
接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
methods(){
demo(msgName,data){......}
}
......
mounted() {
this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
}
//若不在methods中配置回调函数,则在回调函数要写成箭头函数
//this.pid = pubsub.subscribe('xxx',(msgName,data)=>{})
提供数据:pubsub.publish('xxx',数据)
最好在beforeDestroy钩子中,用pubsub.unsubscribe(pid)
去取消订阅。
让父组件可以向子组件指定位置插入html结构
,也是一种组件间通信的方式,适用于 父组件 ===> 子组件 。
适用于同一组件数据、结构不同
html结构1
插槽默认内容...
适用于有多个插槽
html结构1
html结构2
插槽默认内容...
插槽默认内容...
数据在组件的自身,但结构需要组件的使用者来决定。
同一数据不同结构
- {{g}}
{{g}}
语法:this.$nextTick(回调函数)
this.$nextTick(function () {
this.$refs.inputName.focus();
});
作用:在下一次 DOM 更新结束后执行其指定的回调。
什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。
在插入、更新或移除 DOM元素时,在合适的时候给元素添加样式类名。
元素进入的样式:
元素离开的样式:
v-leave:离开的起点
v-leave-active:离开过程中
v-leave-to:离开的终点
v为默认的,要指定相应元素,则v替换为相应元素的name属性
使用
包裹要过度的元素(只能有一个元素),并配置name属性
你好啊!
若有多个元素需要过度,则需要使用:
,且每个元素都要指定key
值。
使用第三方库
引入
import 'animate.css'
使用
enter-active-class
leave-active-class
你好啊!
尚硅谷!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1Fn4yma4-1641102674784)(C:\Users\骑上这只不归鹿\AppData\Roaming\Typora\typora-user-images\image-20211224215549746.png)]
方法一
在vue.config.js中添加如下配置
devServer:{
proxy:"http://XXX:5000" //开启代理服务器,并指定服务器地址
}
在客户端中
getStudents(){
axios.get('http://localhost:8080/students').then(
response => {
console.log('请求成功了',response.data)
},
error => {
console.log('请求失败了',error.message)
}
)
},
上述例子
想要请求http://XXX:5000/students的资源,
先将请求发送给代理服务器http://localhost:8080,
查找是否有http://localhost:8080/students资源,
若有则返回,若没有则去http://XXX:5000/students找
说明:
方法二
在vue.config.js中添加如下配置
module.exports = {
devServer: {
proxy: {
'/demo': {// 匹配所有以 '/api1'开头的请求路径
target: 'http://localhost:5000',// 代理目标的基础路径
changeOrigin: true,
pathRewrite: {'^/demo': ''} //用正则表达式 将路径重写
},
'/api2': {// 匹配所有以 '/api2'开头的请求路径
target: 'http://localhost:5001',// 代理目标的基础路径
changeOrigin: true,
pathRewrite: {'^/api2': ''}
}
}
}
}
/*
changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080
changeOrigin默认值为true
*/
在客户端中
getCars(){
axios.get('http://localhost:8080/demo/cars').then(
response => {
console.log('请求成功了',response.data)
},
error => {
console.log('请求失败了',error.message)
}
)
}
上述例子
想要请求http://XXX:5000/cars的资源,
先将请求发送给代理服务器http://localhost:8080/demo/cars,配置将地址转为http://localhost:8080/cars
查找是否有http://localhost:8080/cars资源,
若有则返回,若没有则去http://XXX:5000/cars找
说明:
**问题:**若使用import引入样式(样式放在assets中),脚手架将使用严格模式检查,若样式中的其他第三方样式未引入则会报错
**解决办法:**将样式放入public中,通过link标签引入
<link rel="stylesheet" href="<%= BASE_URL %>css/bootstrap.css">
在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oFqjq2fW-1641102674784)(C:\Users\骑上这只不归鹿\AppData\Roaming\Typora\typora-user-images\image-20211227151241652.png)]
多个组件需要共享数据时
创建文件:src/store/index.js
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)
//准备actions对象——响应组件中用户的动作()
const actions = {}
//准备mutations对象——修改state中的数据
const mutations = {}
//准备state对象——保存具体的数据
const state = {}
//创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state
})
在main.js
中创建vm时传入store
配置项
......
//引入store
import store from './store'
......
//创建vm
new Vue({
el:'#app',
render: h => h(App),
store
})
组件中读取vuex中的数据:$store.state.sum
组件中修改vuex中的数据:this.$store.dispatch('action中的方法名',数据)
或 this.$store.commit('mutations中的方法名',数据)
配置src/store/index.js
state
共享的状态/数据)必须
state:{
sum:0,
},
actions
处理数据(过滤数据)、接收从后端传递的数据 必须
actions:{
//context相当于一个mini store
jiaOdd(context,value){
if(context.state.sum%2!==0)
{
context.commit('JIA',value)
}
},
jiaWait(context,value){
setTimeout(() => {
context.commit('JIA',value)
}, 500);
}
},
mutations
转换数据 必须
函数名用大写
mutations:{
//value=>payload载荷
JIA(state,value){
state.sum+=value
},
JIAN(state,vlaue){
state.sum-=vlaue
}
},
getters
将state中的数据进行处理 可选
Getter 也可以接受其他 getter 作为第二个参数
getters:{
bigSum(state,getters){
return state.sum*10
}
}
mapState
将 store 中的 state映射到局部计算属性
import { mapState } from 'vuex'
.....
computed: {
//一般写法
sum(){
return this.$store.state.sum
},
school(){
return this.$store.state.school
},
subject(){
return this.$store.state.subject
},
//简写
//借助mapState生成计算属性:sum、school、subject(对象写法)
//计算属性名:'子节点名'
...mapState({sum:'sum',school:'school',subject:'subject'}),
//借助mapState生成计算属性:sum、school、subject(数组写法)
//映射的计算属性的名称与 state 的子节点名称相同时
...mapState(['sum','school','subject']),
},
mapGetters
将 store 中的 getter 映射到局部计算属性
import { mapState } from 'vuex'
.....
computed: {
//一般写法
bigSum(){
return this.$store.getters.bigSum
},
//简写
//借助mapGetters生成计算属性:bigSum(对象写法)
//计算属性名:'子节点名'
...mapGetters({bigSum:'bigSum'}),
//借助mapGetters生成计算属性:bigSum(数组写法)
//映射的计算属性的名称与 state 的子节点名称相同时
...mapGetters(['bigSum'])
},
mapActions
用于帮助我们生成与actions
对话的方法,即:包含$store.dispatch(xxx)
的函数
import { mapActions } from 'vuex'
......
computed: {
//一般写法
incrementOdd(){
this.$store.dispatch('jiaOdd',this.n)
},
incrementWait(){
this.$store.dispatch('jiaWait',this.n)
},
//简写
//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(对象写法)
...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(数组写法)
//回调函数名称与 actions 的子节点名称相同时
...mapActions(['jiaOdd','jiaWait'])
}
mapMutations
用于帮助我们生成与mutations
对话的方法,即:包含$store.commit(xxx)
的函数
/*
若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。
*/
import { mapActions } from 'vuex'
......
computed: {
//一般写法
increment(){
this.$store.commit('JIA',this.n)
},
decrement(){
this.$store.commit('JIAN',this.n)
},
//简写
//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)
...mapMutations({increment:'JIA',decrement:'JIAN'}),
//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法)
//回调函数名称与 actions 的子节点名称相同时
...mapMutations(['JIA','JIAN']),
}
备注:mapActions与mapMutations使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。
目的:让代码更好维护,让多种数据分类更加明确。
第一种写法:引入相应的模块
//index.js
import Vue from 'vue'
import Vuex from 'vuex';
//引入模块
import count from './count';
import person from './person';
//应用插件
Vue.use(Vuex)
export default new Vuex.Store({
modules:{
countAbout:count,
personAbout:person
}
})
//在count.js中
export default {
namespaced: true, //开启命名空间
state: {
},
actions: {
},
mutations: {
},
getters: {
}
}
....
第二种写法:在集中写在store/index.js
//index.js
import Vue from 'vue'
import Vuex from 'vuex';
Vue.use(Vuex)
const countAbout = {
namespaced:true,//开启命名空间
state:{x:1},
mutations: { ... },
actions: { ... },
getters: {
bigSum(state){
return state.sum * 10
}
}
}
export default new Vuex.Store({
modules: {
countAbout,
personAbout
}
})
组件中读取state数据
//方式一:一般写法 this.$store.state.模块名.数据名
this.$store.state.personAbout.list
//方式二:借助mapState读取 ...mapState('模块名',数组方法/对象方法)
...mapState('countAbout',['sum','school','subject']),
组件中读取getters数据
//方式一:一般写法 this.$store.getters['模块名/计算属性名']
this.$store.getters['personAbout/firstPersonName']
//方式二:借助mapGetters读取 ...mapGetters('模块名',数组方法/对象方法)
...mapGetters('countAbout',['bigSum'])
组件中调用dispatch
//方式一:一般写法 this.$store.dispatch['模块名/方法名',传递的数据]
this.$store.dispatch('personAbout/addPersonWang',person)
//方式二:借助mapActions读取 ...mapActions('模块名',数组方法/对象方法)
...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
组件中调用commit
//方式一:一般写法 this.$store.commit['模块名/方法名',传递的数据]
this.$store.commit('personAbout/ADD_PERSON',person)
//方式二:借助mapMutations ...mapGetters('模块名',数组方法/对象方法)
...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),
vue 的一个插件库,专门用来实现 SPA(单页面) 应用
对 SPA 应用的理解
什么是路由?
一个路由就是一组映射关系(key - value)
key 为路径, value 可能是 function 或 component
路由分类
安装vue-router,命令:npm i vue-router
编写src/router/index.js 配置项:
import Vue from 'vue'
//引入VueRouter
import VueRouter from 'vue-router'
//引入相应组件
import Home from '../components/Home'
import About from '../components/About'
//应用插件
Vue.use(VueRouter)
export default new VueRouter({
routes:[
{
path:'/about',
component:About
},
{
path:'/home',
component:Home
}
]
})
实现跳转切换(active-class可配置高亮样式)
router-link
标签相当于a
标签
<router-link active-class="active" to="/about">Aboutrouter-link>
指定组件展示位置
<router-view>router-view>
几个注意点
views
文件夹,一般组件通常存放在components
文件夹。$route
属性,里面存储着自己的路由信息。$router
属性获取到。children
配置项routes:[
{
path:'/about',
component:About,
},
{
path:'/home',
component:Home,
children:[ //通过children配置子级路由
{
path:'news', //第一种写法不加/
component:News
},
{
path:'/home/message', //第二种写法 写完整路径
component:Message
}
]
}
]
<router-link to="/home/news">Newsrouter-link>
传递参数
<router-link :to="/home/message/detail?id=666&title=你好">跳转router-link>
<router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">跳转router-link>
<router-link
:to="{
path:'/home/message/detail',
query:{
id:m.id,
title:m.title
}
}"
>跳转router-link>
简化路由的跳转
给路由命名
{
path:'/home',
component:Home,
children:[
{
name:'news', //给路由命名
path:'news',
component:News,
},
{
path:'message',
component:Message,
children:[
{
path:'detail',
component:Detail
}
]
}
]
}
简化跳转
<router-link to="/home/news">跳转router-link>
<router-link class="list-group-item" active-class="active" :to="{name:'news'}">Newsrouter-link>
<router-link
:to="{
name:'news',
query:{
id:666,
title:'你好'
}
}"
>跳转router-link>
配置路由,声明接收params参数
{
path:'/home',
component:Home,
children:[
{
name:'news', //给路由命名
path:'news',
component:News,
},
{
path:'message',
component:Message,
children:[
{
path:'detail/:id/:title',
component:Detail
}
]
}
]
}
传递参数
<router-link :to="`/home/message/detail/${m.id}/${m.title}`">{{m.title}}router-link>
<router-link
:to="{
name:'detail',
params:{
id:m.id,
title:m.title
}
}"
>跳转router-link>
接收参数
<li>消息编号:{{$route.params.id}}li>
<li>消息标题:{{$route.params.title}}li>
特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!
让路由组件更方便的收到参数
{
name:'xiangqing',
path:'detail/:id',
component:Detail,
//第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
// props:{a:900}
//第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
props:true
//第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
props(route){
return {
id:route.query.id,
title:route.query.title
}
}
}
的replace属性控制路由跳转时操作浏览器历史记录的模式
浏览器的历史记录有两种写入方式:分别为push
和replace
,push
是追加历史记录,replace
是替换当前记录。路由跳转时候默认为push
push:跳转后不会删除跳转前的记录
replace:跳转后将替换跳转前的记录
如何开启replace
模式:
不借助
实现路由跳转,让路由跳转更加灵活
//$router的两个API
this.$router.push({
name:'xiangqing',
params:{
id:xxx,
title:xxx
}
})
this.$router.replace({
name:'xiangqing',
params:{
id:xxx,
title:xxx
}
})
this.$router.forward() //前进
this.$router.back() //后退
this.$router.go(n) //可前进也可后退
让不展示的路由组件保持挂载,不被销毁。
include为组件名
缓存多个:include:”[]”
<keep-alive include="News">
<router-view>router-view>
keep-alive>
activated
路由组件被激活时触发。
deactivated
路由组件失活时触发。
定义路由的时候可以配置 meta
字段
meta配置项中用户可自定义的数据
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
children: [
{
path: 'bar',
component: Bar,
// a meta field
meta: { requiresAuth: true }
}
]
}
]
})
对路由进行权限控制
全局前置守卫:初始化时执行、每次路由切换前
执行
router.beforeEach((to,from,next)=>{
//to:即将要进入的目标 路由对象
//from:当前导航正要离开的路由
if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则
next() //放行
}else{
alert('暂无权限查看')
// next({name:'guanyu'})
}
}else{
next() //放行
}
})
全局后置守卫:初始化时执行、每次路由切换后
执行
router.afterEach((to,from)=>{
if(to.meta.title){
document.title = to.meta.title //修改网页的title
}else{
document.title = 'vue_test'
}
})
可以在路由配置上直接定义 beforeEnter
守卫
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
在路由组件内直接定义以下路由导航守卫
beforeRouteEnter
beforeRouteUpdate
beforeRouteLeave
const Foo = {
template: `...`,
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate(to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave(to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
}
http://oursite.com/user/id
就会返回 404,这就不好看了。Vant https://youzan.github.io/vant
Cube UI https://didi.github.io/cube-ui
Mint UI http://mint-ui.github.io
Element UI https://element.eleme.cn
IView UI https://www.iviewui.com