✔1.前置知识

1.ES6基本语法

2.Node.js、NPM 入门使用经验

✔2.需求分析与前后端接口约定

✔3.Vue-cli 搭建项目

本项目使用的

✔4.初识 Vue-router

1.使用 Vue-router 搭建基础路由结构

✔5.样式&组件&调试

1.新增 Sidebar组件

2.App.Vue 里引入 Sidebar组件

3.给 Sidebar 添加样式

4.封装 Avatar组件

5.Sidebar 组件引入 Avatar 组件

6.引入 Less

7.Vue 调试

浏览器插件:Vue devtool

✔6.Login组件

login页面功能:

  • 交互:点击切换tab;
  • 校验:点击button进行表单内容的校验(成功则创建请求,失败则弹出提示):
  • [x] 表单内容的校验方式:正则表达式;

    1. - ^匹配输入的开始
    2. - $匹配输入的结束
    3. - .匹配除换行符之外的任何单个字符
    4. - {n,m}匹配n~m个字符
    5. - 匹配中文字符的正则表达式: [\u4e00-\u9fa5]
    6. - `RegExp.prototype.test()`执行一个检索,用来查看正则表达式与指定的字符串是否匹配。返回 true false
    1. //模块化API
    2. //校验用户名
    3. validUsername(username) {
    4. return {
    5. isValid: /^[a-zA-Z_0-9\u4e00-\u9fa5]{3,15}$/.test(username),
    6. notice: '用户名必须是3~15个字符,限于字母数字下划线中文'
    7. }
    8. }
    9. //校验密码
    10. validPassword(password) {
    11. return {
    12. isValid: /^.{6,16}$/.test(password),
    13. notice: '密码长度为6~16个字符'
    14. }
    15. }

    1.给 Login组件增加内容和样式

    2.交互、校验的实现(v-bind、v-model、data、methods)

    3.优化体验,实现回车提交

    4.优化效果,增加登录注册切换操作的动画效果

  • [x] 复习vue过渡:

transition标签的用法思路:
html中:
①用 包裹需要过渡的元素标签;
②该目标元素标签配合v-if属性使用,判断是否为真,真则显示过渡后效果
css中:
①在目标元素标签的样式中自定义初始状态;②设置show为true时的样式

✔7.接口封装

1.如何获取数据;引入 axios,并使用 Promise 进行封装,便于后续统一使用

问:axios为什么还要进行promise封装?axios本身就是一个promise对象,为什么还要再return promise
答:

  1. 其实就是便于对数据做二次的处理,便于后面做链式调用,因为它是异步的,我们需要先获取数据,然后再做一些操作
  2. 比如axios对get和post的处理调用的参数不一样,封装起来方便进行统一处理,如get传递的是params对象,而post直接传递的是数据

使用validateStatus校验:
在选项中配置validateStatus,当后端返回状态码不在validateStatus中规定的,则直接catch返回网络异常错误

遇到问题:
页面刷新无法获取之前登陆状态,每次刷新后都退回未登录状态,即跨域问题

解决方法:
服务器设置CORS
前端aixos的话设置withCredentials为true
axios.defaults.withCredentials = true; //支持跨域请求为true,让发送请求携带cookie
注意:chrome在2020年3月份升级了安全策略,对于跨域请求如果想写入cookie,必须是https的网站才可以

  • 获取数据的功能是通用模块,src目录下新建helpers文件夹(通用功能文件夹)下新建request.js
  • 将获取的数据的过程进行封装:使用promise对axios进行封装

遇到问题:
每个组件都分散着登陆、注销等请求,能否将这些请求封装成函数,在需求的时候调用即可,语义化调用,如图
image.png
比如将所有的登录、注销、注册功能都放入Auth中,当业务代码中需要的时候按需调用即可,更加清晰明了
解决方法:
将请求接口封装成api,详情见下:

2.Login 组件增加登录注册接口→接口封装成API

src——apis
将接口封装成函数,在其他业务代码中按需调用即可

  • 模块化接口,接口封装成四个API:

