B站尚硅谷---vue2实战项目《尚品汇》---第二天

1.传参可能会出现的问题 

编程式导航在你多次跳转的时候会出现报错,这个问题时因为下载的路由有问题。我们有两个办法解决:

第一种:治标,通过传入相应成功和失败回调来解决

 this.$router.push({name:"search",params:{keyword:this.keyword},query:{k:this.keyword.toUpperCase()}},()=>{},()=>{});

第二种:根治,重写push和replace方法:

在路由里面router/index下重写

//需要重写VueRouter.prototype原型对象身上的push|replace方法
//先把VueRouter.prototype身上的push|replace方法进行保存一份
let originPush = VueRouter.prototype.push;
let originReplace = VueRouter.prototype.replace;
//重写VueRouter.prototype身上的push方法了
VueRouter.prototype.push = function(location, resolve, reject) {
  //第一个形参:路由跳转的配置对象(query|params)
  //第二个参数:undefined|箭头函数(成功的回调)
  //第三个参数:undefined|箭头函数(失败的回调)
  if (resolve && reject) {
    //push方法传递第二个参数|第三个参数(箭头函数)
    //originPush:利用call修改上下文,变为(路由组件.$router)这个对象,第二参数:配置对象、第三、第四个参数:成功和失败回调函数
    originPush.call(this, location, resolve, reject);
  } else {
    //push方法没有产地第二个参数|第三个参数
    originPush.call(
      this,
      location,
      () => {},
      () => {}
    );
  }
};
//重写VueRouter.prototype身上的replace方法了
VueRouter.prototype.replace = function(location, resolve, reject) {
  if (resolve && reject) {
    originReplace.call(this, location, resolve, reject);
  } else {
    originReplace.call(
      this,
      location,
      () => {},
      () => {}
    );
  }
};

 call和apply的区别:

相同点:都可以调用函数一次,都可以篡改函数上下文一次;

不同点:call传递参数用逗号隔开,而apply用数组传递;

2.三级联动组件 

先看看设置为什么组件,因为我们有很多歌页面都在使用三级联动组件,所有把他拆分成一个全局组件,这样就可以只注册一次,每个地方都可以使用。

 注册全局组件:

  1. 在Vue项目中src/components路径下补充创建 文件夹名(xxx)/ index.vue,在此vue组件中写入自己需要服用多次的代码结构
  2. src/main.js文件中
  3. import Vue from 'vue'
    // 引入封装的复用的组件 注意xxx组件名应采用大驼峰命名
    import TypeNav from '@/components/TypeNav'
    
    //第一个参数:全局组件的名字   第二个参数:哪一个组件
    Vue.component('typenav',TypeNav) // 在全局用Vue.component()方法全局注册
    

    然后在另一个组件上使用

  4. <div>   
    <!-- 因为已经注册为全局组件了,所以这里不要import -->
       <typenav/>
    </div>
    

    这里是在Home组件中使用的

3.完成首页其他静态组件 

注意拉代码的时候:HTML +  CSS  + 图片资源 

这些静态组件只在首页使用,所有直接在局部注册使用就好了 

<template>
  <div>
   <!-- 因为已经注册为全局组件了,所以这里不要import -->
   <typenav/>
   <ListCotainer/>
   <Like/>
   <Rank/>
   <Recommend/>
   <Brand/>
   <Floor/>
  </div>
</template>

<script>
import ListCotainer from '@/pages/Home/ListContainer';
import Like from '@/pages/Home/Like';
import Rank from '@/pages/Home/Rank';
import Recommend from'@/pages/Home/Recommend';
import Brand from '@/pages/Home/Brand'
import Floor from '@/pages/Home/Floor'



export default {
  name:'',
  components:{ListCotainer,Like,Rank,Recommend,Brand,Floor},
}
</script>

 4.用postman软件测试接口

测试最新的端口:http://gmall-h5-api.atguigu.cn/api/product/getBaseCategoryList

服务器返回的code是200就表示返回数据成功

整个项目,接口前缀都有/api字段 

 

5.axios二次封装 

 我们向服务器发送请求的方式有很多种:XMLHttpRequest原生的构造函数、fecth、JQ、axios;

一般我们是使用axios的

为什么要二次封装axios呢?

