Vuex 数据流管理及Vue.js 服务端渲染(SSR)项目见:https://github.com/smallSix6/fed-e-task-liuhuijun/tree/master/fed-e-task-03-03

任务一:Vuex 状态管理

1、课程目标

  • Vue 组件间通信方式回顾
  • Vuex 核心概念和基本使用回顾
  • 购物车案例
  • 模拟实现 Vuex

2、组件内的状态管理流程

  • 状态管理:

    • state:驱动应用的数据源
    • view:以声明方式将 state 映射到视图
    • actions:响应在 view 上的用户输入导致的状态变化

3、组件间通信方式回顾

  • 四种通信方式:

    • 父组件给子组件传值

      • 子组件通过 props 接收数据
      • 父组件中给子组件通过相应属性传值
    • 子组件给父组件传值
      • 子组件通过 this.$emit(fn, data)
      • 父组件中给子组件通过 v-on:子组件emit的函数名=父组件函数名
    • 不相关组件传值
      • 通过事件中心 eventbus 触发和注册事件
      import Vue from 'vue'
      export default new Vue()
      
      • 触发 eventsbus 中的事件
      <template><div><h1>Event Bus Sibling01</h1><div class="number" @click="sub">-</div><input type="text" style="width: 30px; text-align: center" :value="value"><div class="number" @click="add">+</div></div>
      </template><script>
      import bus from './eventbus'export default {props: {num: Number},created () {this.value = this.num},data () {return {value: -1}},methods: {sub () {if (this.value > 1) {this.value--bus.$emit('numchange', this.value)}},add () {this.value++bus.$emit('numchange', this.value)}}
      }
      </script><style>
      .number {display: inline-block;cursor: pointer;width: 20px;text-align: center;
      }
      </style>
      
      • 注册事件
      <template><div><h1>Event Bus Sibling02</h1><div>{{ msg }}</div></div>
      </template><script>
      import bus from './eventbus'
      export default {data () {return {msg: ''}},created () {bus.$on('numchange', (value) => {this.msg = `您选择了${value}件商品`})}
      }
      </script><style></style>
      
    • 通过 ref 获取子组件
      • ref 两个作用

        • 在普通 HTML 标签上使用 ref,获取到的是 DOM
        <template><div><h1>ref Child</h1><input ref="input" type="text" v-model="value"></div>
        </template><script>
        export default {data () {return {value: ''}},methods: {focus () {this.$refs.input.focus()}}
        }
        </script>
        • 在组件标签上使用 ref,获取到的是组件实例
        <template><div><h1>ref Parent</h1><child ref="c"></child></div>
        </template><script>
        import child from './04-Child'
        export default {components: {child},mounted () {this.$refs.c.focus()this.$refs.c.value = 'hello input'}
        }
        </script>
        

4、Vuex 回顾

  • 什么是 Vuex:

    • Vuex 专门为 Vue.js 设计的状态管理库
    • Vuex 采用集中式的方式存储需要共享的状态
    • Vuex 的作用是进行状态管理,解决复杂组件通信,数据共享
    • Vuex 集成到了 devtools 中,提供了 time-travel 时光旅行历史回滚功能
  • 什么情况下使用 Vuex
    • 非必要的情况下不要使用 Vuex
    • 大型的单页应用程序
      • 多个视图依赖于同一状态
      • 来自不同视图的行为需要变更同一状态

5、Vuex 核心概念回顾

  • Store: 是一个容器,包含着应用中的大部分状态,不能直接改变 store 中的状态,要通过 mutation 的方式改变状态。
  • State:是状态,保存在 Store 中,因为 Store 是唯一的,所以 State 也是唯一的,也称为单一状态树。这里的状态是响应式的。
  • Getter:是 Vuex 中的计算属性,方便从一个属性派生出其他的值。它内部会对计算的属性进行缓存,只有当依赖改变的时候,才会重新进行计算。
  • Mutation:状态的变换必须要通过提交 Mutation 来完成。
  • Action:和 MuTation 类似,不同的是 Action 可以进行异步的操作,内部改变状态的时候,都需要提交 Mutation。
  • Module:当 Store 太过臃肿时,可以将 Store 分成多个模块,每个模块里有 State、Mutation、Action、Getter,甚至是子模块。

6、State

  • store/index.js 中定义 store
export default new Vuex.Store({state: {count: 0,msg: 'Hello Vuex'},mutations: {},actions: {},modules: {}
})
  • App.vue 文件中引入 store