注册接口
登录接口
注销接口
判登接口
调用时语义更清晰,格式统一,方便后期维护管理
当我们需要去查看某个模块下对应有哪些功能的时候只需要找对应文件查看,一目了然类型和参数

  1. //接口封装成API,调用时语义更清晰,方便后期维护管理
  2. // apis--auth.js
  3. import request from '../helpers/request'
  4. const URL = {
  5. REGISTER:'/auth/register',
  6. LOGIN:'/auth/login',
  7. LOGOUT:'/auth/login',
  8. GET_INFO:'/auth'
  9. }
  10. register({username,password}) {
  11. return request(URL.REGISTER,'POST',{username,password})
  12. },
  13. ...
  14. //调用时(即发送请求时)
  15. import Auth from '../apis/auth.js'
  16. Auth.register({username:npc,password:123456}).then(data => {...}).catch(..)

3.开发环境和生产环境下baseURL 的自动切换

生产环境:当build部署的时候自动切换为真正线上的地址
开发环境:当开发过程中自动切换为本地测试的地址或者其他mock平台的地址

  • 开发环境和生产环境下baseURL自动转换流程:

①在build目录下新建mock.config.js文件:
文件中定义好:模拟url和真实url
文件中导出一个config方法:其中设置了关于开发环境或者生产环境下对应的不同url
以下示例mock.config.js文件:
image.png
②分别在build目录下的webpack.dev.conf.js文件和webpack.prod.conf.js文件中调用:
分别调用mock.config.js文件中的config方法,且传入形参
require('./mock.config').config({isDev:true})
require('./mock.config').config({isDev:false})

③每次build或者run dev 的时候会在指定路径下创建config-baseURL文件。其中写明了当前环境的baseURL
image.png

4.把 Login 用到的接口封装成 API 便于维护管理

5.Promise补充

  • axios跨域访问设置:
    1. - 默认情况下,跨源请求不提供凭据(cookieHTTP认证及客户端SSL证明等)。
    2. - 通过将withCredentials属性设置为true,可以指定某个请求应该发送凭据。
    3. - 如果服务器接收带凭据的请求,会用下面的HTTP头部来响应。
    4. - `Access-Control-Allow-Credentials: true`
    • 需要注意是,当配置了axios.defaults.withCredentials = true时,必须在后端增加 response 头信息Access-Control-Allow-Origin,且必须指定域名,而不能指定为*。当前示例所采用的node端配置代码:

res.setHeader('Access-Control-Allow-Origin','http://172.19.0.215:3333');

✔8.需求的完善

1.登录或者注册后页面自动跳转

登录或者注册后页面自动跳转到【笔记本列表】
+ 注销后自动跳转到登录页

  • 跳转功能:Vue router文档:

模板中声明式:
js中编程式:router.push(…)
复习Vue router编程时导航
tip:信息的重置:eg.之前登录错误后再次登录成功则显示信息需要重置

  • 补充:Vue生命周期钩子

Vue生命周期:Vue对象在创立之后经历的过程:
钩子:到达某个阶段可插入、可做的某些事情
beforecreate:数据绑定之前,el 和 data 并未初始化均为undefined
created:完成了 data 数据的初始化且可获取其数据,el没有初始化(常用获取数据)
beforemount:完成了el初始化,但data并没有挂载完成,数据已经渲染好了,但还没被挂载到页面上
mounted:完成了挂载(常用对数据的操作、DOM访问)
beforeupdate
updated
beforedestroy
destoryed
Vue.nextTick( [callback, context] ):在数据改变之后,会在模板渲染之后尽快执行,可放在created钩子中

2.Avatar获取用户信息

  • 设置Avatar数据初始状态:‘未登录’
  • 对slug进行监听变化;并截取其首字作为头像显示:charAt(0)
  • 在created钩子中获取其数据:Auth.getInfo()

出现BUG:用户登录成功后,进入页面头像没有变化成登录状态后应有的头像,需要页面手动刷新才有
分析BUG:
用户在login登录页面的时候,avatar组件已经创建并渲染,因为avatar组件隶属于sidebar组件,而sidebar组件在app入口文件中即刻渲染的,故在login登录页面的时候,avatar组件已经创建并渲染,而此时isLogin检查到false,则没能请求到avatar组件的头像和用户名相关用户信息,故当login登录完成进入页面后,avatar组件不会再去created获取数据,故avatar组件的头像没有从之前的“未”变成登录成功后成功传递到的用户信息,即登录成功后的用户信息没有被传递过来