可以设置请求拦截器和响应拦截器:

请求拦截器:可以在发送请求之前处理一些业务

响应拦截器:当服务器返回数据之后,可以处理一些业务

项目一般用src下的api文件放axios

//首先安装axios
npm i axios

//接口当中路劲都带有/api  例如:http://xxxxxxx/api


//对于axios进行二次封装
import axios from "axios"

//引入进度条
import nProgress from "nprogress";
//start:进度条开始    done:进度条结束
//引入进度条样式
import "nprogress/nprogress.css"


//1.利用axios对象方法创造一个实例
//requests就是axios
const requests=axios.create({
//配置对象//基础路径,发请求是,路径会出现/api
baseURL:"/api",
//请求时间超过时间为5s
timeout:5000,
})

//请求拦截器:在请求发出前,可以侦测到,可以再发出去前做点事情
requests.interceptors.request.use((config)=>{
    //进度条开始
    nProgress.start();
    //config:配置对象,对象里面有一个属性很重要,headers请求头
    return config;
});

//响应拦截器,数据返回之后还能干的事情
requests.interceptors.response.use((response)=>{
    //进度条结束
    nProgress.done();
    //状态码是2xx,就执行这个
    return response.data;
},(error)=>{
    return Promise.reject(new Error('faile'));
});

//对外暴露
export default requests;

6.接口统一管理

项目小:可以在组件的生命周期函数种发请求

项目大:axios.get(‘xxx’) 

api文件下的index.js就是用来管理接口的

//当前这个模块:API进行统一管理
import requests from './request';


//三级联动接口
// /api/product/getBaseCategoryList    GET  无参数


// export const reqCategoryList= ()=>{
//     //发请求:axios发请求返回结果promise对象
//    return requests({url:'/product/getBaseCategoryList'})
// }


//测试,用postman测试,对外暴露一个函数,只要外部调用这个函数,就向服务器发起ajax请求来获取我们的三级菜单数据;这个是上面方式的简写
export const reqCategoryList= ()=>requests({url:'/product/getBaseCategoryList'});


//!!!在网上有看到其他版本的但是没试过,就是export const reqCategoryList= ()=>requests({url:'/product/getBaseCategoryList',method:'get',});
在入口文件main.js下测试

// //测试那个端口有没有用的,用postman那个软件
// import {reqCategoryList} from '@/api'
// reqCategoryList();//这是在api下的一个方法

7.跨域问题

所谓跨域就是:协议、域名、端口号不一致,就叫做跨域

前端项目的本地服务器是:http://localhost:8080/#/home

后台项目服务器:http://gmall-h5-api.atguigu.cn

这就是跨域,想要解决跨域问题的方案:JSONP、cros、代理 

//代理跨域
  //配置代理跨域
  devServer: {
    proxy: {
      "/api": {
        target: "http://gmall-h5-api.atguigu.cn",

      },
    },
  },

8.Vuex

这是vuex的原理。

原本的vuex是这样的,然后再在main.js中引入

//该文件用于创建Vuex中最为核心的store
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)

//准备actions——用于响应组件中的动作!!!!!!!!!!!等于餐厅的服务员,如果是固定的东西可以直接跳过他,但是如果是后台发的信息不可以跳过他
const actions = {
	jia(context,value){
		console.log('actions中的jia被调用了')
		context.commit('JIA',value)
	},
	//commit是actions传递给mutations的方法,用了这个要保证mutations里面也有一个(’jia‘)
	//context是上下文的意思,编译器通过看上下文
}
//准备mutations——用于操作数据(state)  !!!!等于厨师
const mutations = {
	JIA(state,value){
		console.log('mutations中的JIA被调用了')
		state.sum += value
	},
}
//准备state——用于存储数据   等于后厨
const state = {
	sum:0 //当前的和
}

//创建并暴露store
export default new Vuex.Store({
	actions,
	mutations,
	state,
})



//在main.js中写的
import store from './store'
new Vue({
  render: h => h(App),
  // $route路由对象  $routers路由器
  // 注册路由信息:当这里写router的时候,组件身上都拥有$route,$router
  router,
  //注册仓库:组件实例身上会多一个属性$store属性
  store
}).$mount('#app')

 但是我们这个项目要用vuex模块化开发