<template><div id="app"><h1>Vuex - Demo</h1><!-- count:{{ count }} <br>msg: {{ msg }} --><!-- count:{{ $store.state.count }} <br>msg: {{ $store.state.msg }} -->count: {{ num }} <br>msg: {{ message }}</div>
</template>
<script>
import { mapState } from 'vuex'
export default {computed: {// count: state => state.count// ...mapState(['count', 'msg'])...mapState({ num: 'count', message: 'msg' })}
}
</script>

7、Getter

  • 用法:
export default new Vuex.Store({state: {count: 0,msg: 'Hello Vuex'},getters: {reverseMsg (state) {return state.msg.split('').reverse().join('')}},mutations: {},actions: {},modules: {}
})
<template><div id="app"><h1>Vuex - Demo</h1>reverseMsg: {{ reverseMsg }}</div>
</template>
<script>
import { mapState, mapGetters } from 'vuex'
export default {computed: {...mapGetters(['reverseMsg'])}
}
</script>

8、Mutation

<template><div id="app"><h1>Vuex - Demo</h1><!-- count:{{ count }} <br>msg: {{ msg }} --><!-- count:{{ $store.state.count }} <br>msg: {{ $store.state.msg }} -->count: {{ num }} <br>msg: {{ message }}<h2>Getter</h2>reverseMsg: {{ reverseMsg }}<h2>Mutation</h2><!-- <button @click="$store.commit('increate', 2)">Mutation</button> --><button @click="increate(3)">Mutation</button></div>
</template>
<script>
import { mapState, mapGetters, mapMutations } from 'vuex'
export default {computed: {// count: state => state.count// ...mapState(['count', 'msg'])...mapState({ num: 'count', message: 'msg' }),...mapGetters(['reverseMsg'])},methods: {...mapMutations(['increate'])}
}
</script>
export default new Vuex.Store({state: {count: 0,msg: 'Hello Vuex'},getters: {reverseMsg (state) {return state.msg.split('').reverse().join('')}},mutations: {increate (state, payload) {state.count += payload}},actions: {},modules: {}
})

9、Action

<template><div id="app"><h1>Vuex - Demo</h1>count: {{ num }} <br><!-- <div @click="$store.dispatch('increateAsync', 5)">Action</div> --><div @click="increateAsync(6)">Action</div></div>
</template>
<script>
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
export default {computed: {// count: state => state.count// ...mapState(['count', 'msg'])...mapState({ num: 'count', message: 'msg' }),...mapGetters(['reverseMsg'])},methods: {...mapMutations(['increate']),...mapActions(['increateAsync'])}
}
</script>
export default new Vuex.Store({state: {count: 0,msg: 'Hello Vuex'},getters: {reverseMsg (state) {return state.msg.split('').reverse().join('')}},mutations: {increate (state, payload) {state.count += payload}},actions: {increateAsync (context, payload) {setTimeout(() => {context.commit('increate', payload)}, 2000)}},modules: {}
})

10、Module

import Vue from 'vue'
import Vuex from 'vuex'
import products from './modules/products'
import cart from './modules/cart'Vue.use(Vuex)export default new Vuex.Store({state: {count: 0,msg: 'Hello Vuex'},getters: {reverseMsg (state) {return state.msg.split('').reverse().join('')}},mutations: {increate (state, payload) {state.count += payload}},actions: {increateAsync (context, payload) {setTimeout(() => {context.commit('increate', payload)}, 2000)}},modules: {products,cart}
})
  • 在 store 中添加 modules 属性,开启多个子模块,products 中的代码如下:
const state = {products: [{ id: 1, title: 'iPhone 11', price: 8000 },{ id: 2, title: 'iPhone 12', price: 10000 }]
}
const getters = {}
const mutations = {setProducts (state, payload) {state.products = payload}
}
const actions = {}export default {namespaced: true,  // 开启命名空间state,getters,mutations,actions
}
  • App.vue 中的代码如下:
<template><div id="app"><h1>Vuex - Demo</h1><!-- count:{{ count }} <br>msg: {{ msg }} --><!-- count:{{ $store.state.count }} <br>msg: {{ $store.state.msg }} -->count: {{ num }} <br>msg: {{ message }}<h2>Getter</h2>reverseMsg: {{ reverseMsg }}<h2>Mutation</h2><!-- <button @click="$store.commit('increate', 2)">Mutation</button> --><button @click="increate(3)">Mutation</button><!-- <div @click="$store.dispatch('increateAsync', 5)">Action</div> --><div @click="increateAsync(5)">Action</div><h2>Module</h2>products: {{ products }} <br><button @click="setProducts([])">Module</button></div>
</template>
<script>
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
export default {computed: {// count: state => state.count// ...mapState(['count', 'msg'])...mapState({ num: 'count', message: 'msg' }),...mapGetters(['reverseMsg']),...mapState('products', ['products'])},methods: {...mapMutations(['increate', 'setProducts']),...mapActions(['increateAsync']),...mapMutations('products', ['setProducts'])}
}
</script>

11、Vuex 严格模式

  • Vuex 中的状态的更新要通过提交 mutation 来修改,但其实在组件中还可以通过$store.state.msg进行修改,从语法从面来说这是没有问题的,但是这破坏了 Vuex 的约定,如果在组件中直接修改 state,devtools 无法跟踪到这次状态的修改。
  • 开启严格模式之后,如果在组件中直接修改 state 会抛出错误,但数据仍被成功修改。
  • 如何开启:在 store 中增加一个属性 strict 为 true
export default new Vuex.Store({strict: process.env.NODE_ENV !== 'production',
})
  • 注意:不要在生产模式下开启严格模式,严格模式会深度检查状态树,检查不合规的状态改变,会影响性能。
  • 我们可以在开发模式下开启严格模式,在生产模式中关闭严格模式:
  • strict: process.env.NODE_ENV !== 'production',

12、Vuex插件介绍

  • Vuex的插件就是一个函数
  • 这个函数接受一个store参数
  • 这个参数可以订阅一个函数,让这个函数在所有的mutation结束之后执行。
const myPlugin = store => {// 当store初始化后调用store.subscribe((mutation, state) => {// 每次mutation之后调用// mutation的格式为{ type, payload }})
}
  • Store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import products from './modules/products'
import cart from './modules/cart'
Vue.use(Vuex)const myPlugin = store => {store.subscribe((mutation, state) => {if (mutation.type.startsWith('cart/')) {window.localStorage.setItem('cart-products', JSON.stringify(state.cart.cartProducts))}})
}export default new Vuex.Store({state: {},mutations: {},actions: {},modules: {products,cart},plugins: [myPlugin]
})

13、模拟 Vuex 的实现

let _Vue = null
class Store {constructor (options) {const {state = {},getters = {},mutations = {},actions = {}} = optionsthis.state = _Vue.observable(state)this.getters = Object.create(null)Object.keys(getters).forEach(key => {Object.defineProperty(this.getters, key, {get: () => getters[key](state)})})this._mutations = mutationsthis._actions = actions}commit (type, payload) {this._mutations[type](this.state, payload)}dispatch (type, payload) {this._actions[type](this, payload)}
}function install (Vue) {_Vue = Vue_Vue.mixin({beforeCreate () {if (this.$options.store) {_Vue.prototype.$store = this.$options.store}}})
}export default {Store,install
}

任务二:服务端渲染基础

1、概述

  • SPA 单页应用

    • 优点:

      • 用户体验好
      • 开发效率高
      • 渲染性能好
      • 可维护性好
    • 缺点:
      • 首屏渲染时间长
      • 不利于 SEO
  • 借鉴传统的服务器渲染

  • 客户端激活为 SPA
    +

  • 同构应用

    • 通过服务端渲染首屏直出,解决SPA应用首屏渲染慢以及不利于SEO问题
    • 通过客户端渲染结果页面内容交互得到更好的用户体验
    • 这种方式通常称之为现代化的服务端渲染,也叫同构渲染
    • 这种方式构建的应用称之为服务端渲染应用或者是同构应用
  • 相关概念

    • 什么是渲染:把数据和模板拼接在一起。渲染的本质就是字符串的解析替换。
    • 传统的服务端渲染:将数据结合页面模板渲染为 HTML 返回给客户端
    • 客户端渲染
    • 现代化的服务端渲染(同构渲染)

2、传统的服务端渲染案例

  • 地址见:…/code/oldRender
  • index.js
const express = require('express')
const fs = require('fs')
const template = require('art-template')const app = express()app.get('/', (req, res) => {// 1. 获取页面模板const templateStr = fs.readFileSync('./index.html', 'utf-8')console.log(templateStr)// 2. 获取数据const data = JSON.parse(fs.readFileSync('./data.json', 'utf-8'))console.log(data)// 3. 渲染:数据 + 模板 = 最终结果const html = template.render(templateStr, data)console.log(html)// 4. 把渲染结果发送给客户端res.send(html)
})app.listen(3000, () => {console.log('running......')
})
  • index.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><p>{{message}}</p>
</body>
</html>
{"message": "人善被人欺,有事不要虚,所有精神小伙全部听令。整起来!天黑路滑,这社会复杂,你往高处爬,那么就有小人来找茬,精神来自灵魂,不是动手伤人,气质来自豪横,但豪横不是进牢门,懂滴撒!散会!(@阿溪												

Vuex 数据流管理及Vue.js 服务端渲染(SSR)相关推荐

  1. Vue.js 服务端渲染

    服务端渲染 SSR 完全指南 在 2.3 发布后我们发布了一份完整的构建 Vue 服务端渲染应用的指南.这份指南非常深入,适合已经熟悉 Vue, webpack 和 Node.js 开发的开发者阅读. ...

  2. Nuxt.js 服务端渲染从安装到部署

    Nuxt.js 服务端渲染方案 了解 Nuxt.js 的作用 掌握 Nuxt.js 中的路由 掌握 layouts.pages 以及 components 的区别 能够在 Nuxt.js 项目中使用第 ...

  3. Node项目部署到阿里云服务器(ECS),以Nuxt.js服务端渲染项目为例

    1.前言 最近打算业余时间搭个网站,选择的技术栈为node+mongodb+Nuxt.js(基于vue,用于创建服务端渲染 (SSR) 应用),以下不会教科书式讲解,只是提供整体思路.参考资料以及关键 ...

  4. vue服务端渲染(ssr)与普通vue的区别,ssr的基本使用

    ssr是vue的服务端渲染技术,nuxt是一个可以用来做ssr服务端渲染开发的框架. ssr是技术基础,nuxt是封装 一.什么是SSR? Vue.js 是构建客户端应用程序的框架.默认情况下,可以在 ...

  5. vue服务端渲染ssr

    vue服务端渲染ssr 一.SSR概念 传统web渲染技术 SPA SSR 二.webpack+vue2的实现方式 1.创建工程 2.安装依赖 3.编写一个简单的SSR 4.完整的ssr 5.代码链接 ...

  6. 服务端渲染(SSR)和Nuxt.js

    服务端渲染(SSR) 客户端渲染和传统服务端渲染的问题 SPA应用有两个非常明显的问题: 首屏渲染慢 不利于 SEO 传统的服务端渲染又存在: 应用的前后端部分完全耦合在一起,在前后端协同开发方面会有 ...

  7. 服务端渲染(SSR) 通用技术解决方案

    项目背景 服务端渲染(SSR) 通用技术解决方案的诞生来源于对 360搜索百科移动端项目的一次重构实践.而当时决定重构该项目的主要原因有以下几点: 1.  技术栈陈旧,熟悉.开发以及维护成本都较高 项 ...

  8. 服务端渲染SSR与客户端渲染

    文章目录 一.服务端渲染和客户端渲染的区别 服务端渲染(SSR -- server side render) 客户端渲染 二.使用服务端渲染(SSR)的利弊 优势 局限 三.实际应用 应用原则 举例 ...

  9. React + Koa 实现服务端渲染(SSR)

    ⚛️React是目前前端社区最流行的UI库之一,它的基于组件化的开发方式极大地提升了前端开发体验,React通过拆分一个大的应用至一个个小的组件,来使得我们的代码更加的可被重用,以及获得更好的可维护性 ...

最新文章

  1. Android XML: unbound prefix
  2. Linux系统vi操作指南
  3. python中的有序字典
  4. 服务器无响应 错误691,错误 691:由于域上的用户名和/密码无效而拒绝访问的原因及解决办法...
  5. 憋不住的心里的一个想法,JVM的BYTECODE是完全平台无关的么?
  6. c纳秒级计时器_使用C+提供以纳米秒为单位的时间的计时器功能
  7. os.walk获取同级目录具有随机性
  8. c语言编写贪吃蛇代码无错,刚学C语言,想写一个贪吃蛇的代码
  9. Linux基础:find命令总结
  10. python replace函数后面的数字的含义
  11. Vlan与VTP的介绍及工作原理
  12. mongodb 创建用户
  13. 学术壁报模板_电子壁报 | 中华医学会第十四次全国妇产科学学术会议
  14. 各厂商服务器存储默认管理口登录信息(默认IP、用户名、密码)收集
  15. 2019年上半年读完的一部分书籍记录及其推荐指数
  16. 石油化工行业智能供应链管理系统解决方案:数智化供应链平台推动企业转型智能化发展
  17. 对比俩个List之间的差异
  18. php jws 数据签名,JSON Web Signature 规范解析
  19. 威纶tk6070ik与台达变频器vdf-s485通讯程序 自己编写的威纶触摸屏与台达变频器的通讯
  20. String转JSON

热门文章

  1. 工科研究生论文常用词汇积累(持续更新)
  2. 西北工业大学 计算机学院 姜,姜保庆-计算机与信息工程学院官网
  3. 蜜罐技术的介绍和思路
  4. 2020最新阿里巴巴必问的200个面试题以及答案,助你斩获阿里offer
  5. mt6739方案开发,mt6739基带芯片模块,mt6739资料下载
  6. 锦标赛算法c语言,多目标遗传算法 ------ NSGA-II (部分源码解析) 交叉操作 crossover.c(示例代码)...
  7. Centos 7下安装pip(简略版)
  8. 整理笔记:初识应广(单片机)
  9. 中移动“5G+”计划:打地基、造工具、耕田地、种庄稼、建生态
  10. Python字符串操作之常忘的字符串、列表、数值