解决思路:
在login登录成功后再去获取用户信息,便能得到最新用户信息,获取到最新用户信息后去通知avatar组件,那这涉及到组件通信
avatar组件和login组件的关系:
sidebar——avatar
login

  • 解决BUG:组件间的传递:

①若为‘强关系’父子组件:
父传子:父组件可以使用 props 向子组件传递数据;
子传父:子组件可以使用$emit触发事件,父组件通过$on监听子组件传递的数据

②若为‘弱关系’不直接相关或兄弟组件:
方法一:通过共同的父组件来传递数据,即可以先从子组件传到父组件,再从父组件传到子组件,最糟糕的情况下,共同的父组件可使用根组件main.js中的全局Vue实例
方法二:但是为了表意清晰,通常新建bus.js文件,通过新建Vue实例作为其需要传递数据的不同组件的父组件,在新建的父组件上绑定和触发事件(此处使用方法二)

  1. //新建Bus.js
  2. import Vue from 'vue'
  3. export default new Vue()
  4. //login组件中
  5. import Bus from ''
  6. Auth.login().then(... Bus.$emit(...))
  7. //avatar组件中
  8. import Bus from ''
  9. created(){
  10. ...
  11. Bus.$on(....)
  12. }

tip:
vm为vue的一个实例/vue对象
$on和$emit方法只能针对同一个组件,如当前示例同为vm1组件实例vm1.$on('click',data=>{console.log(data)});vm1.$emit('click','halo')
如果找不到直接的共同父组件,可以找最顶层的根组件实例,即main.js中的全局vue实例,即有共同的父组件作为中介来传递事件参数
可使用浏览器插件vue devtool 调试工具的vm来测试通信

3.未登录访问登录页以外的页面都跳转到登录页

同理,在组件初始化created的时候获取数据【Auth.getInfo()】,在获取数据成功之后判断用户登录状态,进行对应页面跳转$router.push({path:’login’})

✔9.笔记本列表页

1.笔记本列表页的结构和样式

获取数据展示数据用户交互
流程分解:
先写好静态页面
获取数据
改写静态页面,把获取到的数据填充进页面
模板中增加对应方法
在方法内调用api,完成对应的事情
后面再做进一步的美化

遇到问题:
笔记本列表页渲染中:
笔记本选项是由router-link标签循环遍历出来的,实则a标签
而a标签中包含span等其他子标签,故点击span标签的时候,发生跳转,是因为触发事件冒泡机制,如何禁止跳转?

解决方法:
使用事件修饰符,.stop阻止事件传播 , .prevent阻止默认事件,可以串联写

遇到问题:
对笔记本列表进行增删改查的时候,ui应该和数据同步发生变化,但数据改了,ui没变,这种情况是因为没有对data中需要进行展示的数据进行相应处理

解决方法:
在方法中请求数据、得到数据、改动数据等增删改查操作之后或之前,需要对data中的数据进行处理,举例 删除操作

  1. onDelete(notebook){
  2. let isConfirm = window.confirm('你确定要删除吗')
  3. if(isConfirm) {
  4. Notebooks.deleteNotebook(notebook.id)
  5. .then(res => {
  6. alert(res.msg)
  7. this.notebooks.splice(this.notebooks.indexOf(notebook),1)//处理data中对应数据,
  8. 触发UI渲染
  9. })
  10. }
  11. }

2.新增 notebook 接口 api


notebook增删改查接口,仿照Auth接口,封装统一接口进行处理
后端返回的是时间戳,如何进行美化?
image.png
思考:排序功能、时间戳在哪插入比较合适?
👉首先清楚:helpers——request.js是底层程序进行请求发送功能的;apis——auth.js是具体业务的接口功能;
若想在业务组件代码中直接获取接口调用,
则应该在接口文件中,当进行增删改查的同时进行排序和时间格式化功能

