# 基本语法

# 渐进式框架

# 渐进式

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

渐进式框架

# 框架

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

# MVVM

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

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

# 插值表达式

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

# 指令(14个)

# v-bind

  • 动态地绑定一个或多个属性,或一个组件 prop 到表达式,简写 :
  • class和style可以绑定对象或数组
<!--数据 obj:{class1:true,class2:true,class3:true}-->
<div class="base fz" :class="obj"></div> 

<!--数据 arr:['class1','class2','class3']-->
<div class="base fz" :class="arr"></div>

<!--注意单括号-->
<div class="base fz" :class="{class1:true}"></div>

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

# v-model

在表单控件或者组件上创建双向绑定,会忽略掉表单元素原本的value

  • 收集表单数据
<input type="text"/> # v-mode1收集的是value值,用户输入的就是value值
<input type="radio"/> # v-model收集的是value值,且要给标签配置value值
<input type="checkbox"/>
# 没有配置input的value属性,那么收集的就是checked(勾选or未勾选,是布尔值)
# 配置input的value属性,并且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>
  • 数据改变视图跟着改变
    • angular.js 1.0版本通过脏数据检查机制(数据轮询),性能比较低,兼容IE8
    • 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……
<!--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>

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

注意

在开发过程中会遇到按键修饰符不生效的情况,此时我们需要加上 .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的作用 (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

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

<!--过滤器接收的第一个参数是companyName-->
<div>{{ companyName | Name }}</div> 

<script>
 const vm=new Vue({
	 el:'#app',
	 data: {
	 	companyName: '浙江杭州阿里巴巴',
	 },
	 // 局部过滤器 :只有在当前实例中能使用的过滤器
	 filters: {
	 	Name: function (value) {
	 	   return value.substr(0, 4);     
	 	}
	 }
 })
 
 // 全局过滤器 :可以在所有vue实例中都能使用的过滤器
 Vue.filter('Name',funtion(value){
	 return value.substr(0, 4);     
 })
</script>
  • 可以传递参数
<div>{{ companyName | Name(4) }}</div> 

<script>
 filters: {
	Name: function (value,length) {
		return value.substr(0, length);     
	}
 }
</script>
  • 可以传递多个过滤器
<!--将name1过滤后的数据再传递给name2-->
<div>{{ companyName | Name1 | Name2}}</div> 

# axios

  • vue1.0使用vue-resource,引入它之后可以基于全局Vue,基于xhr,支持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
})

# DOM的异步更新

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

// 局部的,自动绑定到调用它的实例上
vm.$nextTick(function(){
	
}) 

// 或者
// created的先执行,再执行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');
// 更新数组的值
this.$set(this.person.hobby,0,'逛街');

// 添加一个属性'brand'值为'bmw'
this.$set(this.car,'brand','bmw');

// 删除一个属性'brand'
this.$delete(this.car,'brand');
Vue.delete(this.car,'color','red');

# ref操作DOM

  • 给元素设置ref的属性值,在Vue的实例选项mounted方法中通过this.$refs.属性值 获取到要操作的DOM
  • 给组件设置ref的属性值,可以在父组件中使用$refs访问子组件
<div id="app">
   <child ref="btn1"></child>
   <!--添加了ref 使用默认事件时需要加native-->
   <child ref="btn2" @click.native="show"></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节点
const navbar = this.$refs.nav.$el.offsetTop
</script>

# 完整的定时器

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>'
		},
		// 组件名是多个字母 或者 MyDemo
		"my-demo":{
			template:'<div>这是一个局部组件</div>'
		}
	}
})

注意

  • template参数是必须的,并且template中只能有一个根元素
  • VueComponent的实例对象,简称vc,也成为组件实例对象
  • Vue的实例对象简称vm,一个项目只有一个在main.js中

# render函数

在main.js中引入的是残缺版的vue不包含template,需要使用render函数接收createElement指定具体内容

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  router,
  store,
  // 将App组件放入容器中
  render: h => h(App)
  // render:q=>q('h1','你好啊' )
}).$mount('#app')
<template>
	<div id="app">
	  <img alt="vue logo" src="./assets/logo.png" />
	  <HelloWorld mgs="Welcome to your vue" name="李四" age="15"></HelloWorld>
	</div>
</template>
import { createApp, defineComponent, h } from 'vue'
import HelloWorld from './components/HelloWorld.vue'

const img = require('./assets/logo.png') 
const App = defineComponent({
  render() {
	// 第一个参数 节点类型 p为dom原生节点,需要通过字符串"p"来标识
	// 第二个参数 节点属性 p节点得属性
	// 第三个参数 节点的孩子节点 内部节点(子内容)
    return h('p', { id: 'app' }, [
      h('img', {
        alt: 'vue.logo',
        src: img,
      }),
      h(HelloWorld, {
        msg: 'Welcome to your vue',
        name: '李四',
        age: 15
      }),
    ])
  },
})

vue.js与vue.runtime.xxx.js的区别

  • vue.js是完整版的Vue,包含:核心功能 + 板解器
  • vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器
  • 普通的vc组件中是通过 vue-template-compiler(package.json中) 组件解析template

