尚品汇前台笔记

1.vue-cli 脚手架目录结构

vue-cli项目目录

node_modules文件夹:项目依赖文件夹
public文件夹:一般放置一些静态资源(图片),需要注意,放在public文件夹中的静态资源,webpack进行打包的时候,会原封不动地打包到dist文件夹中。
src文件夹(程序员源代码文件夹):
     assets文件:一般也是放置静态资源(一般放置多个组件共用的静态资源),需要注意。放置在assets文件夹里面的静态资源,在webpack打包的时候,webpack会把静态资源当做一个模块,打包在js文件里面。
     components文件夹:一般放置的是非路由组件(全局组件)
     App.vue:唯一的根组件,vue当中的组件(.vue)
     main.js:程序入口文件,也是整个程序当中最先执行的文件
babel.config.js:配置文件(babel相关)
package.json文件:认为项目‘身份证’,记录项目叫做什么、项目当中有哪些依赖、项目怎么运行
package-lock.json文件:缓存性文件
README.md:说明性文件

2. 项目的其他配置

2.1 项目运行起来的时候,让浏览器自动打开

package.json文件中修改"scripts"—>“serve”

"scripts": {
    "serve": "vue-cli-service serve --open",
},
2.2 eslint校验功能关闭

在根目录下,创建一个vue.config.js

module.exports = {
   // 关闭eslint
   lintOnSave: false
}
2.3 src文件夹简写方法,配置别名

新建jsconfig.json配置别名@(@代表的是src文件夹)

{
    "compilerOptions": {
        "baseUrl": "./",
        "paths": {
        "@/*": ["src/*"]
        }
    },
    "exclude": ["node_modules","dist"]
} 

3. 项目路由分析

3.1 项目开发步骤

1.书写静态页面(html+css)
2.拆分组件
3.获取服务器的数据动态展示
4.完成相应的业务逻辑

注意1:项目采用的是less样式,浏览器不识别less样式,需通过less、less-loader进行处理less,把less样式变为css样式,浏览器才可以识别。

npm install --save less less-loader

注意2:如果想让组件识别less样式。需要在style标签身上加上lang=less

<style lang="less"></style>
3.2 配置路由
3.2.1 新建router文件夹->index.js文件
  1. 引入Vue和VueRouter
import Vue from "vue";
import VueRouter from 'vue-router'
  1. 使用插件
Vue.use(VueRouter)
  1. 引入路由组件
import Home from '@/pages/Home'
  1. 配置路由
export default new VueRouter({
    // 配置路由
    routes:[
        {
            path:'/home',
            component:Home
        }// 重定向:在项目跑起来的时候,访问,立马让他定向到首页
        {
            path:'*',
            redirect:"/home"
        }
    ]
})
3.2.2 在main.js 中引入注册
  1. 引入
// 引入路由
import router from '@/router'
  1. 注册路由
new Vue({
  render: h => h(App),
  // 注册路由:下面的写法是kv一致省略v
  // 注册路由信息:当这里书写router的时候,组件身上都拥有$route,$router属性
  router
}).$mount('#app')
  1. App.vue 显示路由
<!-- 路由组件出口的地方 -->
<router-view></router-view>
3.3 路由组件和非路由组件
3.3.1 组件的使用步骤
  1. 创建或定义组件
  2. 引入 import Header from './components/Header/index.vue'
  3. 注册 components:{Header}
  4. 使用 <Header></Header>
3.3.2 路由组件与非路由组件的区别
1.路由组件一般放置在pages|views文件夹,非路由组件一般放置在components文件夹中
2.路由自己一般需要在router文件夹中进行注册(使用的即为组件的名字),非路由组件在使用的时候,一般都是以标签的形式使用
3.注册完路由,不管是路由组件、还是非路由组件身上都有$route/$router属性
    $route:一般获取路由信息(路径、query、params等)
	$router:一般进行编程式导航的路由跳转(push|replace)			
3.4 路由的跳转
  1. 声明式导航router-link
<!-- 声明式导航,务必要有to属性 -->
<router-link to="/login">登录</router-link>
  1. 编程式导航push|replace
//html
<button @click="goSearch">
  搜索
</button>
//js
goSearch(){
   this.$router.push('/search')
}

4. Footer组件的显示与隐藏

显示或隐藏组件:v-if|v-show

