vue-resource
也是对xhr的封装。是一个插件库。官方已经不再维护。推荐axios。
Vue.use(vueResource);
应用插件后,vc上有$http:
默认插槽
多个子组件需要展示不同的内容.
在父组件的模板里写内容,子组件申明一个插槽。内容会在插槽中显示。
父组件解析完结构再传过去。
<template>
<div class="container">
<category title="美食" :data="foods">
<img src="https://s3.ax1x.com/2021/01/16/srJIq0.jpg" alt="" />
</category>
<category title="游戏" :data="games">
<ul>
<li v-for="(item, index) in games" :key="index">{{ item }}</li>
</ul>
</category>
<category title="电影" :data="films">
<video controls src="https://clips.vorwearts-gmbh.de/big_buck_bunny.mp4"></video>
</category>
</div>
</template>
具名插槽
当使用template时,可用v-slot:name.指明插槽位置。
<template>
<div class="ItemList">
<h3>{{title}}</h3>
<slot name='center'></slot>
<slot name='footer'></slot>
</div>
</template>
<template>
<div class="container">
<ItemList title="美食" :data="foods">
<img
slot="center"
src="https://s3.ax1x.com/2021/01/16/srJIq0.jpg"
alt=""
/>
<div class="foot" slot="footer">
<a href="https://www.atguigu.com" >更多美食</a>
</div>
</ItemList>
<ItemList title="游戏" :data="games">
<ul slot="center">
<li v-for="(item, index) in games" :key="index">{{ item }}</li>
</ul>
<div slot="footer" class="foot">
<a href="https://www.atguigu.com">单击游戏</a>
<!-- 可以追加-->
<a href="https://www.atguigu.com">网络游戏</a>
</div>
</ItemList>
<ItemList title="电影" :data="films">
<video
slot="center"
controls
src="https://clips.vorwearts-gmbh.de/big_buck_bunny.mp4"
></video>
<template v-slot:footer>
<div class="foot">
<a href="">经典</a>
<!-- 可以追加-->
<a href="https://www.atguigu.com">热门</a>
<a href="https://www.atguigu.com">推荐</a>
</div>
<h4 slot="footer">welcome to watch!</h4>
</template>
</ItemList>
</div>
</template>
作用域插槽
(也可以有名字)
数据在子组件,结构需要由父组件决定。
需求第一次是无序,第二次是有序,第三次是h4。
可以在app组件传递数据,子组件根据数据进行条件渲染。
弊端是需要写的判断太多。
使用插槽:如果此时games数据不在app里,无法使用。
现需要app组件获取子组件里的数据。
App里面需要使用Category组件,组件里有一个插槽,且在App里需要数据,数据来自于定义插槽的组件。
数据是一个对象,属性名是games,值是数组。所以<slot>可以传递多个数据。
<template>
<div class="ItemList">
<h3>{{title}}</h3>
<slot :gamesMsg='games'></slot>
</div>
</template>
<template>
<div class="container">
<ItemList title="游戏" >
<template scope="gamesMsg">
<ul>
<li v-for="(item, index) in gamesMsg.games" :key="index">{{ item }}</li>
</ul>
</template>
</ItemList>
<ItemList title="游戏">
<template scope="{gamesMsg}">
<ol>
<li style="color:red;" v-for="(item, index) in gamesMsg" :key="index">{{ item }}</li>
</ol>
</template>
</ItemList>
<ItemList title="游戏" >
<template slot-scope="{gamesMsg}">
<h4 v-for="(item, index) in gamesMsg" :key="index">
{{ item }}
</h4>
</template>
</ItemList>
</div>
</template>
插槽总结
Vuex
刷新页面时,vue实例重新加载,从而,store也被重置了。store是用来存储组件状态的,而不是用来做本地数据存储的。所以,对于不希望页面刷新之后被重置的数据,使用本地存储来进行存储。
获取数据的时候,在state和cookie中都保存一份。
在Vue的created钩子中,重新commit store中的方法,从cookie中获取数据设置到state中。
共享数据---全局事件总线实现,bcd需要访问到a里面的x,则需要在bcd中给$bus绑定事件和回调。a中触发事件并传递参数,bcd中触发回调,获取参数。
如果此时bcd中都需要修改x这个数据,此时a需要x修改的值,则此时在a中绑定事件和回调,获取参数,触发回调,在bcd中触发事件,传递参数给a。
共享数据---vuex实现
求和案例
<template>
<div class="ItemList">
<h1>
sum =:{{sum}}
</h1>
<select v-model.number="n">
<option :value="1">1</option>
<option :value="2">2</option>
<option :value="3">3</option>
</select>
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="evenIncrement">求和为偶数再加</button>
<button @click="waitIncrement">等一等再加</button>
</div>
</template>
<script>
export default {
name:'ItemList',
data() {
return {
n:1,
sum:0,
};
},
methods: {
increment(){
this.sum += this.n;
},
decrement(){
this.sum -= this.n;
},
evenIncrement(){
if(!(this.sum % 2)){
this.sum += this.n;
}
},
waitIncrement(){
setTimeout(()=>{
this.sum += this.n;
},500)
}
},
}
</script>
<style>
button{
margin-left: 7px;
}
</style>
Vuex工作原理图
在组件中操作,调用dispatch("操作",数据),Actions是一个object对象,Actions里有个key叫"操作",value是一个函数,函数就会被调用,且会收到数据,在这个函数里面,调用commit,Mutations也是一个object对象,Mutations里也有个key叫"操作",value是一个函数,该函数会得到state和数据,在该函数写state.sum += 2,底层自动mutate,State里面的sum就变为了2,然后render,重新解析组件。
Actions,里面可以发请求,从后端接口中获取数据。也可以进行逻辑判断,如奇数再加,等一等在加等。Vuex运行不通过Actions。
Mutations是真正操作数据的对象。
Actions,Mutations,State这三个对象都需要store进行管理。如store.dispatch,store.commit。
需要让所有的组件实例对象都能看见store。
把store传入创建实例的new Vue()的配置参数对象中,会出现在$options中,$options中出现了会在你Vue.use()时执行this.$store = $options.store的操作。
在组件中可使用 this 访问原型上的属性,template 拥有组件实例的上下文,
可直接通过 {{ $store.state.userName }} 访问,等价于 script 中的 this.$store.state.userName。
至于 {{ store.state.userName }},script 中的 data 需声明过 store 才可访问。
对比全局事件总线:
搭建Vuex环境
npm i vuex@3.
必须在创建store实例之前调用Vue.use(Vuex).
在脚手架中, import语句自动提前。
App,子组件上可以看见store。
index.js:
//该文件用于创建Vuex中最为核心的store
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex);//use Vuex后 ,在Vue配置项可以传入store。
//actions 用于响应组件中的动作
const actions = {};
//mutations 用于真正去操作数据
const mutations = {};
//state 用于存储数据
const state = {};
//创建并导出store
export default new Vuex.Store({
actions,mutations,state
});
main.js:
/*
该文件为入口文件
*/
import Vue from 'vue'
//引入App组件 所有组件的父组件
import App from './App.vue'
//引入store
import store from './store/index';
//关闭vue生产提示
Vue.config.productionTip = false
//创建vue实例对象
new Vue({
//将App组件放入容器中
render: h => h(App),
//store:{}, vm和vc都能看见
store,
beforeCreate() {
//安装全局事件总线
Vue.prototype.$bus = this;
},
}).$mount('#app')
求和案例vuex
若只是直接+和-则可以直接调用commit,
若有业务逻辑,则需要先调用dispatch。
methods: {
increment(){
this.$store.commit('PLUS',this.n);
},
decrement(){
this.$store.commit('REDUCE',this.n);
},
evenIncrement(){
this.$store.dispatch('evenPlus',this.n);
},
waitIncrement(){
this.$store.dispatch('waitPlus',this.n);
}
},
//actions 用于响应组件中的动作
const actions = {
//context:上下文对象
/*plus(context,value){
context.commit('PLUS',value)
},
reduce(context,value){
context.commit('REDUCE',value)
},*/
evenPlus(context,value){
if(!(context.state.sum%2)){
context.commit('PLUS',value)
}
},
waitPlus(context,value){
setTimeout(() => {
context.commit('PLUS',value)
}, 500);
}
};
//mutations 用于真正去操作数据
const mutations = {
PLUS(state,value){
state.sum += value;
},
REDUCE(state,value){
state.sum -= value;
}
};
//state 用于存储数据 响应式 getset
const state = {
sum:0,
};
vuex开发工具的使用
Devtools和mutation交互。
为什么在actions中需要使用context而不是直接commit:
在actions的函数里面还可以调用dispatch,还需要context里面的state。
jiaOdd---demo1----demo2
为什么在actions中不能通过context.state直接操作数据:
此时页面会发生变化,开发者工具失效。
为什么不在组件中进行业务逻辑:
当业务非常复杂时,代码冗余太高,不存在复用。
此时所有涉及到该业务的都通过actions。
因为mutations和actions不支持传递多个参数的,这里的参数又称“载荷”(Payload)。
传递多个参数
let data = {form:form,router:this.$router,message:this.$message}
this.$store.dispatch('user/login',data)
getters配置项
计算属性:本组件复用
目前需要跨组件复用。
此时开发者工具也能看见getters
<h2>
multiplySum =:{{$store.getters.bigSum}}
</h2>
const getters = {
bigSum(state){
return state.sum*10;
}
}
//创建并导出store
export default new Vuex.Store({
actions,
mutations,
state,
getters
});
mapState与mapGetters
简化代码:
ES6语法:
只有当a是一个变量的时候才可以简写。
mapState接收一个对象(数组(名字一致))作为参数,返回的是一个对象:
两者效果相同:
computed:{
/*亲自去写计算属性
sum(){
return this.$store.state.sum;
},*/
//从state中读取数据,对象写法
//...mapState({sum:'sum',school:'school',subject:'subject'})
//从getters中读取数据,数组写法
...mapState(['sum','school','subject']),
...mapGetters(['bigSum'])
},
mapActions与mapMutations
此时生成的increment是下面那个:
当绑定的事件不写小括号时,也会传参,参数是event。
所以需要这么传参:
也可以这么写:
methods: {
//commit 原始写法
/*
increment(){
this.$store.commit('PLUS',this.n);
},
decrement(){
this.$store.commit('REDUCE',this.n);
},*/
//生成对应的方法,方法中会调用commit去联系mutations 对象写法
...mapMutations({increment:'PLUS',decrement:'REDUCE'}),
//数组写法 (需要名字一致)
//...mapMutations(['PLUS','REDUCE']),
//dispatch 原始写法
/*evenIncrement(){
this.$store.dispatch('evenPlus',this.n);
},
waitIncrement(){
this.$store.dispatch('waitPlus',this.n);
}*/
//生成对应的方法,方法中会调用commit去联系actions 对象写法
...mapActions({evenIncrement:'evenPlus',waitIncrement:'waitPlus'}),
//数组写法 (需要名字一致)
//...mapMutations(['evenPlus','waitPlus']),
},
多组件共享数据
vuex中保存sum和persons,在Item中读取persons,在persons中读取sum。
Vuex:
const state = {
sum:0,
school:'atguigu',
subject:'前端',
persons:[{id:'001',name:'张三'}]
};
PersonList:
computed:{
/*personList(){
return this.$store.state.persons;
},*/
...mapState(['persons']),
sum(){
return this.$store.state.sum;
}
},
methods: {
add(){
const personObj = {id:nanoid(),name:this.name};
this.$store.commit('ADD_PERSON',personObj);
this.name = '';
},
},
ItemList:
computed:{
...mapState(['sum','school','subject','persons']),
...mapGetters(['bigSum'])
},
methods: {
//生成对应的方法,方法中会调用commit去联系mutations 对象写法
...mapMutations({increment:'PLUS',decrement:'REDUCE'}),
//生成对应的方法,方法中会调用commit去联系actions 对象写法
...mapActions({evenIncrement:'evenPlus',waitIncrement:'waitPlus'}),
},
vuex模块化+namespace
store里有countOptions,personOptions。
$store.state:
$store.getters:
item.js:
//求和功能相关的配置
export default{
namespaced:true,
actions:{
evenPlus(context,value){
if(!(context.state.sum%2)){
context.commit('PLUS',value)
}
},
waitPlus(context,value){
setTimeout(() => {
context.commit('PLUS',value)
}, 500);
}
},
mutations:{
PLUS(state,value){
state.sum += value;
},
REDUCE(state,value){
state.sum -= value;
},
},
state:{
sum:0,
school:'atguigu',
subject:'前端',
},
getters:{
bigSum(state){
return state.sum*10;
}
},
}
persons.js:
import axios from "axios";
import { nanoid } from "nanoid";
//人员管理功能相关的配置
export default{
namespaced:true,
actions:{
addPersonWang(context,value){
if(value.name.indexOf('王') === 0){
context.commit('ADD_PERSON',value);
}else{
alert('添加的人需要姓王!')
}
},
addPersonSever(context){
axios.get('https://api.uixsj.cn/hitokoto/get?type=social').then(
response => {
context.commit('ADD_PERSON',{id:nanoid(),name:response.data});
},
error =>{
alert(error.message);
}
)
}
},
mutations:{
ADD_PERSON(state,value){
state.persons.unshift(value);
}
},
state:{
persons:[{id:'001',name:'张三'}]
},
getters:{
firstPersonName(state){
return state.persons[0].name;
}
},
}
index.js:
//该文件用于创建Vuex中最为核心的store
import Vuex from 'vuex'
import Vue from 'vue'
import countOptions from './item'
import personOptions from './persons'
Vue.use(Vuex);//use Vuex后 ,在Vue配置项可以传入store。
//创建并导出store
export default new Vuex.Store({
modules:{
countOptions,
personOptions
}
});
personList.vue:
<template>
<div>
<h1>人员列表</h1>
<input type="text" value="请输入名字" v-model="name">
<button @click="add">添加</button>
<ul>
<li v-for='p in persons' :key='p.id'>{{p.name}}</li>
</ul>
<h3>sum:{{sum}}</h3>
<h3>列表中第一个人的名字:{{firstPersonName}}</h3>
<button @click="addWang">添加一个姓王的人</button>
<button @click="addPersonServer">添加一个随机名字的人</button>
</div>
</template>
<script>
import {mapState} from 'vuex';
import {nanoid} from 'nanoid';
export default {
name:'PersonList',
data(){
return{
name:'',
}
},
computed:{
/*personList(){
return this.$store.state.persons;
},*/
...mapState('personOptions',['persons']),
sum(){
//总的state
return this.$store.state.countOptions.sum;
},
firstPersonName(){
return this.$store.getters['personOptions/firstPersonName']
}
},
methods: {
add(){
const personObj = {id:nanoid(),name:this.name};
this.$store.commit('personOptions/ADD_PERSON',personObj);
this.name = '';
},
addWang(){
const personObj = {id:nanoid(),name:this.name};
this.$store.dispatch('personOptions/addPersonWang',personObj);
this.name = '';
},
addPersonServer(){
this.$store.dispatch('personOptions/addPersonSever')
}
},
}
</script>
<style>
</style>
ItemList.vue:
<template>
<div class="ItemList">
<h1>
sum =:{{sum}}
</h1>
<h2>
multiplySum =:{{bigSum}}
</h2>
<h2>
I am at{{school}} learn {{subject}}
</h2>
<h2>
人数:{{persons.length}}
</h2>
<select v-model.number="n">
<option :value="1">1</option>
<option :value="2">2</option>
<option :value="3">3</option>
</select>
<button @click="increment(n)">+</button>
<button @click="decrement(n)">-</button>
<button @click="evenIncrement(n)">求和为偶数再加</button>
<button @click="waitIncrement(n)">等一等再加</button>
</div>
</template>
<script>
import {mapState,mapGetters, mapMutations,mapActions} from 'vuex';
export default {
name:'ItemList',
data() {
return {
n:1,
};
},
computed:{
...mapState('countOptions',['sum','school','subject']),
...mapState('personOptions',['persons']),
...mapGetters('countOptions',['bigSum'])
},
methods: {
//生成对应的方法,方法中会调用commit去联系mutations 对象写法
...mapMutations('countOptions',{increment:'PLUS',decrement:'REDUCE'}),
//生成对应的方法,方法中会调用commit去联系actions 对象写法
...mapActions('countOptions',{evenIncrement:'evenPlus',waitIncrement:'waitPlus'}),
},
}
</script>
<style>
button{
margin-left: 7px;
}
</style>
this.$store: