尚品汇笔记——尚硅谷

  • Post author:
  • Post category:其他




尚品汇笔记



前端核心:


1、构建页面Html+CSS+…

2、接口传来数据

3、vuex接收并处理数据

4、组件接收数据渲染到页面

5、交互




1、Vue的目录分析


node_modules:

是安装node后用来存放用包管理工具下载安装的包的文件夹。


public:

一般用于存放一些静态资源文件,例如图片,视频,音频等资源文件。需要特别注意的是webpack在进行打包的时候,会将public中的所有静态资源原封不动的进行打包。


src:

asset:也是用于存放一些静态资源文件
components:公共组件,非公共放在page
App.vue:是整个项目的根组件,所有组件的后缀名均为·vue
main.js:是文件的入口文件,程序执行先从该文件开始


babel.config.js:

: 配置文件(babel相关)


package.json

: 项目的详细信息记录


package-lock.json

: 缓存性文件(各种包的来源)



2、项目配置

2.1:项目的基本运行指令,自动打开浏览器,打包文件,自动修复

	"scripts": {
	    "serve": "vue-cli-service serve",
	    "build": "vue-cli-service build",
	    "lint": "vue-cli-service lint"
	  },

2.2:关闭eslint工具(不关闭严重影响,不按照eslint语法就报错)

	module.exports = {
	  //关闭eslint
	  lintOnSave: false
	  }

2.3:2.3 src文件夹配置别名,创建jsconfig.json,用@/代替src/

{
  "compilerOptions": {
    "target": "es5",
    "module": "esnext",
    "baseUrl": "./",
    "moduleResolution": "node",
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    //文件运行后产生的
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  }
}



3、注册路由

3.1:

import VueRouter from "vue-router";
import Vue from "vue";

// 引用路由文件
import routes from './routes'

// 使用路由
Vue.use(VueRouter)

// 对外包括,方便在mian.js中应用
export default new VueRouter({
    routes
})


3.2 routes文件夹统一管理路由

 import Home from '../pages/Home/'
 
 const routes=
[
    {
    //query参数的跳转是path的路径,params的跳转参数是name
         path:'/Home',
         name:'/Home',
         component:Home
    }
]
export default routes

3.3 main.js中注册路由,一定不能忘记在Vue中注册路由

	import Vue from 'vue'
	import router from './router'
	
	new Vue({
	  router,
	  render: h => h(App),
	}).$mount('#app')



4、注册全局组件

在main.js中注册过的组件,在全局都是有效的,不需要在任何vue文件中引用,直接用就好

	// 引入公共组件中的TypeNav
	import TypeNav from './components/TypeNav.vue'
	
	// 注册全局组件 第一个参数就是组件的名字
	Vue.component("TypeNav",TypeNav)

直接全局组件TypeNav,注册过

	<template>
	  <div id="app">
	    <Header/>
	    <TypeNav/>
	    <router-view/>
	    <Footer/>
	  </div>
	</template>	



5.重定向:

在初始化项目时,路由自动跳转到设置的路由

// 重定向
    {
       
        path: '/',
        redirect: '/Home'
      },



6. 二次封装axios

主要是要用到请求拦截器和响应拦截器;

请求拦截器:可以在发请求之前可以处理一些业务,配置下baseURL路径

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

	// 封装axios
	import axios from "axios";
	
	// 
	const requests =axios.create({
	    // 配置基础路径,发请求的时候,会直接带上基本路径,不需要重复书写
	    baseURL:'/api',
	    // 相应事件5s,超过则失败
	    timeout:5000
	})
	
	requests.interceptors.request.use((config)=>{
	    // config是一个非常重要的属性,包含着发送的请求头,许多信息可以写在里里面
	    return config
	})
	export default requests

在index.js文件中书写相对应的地址

import requests from "./requests";

// /api/product/getBaseCategoryList 三级菜单
export const reqgetCategoryList=()=>requests({
    url:`/product/getBaseCategoryList`,
    methods:'get'
})



7.代理跨域问题

3.1:先放在这里,之后来填

跨域

的坑

vue.config.json

	module.exports = ({
	  // 代理跨域
	  devServer:{
	    proxy:{
	      '/api':{
	        target:'http://gmall-h5-api.atguigu.cn',
	      }
	    }
	  }
	})



8.Mock

8.1:封装一个mockrequest的二次封装,