4.1 我们可以根据组件身上的$route获取当前路由的信息,通过路由路径判断Footer的显示与隐藏
<Footer v-show="this.$route.path == '/home' || this.$route.path == '/search'"></Footer>
4.2 配置路由的时候,可以给路由添加路由元信息(meta)
//router->index.js
//配置路由
{
    path: '/home',
    component: Home,
    meta: { show: true }
}
//App.vue
//根据路由元信息控制组件显示、隐藏
<Footer v-show="$route.meta.show"></Footer>

5. 路由传参

5.1 路由的跳转有几种方式
  1. 声明式导航:router-link(务必要有to属性),可以实现路由的跳转
<router-link to="/login">登录</router-link>
  1. 编程式导航:利用的是组件实例的$router.push|replace方法,可以实现路由的跳转(可以书写一些自己业务)
 this.$router.push('/search')
5.2 路由传参,参数有几种写法
  1. params参数:属于路径当中的一部分,需要注意,在配置路由的时候,需要占位
//router->index.js->配置路由
{
    path: '/search/:keyword?',
    component: Search,
    meta: { show: true },
    name: 'search'
}
  1. query参数:不属于路径当中的一部分,类似于ajax中的queryString [/home?k=v&kv=],不需要占位
  2. 路由传递参数的方式
    (1)字符串形式
this.$router.push('/search/'+this.keyword+"&k="+this.keyword.toUpperCase())

(2)模板字符串

this.$router.push(`/search/${this.keyword}?k=${this.keyword.toUpperCase()}`)

(3)对象形式

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

6. 路由传参相关面试题

6.1 路由传递参数(对象写法)path是否可以结合params参数一起使用

答:路由跳转传参的时候,对象的写法可以是name、path形式,但是需要注意的是,path这种写法不能与params参数一起使用this.$router.push({path:‘/search’,params:{keyword:this.keyword}})

6.2如何指定params参数可传可不传

比如:配置路由的时候,占位了(params参数),但是路由跳转的时候就不传参,路径就会出现问题
如果路由要求传递params参数,但是你就不传递params参数,会发现url会有问题
如果指定params参数可以传递、或者不传递,在配置路由的时候,在占位的后面加上一个问号(params可以传递或者不传递)
this.$router.push({name:‘search’,query:{k:this.keyword.toUpperCase()}})

6.3 params参数可以传递也可以不传递,但如果传递的是空串,如何解决

使用undefined解决
this.$router.push({name:‘search’,params:{keyword:‘’||undefined},query:{k:this.keyword.toUpperCase()}})

7.编程式路由跳转到当前路由(参数不变),多次执行会抛出NavigationDuplicated的警告错误?

解决办法:重写push | replace

// 先把VueRouter原型对象的push,先保存一份
let originPush = VueRouter.prototype.push;
let originReplace=VueRouter.prototype.replace
// 重写push|replace
// 第一个参数:告诉原来的push方法,你往哪里跳转(传递哪些参数)
VueRouter.prototype.push = function (location, resolve, reject) {
    if (resolve && reject) {
        originPush.call(this, location, resolve, reject)
    }else{
        originPush.call(this,location,()=>{},()=>{})
    }
}

VueRouter.prototype.replace = function (location, resolve, reject) {
    if (resolve && reject) {
        originReplace.call(this, location, resolve, reject)
    }else{
        originReplace.call(this,location,()=>{},()=>{})
    }
}

8. 全局组件的使用

8.1 在main.js中引入全局组件
import TypeNav from '@/pages/Home/TypeNav'
8.2 在main.js中注册全局组件
// 第一个参数:全局组件的名字  第二个参数:哪一个组件
Vue.component(TypeNav.name,TypeNav)
8.3 使用(直接使用即可)
<!-- 三级联动全局组件:三级联动已经注册为全局组件,不用再引入了 -->
<TypeNav></TypeNav>

9. axios二级封装

9.1 为什么需要进行二次封装axios

请求拦截器:可以在发请求之前处理一些业务
响应拦截器:当服务器数据返回以后,可以处理一些事情

9.2 在项目中经常放置在api的文档
  1. 引入axios
import axios from "axios";
  1. 创建axios实例

1.利用axios对象的create方法,去创建一个axios实例
2.request就是axios,只不过稍微配置一下

const requests = axios.create({
    // 配置对象
    // 基础路径,发请求的时候,路径当中会出现api
    baseURL: "/api",
    // 代表请求超过的时间
    timeout: 5000
})
  1. 请求拦截器

