Vue.js状态管理及SSR解析如何实现高效长尾词优化?
- 内容介绍
- 文章标签
- 相关推荐
本文共计4860个文字,预计阅读时间需要20分钟。
目录+前端状态管理出现的意义及解决的问题+Vuex+源码解读+Vuex+公共方法+Vuex+介绍及深入使用+Vuex+使用(官网)+1. 基本框架+2. 基本使用+3. State+3.1 mapState+辅助函数+4. Getter+4.1 通过属性
目录
- 前端状态管理出现的意义及解决的问题
- Vuex 源码解读
- Vuex 公共方法
- Vuex 介绍及深入使用
- Vuex 使用(官网)
- 1、基本框架
- 2、基本使用
- 3、State
- 3.1 mapState 辅助函数
- 4、Getter
- 4.1 通过属性访问
- 4.2 通过方法访问
- 4.3 mapGetters 辅助函数
- 5、Mutation
- 5.1 提交载荷(Payload)
- 5.2 使用常量替代 Mutation 事件类型
- 5.3 Mutation 必须是同步函数
- 5.4 在组件中提交 Mutation
- 6、Action
- 7、分发 Action
- 7.1 在组件中分发 Action
- 7.2 组合 Action
- 8、严格模式
- 8.1 开发环境与发布环境
- vue.js 服务端渲染介绍
- 1、客户端渲染和服务端渲染
- 客户端渲染
- 2、服务端渲染
- 客户端路由
- 3、服务端渲染实例
- 3、同构 - 客户端渲染和服务端渲染
前端状态管理出现的意义及解决的问题
随着前端应用的逐步复杂,我们的组件中需要使用越来越多的状态。有的时候我们需要使用子组件将状态传递给父组件就会比较复杂,数据的向上传递过程我们可能会使用回调函数或是数据绑定的形式去处理,就会让代码晦涩难懂。
我们需要一种方式,能够让数据在所有组件中共享,同时能以简单的方式进行传递,这种组织数据的方式就是状态管理。我们很自然的就想到,把数据放到所有需要使用的组件的公共祖先上,在使用时自上而下传递即可。
在vue.js中,我们主要说的状态管理库就是vuex,当然,只要你能实现有条理的组织数据,那么它都可以认为是一种状态管理库。
事实上,我们可以简单的这样理解这个词,vuex实际上做的事情就是:
- 在顶层实现一个数据管理的仓库
store,将所有组件间需要共享的数据放置于此; - 同时组件也可以对这个
store内的数据进行更新,同时更新完之后响应式更新所有使用此数据组件的视图;
Vuex 源码解读
Vuex 公共方法
- 路径:
src\util.js
export function find(list, f) { return list.filter(f)[0]; } export function deepCopy(obj, cache = []) { if (obj === null || typeof obj !== 'object') { return obj; } const hit = find(cache, c => c.original === obj); if (hit) { return hit.copy; } const copy = Array.isArray(obj) ? [] : {}; cache.push({ original: obj, copy, }); Object.keys(obj).forEach(key => { copy[key] = deepCopy(obj[key], cache); }); return copy; } export function forEachValue(obj, fn) { Object.keys(obj).forEach(key => fn(obj[key], key)); } export function isObject(obj) { return obj !== null && typeof obj === 'object'; } export function isPromise(val) { return val && typeof val.then === 'function'; } export function assert(condition, msg) { if (!condition) throw new Error(`[vuex] ${msg}`); } export function partial(fn, arg) { return function () { return fn(arg); }; }
Vuex 介绍及深入使用
在说vuex之前,我们必须说一说flux架构,flux架构可谓是状态管理的鼻祖。
flux架构最早由facebook推出,主要是为了处理当时他们的react框架下状态管理的问题,但在当时来讲,整个设计比较复杂,后来人们简化了其中的一些理念,但是保留了核心思想,继而依据框架实现了很多不同的状态管理库,例如redux,vuex等等。其中redux大多数被用在了react项目中,而vuex就是在vue框架中实现的这么一个flux架构的状态管理库。
**flux架构约定,存放数据的地方称为store,store内部的state是数据本身,我们必须通过action才能修改store里的state。**这里的action指的意思是行为,在大部分实现里面是一个函数,通过调用函数来更改store内部的state。
vuex中,我们可以通过mutation来的改变state,这时候就可以在组件中通过commit进行调用更改state。
同样的,我们也可以通过action来更改state,不过在action中,我们还是需要调用mutation。
Vuex 使用(官网)
网址链接:vuex.vuejs.org/zh/guide/st…
1、基本框架
import Vue from 'vue'; import Vuex from 'vuex'; Vue.use(Vuex); export default new Vuex.Store({ state: {}, mutations: {}, actions: {}, modules: {}, });
2、基本使用
./store/index.js
import Vue from 'vue'; import Vuex from 'vuex'; Vue.use(Vuex); export default new Vuex.Store({ state: { count: 0, }, mutations: { increment(state, payload = 1) { state.count += payload; }, }, actions: {}, modules: {}, });
./Home.vue
<template> <div class="home"> <div>count: {{ count }}</div> <button @click="increment">增加</button> <button @class="decrement">减少</button> </div> </template> <script> export default { name: 'Home', computed: { count() { return this.$store.state.count; }, }, methods: { increment() { // 使用 commit 派发 mutation 事件 // this.$store.commit("increment"); // 可以传递参数 this.$store.commit('increment', 2); }, decrement() {}, }, }; </script>
3、State
可以使用计算属性获取state中的数据:
computed: { count () { return this.$store.state.count } }
3.1 mapState 辅助函数
当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用mapState辅助函数帮助我们生成计算属性。
import { mapState } from "vuex"; computed: { ...mapState(["num"]) }
4、Getter
Vuex允许我们在store中定义getter(可以认为是store的计算属性)。就像计算属性一样,getter的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
Getter接受state作为其第一个参数:
const store = new Vuex.Store({ state: { todos: [ { id: 1, text: 'foo', done: true }, { id: 2, text: 'bar', done: false }, ], }, getters: { doneTodos: state => { return state.todos.filter(todo => todo.done); }, }, });
4.1 通过属性访问
Getter会暴露为store.getters对象,你可以以属性的形式访问这些值:
store.getters.doneTodos; // -> [{ id: 1, text: '...', done: true }]
Getter也可以接受其他getter作为第二个参数:
getters: { // ... doneTodosCount: (state, getters) => { return getters.doneTodos.length; }; }
4.2 通过方法访问
你也可以通过让getter返回一个函数,来实现给getter传参。
getters: { // ... getTodoById: state => id => { return state.todos.find(todo => todo.id === id); }; }
store.getters.getTodoById(2); // -> { id: 2, text: '...', done: false }
:getter在通过方法访问时,每次都会去进行调用,而不会缓存结果。
4.3 mapGetters 辅助函数
mapGetters辅助函数仅仅是将store中的getter映射到局部计算属性:
import { mapGetters } from 'vuex'; export default { computed: { // 使用对象展开运算符将 getter 混入 computed 对象中 ...mapGetters(['doneTodosCount', 'anotherGetter']), }, };
如果你想将一个getter属性另取一个名字,使用对象形式:
...mapGetters({ // 把 this.doneCount 映射为 this.$store.getters.doneTodosCount doneCount: 'doneTodosCount' })
5、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');
5.1 提交载荷(Payload)
你可以向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 } }
5.2 使用常量替代 Mutation 事件类型
使用常量替代mutation事件类型在各种Flux实现中是很常见的模式。这样可以使linter之类的工具发挥作用,同时把这些常量放在单独的文件中可以让你的代码合作者对整个app包含的mutation一目了然:
// mutation-types.js export const SOME_MUTATION = 'SOME_MUTATION';
// store.js import Vuex from 'vuex' import { SOME_MUTATION } from './mutation-types' const store = new Vuex.Store({ state: { ... }, mutations: { // 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名 [SOME_MUTATION] (state) { // mutate state } } })
5.3 Mutation 必须是同步函数
一条重要的原则就是要记住mutation必须是同步函数。为什么?请参考下面的例子:
mutations: { someMutation (state) { api.callAsyncMethod(() => { state.count++ }) } }
现在想象,我们正在debug一个app并且观察devtool中的mutation日志。每一条mutation被记录,devtools都需要捕捉到前一状态和后一状态的快照。然而,在上面的例子中mutation中的异步函数中的回调让这不可能完成:因为当mutation触发的时候,回调函数还没有被调用,devtools不知道什么时候回调函数实际上被调用 —— 实质上任何在回调函数中进行的状态的改变都是不可追踪的。
5.4 在组件中提交 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')` }), }, };
6、Action
Action类似于mutation,不同在于:
Action提交的是mutation,而不是直接变更状态。Action可以包含任意异步操作。
让我们来注册一个简单的action:
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。当我们在之后介绍到Modules时,你就知道context对象为什么不是store实例本身了。
实践中,我们会经常用到ES2015的参数解构来简化代码(特别是我们需要调用commit很多次的时候):
actions: { increment ({ commit, state, getters }) { commit('increment') } }
7、分发 Action
Action通过store.dispatch方法触发:
store.dispatch('increment');
乍一眼看上去感觉多此一举,我们直接分发mutation岂不更方便?实际上并非如此,还记得mutation必须同步执行这个限制么?Action就不受约束!我们可以在action内部执行异步操作:
actions: { incrementAsync ({ commit }) { setTimeout(() => { commit('increment') }, 1000) } }
Actions支持同样的载荷方式和对象方式进行分发:
// 以载荷形式分发 store.dispatch('incrementAsync', { amount: 10, }); // 以对象形式分发 store.dispatch({ type: 'incrementAsync', amount: 10, });
7.1 在组件中分发 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')` }), }, };
7.2 组合 Action
Action通常是异步的,那么如何知道action什么时候结束呢?更重要的是,我们如何才能组合多个action,以处理更加复杂的异步流程?
首先,你需要明白store.dispatch可以处理被触发的action的处理函数返回的Promise,并且store.dispatch仍旧返回Promise:
actions: { actionA ({ commit }) { return new Promise((resolve, reject) => { setTimeout(() => { commit('someMutation') resolve() }, 1000) }) } }
现在你可以:
store.dispatch('actionA').then(() => { // ... });
在另外一个action中也可以:
actions: { // ... actionB ({ dispatch, commit }) { return dispatch('actionA').then(() => { commit('someOtherMutation') }) } }
最后,如果我们利用async / await,我们可以如下组合action:
// 假设 getData() 和 getOtherData() 返回的是 Promise actions: { async actionA ({ commit }) { commit('gotData', await getData()) }, async actionB ({ dispatch, commit }) { await dispatch('actionA') // 等待 actionA 完成 commit('gotOtherData', await getOtherData()) } }
一个
store.dispatch在不同模块中可以触发多个action函数。在这种情况下,只有当所有触发函数完成后,返回的Promise才会执行。
8、严格模式
开启严格模式,仅需在创建store的时候传入strict: true:
const store = new Vuex.Store({ // ... strict: true, });
在严格模式下,无论何时发生了状态变更且不是由mutation函数引起的,将会抛出错误(但是数据还是会改变)。这能保证所有的状态变更都能被调试工具跟踪到。
8.1 开发环境与发布环境
不要在发布环境下启用严格模式!严格模式会深度监测状态树来检测不合规的状态变更 —— 请确保在发布环境下关闭严格模式,以避免性能损失。
类似于插件,我们可以让构建工具来处理这种情况:
const store = new Vuex.Store({ // ... strict: process.env.NODE_ENV !== 'production', });
vue.js 服务端渲染介绍
大多数我们使用的UI框架如vue和react,都是在客户端进行渲染,也就是说每个用户在加载进来我们所有的html文件和js文件之后,才开始渲染页面的内容。
但是这样做会有两个问题,一个是如果用户网络速度比较慢,如果我们渲染的内容比较多的话,就会产生一个延迟,造成不好的用户体验。另一个是某些爬虫,例如百度的搜索收录的爬虫,在爬取你的页面时,获取不到你的页面的真实内容,导致站点SEO权重变低。
所以很多需要SEO的页面,都需要在服务端提前渲染好html的内容,在用户访问时先返回给用户内容,这杨对用户和爬虫都非常友好。
我们可以通过直接在页面上右击查看网页源代码,来查看一个页面是否有服务端渲染。
1、客户端渲染和服务端渲染
客户端渲染
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>客户端渲染</title> </head> <body> <script> document.body.innerHTML = '<div>你好</div>'; </script> </body> </html>
在Network的中Preview中无数据,在Response中的没有 DOM 标签:
查看网页源代码:
2、服务端渲染
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta localhost:8080/;
到此这篇关于Vue.js 状态管理及 SSR解析的文章就介绍到这了,更多相关Vue.js SSR内容请搜索易盾网络以前的文章或继续浏览下面的相关文章希望大家以后多多支持易盾网络!
本文共计4860个文字,预计阅读时间需要20分钟。
目录+前端状态管理出现的意义及解决的问题+Vuex+源码解读+Vuex+公共方法+Vuex+介绍及深入使用+Vuex+使用(官网)+1. 基本框架+2. 基本使用+3. State+3.1 mapState+辅助函数+4. Getter+4.1 通过属性
目录
- 前端状态管理出现的意义及解决的问题
- Vuex 源码解读
- Vuex 公共方法
- Vuex 介绍及深入使用
- Vuex 使用(官网)
- 1、基本框架
- 2、基本使用
- 3、State
- 3.1 mapState 辅助函数
- 4、Getter
- 4.1 通过属性访问
- 4.2 通过方法访问
- 4.3 mapGetters 辅助函数
- 5、Mutation
- 5.1 提交载荷(Payload)
- 5.2 使用常量替代 Mutation 事件类型
- 5.3 Mutation 必须是同步函数
- 5.4 在组件中提交 Mutation
- 6、Action
- 7、分发 Action
- 7.1 在组件中分发 Action
- 7.2 组合 Action
- 8、严格模式
- 8.1 开发环境与发布环境
- vue.js 服务端渲染介绍
- 1、客户端渲染和服务端渲染
- 客户端渲染
- 2、服务端渲染
- 客户端路由
- 3、服务端渲染实例
- 3、同构 - 客户端渲染和服务端渲染
前端状态管理出现的意义及解决的问题
随着前端应用的逐步复杂,我们的组件中需要使用越来越多的状态。有的时候我们需要使用子组件将状态传递给父组件就会比较复杂,数据的向上传递过程我们可能会使用回调函数或是数据绑定的形式去处理,就会让代码晦涩难懂。
我们需要一种方式,能够让数据在所有组件中共享,同时能以简单的方式进行传递,这种组织数据的方式就是状态管理。我们很自然的就想到,把数据放到所有需要使用的组件的公共祖先上,在使用时自上而下传递即可。
在vue.js中,我们主要说的状态管理库就是vuex,当然,只要你能实现有条理的组织数据,那么它都可以认为是一种状态管理库。
事实上,我们可以简单的这样理解这个词,vuex实际上做的事情就是:
- 在顶层实现一个数据管理的仓库
store,将所有组件间需要共享的数据放置于此; - 同时组件也可以对这个
store内的数据进行更新,同时更新完之后响应式更新所有使用此数据组件的视图;
Vuex 源码解读
Vuex 公共方法
- 路径:
src\util.js
export function find(list, f) { return list.filter(f)[0]; } export function deepCopy(obj, cache = []) { if (obj === null || typeof obj !== 'object') { return obj; } const hit = find(cache, c => c.original === obj); if (hit) { return hit.copy; } const copy = Array.isArray(obj) ? [] : {}; cache.push({ original: obj, copy, }); Object.keys(obj).forEach(key => { copy[key] = deepCopy(obj[key], cache); }); return copy; } export function forEachValue(obj, fn) { Object.keys(obj).forEach(key => fn(obj[key], key)); } export function isObject(obj) { return obj !== null && typeof obj === 'object'; } export function isPromise(val) { return val && typeof val.then === 'function'; } export function assert(condition, msg) { if (!condition) throw new Error(`[vuex] ${msg}`); } export function partial(fn, arg) { return function () { return fn(arg); }; }
Vuex 介绍及深入使用
在说vuex之前,我们必须说一说flux架构,flux架构可谓是状态管理的鼻祖。
flux架构最早由facebook推出,主要是为了处理当时他们的react框架下状态管理的问题,但在当时来讲,整个设计比较复杂,后来人们简化了其中的一些理念,但是保留了核心思想,继而依据框架实现了很多不同的状态管理库,例如redux,vuex等等。其中redux大多数被用在了react项目中,而vuex就是在vue框架中实现的这么一个flux架构的状态管理库。
**flux架构约定,存放数据的地方称为store,store内部的state是数据本身,我们必须通过action才能修改store里的state。**这里的action指的意思是行为,在大部分实现里面是一个函数,通过调用函数来更改store内部的state。
vuex中,我们可以通过mutation来的改变state,这时候就可以在组件中通过commit进行调用更改state。
同样的,我们也可以通过action来更改state,不过在action中,我们还是需要调用mutation。
Vuex 使用(官网)
网址链接:vuex.vuejs.org/zh/guide/st…
1、基本框架
import Vue from 'vue'; import Vuex from 'vuex'; Vue.use(Vuex); export default new Vuex.Store({ state: {}, mutations: {}, actions: {}, modules: {}, });
2、基本使用
./store/index.js
import Vue from 'vue'; import Vuex from 'vuex'; Vue.use(Vuex); export default new Vuex.Store({ state: { count: 0, }, mutations: { increment(state, payload = 1) { state.count += payload; }, }, actions: {}, modules: {}, });
./Home.vue
<template> <div class="home"> <div>count: {{ count }}</div> <button @click="increment">增加</button> <button @class="decrement">减少</button> </div> </template> <script> export default { name: 'Home', computed: { count() { return this.$store.state.count; }, }, methods: { increment() { // 使用 commit 派发 mutation 事件 // this.$store.commit("increment"); // 可以传递参数 this.$store.commit('increment', 2); }, decrement() {}, }, }; </script>
3、State
可以使用计算属性获取state中的数据:
computed: { count () { return this.$store.state.count } }
3.1 mapState 辅助函数
当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用mapState辅助函数帮助我们生成计算属性。
import { mapState } from "vuex"; computed: { ...mapState(["num"]) }
4、Getter
Vuex允许我们在store中定义getter(可以认为是store的计算属性)。就像计算属性一样,getter的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
Getter接受state作为其第一个参数:
const store = new Vuex.Store({ state: { todos: [ { id: 1, text: 'foo', done: true }, { id: 2, text: 'bar', done: false }, ], }, getters: { doneTodos: state => { return state.todos.filter(todo => todo.done); }, }, });
4.1 通过属性访问
Getter会暴露为store.getters对象,你可以以属性的形式访问这些值:
store.getters.doneTodos; // -> [{ id: 1, text: '...', done: true }]
Getter也可以接受其他getter作为第二个参数:
getters: { // ... doneTodosCount: (state, getters) => { return getters.doneTodos.length; }; }
4.2 通过方法访问
你也可以通过让getter返回一个函数,来实现给getter传参。
getters: { // ... getTodoById: state => id => { return state.todos.find(todo => todo.id === id); }; }
store.getters.getTodoById(2); // -> { id: 2, text: '...', done: false }
:getter在通过方法访问时,每次都会去进行调用,而不会缓存结果。
4.3 mapGetters 辅助函数
mapGetters辅助函数仅仅是将store中的getter映射到局部计算属性:
import { mapGetters } from 'vuex'; export default { computed: { // 使用对象展开运算符将 getter 混入 computed 对象中 ...mapGetters(['doneTodosCount', 'anotherGetter']), }, };
如果你想将一个getter属性另取一个名字,使用对象形式:
...mapGetters({ // 把 this.doneCount 映射为 this.$store.getters.doneTodosCount doneCount: 'doneTodosCount' })
5、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');
5.1 提交载荷(Payload)
你可以向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 } }
5.2 使用常量替代 Mutation 事件类型
使用常量替代mutation事件类型在各种Flux实现中是很常见的模式。这样可以使linter之类的工具发挥作用,同时把这些常量放在单独的文件中可以让你的代码合作者对整个app包含的mutation一目了然:
// mutation-types.js export const SOME_MUTATION = 'SOME_MUTATION';
// store.js import Vuex from 'vuex' import { SOME_MUTATION } from './mutation-types' const store = new Vuex.Store({ state: { ... }, mutations: { // 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名 [SOME_MUTATION] (state) { // mutate state } } })
5.3 Mutation 必须是同步函数
一条重要的原则就是要记住mutation必须是同步函数。为什么?请参考下面的例子:
mutations: { someMutation (state) { api.callAsyncMethod(() => { state.count++ }) } }
现在想象,我们正在debug一个app并且观察devtool中的mutation日志。每一条mutation被记录,devtools都需要捕捉到前一状态和后一状态的快照。然而,在上面的例子中mutation中的异步函数中的回调让这不可能完成:因为当mutation触发的时候,回调函数还没有被调用,devtools不知道什么时候回调函数实际上被调用 —— 实质上任何在回调函数中进行的状态的改变都是不可追踪的。
5.4 在组件中提交 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')` }), }, };
6、Action
Action类似于mutation,不同在于:
Action提交的是mutation,而不是直接变更状态。Action可以包含任意异步操作。
让我们来注册一个简单的action:
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。当我们在之后介绍到Modules时,你就知道context对象为什么不是store实例本身了。
实践中,我们会经常用到ES2015的参数解构来简化代码(特别是我们需要调用commit很多次的时候):
actions: { increment ({ commit, state, getters }) { commit('increment') } }
7、分发 Action
Action通过store.dispatch方法触发:
store.dispatch('increment');
乍一眼看上去感觉多此一举,我们直接分发mutation岂不更方便?实际上并非如此,还记得mutation必须同步执行这个限制么?Action就不受约束!我们可以在action内部执行异步操作:
actions: { incrementAsync ({ commit }) { setTimeout(() => { commit('increment') }, 1000) } }
Actions支持同样的载荷方式和对象方式进行分发:
// 以载荷形式分发 store.dispatch('incrementAsync', { amount: 10, }); // 以对象形式分发 store.dispatch({ type: 'incrementAsync', amount: 10, });
7.1 在组件中分发 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')` }), }, };
7.2 组合 Action
Action通常是异步的,那么如何知道action什么时候结束呢?更重要的是,我们如何才能组合多个action,以处理更加复杂的异步流程?
首先,你需要明白store.dispatch可以处理被触发的action的处理函数返回的Promise,并且store.dispatch仍旧返回Promise:
actions: { actionA ({ commit }) { return new Promise((resolve, reject) => { setTimeout(() => { commit('someMutation') resolve() }, 1000) }) } }
现在你可以:
store.dispatch('actionA').then(() => { // ... });
在另外一个action中也可以:
actions: { // ... actionB ({ dispatch, commit }) { return dispatch('actionA').then(() => { commit('someOtherMutation') }) } }
最后,如果我们利用async / await,我们可以如下组合action:
// 假设 getData() 和 getOtherData() 返回的是 Promise actions: { async actionA ({ commit }) { commit('gotData', await getData()) }, async actionB ({ dispatch, commit }) { await dispatch('actionA') // 等待 actionA 完成 commit('gotOtherData', await getOtherData()) } }
一个
store.dispatch在不同模块中可以触发多个action函数。在这种情况下,只有当所有触发函数完成后,返回的Promise才会执行。
8、严格模式
开启严格模式,仅需在创建store的时候传入strict: true:
const store = new Vuex.Store({ // ... strict: true, });
在严格模式下,无论何时发生了状态变更且不是由mutation函数引起的,将会抛出错误(但是数据还是会改变)。这能保证所有的状态变更都能被调试工具跟踪到。
8.1 开发环境与发布环境
不要在发布环境下启用严格模式!严格模式会深度监测状态树来检测不合规的状态变更 —— 请确保在发布环境下关闭严格模式,以避免性能损失。
类似于插件,我们可以让构建工具来处理这种情况:
const store = new Vuex.Store({ // ... strict: process.env.NODE_ENV !== 'production', });
vue.js 服务端渲染介绍
大多数我们使用的UI框架如vue和react,都是在客户端进行渲染,也就是说每个用户在加载进来我们所有的html文件和js文件之后,才开始渲染页面的内容。
但是这样做会有两个问题,一个是如果用户网络速度比较慢,如果我们渲染的内容比较多的话,就会产生一个延迟,造成不好的用户体验。另一个是某些爬虫,例如百度的搜索收录的爬虫,在爬取你的页面时,获取不到你的页面的真实内容,导致站点SEO权重变低。
所以很多需要SEO的页面,都需要在服务端提前渲染好html的内容,在用户访问时先返回给用户内容,这杨对用户和爬虫都非常友好。
我们可以通过直接在页面上右击查看网页源代码,来查看一个页面是否有服务端渲染。
1、客户端渲染和服务端渲染
客户端渲染
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>客户端渲染</title> </head> <body> <script> document.body.innerHTML = '<div>你好</div>'; </script> </body> </html>
在Network的中Preview中无数据,在Response中的没有 DOM 标签:
查看网页源代码:
2、服务端渲染
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta localhost:8080/;
到此这篇关于Vue.js 状态管理及 SSR解析的文章就介绍到这了,更多相关Vue.js SSR内容请搜索易盾网络以前的文章或继续浏览下面的相关文章希望大家以后多多支持易盾网络!