注意tip:
因为接口文件中直接返回的是promise对象给业务组件代码,故得到的是原始时间戳(IOS时间),需要将其格式化(美化)为“刚刚、几天前”等,则需要在接口文件中新建promise对象,在对象中进行排序和时间格式化逻辑操作,再将操作结果返回给业务组件代码
由于日期时间格式化是常用功能,故将其封装为一个通用文件helpers-util.js,按需调用

3.新增 util 辅助方法便于数据处理

新增日期时间格式化文件(通用文件),helpers-util.js

4.笔记本列表功能

5.element-ui 的使用


ElementUI自定义主题报错“primordials is not defined”最佳解决方案:
(由于node版本高于12导致,或者没有安装再全局目录下)

  1. 安装如下配置 ElementUI 主题相关的包:

npm i element-themex element-theme-chalk --save-dev

  1. 创建完整的 element-variables.scss 文件:

node_modules/.bin/et -i

  1. 一般来说项目都有配置按需引入,此时接着修改 babel.config.js 文件:
    1. {
    2. "plugins": [
    3. [
    4. "component",
    5. {
    6. "libraryName": "element-ui",
    7. "styleLibraryName": "~theme"
    8. }
    9. ]
    10. ]
    11. }
    tips:注意Options参数的说明:
    type为字符串类型:’error’/‘success’/‘info’/‘warning’
    设置输入框的初始文本:inputValue,其为字符串类型

10.路由重构

1.细分需求,体验优化

遇到问题:
之前路由是:/note/:noteId 即使用动态的路径参数做跳转的根据,之前想法比较粗糙:每个页面在详情页面肯定需要有一个当前页面的id,那么就设置了一个noteId路径参数,但是后面发现这个做法满足不了交互需求,
比如用户登录后总共有三种选择情况:

  1. 【1. 用户首页登录】 -> 【2. 跳转到笔记本列表页面】-> 【3. 点击菜单栏的笔记 icon】
    ○ 第3步后应展示首个笔记本中的首个笔记
    2. 【1. 用户首页登录】 -> 【2. 跳转到笔记本列表页面】-> 【3. 点击菜单栏的回收站 icon】
    ○ 第3步后应展示最新删除的笔记
    3. 【1. 用户首页登录】 -> 【2. 跳转到笔记本列表页面】-> 【3. 点击列表中某个笔记本 a】-> 【4. 跳转到笔记本 a 的第1条笔记详情页】-> 【5. 此页面切换笔记本 b, 选择 b 的第2条笔记】 -> 【6. 点击笔记本列表 icon】 -> 【7. 点击笔记 icon】
    ○ 在第4步和第5步,我们的应用需要记录当前用户选中的笔记本和笔记,在执行第7步操作时应该默认展示刚刚记录的笔记,而不是首个笔记本的首个笔记

解决方法:
直接使用动态路径参数的话,无法确定点击跳转所需的参数,故分情况讨论,使用url参数

问题:
此时,该如何获取用户点击的笔记详情页和回收站?即用户点击sidebar对应图标该展示哪个页面?
之前是设置了一个:id 标识了各页面
那在这里该如何获取和跳转到目标页面?
解决:
前提思路:
index.js路由中删除:id后缀,不在index.js中决定跳转的页面id;sidebar组件中的路由跳转只负责跳转到对应组件而不负责组件内部动态路径,具体真正笔记详情页面是在对应页面组件中做跳转判断,如果用户有传其他query参数则渲染参数代表的页面,否则默认渲染最新详情页
跳转逻辑:
eg. 点击笔记详情页→获取所有笔记本列表(渲染)→检查用户有无传递笔记本列表的ID
→有 则在获取的所有笔记本列表中找到目标ID并将其渲染展示(当用户登录后手动点击笔记本列表页的某一笔记本时)
→无 则获取最新的笔记本列表的ID并将其最新的笔记渲染展示(当用户登录后手动点击笔记详情页时)

2.重构路由

①/note/:noteId的修正
/note
展示第一个笔记本中的第一个笔记
如果第一个笔记本是哪个?(最新创建的),第一个笔记是哪个?(最新创建的),如果没有笔记本怎么办?如果没有笔记怎么办?

