# 基本语法

# 渐进式框架

# 渐进式

  • 通过各种指令进行申明式渲染
  • 组件系统
  • 页面路由
  • 大型的状态管理-VUEX
  • 构建整个系统

渐进式框架

# 框架

  • Jquery是JS库,函数的集合,不提供逻辑,逻辑由程序员自己控制
  • VUE是JS框架,一整套的解决方案,大部分逻辑已经确定好

# MVVM

一种更好的UI模式解决方案,通过数据双向绑定让数据自动的双向同步

  • M:Model数据模型
  • V:view试图(页面)
  • VM:ViewModel视图模型

# 插值表达式

在data中必须存在: { { msg } }

# 指令(14个)

# v-bind

  • 动态地绑定一个或多个属性,或一个组件 prop 到表达式,简写 :
  • class和style可以绑定对象或数组
<div class="base fz" :class="obj"></div>
<div class="base fz" :class="arr"></div>
<div class="base fz" :class="{red:true}"></div>//注意单括号

//数据
obj:{base:true,red:true,fz:true}
arr:['base','fz','red']

<div :style="{width:w}"></div>
//数据
w:'200px'

# v-model

在表单控件或者组件上创建双向绑定,会忽略掉表单元素原本的value
修饰符:v-model.lazy(onChange时触发)、v-model.number(输入框的内容转为数字)、v-model.trim(去除数据前后的空格)

  • 视图改变数据跟着改变
<p></p>
<input type="text">

<script>
	const data={msg:'哈哈哈'};
	const p=document.querySelector('p');
	const input=document.querySelector('input');
	p.innerText=data.msg;
	input.value=data.msg;

	//键盘弹起时触发,不管弹起的什么键都会触发
	input.addEventListener('keyup',function(){
		console.log('keyup');
	});

	//输入完毕后触发
	input.addEventListener('change',funtion(){
		console.log('change');
	})

	//只要input框中输入内容就会触发
	input.addEventListener('input',function(){
		console.log('input');
		data.msg=input.value;
	})
</script>
  • 数据改变视图跟着改变

1、angular.js 1.0版本通过脏数据检查机制(数据轮询),性能比较低,兼容IE8
2、vue使用的数据劫持,ES5的语法:Object.defineProperty(),不兼容IE678

<p></p>
<input type="text">

<script>
	const data={msg:'哈哈哈'};
	
	let temp=data.msg;
	
	//作用:给对象的某个属性增加修饰
	//参数:对象名、属性名、修饰(是一个对象)
	Object.defineProperty(data,'msg',{
		
		//get方法会再获取到msg这个属性的时候执行
		//劫持后获取不到msg原来的值,需要先定义let temp=data.msg
		get:funtion(){
			return temp;
		},
		
		//set方法会劫持到msg这个属性的修改操作
		set:function(value){
			temp=value;
		}
	})
</script>

# v-on

绑定事件监听,简写 @
修饰符:如果没有,修饰符也可以省略

  • .stop:阻止冒泡,等效于event.stopPropagation()

  • .prevent :阻止默认事件,等效于event.preventDefault()

  • .capture:捕获到事件时使用,先父再子,默认是先子再父(冒泡)

  • .self:点击元素本身上触发,可能不在子元素上

  • .once:只触发一次回调

  • .left:只当点击鼠标左键时触发

  • .right:只当点击鼠标右键时触发

  • .middle:只当点击鼠标中键时触发

  • .passive:以 { passive: true } 模式添加侦听器

  • .native:监听组件根元素的原生事件

  • @dblclick:双击事件

  • @keyup:按键事件 @keyup.enter/tab/delete/esc/space……

//也可以自己定义按键
Vue.config.keyCodes.sylone=13
//使用
@keyup.sylone

//capture捕获到事件,先执行father()再执行child()
<a @click.capture="father()">
 <button @click.capture="child()"></button>
</a>

//事件传递参数
//事件处理函数中增加两个参数 $event,item 。 item就是要传递的对象参数
<el-radio v-model="item" label="A" @change="answer($event, item)"></el-radio>

注意

在开发过程中会遇到按键修饰符不生效的情况,此时我们需要加上 .native 按键修饰符

//只适用于 input 框 获得焦点 时按下回车时生效,失去焦点时,此功能仍不可用
<input v-on:keyup.enter.native="submit">

//如果是button按钮,那么应该把它绑定在document上
created: function () {
    document.onkeyup = e => {
      if (e.keyCode === 13 && e.target.baseURI.match('/')) {
        this.onSubmit('form')
      }
    }
}

# v-text/v-html

  • v-text:更新元素的innerText属性(textContent属性),不如插值表达式好用
  • v-html:更新元素的innerText属性(textContent属性),可以识别html标签
<div id="app">
   <p>{{content}}</p> 
   <p v-text="text"></p>
   <p v-html="html"></p>
</div>
<script>
	new Vue({
		el:"#app",
		data:{
		   content:"<p>测试差值表达式</p>",
		   text:"<p>测试v-text指令</p>",
		   html:"<p>测试v-html指令</p>",
		}
	})
</script>

# v-show/v-if v-else-if v-else

  • v-show:通过display:none隐藏,用于频繁的显示和隐藏
  • v-if:通过删除或者创建一个元素来显示或隐藏,如果

# v-for

维护状态:不加key默认使用"就地更新"策略,在有临时状态的元素(checkbox)时会出现bug
使用:key:'index',当然不会报错,但是其实就地更新策略默认用的就是index,建议还是id做key值
key的作用 (opens new window)

# v-pre/v-once

作用时用于性能优化,如果有大量的文字,不需要vue进行编译的时候
不要轻易使用,除非能明显感觉到速度变慢的时候才会使用

  • v-pre:会跳过插值表达式的编译
  • v-once:插值表达式会编译一次,后续数据更新,插值表达式不会更新
<div id="app">
  <div v-pre>{{ msg }}</div>
  <div v-once>{{ msg }}</div>
  <div>{{ msg }}</div>
</div>

<script src="vue.js"></script>
<script>
  const vm = new Vue({
	el: '#app',
	data: {
	  msg: 'hello vue'
	}
  })
</script>

# v-cloak

用于解决插值表达式的闪烁问题,需要添加样式:[v-cloak]{display:none;}
只有在通过script引用vue.js文件的时候才会用到v-cloak

# 计算属性 computed

  • 计算属性性能非常高,基于缓存实现,只有当它依赖的属性发生改变,才会重新执行
  • 计算属性的完整形态:如果需要修改计算属性的值
<div id="app">
  <input type="text" placeholder="请输入你的姓" v-model="lastName">
  <input type="text" placeholder="请输入你的名" v-model="firstName">
  <input type="text" placeholder="你的名字是" v-model="fullName">