和之前封装的相同,不过baseURL不同,在这里封装了,在index.js中就不需要再次书写

	// 封装axios
	import axios from "axios";
	
	// 
	const MockRequests =axios.create({
	    // 配置基础路径,发请求的时候,会直接带上基本路径,不需要重复书写
	    baseURL:'/mock',
	    // 相应事件5s,超过则失败
	    timeout:5000
	})
	
	MockRequests.interceptors.request.use((config)=>{
	    // config是一个非常重要的属性,包含着发送的请求头,许多信息可以写在里里面
	    return config
	})
	export default MockRequests

8.2 创建mock响应内容

在src下创建mock文件夹,创建mockServer.js,同时在文件下下创建

其他的json数据文件

	import Mock from "mockjs";
	
	// 引入json文件
	import banner from './banner.json'
	import floor from './floor.json'
	
	//mock数据:第一个参数请求地址、第二个参:请求数据	
	Mock.mock('/mock/banner',{code:200,data:banner})
	Mock.mock('/mock/floor',{code:200,data:floor})
	
	//记得要在main.js中引入一下
	//import ''@/mock/mockServer

8.3 在api中发送请求,注意请求变了

	import MockRequests from "./MockRequests";
	
	// banner轮播图mock
	export const reqBannerList =()=>MockRequests({
	    url:`/banner`,
	    methods:'get'
	})
	
	// floor页面 mock
	export const reqFloorList =()=>MockRequests({
	    url:`/floor`,
	    methods:'get'
	})



9.接口数据被分割成两个模块

9.1 可以使用v-show配合v-for一起使用,最好不用v-if,会显示报错

vue2中,v-for的优先级比v-if高,所以警告,并不影响运行

vue3中,v-if的优先级比v-for高

但是如果一起用,还是会浪费性能,所以项目还是老老实实的computed计算属性

v-for="(img,index) in item.recommendList" :key="index" v-show="index<2"



10、轮播图swiper的封装

挖坑



11、Vuex

11.1 安装vuex npm i vuex@3

注意:vueRouter和vuex

	 "vue": "^2.6.14",
    "vue-router": "^3.6.5",
    "vuex": "^3.6.2"



12.编程式导航+事件委托解决路由跳转

12.1 事件委托:
	 概念:
    
     事件委托也叫事件代理,“事件代理”即是把原本需要绑定在子元素的响应事件(click keydown…)委托给
     父元素,让父元素担当事件监听的职务。事件代理的原理是DOM元素的事件冒泡。

举个例子

 - 英语老师要受英语作业,老师让学习委员统一收取作业,再交给她
 
	学习委员就充当了委托中的父元素

好处:不需要对任何的一个子元素进行单独处理,而是汇总起来批量处理,可以大量节省内存占用,减少事件注册,比如在ul上代理所有li的click事件。

	<ul id="list">
	  <li>item 1</li>
	  <li>item 2</li>
	  <li>item 3</li>
	  ......
	  <li>item n</li>
	</ul>
	// ...... 代表中间还有未知数个 li

12.2

在这里插入图片描述

实现点击任何标题都可以跳转到Search页面

实现思路:

      <div class="all-sort-list2" v-for="(nav) in categoryList" :key="nav.categoryId">
                        <div class="item bo">
                            <h3>
                                <!-- 一级菜单 -->
                                <a  @click="goSearch" :data-categoryName='nav.categoryName' :data-							   categoryId1='nav.categoryId'>{{nav.categoryName}}</a>
                            </h3>
                            <div class="item-list clearfix" >
                                <div class="subitem" v-for="nav1 in nav.categoryChild" :key='nav1.categoryId'>
                                    <dl class="fore">
                                        <dt>
                                            <!-- 二级菜单 -->
                                            <a  @click="goSearch" :data-categoryName='nav1.categoryName' :data-categoryId2='nav1.categoryId'>{{nav1.categoryName}}</a>
                                        </dt>
                                        <dd >
                                            <em v-for=" nav2 in nav1.categoryChild " :key="nav2.categoryId">
                                                <!-- 三级菜单 -->
                                                <a @click="goSearch" :data-categoryName='nav2.categoryName' :data-categoryId3='nav2.categoryId'>{{nav2.categoryName}}</a>
                                            </em>
                                          
                                        </dd>
                                    </dl>
                                </div>
                            </div>
                        </div>                     
                    </div>
                </div>