/note?noteId=1&notebookId=2
展示 notebookId 为2的笔记本中 noteId 为1的笔记
如果笔记本不存在怎么办?如果笔记不存在怎么办?

/note?notebookId=2
展示 notebookId 为2的笔记本中的第一个笔记
如果笔记本不存在怎么办?如果笔记本为空怎么办?

②/trash/:noteId的修正
/trash
展示回收站中的所有已删除的笔记中的第一个
如果回收站为空怎么办

/trash?noteId=1
展示 noteId 为1的已删除笔记
如果 noteId 为1且已删除的笔记不存在怎么办

注意tip:两种参数的区别:
一种是动态path路径参数 :noteId(以冒号开头),$route.params.noteId 获取变量
一种是url中携带的参数 ?noteId=1(以问号开头往后) $route.query.noteId获取变量

10.笔记详情页

1.笔记详情页结构和样式

2.新增api 接口

3.路由的匹配与数据获取

4.笔记本切换功能

5.笔记详情展示

6.父子组件间的数据传递


NoteSidebar子组件和NoteDetail父组件之间传递数据:父子组件如何传递数据? 通过什么?
可以发现,点击不同笔记的时候url显示不同noteId,考虑是否和后端约定了“通过noteId拿到笔记详情页”的接口?(×)
image.png
需求体现①:
当点击NoteSidebar侧边栏对应的笔记时,右边笔记详情页note-detail显示对应笔记详情
解决方式①:
使用子传父emit自定义事件+组件内的导航守卫:
补充:组件内的导航守卫:
针对NoteDetail组件内;在某个时机触发某个事件(监听和劫持);当NoteDetail组件的url中noteId发生变化的时候触发事件

  1. beforeRouteUpdate(to, from, next) {
  2. // 在当前路由改变,但是该组件被复用时调用
  3. // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
  4. // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
  5. // 可以访问组件实例 `this`
  6. },

具体解决👉

  • →子组件向父组件发送数据用自定义事件:NoteSidebar可以将笔记列表拿给NoteDtail:

NoteSidebar子组件中:this.$emit('update:notes', this.notes) ,设置于notes每次更新时;
NoteDtail父组件中:@update:notes="val => notes = val",监听事件获取数据,且在data中设置notes数组

  • → 在NoteDetail组件内利用beforeRouteUpdate监听noteId的变化 → 监听到变动后就将笔记列表拿出到current笔记

this.curNote = this.notes.find(note => note.id ===to.query.noteId)

需求体现②:
当NoteDetail页面刷新后自动显示当前curNote内容,而不是在切换notes的时候触发beforeRouteUpdate才能显示内容

解决方式②:
使用eventBus组件通信

补充:eventBus组件通信
对于比较小型的项目,没有必要引入 vuex 的情况下,可以使用 eventBus
eventBus 可以实现任意两个组件间的通信
它的实现思想也很好理解,在要相互通信的两个组件中,都引入同一个新的vue实例,然后在两个组件中通过分别调用这个实例的事件触发和监听来实现通信。

  1. //eventBus.js
  2. import Vue from 'vue';
  3. export default new Vue();
  4. <!--组件A-->
  5. <script>
  6. import Bus from 'eventBus.js';
  7. export default {
  8. methods: {
  9. sayHello() {
  10. Bus.$emit('sayHello', 'hello');
  11. }
  12. }
  13. }
  14. </script>
  15. <!--组件B-->
  16. <script>
  17. import Bus from 'eventBus.js';
  18. export default {
  19. created() {
  20. Bus.$on('sayHello', target => {
  21. console.log(target); // => 'hello'
  22. });
  23. }
  24. }
  25. </script>

具体解决👉

  • helpers文件夹新建bus.js文件作为通信中介
  • NoteSidebar组件发出事件,更新notesBus.$emit('update:notes', this.notes)
  • NoteDetail组件监听事件,在Bus上调用监听;once监听一次Bus.$once('update:notes',val => {<br /> this.curNote = val.find(note => note.id === this.$route.query.noteId) || {}<br />})

需求体现③:
当detail内容页面的标题发生变化时自动同步更新到左侧栏notesidebar的笔记标题