# template属性

定义模板的四种形式:

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

# el属性

<div id="root">
  <h1>你好,{{name}}</h1>
</div>

<script>
  Vue.config.productionTip = false // 阻止vue在启动时生成生产提示
  
  const v = new Vue({
	  el: "#root",
	  data:{
		  name:"zhangsan"
	  }
  })
  
  // 或者
  v.$mount("#root")
</script>

# data属性

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









 
 
 
 
 


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

# Prop属性

  • props中的数据是不能修改的,只读的,单向数据流,防止意外改变父组件的信息
  • 如果props传递的是对象,是可以增、删、改对象的某个属性的,但是不提倡这样,不容易定位错误
Vue.component('demo',{
	props:{
		propA:Number, // 基础类型检测,首字母大写
		propA:[ Number,String ], // 多种类型检测
		propC:{
			// 必须是字符串
			type:String,
			required:true
		},
		propD:{
			// 有默认值
			type:Number,
			default:100
		}
	}
})
  • html的属性名是忽略大小写的,所以Prop中不能出现大写,或者camelCase(驼峰命名法) 的prop名需要使用其等价的kebab-case(短横线分隔命名)命名
<!--在HTML中是 kebab-case-->
<blog post-title="hello!"></blog>
<script>
	Vue.component('blog', {
	  // 在 JavaScript 中是 camelCase 的
	  props: ['postTitle'], // 数组不支持校验
	  template: '<h3>{{ postTitle }}</h3>'
	})
</script>
  • 非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>

# 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>

# 插件

相信很多人在用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>

<script>
//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)
</script>

本质是一个包含install方法的对象,install的第一个参数是vue,第二个以后的参数是传递的数据

对象.install = function (Vue, options) {
	// 添加全局过滤器
	Vue.filter(……)
	
	// 添加全局指令
	Vue.directive(……)
	
	// 配置全局混入
	Vue.mixin(……)
	
	// 添加实例方法
	Vue.prototype.$myMethod = function(){……}
	Vue.prototype.$myProperty = xxx
}

// 使用插件
Vue.use()

注意

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

# 父传子

子组件通过属性props,可以接受父组件传递过来的数据

<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'>
  <!--第一种方式,只触发一次可以加once-->
  <son @son-fn='parentFn'></son>
</div>

<script>
Vue.component('son',{
	template:`<div>这是子组件 {{money}} <button @click='fn'>传值给父组件</button> </div>`,
	data:{
		money:1000,
		car:'朗逸'
	},
	methods:{
		fn(){
			// 传递的参数可以是一个对象
			this.$emit('son-fn',this.money,this.car);
			// 只触发一次
			this.$once('son-fn',this.money,this.car);
		}
	}
});
const vm=new Vue({
	el:'#app',
	data:{
		money:1000,
		car:'朗逸'
	},
	mounted(){
		// 第二种方式
		this.$on('son-fn',this.parentFn)
	},
	methods:{
		parentFn(money,car){
			this.money=money;
			this.car=car;
		}
	}
})
</script>

# 任意组件间通信

  • 创建一个bus(事件总线:event bus),所有组件都能访问,bus实质是一个空的vue实例
  • vm.$on 注册当前实例上的自定义事件,可以由vm.$emit触发
  • 最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件
// 1 注册一个bus:事件总线 main.js
new Vue({
	el:'#app',
	render: h=> h(App),
	beforeCreate() {
		Vue.prototype.$bus = this
	}
})

Vue.component('jack',{
	template:'<div>我是Jack組件 <button @click="say">Jack说</button></div>',
	data:(){
		return {
			msg:'you jump,i look'
		}
	},
	methods:{
		say(){
			// 2 去触发bus的一个自定义事件,可以传递参数
			this.$bus.$emit('shuo',this.msg)
		}
	},
	beforeDetory(){
		this.$bus.$off('shuo') // 解绑单个事件
		this.$bus.$off(['shuo1'],['shuo2']) // 解绑多个事件
		this.$bus.$off() // 解绑所有事件
	}
})

Vue.component('rose',{
	template:'<div>我是Rose組件 <font>{{msg}}</font></div>',
	data(){
		return {
			mag:''
		}
	},
	// 3 注册这个自定义的事件,提供一个函数,通过这个函数传递参数,这个事件注册的越早越好
	created(){
		// 必须使用箭头函数,否则this指向的是bus
		this.$bus.$on('shuo',(msg)=>{
			this.msg=msg
		})
	}
})

# keep-alive

是Vue提供的一个抽象组件,用来对组件进行缓存,而不是销毁它们,从而节省性能,由于是一个抽象组件,所以在页面渲染完毕后不会被渲染成一个DOM元素

  • 当组件在keep-alive内被切换时组件的activated、deactivated这两个生命周期钩子函数会被执行
  • 当组件使用keep-alive的时候created钩子函数只会被执行一次
<!--创建一个A组件-->
<template>
    <div>
        <h1>我是A组件</h1>
    </div>
