# SPH-project
**Repository Path**: ouzai-zai/sph-project
## Basic Information
- **Project Name**: SPH-project
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 1
- **Created**: 2021-12-01
- **Last Updated**: 2024-06-09
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# SPH
[TOC]
## 一、初始化项目
- vue-cli脚手架初始化项目。
- node + webpack + 淘宝镜像
- 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:缓存性文件
### 1.1、自动打开项目
在package.json中找到运行代码的那部分并加上 --open
```js
"scripts": {
"serve": "vue-cli-service serve --open",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
```
### 1.2、eslint校验关闭
在根目录下创建vue.config.js
比如:声明变量但是没有使用。运行时就会报错
```js
module.exports = {
lintOnSave:false
}
```
### 1.3、配置别名
在根目录下创建 jsconfig.json
```js
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": [
"src/*"
]
}
},
"exclude": [
"node_modules",
"dist"
]
}
```
## 二、路由传参
2.1、路由的跳转方式:
- **声明式导航**:router-link(务必有to属性),可以实现路由的跳转
- **编程式导航**:利用的是组件实例的$router.push | replace 方法,可以实现路由的跳转(可以书写一些自己的业务)
2.2、路由传参
- **params**参数:属于路径当中的一部分,需要注意,在配置路由的时候,需要占位
- **query**参数:不属于路径当中的一部分,类似于ajax中的queryString
- 下文中的所有toUpperCase()是指把字符串进行大写
- 第一种:字符串形式
- 首先,在配置路由中给需要传参的路由路径后加:和一个形参
- 然后使用params的方法传递
- 最后通过这样的方式进行获取
- 第二种:模板字符串
- 第一步、第三步与上相同,不同的是第二步
- 
- 第三种:对象(最常用的)
- 在配置路由中给需要传参的路由加上一个name属性
- 
- 
面试题:
1. 路由传递参数(对象写法)path是否可以结合params参数一起使用?
1. 答:路由跳转传参的时候,对象的写法可以是name、path形式,但是注意,path这种写法不能与params参数一起使用
2. 
2. 如何指定params参数可传可不传?
1. 如果路由要求传递params参数,但是你就不传递params参数。
2. 指定params参数传递、或者不传递,在配置路由的时候,在占位的后面加上一个问号
3. params参数可以传递或不传递,但是如果传递的是空串,如何解决?
1. 使用undefined解决:params参数可以传递、不传递(空字符串),如果传递的是空串,路径会出现问题
2. 
## 三、编程式路由多次点击报错
因为路由跳转它有一个返回值,返回一个**promise**值
通过给push方法传递相应的成功、失败的回调函数,可以捕获到当前的错误
```js
// 先把VueRouter原型对象的push,保存一份
let originPush = VueRouter.prototype.push;
// 重写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) {
originPush.call(this, location, resolve, reject)
} else {
originPush.call(this, location, () => { }, () => { })
}
}
```
## 四、二次封装axios
### 4.1、为什么需要进行二次封装axios?
> - 主要为了请求拦截器、响应拦截器
>
> - 请求拦截器:可以在发请求之前处理一些业务
> - 响应拦截器:当服务器返回数据之后,可以处理一些业务
### 4.2、创建request文件
在项目中经常在API文件夹中创建request
```js
// 首先是引用axios
// 对于axios进行二次封装
import axios from "axios";
// 1、利用axios对象的方法create,去创建一个axios实例
// 2、requests就是axios
const requests = axios.create({
baseURL: '',
timeout: 5000 // 超时时间
})
// 请求拦截器:在发请求之前,请求拦截器可以检测到,可以在请求发出之前做一些事
requests.interceptors.request.use((config) => {
// config:配置对象。对象里面有一个属性很重要,header请求头
return config
})
// 响应拦截器
requests.interceptors.response.use((res) => {
// 成功的回调函数:服务器相应数据回来以后,响应拦截器可以检测到
return res.data
}, (error) => {
// 响应失败的回调函数
return Promise.reject(new Error('faile'))
// return error
})
// 对外暴露
export default requests
```
### 4.3、创建index文件
```js
// 对api进行统一管理
import axios from "axios";
import requests from "./request";
// 三级接口联动
// /api/product/getBaseCategoryList get 无参数
// 发请求axios发请求返回Promise对象
export const reCategoryList = () => requests({
url: '/api/product/getBaseCategoryList',
method: 'get'
})
```
## 五、跨域问题
跨域:指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。
在vue.config.js中写入
```js
module.exports = {
lintOnSave: false,
// 代理跨域
devServer: {
proxy: {
'/api': {
target: 'http://39.98.123.211',
},
},
},
}
```
## 六、nprogress进度条
- 使用npm i nprogress --save 来安装这个插件
- 然后在request.js中引入nprogress
- ```js
import nProgress from "nprogress";
```
- start:进度条开始,done:进度条结束
- 分别写在请求拦截器和响应拦截器里
- ```js
// 请求拦截器:在发请求之前,请求拦截器可以检测到,可以在请求发出之前做一些事
requests.interceptors.request.use((config) => {
// config:配置对象。对象里面有一个属性很重要,header请求头
nProgress.start()
return config
})
// 响应拦截器
requests.interceptors.response.use((res) => {
// 成功的回调函数:服务器相应数据回来以后,响应拦截器可以检测到
nProgress.done()
return res.data
}, (error) => {
// 响应失败的回调函数
return Promise.reject(new Error('faile'))
// return error
})
```
## 七、Vuex状态管理库
### 7.1、什么是vuex
> vuex是官方提供的一个**插件**,**状态管理库**,**集中式**管理项目中组件共用的数据
### 7.2、使用方法
- 首先创建store文件夹,下创建index.js文件
- 引入vue和vuex,再使用插件一次
- 初始化vuex
- ```js
import Vue from "vue";
import Vuex from 'vuex';
Vue.use(Vuex);
// state:仓库储存数据的地方
const state = {}
// mutations:修改state的唯一手段
const mutations = {}
// actions:处理action,可以书写自己的业务逻辑,也可以处理异步
const actions = {}
// getters:可以理解为计算属性,用于简化仓库数据,让组件获取仓库数据更加方便
const getters = {}
const store = new Vuex.Store({
state,
mutations,
actions,
getters
});
export default store
```
- 最后在main.js中引入
- ```js
// 引入vuex
import store from '@/store'
new Vue({
render: h => h(App),
router,
store
}).$mount('#app')
```
### 7.3、vuex模块式开发
- 初始化index.js文件
- ```js
import Vue from "vue";
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
});
```
- 创建各组件的小仓库例如
- 进行小仓库初始化
- ```js
const state = {}
const mutations = {}
const actions = {}
const getters = {}
export default {
state,
mutations,
actions,
getters
}
```
- 再将各小仓库和大仓库合并
- ```js
import home from "./home/home";
export default new Vuex.Store({
// modules里面注册你所需要的小仓库
modules: {
home
}
});
```
### 7.4、使用vuex获取数据
- 首先发出请求
- ```js
mounted(){
// 这里的caregoryList和vuex中组件的小仓库中的actions对象是对应的
this.$store.dispatch('caregoryList')
},
```
- 在小仓库这边引入封装好的接口
- ```js
// 通过api里面的接口函数调用,向服务器发送请求,获取数据
import { reqCategoryList } from '@/api'
```
- 书写第一步定义的方法**caregoryList**
- ```js
const actions = {
async caregoryList({ commit }) {
// 首先执行reqCategoryList方法,它的返回值是一个promise
let result = await reqCategoryList()
// console.log(result);
如果返回的code为200,则为成功,
if (result.code == 200) {
// 但是它没有CATEGORYLIST,所以需要在mutations中写
commit('CATEGORYLIST', result.data)
}
}
}
```
- 在mutations对象中写
- ```JS
const mutations = {
CATEGORYLIST(state, categoryList) {
// state.caregoryList:仓库的数据, categoryList:服务器返回的数据
state.caregoryList = categoryList
}
}
```
- 在state中写起始值,用来存储
- ```js
const state = {
caregoryList: []
}
```
- 这时候通过开发者工具就能看见state中已经有数据了
- 
## 八、节流与防抖
在多个事件需要执行时,如果用户操作太快,这多个事件会因为浏览器反应不过来而不会全部都执行,只会执行个别。
> 防抖:前面的所有的触发都被取消,最后一次执行在规定的时间之后才会触发,也就是说如果连续快速触发,只会执行一次
>
> - 使用loadsh.js插件进行防抖,可通过官网下载或者 npm i loadsh --save 进行安装
> - **函数的使用方法:**`_.debounce(func, [wait=0], [options=])`
> - `func` *(Function)*: 要防抖动的函数。
> - `[wait=0]` *(number)*: 需要延迟的毫秒数。
> - `[options=]` *(Object)*: 选项对象。
> - `[options.leading=false]` *(boolean)*: 指定在延迟开始前调用。
> - `[options.maxWait]` *(number)*: 设置 `func` 允许被延迟的最大值。
> - `[options.trailing=true]` *(boolean)*: 指定在延迟结束后调用。
>
> - 
> 节流:在规定的间隔时间范围内不会重复触发回调,只要大于这个时间间隔才会触发回调,把频繁触发变为少量
>
> - 使用loadsh.js插件进行防抖,可通过官网下载或者 npm i loadsh --save 进行安装
> - **函数的使用方法:**`_.throttle(func, [wait=0], [options=])`
> - `func` *(Function)*: 要节流的函数。
> - `[wait=0]` *(number)*: 需要节流的毫秒。
> - `[options=]` *(Object)*: 选项对象。
> - `[options.leading=true]` *(boolean)*: 指定调用在节流开始前。
> - `[options.trailing=true]` *(boolean)*: 指定调用在节流结束后。
> - 
## 九、多级菜单的路由跳转及传参
传参的方式有两种,声明式导航和编程式导航。如果菜单有很多的话,不适合用声明式导航,因为每一个声明式导航都是一个组件,影响性能,出现卡顿等bug。
主要手段:利用**事件委派** + **编程式导航**实现路由跳转及传参
> 存在的问题:
>
> 1、事件委派,是把全部的子节点【h3、dt、dl】等的事件委派给父节点。
>
> 如何解决点击a标签的时候,才会进行路由跳转,也就是怎么确定点击的一定是a标签
>
> 解决的方法:
>
> 使用自定义属性,给所有的a标签定义一个属性,键为自定义,值为一级菜单的内容,如:
>
> 如何获取当前点击的子节点?
>
> 通过**event**中的event.target,但是不需要所有的节点,只需要带有自定义属性data-categoryName这样类名的子节点
>
> ```js
> goSearch(event){
> let element = event.target
> console.log(emelent)
> }
> ```
>
> 可以通过event.target.dataset属性,获取节点的自定义属性与值
>
> 使用解构赋值的方式更直观,caregory1id就是一级菜单的id,也是通过自定义标签设置的(看第二个问题),以此类推
>
> 然后就是判断是不是带这个类名的
>
> 
> 存在的问题:
>
> 2、如何区别一级菜单、二级菜单、三级菜单的标签
>
> 解决的方法:
>
> 也通过自定义标签来解决
>
> 
>
> 都设置好了就开始判断
>
> ```js
> if (categoryname) {
> // 指定要跳转的路由
> let loaction = { name: "search" };
> // 创建一个变量,用来传获取到的标签数据,
> let query = { categoryName: categoryname };
> // 因为要区分多级菜单,所有采用动态传入id值
> if (category1id) {
> query.category1Id = category1id;
> } else if (category2id) {
> query.category2Id = category2id;
> } else if (category3id) {
> query.category3Id = category3id;
> }
> // 整理参数,将获取到的标签名传入loaction变量中
> loaction.query = query;
> console.log(loaction);
> // 下面拼接起来就是这样
> // this.$router.push({name: 'search',query:{categoryName:'xxx',id:xxx}})
> this.$router.push(loaction);
> }
> ```
## 十、过渡动画
给需要过渡动画的部分套上`