请求拦截器:在发请求之前,请求拦截器可以检测到,可以在请求发出去之前做一些事情

// config:配置对象,对象里面有一个属性很重要,headers请求头
requests.interceptors.request.use((config) => {
    return config
})
  1. 响应拦截器
requests.interceptors.response.use((res) => {
    // 成功的回调函数:服务器响应数据回来以后,响应拦截器可以检测到,可以做一些事情
    return res.data
}, (error) => {
    // 响应失败的回调函数
    return Promise.reject(new Error('fail'))
})
  1. 对外暴露
export default requests

完整代码:

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

// 1.利用axios对象的create方法,去创建一个axios实例
// 2.request就是axios,只不过稍微配置一下
const requests = axios.create({
    // 配置对象
    // 基础路径,发请求的时候,路径当中会出现api
    baseURL: "/api",
    // 代表请求超过的时间
    timeout: 5000
})

// 请求拦截器:在发请求之前,请求拦截器可以检测到,可以在请求发出去之前做一些事情
// config:配置对象,对象里面有一个属性很重要,headers请求头
requests.interceptors.request.use((config) => {
    return config
})


// 响应拦截器
requests.interceptors.response.use((res) => {
    // 成功的回调函数:服务器响应数据回来以后,响应拦截器可以检测到,可以做一些事情
    return res.data
}, (error) => {
    // 响应失败的回调函数
    return Promise.reject(new Error('fail'))
})


// 对外暴露
export default requests

10. 统一接口管理

api->index.js(所有接口在这里进行发送请求)

10.1 引入requests
import requests from "./request";
10.2 发请求
export const reqCategoryList = () => requests({ url: "/product/getBaseCategoryList", method: 'get' })

完整代码:

// 当前这个模块,api进行统一管理
import requests from "./request";
// 发请求:axios发请求返回结果为Promise对象
export const reqCategoryList = () => requests({ url: "/product/getBaseCategoryList", method: 'get' })

11. 跨域问题

什么是跨域:协议、域名、端口号不同的请求,称之为跨域

在vue.config.js中配置代理

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

12. nprogress进度条的使用

在发送请求时显示进度条,在api–>request.js中使用

12.1 引入进度条
import nprogress from "nprogress";
12.2 引入进度条样式
import "nprogress/nprogress.css"
12.3 请求拦截器中进度条开始
requests.interceptors.request.use((config) => {
    // 进度条开始
    nprogress.start()
    return config
})
12.4 响应拦截器中进度条结束
requests.interceptors.response.use((res) => {
    // 成功的回调函数:服务器响应数据回来以后,响应拦截器可以检测到,可以做一些事情
    // 进度条结束
    nprogress.done()
    return res.data
}, (error) => {
    // 响应失败的回调函数
    return Promise.reject(new Error('fail'))
})

13. Vuex状态管理库

13.1 vuex是什么?

vuex是官方提供一个插件,状态管理库,集中式管理项目中共用的数据
切记,并不是全部项目都需要vuex,如果项目很小,完全不需要vuex,如果项目很大、组件很大、数据很多,数据维护很费劲

13.2 vuex基本使用

1. 下载vuex

npm install --save vuex

下载完成后,查看package.json:"vuex": "^3.6.2"

2. 新建store文件夹—>index.js文件,实行仓库模块式开发存储数据。
(1)引入Vue、Vuex

import Vue from "vue";
import Vuex from "vuex";

(2)使用一次插件

Vue.use(Vuex)

(3)引入子仓库

import home from "./home";

(4)对外暴露Store类的一个实例,并实现vuex仓库模块化开发存储数据

export default new Vuex.Store({
    // 实现vuex仓库模块式开发存储数据
    modules:{
        home,
        search
    }
})

3. 在store文件夹中新建一个仓库,为子仓库

// state:仓库存储数据的地方
const state={}

// mutation:修改state的唯一属性
const mutations={}

// actions:处理action,可以书写自己的业务逻辑,也可以处理异步
const actions={
    // 这里可以书写业务逻辑,但不能修改state
}

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

export default{
    state,
    mutations,
    actions,
    getters
}

4. 在main.js中进行注册

// 引入仓库
import store from './store'
new Vue({
  render: h => h(App),
  // 注册仓库:组件实例身上会多一个$store属性
  store
}).$mount('#app')
13.3 vuex 实现