所谓的模块化就是将创建一个个小仓库分别当不同组件的仓库,大仓库统一管理

第一步:在组件中挂载

//因为在三级联动上向服务器发请求,所有发送装置放在三级联动组件
  mounted() {
        //通知vuex发请求,获取数据,储存在仓库中
        // this.$store.dispatch('home/getCategoryList');//开启命名空间就这样写,不开就写getCategoryList
        // 这个之所以不用是因为其他组件调用它就会发一次请求,跳转到其他页面后就自动销毁前面的组件,导致后面还要用又要发请求,所以放到app。vue里面,它只执行一次,就不会反复调用了
    },


// 因为跳转页面后会销毁上一个组件,后面不希望每次跳转页面都要重写加载一次数据,所有放在app。vue
 mounted(){
    //因为只有main.js和app.vue的只执行一次,这也可以减低服务器的压力
    //派发一个action||获取商品分类的三级列表的数据,这是发送异步请求,需要在路由那边写好异步请求的东西
    //开了命名空间才要home/getCategoryList,如果没开命名空间就直接getCategoryList
    this.$store.dispatch("getCategoryList")
  }

第二步:创建home的小仓库就是store/home/index.js

//home的仓库
import {reqCategoryList} from '@/api'


//getters:理解为计算属性,用于简化仓库数据,让组件获取仓库的数据更加方便
const getters = {};


//action:处理action,可以书写自己e的业务逻辑,也可以处理异步
const actions = {
        //通过API里面的接口函数调用,向服务器发请求,获取服务器的数据
      async  getCategoryList({commit}){
                let result = await reqCategoryList();
        //成功返回时,我们要修改仓库中的数据
                if(result.code==200){
                        commit("CETEGORYLIST",result.data);
                }
        },

};


//mutations:修改state的唯一手段
const mutations = {
        CETEGORYLIST(state,categoryList){
    //修改state中的categoryList---事先准备好空的categoryList
                state.categoryList = categoryList;
        }
};


//state:仓库存储数据的地方
const state ={
        //state中的数据默认初始值别乱写,服务器返回对象,服务器返回数组【根据接口返回值初始化的】
        categoryList:[],

}
   



//对外暴露store类的实例
export default ({
//开启命名空间
           namespaced:true,
        state,mutations,actions,getters

});

然后统一大仓库就是文件夹store/index.js管理

import Vue from 'vue';
import Vuex from 'vuex';

//需要使用插件一次
Vue.use(Vuex);

//引入小仓库
import home from './home';

//对外暴露store类的实例
export default new Vuex.Store({
        //模块化开发
        modules:{
                home
        }
});

第三步:展示,从仓库中获取到数据之后就可以拿出来展示了。就是下面的c1.categoryId。。。。


<div class="item bo" v-for="c1 in categoryList" :key="c1.categoryId">
  <h3>
    <a href="">{{ c1.categoryName }}</a>
  </h3>
  <div class="item-list clearfix">
    <div
      class="subitem"
      v-for="c2 in c1.categoryChild"
      :key="c2.categoryId"
    >
      <dl class="fore">
        <dt>
          <a href="">{{ c2.categoryName }}</a>
        </dt>
        <dd>
          <em v-for="c3 in c2.categoryChild" :key="c3.categoryId">
            <a href="">{{ c3.categoryName }}</a>
          </em>
        </dd>
      </dl>
    </div>
  </div>
</div>
<script>
import {mapState} from 'vuex';
import {throttle} from 'lodash';


export default {
    name:"typeNav",
    //组件挂在完毕:可以向服务器发请求
    data(){
        return{
            currentIndex:-1,
            show:true
        }
    },
    computed:{        
// xxx为自定义命名,这里是指把state仓库里的search小仓库里的searchList里的total的值拿到,
// 赋值给左边xxx的身上,这样xxx身上就有数据里,在组件里就
// 可以用{{xxx}}拿到仓库里的total数据了
// ...mapState({
//       xxx:state=>state.search.searchList.total,
//     }),

//这个。。。mapState的作用就是获取仓库中的数据,然后上面展示的时候用。
        ...mapState({
            //没开命名空间的写法
            categoryList:state=>state.home.categoryList
            //开了命名空间就是。。。mapState("home",["categoryList"])
        })
    },

</script>

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值