Vue详解知识概括
- Vue CLI
- Vue-router
- Promise
- Vuex
- axios
- Vue开发总结
Vue CLI
什么是Vue CLI:
- 如果你只是简单写几个Vue的Demo程序, 那么你不需要Vue CLI.
- 如果你在开发大型项目, 那么你需要, 并且必然需要使用Vue CLI
①使用Vue.js开发大型应用时,我们需要考虑代码目录结构、项目结构和部署、热加载、代码单元测试等事情。
②如果每个项目都要手动完成这些工作,那无疑效率比较低效,所以通常我们会使用一些脚手架工具来帮助完成这些事情。 - CLI是什么意思?
①CLI是Command-Line Interface, 翻译为命令行界面, 但是俗称脚手架.
②Vue CLI是一个官方发布 vue.js 项目脚手架
③使用 vue-cli 可以快速搭建Vue开发环境以及对应的webpack配置.
Vue CLI使用前提:
- 安装NodeJS
①默认情况下自动安装Node和NPM
②NPM的全称是Node Package Manager
③是一个NodeJS包管理和分发工具,已经成为了非官方的发布Node模块(包)的标准。 - Vue.js官方脚手架工具就使用了webpack模板
①对所有的资源会压缩等优化操作
②它在开发过程中提供了一套完整的功能,能够使得我们开发过程中变得高效。
Vue CLI的使用:
- 安装Vue脚手架:npm install -g @vue/cli
①查看vue版本:vue –version
②注意:上面安装的是Vue CLI3的版本,如果需要想按照Vue CLI2的方式初始化项目时不可以的。
③拉取2.X模板(旧版本):npm install -g @vue/cli-init - Vue CLI2初始化项目:vue init webpack my-project
- Vue CLI3初始化项目:vue create my-project
Vue CLI2详解:
- 初始化命令使用:
- 目录简介:
①build.js:构建入口
②index.js:变量,配置信息
③webpack.base.conf.js:公共webpack配置信息
④webpack.dev.conf.js:部署配置信息
⑤webpack.prod.conf.js:构建配置信息
(运行时+编译器)Runtime-Compiler和(只含有运行时版本)Runtime-only的区别:
- 如果你需要在客户端编译模板(例如, 向template 选项传入一个字符串,或者需要将模 板中的非DOM的HTML挂载到一个元素),你需要带有编译器的版本,因而需要完整构建版本。
- 在使用vue-loader或vueify 时,* . vue文件中的模板会在构建时(build time)预编译(pre-compile)为JavaScript.最终生成的bundle中你不再需要编译器(compiler),因此可以直接使用只含有运行时的构建版本(runtime- only)。
//这种情况需要编译器(compiler)new Vue({template: '<div>({ hi }}</div>')//这种情况不需要new Vue({render (h) {return h('div', this.h1)}})
- 由于只含有运行时构建版本(runtime-only)比完整构建版本(ull-build)轻量大约30%,你应该尽可能使用只含有运行时的构建版本。如果你还是希望使用完整构建版本,则需要在打包器中配置别名。
- 简单总结:
①如果在之后的开发中,你依然使用template,就需要选择Runtime-Compiler。
②如果你之后的开发中,使用的是.vue文件夹开发,那么可以选择Runtime-only。
render和template区别:
//Runtime-Compilernew Vue({el :'#app',components: { App },template: '<App/>'})//Runtime-onlynew Vue({el : '#app',render: h => h(App)})
- 为什么存在这样的差异呢?
①我们需要先理解Vue应用程序是如何运行起来的。
②Vue中的模板如何最终渲染成真实DOM。
③我们来看下面的一幅图。
npm run build与npm run dev详解:
- npm run build:
- npm run dev:
webpack.base.conf.js起别名:
resolve: {extensions: ['.js', '.vue', '.json'],alias: {'@':resolve('src'),'pages': resolve('src/pages'),'common': resolve('src/common'),'components':resolve('src/components'),'network': resolve('src/network')}},
Vue CLI3详解:
- vue-cli 3 与 2 版本有很大区别:
①vue-cli 3 是基于 webpack 4 打造,vue-cli 2 还是 webapck 3
②vue-cli 3 的设计原则是“0配置”,移除的配置文件根目录下的,build和config等目录
③vue-cli 3 提供了 vue ui 命令,提供了可视化配置,更加人性化
④移除了static文件夹,新增了public文件夹,并且index.html移动到public中 - 初始化使用:
- 目录结构详解:
- 配置去哪里了:
①UI方面的配置:启动配置服务器:vue ui
②一大堆配置文件去哪里了?
Vue-router
什么是路由?
- 路由(routing)就是通过互联的网络把信息从源地址传输到目的地址的活动
- 路由器提供了两种机制: 路由和转送.
①路由是决定数据包从来源到目的地的路径.
②转送将输入端的数据转移到合适的输出端. - 路由中有一个非常重要的概念叫路由表。路由表本质上就是一个映射表, 决定了数据包的指向。
后端路由阶段:
- 早期的网站开发整个HTML页面是由服务器来渲染的:
①服务器直接生产渲染好对应的HTML页面, 返回给客户端进行展示. - 但是一个网站, 这么多页面服务器如何处理呢” />执行:location.hash = ‘/foo’返回:”/foo”执行:location. href返回:”http: //192.168.1.101: 8000/examples/urlChange/#/foo”
HTML5的history模式:
- history接口是HTML5新增的, 它有五种模式改变URL而不刷新页面。
- history接口底层的数据结构是栈。
- 函数:
①history.pushState():插入锚点,改变url,支持前进后退。
②history.replaceState():插入锚点,改变url,不支持前进后退。
③history.go() - 补充说明:
①上面只演示了三个方法
②因为 history.back() 等价于 history.go(-1)
③history.forward() 则等价于 history.go(1)
④这三个接口等同于浏览器界面的前进后退 与URL的hash相比,没有了#符号
。
vue-router认识:
- 目前前端流行的三大框架, 都有自己的路由实现:
①Angular的ngRouter
②React的ReactRouter
③Vue的vue-router - 当然, 我们的重点是vue-router,vue-router是Vue.js官方的路由插件,它和vue.js是深度集成的,适合用于构建单页面应用。
- vue-router是基于路由和组件的:
①路由用于设定访问路径, 将路径和组件映射起来.
②在vue-router的单页面应用中, 页面的路径的改变就是组件的切换.
安装和使用vue-router:
- 安装vue-router:npm install vue-router –save
- 在模块化工程中使用它(因为是一个插件, 所以可以通过Vue.use()来安装路由功能)
①导入路由对象,并且调用 Vue.use(VueRouter)
②创建路由实例,并且传入路由映射配置
③在Vue实例中挂载创建的路由实例 - 使用vue-router的步骤:
①创建路由组件
②配置路由映射: 组件和路径映射关系
③使用路由: 通过和 - 路由标签详解:
①: 该标签是一个vue-router中已经内置的组件, 它会被渲染成一个标签.
②: 该标签会根据当前的路径, 动态渲染出不同的组件。
③网页的其他内容, 比如顶部的标题/导航,或者底部的一些版权信息等会和处于同一个等级。
④在路由切换时, 切换的是挂载的组件,其他内容不会发生改变。
----创建router文件夹,然后创建index.js文件,创建router实例,配置组件和路径的映射关系:----import Vue from 'vue'import VueRouter from 'vue-router'// 1.注入插件Vue.use(VueRouter)// 2.定义路由const routes = [{path: '/home',component: Home}{path: '/about', component: About}]// 3.创建router实例const router = new VueRouter({routes})// 4.导出router实例export default router----main.js文件中挂载到Vue实例中:----import Vuefrom 'vue'import App from './App'import router from './router'new Vue({el: '#app',router,render: h => h(App)})----创建About和Home路由组件--------使用路由:----<template><div id="app"><h1>我是网站的标题<router-link to="/home">首页</router-link><router-link to="/about">关于</router-link><router-view></router-view><h2>我是APP中一些底部版权信息</h2></div></template>----最终效果如下:----一、当路径是根路径时,router-view没有渲染试图二、点击首页,路径切换,router-view渲染home组件伞、点击关于,路径切换,router-view渲染about组件
vue-router细节处理:
- 路由的默认路径:
①默认情况下, 进入网站的首页, 我们希望渲染首页的内容,但是我们的实现中, 默认没有显示首页组件, 必须让用户点击才可以。
②如何可以让路径默认跳到到首页, 并且渲染首页组件呢,非常简单, 我们只需要配置多配置一个映射就可以了。
③我们在routes中又配置了一个映射,path配置的是根路径: /,redirect是重定向, 也就是我们将根路径重定向到/home的路径下, 这样就可以得到我们想要的结果了。
const routes =[{path: '/'redirect: '/home'}]
- HTML5的History模式:
①我们前面说过改变路径的方式有两种:URL的hash,HTML5的history。默认情况下, 路径的改变使用的URL的hash,如果希望使用HTML5的history模式, 非常简单, 进行如下配置即可:
const router =new VueRouter({routes,mode: 'history'})
- router-link补充:
①在前面的中, 我们只是使用了一个属性: to, 用于指定跳转的路径。
②还有一些其他属性:
tag:
tag可以指定之后渲染成什么组件, 比如上面的代码会被渲染成一个 - 元素, 而不是。
replace:
replace不会留下history记录, 所以指定replace的情况下, 后退键返回不能返回到上一个页面中。
active-class:
当对应的路由匹配成功时, 会自动给当前元素设置一个router-link-active的class, 设置active-class可以修改默认的名称。在进行高亮显示的导航菜单或者底部tabbar时, 会使用到该类。但是通常不会修改类的属性, 会直接使用默认的router-link-active即可。 - 修改linkActiveClass:
①该class具体的名称也可以通过router实例的属性进行修改。
const router = new VueRouter({routes,mode: 'history',linkActiveClass:'active'})
- 路由代码跳转:
①有时候, 页面的跳转可能需要执行对应的JavaScript代码, 这个时候, 就可以使用第二种跳转方式了。
<template><div id="app"><h1>我是网站的标题<button @click="li nkToHomel">首页</button><button @click="l inkToAbout">关于</button><router-view></router-view><h2>我是APP中一些底部版权信息</h2></div><template><script>export default{name:'App',methods: {linkToHome() {this.$router.push('/home')}linkToAbout() {this.$router.push('/about')}}</script>
- 动态路由:
①在某些情况下,一个页面的path路径可能是不确定的,比如我们进入用户界面时,希望是如下的路径:/user/aaaa或/user/bbbb。除了有前面的/user之外,后面还跟上了用户的ID。这种path和Component的匹配关系,我们称之为动态路由(也是路由传递数据的一种方式)。
-----路由配置页面---{path: '/user/:id',component: User}----使用页面----<div> <h2>{{$route.params.id}}</h2> </div>----组件页面----<router-link to="/user/123">用户</router-link>
路由懒加载:
- 官方给出了解释:当打包构建应用时,Javascript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了
- 官方在说什么呢?首先, 我们知道路由中通常会定义很多不同的页面.这个页面最后被打包在哪里呢? 一般情况下, 是放在一个js文件中.但是, 页面这么多放在一个js文件中, 必然会造成这个页面非常的大.如果我们一次性从服务器请求下来这个页面, 可能需要花费一定的时间, 甚至用户的电脑上还出现了短暂空白的情况.
- 如何避免这种情况呢? 使用路由懒加载就可以了.路由懒加载做了什么?路由懒加载的主要作用就是将路由对应的组件打包成一个个的js代码块.只有在这个路由被访问到的时候, 才加载对应的组件。
- 懒加载的方式:
方式一: 结合Vue的异步组件和Webpack的代码分析.const Home = resolve => { require.ensure(['../components/Home.vue'], () => { resolve(require('../components/Home.vue')) })};方式二: AMD写法const About = resolve => require(['../components/About.vue'], resolve);方式三: 在ES6中, 我们可以有更加简单的写法来组织Vue异步组件和Webpack的代码分割.const Home = () => import('../components/Home.vue')
//使用懒加载const routes =[{path: '/home', component: () => import(' ../components/Home')},path: '/about', component: () => import('../components/About')},];
路由嵌套:
- 嵌套路由是一个很常见的功能。比如在home页面中, 我们希望通过/home/news和/home/message访问一些内容。一个路径映射一个组件, 访问这两个路径也会分别渲染两个组件。路径和组件的关系如下:
/home --------> Home/home/news --------> News/home/message --------> Message/about --------> About
- 实现嵌套路由有两个步骤:
①创建对应的子组件, 并且在路由映射中配置对应的子路由。
②在组件内部使用标签。
-----一、定义两个组件----------二、配置嵌套路由映射-----import Vue from 'vue'import VueRouter from 'vue-router'const Home = () => import('../components /Home')const Message = () => import('../components/message')const News = () => import('../components/news')const About = ( ) => import('../components/About')// 1.注入插件Vue.use (VueRouter)// 2.定义路由const routes = [{path: '/home',component: Home,children: [{path: 'message'component: Message},{path: 'news',component: News}]}]-----三、在父组件模板中使用-----<template><div id="home"><h2>我是首页标题</h2><router-link to="/home/message">消息</router-link><router-link to="/home/news">新闻<router-view></router-view></div></template>
- 嵌套路由也可以配置默认的路径, 配置方式如下:
{path: '',redirect:'message'}
路由传递参数:
- 传递参数主要有两种类型: params和query
- params的类型:
①配置路由格式: /router/:id
②传递的方式: 在path后面跟上对应的值
③传递后形成的路径: /router/123, /router/abc - query的类型:
①配置路由格式: /router, 也就是普通配置
②传递的方式: 对象中使用query的key作为传递方式
③传递后形成的路径: /router?id=123, /router?id=abc - 如何使用它们呢? 也有两种方式: 的方式和JavaScript代码方式
----传递参数方式一: <router-link>:----<router-link :to="{path: '/profile/' + 123,query: { name:'why',age: 18}}">档案</router-link>----传递参数方式二: JavaScript代码:----export default {name:'App',methods: {toProfile() {this.$router.push({path: '/profile/' + 123,query: {name: 'why',age: 18}})}}}
- 获取参数:
①获取参数通过$route对象获取的,在使用了vue-router 的应用中,路由对象会被注入每个组件中,赋值为 this.$route,并且当路由切换时,路由对象会被更新。
②通过$route获取传递的信息如下:
<p>params: {{ $route.params}}</p><p>query: {{$route.query}}</p>
- $route和$router是有区别的:
①$router为VueRouter实例,想要导航到不同URL,则使用$router.push方法
②$route为当前router跳转对象里面可以获取name、path、query、params等
路由导航守卫:
- 我们来考虑一个需求: 在一个SPA应用中, 如何改变网页的标题呢?
①网页标题是通过来显示的, 但是SPA只有一个固定的HTML, 切换不同的页面时, 标题并不会改变.
②但是我们可以通过JavaScript来修改的内容.window.document.title = ‘新的标题’.
③那么在Vue项目中, 在哪里修改? 什么时候修改比较合适呢? - 普通的修改方式:
①我们比较容易想到的修改标题的位置是每一个路由对应的组件.vue文件中.
②通过mounted声明周期函数, 执行对应的代码进行修改即可.
③但是当页面比较多时, 这种方式不容易维护(因为需要在多个页面执行类似的代码). - 有没有更好的办法呢? 使用导航守卫即可,什么是导航守卫?
①vue-router提供的导航守卫主要用来监听监听路由的进入和离开的.
②vue-router提供了beforeEach和afterEach的钩子函数, 它们会在路由即将改变前和改变后触发. - 详解:
①我们可以在钩子当中定义一些标题, 可以利用meta来定义。
②导航钩子的三个参数解析:
to: 即将要进入的目标的路由对象.
from: 当前导航即将要离开的路由对象.
next: 调用该方法后, 才能进入下一个钩子
// 定义路由const routes =[{path:"/home',component: Home,children: [...],meta:{title:'首页'}},{path: '/about',component: About,meta: {title:'关于'}},{path: '/profile/:id',component: Profile ,meta: {title:'档案'}}]//配置路由const router = new VueRouter ({routes,mode: 'history',linkActiveClass: 'active',})router.beforeEach( (to, from, next) => {window.document.title = to.meta.titlenext()})
- 补充:
①如果是后置钩子, 也就是afterEach, 不需要主动调用next()函数.
②上面我们使用的导航守卫, 被称之为全局守卫.
路由独享的守卫.
组件内的守卫.
路由keep-alive:
- keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。它们有两个非常重要的属性:
①include – 字符串或正则表达,只有匹配的组件会被缓存
②exclude – 字符串或正则表达式,任何匹配的组件都不会被缓存 - router-view 也是一个组件,如果直接被包在 keep-alive 里面,所有路径匹配到的视图组件都会被缓存:
<keep-alive><router-view><!--所有路径匹配到的视图组件都会被缓存! - - ></router-view></keep-alive>
- 通过create声明周期函数来验证
Promise
什么是Promise呢?
- Promise到底是做什么的呢?
①Promise是异步编程的一种解决方案。 - 那什么时候我们会来处理异步事件呢?
①一种很常见的场景应该就是网络请求了。
②我们封装一个网络请求的函数,因为不能立即拿到结果,所以不能像简单的3+4=7一样将结果返回。
③所以往往我们会传入另外一个函数,在数据请求成功时,将数据通过传入的函数回调出去。
④如果只是一个简单的网络请求,那么这种方案不会给我们带来很大的麻烦。 - 但是当网络请求非常复杂时就会出现回调地狱。
- 我们更加期望的是一种更加优雅的方式来进行这种异步操作。Promise可以以一种非常优雅的方式来解决这个问题。
Promise基本使用:
- new Promise很明显是创建一个Promise对象。小括号中((resolve, reject) => {})也很明显就是一个函数,而且我们这里用的是之前刚刚学习过的箭头函数。但是resolve, reject它们是什么呢?
- 我们先知道一个事实:在创建Promise时,传入的这个箭头函数是固定的(一般我们都会这样写)
resolve和reject它们两个也是函数,通常情况下,我们会根据请求数据的成功和失败来决定调用哪一个。 - 成功还是失败?
①如果是成功的,那么通常我们会调用resolve(messsage),这个时候,我们后续的then会被回调。
②如果是失败的,那么通常我们会调用reject(error),这个时候,我们后续的catch会被回调。
//普通方式setTimeout( function () {let data = 'Hello World'console. log (content) ;}, 1000)//Promise方式new Promise((resolve, reject) => {setTimeout( function () {resolve( 'Hello World')reject( 'Error Data')}, 1000)}).then(data => {console. log(data);}).catch(error => { console. log(error);})
Promise三种状态:
- 当我们开发中有异步操作时, 就可以给异步操作包装一个Promise 异步操作之后会有三种状态
①pending:等待状态,比如正在进行网络请求,或者定时器没有到时间。
②fulfill:满足状态,当我们主动回调了resolve时,就处于该状态,并且会回调.then()
③reject:拒绝状态,当我们主动回调了reject时,就处于该状态,并且会回调.catch()
Promise链式调用:
- 我们在看Promise的流程图时,发现无论是then还是catch都可以返回一个Promise对象。所以,我们的代码其实是可以进行链式调用的:
①这里我们直接通过Promise包装了一下新的数据,将Promise对象返回了
Promise.resovle():将数据包装成Promise对象,并且在内部回调resolve()函数
Promise.reject():将数据包装成Promise对象,并且在内部回调reject()函数
//链式调用的代码new Promise((resolve, reject) => {setTimeout(function () {resolve('Hello World')},1000 )}). then(data => {console. log(data); // => Hello Worldreturn Promise. resolve(data + '111')}). then(data => {console. log(data); // => Hello World111return Promise. resolve(data + '222')}). then(data => {console. log(data); // => Hello World111222return Promise.reject(data + 'error')}). then(data => {console. log(data); //这里没有输出,这部分代码不会执行return Promise. resolve(data + '333')}).catch(data => {console. log(data); // => Hello World111222errorreturn Promise. resolve(data + '444')}).then(data => {console. log(data); // => Hello World111222error444})
- 简化版代码:如果我们希望数据直接包装成Promise.resolve,那么在then中可以直接返回数据
注意下面的代码中,我将return Promise.resovle(data)改成了return data。结果依然是一样的。
//链式调用的简便写法new Promise((resolve, reject) => {setTimeout(function () {resolve( 'Hello World')},1000)}). then(data => {console. log(data); // => Hello Worldreturn data + '111 '}). then(data => {console. log(data); // => Hello World111return data + '222'}). then(data => {console. log(data); // => Hello World111222return Promise. reject(data + 'error')}). then(data => console. log(data); //这里没有输出,这部分代码不会执行return data + ' 333}).catch(data => {console. log(data); // => Hello World111222errorreturn data + ' 444}). then(data => {console. log(data); // => Hello World111222error444 })
如果希望返回的是reject方法,可以直接throw "字符串"。
Vuex
Vuex是做什么的” />
- 多界面状态管理:
①对于某些状态(状态1/状态2/状态3)来说只属于我们某一个试图,但是也有一些状态(状态a/状态b/状态c)属于多个试图共同想要维护的
状态1/状态2/状态3你放在自己的房间中,你自己管理自己用,没问题。
但是状态a/状态b/状态c我们希望交给一个大管家来统一帮助我们管理!!!
没错,Vuex就是为我们提供这个大管家的工具。
②全局单例模式(大管家)
我们现在要做的就是将共享的状态抽取出来,交给我们的大管家,统一进行管理。
之后,你们每个试图,按照我规定好的规定,进行访问和修改等操作。
这就是Vuex背后的基本思想。
Vuex基本使用:
- 首先,我们需要在某个地方存放我们的Vuex代码: 这里,我们先创建一个文件夹store,并且在其中创建一个index.js文件,在index.js文件中写入如下代码:
import Vuex from 'vuex'import Vue from 'vue'Vue.use(Vuex)const store = new Vuex.Store({state: {count: 0},mutations: {increment(state) {state.count++},decrement(state) {state.count--}}})export default store
- 其次,我们让所有的Vue组件都可以使用这个store对象 来到main.js文件,导入store对象,并且放在new Vue中这样,在其他Vue组件中,我们就可以通过this.$store的方式,获取到这个store对象了
import Vue from 'vue'import App from './App'import store from './store'new Vue({el: '#app',store,render: h => h(App) })
- 使用Vuex的count:
<template><div id="app"><p>{{count}}</p><button @click="increment">+1</button><button @click="decrement">-1</button></div></template><script>export default {name:" App',components: {},computed: {count: function () {return this. Sstore.state.count}},methods: {increment: function () {this.$store.commit('increment')},decrement: function () {this.$store.commit('decrement')}}}</script>
- 总结:
①小结:
提取出一个公共的store对象,用于保存在多个组件中共享的状态
将store对象放置在new Vue对象中,这样可以保证在所有的组件中都可以使用到
在其他组件中使用store对象中保存的状态即可
1、通过this.$store.state.属性的方式来访问状态
2、通过this.$store.commit(‘mutation中方法’)来修改状态
②注意事项:
我们通过提交mutation的方式,而非直接改变store.state.count。
这是因为Vuex可以更明确的追踪状态的变化,所以不要直接改变store.state.count的值。
Vuex核心概念:
- Vuex有几个比较核心的概念:
①State
②Getters
③Mutation
④Action
⑤Module
State简介:
- Vuex提出使用单一状态树, 什么是单一状态树呢?英文名称是Single Source of Truth,也可以翻译成单一数据源。
- 这个和我们在应用开发中比较类似:
①如果你的状态信息是保存到多个Store对象中的,那么之后的管理和维护等等都会变得特别困难。所以Vuex也使用了单一状态树来管理应用层级的全部状态。
②单一状态树能够让我们最直接的方式找到某个状态的片段,而且在之后的维护和调试过程中,也可以非常方便的管理和维护。
Getters简介:
- 有时候,我们需要从store中获取一些state变异后的状态,我们可以在Store中定义getters。
- getters默认是不能传递参数的, 如果希望传递参数, 那么只能让getters本身返回另一个函数。
Getters类似计算属性。
getters: {//直接获取store数据greaterAgesStus: state => {return state.students.filter(s => s.age >= 20)},//获取getters数据greaterAgesCount: (state, getters) => {return getters.greaterAgesstus.length},stuByID: state => {return id => {return state.students.find(s => s.id === id)}}}
Mutation简介:
- Vuex的store状态的更新唯一方式:提交Mutation
- Mutation主要包括两部分:
①字符串的事件类型(type)
②一个回调函数(handler),该回调函数的第一个参数就是state。
//multation的定义方式:mutations: {increment(state) {state.count++}}//通过mutation更新:increment: function () {this.$store.commit('increment')}
- 在通过mutation更新数据的时候, 有可能我们希望携带一些额外的参数:参数被称为是mutation的载荷(Payload)。
------单参数:-------//multation的定义方式:decrement(state,n) {state.count -= n}//通过mutation更新:decrement: function () {this.Sstore.commit('decrement',2)}------对象:-------//multation的定义方式:changeCount (state,payload) {state.count = payload.count}//通过mutation更新:changeCount: function () {this.$store.commit('changeCount', {count: 0})}
- Mutation提交风格:
①上面的通过commit进行提交是一种普通的方式。
②Vue还提供了另外一种风格, 它是一个包含type属性的对象:
//通过mutation更新:this.$store.commit ({type:'changeCount'count: 100})//Mutation中的处理方式是将整个commit的对象作为payload使用, 所以代码没有改变, 依然如下:changeCount (state,pay1oad) {state.count = payload.count}
- Mutation响应规则:
①Vuex的store中的state是响应式的, 当state中的数据发生改变时, Vue组件会自动更新.
②这就要求我们必须遵守一些Vuex对应的规则:
提前在store中初始化好所需的属性.
当给state中的对象添加新属性时, 使用下面的方式:
方式一: 使用Vue.set(obj, ‘newProp’, 123)
方式二: 用心对象给旧对象重新赋值 - Mutation常量类型:
①在mutation中, 我们定义了很多事件类型(也就是其中的方法名称),当我们的项目增大时, Vuex管理的状态越来越多, 需要更新状态的情况越来越多, 那么意味着Mutation中的方法越来越多,方法过多, 使用者需要花费大量的经历去记住这些方法, 甚至是多个文件间来回切换, 查看方法名称, 甚至如果不是复制的时候, 可能还会出现写错的情况。
②在各种Flux实现中, 一种很常见的方案就是使用常量替代Mutation事件的类型,我们可以将这些常量放在一个单独的文件中, 方便管理以及让整个app所有的事件类型一目了然。
③我们可以创建一个文件: mutation-types.js, 并且在其中定义我们的常量,定义常量时, 我们可以使用ES2015中的风格, 使用一个常量来作为函数的名称。
文件名:mutation-typesjs代码:export const UPDATE_ INFO = 'UPDATE_INFO'
- Mutation同步函数:
①通常情况下, Vuex要求我们Mutation中的方法必须是同步方法,主要的原因是当我们使用devtools时, 可以devtools可以帮助我们捕捉mutation的快照。
②但是如果是异步操作, 那么devtools将不能很好的追踪这个操作什么时候会被完成。所以通常情况下, 不要再mutation中进行异步的操作。
Action简介:
- 我们强调, 不要再Mutation中进行异步操作. 但是某些情况, 我们确实希望在Vuex中进行一些异步操作, 比如网络请求,必然是异步的. 这个时候怎么处理呢” />\\定义:const store = new Vuex.Store({state: {count: 0},mutations: {increment(state) {state.count++}},actions: {increment (context) {context.commit(‘increment’)}}})\\使用:methods: {increment() {this.$store.dispatch(‘increment’) }}
Module简介:
- Module是模块的意思, 为什么在Vuex中我们要使用模块呢?
①Vue使用单一状态树,那么也意味着很多状态都会交给Vuex来管理,当应用变得非常复杂时,store对象就有可能变得相当臃肿。
②为了解决这个问题, Vuex允许我们将store分割成模块(Module), 而每个模块拥有自己的state、mutation、action、getters等。
- 注意:
①虽然, 我们的getters
和mutation
都是定义在对象内部的,但是在调用的时候, 依然是通过this.$store来直接调用的。
②actions:
局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState
如果getters中也需要使用全局的状态, 可以接受更多的参数
const moduleA = {//...actions: {incrementIfoddonRootSum ({state,commit,rootState}) {if ((state.count + rootState.count) %2 ===1) {commit('increment')}}}}
- 代码结构:
const moduleA = {state: { ... },mutations: { ... },actions: { ... },getters: { ... }}const moduleB = {state: { ... },mutations: { ... },actions: { ... }}const store = new Vuex.Store({modules: {a: modu1eA,b: modu1eB}})store.state.a // -> moduleA的状态store.state.b // -> moduleB的状态
项目结构:
- 当我们的Vuex帮助我们管理过多的内容时, 好的项目结构可以让我们的代码更加清晰:
axios
选择什么网络模块” />
- Module是模块的意思, 为什么在Vuex中我们要使用模块呢?
为什么选择axios?
- 功能特点:
①在浏览器中发送 XMLHttpRequests 请求
②在 node.js 中发送 http请求
③支持 Promise API
④拦截请求和响应
⑤转换请求和响应数据
axiox请求方式:
- 支持多种请求方式:
①axios(config)
②axios.request(config)
③axios.get(url[, config])
④axios.delete(url[, config])
⑤axios.head(url[, config])
⑥axios.post(url[, data[, config]])
⑦axios.put(url[, data[, config]])
⑧axios.patch(url[, data[, config]]) - 发送get请求演示:
import axios from 'axios'export default {name : 'app',created() {//提问:为什么我这里没有跨域的问题?//1.没有请求参数axios.get( 'http://123.207.32.32:8000/category').then(res => {console.log(res);}).catch(err =>{console.log(err);})//2.有请求参数axios.get( 'http:/ /123.207.32.32:8000/home/data',{params: itype: 'sell', page: 1}}).then(res => {console.log ( res);}).catch(err => {console.log(err);})}}
- 发送并发请求:
①有时候, 我们可能需求同时发送两个请求
②使用axios.all, 可以放入多个请求的数组.
③axios.all([]) 返回的结果是一个数组,使用 axios.spread 可将数组 [res1,res2] 展开为 res1, res2
import axios from 'axios'export default {name : 'app',created () {[//发送并发请求axios.all([axios.get('http://123.207.32.32:8000/category'),axios.get('http://123.207.32.32:8000/home/data',{params : {type: 'sell', page: 1}})]).then(axios.spread((res1,res2) => {console.log(res1);console.log(res2);}))}
全局配置:
- 在上面的示例中, 我们的BaseURL是固定的
①事实上, 在开发中可能很多参数都是固定的.
②这个时候我们可以进行一些抽取, 也可以利用axiox的全局配置
created( {//提取全局的配置axios.defaults.baseURL = 'http://123.207.32.32:8000'//发送并发请求axios.all([axios.get('/category'),axios.get('/home/data',{params: {type: 'sell', page:1}})]).then(axios.spread((res1,res2) =>{console.log (res1);console.log(res2);}))}
- 常见的配置选项:
请求地址url: '/user',请求类型method: 'get',请根路径baseURL: 'http://www.mt.com/api',请求前的数据处理transformRequest:[function(data){}],请求后的数据处理transformResponse: [function(data){}],自定义的请求头headers:{'x-Requested-With':'XMLHttpRequest'},URL查询对象params:{ id: 12 },查询对象序列化函数paramsSerializer: function(params){ }request bodydata: { key: 'aa'},超时设置stimeout: 1000,跨域是否带TokenwithCredentials: false,自定义请求处理adapter: function(resolve, reject, config){},身份验证信息auth: { uname: '', pwd: '12'},响应的数据格式 json / blob /document /arraybuffer / text / streamresponseType: 'json',
axios的实例:
- 为什么要创建axios的实例呢?
①当我们从axios模块中导入对象时, 使用的实例是默认的实例.
②当给该实例设置一些默认配置时, 这些配置就被固定下来了.
③但是后续开发中, 某些配置可能会不太一样.
④比如某些请求需要使用特定的baseURL或者timeout或者content-Type等.
⑤这个时候, 我们就可以创建新的实例, 并且传入属于该实例的配置信息.
//创建新的实例const axiosInstance = axios.create({baseURL: 'http:/ /123.207.32.32:8000',timeout: 5000,headers: {'Content-Type' : 'application/x-www-form-urlencoded '}})//发送网络请求axiosInstance({url: '/category',method: 'get'}).then(res => {console. log(res);}).catch(err => {console. log(err);})
- axios封装:
import originAxios from 'axios'export default function axios(option) {return new Promise((resolve, reject) =>{//1.创建axios的实例const instance = originAxios.create({baseURL: '/api',timeout: 5000,headers:''});//2.传入对象进行网络请求instance(option).then(res => {resolve(res)}).catch(err => {reject(err)})})}
axios拦截器:
- axios提供了拦截器,用于我们在发送每次请求或者得到相应后,进行对应的处理。
- 如何使用拦截器呢?
//配置请求和响应拦截instance.interceptors.request.use(config =>{console.log('来到了request拦截success中');return config}, err => {console.log('来到了request拦截failure中');return err}instance.interceptors.response.use(response =>{console.log('来到了response拦截success中');return response.data}, err =>{console.log('来到了response拦截failure中');return err})
- 拦截器中都做什么呢?
①请求拦截中错误拦截较少,通常都是配置相关的拦截。可能的错误比如请求超时,可以将页面跳转到一个错误页面中。
②响应拦截中完成的事情:
响应的成功拦截中,主要是对数据进行过滤。
响应的失败拦截中,可以根据status判断报错的错误码,跳转到不同的错误提示页面。
//请求拦截instance.interceptors.request.use(config =>{console.log('来到了request拦截success中');//1.当发送网络请求时,在页面中添加一个loading组件,作为动画//2.某些请求要求用户必须登录,判断用户是否有token,如果没有token跳转到login页面//3.对请求的参数进行序列化config.data = qs.stringify (config.data)console.log(config);//4.等等return config})//响应的成功拦截instance.interceptors.response.use(response => {console.log('来到了response拦截success中');return response.data})//响应的失败拦截},err =>{console.log('来到了response拦截failure中');if (err && err.response) {switch (err.response.status) {case 400:err.message ='请求错误'breakcase 401:err.message ='未授权的访问'break}}return err
Vue开发总结
VSCode开发简介:
- 我们在html文件上输入【!】叹号(在英文状态下),然后按键盘的【Tab】键,只有在这种情况下按住Tab键可以快速创建一个html模板。
- 代码缩进时只缩进两个空格。
- 开发快捷键:
①Ctrl + p:查找文件
②Ctrl + shift + f:全局搜索字符串
③Ctrl+j:切换终端
④Ctrl + b:显示/隐藏左侧目录栏
⑤Ctrl + shift + p,F1:显示所有命令
⑥拆分当前编辑窗口:Ctrl + \ ,Ctrl +1 2 3 4 等 在编辑窗口间切换
⑦选中方法名->Shift + F12,选中方法名->鼠标右键:显示引用 Show References - 链接:VSCode Vue开发推荐插件和VSCode快捷键(小结)
- 插件:
①Volar 是一个 vscode 的插件,volar提供了非常卧槽的功能。
Vue开发简介:
- src目录分析:
①apis:请求后台接口文件
②assets:资源文件,比如存放 css,图片等资源。
③component:组件文件夹,用来存放 vue 的公共组件(注册于全局,在整个项目中通过关键词便可直接输出)
④router:用来存放 index.js,这个 js 用来配置路由
⑤views:用来放主体页面,虽然和组件文件夹都是 vue 文件,但 views 下的 vue 文件是可以用来充当路由 view 的。
⑥store:Vuex状态管理文件
⑦tool/utils:用来存放工具类 js,将 js 代码封装好放入这个文件夹可以全局调用(比如常见的 api.js,http.js 是对 http 方法和 api 方法的封装)。
⑧request:http工具类,可以放在tool/utils中,也可以自定义一个文件夹。