1. 组件挂载完毕,可以向服务器发请求

mounted() {
   // 通知vuex发请求,获取数据,存储于仓库中
   this.$store.dispatch("categoryList");
 },

2.引入请求

import { reqCategoryList } from "@/api"

3. 在action中通过API里面的接口函数调用,向服务器发请求,获取服务器数据

commit 将后台获取到的数据提交到mutation

const actions = {
    // 通过API里面的接口函数调用,向服务器发请求,获取服务器数据
    async categoryList({ commit }) {
        let result = await reqCategoryList()
        if (result.code == 200) {
            commit("CATEGORYLIST",result.data)
        }    
    }
}

4. 在state中初始化数据

state中数据默认初始值别瞎写,根据服务器返回数据而定

const state = {
    categoryList:[]
}

5. 在mutation中修改属性

const mutations = {
    CATEGORYLIST(state,categoryList){
        state.categoryList=categoryList
    }
}

6. 在组件中引入并使用仓库

右侧需要的是一个函数,当使用这个计算属性的时候,右侧函数会立即执行一次
注入一个参数state,其实即为大仓库中的数据

import {mapState} from 'vuex'
computed:{
  ...mapState({
    categorList:state=>state.home.categoryList
  })
}

查看vue开发者工具,是否获得数据
在这里插入图片描述
完整代码

  1. 仓库
import { reqCategoryList } from "@/api"
// state:仓库存储数据的地方
const state = {
    // state中数据默认初始值别瞎写,根据服务器返回数据而定2.
    categoryList: []
}

// mutation:修改state的唯一属性
const mutations = {
    CATEGORYLIST(state, categoryList) {
        state.categoryList = categoryList
    }
}

// action:处理action,可以书写自己的业务逻辑,也可以处理异步
const actions = {
    // 这里可以书写业务逻辑,但不能修改state
    // 通过API里面的接口函数调用,向服务器发请求,获取服务器数据
    async categoryList({ commit }) {
        let result = await reqCategoryList()
        if (result.code == 200) {
            commit("CATEGORYLIST", result.data)
        }
    }
}
// getters:理解为计算属性,用于简化仓库数据,让组件获取仓库的数据更加方便
const getters = {}

export default {
    state,
    mutations,
    actions,
    getters
}
  1. 组件
import {mapState} from 'vuex'
export default {
  name: "TypeNav",
  // 组件挂载完毕,可以向服务器发请求
  mounted() {
    // 通知vuex发请求,获取数据,存储于仓库中
    this.$store.dispatch("categoryList");
  },
  computed:{
    ...mapState({
      // 右侧需要的是一个函数,当使用这个计算属性的时候,右侧函数会立即执行一次
      // 注入一个参数state,其实即为大仓库中的数据
      categorList:state=>state.home.categoryList
    })
  }
};

14. 完成一级分类动态添加背景颜色

14.1 采用样式完成
14.2 通过js完成
  1. 存储用户鼠标移上哪一个一级分类
data() {
   return {
     currentIndex: -1,
   };
 },
  1. 鼠标进入修改响应式数据
changeIndex: throttle(function (index) {
   this.currentIndex = index;
 }, 50),
  1. 一级分类鼠标移除的事件
leaveIndex() {
    this.currentIndex = -1;
}

15. 通过js控制二三级商品分类的显示与隐藏

:style="{display:currentIndex==index?'block':'none'}"

16. 演示卡顿现象

正常:事件触发非常频繁,而且每触发一次,回调函数都要去执行(如果时间很短,二回调函数内部有计算,那么很有可能出现浏览器卡顿)

节流:在规定的时间范围内不会重复触发回调,只有大于这个时间间隔才会触发回调,把频繁触发变为少量触发

防抖:前面的所有的触发都被取消,最后一次执行在规定时间之后才会触发,也就是说如果连续款速的触发,只会执行一次

三级联动节流
使用lodash中封装的节流方法lodash官网

  1. 引入throttle
import throttle from "lodash/throttle";
  1. 使用
changeIndex: throttle(function (index) {
    this.currentIndex = index;
}, 50)

17. 三级联动组件的路由跳转与参数传递

利用 [ 事件委派+编程式导航 ] 实现路由跳转与传递参数

17.1 事件委派,是把全部的子节点(h3,dt,dl,em)的事件委派给父亲节点,点击a标签的时候,才会进行路由跳转(怎么确定点击的一定是a标签)