1、需要在点击获取点击时的数据,自定义属性就登上帷幕,在event.target.dataset属性中可以获取
2、利用es6新属性,解构赋值,取出属性,判断
3、跳转路由,这个地方写的特别好,分别定义location和query,随后赋值
 goSearch(event){
            // 获取当前点击的数据
            let element =event.target
            console.log(element)
            
            let {categoryid1,categoryid2,categoryid3,categoryname}=element.dataset

            console.log(categoryid1,categoryid2,categoryid3,categoryname  )

            // 如果有categoryname的话那就是a标签里面的
            if(categoryname){
              
            //   创建跳转路由
            let location={name:'Search'}

            // 跳转参数
            let query ={categoryName:categoryname}
                if(categoryid1){
                    query.categoryid1=categoryid1
                }else if(categoryid2){
                     query.categoryid2=categoryid2
                }else if(categoryid3){
                   query.categoryid3=categoryid3
                }
               location.query=query
                 this.$router.push(location)
            }
          
        },



13.transition过度效果



14.父子组件通信面包屑


Vue组件间通信的几种常见方式:

  1. props / $emit



  2. e

    m

    i

    t

    /

    emit/






    e


    mi


    t


    /





    on 全局时间总线$bus

  3. ref / $refs 父子
  4. 依赖注入(provide / inject) 祖孙子
  5. Vuex 全局(用的多)

这里我们用的是props/$emit子父通信

在这里插入图片描述

需求:点击子组件,在index页面上呈现面包屑的效果
分析:
	1、绑定点击事件
	2、绑定emit的事件 this.$emit('事件名',传参)
	3、父组件中写自定义事件
	4、渲染页面
<template>
			//简化代码,方便查看
          <li v-for="mark in trademarkList " :key="mark.id"  @click="BrandHandler(mark)">{{mark.tmName}}</li>
  
           <a @click="attrHandler(attr2,attr)">{{attr2}}</a>
</template>

<script>
  export default {
    name: 'SearchSelector'
    methods:{
      // 点击品牌,出现面包屑
      BrandHandler(Brand){
        this.$emit('BrandHandler',Brand)
      },
      // 属性的面包屑
      attrHandler(attr2,attr){
         this.$emit('attrHandler',attr2,attr.attrId)
      }
    }
  }
</script>

//传参props       or  子传父事件      
< template>  
                      	
<SearchSelector :attrsList='attrsList' :trademarkList='trademarkList' @BrandHandler='BrandHandler' @attrHandler='attrHandler'/>

