项目功能界面:

技术选型:

前端路由:

笔记:
vue-cli脚手架初始化项目:
node_modelus文件夹:项目依赖文件夹
public文件夹:一般放置静态资源(图片),webpack进行打包的时候会原封不动打包到dist文件夹中。
src文件夹(程序员源代码文件夹)
assets: 存放共用的静态资源,webpack打包时会把静态资源当作一个模块打包到js文件里
components: 一般放非路由组件(全局组件),其他组件放在views或者pages文件夹中
App.vue: 唯一的跟组件
main.js: 程序入口文件,最先执行的文件
babel.config.js: 配置文件(babel相关)
package.json: 项目的详细信息记录
package-lock.json: 缓存性文件(各种包的来源)
README.md:项目说明文件
---------------------------------------------------------------------------------------
项目配置(配置完后要重新启动项目才有效):
1. 项目运行,浏览器自动打开
----package.json中
"scripts": {
"serve": "vue-cli-service serve --open",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
2. 关闭eslint校验工具(不关闭会有各种规范,不按照规范就会报错)
----vue.config.js中
module.exports = {
//关闭eslint
lintOnSave: false
}
3. src简写方法,配置别名:
jsconfig.json配置别名@提示(@代表的是src文件夹,这样将来文件过多,找的时候方便很多)
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": [ "src/*"]
}
},
"exclude": ["node_modules","dist"]
}
---------------------------------------------------------------------------------------
项目路由的分析
确定项目结构顺序:上中下 -----只有中间部分的在发生变化,中间部分应该使用的是路由组件
路由组件:
Home首页路由组件,Search搜索页组件,Login登陆路由组件,Refister注册路由组件
非路由组件:
Header(首页,搜索页)
Footer(首页,搜索页),但是在登录、注册页没有
---------------------------------------------------------------------------------------
在项目中,不再以html,css为主,主要搞业务、逻辑
在开发项目时:
1. 书写静态页面(html,css)
2. 拆分组件
3. 获取服务器的数据动态展示
4. 完成相应的动态业务逻辑
注意:创建组件时,组件结构+组件样式+图片资源
---------------------------------------------------------------------------------------
路由元信息(程序员自定义的信息):
配置路由的时候,可以给路由添加路由元信息meta,路由需要的是配置对象,它的key不能瞎写
{
path:'/about',
component:About,
meta:{...}
}
---------------------------------------------------------------------------------------
如果路由要求传递params参数,但是没有传递parmas参数,url会出问题
解决方法:指定params参数可传可不传
在配置路由的时候,在占位的后面加上一个?表示params可以传也可以不传
{
path:'/news/:id?',
component:xxx
}
如果params参数传递空串,url也会出问题
解决方法:用undefined解决
this.$router.push({
name:'xiangqing'
params:{id:""||undefined},
})
---------------------------------------------------------------------------------------
编程式路由跳转到当前路由(参数不变),多次执行会抛出NavigationDuplicated的警告错误?
声明式导航没有这类问题,因为vue-router底层已经处理好了
1. 为什么编程式导航进行路由跳转的时候,会有这种警告?
最新的vue-souter引入了promise
2. 解决(1):通过给push方法传递相应的成功、失败的回调,可以捕获到当前错误,可以解决
this.$router.push({name:"xxx",params:{},query: {}},()=>{},()=>{})
但是这种写法治标不治本,将来在别的组件中push|replace,编程式导航还是有类似错误
解决(2):在router/index.js中重写push和replace
this:当前组件实例
this.$router属性:属性值是VueRouter类的一个实例,当在入口文件注册路由时
,会给组件添加$router|$route属性
push:VueRouter原型上的方法
在main.js中:
先把VueRouter原型对象的push和replace保存一份
let originPush=VueRouter.prototype.push
let originReplace=VueRouter.prototype.replace
重写push|replace
第一个参数:告诉原来的push方法往哪跳转(传递哪些参数)
第二个参数:成功的回调
第三个参数:失败的回调
VueRouter.prototype.replace=function(location,resolve,reject){
if(resolve&&reject){
originReplace.call(this,location,resolve,reject)
}else{
originReplace.call(this,location,()=>{},()=>{})
}
}
VueRouter.prototype.push=function(location,resolve,reject){
if(resolve&&reject){
originPush.call(this,location,resolve,reject)
}else{
originPush.call(this,location,()=>{},()=>{})
}
}
---------------------------------------------------------------------------------------
nprogress进度条的使用:
安装:npm i --save nprogress
import nProgress from "nprogress"//引入进度条
import "nprogress/nprogress.css"//引入进度条样式
nProgress.start() //nProgress的start代表进度条开始,done代表进度条结束
nProgress.done() //nProgress的start代表进度条开始,done代表进度条结束
进度条颜色可以修改,需要修改人家的样式
---------------------------------------------------------------------------------------
axios二次封装:
为什么二次封装?
请求拦截器,响应拦截器
请求拦截器:可以在发请求之前处理一些业务
响应拦截器:当服务器数据返回后可以处理一些事情
在项目中经常出现一个api文件夹(axios)
src/api/request.js文件:
对于axios进行二次封装
import axios from "axios"
//引入进度条
import nProgress from "nprogress"
//引入进度条样式
import "nprogress/nprogress.css"
//利用axios对象的方法create去创建一个axios实例
//request就是axios,只不过稍微配置一下
const requests=axios.create({
//配置对象
baseURL:"/api",//设置默认url地址,基础路径,发请求时路径会出现api
timeout:5000,//代表请求超时的时间5s
})
//请求拦截器:在发送请求前,请求拦截器可以检测到,可以在请求发出去之前做一些事
requests.interceptors.request.use((config)=>{
//进度条开始动
nProgress.start() //nProgress的start代表进度条开始,done代表进度条结束
//config:配置对象,对象里又一个属性很重要,headers请求头
return config
})
//响应拦截器
requests.interceptors.response.use((res)=>{
nProgress.done() //nProgress的start代表进度条开始,done代表进度条结束
//成功的回调函数:服务器响应数据回来后,响应拦截器可以检测到,可以做一些事
return res.data
},(error)=>{
//响应失败的回调函数
return Promise.reject(new Error("faile"))
})
//对外暴露
export default requests
---------------------------------------------------------------------------------------
接口统一管理:
项目很小:完全可以在组件生命周期函数中发请求
项目很大:API统一管理
在在src下的api文件夹再新建一个index.js:
import requests from "./requert"; 引进之前二次封装的axios
export const xxx = () =>requests({ url: "/product/getBaseCategoryList", method: "get" }); //向外暴露函数
在要发请求的地方引入
import {xxx} from "xxx"
xxx() 直接调用函数就能发送请求
跨域问题:
协议、域名、端口号不同,称为跨域
解决:jsonp cros 代理
配置代理:
在vue.config.js的module.exports = defineConfig({})中加入
//代理跨域
devServer:{
proxy:{
'/api':{ //匹配所有以 '/api1'为前缀的请求路径并配置代理,如不希望传递/api则需要重写路径,
target:"http://gmall-h5-api.atguigu.cn",//要获取数据的服务器的ip地址
pathRewrite: {'^/api1': ''}
//让代理服务器拿到的地址不带设置的前缀,如果真实服务器地址有/api就不写
}
}
}
发请求时要在地址的端口号后面加上上面定义的前缀(加上前缀就强制走代理)
端口号8080可写可不写默认就是当前端口号
例如:
axios.get("http://localhost:8080/api/product/getBaseCategoryList").then((res) => {
console.log(res);
});
---------------------------------------------------------------------------------------
防抖和节流:
防抖:前面所有触发都被取消,最后一次执行在规定时间后才会触发,也就是如果连续快速触发,只触发一次
用户操作很频繁,但是只执行一次
节流:在规定的间隔时间内不会重复触发,只有大于这个时间间隔才会触发,频繁的操作变为少量操作
用户操作很频繁,但是把频繁的操作变为少量操作(可以给浏览器有充裕的时间解析代码)
lodash插件:里面封装函数的防抖与节流的业务(闭包+延迟器)
vue脚手架自带
使用时里面的回调函数不要用箭头,不然可能会出现this问题
在vue脚手架中使用lodash:
引入:import throttle from "lodash/throttle"(按需引入)
或import _ from "lodash"(全部引入)
methods:{
xxx:_.throttle(function(){
......
},1000), methods里要写成es5完整形式
xxx:throttle(function(){
......
},1000)
}
--------------------------------------------------------------------------------------
三级联动实现跳转路由功能
1. 如果把所有a标签变成声明式导航router-link的形式会出现卡顿现象
因为每个<router-link to="xxx"></router-link>属于一个组件,启动时突然生成的太多组件导致卡顿
2. 而给每个a标签绑定一个相同的点击事件进行编程式导航产生很多回调函数,也不太好
3. 用事件委派把点击事件绑给他们共同的父元素,给一级二级三级的a设置一个相同的自定义属性值为当前点
击a的categoryName,给一级二级三级的a分别设置一个不同的自定义属性值为当前a的categoryId,利用
event.target.dataset判断点击的是不是a和是几级分类,然后是跳转路由
自定义属性:
写法::data-xxx="xxx"
读取自定义属性:event.target.dataset(只能读取data-开头的自定义属性,data-后面的才是属性名)
注意:不管定义时的属性是大写还是小写,读取出来的自定义属性都是小写
设置query参数时一定要注意属性的大小写,不要跟自定义属性的小写弄混
---------------------------------------------------------------------------------------
过渡动画:前提是组件或元素必须有v-if或v-show指令才可以进行过度动画
----------------------------------------------------------------------------------------
mockjs模拟数据:
如果想模拟数据,需要用到一个插件mockjs
http://mockjs.com/
注意:json文件和图片都是默认暴露的,直接引入就能用
1. 在项目中src文件夹中创建mock文件夹
2. 准备JSON数据(mork文件夹中创建相应的JSON文件),格式化一下,别留空格(不然跑不起来)
3. 把mock数据需要的图片放置在public文件夹中(在public文件夹中新建一个images文件夹)
(piblic文件夹在打包的时候,会把相应的资源原封不动的打包的dist文件夹)
(必须这样不然图片会找不到!!!)
4. 创建mockServe.js文件通过mockjs插件实现模拟数据
import Mock from "mockjs" //引入mockjs模块
//把json数据格式引入(json数据格式没有对外暴露但可以引入)
import banner from './banner'
import floor from './floor' //webpack默认对外暴露的:图片,json数据格式
//mock数据:第一个参数为请求地址,第二个参数为请求数据
Mock.mock("mock/banner",{code:200,data:banner})
Mock.mock("mock/floor",{code:200,data:floor})
5. mockServe.js文件在入口文件main.js中中引入(至少执行一次,才能模拟数据,不用暴露)
import "@/mock/mokeServe"
------------------------------------------------------------------------------------
swiper插件:
https://www.swiper.com.cn/
经常制作轮播图(移动端|pc端也可以使用)
使用步骤:(视频中用的5版本)
1. 引入相应的依赖包(swiper.js|swiper.css)
在使用的组件中:import Swiper from "swiper"
在main.js中:import "swiper/css/swiper.css"
2. 页面中的结构务必要有
3. 初始化swiper实例,给轮播图添加动态效果
注意: 初始化Swiper时如果是把它封装为了全局组件或数据有多个要重复复用这个组件那么
var mySwiper = new Swiper ('.swiper', {})的时候不要写成class选择器和id选择器的形
式
记得加宽高,不然不生效
用id选择器只能使第一个轮播有效,用class选择器会出现bug
要用ref, var mySwiper = new Swiper (this.$refs.xxx, {})
---------------------------------------------------------------------------------------
模块开发流程:
1. 先静态页面+静态组件拆分出来
2. 发请求(ApI)
3. vuex(三连环)
4. 组件获取仓库数据,动态展示数据
----------------------------------------------------------------------------------------
Object.assign
Object.assign是ES6新添加的方法,主要的用途是用来合并多个JavaScript的对象。
Object.assign()接口可以接收多个参数,第一个参数是目标对象,后面的都是源对象,assign方法
将多个原对象的属性和方法都合并到了目标对象上面,如果在这个过程中出现同名的属性(方法),后
合并的属性(方法)会覆盖之前的同名属性(方法)。
var target = {a : 1}; //目标对象
var source1 = {b : 2}; //源对象1
var source2 = {c : 3}; //源对象2
var source3 = {c : 4}; //源对象3,和source2中的对象有同名属性c
Object.assign(target,source1,source2,source3);
结果如下:
{a:1,b:2,c:4}
----------------------------------------------------------------------------------------
有些带给服务器的参数不是必选的,可以不传
如果把它的值设为""还是会被带入服务器
如果把它的值设为undefined,那就不会带给服务器
----------------------------------------------------------------------------------------
一般轮播图组件会放在新建Carousel文件夹里
分页器组件会放在新建Pagination文件夹里
分页器显示,需要哪些数据(条件)?
需要知道当前是第几页 用pageNo表示
需要知道每一页需要展示多少条数据 用pageSize表示
需要知道整个分页器一共有多少条数据 用total表示
需要知道分液器一共多少页 totalPage total/pageSize向上取整
需要知道分页器连续页码的个数:5/7(一般为奇数),奇数对称好看 用continues表示
自定义分页器在开发时先自己传递假的数据进行调试,调试成功后再用服务器数据
对于分页器而言,很重要的地方是算出连续页面的起始数字和结束数字
-----------------------------------------------------------------------------------------
商品详情页面:
商品详情页面要注册为路由组件
点击商品图片的时候,跳转到详情页面,在路由跳转的时候需要带上产品id给详情页面
路由跳转后让滚动条在最顶部:
在设置路由的地方与路由平级
export default new VueRouter({
routes: [......],
scrollBehavior(to, from, savedPosition) {
return { y: 0 }; 返回的这个y=0代表滚动条在最上方
},
});
-----------------------------------------------------------------------------------------
如果跳转路由时要携带的参数是对象形式的(复杂数据类型)
为了url地址的美观可以把简单类型的参数放在params或query上,把对象用浏览器会话存储进行保存
进入跳转的路由时再取出来
为了节省内存一般采用SessionStorage会话存储
-----------------------------------------------------------------------------------------
购物车静态组件-需要修改样式结构
调整css让各个项目对齐删除第三项15 35 10 17 10 13
-----------------------------------------------------------------------------------------
在向服务器发起ajax获取购物车数据时,获取不到购物车里面的数据,因为服务器不知道你是谁
要在加入购物车的一瞬间告诉服务器你是谁
在发送请求时的请求拦截器中给请求头设置一个字段(和后台商量好的),字段的值必须为唯一的不变的
利用uuid可以解决,把字段值设为uuid的值
(也可以用随机数或Data.now(),但是随机数可能会重复,用随机数和Data.now()步骤和uuid一样,就是把
uuid换成了随机数或Data.now(),不能用Symbol("xxx")!!!)
uuid(脚手架已自带) https://www.npmjs.com/package/uuid
在src下新建一个文件夹utils,这个文件夹经常放一些工具模块(正则,uuid临时身份......)
在utils文件夹中新建uuid文件夹,在uuid文件夹新建index.js文件
1. 在src/utils/uuid/index.js文件中:
import { v4 as uuidv4 } from "uuid"; 引入uuid
要生成一个随机字符串,且每次执行不能发生变化,游客身份持久化存储
export const getUUID = () => {
先在本地存储看看有没有,如果没有就生成uuid并存进去并返回,有就直接返回
if (!localStorage.getItem("uuid")) {
localStorage.setItem("uuid", uuidv4());
}
return localStorage.getItem("uuid");
};
2. 在store相应的仓库中引入新建的uuid文件夹(哪个仓库都行,因为一上来就会执行就有数据)
import { getUUID } from "@/utils/uuid";生成游客身份模块uuid,生成一个随机字符串(不能变的)
并在state中设置uuid的值:
state: {
uuid: getUUID(), 游客临时身份
}
3. 在src/api/request.js中设置请求拦截器中的响应头:
import store from "@/store/index"; 引入store仓库
requests.interceptors.request.use((config) => {
if (store.state.detail.uuid) {
config.headers.userTempId = store.state.detail.uuid;请求头添加一个字段,和后台商量好的
}
nProgress.start();
return config;
});
-----------------------------------------------------------------------------------------
在修改商品数据时遇到的问题:
1. 点击删除按钮无效,报400错误
原因:vuex仓库触发actions里的xxx函数时只传入了一个参数
例如:actions: {
xxx(content,value){...} 被触发的xxx()函数里应该传入两个参数(必须两个),
} 只传入一个默认把我想传的第二个参数当成了第一个参数
2. 点击加减号修改数量点击一次没有变化,点两次才有变化而且是点两次的结果:
原因:点击触发的函数前面没有写async,函数里调用this.$store.dispatch前面没有写await
没加async和await导致上面的代码是异步的还没执行完结果还没回来就执行了下面的代码,下面
代码执行后无法获取更新的数据,使用await可以阻塞当前线程,将异步操作变成同步
3. 点击全选按钮上面数据input按钮更新的不全:
原因:和2一样,没加async和await
不加async和await上就等于上面和下面代码一起执行,完全看哪个执行快,上面代码执行的多,
上面的还没执行完没完全出结果就执行下面的,下面的就获取不到刚更新的数据
-----------------------------------------------------------------------------------------
登陆业务:
登陆成功后后台为了区分用户是谁,服务器会下发token(令牌:唯一标识),
前台持久化存储token(带着token找服务器要用户信息进行展示)
注意:
vuex仓库存储不是持久化的,一刷新就没了就获取不到用户信息了,所以要配合本地存储使用
登录成功后会返回一个token,要通过这个token获取用户信息
要把token用LocalStorage存起来,在vuex仓库中state中用一个值存储token
token:localStorage.getItem("token")
在请求拦截器中的请求头上带上token:
if (store.state.login.token) {
config.headers.token = store.state.login.token;
}
-----------------------------------------------------------------------------------------
获取交易页面用户信息:
用户登录了才可以获取用户地址信息,不登录没办法获取到
统一登录的账号:13700000000 密码:111111
自己注册的接口获取用户地址信息没有数据(可能统一登陆账户也没数据,那就用mock模拟一个数据)
-----------------------------------------------------------------------------------------
不用vuex直接在组件发请求:
在main.js中用通用方式导入写发请求的文件
import * as api from './api'
new Vue({
render: (h) => h(App),
beforeCreate()
Vue.prototype.$api = api;
},
}).$mount("#app");
-----------------------------------------------------------------------------------------
注意:
工作中尽量不要在生命周期函数前用async
因为async await有时候会把同步变异步而且返回值是Promise
如果需要使用async await可以把它封装到函数里然后在生命周期函数里调用
-----------------------------------------------------------------------------------------
二维码生成插件:
qrcode: https://www.npmjs.com/package/qrcode
可以将字符串转为二维码
弹窗使用Element ui this.$msgbox.close()表示关闭弹窗
二维码弹出后要一直发送请求,直到支付成功停止
-----------------------------------------------------------------------------------------
在未登录的情况下去支付,我的订单等页面不会被放行
而是会强制带到登录页面,但登录之后需要跳转到之前要访问的页面
方法:
可以在路由守卫中强制带到登录页面时把to.path的值保存下来,用query参数的形式带给登录页面
进入登录页面点击登录时加一个判断,如果有带过来的那个query参数就跳到参数的页面,没有再跳到home
-----------------------------------------------------------------------------------------
图片懒加载:https://www.npmjs.com/package/vue-lazyload
要安装1.3.3版本要不用不了,可能有版本冲突
-----------------------------------------------------------------------------------------
表单验证:vee-validate
https://www.npmjs.com/package/vee-validate
官方文档:https://vee-validate.logaretm.com/v2
安装2版本,不太好用建议使用别的
-----------------------------------------------------------------------------------------
路由懒加载:
当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割
成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就会更加高效。
Vue Router 支持开箱即用的动态导入,这意味着你可以用动态导入代替静态导入:
把import Home from "@/pages/Home" 替换为:
const Home = () => import('@/pages/Home')
routes: [
{ path: '/home', component:Home }
]
或者(这样更简单明了):
routes: [
{ path: '/home', component:() => import('@/pages/Home') }
]
-----------------------------------------------------------------------------------------
打包上线:
npm run build
项目打包后,代码都是经过压缩加密的,如果运行时报错,输出的错误信息无法准确得知是哪里的代码报错
有了map就可以像未加密的代码一样,准确的输出是哪一行出错
所以该文件如果项目不需要可以去除掉
vue.config.js中配置:productionSourceMap:false