1:node + webpack + VScode + 谷歌浏览器 + git
2:数组的方法 + promise + await + async + 模块化…
vue init webpack 项目的名字
vue create 项目名称
脚手架目录:public + assets文件夹区别
node_modules:放置项目依赖的地方
public:一般放置一些共用的静态资源,打包上线的时候,public文件夹里面资源原封不动打包到dist文件夹里面
src:程序员源代码文件夹
-----assets文件夹:经常放置一些静态资源(图片),assets文件夹里面资源webpack会进行打包为一个模块(js文件夹里面)
-----components文件夹:一般放置非路由组件(或者项目共用的组件)
App.vue 唯一的根组件
main.js 入口文件【程序最先执行的文件】
babel.config.js:babel配置文件
package.json:看到项目描述、项目依赖、项目运行指令
package-lock.json:缓存性文件
README.md:项目说明文件
在package.json文件中
//脚手架五有bug,建议不配置
"scripts": {
"serve": "vue-cli-service serve --open",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
创建vue.config.js文件:需要对外暴露
module.exports = {
lintOnSave:false,
}
现在脚手架生成该文件,直接添加配置
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
lintOnSave: false
})
因为项目大的时候src(源代码文件夹):里面目录会很多,找文件不方便,设置src文件夹的别名的好处,找文件会方便一些
创建jsconfig.json文件,脚手架已经生成改文件
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"baseUrl": "./",
"moduleResolution": "node",
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"exclude": [
"node_modules",
"dist"
]
}
vue-router
路由分为Key-Value
前端路由:
K即为URL(网络资源定位符)
V即为相应的路由组件
npm i vue-router@3
vue2中使用vue-router的3版本
vue3中使用vue-router的4版本
router/index.js
//引入VueRouter
import Vue from 'vue';
import VueRouter from 'vue-router';
//使用插件
Vue.use(VueRouter);
//引入路由 组件
import Home from '@/pages/Home'
import Search from '@/pages/Search'
import Register from '@/pages/Register'
import Login from '@/pages/Login'
//创建router实例对象,去管理一组一组的路由规则
export default new VueRouter({
routes: [{
path: '/search',
component: Search
},
{
path: '/home',
component: Home
},
{
path: '/login',
component: Login
},
{
path: '/register',
component: Register
},
]
})
main.js 添加程序
import router from '@/router';
new Vue({
render: h => h(App),
router
}).$mount('#app')
(2个:Header、Footer)
非路由组件使用分为几步:
第一步:定义
第二步:引入
第三步:注册
第四步:使用
项目采用的less样式,浏览器不识别less语法,需要一些loader进行处理,把less语法转换为CSS语法
1:安装less less-loader@5
npm i less less-loader@5
切记less-loader安装5版本的,不要安装在最新版本,安装最新版本less-loader会报错,报的错误setOption函数未定义
2022/3/23 可以安装最新版less-loader
"less": "^4.1.2",
"less-loader": "^10.2.0",
2:需要在style标签的身上加上lang="less"
,不添加样式不生效
路由的跳转就两种形式:
给主页的a和搜索按钮分别换成声明式导航和编程式导航
登录
免费注册
面试题:v-show与v-if区别?
v-show:通过样式display控制
v-if:会操作dom
面试题:开发项目的时候,优化手段有哪些?
1:v-show|v-if
2:按需加载
路由元信息meta
{
path: '/search/:keyword',
component: Search,
meta: {
footerShow: true
}
},
//传递字符串 ,也可以使用模板字符串
this.$router.push(
"/search/" + this.keyword + "?k=" + this.keyword.toUpperCase()
);
路由传递参数先关面试题
路由传递参数(对象写法)path是否可以结合params参数一起使用?
答:路由跳转传参,对象可以说name、path形式,但是path形式不可以和params结合使用
如何指定params参数可传可不传?
答:配置路由占位,必须传递参数,配置占位后面加上问号,那么这个参数可传可以不传
{
path: '/search/:keyword?',
component: Search,
meta: {
footerShow: true
}
},
params参数可以传递也可以不传递,但是如果传递是空串,如何解决?
undefined解决:params:{keyword:''|undefined}
如果指定name与params配置, 但params中数据是一个"", 无法跳转,路径会出问题
路由组件能不能传递props数据?
{
name:'xiangqing',
path:'detail/:id',
component:Detail,
//第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
// props:{a:900}
//第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
// props:true
//第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
props(route){
return {
id:route.query.id,
title:route.query.title
}
}
}
编程式导航路由跳转到当前路由(参数不变), 多次执行会抛出NavigationDuplicated的警告错误?
注意:编程式导航(push|replace)才会有这种情况的异常,声明式导航是没有这种问题,因为声明式导航内部已经解决这种问题。
这种异常,对于程序没有任何影响的。
为什么会出现这种现象:
由于vue-router最新版本,引入了promise,当传递参数多次且重复,会抛出异常,因此出现上面现象,
this.$router.push(
{
name: "search",
params: { keyword: this.keyword },
query: { k: this.keyword.toUpperCase() },
},
() => {},
() => {}
);
//这种解决方案可以暂时解决当前问题,但是以后再用push|replace还是会出现类似现象,因此我们需要从‘根’治病;
//this.$router.push
//this :当前组件实例
//this.$router属性:VueRouter的一个实例,当在入口文件注册路由的时候,给组件实例添加$router|$route属性
//push在实例$router的原型对象上
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,
() => {},
() => {}
);
}
};
开发步骤
1.完成静态页面(样式)
2.拆分静态组件
3.发请求获取服务器数据进行展示
4.开发动态业务
拆分组件:结构+样式+图片资源
这里使用postman
写项目的时候发请求在哪里发
AJAX:客户端可以’敲敲的’向服务器端发请求,在页面没有刷新的情况下,实现页面的局部更新。
Ajax的相关函数:XMLHttpRequest、$、fetch、axios
跨域:如果多次请求协议、域名、端口号有不同的地方,称之为跨域
为什么需要二次封装axios?
安装axios
npm i axios
接口的统一管理
跨域问题
进度条:nprogress模块实现进度条功能npm i nprogress
vuex
vue2中使用vuex的3版本
vue3中使用vuex的4版本
vuex:书写任何项目都需要vuex?
项目大的时候,需要有一个地方‘统一管理数据’即为仓库store
Vuex基本使用:
//对于axios进行二次封装
import axios from "axios";
import nprogress from "nprogress";
//在当前模块中引入store
import store from '@/store';
//如果出现进度条没有显示:一定是你忘记了引入样式了
import "nprogress/nprogress.css";
//底下的代码也是创建axios实例
let requests = axios.create({
//基础路径
baseURL: "/api",
//请求不能超过5S
timeout: 5000,
});
//请求拦截器----在项目中发请求(请求没有发出去)可以做一些事情
requests.interceptors.request.use((config) => {
//现在的问题是config是什么?配置对象:有个属性很重要,headers请求头
//可以让进度条开始动
if (store.state.detail.uuid_token) {
//请求头添加一个字段(userTempId):和后台老师商量好了
config.headers.userTempId = store.state.detail.uuid_token;
}
//需要携带token带给服务器
if (store.state.user.token) {
config.headers.token = store.state.user.token;
}
nprogress.start();
return config;
});
//响应拦截器----当服务器手动请求之后,做出响应(相应成功)会执行的
requests.interceptors.response.use(
(res) => {
//进度条结束
nprogress.done();
//响应成功做的事情
return res.data;
},
(err) => {
alert("服务器响应数据失败");
//return Promise.reject(new Error('faile'))
}
);
//最终需要对外暴露(不对外暴露外面模块没办法使用)
//这里的代码是暴露一个axios实例
export default requests;
:class="{ cur: currentIndex == index }
,动态添加样式触发: 回调函数都要去执行(如果时间很短,而回调函数内部有计算,那么很可能出现浏览器卡顿)
防抖: 前面的所有的触发都被取消,最后一次执行在规定的时间之后才会触发,也就是说如果连续快速的触发,只会执行最后一次
节流: 在规定的间隔时间范围内不会重复触发回调,只有大于这个时间间隔才会触发回调,把频繁触发变为少量触发
_.throttle(func, [wait=0], [options=])
节流_.debounce(func, [wait=0], [options=])
防抖选择合适的路由导航
第一种声明式导航:为什么使用router-link组件的时候,会出现卡顿那?
router-link是一个组件:相当于VueComponent类的实例对象,一瞬间
new VueComponent很多实例(1000+),很消耗内存,因此导致卡顿。
第二种编程式导航:push|replace
data-* 全局属性 是一类被称为自定义数据属性的属性,它赋予我们在所有 HTML 元素上嵌入自定义数据属性的能力,并可以通过脚本在 HTML 与 DOM 表现之间进行专有数据的交换。
data-categoryName ==>event.target.dataset.categoryname 转化为小写
data-category-name ==>event.target.dataset.categoryName 单词用-连接,第二个单词开始,首字母大写
this.$router.push()
{
name:'search',
query:{
categoryName:'电子书',
category2Id:4
}
}
v-for在大项目中index索引值切记加上
项目:home切换到search或者search切换到home,你会发现一件事情,组件在频繁的向服务器发请求,获取三级联动的数据进行展示。
为什么需要合并参数(query|params):因为这些参数,对于search是有用的,因为search通过这些参数向服务器发请求,需要把这些参数携带给服务器,服务器就会返回相应的用户的搜索的数据,search就可以进行展示。
开发的三级联动业务,当你点击a标签的时候,会进行路由的跳转,将产品的名字与id传递给search模块----(query)
点击搜索按钮的时候,用户输入进来的关键字,点击按钮的时候会通过params参数传递给search模块-----(params)
路由跳转(home->search),两个地方,三级联动(typeNav)、Header组件(搜索按钮)
合并 params和query参数
点击搜索传递params参数
点击三级联动链接传递query参数
但是有了一个参数后,采用另外一种方式参数会消失
这里合并参数来进行搜索
goSearch() {
if (this.$route.query) {
let location = {
name: "search",
params: { keyword: this.keyword || undefined },
};
location.query = this.$route.query;
this.$router.push(location);
}
},
mock数据【生成随机数据,拦截Ajax请求】。
注意:mock(模拟数据)数据需要使用到mockjs模块,可以帮助我们模拟数据。
注意:mockjs【并非mock.js mock-js】
下载
npm i mockjs
生成随机数据,拦截 Ajax 请求的理解
使用步骤
1.安装依赖包mockjs
2.在src文件夹下创建一个文件夹,文件夹mock文件夹。
3.准备模拟的数据
**把mock数据需要的图片放置于public文件夹中! **
模拟数据JSON:没有空格,最好使用格式化插件进行格式化
listContainer中的轮播图的数据
[
{
"id": "1",
"imgUrl": "/images/banner1.jpg"
},
{
"id": "2",
"imgUrl": "/images/banner2.jpg"
},
{
"id": "3",
"imgUrl": "/images/banner3.jpg"
},
{
"id": "4",
"imgUrl": "/images/banner4.jpg"
}
]
istContainer中的轮播图的数据
[
{
"id": "001",
"name": "家用电器",
"keywords": [
"节能补贴",
"4K电视",
"空气净化器",
"IH电饭煲",
"滚筒洗衣机",
"电热水器"
],
"imgUrl": "/images/floor-1-1.png",
"navList": [
{
"url": "#",
"text": "热门"
},
{
"url": "#",
"text": "大家电"
},
{
"url": "#",
"text": "生活电器"
},
{
"url": "#",
"text": "厨房电器"
},
{
"url": "#",
"text": "应季电器"
},
{
"url": "#",
"text": "空气/净水"
},
{
"url": "#",
"text": "高端电器"
}
],
"carouselList": [
{
"id": "0011",
"imgUrl": "/images/floor-1-b01.png"
},
{
"id": "0012",
"imgUrl": "/images/floor-1-b02.png"
},
{
"id": "0013",
"imgUrl": "/images/floor-1-b03.png"
}
],
"recommendList": [
"/images/floor-1-2.png",
"/images/floor-1-3.png",
"/images/floor-1-5.png",
"/images/floor-1-6.png"
],
"bigImg": "/images/floor-1-4.png"
},
{
"id": "002",
"name": "手机通讯",
"keywords": [
"节能补贴2",
"4K电视2",
"空气净化器2",
"IH电饭煲2",
"滚筒洗衣机2",
"电热水器2"
],
"imgUrl": "/images/floor-1-1.png",
"navList": [
{
"url": "#",
"text": "热门2"
},
{
"url": "#",
"text": "大家电2"
},
{
"url": "#",
"text": "生活电器2"
},
{
"url": "#",
"text": "厨房电器2"
},
{
"url": "#",
"text": "应季电器2"
},
{
"url": "#",
"text": "空气/净水2"
},
{
"url": "#",
"text": "高端电器2"
}
],
"carouselList": [
{
"id": "0011",
"imgUrl": "/images/floor-1-b01.png"
},
{
"id": "0012",
"imgUrl": "/images/floor-1-b02.png"
},
{
"id": "0013",
"imgUrl": "/images/floor-1-b03.png"
}
],
"recommendList": [
"/images/floor-1-2.png",
"/images/floor-1-3.png",
"/images/floor-1-5.png",
"/images/floor-1-6.png"
],
"bigImg": "/images/floor-1-4.png"
}
]
4.在mock文件夹中创建一个server.js文件
注意:在server.js文件当中对于banner.json||floor.json的数据没有暴露,但是可以在server模块中使用。
对于webpack当中一些模块:图片、json,不需要对外暴露,因为默认就是对外暴露。
5.通过mock模块模拟出数据
//先引入mockjs模块
import Mock from 'mockjs';
//把JSON数据格式引入进来[JSON数据格式根本没有对外暴露,但是可以引入]
//webpack默认对外暴露的:图片、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});
6.回到入口文件,引入serve.js
mock需要的数据|相关mock代码页书写完毕,关于mock当中serve.js需要执行一次,
如果不执行,和你没有书写一样的。
7.在API文件夹中创建mockRequest【axios实例:baseURL:’/mock’】
专门获取模拟数据用的axios实例。
//对于axios进行二次封装
import axios from "axios";
import nprogress from "nprogress";
//如果出现进度条没有显示:一定是你忘记了引入样式了
import "nprogress/nprogress.css";
//底下的代码也是创建axios实例
let requests = axios.create({
//基础路径
baseURL: "/mock",
//请求不能超过5S
timeout: 5000,
});
//请求拦截器----在项目中发请求(请求没有发出去)可以做一些事情
requests.interceptors.request.use((config) => {
//现在的问题是config是什么?配置对象
//可以让进度条开始动
nprogress.start();
return config;
});
//响应拦截器----当服务器手动请求之后,做出响应(相应成功)会执行的
requests.interceptors.response.use(
(res) => {
//进度条结束
nprogress.done();
//相应成功做的事情
return res.data;
},
(err) => {
alert("服务器响应数据失败");
}
);
//最终需要对外暴露(不对外暴露外面模块没办法使用)
//这里的代码是暴露一个axios实例
export default requests;
在开发项目的时候:切记,单元测试,某一个功能完毕,一定要测试是否OK
swiper移动端可以使用,pc端也可以使用。这个插件经常可以快速开发轮播图。
前端开发:轮播图、分页器、日历。
swiper常用于哪些场景?
https://www.swiper.com.cn/ 官网地址
swiper使用步骤:
(开发ListContainer组件【首页banner图片】)
**提示:**卸载插件,你可以不用删除node_modules文件夹,可以使用npm uninstall xxxx进行卸载
npm i swiper@5
但是需要注意,home模块很多组件都使用到swiper.css,没必要在每一个组件内部都引入样式一次,
只需要在入口文件引入一次即可。
//main.js
import "swiper/css/swiper.css";
//ListContainer/index.vue
import Swiper from "swiper";
Swiper需要获取到轮播图的节点DOM,才能给swiper轮播添加动态效果,因为没有获取到节点。
同学的想法:都不是完美的【错误的想法】
updated里面:如果组件有很多响应式(data),只要有一个属性值发生变化updated还会再次执行,再次初始化实例。不需要重复初始化swiper
**总结:**延迟器解决方案可以通过延迟器(异步)去解决问题,但是这种解决方案存在风险(无法确定用户请求到底需要多长时间),因此没办法确定延迟器时间。
v-for,在遍历来自于Vuex数据(数据:通过ajax向服务器发请求,存在异步)
watch:监听属性,watch可以检测到属性值的变化,当属性值发生变化的时候,可以出发一次。
Vuex当中的仓库数据bannerList(组件在使用):
bannerList仓库数据有没有发生过变化?
组件实例的一个方法:$nextTick
this.$nextTick(()=>{
})
this.$nextTick(function () {
new Swiper(".swiper-container", {
//this.$refs.mySwiper,
loop: true, // 循环模式选项
// 如果需要分页器
pagination: {
el: ".swiper-pagination",
clickable: true,
},
// 如果需要前进后退按钮
navigation: {
nextEl: ".swiper-button-next",
prevEl: ".swiper-button-prev",
},
// 如果需要滚动条
scrollbar: {
el: ".swiper-scrollbar",
},
});
});
nextTick官网解释:
在下次DOM更新, 循环结束之后,执行延迟回调。在 修改数据之后 立即使用这个方法,获取更新后的DOM。
注意:组件实例的$nextTick方法,在工作当中经常使用,经常结合第三方插件使用,获取更新后的DOM节点
开发Floor组件:Floor组件它被复用的(重复使用两次)
1:Floor组件获取mock数据,发请求的action书写在哪里?
派发action应该是在父组件的组件挂载完毕生命周期函数中书写,因为父组件需要通知Vuex发请求,父组件获取到mock数据,通过v-for遍历,生成多个floor组件,因此达到复用作用。
2.父组件派发action,通知Vuex发请求,Home父组件获取仓库的数据,通过v-for遍历出多个Floor组件
3.v-for|v-show|v-if|这些指令可以在自定义标签(组件)的身上使用
4组件间通信面试必问的东西
5.为什么在Floor组件的mounted中初始化SWiper实例轮播图可以使用.
因为父组件的mounted发请求获取Floor组件,当父组件的mounted执行的时候。
Floor组件结构可能没有完整,但是服务器的数据回来以后Floor组件结构就一定是完成的了,因此v-for在遍历来自于服务器的数据,如果服务器的数据有了,Floor结构一定的完整的。
发现home多次使用相同结构 轮播图
如果项目当中出现类似的功能,且重复利用,封装为全局组件----【不封装也可以】
为了封装全局的轮播图组件:让Floor与listContainer组件中的代码一样,如果一样完全可以独立出来封装为一个全局组件。
步骤:
1.书写静态页面【布局、样式】
2.拆分组件
3.获取服务器数据展示数据(发请求【API】,vuex,获得数据)
4.玩业务
//是搜索模块需要携带给接口的参数
{
"category1Id": "61",//一级分类的id
"category2Id": "61",//二级分类的id
"category3Id": "61",//三级分类的id
"categoryName": "手机",//产品的名字
"keyword": "小米",//关键字
"order": "1:desc",//排序
"pageNo": 1,//当前第几页
"pageSize": 10,//每一页需要展示多少条数据
"props": ["1:1700-2799:价格", "2:6.65-6.74英寸:屏幕尺寸"],//平台属性的选择参数
"trademark": "4:小米"//品牌参数
}
套路1:路由自己跳自己----修改路由
套路2:watch监听路由的变化发请求
@tradeMarkInfo= "tradeMarkInfo"
请求的性能优化:
发一个请求,需要向服务器携带参数:参数多,消耗的带宽多,空值可以将其改为undefind
对于给服务器携带的参数:如果数值为undefind,向服务器发请求的时候,参数不携带给服务器的
什么是富文本,插件有哪些,如何使用【在Vue当中使用】
完成排序业务
综合与价格按钮,点击谁,谁的背景颜色变为红色。(类名:active)
谁有类这件事情,区分开综合与价格
将来点击综合||价格,还是需要给服务器发请求
【价格升序:把这个信息给服务器传递过去,服务器接收到信息,数据库自动把排序这件事情做了,把排序做好的数据返回给你,你展示即可】
前端三大件:轮播图、分页、日历。这属于前端开发常见三种业务
分页功能,两种方式
为什么很多项目中都采用分页功能?
比如电商平台:搜索一个奶粉,奶粉的产品有10000+,一次渲染10000+条数据,可能慢。数据多的时候,可以选择分页,比如每一次只是展示10
拆分分页组件(静态组件),注册为全局组件,因为其他模块也在使用分页功能。
面试当中:你自己封装过一个通用的组件吗?-----分页组件
封装分页器组件的时候:需要知道哪些条件?
分页器组件需要知道我一共展示多少条数据 ----total【100条数据】
每一个(页)需要展示几条数据------pageSize【每一页3条数据】
需要知道当前在第几页-------pageNo[当前在第几页]
需要知道连续页码数【起始数字、结束数字:连续页码数市场当中一般5、7、9】奇数,对称好看 continues
一般当前页码是连续页码的中央,但是也有最开始与结束的特殊情况,下面有一些分析
已经条件: total=【99】 pageSize =【3】 pageNo=6 continues 5
4 5 6 7 8
已经条件: total=【99】 pageSize =【3】 pageNo= 1 continues 5
错误:-1 0 1 2 3
正确: 1 2 3 4 5
已经条件: total=【99】 pageSize =【3】 pageNo= 2 continues 5
错误: 0 1 2 3 4
正确:1 2 3 4 5
已经条件: total=【99】 pageSize =【3】 pageNo= 33 continues 5
错误: 31 32 33 34 35
正确:29 30 31 32 33
已经条件: total=【99】 pageSize =【3】 pageNo= 32 continues 5
错误:30 31 32 33 34
正确: 29 30 31 32 33
************************************
进行单元测试
连续页码5: 8 [6,7,8,9,10]
连续页码7: 8 [5,6,7,8,9,10,11]
:disabled="pageNo == 1"
使上一页按键失效
经典面试题:v-for与v-if优先级? v-for优先级更高
不应该把’v-for’和’v-if’混在一起 ,可以给v-for设置一个计算属性遍历
正常情况:再回来因该还是第一页【遇见脑袋xxxx产品可能有这种操作】
需求:【正常说:没有这个需求的】
开发详情Detail业务步骤
过程
scrollBehavior(to) {
if (to.name == 'detail') {
console.log(1);
return { x: 0, y: 0 }
// vuex-router@3 x y
// vue3 : vuex-router@4 top bottom
}
}
路由模块化:router.js
数据获取:ajax(axios),vuex(getters 简化数据,未发请求vuex的state数据为空,getters数据若为undefined,就会警告,逻辑’||'解决),详情页派发dispatch
vue-warn:警告(不影响的你程序),对于你的代码提出一个警告。对于程序没有任何影响,俗称假报错。
vuex:时不时会有假报错现象
详情模块开发之商品属性的开发
{
attr:'颜色',
attrValue:['红色','黑色','白色']
}
放大镜的功能
----插件:插件解决可以【巧劲】
产品个数业务
考虑小数,非数字,负数的输入
加入购物车的业务? 购物车项目第二个重要地方
购物车:每一个人都有属于自己的购物车,那为什么不同用户登录自己账号,可以看见属于自己产品一定是用户点击加入购物车,把你的产品信息提交给服务器进行保存,当你下次在进入购物车的时候,需要向服务器发请求,获取你购物车里面的信息展示
项目:点击加入购物车按钮的时候,以前经常进行路由跳转【调到另外一个路由】,但是你要注意,点击加入购物车这个按钮的时候,将用户选择产品,提交给服务器进行存储,如果服务器存储成功,之后在进行路由跳转
面试题:GET与POST
相同点:都是HTTP协议。
不同点:
1:GET请求携带参数是有上限的 post请求携带的参数是没有’上限的’
2:GET请求相对而言不安全,POST安全
面试题:H5新增那些特性?
CSS3、本地存储、多媒体、canvas
面试题:本地存储与会话存储区别?
本地存储可以长久保存,会话存储只在本次回话保存
前台项目三个重要地方:分页器、购物车、登录注册。
面试题:token相关的面试
问题1:什么时候用router-link、什么时候用编程式导航。
问题2:promise问题。【基础问题】
举例子:用户是淘宝平台的用户。
为什么目前我们获取不到自己购物车的数据,你没有给我分配一个用户id
张三:奶粉、鞋子、手机
李四:羽绒服
问题1:用哪个技术可以生成用户id【身份】
问题2:用户身份如何给后台专递过去?
requests.interceptors.request.use((config) => {
nprogress.start();
config.headers.userTempId = localStorage.getItem("UUIDTOKEN");
return config;
})
工作的时候不这么玩
3.6配置一些文件[JS],不能操作仓库
设计购物车的数据
注意:获取购物车的数据的时候,读取的时候切记小心。后台老师写的数据格式有问题的。
张三:衣服、裤子、鞋子
[
{
cart:[ {name:'衣服'},{name:'裤子'},{name:'鞋子'}]
}
]
购物车静态结构需要注意
修改购物车产品数量的时候,需要发请求的,通知服务器产品最新的个数【服务器需要保存】,
当你组件在获取购物车的数据时候,不就是最新的数值【用户刷新刷不掉】
产品个数变化接口参数:
skuID string Y 商品ID
skuNum:在修改产品个数的时候,需要给服务器传递的是【变化的数值】
比如:
blur:失去焦点—>点击空白的地方
change:文本需要有变化,而且还需要点击空白的地方
input:只要文本发生变化立马执行【不许点击空白的地方】
数量操作的加减号节流优化
购物车中产品的数据:isChecked属性,‘1’:代表这个产品勾选中 ‘0’:代表这个产品没有被选中
注意:以后工作的时候,最基本的基本功看API文档(在线文档),在开发后台管理系统项目的时候
一定要培养看在线文档能力
assets文件夹:经常放置一些静态资源(图片),assets文件夹里面资源webpack会进行打包为一个模块(js文件夹里面)
对于企业当中,一般项目都有登录注册功能【这个业务很重要】
当然有一些项目不需要注册,后台管理系统项目,一般不需要注册。
登录与注册的静态组件(图片问题会报错)
assets【放置静态资源文件的地方】
background: url(~@/assets/images/icons.png) no-repeat -10px -201px;
也可以
background: url("@/assets/images/icons.png") no-repeat -10px -201px;
<button style="width: 100px; height: 38px">获取验证码button>
现在在做注册、登录业务的时候,先不处理表单的验证功能,在项目最后一天,在把表单如何验证,哪些插件解决【最后去处理】
获取验证码【api文档最后,其中15. 获取注册验证码 不可用 】
token面试题:项目当中token过期、失效如何处理?
注册业务【手机号、验证码、登录密码】,点击注册按钮的时候,需要把这些信息给服务器传递过去
当你点击登录按钮的时候,需要把手机号、密码需要携带给服务器,服务器需要判断,你是不是我的用户【注册过的】
如果是用户登录成功,进行登录,如果用户登录失败给一个提示即可。
一、用户登录以后获取用户信息进行展示
企业项目:登录成功以后,服务器会返回token【存储于vuex当中】,如果想获取用户信息
还需要再发请求【用户信息】,携带token给服务器。
api/user/passport/auth/getUserInfo 获取用户信息的接口
为什么刷新页面,用户信息就消失
为什么去别的模块【非home模块】获取用户信息失败?
因为你去别的模块根本没有发请求获取用户信息,没办法展示用户信息
怎么解决:
二、退出登录
三、导航守卫
//全局守卫:[后置守卫:在路由跳转完毕之后才会执行一次]
router.afterEach(()=>{
console.log('守卫:路由跳转完毕才会执行一次')
})
用户已经登录了,不应该在访问login?【通过什么条件能判断用户登录、未登录】
路由独享守卫:
组件内守卫:
登录页面的登录按钮点击取消默认行为,否则有bug
身份凭证
TOKEN身份为大
UUID生成的临时省份
用户(注册与登录)token【正式身份】
不要再生命周期中使用async
获取用户地址信息,需要下面账号,要不就自己模拟数据
获取用户地址信息、获取用户购物车清单信息
//用户地址信息
/api/user/userAddress/auth/findUserAddressList
//商品清单接口
/api/order/auth/trade
展示购物车清单数据
//引入所有API
import * as API from "@/api"
new Vue({
render: h => h(App),
router,
store,
beforeCreate() {
Vue.prototype.$bus = this; //安装全局事件总线,$bus就是当前应用的vm
Vue.prototype.$API = API;
},
}).$mount('#app')
当用户点击提交订单按钮的时候,需要发请求的
提交订单的请求地址:/api/order/auth/submitOrder?tradeNo={tradeNo}
前台:需要告诉服务器:谁买的、买的啥、买几个、 支付多少钱、留言信息…
后台:订单号,这笔交易的一个标识符【支付的】
axios({url:‘xxx’,methods:‘post’,data:{a:1}})
微信支付、支付宝支付等等
export const reqPayment = (orderId) => requests({ url: `/payment/weixin/createNative/${orderId}`, method: "get" })
vscode插件:vue-helper:拓展element-ui组件库
react框架:
Vue框架:
Element UI基本使用
npm i element-ui -S
完整引入
在 main.js 中写入以下内容:
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';
Vue.use(ElementUI);
new Vue({
el: '#app',
render: h => h(App)
});
按需引入
按需引入
借助 babel-plugin-component,我们可以只引入需要的组件,以达到减小项目体积的目的。
首先,安装 babel-plugin-component:
npm install babel-plugin-component -D
然后,将 .babelrc(.babel.config.js) 修改为:
{
presets: [
'@vue/cli-plugin-babel/preset'
],
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
接下来,如果你只希望引入部分组件,那么需要在 main.js 中写入以下内容:
按照需求引入相应的组件即可
Vue.component();
Vue.prototype.$xxx = xxx;
import Vue from 'vue';
import { Button, Select } from 'element-ui';
import App from './App.vue';
//饿了么ui组件
import { Button, MessageBox } from "element-ui"
Vue.component(Button.name, Button)
// Vue.prototype.$msgbox = MessageBox;
Vue.prototype.$alert = MessageBox.alert;
new Vue({
el: '#app',
render: h => h(App)
});
弹窗MessageBox:使用 HTML 片段
this.$alert(`${url} />`, "请你微信支付", {
dangerouslyUseHTMLString: true,
//中间布局
center: true,
//是否显示取消按钮
showCancelButton: true,
//取消按钮的文本内容
cancelButtonText: "支付遇见问题",
//确定按钮的文本
confirmButtonText: "已支付成功",
//右上角的叉子没了
showClose: false,
//关闭弹出框的配置值
beforeClose: (type, instance, done) => {
//type:区分取消|确定按钮
//instance:当前组件实例
//done:关闭弹出框的方法
if (type == "cancel") {
alert("请联系管理员豪哥");
//清除定时器
clearInterval(this.timer);
this.timer = null;
//关闭弹出框
done();
} else {
//判断是否真的支付了
//开发人员:为了自己方便,这里判断先不要了
// if (this.code == 200) {
clearInterval(this.timer);
this.timer = null;
done();
this.$router.push("/paysuccess");
// }
}
},
});
codeUrl:"weixin://wxpay/bizpayurl?pr=A6XYgvyzz" //是一个字符串
通过qrCode.toDataUrl方法,将字符串转换为加密的在线二维码链接,通过图片进行展示。
1.安装
npm i qrcode
2.引入
import QRCode from 'qrcode'
3.使用
const url = await QRCode.toDataURL(this.payInfo.codeUrl);
GET|POST:短轮询,请求发一次,服务器响应一次,完事。
第一种做法:前端开启定时器,一直找服务器要用户支付信息【定时器】
第二种做法:项目务必要上线 + 和后台紧密配合
当用户支付成功以后,需要后台重定向到项目某一个路由中,将支付情况通过URL参数形式传给前端,
前端获取到服务器返回的参数,就可以判断了。
前置守卫:在路由跳转之前,进行判断
后置守卫:路由都已经跳转完毕才执行。
全局守卫:只要的项目当中任何某一个路由发生变化,就会出发。
组件内守卫有三个,进入时beforeRouteEnter,更新时(动态路径)beforeRouteUpdate (2.2 新增),离开时beforeRouteLeave
组件内守卫:也是专门负责某一个路由【并非负责全部路由】,写法和路由独享守卫有区别?
未登录的情况:
用户未登录:
所以到这写页面直接到登录页面,登陆完需要回到这些页面
去交易页面:从购物车才能跳转到交易页面。
next():你本来想去哪里,我就放行,你就去完事了。
next(’/login’):执行守卫放行到执行的路由。
next(false):路由跳转的时候,从哪里来回那里去。
vue-lazyload:图片懒加载
图片:用户网络不好,服务器的数据没有回来,总不可能让用户看白色,至少有一个默认图片在展示。
// 安装 npm i vue-lazyload
import VueLazyload from 'vue-lazyload'
Vue.use(VueLazyload)
// or with options
Vue.use(VueLazyload, {
preLoad: 1.3,
error: 'dist/error.png',
loading: 'dist/loading.gif', //懒加载图片
attempt: 1
})
//多了一个指令 v-lazy
// 用v-lazy代替:src
复习路由知识点
当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。
如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
const Foo = () =>
Promise.resolve({
/* 组件定义对象 */
})
动态 import
语法来定义代码分块点 (split point):import('./Foo.vue') // 返回 Promise //我们不需要做第一步了
const Foo = () => import('./Foo.vue')
Foo
const router = new VueRouter({
routes: [{ path: '/foo', component: Foo }]
})
面试【高频的面试】:项目的性能优化手段有哪些?
vee-validate插件
使用步骤:
1:安装vee-validate,别安装最新版本@2
npm i vee-validate@2
2:在plugins文件夹中创建一个validate.js[专门注册vee-valadite]
3: 注册插件
插件安装与引入
import Vue from 'vue'
import VeeValidate from 'vee-validate'
import zh_CN from 'vee-validate/dist/locale/zh_CN' // 引入中文 message
Vue.use(VeeValidate)
提示信息:注册插件的时候,用中文,以及需要验证的字段【用中文显示提示形式】
VeeValidate.Validator.localize('zh_CN', {
messages: {
...zh_CN.messages,
is: (field) => `${field}必须与密码相同` // 修改内置规则的 message,让确认密码和密码相同
},
attributes: { // 给校验的 field 属性名映射中文名称
phone: '手机号',
code: '验证码',
password: '密码',
password1: '确认密码',
agree: '协议'
}
})
//自定义校验规则
//定义协议必须打勾同意
VeeValidate.Validator.extend('agree', {
validate: value => {
return value
},
getMessage: field => field + '必须同意'
})
4:在入口文件需要引入执行一次
import "@/plugins/validate.js"
5: 使用vee-valadiate插件
{{ errors.first("phone") }}
//确认密码写法
//v-validate="{ required: true, is:password }"
const success = await this.$validator.validateAll(); //全部表单验证 返回true 或 false
npm run build
项目打包后,代码都是经过压缩加密的,如果运行时报错,输出的错误信息无法准确得知是哪里的代码报错。
有了.map文件
就可以像未加密的代码一样,准确的输出是哪一行哪一列有错。
所以该文件如果项目.map文件
不需要是可以去除掉
本地部署node.js
// npm init -y
// npm i express
// npm i connect-history-api-fallback //解决history模式刷新404的问题//也可以用nginx解决
//服务器
const express = require('express')
const app = express()
app.use(express.static(__dirname + '/static'))
app.get('/person', (req, res) => {
res.send({
name: 'tom',
age: 18
})
})
app.listen(5005, (err) => {
if (!err) console.log('服务器启动成功');
})
购买服务器
nginx反向代理
/etc
目录中location / {
root /lightofd/www/shangpinhui;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://39.98.123.211;
}
这里我配置失败,配置一天找到一个方便的配置方式
首先我们需要在腾讯云买一个服务器
到控制台
搜索轻量
重装系统
选择宝塔
点击空白进入服务器
找到这个面板,点击管理应用
点击防火墙里的添加规则,端口位置输入 20,21,888,8888 然后确定,老师讲的安全组也要操作一下
直接点击默认安装
点击网站栏的添加站点
上一步操作完是这样
try 捕捉返回promis对象函数错误,一般要在前加上 await进行捕捉 否则无法进入catch