把子节点当中的a标签,加上自定义属性data-categoryName,其余子节点是没有的

<a :data-categoryName="c1.categoryName">{{ c1.categoryName }}></a>
17.2 如何区分是以一级、二级、三级分类的标签

给一级、二级、三级分类的标签分别添加自定义属性

17.3 代码
goSearch(event) {
   // 编程式导航+事件委派
   let element = event.target;
   console.log(element.dataset);
   //获取到当前触发这个事件的节点,需要带有data-categoryName,这样节点一定是a标签
   //节点有一个dataset属性,可以获取节点的自定义属性和属性值
   let { categoryname, category1id, category2id, category3id } =
     element.dataset;
   // 如果标签上有categoryname一定是a标签
   if (categoryname) {
     // 整理路由跳转的参数
     let location = { name: "search" };
     let query = { categoryName: categoryname };
     // 区分一级分类、二级分类、三级分类的a标签
     if (category1id) {
       query.category1Id = category1id;
     } else if (category2id) {
       query.category2Id = category2id;
     } else {
       query.category3Id = category3id;
     }
     // 整理参数
     location.query = query;
     // 路由跳转
     this.$router.push(location);
   }
 }

18. 开发Search模块中的TypeNav商品分类菜单(过渡动画效果)

过渡动画:前提组件|元素务必要有v-if|v-show指令才可以使用过渡动画

在三级联动div外部添加一个trensition标签

 <!-- 过渡动画 -->
<transition name="sort">
	<!-- 三级联动 -->
	<div class="sort" v-show="show">
		......
	</div>
</transition>

过渡动画样式

.sort-enter {
  height: 0px;
}
.sort-enter-to {
  height: 461px;
}
.sort-enter-active {
  transition: all 0.5s linear;
}

19.typeNav三级联动列表的性能优化

出现问题:当路由在Home和Search中切换的时候,切换一次,getBaseCategoryList便会发起一次请求,现想要实现只发起一次请求
实现方法:在App根组件当中发请求:根组件mounted只执行一次

mounted() {
    // 派发一个action,获取商品分类的三级联动列表
    // 通知vuex发请求,获取数据,存储于仓库中
    this.$store.dispatch("categoryList");
},

20. 合并params和query参数

当从home页面跳转到search页面时,会传递params参数和query参数,现在出现一个问题,即两种参数不能同时进行传递。

TypeNav组件中修改goSearch方法

//  如果路由跳转是,带有params参数,也要传递过去
if (this.$route.params) {
	location.params = this.$route.params;
	// 动态给location添加query参数
	location.query = query;
	// 路由跳转
	this.$router.push(location);
}

Header组件中修改goSearch方法

if (this.$route.query) {
	// query参数也要带过去
	let location = {
	 name: "search",
	 params: { keyword: this.keyword || undefined },
	};
	location.query=this.$route.query
	this.$route.push(location)
}

21. swiper

swiper官网
使用方法
1. 引包(相应 js、css)

import Swiper from "swiper";
import 'swiper/css/swiper.css'

2.页面中结构务必要有

<div class="swiper-wrapper">
	<div
		class="swiper-slide"
		v-for="(item, index) in list"
		:key="item.id"
		>
		<img :src="item.imgUrl" />
	</div>
</div>

3. new swiper实例(轮播图添加动态效果)

var mySwiper = new Swiper(this.$refs.cur, {
	loop: true, // 循环模式选项
	// 如果需要分页器
	pagination: {
		el: ".swiper-pagination",
		clickable: true,
	},
	// 如果需要前进后退按钮
	navigation: {
		nextEl: ".swiper-button-next",
		prevEl: ".swiper-button-prev",
	},
	// 如果需要滚动条
	scrollbar: {
		el: ".swiper-scrollbar",
	},
});

22. 实现首页轮播图

最完美的解决方案
   watch + nextTick:数据监听:监听已有数据变化
   $nextTick:在下次dom更新,循环结束之后 执行延迟回调,在修改数据之后立即使用这个方法,获取更新后的dom
   $nextTick:可以保证页面中的结构一定是有的
 watch: {
    list: {
      immediate: true,
      handler(oldValue, newValue) {
        this.$nextTick(() => {
          // 使用ref获取dom
          var mySwiper = new Swiper(this.$refs.cur, {
            ......
          });
        });
      },
    },
  },
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值