需求体现④:
更改笔记详情detail内容页面后需要发起一个请求去保存笔记,然后将得到的结果状态更新到笔记详情页的statusText

7.笔记的markdown编辑与预览

引入markdown库(markdown-it),结合vue的v-html

  1. import MarkdownIt from 'markdown-it'
  2. let md = new MarkdownIt()
  3. //使用计算属性同步生成html结果:
  4. computed:{
  5. previewContent() {
  6. return md.render(this.curNote.content || '')
  7. }
  8. }
  9. //结合v-html
  10. <div class="preview markdown-body" v-html="previewContent" v-show="isShowPreview"></div>


less编译出现BUG:calc(100%-70px)无法成功渲染
则禁止改行进行less编译即可:~"calc(100%-70px)"

11.Vuex 状态管理

1.数据传递、状态共享的问题分析

整个vue只有一个对象,这个对象存储着一些数据,而这些数据可以被所有组件共享

2.Vuex 速学

vuex是一个状态管理工具

  • 核心概念:

state 放原始数据

  1. const state ={
  2. notes:null,
  3. curNoteId:null,
  4. }

getters 放数据获取,可对原始数据做进一步封装和判断,以供组件模板获取使用,getters也会随着state数据变化而变化

  1. const getters = {
  2. notes: state => state.notes|| [],
  3. curNote: state => {
  4. if(!Array.isArray(state.notes)) return {title:'',content:''}
  5. if(!state.curNoteId) return state.notes[0] ||{title:'',content:''}
  6. return state.notes.find(note => note.id == state.curNoteId) || {title:'',content:''}
  7. }
  8. }

mutations 放同步操作,放入数据(针对视图[浅显的])的增删改查,更改 Vuex 的 store 中的状态的唯一方法是提交 mutation

  1. const mutations = {
  2. setNote(state,payload) {
  3. state.notes = payload.notes
  4. },
  5. ...
  6. }
  7. //调用:不能直接调用一个 mutation 处理函数。这个选项更像是事件注册
  8. store.commit('setNote')

actions 放异步操作,放入更复杂(需要向服务器发送请求 获取响应 然后 再进行视图型数据操作)的操作, 提交的是 mutation,而不是直接变更状态

  1. const actions = {
  2. getNotes({commit},{notebookId}) { // {commit}是用来调用mutations的
  3. return Notes.getAll({notebookId})
  4. .then(res => {
  5. commit('setNote',{notes: res.data}) //调用
  6. })
  7. },
  8. ...
  9. }
  10. //actions一般基于mutations,即异步获取到服务器响应后.then中调用commit提交对应mutations

state和getters 是表示数据在里面存储的一个状态,其他组件可以得到
单向数据流 理念的示意图:
image.png
state 驱动应用的数据源
view 以声明的方式将state映射到视图
actions 响应用户在view 上的交互,执行一些操作,再去更改数据state

tip:
mutations中的操作方法可与actions中的操作方法名相同
state的数据:读取用getters;修改用mutation或actions;而不要直接在state中拿

  • Vuex+Vue的使用:

①src——store——modules——创建各Vuex仓储(导入api接口,声明state、getters、mutataions、actions,尽量放与纯数据有关的信息)
image.png
②src——store——index.js
image.png
③main.js全局导入和全局注入store
import store from './store'
image.png
全局注入方便各组件直接使用:eg.this.$store.state
③各组件导入方法import {**_mapState_**, **_mapActions_**, **_mapGetters_**} from 'vuex'
④使用辅助函数,在各组件中映射Vuex