</template>
<script>
 methods:{
 
      // 品牌的面包屑 添加props
	   BrandHandler(Brand){
	   // es6 解构
	        let props=`${Brand.tmId}:${Brand.tmName}`
	        if( this.searchParams.props.indexOf(props)==-1){
	       		 this.searchParams.props.push(props)
	       }
      },
      // 属性的面包屑同样添加props
      attrHandler(attr,attrId){
	       let props =`${attrId}:${attr}`
	       if( this.searchParams.props.indexOf(props)==-1){
	        	this.searchParams.props.push(props)
	       }
      },
</script>



15.分页器

这里我懒了,不太像手写分页器了,用了element-ui就当是锻炼下插件能力了

14.1 安装element-ui (npm i element-ui)

14.2main.js中注册(官网有安装教程)

import Vue from 'vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css';

Vue.use(ElementUI)

new Vue({
  render: h => h(App),
}).$mount('#app')

14.3 在element-ui中取分页器代码,自己改下

 <div class="block page">
            <el-pagination
              :current-page.sync="searchParams.pageNo"
              :page-size="searchParams.pageSize"
              layout="prev, pager, next, jumper"
              :total="searchParams.total">
            </el-pagination>
 </div>



第三个页面 /detail

在这里插入图片描述

五个需求点:

  1. 左上方的面包屑
  2. 左边图片的轮播图和图片
  3. 中间的属性选择
  4. 数量的增减和改变
  5. 加入购物车的跳转

左上方的面包屑 :

1、 分析:参数是由上一层Search传输而来的路由参数,在向服务器发送数据的时候带上这个参数

routes.js

在这里插入图片描述

Detail.js

在这里插入图片描述

控制台的数据传回

在这里插入图片描述

渲染上页面:

在这里插入图片描述

面包屑效果图:

加粗样式

2、左边图片的轮播图和图片:

分析:父子组件通信、ImageList子组件,利用swiper轮播官网提供组件

效果图:

在这里插入图片描述

实现大图和轮播图的兄弟通信功能:

imageList:

在这里插入图片描述

Zoom兄弟组件:

在这里插入图片描述


这里特别说明一下使用全局$bus的时候记得main.js中注册,否则就是报错emit/on

反思:这个案例还是非常有代表性的组件通信,动态改变页面功能,剖析本质,组件通信也就这回事

  1. 中间的属性选择 :

    在这里插入图片描述

    分析:选择属性只能选择一种,所以可以考虑到排他算法,这是最优解

    在这里插入图片描述

    item是单独的属性值 arr是item的父级,把父级的所有置空,然后一个子级脱颖而出,写好active就好

  2. 数量的增减和改变:

在这里插入图片描述

mouseout:鼠标移除的时候触发校正

  1. 加入购物车的跳转

    在这里插入图片描述

    跳转路由的方法有编程式路由和。。。。,这里就是编程式路由,比较直观的带参

第四个页面 AddCartSuccess

在这里插入图片描述

三个需求:

  • 图片渲染上页面
  • 文字和带参skuNum渲染页面
  • 两个跳转页面(原路由,新路由)

思路:这是一个小页面。所以在数据上,接口并未提供返回数据,仅提供向服务器增添数据的接口,这时候选择用sessionStorage会话储存来保存上个页面的数据,也可以做到页面间的数据共享,不过劣势在于仅单次使用有效。

图片渲染上页面:

通过会话储存来存储Detail页面的数据,注意要更改成Json格式,否则就是object报错,再在下个页面取出

在这里插入图片描述

在这里插入图片描述



第五个页面:shopcart

在这里插入图片描述

七个需求:

  1. 商品的基础渲染
  2. 商品的增减、保证输入框是整数
  3. 删除商品
  4. 选择商品
  5. 全选全部商品
  6. 选择商品件数和总金额呜
  7. 结算页面的选择



UUID

接口: url:

/cart/cartList

,在接口文档中,是一个get请求,无法在参数中传递身份信息,只能在requsts 分装函数中定义

先在src下创建utils工具,创建uuid的js文件,对外暴露函数(记得 npm install uuid)

import {v4 as uuidv4} from 'uuid'

// 生成临时游客的uuid,不能发生变化,耗能持久储存
export const getUUID =()=>{
    // 1、判断本地储存是否uuid
    let uuid_token =localStorage.getItem('UUIDTOKEN')
    // 2、本地没有uuid
    if(!uuid_token){
        // 2.1 生成uuid
        uuid_token =uuidv4()
        //2.2储存本地
        localStorage.setItem('UUIDTOKEN',uuid_token)
    }
    // 当用户有uuid时就不会生成
    return uuid_token
}

用户的uuid_token定义在Detail.js中

import { getUUID } from "@/utils/uuid";

const state ={
    goodList:[],

    // 游客身份
    uuid_token:getUUID()
}

requests.js中去发送uuid的参数信息给服务器,返回数据


import store from "@/store";

// 请求拦截器:在请求发出去之前会作出一些操作
requests.interceptors.request.use((config)=>{

    // 1、先判断uuid是否为空
    if(store.state.Detail.uuid_token){
        // 2、userTempId字段和后端统一
        config.headers.userTempId = store.state.Detail.uuid_token
    }
    
    nProgress.start();
    nProgress.done()
    return config
})



输入框的加减以及数字的取整

temlple部分

      <!-- 减号 -->
          <li class="cart-list-con5">
            <a  class="mins" @click="handler('minus',-1,cart)">-</a>
            
            <!-- 显示框 -->
            <input 
            autocomplete="off" 
            type="text" 
            :value="cart.skuNum"
            minnum="1" 
            class="itxt"
            @change="handler('input',$event.target.value,cart)"
           >
           
           <!-- 加号 -->
            <a  class="plus" @click="handler('add',1,cart)" >+</a>
          </li>

逻辑部分

      // 通过服务器更改产品数量
     async handler(type,disNum,cart){

        // 如果type是minus的
        if(type=='minus'){
          // 因为值的本身不能低于1
          disNum =cart.skuNum <1 ? 0:-1
        }

        // 如果type是add的话
        if(type=='add'){
          disNum=1
        }

        // 如果type==change
        if(type=='input'){
          if(isNaN(disNum)&&disNum>0){
            disNum=0
          }else{
            disNum=parseInt(disNum)-cart.skuNum
          }
        }
      try {
        this.$store.dispatch('getAddorReduceShopCart',{skuId:cart.skuId,skuNum:disNum})
        this.getData();
      } catch (error) {
        alert(error)
      }
      }



版权声明:本文为qq_53523338原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。