了解复杂项目中的状态管理方案 Vuex了解复杂项目中的状态管理方案 Vuex了解复杂项目中的状态管理方案 Vuex
学会手写一个自己的 Vuex
了解 Vue.js 服务端渲染的使用
弄清楚为什么要使用 SSR
使用 SSR 框架 Nuxt.js 快速开发一个服务端渲染的项目
提问
一、什么是 Vuex 状态管理?
一种集中式的状态解决方案,用于处理项目多组件共享同一个状态的问题
Vuex 状态管理
课程目标
- 组建通信方式回顾
- Vuex 核心概念和基本使用回顾
- 购物车案例
- 模拟实现 Vuex
项目复杂多组件共享同一个状态的时候,组件间的通信比较麻烦,Vuex 作为集中式的状态解决方案可以处理此类问题。
组件内状态管理流程
每个组件内部都有自己的数据、模板、方法,也可以称之为状态、视图、行为。
我们所说的状态管理指的是通过状态,集中管理和分发,解决多个组件共享状态的问题
状态管理的组成:
- state:驱动应用的数据源(状态)
- view:通过把状态绑定到视图呈现给用户
- actions:响应在 view 上的用户输入导致的状态变化行为,用户和视图交互改变视图的方式
组件间通信方式回顾
- 父组件给子组件传值
- 子组件给父组件传值
- 不相关组件之间传值
父组件给子组件传值
- 子组件通过 props 接收数据
- 父组件中给子组件通过相应属性传值
props可以接收两种值,对象或数组,如果是对象,可约定传入对象类型
子组件给父组件传值
子组件通过自定义事件来实现给父组件传值
自定义事件是父组件中注册的,子组件中通过$emit触发父组件注册的自定义事件,并向其传值
在父组件使用子组件的时候使用v-on注册子组件中的自定义事件
在父组件的事件处理函数中接收自定义事件传入的值
另一种方式是在行内获取自定义事件传递数据的时候直接通过$event获取
核心在于子组件在内部触发事件的时候携带参数,然后在父组件中注册子组件内部触发的事件,并接收传递的数据,完成子向父的传值,在注册事件的时候,行内可以通过$event来获取自定义事件传递的参数
不相关组件之间传值
不相关组件之间的通信也是使用自定义事件的方式,但是与之前传值不同的是不能由子组件触发传值的方式,这里使用的是Event Bus,就是创建一个公共的vue实例,这个vue实例的作用是作为事件组件或者事件中心,
这里的不相关组件的关系包含兄弟组件的关系
eventbus.js文件只是用来导出Vue的实例,其目的是为了调用$init和$on,用来触发和注册事件
这个机制就是发布订阅模式
通过ref获取子组件
除了前三种组件通信的方式外还有其他的几种方式
$root、$parent、$children、$refs
$refs可以用来获取子组件的实例,通过实例改变子组件的状态值
如果滥用的话容易导致状态管理的混乱
问题
- 多个视图依赖同一状态
- 来自不同视图的行为需要变更同一状态
为了解决这些问题,我们可以把不同组件中的共享状态抽取出来,存储到一个全局对象中,并且保证将来使用的时候是响应式的,这个对象创建好后,任何组件都可以获取或者修改全局对象中的状态
简易版的状态管理我们称之为store
什么是Vuex
- Vuex 是专门为 Vue.js 设计的状态管理库
- Vuex 采用集中式的方式存储需要共享的状态
- Vuex 的作用是进行状态管理,解决复杂组件通信,数据共享
- Vuex 集成到了 devtools 中,提供了 time-teravel 时光旅行历史回滚功能
什么情况下使用 Vuex
- 非必要情况下不要使用 Vuex
- 大型的单页应用程序
- 多个视图依赖于同一状态
- 来自不同视图的行为需要变更同一状态
state是我们管理的全局状态,把状态绑定到组件也就是视图上
用户与视图交互通过Dispatch分发Actions,此处为什么不直接触发Mutations,因为Acitons中可以进行异步的操作
异步操作结束时可以通过提交Mutation记录状态的更改
Mutation必须是同步的,这样做的目的是为了通过Mutation追踪到所有状态的变化,阅读代码的时候更容易追踪状态的改变,还可以记录每一次状态的改变,实现高级的调试功能
Vuex 核心概念
- Store:仓库,每一个应用仅有一个Store,Store是一个容器包含应用中的大部分状态,我们不能直接改变Store中的状态,我们需要通过提交Mutations的方式改变状态
- State:状态,State存储在Store中,Store是唯一的,State也是唯一的,称为单一状态树,但是所有的状态都保存在State中的话,会让程序难以维护,可以通过后续的模块解决该问题,要注意这里的状态是响应式的
- Getter:计算属性,方便从一个属性派生出一个其他的值,其内部可以对计算结果做响应的缓存,只有依赖的状态发生改变的时候才会进行相应的计算
- Mutation:状态的变化需要通过提交Mutation完成
- Action:Action和Mutation类似,但是可以进行异步的操作,内部改变状态的时候都需要提交Mutation
- Module:模块,由于使用单一状态树,应用的所有状态都会集中到一个比较大的对象上来,当应用变得非常复杂时,Store对象就会变得非常臃肿,为了解决这个问题Vuex允许我们将Store分割成几个模块,每个模块拥有自己独立的State、Getter、Mutation、Action甚至是嵌套的子模块
Vuex 基本结构
在store模块中导入Vue和Vuex
通过Vue.use(Vuex)注册插件
插件内部将Vuex.Store注入到了Vue的实例上
创建Vuex.Store 对象并且导出
在App.js中导入Store对象
创建Vue实例的时候传入Store选项
这个Store选项会被注入到Vue实例之中
Store
Vuex 使用单一状态树,用一个对象就包含了全部的应用层级状态。
使用 mapState 简化 State 在视图中的使用,mapState 返回计算属性
mapState 有两种使用的方式:
接收数组参数
接收对象参数:接收对象参数可以解决命名冲突的问题
Get
Getter 就是Store中的计算属性,使用mapGetter 监视视图中的使用
Mutation
不要在mutation中执行异步操作修改state
mutation的调用commit
Action
Module
购物车组件
- 商品列表
- 购物车列表组件
- 我的购物车组件
商品列表
- 展示商品列表
- 在Sotre文件下创建modules模块文件夹
- 定义模块文件中的常亮并且导出模块对象配置命名空间namespaced:true
- 在Store文件夹下的index.js中也就是vuex中导入模块并在vuex的modules中配置写入的模块
- 回到之前定义的模块文件中,定义state商品数据,在mutations中添加方法修改state,在actions中添加异步请求商品数据的方法并调用mutations定义的方法给state添加商品数据
- 在页面中设置计算属性computed,开启了命名空间后第一个入参是数组要映射的入参属性
- 添加购物车
功能列表
- 购物车列表
- 全选
- 数字文本框加减功能
- 删除
- 统计选中商品的数量和总价
Vuex插件
创建完插件后需要在创建store实例时进行plugins注册
subscribe的作用是订阅store中的mutation,其回调函数会在每个mutation完成之后调用
模拟Vuex
创建一个Store类
基于VUE的插件规则创建一个install函数
在install中可以获取到vue的构造函数
在顶部声明一个_vue变量来存储install中获取到的vue构造函数
在install内部先将传入的vue存储到_vue中
在最外层导出Store和install
在install中,我们要将创建vue实例的时候,传入的store对象注入到vue原型上的$store
在所有组件中可以直接通过this.$store,来获取到vuex中的仓库,从而可以在所有组件中共享状态
在install中我们获取不到vue实例,所以我们通过混入beforeCreate来获取vue实例,从而拿到选项中的store对象
在beforeCreate中首先判断当前vue实例的$options中是否有store,如果是组件实例的话没有store选项,我们在其中挂载store
接下来是store类如何实现
首先需要一个构造函数接收一个对象
store中应该有与核心概念对应的几个属性,其中state是响应式的,还具备两个方法,分别是commit和dispatch用来提交mutation和分发actions
服务端渲染
同构应用
- 什么是渲染
- 传统的服务端渲染
- 客户端渲染
- 现代化的服务端渲染(同构渲染)
什么是渲染?
渲染指的是把数据和模板拼接到一起,渲染的本质是字符串的解析替换
我们要关注的不是如何渲染,而是在哪里渲染
为什么客户端首屏渲染慢?
因为需要在客户端进行三个步骤的请求,才有完整内容展示,而服务端渲染只需要一次内容直出
为什么客户端渲染不利于seo?
客户端的网页内容在被seo检索的时候是空的内容,原因是客户端需要解析js脚本来呈现完整内容,seo相当于字符串获取检索而不是浏览器回去执行脚本加载
现代化客户端渲染同构渲染
同构渲染 = 后端渲染 + 前端渲染
如何实现同构渲染
使用Vue、react等框架的官方解决方案
同构渲染的问题
- 开发条件限制
- 设计构建设置和部署的更多要求
- 更多的服务器端复杂
Nuxt.js是什么
一个基于Vue.js生态的第三方开源服务端渲染应用框架
可以帮我们轻松的使用vue.js技术构建同构应用
gii
Nuxt项目的搭建
按照文档创建项目
初始化git
配置gitgone,忽略依赖包和.nuxt
git add .
git commit -m "xxx"
git branch 分支名称 创建本地分支
git checkout 分支名 切换本地分支
git checkout -b 分支名 创建新本地分支的同时切换到该分支
页面之间使用路由,nuxt-link
了解路由规则默认配置结构
路由跳转
nuxt-link或者编程式导航
nuxt-link和vue的routerLink是一个东西,可以在.nuxt文件夹中查看router文件
如果通过a链接跳转会走服务端渲染导致页面刷新
编程式导航参照vueRouter中的编程式导航
动态路由得根据nuxt提供的文件常见规则定义
$route和$router的区别是,前者是当前的路由对象,后者是全局的路由对象
嵌套路由
创建同名文件夹和文件,文件夹下的文件会被视作子路由
自定义路由配置可以到api中寻找router
Nuxt.js异步数据-asyncData
用于异步数据请求放在服务端请求而不是客户端
但在非首屏情况下,客户端spa激活时作为客户端请求
但要注意的是只能在页面组件中使用
因为是在组件初始化之前调用的所以其内部无法使用this
异步数据上下文对象
为asyncData放入一个入参,该入参会被视作上下文对象,可在该对象中获取路由传参,也可以通过他获取到$route实例
Nuxts.js综合案例
项目初始化
创建项目
mkdir realworld-nuxtjs
cd realworld-nuxtjs
npm init -y
npm install nuxt
// 在 package.json 中添加启动脚本
"script":{
"dev":"nuxt"
}
创建pages目录,配置初始页面
执行 npm run dev 项目启动
导入样式资源
根目录创建app.html,在官网获取默认模板放入
在提供的模板中提取三个样式文件引入到默认模板中
对于一些国外资源的文件加载,可以进行本土化
打开网站www.jsdelivr.com,搜索相关的国内的cdn链接,根据版本选择找到对应的链接在进行替换
布局组件
page文件件下创建layout文件夹并在内部创建index.vue
配置顶部导航栏和底部内容,中间嵌入子路由入口
设置完毕后创建home页面,在nuxt.config.js中配置自定义路由规则,通过splice清空默认生成的路由规则,设置layout作为主页面,内部嵌套home作为子路由
导入登录注册页面以及剩余页面导入
处理顶部导航链接并设置高亮
将导航栏的a标签跳转都替换为nuxt-link
在route中配置linkActiveClass自定义class,来替换默认高亮class
在路由跳转中放入exact开启精确匹配,防止嵌套路由导致的导航链接高亮
封装请求模块并给输入框添加表单验证
安装axios
项目根目录创建utils文件夹
文件夹内创建request.js文件
创建api文件夹,内部创建模块请求文件
表单验证required会视作必填项目
登录注册
实现基本登录功能
封装请求方法
表单验证
错误处理
用户注册
解析存储登录状态实现流程
与以往的登录状态存储不同,要考虑到服务端和客户端都能获取到登录状态的存储
Nuxt提供了跨域身份验证
不能存储到storage中,否则服务端无法获取
Cookie.set(‘user’,data.user)
将登录状态存储到容器中
登录状态持久化
处理导航栏链接展示状态
处理页面访问权限
通过中间件处理未登录需要授权保护的页面
在需要保护的页面中载入中间件
中间件的定义
中间件的载入
首页
- 文章列表展示
- 文章列表分页
- 公共的文章列表与关注的文章列表,非登录状态关注的文章列表入口不显示
-
展示公共文章列表
封装对应的公共文章列表接口
在首页页面中将请求放入asyncData中,请求完毕将需要的结果return,目的是为了首屏渲染
样式单个class条件激活<button
class="btn btn-outline-primary btn-sm pull-xs-right"
:class="{active:article.favorited}"
>
<i class="ion-heart"></i> {{article.favoritesCount}}
</button>
分页参数的使用
页码处理
监听query参数改变使用watchQuery监听参数,当数据改变自动触发asyncData
展示文章标签列表
优化并行异步任务
处理标签列表链接和数据
处理导航栏
标签高亮及链接
展示用户关注的文章列表
统一设置用户token
通过axios的请求拦截器,利用nuxt的插件机制注册插件,通过插件机制获取上下文,拦截器通过插件获取到的上下文取得token写入请求headers中
时间发布格式化处理
使用第三方依赖来自己配置插件,在plugins目录下创建dayjs.js文件
写入过滤规则
文章点赞
文章详情
展示基本信息
markdown 转 HTML
使用markdown it 将markdown转化为html
展示文章作者相关信息
设置页面meta优化seo
搜索nuxt官网,视图:HTML头部
个性化特定页面的meta标签通过客户端展示评论内容
发布部署
配置 Host + port
- 压缩发布包
- 把发布包传到服务器
- 解压
- 安装依赖
- 启动服务
处理中:C端小程序_从团详情页面购买:对于落地配类型商品,页面展示错误,显示成了一件代发的页面,导致无法下单
pm2 的启动 pm2 start npm — start
终止pm2 stop id
注意:前缀命令需要软链接配置,要确保版本对应正确,并且是在node文件夹下执行
scp realworld-nuxtjs.zip root@121.4.116.89:/root/realworld-nuxtjs
npm install —global pm2