</template>
 
<script>
    export default {
        name: "index",
		// 当第一次点击切换到A的时候,调用了A组件的create钩子
        created(){
            console.log("created");
        },
		// 当第一次点击切换到A的时候,调用了A组件的activeted钩子
		// 点击切换到A的时候执行了activeted钩子函数
        activated(){
            console.log("activated");
        },
		// 点击切换到B的时候执行了deactivated钩子函数
        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>

# 如何强制刷新某些组件

  • 在keep-alive激活会触发activated钩子函数 (页面被切换回来,展示在页面上的时候执行)
  • 利用include、exclude属性(注意是组件的名字,不是路由的名字)
<!--include属性表示只有name属性为bookLists,bookLists的组件会被缓存-->
<keep-alive include="bookLists,bookLists">
   <router-view></router-view>
</keep-alive>

<!--其它组件不会被缓存exclude属性表示除了name属性为indexLists的组件不会被缓存-->
<keep-alive exclude="indexLists">
   <router-view></router-view>
</keep-alive>
  • 利用meta属性
<script>
export default[
 {
  path:'/',
  name:'home',
  components:Home,
  meta:{
    keepAlive:true // 需要被缓存的组件
 },
 {
  path:'/book',
  name:'book',
  components:Book,
  meta:{
     keepAlive:false // 不需要被缓存的组件
 } 
]
</script>

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

# 动态组件

我们之前在一个多标签的界面中使用 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>

# 插槽使用

  • Vue实现了一套内容分发的API,将slot元素作为承载分发内容的出口
  • 让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式

# 插槽的类型

  • 默认插槽、匿名插槽:只有一个,不带name的slot会有一个隐含的default
  • 具名插槽:带有名字的插槽 v-slot:header,具名插槽的内容需要包含在template标签中
  • 作用域插槽:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定
<div id="app">
   <modal>
     <!--具名插槽-->
     <tempalte v-slot:header>
	    <h3>温馨提示</h3>
	 </template>
	 
	 <!--默认插槽、匿名插槽-->
     <p>确定要删除吗?</p>
	 
	 <!--作用域,是一个对象,其中包含插槽中所有的数据-->
	 <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:'取消'
		  }
	  }
  })
</script>

# 自定义指令


 
 




 















 









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

<script>
// 全局指令(指令名、对象)
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();
			}
		}
	}
})
</script>

# 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>

<script>
Vue.directive('demo',{
	bind(el,binding){
		// 指令的名字->demo
		binding.name 
		// 指令值->hello vue
		binding.value
		// 指令的参数 冒号后面的部分->aa
		binding.arg
		// 指令的修饰符,可以有多个->bb、cc
		binding.modifiers
	}
})
</script>

# 参数的使用

<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>

<script>
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();
			  }
		 });
	}
})
</script>

# 指令简写

如果自定义指令只需要提供bind和update,并且逻辑是一样的

Vue.directive('color',function(el,binding){
	
})

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

# 路由使用

2022年2月7日以后,vue-router的默认版本为4版本

# 懒加载和魔法注释

魔法注释:对打包起作用

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

# 路由器工作模式

history模式

  • 优点:URL更加美观,不带有#,更接近传统的网站URL
  • 缺点:后期项目上线,需要服务端配合处理路径问题,否则刷新会有404错误









 



server {
	listen       80;
	server_name  localhost;
    root         /root/gshop;
	
    location /api {
       root      /root/gshop;
	   index     index.html;
	   // nginx添加配置
	   try_files $uri $uri/ /index.html;
	}
}

hash 模式

  • 优点:兼容性更好,因为不需要服务器端处理路径
  • 缺点: URL 带有#不太美观,且在 SEO 优化方面相对较差

# 路由导航

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="/index">首页</router-link>
<router-link :to="{path:'/index'}">首页</router-link>
<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);
		this.$router.forward();
		this.$router.back();
	}
}

# 动态路由匹配

  • $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

作用:让路由组件更方便的收到参数

{
	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
		}  
	}
}

# 多视图控制

<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('全局守卫结束……')
	if(to.meta.title){
	   document.title=to.meta.title //修改网页的title
	} else {
	  document.title ='vue test'
	}
	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>

# 路由动画

想要在你的路径组件上使用转场,并对导航进行动画处理,你需要使用 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>

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

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

<!-- 使用动态过渡名称 -->
<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 模式
const router = new VueRouter({
  routes: [...],
  // 如果没有传入滚动条位置信息(savedPosition),就回到页面的顶部,即返回位置信息 {x:0,y:0}
  scrollBehavior (to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition
    } else {
      return { x: 0, y: 0 }
    }
  }
})
// hash 模式,需要使用官方的导航守卫中的router.beforeEach
router.beforeEach((to, from, next) => {
  window.scrollTo(0, 0)  
  next()
})

# 状态管理

vue2种使用vuex3,vue3种使用vuex4

# 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混入

可以把多个组件公用的配置提取成一个混入对象

# 第一步 定义混入

创建minxins/index.js,写入

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

# 第二步 使用混入

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