组件树html
DOM访问前端
数据访问vue
DOM方法的使用git
// vue 的 渲染过程是异步的<template> <div id="app"> <p>{{text}}</p> </div></template><script>export default { data() { return { text: 0 }; } mounted(){ setInterval(()=> { this.text +=1; this.text +=1; this.text +=1; this.text +=1; this.text +=1; },1000) }}</script>
能够看到text值的变化是0 5 10 15 … 而并无出现 0 1 2 3 … 这样连续的变化github
event方法的使用vue-router
1.监听vuex
// 第一种写法watch: { text(new, old) { console.log(`${new}:${old}`); }}// 第二种写法const unWatch = this.$watch('text',(new,old)=> console.log(`${new}:${old}`);})// 2秒后销毁 unWatchsetTimeout(()=> { unWatch();},2000)// 两种写法的结果同样,只是第二种须要在组件销毁手动销毁$watch
2.触发数组
3.删除浏览器
4.其余缓存
<template> <div id="app"> <p>{{obj.a}}</p> </div></template><script>export default { data() { return { obj:{} }; } mounted(){ let i = 0; setInterval(()=> { i++; // 第一种 this.obj.a = i ; // obj.a没有定义,vue是没法监听到这个属性的变化,因此页面的值也不会变化,这时能够用$forceUpdate进行强制渲染,固然不推荐这种用法 this.$forceUpdate(); // 第二种 this.$set(this.obj,'a',i); },1000) }}</script>
Vue 生命周期
vue 官方生命周期
render (h) { throw new TypeError('render error') // console.log('render function invoked') // render 在beforeMount 和 mounted之间执行 // return h('div', {}, this.text) // 虚拟DOM},renderError (h, err) { return h('div', {}, err.stack)},errorCaptured () {// 会向上冒泡,而且正式环境可使用}
若是要修改data里面的值,最先只能放到create生命周期中
Vue 数据绑定
<template> <div id="app"> <p>{{isActive?'active':'notActive'}}</p> <p>{{arr.join(' ')}}</p> <p>{{Date.now()}}</p> <p v-html="html"></p> <div :class="{ active: isActive }" :style="[styles, styles2]" ></div> <div :class="[isActive? 'active':'']"></div> <ul> <li v-for="(item,index) in arr" :key="index">{{item}}</li> </ul> // 单个checkbox <input type="checkbox" v-model="a"> {{a}} <br/> // 多个checkbox 爱好:<input type="checkbox" v-model="b" value="游泳"> 游泳 <input type="checkbox" v-model="b" value="游泳"> 登山 <input type="checkbox" v-model="b" value="游泳"> 睡觉 性别:<input type="radio" v-model="c" value="男"> 男 <input type="radio" v-model="c" value="女"> 女 // 只绑定一次 <p v-once="a"></p> </div></template><script>export default { data() { return { isActive: false, arr: [1, 2, 3], html: '<span>123</span>', styles: { color: 'red', appearance: 'none' }, styles2: { color: 'black' }, a: false, b:[], // 能够拿到checkbox 的 value c:'' // 性别 }; }}</script>
v-model 的修饰符
来自官网的例子:
1. .number
若是想自动将用户的输入值转为数值类型,能够给 v-model 添加 number 修饰符:
<input v-model.number="age" type="number">
这一般颇有用,由于即便在 type=”number” 时,HTML 输入元素的值也总会返回字符串。
2. .trim
若是要自动过滤用户输入的首尾空白字符,能够给 v-model 添加 trim 修饰符:
<input v-model.trim="msg">
3. .lazy
在默认状况下, v-model 在每次 input 事件触发后将输入框的值与数据进行同步 。你能够添加 lazy 修饰符,从而转变为使用 change 事件进行同步(当输入框失去焦点):
<!-- 在“change”时而非“input”时更新 --><input v-model.lazy="msg" >
数组和对象的注意事项
数组
因为 JavaScript 的限制,Vue 不能检测如下变更的数组:
var vm = new Vue({ data: { items: ['a', 'b', 'c'] }})vm.items[1] = 'x' // 不是响应性的vm.items.length = 2 // 不是响应性的
为了解决第一类问题,如下两种方式均可以实现和 vm.items[indexOfItem] = newValue 相同的效果,同时也将触发状态更新:
// Vue.setVue.set(vm.items, indexOfItem, newValue)// Array.prototype.splicevm.items.splice(indexOfItem, 1, newValue)
你也可使用 vm.$set 实例方法,该方法是全局方法 Vue.set 的一个别名:
vm.$set(vm.items, indexOfItem, newValue)
为了解决第二类问题,你可使用 splice:
vm.items.splice(newLength)
对象
Vue 不能检测对象属性的添加或删除:
var vm = new Vue({ data: { a: 1 }})// `vm.a` 如今是响应式的vm.b = 2// `vm.b` 不是响应式的
对于已经建立的实例,Vue 不能动态添加根级别的响应式属性。可是,可使用 Vue.set(object, key, value) 方法向嵌套对象添加响应式属性。例如,对于:
var vm = new Vue({ data: { userProfile: { name: 'Anika' } }})
你能够添加一个新的 age 属性到嵌套的 userProfile 对象:
Vue.set(vm.userProfile, 'age', 27)
你还可使用 vm.$set 实例方法,它只是全局 Vue.set 的别名:
vm.$set(vm.userProfile, 'age', 27)
有时你可能须要为已有对象赋予多个新属性,好比使用 Object.assign() 或 _.extend() 。在这种状况下,你应该用两个对象的属性建立一个新的对象。因此,若是你想添加新的响应式属性,不要像这样:
Object.assign(vm.userProfile, { age: 27, favoriteColor: 'Vue Green'})
你应该这样作:
vm.userProfile = Object.assign({}, vm.userProfile, { age: 27, favoriteColor: 'Vue Green'})
computed 计算属性
计算属性的使用
<template> <div id="app"> <p>{{name}}</p> </div></template><script>export default { data() { return { firstName: 'Fin', lastName: 'Get', }; }, computed: { name() { return `${this.firstName}${this.lastName}` } }}</script>
双向绑定的计算属性与Vuex
// vuex state是没法直接修改的,官方给出了 v-model 的解决方案<input v-model="message">computed: { message: { get () { return this.$store.state.obj.message }, set (value) { this.$store.commit('updateMessage', value) } }}
若是在方法或者生命周期中使用了计算属性,则必须设置一个set
watch 监听器
watch 简单使用
<div id="demo">{{ fullName }}</div>var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar', fullName: 'Foo Bar' }, watch: { // watch 方法最初绑定的时候,它是不会执行的,只有变化了才会执行 firstName: function (val) { this.fullName = val + ' ' + this.lastName }, lastName: function (val) { this.fullName = this.firstName + ' ' + val } }})watch: { // 声明一个handler,这样在初始化时就会执行一次 handler firstName: { handler(val) { this.fullName = val + ' ' + this.lastName }, immediate: true }}
监听对象属性的变化
<div id="demo">{{ obj.a }}</div><input v-model="obj.a" />var vm = new Vue({ el: '#demo', data: { obj: { a: '123' } }, watch: { obj: { handler() { console.log('obj.a changed'); }, immediate: true, deep: true // 若是不加这一句,在输入框中输入值,并不会打印 obj.a changed } }})// 这样写就能监听到属性值的变化watch: { 'obj.a': { handler() { console.log('obj.a changed'); } }}
Vue 组件
Vue 组件中的data为何必须是函数
官网解释
在Vue组件中data必须是函数,可是在 new Vue() 中data能够是一个对象
Vue.component('MyComponent', { template: '<div>this is a component</div>', data() { return {} // 返回一个惟一的对象,不要和其余组件共用一个对象进行返回 },})
上面定义了一个 MyComponent 组件,在这里咱们能够把这个组件当作一个构造函数。在其余页面引入,并注册组件时,其实是对这个构造函数的一个引用。当在模板中正真使用组件时相似于实例化了一个组件对象。
// 模拟一下let MyComponent = function() { // 定义一个构造函数}MyComponent.prototype.data = { name: 'component', age: 0}// 实例化组件对象let componentA = new MyComponent();let componentB = new MyComponent();componentA.data.name === componentB.data.name; // truecomponentA.data.age = 4;componentB.data.name;
能够看出,两个实例组件对象的data是如出一辙的,一个改变也会致使另外一个改变,这在实际开发中是不符合组件式思想的。
// 模拟一下let MyComponent = function() { // 定义一个构造函数}// 这样就好了 写成函数,函数有本身的做用域,不会相互影响MyComponent.prototype.data = function() { return { name: 'component', age: 0 }}
用 Vue.use() 定义全局组件
// 定义一个 button 组件// button.vue<template> <div class="button"> 按钮 </div></template><script></script>// button.jsimport ButtonComponent from './button.vue';const Button={ install:function (Vue) { Vue.component('Button',ButtonComponent) }}export default Button;// main.jsimport Button from './component/button.js';Vue.use(Button);
完成上面的步骤就能够在全局使用button组件了,其实最重要的 Vue.component('Button',ButtonComponent) , Vue.use(Button) 会执行install方法,也能够直接在 main.js 使用 Vue.component() 注册全局组件。
props
<template> <div class="button"> 按钮 </div></template><script>export default { props: ['msg'], // 没有任何限制 // 输入限制 props: { // 基础的类型检查 (`null` 匹配任何类型) propA: Number, // 多个可能的类型 propB: [String, Number], // 必填的字符串 propC: { type: String, required: true }, // 带有默认值的数字 propD: { type: Number, default: 100 }, // 带有默认值的对象 propE: { type: Object, // 对象或数组且必定会从一个工厂函数返回默认值 default: function () { return { message: 'hello' } } }, // 自定义验证函数 propF: { validator: function (value) { // 这个值必须匹配下列字符串中的一个 return ['success', 'warning', 'danger'].indexOf(value) !== -1 } } }}</script>
子组件是不能直接修改props的。
Vue组件之间的通讯问题能够看这里…
Vue 组件 extend
使用 Vue.extend 就是构造了一个Vue构造函数的“子类”。它的参数是一个 包含组件选项的对象 ,其中 data 选项必须是函数。
import Vue from 'vue'// 一个包含组件选项的对象const compoent = { props: { active: Boolean, propOne: String }, template: ` <div> <input type="text" v-model="text"> <span v-show="active">see me if active</span> </div> `, data () { return { text: 0 } }, mounted () { // 这个mounted先打印 console.log('comp mounted'); }}// 建立一个“子类”const CompVue = Vue.extend(compoent);// 实例化一个“子类”new CompVue({ el: '#root', propsData: { // 这里若是用props,组件内是拿不到值的 propOne: 'xxx' }, data: { text: '123' }, mounted () { console.log('instance mounted'); }})const component2 = { extends: component, // 继承于 component data(){ return { text: 1 } }, mounted () { this.$parent.text = '111111111'; // 能够改变父组件的值 console.log('comp2 mounted') }}new Vue({ name: 'Root', el: '#root', mounted () { console.log(this.$parent.$options.name) }, components: { Comp: componet2 }, data: { text: 23333 }, template: ` <div> <span>{{text}}</span> <comp></comp> </div> `})
Vue 组件高级属性
Vue 组件插槽
一般咱们会向一个组件中传入一些自定义的内容,这个时候就能够用到插槽。插槽内能够包含任何模板代码,包括HTML或者是一个组件。
// 定义一个带插槽的组件const component = { name: 'comp', template: ` <div> <slot></slot> </div> `}new CompVue({ el: '#root', components:{ Comp }, template: ` <div> <comp> <p>这里的内容显示在插槽内</p> </comp> </div> `}
具名插槽
官网连接: https://cn.vuejs.org/v2/guide/components-slots.html
<div class="container"> <header> <!-- 咱们但愿把页头放这里 --> <slot name="header"></slot> </header> <main> <!-- 咱们但愿把主要内容放这里 --> <slot name="main"></slot> </main> <footer> <!-- 咱们但愿把页脚放这里 --> <slot name="footer"></slot> </footer></div>
具名插槽的使用:
第一种:在一个父组件的 <template> 元素上使用 slot 特性
<base-layout> <template slot="header"> <h1>Here might be a page title</h1> </template> <template slot="main"> <p>A paragraph for the main content.</p> <p>And another one.</p> </template> <template slot="footer"> <p>Here's some contact info</p> </template></base-layout>
第二种:直接在普通元素上使用
<base-layout> <h1 slot="header">Here might be a page title</h1> <div slot="main"> <p>A paragraph for the main content.</p> <p>And another one.</p> </div> <p slot="footer">Here's some contact info</p></base-layout>
插槽的默认内容
在插槽中能够设置一个默认内容,若是用户没有设置新的内容,则会显示默认内容
<button> <slot>提交</slot></button>
做用域插槽
2.1.0+ 新增 在 2.5.0+, slot-scope 再也不限制在 <template> 元素上使用,而能够用在插槽内的任何元素或组件上。
const component = { name: 'comp', template: ` <div> <slot value="456" name="finget"></slot> </div> `}new CompVue({ el: '#root', components:{ Comp }, template: ` <div> <comp> <p slot-scope="props">{{props.value}} {{props.name}}</p> // 456 finget </comp> </div> `}
provide/inject 跨级组件交互
2.2.0 新增
这对选项须要一块儿使用,以容许一个祖先组件向其全部子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。
// 父级组件提供 'foo'var Provider = { provide: { foo: 'bar' }, // ...}// 子组件注入 'foo'var Child = { inject: ['foo'], created () { console.log(this.foo) // => "bar" } // ...}
若是是注入一个父级组件内部的值,provide须要做为一个函数,相似于data
const component = { name: 'comp', inject: ["value"] template: ` <div>子组件 {{value}}</div> `}new CompVue({ el: '#root', data() { return { value: '123' } } components:{ Comp }, provide() { // 这里若是只是一个对象的话是没法拿到this.value的 return { value: this.value } }, template: ` <div> <comp></comp> <input type="text" v-model="value"> </div> `}
若是要监听父级组件的属性值的变化,从而自动更新子组件的值,须要手动实现监听
const component = { name: 'comp', inject: ["data"] template: ` <div>子组件 {{data.value}}</div> `}...provide() { const data = {} // 这是vue双向绑定的基础 Object.defineProperty(data,"value",{ get: () => this.value, enumerable: true }) return { data }},...
Vue 的render
Vue模板的解析: https://finget.github.io/2018/05/31/mvvm-vue/
Vue-router
router构建选项
重定向:
{ path: '/', redirect: '/app'}
History 模式:
const router = new VueRouter({ mode: 'history', routes: [...]})
vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,因而当 URL 改变时,页面不会从新加载。
不过这种模式要玩好,还须要后台配置支持。由于咱们的应用是个单页客户端应用,若是后台没有正确的配置,当用户在浏览器直接访问 http://oursite.com/user/id 就会返回 404,这就很差看了。
给个警告页:
const router = new VueRouter({ mode: 'history', routes: [ { path: '*', component: NotFoundComponent } ]})
base
const router = new VueRouter({ mode: 'history', base: '/base/', routes: [ { path: '/hello', component: hello } ]})
当访问 localhost:8080/hello 会变成 localhost:8080/base/hello ,全部的路由路径都会加上 /base ,固然手动删除 /base 仍是能够打开页面
linkActiveClass 和 linkExactActiveClass
<router-link to="/app">app</router-link><router-link to="/login">login</router-link>
router-link 在页面中会渲染成 a 标签,点击以后会添加两个类名: router-link-exact-active 和 router-link-active
const router = new VueRouter({ linkActiveClass: 'active-link', linkExactActiveClass: 'exact-active-link'})
这至关因而从新命名了两个类名。
二者的不一样点:
<router-link to="/login">login</router-link><router-link to="/login/exact">login exact</router-link>
上面这两个路由有一部分 /login 是相同的,在点击了 login exact 路由调转到 /login/exact 后:
/login 上还保留了 router-link-active 类名
scrollBehavior
使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像从新加载页面那样。
注意: 这个功能只在支持 history.pushState 的浏览器中可用。
const router = new VueRouter({ scrollBehavior(to, form, savedPosition){ if (savedPosition) { return savedPosition } else { return { x: 0, y: 0 } } }, routes: [...]})
scrollBehavior 方法接收 to 和 from 路由对象。第三个参数 savedPosition 当且仅当 popstate 导航 (经过浏览器的 前进/后退 按钮触发) 时才可用。
parseQuery 和 stringifyQuery
提供自定义查询字符串的解析/反解析函数。覆盖默认行为。
const router = new VueRouter({ parseQuery (query) { console.log(query) }, stringifyQuery (obj) { console.log(obj) }})
fallback
当浏览器不支持 history.pushState 控制路由是否应该回退到 hash 模式。默认值为 true。
在 IE9 中,设置为 false 会使得每一个 router-link 导航都触发整页刷新。它可用于工做在 IE9 下的服务端渲染应用,由于一个 hash 模式的 URL 并不支持服务端渲染。
const router = new VueRouter({ fallback: true})
路由元信息
官网例子:
const router = new VueRouter({ routes: [ { path: '/foo', component: Foo, children: [ { path: 'bar', component: Bar, // a meta field meta: { requiresAuth: true } } ] } ]})
那么如何访问这个 meta 字段呢?
首先,咱们称呼 routes 配置中的每一个路由对象为 路由记录。路由记录能够是嵌套的,所以,当一个路由匹配成功后,他可能匹配多个路由记录
例如,根据上面的路由配置, /foo/bar 这个 URL 将会匹配父路由记录以及子路由记录。
一个路由匹配到的全部路由记录会暴露为 $route 对象 (还有在导航守卫中的路由对象) 的 $route.matched 数组。所以,咱们须要遍历 $route.matched 来检查路由记录中的 meta 字段。
下面例子展现在全局导航守卫中检查元字段:
router.beforeEach((to, from, next) => { if (to.matched.some(record => record.meta.requiresAuth)) { // this route requires auth, check if logged in // if not, redirect to login page. if (!auth.loggedIn()) { next({ path: '/login', query: { redirect: to.fullPath } }) } else { next() } } else { next() // 确保必定要调用 next() }})
命名视图
在一个路由下展现多个视图组件,用的并很少
// 在这个页面中要分别展现三个视图<router-view></router-view> // 默认的<router-view name="a"></router-view> // 视图a<router-view name="b"></router-view> // 视图bconst router = new VueRouter({ routes: [ { path: '/', components: { // 加s default: Foo, // 对应默认router-view a: Bar, // name = "a" b: Baz // name = "b" } } ]})
导航守卫
路由改变时,按顺序触发的钩子函数
全局守卫
const router = new VueRouter({ ... })router.beforeEach((to, from, next) => { console.log('before each invoked'); next();})router.beforeResolve((to, from, next) => { console.log('before resolve invoked'); next();})
每一个守卫方法接收三个参数:
确保要调用 next 方法,不然钩子就不会被 resolved 。
路由对象
一个路由对象 (route object) 表示当前激活的路由的状态信息,包含了当前 URL 解析获得的信息,还有 URL 匹配到的路由记录 (route records)。
路由对象是不可变 (immutable) 的,每次成功的导航后都会产生一个新的对象。
路由对象属性:
const router = new VueRouter({ routes: [ // 下面的对象就是路由记录 { path: '/foo', component: Foo, children: [ // 这也是个路由记录 { path: 'bar', component: Bar } ] } ]})
当 URL 为 /foo/bar, $route.matched 将会是一个包含从上到下的全部对象 (副本)。
全局后置钩子
router.afterEach((to, from) => { console.log('after each invoked');})
路由独享的守卫
const router = new VueRouter({ routes: [ { path: '/foo', component: Foo, beforeEnter: (to, from, next) => { // ... } } ]})
组件内的守卫
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` }}
beforeRouteEnter 守卫 不能 访问 this,由于守卫在导航确认前被调用,所以即将登场的新组件还没被建立。
不过,你能够经过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,而且把组件实例做为回调方法的参数。
beforeRouteEnter (to, from, next) { next(vm => { // 经过 `vm` 访问组件实例 })}
完整的导航解析流程
异步路由
在路由文件中,直接import全部组件势必形成页面首次渲染时间变长,异步路由,当进入对应的路由才加载对应的页面。
const router = new VueRouter({ routes: [ { path: '/foo', component: () => import('../view/...'), } ]})
这种写法须要安装 syntax-dynamic-import ,并在 .babelrc 进行配置
// .babelrc{ "plugins": ["syntax-dynamic-import"]}
Vux
如下内容来自 官网:https://vuex.vuejs.org/zh/
简单使用vuex
// store.jsimport Vuex from 'vuex'import Vue from 'vue'Vue.use(Vuex)const store = new Vuex.Store({ state: { count: 0 }, mutations: { updateCount(state, num) { state.count = num } }})export default store// main.jsimport Vue from 'vue'import App from './App'import store from './store/store.js'Vue.config.productionTip = false/* eslint-disable no-new */new Vue({ el: '#app', store, // 挂载 components: { App }, template: '<App/>'})// 任意组件mounted(){ console.log(this.$store) let i = 1 setInterval(() => { this.$store.commit('updateCount', i++) })},computed: { count() { return this.$store.state.count }}
核心概念
State
Vuex 使用单一状态树——是的,用一个对象就包含了所有的应用层级状态。至此它便做为一个“惟一数据源 (SSOT)”而存在。这也意味着,每一个应用将仅仅包含一个 store 实例。单一状态树让咱们可以直接地定位任一特定的状态片断,在调试的过程当中也能轻易地取得整个当前应用状态的快照。
大白话: state就至关因而个全局对象,经过 Vue.use(Vuex) 全局注册了vuex以后,在任意组件中能够用 this.$store.state 拿到该对象
Vuex的状态存储是响应式的,从store实例中读取状态最简单的方法就是在计算属性中返回某个状态。
computed: { count() { return this.$store.state.count }}
当 state 中的 count 变化时,自动会更新 computed ,从而改变相关 DOM
mapState 辅助函数
当一个组件须要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,咱们可使用 mapState 辅助函数帮助咱们生成计算属性,让你少按几回键:
// 在单独构建的版本中辅助函数为 Vuex.mapStateimport { mapState } from 'vuex'export default { // ... computed: mapState({ // 箭头函数可以使代码更简练 count: state => state.count, // 传字符串参数 'count' 等同于 `state => state.count` countAlias: 'count', // 为了可以使用 `this` 获取局部状态,必须使用常规函数 不能用箭头函数 countPlusLocalState (state) { return state.count + this.localCount } })}
当映射的计算属性的名称与 state 的子节点名称相同时,咱们也能够给 mapState 传一个字符串数组。
computed: mapState([ // 映射 this.count 为 store.state.count 'count'])// 经常使用操做computed: { ...mapState(['count'])}// 换一个变量名computed: { ...mapState({ count1 : 'count', count2 : state => state.count })}
Getter
Getter就是vuex种state的computed,经过state派生出新的state,并且它会被缓存起来,只有依赖的state发生变化才会从新计算
export default { fullName(state) { // 默认接收state做为第一个参数 return `${state.firstName}${state.lastName}` }}
mapGetters 辅助函数
getter的使用和state相似,能够把它当作state来用。
import { mapGetters } from 'vuex'export default { // ... computed: { // 使用对象展开运算符将 getter 混入 computed 对象中 ...mapGetters([ 'doneTodosCount', 'anotherGetter', // ... ]) }}
若是想给getter换个名字,方法和state同样,不重复
Mutation
Mutation必须是同步的
更改 Vuex 的 store 中的状态的惟一方法是提交 mutation。Vuex 中的 mutation 很是相似于事件:每一个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是咱们实际进行状态更改的地方,而且它会接受 state 做为第一个参数:
const store = new Vuex.Store({ state: { count: 1 }, mutations: { increment (state) { // 变动状态 state.count++ } }})
你不能直接调用一个 mutation handler。这个选项更像是事件注册:“当触发一个类型为 increment 的 mutation 时,调用此函数。”要唤醒一个 mutation handler,你须要以相应的 type 调用 store.commit 方法:
store.commit('increment')
提交载荷(传参)
你能够向 store.commit 传入额外的参数,即 mutation 的 载荷(payload):
// ...mutations: { increment (state, n) { state.count += n }}store.commit('increment', 10)
在大多数状况下,载荷应该是一个 对象 ,这样能够包含多个字段而且记录的 mutation 会更易读:
// ...mutations: { increment (state, payload) { state.count += payload.amount }}store.commit('increment', { amount: 10})
对象风格的提交方式
提交 mutation 的另外一种方式是直接使用包含 type 属性的对象:
store.commit({ type: 'increment', amount: 10})
当使用对象风格的提交方式,整个对象都做为载荷传给 mutation 函数,所以 handler 保持不变:
mutations: { increment (state, payload) { state.count += payload.amount }}
使用常量替代 Mutation 事件类型
使用常量替代 mutation 事件类型在各类 Flux 实现中是很常见的模式。这样可使 linter之类的工具发挥做用,同时把这些常量放在单独的文件中可让你的代码合做者对整个 app 包含的 mutation 一目了然:
// mutation-types.jsexport const SOME_MUTATION = 'SOME_MUTATION'// store.jsimport Vuex from 'vuex'import { SOME_MUTATION } from './mutation-types'const store = new Vuex.Store({ state: { ... }, mutations: { // 咱们可使用 ES2015 风格的计算属性命名功能来使用一个常量做为函数名 [SOME_MUTATION] (state) { // mutate state } }})
在组件中提交 Mutation
你能够在组件中使用 this.$store.commit('xxx') 提交 mutation ,或者使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用(须要在根节点注入 store)。
import { mapMutations } from 'vuex'export default { // ... methods: { ...mapMutations([ 'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')` // `mapMutations` 也支持载荷: 'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)` ]), ...mapMutations({ add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')` }) }}
Action
Action 能够包含异步操做
Action跟Mutation相似,Action是调用 commit 方法,提交 mutation 的。
const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } }, actions: { increment (context) { context.commit('increment') } }})
Action 函数接受一个与 store 实例具备相同方法和属性的 context 对象,所以你能够调用 context.commit 提交一个 mutation ,或者经过 context.state 和 context.getters 来获取 state 和 getters 。
实践中,咱们会常常用到 ES2015 的 参数解构 来简化代码(特别是咱们须要调用 commit 不少次的时候):
actions: {// {commit} = context 解构出来 increment ({ commit }) { commit('increment') }}
实际代码:
在组件中分发 Action
你在组件中使用 this.$store.dispatch('xxx') 分发 action ,或者使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用(须要先在根节点注入 store):
import { mapActions } from 'vuex'export default { // ... methods: { ...mapActions([ 'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')` // `mapActions` 也支持载荷: 'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)` ]), ...mapActions({ add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')` }) }}
严格模式
开启严格模式,仅需在建立 store 的时候传入 strict: true:
const store = new Vuex.Store({ // ... strict: true})
在严格模式下,不管什么时候发生了状态变动且不是由 mutation 函数引发的,将会抛出错误。这能保证全部的状态变动都能被调试工具跟踪到。
开发环境与发布环境
不要在发布环境下启用严格模式!严格模式会深度监测状态树来检测不合规的状态变动——请确保在发布环境下关闭严格模式,以免性能损失。
相似于插件,咱们可让构建工具来处理这种状况:
const store = new Vuex.Store({ // ... strict: process.env.NODE_ENV !== 'production'})
原文连接:https://finget.github.io/2018/06/28/vue-family/?utm_source=tuicool&utm_medium=referral 若有侵权请联系删除,谢谢