你可以在组件中使用 this.$store.commit(‘xxx’) 提交 mutation,或者使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用(需要在根节点注入 store)。

  1. computed: {
  2. ...mapGetters([
  3. 'notebooks',
  4. 'notes',
  5. 'curBook'
  6. ])
  7. },
  8. methods: {
  9. ...mapMutations([
  10. 'setCurBook',
  11. 'setCurNote'
  12. ]),
  13. ...mapActions([
  14. 'getNotebooks',
  15. 'getNotes',
  16. 'addNote'
  17. ]),

有了map映射方法,组件内可直接使用其操作方法,形同普通JS函数使用方式

3.Vuex 改造笔记本列表页面

核心思路:
举例notebookList组件
notebookList组件中的某些组件搬到vuex中,因为这些核心组件其他组件可能需要共享
然后对应的操作全部使用vuex的state和getters和mutation和actions做

新建store目录→新建modules目录→新建各组件的仓库

  • 举例封装actions,暴露统一接口(已对请求接口进行封装apis的前提下),组件层直接调用即可:

actions执行异步操作有两个步骤:(将以下两个步骤进行封装,统一接口)
①通过异步操作,与服务器交互,对服务器发起请求和接收响应
②通过同步操作,通过mutations提交commit给state
eg从服务器获取笔记本列表数据 并 修改数据后 再渲染到页面

  • 在 Vue 组件中获得 Vuex 状态,两种方法:

①通过computed,从state中获取:computed: { ...mapState( ['notebooks'] ) }
②通过computed,从getters中获取:computed: { ...mapGetters( ['notebooks'] ) }(常用)

mutation和actions中一般存放“增删改”和“读取”操作;
getters一般存放“查”操作

4.Vuex 改造笔记详情页

5.Vue改造用户信息数据user.js

用户信息数据(username,password)由login 和register两个接口获取
slug是由username首字母构成的头像

问题:用户第一次登录进来Avatar获取slug正常,但刷新页面后,user数据变为null,slug变为默认“未”,用户信息数据没有被判断标记并记录
解决:当用户登录或者注册后需要有一系列判断机制标记,由于该判断机制在多个页面均需要,且与数据相关,所以最好放入store仓库,若未登录则跳转到登录注册界面,若已登录则提交记录设置user信息

  1. checkLogin({commit}) {
  2. return Auth.getInfo()
  3. .then(res => {
  4. if(!res.isLogin) {
  5. console.log('jump') //跳转到登录注册界面
  6. } else {
  7. commit('setUser',{user:res.data}) //提交记录设置user信息
  8. }
  9. })
  10. }

tip:store中可以看需直接引入router:import router from '../router'
前提是main.js中在Vue中注入全局store和router,此时,store和router都是vue下的子模块

12.回收站页面

1.回收站页面结构、样式、

2.接口与基本功能完善

3.Vuex 改造回收站页面

tip:
①setTrashNote和addTrashNote区别:
一个是设置,一个是添加;
setTrashNote用在:页面刷新后对服务器发出请求,得到响应后一次性提交设置trashNotes(组件中常应用于异步操作中)
addTrashNote用在:用户在noteDetail页面中删除笔记后,对应地,trashDetail页面中相应地增加该笔记

②revertTrashNote异步方法和deleteTrashNote异步方法在与服务器交互后,再对视图层进行操作(增删改查)时,均需要对数据进行视图上的“删除”,故均需提交删除的同步方法

③store数据仓库跨模块获取数据:

  1. belongTo:(state,getters,rootState,rootGetters) => {
  2. //rootState,rootGetters表示全局/底层数据
  3. //state,getters表示当前模块数据
  4. let notebook = rootGetters.notebooks.find(notebook => notebook.id == getters.curTrashNote.notebookId) || {}
  5. return notebook.title || ''
  6. }

④减少各组件对同一请求的多次发送,提高发送效率,通过设置一系列判断机制以引流:
eg. getNotebooks的请求改造:在其数据仓库notebook.js做判断,若原始数据state中的notebooks为null才发送请求,若不为null则直接返回promise对象,可直接promise.resolve()

  1. const actions = {
  2. getNotebooks({commit,state}) {
  3. if(state.notebooks != null) return Promise.resolve()
  4. return Notebook.getAll()
  5. .then(res => {
  6. commit('setNotebooks',{notebooks: res.data})
  7. })
  8. },

13.产品完善

1.产品完善

①从两个途径(笔记本列表和笔记详情页)点击进入笔记详情页时,默认选中第一个笔记,且出现选中框
👉关键:需要获得当前笔记id信息,则可框选笔记项

②Vue单页面应用打包后统一文件庞大,影响页面加载效率
👉关键:路由懒加载
把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了

2.问题总结