</div>
<script>
const vm=new Vue({
	el:'#app',
	data:{
		lastName:'',
		firstName:''
	},
	computed:{
		//普通用法
		fullName:function(){
			return this.lastName+''+this.firstName
		}
		fullName() {
			return this.lastName+''+this.firstName
		}
		//完整形态
		fullName:{
			get(){
				return this.lastName+''+this.firstName
			},
			set(value){
				this.lastName=value.split(' ')[0];
				this.firstName=value.split(' ')[1];
			}
		}
	}
})
</script>

# 侦听器watch

使用watch来响应数据的变化,watch的用法大致有三种

# 第一种:简单用法

直接在watch里面写一个监听处理函数,当每次监听到 cityName 值发生改变时,执行函数

<input type="text" v-model="cityName"/>

new Vue({
  el: '#root',
  data: {
    cityName: 'shanghai'
  },
  watch: {
    cityName(newName, oldName) {      // ...    }
  } 
})

//也可以在所监听的数据后面直接加字符串形式的方法名
watch: {
    cityName: 'nameChange'
 }

# 第二种:使用immediate和handler

这样使用watch时有一个特点,就是当值第一次绑定的时候,不会执行监听函数,只有值发生改变才会执行。如果我们需要在最初绑定值的时候也执行函数,则就需要用到immediate属性 比如当父组件向子组件动态传值时,子组件props首次获取到父组件传来的默认值时,也需要执行函数,此时就需要将immediate设为true

new Vue({
  el: '#root',
  data: {
    cityName: ''
  },
  watch: {
    cityName: {
      handler(newName, oldName) {
        // ...      
	   },
      immediate: true
    }
  } 
})

# 第三种:使用deep深度监听

当需要监听一个对象的改变时,普通的watch方法无法监听到对象内部属性的改变,只有data中的数据才能够监听到变化,此时就需要deep属性对对象进行深度监听

<input type="text" v-model="cityName.name"/>

const vm=new Vue({
  el: '#root',
  data: {
    cityName: {id: 1, name: 'shanghai'}
  },
  watch: {
    cityName: {
      handler(newName, oldName) {      // ...    },
      deep: true,
      immediate: true
    }
  } 
})

设置deep: true 则可以监听到cityName.name的变化,此时会给cityName的所有属性都加上这个监听器,当对象属性较多时,每个属性值的变化都会执行handler。如果只需要监听对象中的一个属性值,则可以做以下优化:使用字符串的形式监听对象属性:

watch: {
  'cityName.name': {
      handler(newName, oldName) { 
         // ...    
      },
      deep: true,
      immediate: true
   }
}

# 第四种:使用$watch进行属性监听

//监听单个属性
vm.$watch('cityName',function(newValue,oldValue){
	// ...    
})
//监听多个属性
vm.$watch(function(){
	return this.name+this.age;
},function(newValue,oldValue){
	console.log(newValue+''+oldValue);
})

# 过滤器 filters

常用于格式化我们的文本,其结构为

<div>{{ companyName | Name }}</div> 

中间使用 “|”管道符隔开,后面是针对前面关键字携带的参数,过滤器接收的第一个参数是companyName(data中的数据),然后才依次传过滤器的名字

 const vm=new Vue({
	 el:'#app',
	 data: {
	 	companyName: '浙江杭州阿里巴巴',
	 },
	 //局部过滤器 :只有在当前实例中能使用的过滤器
	 filters: {
	 	Name: function (value) {
	 	    //编写事件要求,可以设置对companyName的一些限制,这里是截取前四个字
	 	   return value.substr(0, 4);     
	 	}
	 }
 })
 
 //全局过滤器 :可以在所有vue实例中都能使用的过滤器
 Vue.filter('Name',funtion(value){
	 return value.substr(0, 4);     
 })

  • 可以传递参数
<div>{{ companyName | Name(4) }}</div> 

filters: {
	Name: function (value,length) {
		return value.substr(0, length);     
	}
 }
  • 可以传递多个过滤器
<div>{{ companyName | Name | Name}}</div> 

# axios

  • vue1.0使用vue-resource,引入它之后可以基于全局Vue对象或者某个vue使用http,支持jsonp跨域
  • vue2.0开始使用axios,是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中,不支持jsonp
axios({
	method:'post',
	url:'/user/list',
	params:'id=2', //设置URL地址里面的参数
	data:{
		firstName:'zhao',
		lastName:'sylone'
	}
}).then(res=>{ 
	//res.data
}).catch(err=>{ //使用箭头函数
	//err
})

注意

then/catch 使用箭头函数

# 跨域设置

withCredentials:是否允许带cookie,true时服务端需要设置SupportsCredentials=true


import axios from 'axios'
import router from './../router'
import comm from './comm'


//axios.defaults.baseURL='/api'; 
const myAxios = axios.create({
  // 只要发送axios请求,就在请求前加入/api的开头,例如 /zzz/one -> /api/zzz/one
  baseURL: '/api',
  // 设置超时时间 5s
  timeout: 10000,
  responseType: 'json',
  // 是否允许带cookie,true时服务端需要设置SupportsCredentials
  withCredentials: false,
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'
  }
})

// POST传参序列化(添加请求拦截器)
myAxios.interceptors.request.use(
  config => {
    // 在发送请求之前做某件事
    if (config.method === 'post' || config.method === 'put' || config.method === 'delete') {
      // 序列化
      const data = new FormData()
      Object.keys(config.data).forEach(item => {
        data.append(item, config.data[item])
      })
      config.data = data
    }
    if (localStorage.getItem('token')) {
      config.headers.Authorization = localStorage.token
    }
    console.log(config.data)
    return config
  },
  error => {
    comm.toast(error.data.message)
    return Promise.reject(error.data.message)
  }
)

// 返回状态判断(添加响应拦截器)
myAxios.interceptors.response.use(
  res => {
    // 对响应数据做些事
    if (res.data && res.data.code !== 1) {
      comm.toast(res.data.msg)
      return Promise.reject(res.data.msg)
    }
    return res.data
  },
  error => {
    // 若是发现有鉴权的基础信息
    if (localStorage.getItem('loginUserBaseInfo')) {
      // 并且当前的时间大于服务器过期的时间
	  const loginUserBaseInfo = window.localStorage.getItem('loginUserBaseInfo')
      const lifeTime = JSON.parse(loginUserBaseInfo).lifeTime * 1000
      const nowTime = new Date().getTime()
      if (nowTime > lifeTime) {
        localStorage.removeItem('loginUserBaseInfo')
        comm.toast('登录状态信息过期,请重新登录')
        router.push({
          path: '/login'
        })
      }
    } else {
      console.log(error)
      // 服务器状态码不是200的情况,这些自定义
      comm.toast(error.response.data.Message)
    }
    return Promise.reject(error.response)
  }
)

export default myAxios

devServer: {
	proxy: {
	  '/api': {
		disableHostCheck:true, //当设置为true时,此选项将绕过主机检查

		//默认情况下是/,所以您可以作为http://localhost:8080使用
		//更改为 /assets/ 就变为 http://localhost:8080/assets
		publicPath: '/assets/', 
		target: 'http://expo.sdxsk.com/api', //请求接口域名
		ws: true,
		secure: false,
		// 是否允许跨域
		changOrigin: true,
		// 如果配置了axios的baseURL为'/api',在这里需要用空字符串代替
		pathRewrite: {
		  '^/api': ''
		}
	  }
	}
}
//这个是对所有的接口都代理的,不止是检测到 /api 的接口
devServer: {
 proxy: 'http://e.dxy.net'
}

在Vue项目中配置了代理,但是部署之后不生效,并且报404

这是因为Vue配置的代理仅在本地开发下有效,部署之后,需要:

  • 在nginx中进行配置代理
  • 或者后端配置跨域

# DOM的异步更新

  • DOM渲染完成后会立即执行nextTick
  • vue在数据发生改变的时候,视图会自动跟着改变,但这个过程是异步的,我们在修改完数据后不能立马获取更新后的DOM结构,为了提高渲染的性能,vue中会等待数据都修改完成才会渲染DOM
//全局的
Vue.nextTick(function(){
	
})

//局部的,自动绑定到调用它的实例上
vm.$nextTick(function(){
	
}) 
//或者
//mounted的先执行,再执行nextTick
//此时的nextTick可以获取到dom结构
created(){
	this.$nextTick(function(){
		console.log('这是执行了nextTick')
	})
},
mounted(){
	console.log('这是执行了mounted')
}

# 动态添加/修改的属性不是响应式

如果给data中的引用类型(数组或对象)动态添加或修改了一个属性,这个属性不是响应式的

//参数1:需要添加属性的对象,参数2:增加的属性名,参数3:属性的值
Vue.set(this.car,'color','red');
vm.$set(this.car,'color','red');
this.$set(this.car,'color','red');

# ref操作DOM

  • 给元素设置ref的属性值,在Vue的实例选项mounted方法中通过this.$refs.属性值 获取到要操作的DOM
  • 给组件设置ref的属性值,可以在父组件中使用$refs访问子组件
<div id="app">
   <child ref="btn1"></child>
   <child ref="btn2"></child>
</div>

<script>
Vue.component('child',{
	template:'<button>{{count}}</button>',
	data(){
		return count:0
	},
	method(){
		hello(){
			console.log('hello')
		}
		this.$on('childmethod',function(){
			console.log('我是子组件的方法')
		})
	}
})
const vm=new Vue({
	el:'#app'
})

//获取值
vm.$refs.btn1.count=0;
vm.$refs.btn2.count=0;
//也可以修改值
vm.$refs.btn1.count=1;
vm.$refs.btn2.count=2;

//操作子组件的方法
this.$refs.btn1.hello();
//或者
this.$refs.btn1.$emit('childmethod')

//在组件对象中使用$el获取DOM节点
cont navbar = this.$refs.nav.$el.offsetTop
</script>
<ul @click ="clickfun($event)">
   <li></li>
</ul>

methods: {
	clickfun(e) {
	    e.target //是你当前点击的元素
	    e.currentTarget //是你绑定事件的元素
	    //获得点击元素的前一个元素
	    e.currentTarget.previousElementSibling.innerHTML
	    //获得点击元素的第一个子元素
	    e.currentTarget.firstElementChild
	    //获得点击元素的下一个元素
	    e.currentTarget.nextElementSibling
	    // 获得点击元素中id为string的元素
	    e.currentTarget.getElementById("string")
	    //获得点击元素的class属性
	    e.currentTarget.getAttributeNode('class')
	    // 获得点击元素的父级元素
	    e.currentTarget.parentElement
	    // 获得点击元素的前一个元素的第一个子元素的HTML值
	    e.currentTarget.previousElementSibling.firstElementChild.innerHTML
	}
}

# 完整的定时器

const vm=new Vue({
	el:'#app',
	data:{
		msg:'张三的速递',
		timeID:''  
	},
	methods:{
		start(){
			//如果有定时器在跑,直接结束
			if(this.timeID){
				return
			}
			this.timeID=setInterval(()=>{
				//文字跑马灯,每次取字符串的第一个字符放到最后
				this.msg=this.msg.slice(1)+this.msg.slice(0,1)
			},300)
		},
		end(){
			clearInterval(this.timeID)
			//清空定时器ID
			this.timeID=''
		}
	}
})

# 生命周期

生命周期

const vm=new Vue({
	el:'#app',
	data:{
		msg:'hello vue'
	},
	beforeCreate(){
		console.log('beforeCreate','会在vue实例数据初始化前执行');
	},
	created(){
		console.log('created','会在vue实例数据初始化后执行');
	},
	beforeMount(){
		console.log('beforeMount','在渲染的结构替换el之前执行');
	},
	mounted(){
		console.log('mounted','在渲染的结构替换el之后执行');
	},
	beforeUpdate(){
		console.log('beforeUpdate','数据发生改变,DOM更新之前执行');
	},
	Updated(){
		console.log('beforeUpdate','数据发生改变,DOM更新之后执行');
	},
	beforeDestory(){
		console.log('beforeDestory','vue实例销毁前执行');
	},
	destoryed(){
		console.log('beforeDestory','vue实例销毁后执行');
	}
})

# created 创建

钩子函数,类似Uniapp的OnLoad,发送ajax、从缓存读取数据都在此方法内

# mounted 挂载

钩子函数,一般在初始化页面完成后,此时可以dom节点进行相关操作,类似Uniapp的OnReady
通常是为 metheds 函数提前定义( 类似提前声明变量 进入页面内容全部渲染完成后自动引函数),即





 




mounted() {
  this.initData()
},
methods: {
  //initData:function() { }的简写
  initData() {
	
  }

# 组件使用

模块化:一个JS文件就是一个模块,把一个独立的功能写到一个单独的JS文件,称之为一个模块
组件化:一个组件会包含有结构、样式、功能(js),一个组件就是一个vue实例

 
 


















//全局组件,在所有的vue实例中都可以使用
//参数1:组件名 参数2:可以配置和vue实例相同的配置 methods/computed/watch/template
Vue.component('demo',{
	template:'<div>这是一个全局组件</div>'
	//以下是错误的,只能有一个根元素
	//template:'<div>hello</div><div>hello</div>'
})

//根组件
const vm=new Vue({
	el:'#app',
	data:{},
	//局部组件
	componets:{
		demo:{
			template:'<div>这是一个局部组件</div>'
		}
	}
})

注意template

template参数是必须的,并且template中只能有一个根元素

# data属性

组件中的data必须是一个函数,且函数内部需要返回一个对象,这样可以保证每个组件的数据是独立的









 
 
 
 
 


Vue.component('demo',{
	template:'<div>这是一个全局组件</div>',
	data:function(){
		return {
			money:100
		}
	}
	//或者是
	data(){
		return {
			money:100
		}
	}
})

# Prop属性

  • props中的数据是不能修改的,只读的,单向数据流,防止意外改变父组件的信息
  • 如果props传递的是对象,是可以增、删、改对象的某个属性的,但是不提倡这样,不容易定位错误
  • html的属性名是忽略大小写的,所以Prop中不能出现大写,或者camelCase(驼峰命名法) 的prop名需要使用其等价的kebab-case(短横线分隔命名)命名
//在 HTML 中是 kebab-case 的
<blog post-title="hello!"></blog>

Vue.component('blog', {
  // 在 JavaScript 中是 camelCase 的
  props: ['postTitle'], //数组不支持校验
  template: '<h3>{{ postTitle }}</h3>'
})


//props校验
Vue.component('demo',{
	props:{
		propA:Number, //基础类型检测,首字母大写
		propA:[ Number,String ], //多种类型检测
		propC:{
			//必须是字符串
			type:String,
			required:true
		},
		propD:{
			//有默认值
			type:Number,
			default:100
		}
	}
})

//非props属性,会自动合并到子组件上,class和style也会自动合并
<div id="app">
   <hello class="aa" style="color:#ff0000"></hello>
</div>
<script>
  Vue.component('hello',{
	  template:'<div class="bb" style="font-size:14px">hello vue</div>'
  })
</script>

# template属性

定义模板的四种形式:

  • 直接使用字符串
  • < script type="text/x-template" id="tpl1">,使用template:"#tpl1"
  • 标签< template id="tpl1">,使用template:"#tpl1"
  • 使用.vue组件

# is属性

像table、ol、ul、select这种有特殊结构的html标签,不能直接使用组件

<table id="app">
   //自定义标签嵌套失效
   //<hello></hello>
   <tr is="hello"></tr>
</table>

<script>
Vue.component('hello',{
	template:'<h1>hello vue</h1>'
})
const vm=new Vue({
	el:'#app'
})
</script>

# keep-alive

  • 是Vue提供的一个抽象组件,用来对组件进行缓存,而不是销毁它们,从而节省性能,由于是一个抽象组件,所以在页面渲染完毕后不会被渲染成一个DOM元素
  • 当组件在keep-alive内被切换时组件的activated、deactivated这两个生命周期钩子函数会被执行
  • 当组件使用keep-alive的时候created钩子函数只会被执行一次
//创建一个A组件
<template>
    <div>
        <h1>我是A组件</h1>
    </div>
</template>
 
<script>
    export default {
        name: "index",
        created(){
            console.log("created");
        },
        activated(){
            console.log("activated");
        },
        deactivated(){
            console.log("deactivated");
        }
    }
</script>

//创建一个B组件
<template>
    <div>
        <h1>我是B组件</h1>
    </div>
</template>
 
<script>
    export default {
        name: "index"
    }
</script>
 
//修改App.vue文件
<template>
  <div id="app">
    <router-link to="/a">切换到a</router-link>
    <span>-----</span>
    <router-link to="/b">切换到b</router-link>
    <keep-alive>
      <!-- 路由匹配到的组件将渲染在这里 -->
      <router-view></router-view>
    </keep-alive>
 
  </div>
</template>
 
<script>
	export default {
	  name: 'App'
	}
</script>

// 当第一次点击切换到A的时候,调用了A组件的create和activeted钩子
// 点击切换到B的时候执行了deactivated钩子函数
// 点击切换到A的时候执行了activeted钩子函数

# 如何强制刷新某些组件

  • 在keep-alive激活会触发activated钩子函数 (页面被切换回来,展示在页面上的时候执行)
  • 利用include、exclude属性(注意是组件的名字,不是路由的名字)
//include属性表示只有name属性为bookLists,bookLists的组件会被缓存
//其它组件不会被缓存exclude属性表示除了name属性为indexLists的组件不会被缓存
<keep-alive include="bookLists,bookLists">
   <router-view></router-view>
</keep-alive>
<keep-alive exclude="indexLists">
   <router-view></router-view>
</keep-alive>
  • 利用meta属性
export default[
 {
  path:'/',
  name:'home',
  components:Home,
  meta:{
    keepAlive:true //需要被缓存的组件
 },
 {
  path:'/book',
  name:'book',
  components:Book,
  meta:{
     keepAlive:false //不需要被缓存的组件
 } 
]

<keep-alive>
  //这里是会被缓存的组件
  <router-view v-if="this.$route.meat.keepAlive"></router-view>
</keep-alive>
//这里是不会被缓存的组件
<keep-alive v-if="!this.$router.meta.keepAlive"></keep-alive>

# 动态组件

我们之前在一个多标签的界面中使用 is attribute 来切换不同的组件:

<component v-bind:is="currentTabComponent"></component>

当在这些组件之间切换的时候,你有时会想保持这些组件的状态,以避免反复重新渲染导致的性能问题

<div id="app">
	<button @click="page=='index'"></button>
	<button @click="page=='news'"></button>
	<button @click="page=='login'"></button>
	//可以动态调用不同的组件,keep-alive失活的组件将会被缓存
	<keep-alive>
	  <component :is="page"></component>
	</keep-alive>
</div>

<script>
  Vue.component('index',{
	  template:'<h1>首页</h1>'
  })
  Vue.component('news',{
  	  template:'<h1>新闻</h1>'
  })
  Vue.component('login',{
  	  template:'<h1>登录</h1>'
  })
  const vm=new Vue({
	  el:'#app',
	  data:{
		  page:'index'
	  }
  })
</script>

# 父传子

  • 子组件通过属性props,可以接受父组件传递过来的数据。props:['money','car']
  • 父组件需要给子组件传值 < son :money='money' :car='car' >< /son >
<div id='app'>
  <son :money='money' :car='car'></son>
</div>
<script>
Vue.component('son',{
	template:'<div>这是子组件 {{money}} {{car}}</div>',
	props:['money','car']
});
const vm=new Vue({
	el:'#app',
	data:{
		money:1000,
		car:'朗逸'
	}
})
</script>

# 子传父

  • 子组件触发一个自定义事件,通过this.$emit()触发某个实例来传递事件名和参数
  • 父组件给子组件注册自定义事件,使用@标记
  • 父组件提供一个方法,注册事件时使用的方法
<div id='app'>
  <son @son-fn='parentFn'></son>
</div>
<script>
Vue.component('son',{
	template:'<div>这是子组件 {{money}} {{car}} 
	          <button @click='fn'>传值给父组件</button> 
			  </div>',
	data:{
		money:1000,
		car:'朗逸'
	},
	methods:{
		fn(){
			//传递的参数可以是一个对象
			this.$emit('sonFn',this.money,this.car);
		}
	}
});
const vm=new Vue({
	el:'#app',
	data:{
		money:1000,
		car:'朗逸'
	},
	methods:{
		parentFn(money,car){
			this.money=money;
			this.car=car;
		}
	}
})
</script>

# 非父子

  • 创建一个bus(事件总线:event bus),所有组件都能访问,bus实质是一个空的vue实例
  • jack去触发bus的一个自定义事件,可以传递参数
  • rose给bus注册这个自定义的事件,提供一个函数,通过这个函数传递参数
  • vm.$on 注册当前实例上的自定义事件,可以由vm.$emit触发
//1、注册一个bus:事件总线
const bus=new Vue();

Vue.component('jack',{
	template:'<div>我是Jack組件
	         <button @click='say'>Jack说</button>
	         </div>',
	data:(){
		return {
			msg:'you jump,i look'
		}
	},
	methods:{
		say(){
			//2、通过bus触发某个实例
			bus.$emit('shuo',this.msg)
		}
	}
})

Vue.component('rose',{
	template:'<div>我是Rose組件
	         <font>{{msg}}</font>
	         </div>',
	data(){
		return {
			mag:''
		}
	},
	//3、rose给bus注册事件,这个事件注册的越早越好
	created(){
		//vm.$on 注册当前实例上的自定义事件,可以由vm.$emit触发
		//必须使用箭头函数,否则this指向的是bus
		bus.$on('shuo',(msg)=>{
			this.msg=msg
		})
	}
})

# 插槽slot

  • 当组件中某一项需要单独定义,那么就应该使用slot
  • Vue实现了一套内容分发的API,将slot元素作为承载分发内容的出口

# 插槽的类型

  • 默认插槽、匿名插槽:只有一个,不带name的slot会有一个隐含的default
  • 具名插槽:带有名字的插槽 name="名字",具名插槽的内容需要包含在template标签中
  • 作用域插槽:带有数据的插槽< slot name="header" money="100" car="小黄车" >< /slot >


 


 

 













 
 


 
 


 
 
 





















<div id="app">
   <modal>
     //具名插槽
     <tempalte v-slot:header>
	    <h3>温馨提示</h3>
	 </template>
	 //默认插槽、匿名插槽
     <p>确定要删除吗?</p>
	 //scope:作用域,是一个对象,其中包含插槽中所有的数据
	 <tempalte v-slot:btnSlot1="scope">
	    <button>{{scope.aa}}</button>
	 </template>
	 <tempalte v-slot:btnSlot2="scope">
	    <button>{{scope.ab}}</button>
	 </template>
   </modal>
</div>

<script>
  Vue.component('modal',{
	  template:`
	    <div class="modal">
		   <div class="top">
			 //具名插槽
			 <slot name="header"></slot>
		   </div>
		   <div class="content">
		     //默认插槽、匿名插槽
		     <slot></slot>
		   </div>
		   <div class="bom">
		     //作用域插槽,value字符串可以随便写
		     <slot name="btnSlot1" :aa="btn1"></slot>
			 <slot name="btnSlot2" :ab="btn2"></slot>
		   </div>
		</div>
	  `,
	  data(){
		  return {
			  btn1:'确定',
			  btn2:'取消'
		  }
	  }
  })
  
  const vm=new Vue({
	  el:'#app',
	  data:{
		  msg:'hello vue'
	  }
  })
</script>

# 自定义指令

  • 全局指令
  • 局部指令

 
 


 
 
 
 
 
 
 
 
 
 






 
 
 
 
 
 




<div id="app">
  //必须加 v-
  <input type="text" v-focus>
</div>

//全局指令(指令名、对象)
Vue.directive('focus',{
	//写指令的5个钩子函数
	
	//表示指令所在的元素已经插入到页面中
	//el:指的就是当前的元素
	inserted(el){
		el.focus();
	}
})

const vm=new Vue({
	el:'#app',
	data:{
		msg:hello vue''
	},
	//局部指令
	directives:{
		focus:{
			inserted(el){
				el.focus();
			}
		}
	}
})

# 5个钩子函数

  • bind():只会执行一次,在指令绑定到元素上的时候执行,此时元素不一定在页面中显示
  • inserted():所在的元素插入到页面中(此时元素在页面中显示)
  • update():当指令的值发生改变的时候触发,例如:v-text="msg"
  • componentUpdated():当所有的DOM都更新完成的时候触发
  • unbind():当指令在DOM元素上移除的时候触发

# 钩子函数的参数

所有钩子函数的参数是一样的

  • el:指令所在的DOM元素
  • binding:是一个对象,所含以下属性
 
 


 
 

 
 
 
 
 
 
 
 
 
 
 








v-on:click.stop.prevent='clickFn' 对应以下
v-指令名:指令的参数.指令的修饰符.指令的修饰符='指令的值'

<div id="app">
  //指令的值必须在vue的data存在
  <h1 v-demo:aa.bb.cc="msg">{{msg}}</h1>
</div>
Vue.directive('demo',{
	bind(el,binding){
		//指令的名字->demo
		binding.name 
		//指令值->hello vue
		binding.value
		//指令的参数 冒号后面的部分->aa
		binding.arg
		//指令的修饰符,可以有多个->bb、cc
		binding.modifiers
	}
})
const vm=new Vue({
	el:'#app',
	data:{
		msg:'hello vue'
	}
})

参数的使用

<div id="app">
  <h1 v-mybind:title="msg">{{msg}}</h1>
  <h1 v-color:bg.bold="color">{{color}}</h1>
  <h1 v-myon:click.prevent="clickFn">{{color}}</h1>
</div>

Vue.directive('mybind',{
	bind(el,binding){
		 el.setattribute(binding.arg,binding.vue)
	},
	//修改msg的时候title的值会跟着变
	update(el,binding){
		 el.setattribute(binding.arg,binding.vue) 
	}
})

Vue.directive('color',{
	bind(el,binding){
		 if(binding.modifiers.bold){
			 el.style.fontWeight='bold'
		 }
	},
	update(el,binding){
		 if(binding.modifiers.bold){
		 	el.style.fontWeight='bold'
		 }
	}
})

Vue.directive('myon',{
	bind(el,binding){
		 el.addEventListener(binding.arg,function(e){
			 binding.value();
			 if(binding.modifiers.prevent){
				e.preventDefault();
			 }
			 if(binding.modifiers.stop){
			 	e.stopPropagation();
			 }
		 });
	},
	update(el,binding){
		 el.addEventListener(binding.arg,function(e){
		 	  binding.value();
			  if(binding.modifiers.prevent){
			  	e.preventDefault();
			  }
			  if(binding.modifiers.stop){
			  	e.stopPropagation();
			  }
		 });
	}
})

//指令简写
//如果自定义指令只需要提供bind和update,并且逻辑是一样的
Vue.directive('color',function(el.binding){
	
})

const vm=new Vue({
	el:'#app',
	data:{
		msg:'hello vue',
		color:'red'
	},
	methods:{
		clickFn(){
			
		}
	}
})

# 路由使用

路由是浏览器URL中的哈希值(#hash)与视图组件之间的对应规则
vue中的路由:是hash和component的对应关系,一个哈希值对应一个组件

# 基本使用

  • 下载vue-router
  • 引入vue-router,一定要在vue.js后面
  • 创建一个路由对象
  • 关联路由对象和vue实例






 
 


const router=new VueRouter();
const vm=new Vue({
	el:'app',
	data:{
		msg:'hello'
	},
	//指定路由对象
	router:router
})

# 具体使用

  • 配置路由规则:hash值和组件的映射规则
  • 提供组件
  • 配置路由的显示出口,组件显示的位置router-view
//方式1:Vue.component('index',Index) 全局注册
//方式2:局部注册
//方式3:配置到路由的规则中

<div>
  <p>{{msg}}</p>
  <hr>
  //配置路由的显示出口
  <router-view></router-view>
</div>

//一个对象其实就可以是一个组件,但这个组件默认是是用不了的
const Index={
	template:`
	<div>我是首页组件</div>
	`
}
//方式1:全局注册
Vue.component('index',Index) 


//router:一个路由对象,一个项目只有一个路由对象
//route:一条路由规则
const router=new VueRouter({
	routes:[
		//方式3:配置到路由的规则中
		{path:'/index',component:Index},
		{path:'/user',component:User},
		{path:'/login',component:Login},
	]
});
const vm=new Vue({
	el:'app',
	data:{
		msg:'hello'
	},
	//指定路由对象的简写方式
	router,
	//方式2:局部注册
	components:{
		index:Index
	}
})

# 关于Vue.use

相信很多人在用Vue使用别人的组件时,会用到 Vue.use() 。例如:Vue.use(VueRouter)、Vue.use(Vuex)
但是用 axios时,就不需要用 Vue.use(axios),就能直接使用,是因为开发者在封装 axios 时,没有写 install 这一步

//1、在 Loading.vue 中定义一个组件
<template>
    <div class="loading-box">
        Loading...
    </div>
</template>

//2、在index.js中引入Loading.vue,并导出
import LoadingComponent from './loading.vue'
const Loading={
    // install 是默认的方法
	// 当外界在 use 这个组件的时候,就会调用本身的 install 方法,同时传一个 Vue 这个类的参数
    install:function(Vue){
        Vue.component('Loading',LoadingComponent)
    }
}
export default Loading

//3、在main.js中引入loading文件下的index
import Loading from './components/loading/index'
// 这时需要 use(Loading),如果不写 Vue.use()的话,浏览器会报错
Vue.use(Loading)

注意

通过全局方法 Vue.use() 使用插件,Vue.use 会自动阻止多次注册相同插件

# 路由导航

router-link 最终会渲染成a标签

 



 
 






<router-link to="/index">首页</router-link>

const router=new VueRouter({
	routes:[
		//路由的重定向
		{path:'/',redirect:'/index'},
		{path:'/index',component:index},
		{path:'/user',component:user},
		{path:'/login',component:login},
	]
})

# 路由命名

{path:'/index',component:index,name:'index'}
//使用
<router-link to="{name:'index'}}">首页</router-link>

# 当前导航

router-link-active:模糊匹配
router-link-exact-active:精确匹配

//让它精确匹配
<router-link to="/" exact></router-link>
  • 修改方式一:直接该类的样式
.router-link-exact-active,
.router-link-active{
	color:red;
	font-size:24px;
}
  • 修改方式二:修改默认类名
//全局配置<router-link>默认激活的class类名
const router=new VueRouter({
	routes:[],
	linkActiveClass:'cur',
	linkExactActiveClass:'cur'
})

# 路由嵌套

  • 子路由需要配置children
  • 子路由path不需要加'/'
  • 子路由需要单独加显示的位置 router-view
const index={
	template:`
	 <div>
	    这里是首页组件
	    <router-link to='/index/login'>登录</router-link>
		<router-link to='/index/reg'>注册</router-link>
		<hr>
		<router-view></router-view>
		//或者
		<router-view/>
	 </div>
	`
}

const router=new VueRouter({
	routes:[
		{path:'/index',component:index,children:[
			{path:'reg',component:reg},
			{path:'log',component:log},
		]}
	]
})

# 编程式导航

除了使用router-link 创建声明式导航外,还可以使用router的实例方法,通过编写代码来实现路由的跳转
比如登录成功后跳转到用户中心

methods:{
	login(){
		//this.$router指整个的路由对象
		this.$router.push('index'); // index
		this.$router.push({path:'index'}); // index
		this.$router.push({name:'index',params:{id:3}}); // index/3
		this.$router.push({path:'index',query:{age:30}}); // index?age=30
		this.$router.replace({path:'home'}); //替换当前路由
		this.$router.go(-1);
	}
}

# 动态路由匹配

  • $router:整个项目的路由对象,一个项目只有一个
  • $route:当前的路由规则 => 路径(当前地址栏中的路径)
const product={
	template:`
	  <div>这是id为{{this.$route.params.id}}的商品</div>
	`,
	created(){
		console.log(this.$router);
		console.log(this.$route);
	}
}

const router=new VueRouter({
	routes:[
		// :id 动态路由匹配
		{ path:'/product/:id',component:product},
	]
})

//$route详解
例:http://blog.1ge0.com/vue/02.html#/product/2?age=18&name=123
fullpath:#后面的内容 "/product/2?age=18&name=123" 
params:匹配动态路由的数据 {id:"2"} -> this.$route.params.id  -> 需要设置:id 动态路由匹配
query:?后面的内容 "age=18&name=123" -> this.$route.query.age
path:fullpath中除了query的部分"/product/2"

# 路由props

//使用props
<router-link to="/user/12">User</router-link>
const User = {
  props: ['id'],
  template: '<div>User {{ id }}</div>'
}
const routes = [
	{ path: '/user/:id', component: User, props: true }
]

//静态props
<router-link to="/tom">Tim</router-link>
const Tom = {
  props: ['id','age'],
  template: '<div>Tom {{ id }} {{ age }}</div>'
}
const routes = [
	{ path: '/tom', component: Tom, props: {id:2,age:20} }
]

//查询参数
<router-link to="/tim?q=456">Tim</router-link>
const Tim = {
  props: ['q'],
  template: '<div>Tim  {{ q }}</div>'
}
const routes=[
	{ path: '/tim', component: Tim, props:(route)=>({q:route.query.q}) }
]

# 命名视图

<div class="app">
  <h3>hello vue</h3>
  //可以打开多个视图
  <router-link to="/user">user</router-link>
  
  <router-view></router-view> //默认视图
  <router-view name="a"></router-view> //a视图
  <router-view name="b"></router-view> //b视图
</div>

<script>
const user={template:'welcome to you'};
const tim={template:'<h4>tim</h4>'};
const tom={template:'<h4>tom</h4>'};

//只替换默认视图的部分
const routes=[{path:'/user',components:{default:user,a:tim,b:tom}}]
const router=new VueRouter({
	routes
})

const vm=new Vue({
	el:'#app'
})
</script>

# 多视图控制

<div class="app">
  <h3>hello vue</h3>
  //可以打开多个视图
  <router-link to="/user/12">user</router-link>
  
  <router-view></router-view> //默认视图
  <router-view name="a"></router-view> //a视图
  <router-view name="b"></router-view> //b视图
</div>

<script>
const user={props:['id'],template:'welcome to you'};
const tim={props:['id'],template:'<h4>tim</h4>'};
const tom={props:['id'],template:'<h4>tom</h4>'};

//只替换默认视图的部分
const routes=[
	{
		path:'/user',
		components:{default:user,a:tim,b:tom},
		//只有a和b视图能获取到id的值12
		props:{default:false,a:true,b:true}
	}
]

const router=new VueRouter({
	routes
})

const vm=new Vue({
	el:'#app'
})
</script>

# 路由守卫

路由的钩子函数,分为全局的、路由的、组件的

<div id="app">
  <h3>hello vue</h3>
  <router-link to="/user/12">User-12</router-link>
  <router-link to="/def/34">Def-34</router-link>
  <router-link to="/def/56">Def-56</router-link>
</div>

<script>
const user={
	props:['id'],
	template:`<div>User:welcome!{{id}}</div>`
}

//组件的守卫
const ref={
	props:['id'],
	template:`<div>Ref:welcome!{{id}}</div>`,
	beforeRouteEnter(to,from.next){
		//在渲染该组件的对应路由前调用
		//不能使用this,因为当前组件实例还没创建
		console.log('组件def的守卫before……')
		next(vm=>{
			//通过vm访问组件实例
		})
	},
	beforeRouteUpdate(to,from,next){
		//在当前路由改变,组件被复用时调用
		//例如:对于带有动态参数的路径/user/:id,在/user/1和/user/2之间跳转的时候
		//由于会渲染同样的组件,因此组件实例会被复用,此时就用调用这个钩子函数
		//可以使用this
		console.log('组件def的守卫update……')
		next(vm=>{
			//通过vm访问组件实例
		})
	},
	beforeRouteLeave(to,from,next){
		//导航离开该组件的时候调用
		//可以使用this
		console.log('组件def的守卫leave……')
		next(vm=>{
			//通过vm访问组件实例
		})
	}
}

//路由的守卫
//与全局守卫相比路由守卫只是对当前路由进行单一控制,参数和全局前置守卫相同
const routes=[
	{
		path:'/user/:id',component:user,props:true,
		beforeEnter:(to,from,next)=>{
			console.log('路由守卫before……')
			next(vm=>{
				//通过vm访问路由实例
			})
		}
	},
	{path:'/def:id',component:tom,props:true}
]

//全局守卫
const router=new VueRouter({
	routes
})
router.beforeEach((to,from,next)=>{
	//前置守卫
	console.log('全局守卫开始……')
	next(vm=>{
		//通过vm访问路由实例
	})
})
router.afterEach((to,from,next)=>{
	//前置守卫
	console.log('全局守卫结束……')
	next(vm=>{
		//通过vm访问路由实例
	})
})

const vm=new Vue({
	el:'#app',
	router
})
</script>

# 数据获取

  • 导航完成之后获取
    跳转到页面后,在组件的生命周期created钩子函数中获取数据。在数据获取期间显示“加载中”之类指示
  • 导航完成之前获取
    页面跳转之前,在组件守卫beforeRouteEnter中获取数据,在数据获取成功后执行导航
<div id="app">
  <h3>hello vue</h3>
  <router-link to="/user/1">User1</router-link>
  <router-link to="/user/2">User2</router-link>
  
  <router-view></router-view>
</div>

<script>
const user= { 
	template:'<div>user:welcome!</div>',
	beforeRouteUpdate(to,from,next){
		console.log('导航完成之前获取……')
	}
}

const routes=[
	{ path:'/user/:id',component:user }
]

const router=new VueRouter({
	routes
})

const vm=new Vue({
	el:'#app',
	router,
	created(){
		console.log('导航完成之后获取……')
	}
})
</script>

# 路由元信息

mate字段来定义路由的额外条件
比如:访问用户中心需要先登录,那么用户未登录访问用户中心应该能被检测到

<div id="app">
  <h3>hello vue</h3>
  <router-link to="/user/12">User</router-link>
  <router-link to="/login">Login</router-link>
  
  <router-view></router-view>
</div>

<script>
const user= { template:'<div>user:welcome!</div>'}
const login= { template:'<div>name:<input/> pass:<input/> <button>登录</button></div>'}

const routes=[
	{
		path:'/user/:id',component:user,
		//元信息,需要登录校验
	    meta:{ requiresAuth:true }
	},
	{ path:'login',component:login}
]

const router=new VueRouter({
	routes
})
//全局首位检查元信息
router.beforeEach((to,from,next)=>{
	//检查匹配的路径元信息,判断是否需要登录
	if(to.matched.some(record=>record.meta.requiresAuth)){
		let login=false;//从后台查询的
		if(!login) {
			next({
				path:'/login',
				query:{ reditect:to.fullPath }
			})
		}else{
			next()
		}
	}else{
	   next()  //确保一定要调用
	}
})

const vm=new Vue({
	el:'#app',
	router
})
</script>

# 懒加载和魔法注释

魔法注释:对打包起作用

const router=new VueRouter({
	routes:[{
		path:'about',
		name:'About',
		component:()=>import(/* webpackChunkName:"about" */ '../views/About.vue')
	}]
})

# 路由动画

想要在你的路径组件上使用转场,并对导航进行动画处理,你需要使用 v-slot

<router-view v-slot="{ Component }">
  <transition name="fade">
    <component :is="Component" />
  </transition>
</router-view>

# 单个路由的过渡

上面的用法会对所有的路由使用相同的过渡。如果你想让每个路由的组件有不同的过渡,你可以将元信息和动态的 name 结合在一起,放在transition上

<router-view v-slot="{ Component, route }">
  <!-- 使用任何自定义过渡和回退到 `fade` -->
  <transition :name="route.meta.transition || 'fade'">
    <component :is="Component" />
  </transition>
</router-view>

const routes = [
  {
    path: '/custom-transition',
    component: PanelLeft,
    meta: { transition: 'slide-left' },
  },
  {
    path: '/other-transition',
    component: PanelRight,
    meta: { transition: 'slide-right' },
  },
]

# 基于路由的动态过渡

也可以根据目标路由和当前路由之间的关系,动态地确定使用的过渡。使用和刚才非常相似的片段

<!-- 使用动态过渡名称 -->
<router-view v-slot="{ Component, route }">
  <transition :name="route.meta.transition">
    <component :is="Component" />
  </transition>
</router-view>

我们可以添加一个 after navigation hook,根据路径的深度动态添加信息到 meta 字段

router.afterEach((to, from) => {
  const toDepth = to.path.split('/').length
  const fromDepth = from.path.split('/').length
  to.meta.transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left'
})

# 滚动行为

使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。 我们在创建一个router实例的时候,可以提供一个scrollBehavior方法,该方法会在用户切换路由时触发

# history 模式

vue-router 在 history 模式下,提供了一个 scrollBehavior 方法,用以控制滚动行为,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样

const router = new VueRouter({
  routes: [...],
  scrollBehavior (to, from, savedPosition) {
    // return 期望滚动到哪个的位置
  }
})

如下为浏览器页面加载的默认操作,如果没有传入滚动条位置信息(savedPosition),就回到页面的顶部,即返回位置信息 {x:0,y:0}

scrollBehavior (to, from, savedPosition) {
  if (savedPosition) {
    return savedPosition
  } else {
    return { x: 0, y: 0 }
  }
}

# hash 模式

在 hash 模式下,需要使用官方的导航守卫中的router.beforeEach

router.beforeEach((to, from, next) => {
  window.scrollTo(0, 0)  
  next()
})

# 状态管理

# Module

store模块分割,每个模块拥有自己的State、Mutation、Action、Getter

const store=new Vuex.Store({
	modules:{
		user,
		login
	}
})

//user.js
export default {
  namespaced: true, //设置模块声明
  state: {
    userStatus: false,
    userInfo: {},
    userToken: ''
  },
  ……
}

# State 公共数据源

提供唯一的公共数据源,所有共享的数据都要统一当道Store的State中存储

//创建数据源
const store=new Vuex.Store({
	state:{
		// 登录状态
		loginStatus:false,
		// token
		userToken:'',
		// 用户信息
		userInfo:{}
	}
})

//第一种访问数据源方式
this.$store.state.userToken
this.store.state.userToken
<div>{{$store.state.userToken}}</div>

//第二种访问数据源方式:辅助函数
//只有mapState没加s
import { mapState } from 'vuex'
computed:{
	...mapState(['loginStatus','userToken','userInfo'])
	//或者
	...mapState({
		loginStatus:state=>state.user.userInfo,
		userInfo:state=>state.login.userInfo,
		userToken:state=>state.user.userToken
	})
}

# Getter 加工处理Store中的数据

  • Getter用于对Store中的数据进行加工处理形成新的数据,不会改变原数据,类似计算属性
  • Store中的数据发生变化,Getter的数据也会发生变化
const store=new Vue.Store({
	state:{
		count:0
	},
	getters:{
		showNum:state=>{
			return '当前的数量是:'+ state.count +''
		}
		//或者
		showNum(state){
			return '当前的数量是:'+ state.count +''
		}
	}
})

//使用方式一
this.$store.getters.showNum
//使用方式二
import {mapGetters} from 'vuex'
computed:{
	…mapGetters(['showNum'])
	//或者
	...mapGetters({
		showNum1:state=>state.user.showNum1,
		showNum2:state=>state.login.showNum2
	})
}

# Mutation 变更State中的数据

不能在方法中执行异步的操作 setTimeOut(()=>{},1000)

const ADD_CART='addCart'

const store=new Vuex.Store({
	mutations:{
		login(state,userinfo){
			state.userInfo =userinfo
			state.loginStatus = true
			state.userToken = userinfo.UserToken
			// 持久化存储
			localStorage.userInfo=JSON.stringify(userinfo)
		},
		//常量事件类型
		[ADD_CART](state){
			//todo
		}
	}
})

//触发方式一
this.$store.commit('login',userinfo)
this.store.commit('login',userinfo)

//触发方式二
import { mapMutations } from 'vuex'
methods:{
	//mutation依然没有命名空间的概念 所以在定义 mutations 时要注意全局的唯一性
	...mapMutations(['login','loginOut'])
	...mapMutations('user',['loginUser','loginOutUser'])
	...mapMutations('shop',['loginShop','loginOutShop'])
}
//可以直接调用此方法
<button @click="login">点击</button>
<button @click="loginOut">点击</button>

# Action 处理异步任务

在Action中还是要通过触发Mutation的方式间接变更数据

const store=new Vue.Store({
	state:{
		count:0
	},
	mutations:{
		add1(state){
			state.count++
		},
		add2(state,step){
			state.count+=step
		},
	},
	actions:{
		addAsync1(context){
			setTimeout(()=>{
				//mutation中的方法add
				context.commit('add1')
			},1000)
		},
		addAsync2(context,step){
			setTimeout(()=>{
				//mutation中的方法add
				context.commit('add2',step)
			},1000)
		}
	}
})

//触发方式一
this.$store.dispatch('addAsync1');
this.$store.dispatch('addAsync2',5);

this.store.dispatch('addAsync1');
this.store.dispatch('addAsync2',5);

//触发方式二
import { mapActions } from 'vuex'
methods:{
	...mapAction(['addAsync1','addAsync2'])
	...mapAction('user',['loginUser','loginOutUser'])
	...mapAction('shop',['loginShop','loginOutShop'])
}
//可以直接调用此方法
<button @click="addAsync1">点击</button>
<button @click="addAsync2(5)">点击</button>

# Mixin混入

定义一部分公共的方法或者计算属性,然后混入到各个组件中使用,方便管理和统一修改,同一个生命周期,混入对象会比组件的先执行

# 基础使用

# 第一步 导出mixins

创建minxins/index.js,写入

export const MixinsFn={
	created(){
		console.log("这是minxins触发的created")
	}
}

# 第二步 引用mixins

import { MixinsFn } from '@/mixins/index.js'
export default {
	mixins:[MixinsFn],
	created(){
		console.log("这是组件触发的created")
	}
}