组件封装思想
当没有封装复用思维时,若想做一个tabbar导航栏
TabBar.vue
<!-- 手机导航栏 -->
<template>
<div id="tab-bar">
<!-- 一般一个项目只有一个tabbar导航条,所以可以直接用id作一个标识 -->
<div class="tabbar-item">首页</div>
<div class="tabbar-item">推荐</div>
<div class="tabbar-item">购物车</div>
<div class="tabbar-item">我的</div>
</div>
</template>
<script>
export default {
name: 'TabBar',
data() {
return {
}
}
}
</script>
<style>
@import '../../assets/css/base.css';
/* 可以改为引用@import
也可以直接在main.js中使用require('./assets/css/base.css')
body{
padding:0px;
margin:0px;
} */
#tab-bar{
/* 水平展示,flex布局 */
display:flex;
background-color:rgb(244, 243, 245);
/* 一般用f6,且bgc加回车可以简写background-color */
/* background-color: #f6f6f6; */
/* position:fixed能够固定布局,设置left,bottem等属性 */
position:fixed;
/* 设置left,right属性可以保证占据屏幕整个宽度 */
left:0;
right:0;
bottom:0;
/* 设置阴影 */
/* 参数:x方向向右偏移量,y向下偏移量,模糊程度,颜色(最后的.15表示0.15的透明度) */
box-shadow:0 -1px 5px rgba(100,100,100,.15)
}
.tabbar-item{
/* flex:1px可以使flex布局均等分 */
flex:1px;
/* text-align让文字居中展示 */
text-align:center;
/* 一般tabbar的高度都是49 */
height:49px;
}
</style>
App.vue中引用
<template>
<div id="app">
<tabbar></tabbar>
</div>
</template>
<script>
import tabbar from './components/tabbar/TabBar'
export default {
name: 'App',
components:{
tabbar
}
}
</script>
<style>
</style>
开始封装:
将tabbar抽取成一个组件TabBar.vue
将TabBar抽取成多个TabBarItem.vue
App.vue
<template>
<div id="app">
<router-view></router-view>
<tabbar>
<!-- 动态传入样式 -->
<tabbar-item path="/home" activeColor="green">
<img slot="item-icon" src="./assets/img/tabbar/home.svg" alt="">
<img slot="item-icon-active" src="./assets/img/tabbar/home_active.svg" alt="">
<div slot="item-text">首页</div>
</tabbar-item>
<tabbar-item path="/category" activeColor="green">
<img slot="item-icon" src="./assets/img/tabbar/category.svg" alt="">
<img slot="item-icon-active" src="./assets/img/tabbar/category_active.svg" alt="">
<div slot="item-text">分类</div>
</tabbar-item>
<tabbar-item path="/shopcar" activeColor="green">
<img slot="item-icon" src="./assets/img/tabbar/shop_car.svg" alt="">
<img slot="item-icon-active" src="./assets/img/tabbar/shop_car_active.svg" alt="">
<div slot="item-text">购物车</div>
</tabbar-item>
<tabbar-item path="/profile" activeColor="green">
<img slot="item-icon" src="./assets/img/tabbar/my.svg" alt="">
<img slot="item-icon-active" src="./assets/img/tabbar/my_active.svg" alt="">
<div slot="item-text">我的</div>
</tabbar-item>
</tabbar>
</div>
</template>
<script>
import tabbar from './components/tabbar/TabBar'
import tabbarItem from './components/tabbar/TabBarItem'
export default {
name: 'App',
components:{
tabbar,
tabbarItem
}
}
</script>
<style>
</style>
TabBar.vue
<!-- 手机导航栏 -->
<template>
<div id="tab-bar">
<!-- 一般一个项目只有一个tabbar导航条,所以可以直接用id作一个标识 -->
<slot></slot>
</div>
</template>
<script>
export default {
name: 'TabBar',
data() {
return {
}
}
}
</script>
<style>
@import '../../assets/css/base.css';
/* 可以改为引用@import
也可以直接在main.js中使用require('./assets/css/base.css')
body{
padding:0px;
margin:0px;
} */
#tab-bar{
/* 水平展示,flex布局 */
display:flex;
background-color:rgb(244, 243, 245);
/* 一般用f6,且bgc加回车可以简写background-color */
/* background-color: #f6f6f6; */
/* position:fixed能够固定布局,设置left,bottem等属性 */
position:fixed;
/* 设置left,right属性可以保证占据屏幕整个宽度 */
left:0;
right:0;
bottom:0;
/* 设置阴影 */
/* 参数:x方向向右偏移量,y向下偏移量,模糊程度,颜色(最后的.15表示0.15的透明度) */
box-shadow:0 -1px 5px rgba(100,100,100,.15)
}
</style>
TabBarItem.vue
<!-- -->
<template>
<div class="tabbar-item" @click="tabbarItemClick">
<!-- 由于插槽最终是要被替换的,所以一般在外面包一个div,直接对div进行控制 -->
<div v-if="!isActive"><slot name="item-icon"></slot></div>
<div v-else><slot name="item-icon-active"></slot></div>
<!-- <div :class="{active:isActive}"><slot name="item-text"></slot></div> -->
<div :style="activeStyle"><slot name="item-text"></slot></div>
</div>
</template>
<script>
export default {
name: 'TabBarItem',
props:{
path:String,
activeColor:{
type:String,
default:'red'
}
},
data() {
return {
// isActive:false
}
},
computed:{
isActive(){
// indexOf()方法可返回某个指定的字符串值在字符串中首次出现的位置。
// 取到path与当前活跃的path比较,如果未找到就===-1,返回false,相反返回true
return this.$route.path.indexOf(this.path)!==-1
},
activeStyle(){
return this.isActive ? {color:this.activeColor} : {}
}
},
methods:{
tabbarItemClick(){
// this.isActive = !this.isActive
this.$router.push(this.path)
}
}
}
</script>
<style>
.tabbar-item{
/* flex:1px可以使flex布局均等分 */
flex:1px;
/* text-align让文字居中展示 */
text-align:center;
/* 一般tabbar的高度都是49 */
height:49px;
font-size:14px;
}
.tabbar-item img{
width:24px;
height:24px;
margin-top:3px;
vertical-align: middle;
margin-bottom: 3px;
}
/*为了能动态赋予样式,需要封装
.active{
color:yellow;
} */
</style>
还可以进一步对App.vue中的代码进一步抽取
在components文件夹下新建一个文件MainTabBar.vue
MainTabBar.vue
<!-- -->
<template>
<div>
<tabbar>
<!-- 动态传入样式 -->
<tabbar-item path="/home" activeColor="green">
<img slot="item-icon" src="../../assets/img/tabbar/home.svg" alt="">
<img slot="item-icon-active" src="../../assets/img/tabbar/home_active.svg" alt="">
<div slot="item-text">首页</div>
</tabbar-item>
<tabbar-item path="/category" activeColor="green">
<img slot="item-icon" src="../../assets/img/tabbar/category.svg" alt="">
<img slot="item-icon-active" src="../../assets/img/tabbar/category_active.svg" alt="">
<div slot="item-text">分类</div>
</tabbar-item>
<tabbar-item path="/shopcar" activeColor="green">
<img slot="item-icon" src="../../assets/img/tabbar/shop_car.svg" alt="">
<img slot="item-icon-active" src="../../assets/img/tabbar/shop_car_active.svg" alt="">
<div slot="item-text">购物车</div>
</tabbar-item>
<tabbar-item path="/profile" activeColor="green">
<img slot="item-icon" src="../../assets/img/tabbar/my.svg" alt="">
<img slot="item-icon-active" src="../../assets/img/tabbar/my_active.svg" alt="">
<div slot="item-text">我的</div>
</tabbar-item>
</tabbar>
</div>
</template>
<script>
import tabbar from '../tabbar/TabBar'
import tabbarItem from '../tabbar/TabBarItem'
export default {
name: '',
data() {
return {
}
},
components:{
tabbar,
tabbarItem
}
}
</script>
<style>
</style>
App.vue
<template>
<div id="app">
<router-view></router-view>
<main-tab-bar></main-tab-bar>
</div>
</template>
<script>
import MainTabBar from "./components/tabbar/MainTabBar"
export default {
name: 'App',
components:{
MainTabBar
}
}
</script>
<style>
</style>
tabbar的文件路径的引用问题
有时候,查找文件路径需要写很多…/…/…/有可能还找不到正确路径
这时,就可以设置一下路径别名
找到build/webpack.base.conf.js
文件进行如下配置
// 专门解决路径相关问题
resolve: {
// 导入文件时可以省略'.js','.vue','.json'
extensions: ['.js', '.vue', '.json'],
// alias表示别名,通过'@'符号可以找到'src'所在路径
alias: {
'@': resolve('src'),
'assets':resolve('src/assets'),
'components':resolve('src/components'),
'views':resolve('src/views')
}
},
引用实例
MainTabBar.vue
import tabbar from 'components/tabbar/TabBar'
import tabbarItem from '@/components/tabbar/TabBarItem'
但是对于不是import
导入的文件,例如img
元素中的属性src
,直接写@或其他别名,不会起作用,会报错无法找到文件,需要在前面添加一个波浪符号~
MainTabBar.vue中
不加~
符号
<tabbar-item path="/category" activeColor="green">
<img slot="item-icon" src="@/assets/img/tabbar/category.svg" alt="">
<img slot="item-icon-active" src="assets/img/tabbar/category_active.svg" alt="">
<div slot="item-text">分类</div>
</tabbar-item>
报错:
更正:
<tabbar-item path="/category" activeColor="green">
<img slot="item-icon" src="~assets/img/tabbar/category.svg" alt="">
<img slot="item-icon-active" src="~assets/img/tabbar/category_active.svg" alt="">
<div slot="item-text">分类</div>
</tabbar-item>
Promise的使用
一般在什么时候会使用promise呢?
一般在需要进行网络请求时,由于同步容易造成网络阻塞等问题,通常会开启一个异步任务,进行异步操作,最简单的一种异步是回调callback,
但存在*
高耦合,维护困难,回调地狱;每个任务只能指定一个回调函数;如果几个异步操作之间并没有顺序之分,同样也要等待上一个操作执行结束再进行下一个操作。
等问题,且容易产生回调地狱问题,因此,我们更偏向于选择promise来进行异步操作。
(
这里代码如果暂时看不懂可以先看后面的几个问题的代码
)
// 什么情况下会用到promise
// 一旦有异步操作时,就可以将其塞到promise里,异步请求后调用一下resolve方法,别人
// 就可以在then方法中进行操作
// 简单来说,即一般情况下,当有异步操作时,使用promise对这个即将进行的异步操作进行封装
//其中流程:new的时候,类似于一个构造函数
// (1、保存了一些状态信息 2、执行传入的函数)
// 并且例如从网络请求中传过来一些数据时,不要直接在异步请求(如setTimeout函数中)进行操作
// 而是通过在异步请求中调用resolve()或reject()函数,通过这两个参数函数,数据会自动传递到
// 后面的.then()函数或.catch()函数中,你可以在then()/catch()函数中操作传过来的数据,简洁明了
new Promise((resolve,reject) => {
setTimeout(() => {
// 成功的时候调用resolve,表示解决,进入.then()表示下一步
// resolve('Hello')
// 失败的时候调用reject,表示拒绝,进入.catch()表示捕获,捕获异常等
reject('error message')
},1000)
}).then((data) => { //这里的data就是resolve中传过来的参数,有时setTimeout中含有参数data,
// 这里将打印Hello字符串,因为从resolve传入的是Hello
console.log(data) //也可以通过resolve(data),数据会自动作为传入函数then的参数.then(data)
}).catch(err => {
// 这里打印error message字符串,因为从reject传入的是字符串'error message'
console.log(err)
})
// 总而言之,promise是为了写代码更优雅
什么是Promise?
Promise是JS对象,它们用于表示一个异步操作的最终完成 (或失败), 及其结果值.您可以通过使用回调方法或使用Promise执行异步操作来获得结果。
Promise是把类似的异步处理对象和处理规则进行规范化, 并按照采用统一的接口来编写。是抽象异步处理对象以及对其进行各种操作的组件。
简单来说,也可以看做是对一些异步操作进行的封装
什么是回调地狱
类似嵌套回调,即回调函数里又需要进行回调
用setTimeout定时器先来模拟一个异步操作
<script>
// 使用setTimeout进行异步操作,1000ms之后会回调setTimeout函数
// alt+shift+向下或向上箭头可向下/上复制
// setTimeout(() => {
// console.log('hello world')
// console.log('hello world')
// console.log('hello world')
// console.log('hello world')
// },1000)
// 使用promise类进行封装
// 参数是函数(resolve,reject),这两个函数的参数也是函数
// 延迟1S打印helloworld ,再延迟1s打印hellovue.js,再延迟1s打印hello
// 这样的操作就陷入了回调地狱
new Promise((resolve,reject) => {
setTimeout(() => {
console.log('hello world')
console.log('hello world')
console.log('hello world')
console.log('hello world')
setTimeout(() => {
console.log('hello vue.js')
console.log('hello vue.js')
console.log('hello vue.js')
console.log('hello vue.js')
setTimeout(() => {
console.log('hello')
console.log('hello')
console.log('hello')
console.log('hello')
},1000)
},1000)
},1000)
})
</script>
// 为了避免上述这种回调地狱,需要将里面的东西给抽取出来
// 内部调用resolve()函数,当调用该函数时,函数会去到.then中去执行,then的参数也是一个函数
// new Promise((resolve,reject) => {
// setTimeout(() =>{
// resolve()
// },1000)
// }).then(() => {
// console.log('hello world')
// console.log('hello world')
// console.log('hello world')
// console.log('hello world')
// // 通过返回一个Promise值
// return new Promise((resolve,reject) => {
// setTimeout(() => {
// resolve()
// },1000)
// }).then(() => {
// console.log('hello vue.js')
// console.log('hello vue.js')
// console.log('hello vue.js')
// console.log('hello vue.js')
// return new Promise((resolve,reject) => {
// setTimeout(() => {
// resolve()
// },1000)
// }).then(() => {
// console.log('hello')
// console.log('hello')
// console.log('hello')
// console.log('hello')
// })
// })
// })
// 注意,应该是这种形式,
// 即new Promise().then( return new Promise()) .then( return new Promise()).then()
// 上面的那种形式变成了then嵌套了,虽然同样能正确展示,应该也是变成回调地狱的一种了????
new Promise((resolve,reject) => {
setTimeout(() =>{
resolve()
},1000)
}).then(() => {
console.log('hello world')
console.log('hello world')
console.log('hello world')
console.log('hello world')
// 通过返回一个Promise值
return new Promise((resolve,reject) => {
setTimeout(() => {
resolve()
},1000)
})
}).then(() => {
console.log('hello vue.js')
console.log('hello vue.js')
console.log('hello vue.js')
console.log('hello vue.js')
return new Promise((resolve,reject) => {
setTimeout(() => {
resolve()
},1000)
})
}).then(() => {
console.log('hello')
console.log('hello')
console.log('hello')
console.log('hello')
},1000)
</script>
任何需要等promise的行为放在".then"里面
// 什么情况下会用到promise
// 一旦有异步操作时,就可以将其塞到promise里,异步请求后调用一下resolve方法,别人
// 就可以在then方法中进行操作
// 简单来说,即一般情况下,当有异步操作时,使用promise对这个即将进行的异步操作进行封装
//其中流程:new的时候,类似于一个构造函数
// (1、保存了一些状态信息 2、执行传入的函数)
// 并且例如从网络请求中传过来一些数据时,不要直接在异步请求(如setTimeout函数中)进行操作
// 而是通过在异步请求中调用resolve()或reject()函数,通过这两个参数函数,数据会自动传递到
// 后面的.then()函数或.catch()函数中,你可以在then()/catch()函数中操作传过来的数据,简洁明了
new Promise((resolve,reject) => {
setTimeout(() => {
// 成功的时候调用resolve,表示解决,进入.then()表示下一步
// resolve('Hello')
// 失败的时候调用reject,表示拒绝,进入.catch()表示捕获,捕获异常等
reject('error message')
},1000)
}).then((data) => { //这里的data就是resolve中传过来的参数,有时setTimeout中含有参数data,
// 这里将打印Hello字符串,因为从resolve传入的是Hello
console.log(data) //也可以通过resolve(data),数据会自动作为传入函数then的参数.then(data)
}).catch(err => {
// 这里打印error message字符串,因为从reject传入的是字符串'error message'
console.log(err)
})
// 总而言之,promise是为了写代码更优雅
这里有两篇,其中这篇暂时没找到与作者联系的方式
https://juejin.im/post/5d5120e1f265da03be48ccad
https://zhuanlan.zhihu.com/p/24684803
Promise 的三种状态
sync(Synchronization同步)
async(异步)
三种状态:
- pending:等待状态,此时执行promise封装的内部定时器等
- fulfill:满足状态,当回调了resolve时,就表示处于该状态,并且回调.then()进入成功的下一步
- reject:拒绝状态,当回调了reject时,就表示处于该状态,并且回调.catch()捕获失败和拒绝
Promise的链式调用
<!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>
<script>
// 需求:
// 网络请求获得数据:aaa ->先自己处理
// 第一层处理:aaa111(要求在数据后面跟上111) ->自己处理然后提交下一步处理
// 第二层处理:aaa111222 ->自己处理
// new Promise((resolve,reject) => {
// setTimeout(() => {
// resolve('aaa')
// },1000)
// }).then(res => {
// // 1、第一次自己处理,res是resolve的简写,表示resolve传入的参数
// console.log(res,'第一层的自己处理代码')
// // 2、对结果第一次处理,这里的reject没有用到,所以就省略了
// return new Promise((resolve) => {
// resolve(res + '111')
// })
// }).then(res => {
// console.log(res,'第二层的自己处理代码')
// return new Promise(resolve => {
// resolve(res + '222')
// })
// }).then(res => {
// console.log(res, '第三层的自己处理代码')
// })
// 简写一:直接return Promise.resolve()
// new Promise((resolve,reject) => {
// setTimeout(() => {
// resolve('aaa')
// },1000)
// }).then(res => {
// console.log(res,'第一层的自己处理代码')
// return Promise.resolve(res + '111')
// }).then(res => {
// console.log(res,'第二层的自己处理代码')
// return Promise.resolve(res + '222')
// }).then(res => {
// console.log(res, '第三层的自己处理代码')
// })
// 简写二:省略掉Promise.resolve,直接写resolve中的参数作为返回值,其会自动将其封装在Promise中,
// 并调用resolve方法
// new Promise((resolve,reject) => {
// setTimeout(() => {
// resolve('aaa')
// },1000)
// }).then(res => {
// console.log(res,'第一层的自己处理代码')
// return res + '111'
// }).then(res => {
// console.log(res,'第二层的自己处理代码')
// return res + '222'
// }).then(res => {
// console.log(res, '第三层的自己处理代码')
// })
// 不一定每次都是resolve,当出现reject时,将不再执行.then,而是直接跳转到.catch执行
// new Promise((resolve,reject) => {
// setTimeout(() => {
// resolve('aaa')
// },1000)
// }).then(res => {
// console.log(res,'第一层的自己处理代码')
// return Promise.reject('errrr')
// }).then(res => {
// console.log(res,'第二层的自己处理代码')
// return res + '222'
// }).then(res => {
// console.log(res, '第三层的自己处理代码')
// }).catch(err => {
// console.log(err)
// })
// 简写四:可以通过throw直接手动抛出异常,进入catch()
new Promise((resolve,reject) => {
setTimeout(() => {
resolve('aaa')
},1000)
}).then(res => {
console.log(res,'第一层的自己处理代码')
throw 'errrr message'
}).then(res => {
console.log(res,'第二层的自己处理代码')
return res + '222'
}).then(res => {
console.log(res, '第三层的自己处理代码')
}).catch(err => {
console.log(err)
})
</script>
</body>
</html>
promise的all方法
<!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>
<script>
// 当遇到需要多个异步请求时,例如,需要获取到两次异步请求的结果后才能进行相应的操作,这时就可以
// 使用promise的all方法来处理
// 因为没有安装jQuery,无法使用$ajax,这里我使用setTimeout来模拟异步请求
// promise.all(iterator迭代,即可遍历的)
// 当两个异步请求的结果都成功获取后,就会自动调用.all([])的.then(),传入一个数组名为results
Promise.all([
new Promise((resolve,reject) => {
setTimeout(() =>{
resolve({name:'konan',age:17})
},1000)
}),
new Promise((resolve,reject) => {
setTimeout(() => {
resolve({first:'ittawa',hobby:'football'})
})
})
]).then(results => {
console.log(results[0])
console.log(results[1])
console.log(results)
})
</script>
</body>
</html>
Vuex
你可以想象这样一个场景:
有十多个组件想要共享同一个状态(这里可以先暂时理解为变量,因为变量可以保存状态信息等),如果放到一个组件中的话,其他组件访问可能需要跨越很多其他组件,历经千辛才能获取使用,这样就会造成很多不方便,这时如果能有一个中转站能来统一管理就好了,于是就有了vuex,vuex作为一个状态管理模式,就像一座大宅邸里的一个大管家一样,将少爷主子们(组件)进行集中式管理,并实时管理着一些状态(或变量等),有谁需要,谁就可以直接去大管家那里拿来用,除此之外,vuex还可以实现响应式,即组件不仅可以访问状态或变量,还可以进行更改,并且因为vuex的管理是响应式的,一旦数据更改,界面会实时跟着刷新。
<!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>
<!-- 你可以想象这样一个场景:
有十多个组件想要共享同一个状态(这里可以先暂时理解为变量,因为变
量可以保存状态信息等),如果放到一个组件中的话,其他组件访问可能
需要跨越很多其他组件,历经千辛才能获取使用,这样就会造成很多不方便,
这时如果能有一个中转站能来统一管理就好了,于是就有了vuex,vuex作为一个
状态管理模式,就像一座大宅邸里的一个大管家一样,将少爷主子们(组件)进行
集中式管理,并实时管理着一些状态(或变量等),有谁需要,谁就可以直接去大
管家那里拿来用,除此之外,vuex还可以实现响应式,即组件不仅可以访问状态或
变量,还可以进行更改,并且因为vuex的管理是响应式的,一旦数据更改,界面会
实时跟着刷新。 -->
<script>
const shareObj = {
name:'konan'
}
// 所有组件都自动继承vue的原型,利用prototype原型添加一个共享对象
Vue.prototype.shareObj = shareObj
// 但是当shareObj中的内容变化时,组件中的数据不会跟着改变,这样就做不到响应式
Vue.component('cpn1',{
template:'',
})
Vue.component('cpn2',{
})
const app = new Vue({
el:'#app',
data:{
}
})
</script>
</body>
</html>
我们这样做可以实现状态的管理,但这样不会实现响应式,若实现了响应式,这样相当于重复造了一个vuex的轮子
一般父子组件之间共享状态或变量信息直接使用父子组件通信
当多个页面,甚至几乎所有组件页面都需要使用时,才使用vuex
vuex的使用
vuex是一个插件,所以需要安装,部署时仍然需要它来管理我们的状态信息,所以不是开发时依赖,是生产运行时依赖
进入文件夹,在终端输入
npm install --save vuex
等待下载
安装
可以在main.js中直接vue.use安装,但这样的话main.js中的代码会变得杂乱,因此,我们在src文件夹下新建一个文件夹,通常起名为store(仓库),store下新建文件index.js
src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
// 1、安装插件
Vue.use(Vuex)
// 2、创建对象
// 注意store要小写
const store = new Vuex.Store({
state:{
},
mutations:{
},
actions:{
},
getters:{
},
modules:{
}
})
// 3、导出store独享
// 4、main.js中挂载store
回到main.js中进行挂载
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
Vue.config.productionTip = false
// 挂载后相当于添加了语句Vue.prototype.$store = store,以后就可以全局使用$store
/* eslint-disable no-new */
new Vue({
el: '#app',
store,
router,
render: h => h(App)
})
使用
简单案例
$store.state.counter
store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
// 1、安装插件
Vue.use(Vuex)
// 2、创建对象
// 注意store要小写
const store = new Vuex.Store({
state:{
// 状态
counter:0
},
mutations:{
// 方法,默认会传入state
increment(state){
store.state.counter++
},
decrement(state){
store.state.counter--
}
},
actions:{
// 异步请求
},
getters:{
},
modules:{
}
})
// 3、导出store独享
export default store
// 4、main.js中挂载store
main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
Vue.config.productionTip = false
// 挂载后相当于添加了语句Vue.prototype.$store = store,以后就可以全局使用$store
/* eslint-disable no-new */
new Vue({
el: '#app',
store,
router,
render: h => h(App)
})
Hellovuex.vue
<template>
<div class="hello">
<h2>HelloVuex中的counter</h2>
<h1>{{$store.state.counter}}</h1>
<button @click="add">+</button>
<button @click="sub">-</button>
</div>
</template>
<script>
export default {
name: 'HelloVuex',
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
},
methods:{
add(){
return this.$store.commit('increment')
},
sub(){
return this.$store.commit('decrement')
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
App.vue
<template>
<div id="app">
<h2>App中的counter{{$store.state.counter}}</h2>
<router-view/>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
</style>
可以看到,即使组件内部没有定义变量,没有进行父子通讯也可以使用counter变量及其他方法
单一状态树
可以有多个store来管理我们的信息,状态等,index.js中const store1,const store2,const store3等等,但是目前不必要,为了便于管理,我们只用一个store来存储所有的状态信息,需要用时,只需找$store
这一个就行
vuex的getters使用
store/index.js
// 2、创建对象
// 注意store要小写
const store = new Vuex.Store({
state:{
// 状态
counter:0,
students:[
{id:110,name:'one',age:15},
{id:111,name:'two',age:20},
{id:112,name:'three',age:10},
{id:113,name:'four',age:32},
]
},
mutations:{
// 方法,默认会传入state
// mutation可以更改值,但getters一般只是计算并返回值
increment(state){
state.counter++
},
decrement(state){
state.counter--
}
},
actions:{
// 异步请求
},
getters:{
// 类似于单个组件中的计算属性,可以想象成计算器,计算器一般是对一些数据进行处理,计算后返回
// 1、数据进行计算,同样默认传入参数state
powerCounter(state){
// 使用时:$store.getters.powerCounter
return state.counter *state.counter
},
// 2、传参用法:查找students数组中年龄大于age的对象
// 使用时:$store.getters.moreAgeStu(参数Age:例如13)
moreAgeStu(state){
return function(Age){
return state.students.filter(s => s.age>Age)
}
},
more14Stu(state){
return state.students.filter(s => s.age>14)
},
// 3、还可根据需要添加一个参数getters
moreAgeStuLength(state,getters){
// 使用时:$store.getters.more14StuLength
// return getters.more14Stu.length
// 使用时:$store.getters.moreAgeStuLength(14)
return Age =>{
return getters.moreAgeStu(Age).length
}
}
// 使用过程均可查看HelloVuex.vue
},
modules:{
}
})
// 3、导出store独享
export default store
// 4、main.js中挂载store
HelloVuex.vue
<div class="hello">
<h2>HelloVuex中的counter</h2>
<h1>{{$store.state.counter}}</h1>
<button @click="add">+</button>
<button @click="sub">-</button>
<h2>平方{{$store.getters.powerCounter}}</h2>
<h2>{{$store.getters.moreAgeStu(14)}}</h2>
<h2>年龄大于Age的人数{{$store.getters.moreAgeStuLength(14)}}</h2>
</div>
vuex的mutation的使用
通常情况下,vuex要求mutations中是同步方法,异步的请求一般都放到actions
store/index.js
mutations:{
// 方法,默认会传入state
// mutation可以更改值,但getters一般只是计算并返回值
// increment称为事件类型,后面的(state){}是回调函数
increment(state){
state.counter++
},
decrement(state){
state.counter--
},
// 传入一个参数
incrementCount(state,count){
state.counter += count
},
// 传入对象作为参数
addStu(state,stu){
state.students.push(stu)
}
},
完整的store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
// 1、安装插件
Vue.use(Vuex)
// 2、创建对象
// 注意store要小写
const store = new Vuex.Store({
state:{
// 状态
counter:0,
students:[
{id:110,name:'one',age:15},
{id:111,name:'two',age:20},
{id:112,name:'three',age:10},
{id:113,name:'four',age:32},
]
},
mutations:{
// 方法,默认会传入state
// mutation可以更改值,但getters一般只是计算并返回值
// increment称为事件类型,后面的(state){}是回调函数
increment(state){
state.counter++
},
decrement(state){
state.counter--
},
// 传入一个参数
incrementCount(state,count){
state.counter += count
},
// 传入对象作为参数
addStu(state,stu){
state.students.push(stu)
}
},
actions:{
// 异步请求
},
getters:{
// 类似于单个组件中的计算属性,可以想象成计算器,计算器一般是对一些数据进行处理,计算后返回
// 1、数据进行计算,同样默认传入参数state
powerCounter(state){
// 使用时:$store.getters.powerCounter
return state.counter *state.counter
},
// 2、传参用法:查找students数组中年龄大于age的对象
// 使用时:$store.getters.moreAgeStu(参数Age:例如13)
moreAgeStu(state){
return function(Age){
return state.students.filter(s => s.age>Age)
}
},
more14Stu(state){
return state.students.filter(s => s.age>14)
},
// 3、还可根据需要添加一个参数getters
moreAgeStuLength(state,getters){
// 使用时:$store.getters.more14StuLength
// return getters.more14Stu.length
// 使用时:$store.getters.moreAgeStuLength(14)
return Age =>{
return getters.moreAgeStu(Age).length
}
}
// 使用过程均可查看HelloVuex.vue
},
modules:{
}
})
// 3、导出store独享
export default store
// 4、main.js中挂载store
HelloVuex.vue
<template>
<div class="hello">
<!-- mutations使用 -->
<button @click="add">+</button>
<button @click="sub">-</button>
<!-- 传入一个数字作为参数 -->
<button @click="addCount(5)">+5</button>
<!-- 传入对象作为参数 -->
<button @click="addStu()">添加学生</button>
</div>
</template>
<script>
export default {
methods:{
add(){
// mutation更新用commit
return this.$store.commit('increment')
},
sub(){
return this.$store.commit('decrement')
},
// 更新mutation时传入参数,这个参数称为playload负载
addCount(counts){
// mutation更新用commit
return this.$store.commit('incrementCount',counts)
},
addStu(){
const studentobj = {id:114,name:'five',age:55}
return this.$store.commit('addStu',studentobj)
}
}
}
</script>
store/index.js
// 传入一个参数的type写法
// 这里因为从type提交封装,传入参数是一整个对象,不适宜使用其中的参数,如果传入的对象中还有其他变量?
// 不可能将其做另一个参数传,因此,这里一般用playload(负载)做对象参数,调用其中的变量
incrementCount(state,playload){
state.counter += playload.counts
},
HelloVuex.vue
// 更新mutation时传入参数,这个参数称为playload负载
addCount(counts){
// mutation更新用commit
// return this.$store.commit('incrementCount',counts)
// 这里的counts传入的是一个数
// 还有另一种提交方式,type方式
return this.$store.commit({
type:'incrementCount', //type:事件类型
counts:counts //这里传参数过去时传的是一整个数组
})
},
组件中定义更新提交update方法
mutation的类型常量
有时有许多代码需要用到同一个类型,可能会出错,这时可以创建使用类型常量
在store中创建文件store/mutation_types.js
export const INCREMENT = 'increment'
// 注意这里export default导出的才能直接通过import INCREMENT from '...'导入,
// 这里不是default导出的,所以只能通过import { INCREMENT } from '...'导入
// store的mutations中使用可以见store/index.js
mutations中使用类型常量定义方法
store/index.js
mutations:{
// 方法,默认会传入state
// mutation可以更改值,但getters一般只是计算并返回值
// increment称为事件类型,后面的(state){}是回调函数
// increment(state){
// state.counter++
// },
// 类型常量
// import { INCREMENT } from './mutation_type'
[INCREMENT](state){
state.counter++
}
},
使用
Hellovuex.vue
<script>
import {
INCREMENT
} from '../store/mutation_type.js'
export default {
methods:{
add(){
// mutation更新用commit
// return this.$store.commit('increment')
// 使用类型常量
return this.$store.commit(INCREMENT)
}
}
}
</script>
actions的使用
mutations:{
updateInfo(state){
state.info.name='itachi'
}
},
actions:{
// 异步请求,只要是有异步操作的,必须在actions中多这样一群代码
// context上下文,是actions中的默认参数
// actions中commit mutations中的事件类型
// 在使用时,vue组件中的methods中要通过this.$store.dispatch('aUpdateInfo')
aUpdateInfo(context,payload){
// setTimeout(() => {
// context.commit('updateInfo',payload)
// console.log(payload.message)
// payload.success()
// })
// 写法二、
return new Promise((resolve,reject) => {
setTimeout(() => {
context.commit('updateInfo')
console.log(payload)
resolve('参数')
})
})
}
},
Hellovuex.vue
<template>
<div class="hello">
<!-- actions的使用 -->
<h2><button @click="updateinfo">修改信息</button></h2>
</div>
</template>
<script>
import {
INCREMENT
} from '../store/mutation_type.js'
export default {
name: 'HelloVuex',
data () {
return {
}
},
methods:{
updateinfo(){
// this.$store.dispatch('aUpdateInfo','我是payload')
// this.$store.dispatch('aUpdateInfo',{
// message :'我是携带的信息',
// success:() => {
// console.log('里面已经完成了')
// }
// })
// 写法二
this.$store
.dispatch('aUpdateInfo','我是携带的信息')
.then(res => {
console.log(res)
})
}
}
}
</script>
<style scoped>
</style>
vuex中的modules的使用
store/index.js
// 创建modules
const moduleB = {
state:{},
getters:{},
mutations:{},
// 等等
}
// 2、创建对象
// 注意store要小写
const store = new Vuex.Store({
state:{
// 状态
counter:0,
students:[
{id:110,name:'one',age:15},
{id:111,name:'two',age:20},
{id:112,name:'three',age:10},
{id:113,name:'four',age:32},
],
info:{
name:'naruto'
}
},
mutations:{
},
actions:{
},
getters:{
},
modules:{
// 模块
// 有时为了能够将某些模块单独抽离出来,可以在modules中写多个模块
a:{
state:{
name:'peien'
},
getters:{},
mutations:{},
actions:{}
// 等等
},
b:moduleB
// <!-- modules的使用:虽然state中没有a,但可以通过modules使用,另外,modules中的mutations的方法
也是靠methods中的this.$store.commit()提交的,所以模块中的mutations事件类型名称最好
不要和stores中的mutations中事件类型名称相同 -->
// {{$store.state.a.name}}
}
})
getters的使用基本相同,但还可以添加一个参数rootState,表示根的状态,即store的state
actions的使用,传入的参数context是上下文的意思,所以在模块中的context.commit,提交的是模块中的东西,如果想拿到根里的东西时,可以通过root来拿,例如context.rootGetters,rootStates等
插播一个对象的解构
<script>
// 对象的解构
const obj = {
name:'zhangsi',
age:18,
height:1.88
}
// 对象解构写法一
// const name = obj.name
// const age = obj.age
// const height = obj.height
// 对象的解构写法二
// 按名字对应
const {name,age,height} = obj
</script>
具体抽离方法可以查阅官网
网络模块封装axios
axios框架的基本使用
安装axios
npm install --save axios
引用axios
main.js中
import axios from 'axios'
new Vue({
el: '#app',
store,
router,
render: h => h(App)
})
// 在任何地方都可以引用
// 传入一些你要进行的相关网络请求,相关的一些配置即可引用
// 由于可能需要传入很多的一些相关配置,因此,config应该是一个对象
// axios(config)
// 需要有一个服务器作为url为我们返回数据
// httpbin.org,该网站可以为我们做很多请求的模拟,可以做为一个模拟封装,模拟测试的网站
// axios可以使用promise,可以直接使用.then()方法,它会在内部自动调用promise.resolve
// 最简单的一种写法:
// 默认情况下,如果只传一个url的话,默认执行get请求,用method可以指定请求类型
axios({
url:'http://httpbin.org/',
method:'get'
}).then(res => {
// res这里是result的一个简称
console.log(res)
})
// 写法二
axios.get()
axios.post()
// 写法三:专门针对get请求的参数拼接
// 有时候,如果不想把参数直接拼接在url后面,还可以通过params参数来传递参数
axios({
url:'http://httpbin.org/',
method:'get',
params:{
type:'pop',
page:1
}
}).then(res => {
// res这里是result的一个简称
console.log(res)
})
axios发送并发请求
main.js
// axios的并发请求
// 类似promise
// axios.all([axios(),axios()]).then(resultes => {})
axios.all([
axios({
url:''
}),
axios({
url:''
})]).then(results => {
console.log(results)
console.log(results[0])
console.log(results[1])
})
// 如果要分开使用的话,可以通过axios.spread将数组[res1,res2]展开为res1,res2
// 这里的res1<=>(等价于)results[0],res2<=>(等价于)results[1]
axios.all([
axios({
url:''
}),
axios({
url:''
})
]).then(axios.spread((res1,res2) => {
console.log(res1)
console.log(res2)
}))
// 等价于 ]).then(axios.spread([res1,res2] => {,这里可以理解为相当于对results数组的解构
axios的全局配置
同样,有时,有的axios配置有一些共同的地方,可以通过default设置为全局配置,
axios创建实例
// 创建axios实例instance,由于可能某些url不是用的全局配置中的baseURL,这时,可以创建多个axios实例,
// 相当于封装成模块
const instance1 = axios.create({
baseURL:'http://123.207.32.33:8081',
timeout:5000
})
instance1({
url:'/home/multidata'
}).then(res => {
console.log(res)
})
instance1({
url:'/home/data',
params:{
type:'roll',
page:3
}
}).then(res => {
console.log(res)
})
// 当有另外几个网页对不同的服务器请求时,可以再创建实例
const instance2 = axios.create({})
axios的封装
直接引用
main.js下配置了axios,在组件中就可以直接引用axios
例如:
Helloaxios.vue
<template>
<div class="hello">
<p> {{result}} </p>
</div>
</template>
<script>
// 直接在这里引用axios
import axios from 'axios'
export default {
name: 'Helloaxios',
data () {
return {
result:''
}
},
created(){
axios({
url:'http://httpbin.org/'
}).then(res => {
// console.log(res)
this.result = res
})
},
methods:{
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
在src文件夹下新建文件夹network,再在network下新建文件require.js,用来封装网络请求
src/network/require.js
// 导入
import axios from 'axios'
// 由于要封装多个实例,就不用export default了,default只能导出一个实例,因此这里使用function
// export function request(config,success,failure){
// // 1、创建axios实例
// const instance = axios.create({
// baseURL:'httpbin.org',
// timeout:5000
// })
// //2、发送真正的网络请求
// instance(config)
// .then(res => {
// console.log(res)
// // 通过前面设置的success和failure函数将结果回调出去
// success(res)
// })
// .catch(err => {
// failure(err)
// })
// // 3、使用,看main.js中request模块的封装
// }
// 写法二
// export function request(config){
// // 1、创建axios实例
// const instance = axios.create({
// baseURL:'httpbin.org',
// timeout:5000
// })
// //2、发送真正的网络请求
// instance(config.baseConfig)
// .then(res => {
// console.log(res)
// // 通过前面设置的success和failure函数将结果回调出去
// config.success(res)
// })
// .catch(err => {
// config.failure(err)
// })
// // 3、使用,看main.js中request模块的封装写法二
// }
// 写法三
// export function request(config){
// return new Promise((resolve,reject) => {
// // 1、创建axios实例
// const instance = axios.create({
// baseURL:'httpbin.org',
// timeout:5000
// })
// // 发送真正的网络请求
// instance(config)
// .then(res => {
// resolve(res)
// })
// .catch(err =>{
// reject(err)
// })
// })
// }
// 写法四
export function request(config){
// 1、创建axios的实例
const instance = axios.create({
baseURL:'httpbin.org',
timeout:5000
})
// 发送真正的网络请求
// 查看axios.create()的源码,发现返回值instance本身就是一个promise.
return instance(config)
}
main.js
// 5、request模块的封装
// 前面步骤查看network/request.js写法一
// import {request} from './network/request'
// request({
// // url:'/home/multidata'
// url:''
// },
// // 作为request模块函数的success参数
// res => {
// console.log(res)
// },
// // 作为request模块函数的failure参数
// err => {
// console.log(err)
// })
// 写法二
// request({
// baseConfig:{
// },
// success(res){
// },
// failure(err){
// }
// })
// // 对应的写法三
// request({
// url:''
// }).then(res => {
// }).catch(err => {
// })
// 对应的写法四
request({
url:''
}).then(res => {
}).catch(err => {
})
什么是回调
:简单来说,就是把某一个函数作为参数传给另一个函数
axios拦截器的使用
拦截器:
当我们向网络发送请求时,可能需要在请求过程中进行拦截,获取一些信息或进行一些操作
network/request.js
// 添加了拦截器
export function request(config){
// 1、创建axios的实例
const instance = axios.create({
baseURL:'http://httpbin.org/',
// timeout:5000
})
// 2、axios的拦截器设置
// 如果是全局的拦截,可以直接用axios.interceptors《==拦截器的意思,
// 但这里只是一个实例的拦截,所以只用instance.interceptors
// 拦截请求instance.interceptors.request.use()
// 拦截响应instance.interceptors.response.use()
// 其中,use()传入的两个参数均为函数,config表示请求成功时的拦截,
// 注意不是拦截成功,而是请求/响应成功时返回的拦截信息去向
// 2.1请求拦截的作用
instance.interceptors.request.use(config =>{
console.log(config)
//1、比如config中的一些信息不符合服务器的要求,这时可以通过拦截器来查看并更改调试
//2、比如每次发送网络请求时,都希望在界面中显示一个请求的图标,如网页加载中的那个循环转着的小圈
//3、某些网络请求(比如登录(须携带token)),必须携带一些特殊的信息,可以通过请求拦截查看是否携带了
// 使用后一定记得返回config,否则拿不到信息会报错
return config
},err => {
console.log(err)
})
// 2.2、响应拦截
// ,响应拦截也有响应成功和响应失败两种情况这里传入的参数是result,因为服务器已经响应过了,所以拿到的是个结果
instance.interceptors.response.use(res => {
console.log(res)
// 这里可以只返回res.data
return res
},err => {
console.log(err)
})
// 3、发送真正的网络请求
// 查看axios.create()的源码,发现返回值instance本身就是一个promise.
return instance(config)
}
main.js中的使用
// 有拦截器时对应的使用写法也是如此,如果baseURL后面还有其他参数的话可以直接在这里的url中进行拼接
request({
url:''
}).then(res => {
}).catch(err => {
})