1.项目的其他配置
创建项目 vue create name --packageManager npm
-
eslint
验证关闭lintOnSave:false //在vue.config.js文件中加入这行代码
-
重启时打开浏览器
-
在
package.json
文件中 找到script
配置项,在serve后面加--open
"scripts": { "serve": "vue-cli-service serve --open", "build": "vue-cli-service build", "lint": "vue-cli-service lint" }
-
2.vue-router插件的使用
-
路由切换就是不断销毁和生成组件
-
路由组件一般放置在views文件夹中
-
配置路由一般放在router文件夹中 一级路由
// 配置路由 import Vue from 'vue' import VueRouter from 'vue-router' // 使用插件 Vue.use(VueRouter) // 引入路由组件 import Home from '@/views/Home' import Register from '@/views/Register' import Search from '@/views/Search' import Login from '@/views/Login' // 配置路由 export default new VueRouter({ routes:[ { path:'/home', component:Home }, { path:'/register', component:Register }, { path:'/search', component:Search }, { path:'/login', component:Login } ] })
-
使用路由
-
在入口文件
main.js
引入路由,然后注册路由
import router from '@/router' new Vue({ render: h => h(App), router }).$mount('#app')
-
路由出口:展示路由组件的地方
<template> <div> <Header></Header> <!-- 路由出口 --> <router-view></router-view> <Footer></Footer> </div> </template>
-
<router-view></router-view>
就是展示路由组件的入口
-
-
-
重定向路由
{ path:'*', redirect:'/home' }
-
路由的跳转
-
声明式跳转
router-link
-
本质是 a 标签,只是简单跳转的时候可以用
<router-link to="/login">登录</router-link> <router-link to="/register">注册</router-link>
-
to 属性:跳转到哪个组件
-
-
-
编程式跳转
push|replace
-
需要其他业务逻辑的时候,比如传参给其他路由
-
在需要跳转的地方定义 事件
<button type="button" @click="goSearch">搜索</button>
-
调用方法
methods:{ goSearch(){ this.$router.push('/search') } }
-
当我们注册路由之后 每个组件都有
$router
属性 里面的 push | replace 方法可以帮助我们跳转到对应的了路由
-
-
路由原信息,自己定义的东西 对象形式
$route.meta //存放原信息
-
里面存放的数据,非路由组件也能访问到
-
-
-
路由传参
-
三种形式
-
对象形式
goSearch(){ this.$router.push({ name:'search', params:{ keyword:this.keyword } }) }
-
这种形式最好要在路由中定义name
{ name:'search', path:'/search/:keyword', component:Search, meta:{isShow:true} }
-
字符串和模板字符串形式不做演示
-
-
-
-
params参数
-
需要在路径上使用占位符
path:'/search/:keyword', component:Search, meta:{isShow:true}
-
存放在
$router.params
当中
-
-
query参数
-
类似于
ajax
中的GET传参?k=v的形式 -
存放在
$router.query
当中
-
-
-
-
-
路由几个重要的点
-
path 跳转不能和 params一起使用
goSearch(){ // this.$router.push({ // name:'search', // params:{ // keyword:this.keyword // } // }) this.$router.push({ path:"/search", params:{ keyword: this.keyword } })
-
会报错的,必须结合name来使用
-
-
如何指定params参数可传可不传
-
如果params已经占位了,不穿的话URL会有问题
-
解决方法
在占位的时候在后面加一个问号
{ name:'search', path:'/search/:keyword?', component:Search, meta:{isShow:true} }
-
-
如果传一个空字符的解决方法
goSearch(){ this.$router.push({ name:'search', params:{ keyword:this.keyword || undefined } }) }
-
undefined解决
-
-
路由组件也可以传props
-
有三种写法,因为不常用所以不写了
-
-
重写push | replace 方法
-
加入中断Promise的功能
-
3.axios 二次封装
-
利用 axios 的create 方法新建一个实例
-
添加拦截器
-
暴露出去
// 对axios进行二次封装 import axios from "axios"; // 创建实例 const myApi = axios.create({ baseURL:'/api', timeout:5000, }) // 请求拦截器 myApi.interceptors.request.use((config)=>{ // config里面有一个请求头 return config },(err)=>{ return Promise.reject(err) }) // 响应拦截器 myApi.interceptors.response.use((response)=>{ return response.data },(err)=>{ return Promise.reject(`请求错误:${err}`) }) export default myApi
4.代理服务器
Vue跨域的一种解决方式
-
在vue.config中配置
devServer: { proxy: { '/api':{ target:'http://gmall-h5-api.atguigu.cn' }, }, }
5.进度条的使用
-
安装
npm install --save nprogress
-
// 进度条 import NProgress from "nprogress"; // 导入 nprogress模块 import "nprogress/nprogress.css"; // 导入样式,否则看不到效果 NProgress.configure({ showSpinner: false }); // 显示右上角螺旋加载提示
-
开启进度条
NProgress.start()
-
结束进度条
NProgress.done()
6.Vuex的使用
-
集中式管理组件共用的数据
import Vue from 'vue' import Vuex from 'vuex' // 引入小仓库 import home from './home' // 使用插件 Vue.use(Vuex) export default new Vuex.Store({ modules:{ home } })
-
在main.js中注册
new Vue({ render: h => h(App), router, store }).$mount('#app')
-
-
核心
-
State:存储数据的地方
const state = { count:1 }
-
actions:通常处理异步请求,在组件中通过
this.$store.dispatch
联系它,他将处理完的数据通过调用commit()
函数来联系 mutationsconst actions = { add({commit},data){ commit('ADD',data) } }
-
mutations 唯一一个能操控state的地方,它接受两个参数,第一个是state 第二个是传递过来的参数
const mutations = { ADD(state,data){ state.count+=data } }
-
getters state的计算属性 可以通过
...mapGetters({bigCount:"bigCount"})
访问到const getters = { bigCount(state){ return state.count*2 } }
-
-
模块化
这是专属home模块的vuex
// home的vuex const state = { count:1 } const actions = { add({commit},data){ commit('ADD',data) } } const mutations = { ADD(state,data){ state.count+=data } } const getters = { bigCount(state){ return state.count*2 } } export default { namespaced: true, state, actions, mutations, getters }
-
四个生成代码的辅助
-
生成方法
import { mapActions,mapMutations } from 'vuex'
-
使用
methods:{ ...mapActions('home',['add']) //必须开启命名空间 }
-
-
计算属性
import { mapState,mapGetters } from 'vuex'
-
使用
//必须开启命名空间 computed:{ ...mapState('home',['count']), ...mapGetters('home',['bigCount']) }
-
-
7.节流与防抖
-
防抖: 如果连续快速触发,只执行一次
lodash 是vue脚手架自带库,向外暴露
_
_.debounce(func, [wait=0], [options=])
-
节流: 把频繁触发变为少量触发
_.throttle(func, [wait=0], [options=])
-
参数详解
-
func
(Function): 要节流的函数。 -
[wait=0]
(number): 需要节流的毫秒。 -
[options=]
(Object): 选项对象。
-
-
8.获取自定义属性
-
当使用事件委托时,不知道对应标签,即可使用这个方法(自定义属性)
-
goSearch(event){ // 传ID和标题 let element = event.target console.log(element,element.dataset); }
-
element.dataset 即可获取自定义属性
-
9.过度动画
-
Vue的transition属性 (多组件用 <transition-group>)
-
使用它的前提条件: 要做运动的组件必须要有 v-if | v-show 指令
<transition name="sort"> <div v-show="navShow" class="sort"> </transition>
-
六个属性(如果transition标签定义了name属性 则下面的 v- 改为 自己定义的 name):
隐藏:加入类名: v-leave :定义离开过渡的开始状态。 v-leave-active :定义离开过渡生效时的状态。 v-leave-to :定义离开过渡的结束状态。 显示:加入类名: v-enter: 准备进行运动的状态(起始状态) v-enter-active: 整个运动状态 v-enter-to: 整个运动状态(强调运动的结果,结束状态)
-
-
-
配合animate动画库
-
transition的name属性要改为
animate__animated animate__bounce
<transition name="animate__animated animate__bounce" enter-active-class="animate__bounceInUp" leave-active-class="animate__bounceOutLeft" > </transition>
-
在transition添加以下属性
enter-class enter-active-class enter-to-class (2.1.8+) leave-class leave-active-class leave-to-class (2.1.8+)
-
改变动画时间
/* This only changes this particular animation duration */ /*只改变局部*/ .animate__animated,.animate__bounce { --animate-duration: 2s; } /* This changes all the animations globally */ /*改变全局*/ :root { --animate-duration: 800ms; --animate-delay: 0.9s; }
-
10.mock.js模拟技术
-
默认对外暴露 JSON ,图片
import Mock from "mockjs" // 引入数据 import banner from './banner.json' import floor from './floor.json' Mock.mock('/mock/banner',{code:200,data:banner}) Mock.mock('/mock/floor',{data:200,data:floor})
-
Mock.mock()模拟数据
-
第一个参数是请求地址
-
第二个请求方式(可不写)
-
第三个参数是返回数据
-
-
-
在main.js中引用运行一下
import '@/mock/mockServe.js'
11.swiper的基本使用
12.组件通信
-
props
:用于父子组件通信 -
@on | @emit 可以实现子给父通信 (自定义事件)
-
全局事件总线
$bus
全能 -
pubsub-js :发布订阅模式 全能
-
插槽 3种方式
-
Vuex
13.将轮播图封装成一个组件
<template> <div class="swiper-container" ref="cur"> <div class="swiper-wrapper"> <div class="swiper-slide" v-for="carousel in list" :key="carousel.id"> <img :src="carousel.imgUrl"> </div> </div> <!-- 如果需要分页器 --> <div class="swiper-pagination"></div> <!-- 如果需要导航按钮 --> <div class="swiper-button-prev"></div> <div class="swiper-button-next"></div> </div> </template> <script> import Swiper from 'swiper'; export default { name:'Carousel', props:['list'], watch:{ list:{ immediate:true, handler(){ this.$nextTick(()=>{ new Swiper (this.$refs.cur, { loop: true, // 循环模式选项 // 如果需要分页器 pagination: { el: '.swiper-pagination', clickable:true }, // 如果需要前进后退按钮 navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev', } }) }) } } } } </script> <style> </style>
-
因为轮播图在多个地方有使用,所以将他做成一个全局组件
-
Object.assign()
:浅拷贝
14.Set集合数组去重
-
set 唯一性的特点配合拓展运算符 进行数组
[...new Set(Array)]
15.arr.splice()方法
-
splice 方法用于数组或伪数组,根据参数个数和形式的不同,可以在数组中删除指定元素或者插入元素、替换元素。
-
原地修改数组
-
arr.splice(i,j,k)
-
三个参数时:删除从i开始,删除j个元素,并且插入元素k
-
两个参数时:删除从i开始,删除j个元素
-
一个参数时:删除从i包括i后面的元素
-
16.分页器
-
分页器展示,需要哪些条件
-
当前页数 : pageNo
-
每页展示的数据 : pageSize
-
一共要展示的数据 : total
-
一共有多少页 : totalPage
-
连续页码数 : continues
-
-
分页器的边界问题
computed:{ // 总的页数 totalPage(){ return Math.ceil(this.total/this.pageSize) }, startNumAndEndNum(){ const {pageNo, pageSize, continues, total, totalPage} = this // 定义起始 let start = 0, end = 0 if(tcontinues > totalPage){ start = 1 end = totalPage }else{ start = pageNo - parseInt(continues / 2) - 1 end = pageNo + parseInt(continues / 2) if(start < 1){ start = 1 end = continues } if(end > totalPage){ end = totalPage start = totalPage - continues + 1 } } return {start,end} } }
-
动态展示分页器
<button v-for="(page, index) in startNumAndEndNum.end" :key="index" v-show="page > startNumAndEndNum.start" > {{ page }} </button>
-
分页组件源代码(无样式)
<template> <div class="pagination"> <button :disabled="pageNo == 1" @click="$emit('getPage', pageNo-1)">上一页</button> <button v-show="startNumAndEndNum.start > 1" @click="$emit('getPage', 1)" :class="{bg:pageNo == 1}">1</button> <button v-show="startNumAndEndNum.start > 2">···</button> <button @click="$emit('getPage', page)" v-for="(page, index) in startNumAndEndNum.end" :key="index" v-show="page > startNumAndEndNum.start" :class="{bg:pageNo == page}"> {{ page }} </button> <button v-show="startNumAndEndNum.end < totalPage - 1">···</button> <button @click="$emit('getPage', totalPage)" v-show="startNumAndEndNum.end < totalPage" :class="{bg:pageNo == totalPage}">{{ totalPage }}</button> <button @click="$emit('getPage', pageNo+1)" :disabled="pageNo == totalPage">下一页</button> <button style="margin-left: 30px">共 {{ total }} 条</button> </div> </template> <script> export default { name: "Pagination", props:['pageNo', 'pageSize', 'continues', 'total'], computed:{ // 总的页数 totalPage(){ return Math.ceil(this.total/this.pageSize) }, startNumAndEndNum(){ const {pageNo, continues, totalPage} = this // 定义起始 let start = 0, end = 0 if(continues > totalPage){ start = 1 end = totalPage }else{ start = pageNo - parseInt(continues / 2) - 1 end = pageNo + parseInt(continues / 2) if(start < 1){ start = 1 end = continues } if(end > totalPage){ end = totalPage start = totalPage - continues + 1 } } return {start,end} } } } </script>
17.路由滚动行为
const router = new VueRouter({ routes: [...], scrollBehavior (to, from, savedPosition) { // return 期望滚动到哪个的位置 } })
比如
scrollBehavior (to, from, savedPosition) { return { x: 0, y: 0 } }
-
跟routes同级别
18.放大镜
-
所需API
-
event.offsetX :鼠标在当前元素的位置
-
element.offsetLeft :元素距离带定位的父元素的偏移量
-
-
等比例移动
-
大图片移动距离 = 遮挡层移动距离 * 大图片最大移动距离 / 遮挡层最大移动距离
-
-
js代码(原生JS)
class Zoom{ constructor(){ let small = document.querySelector(".small");//装小图片的盒子 let z = document.querySelector(".z");//遮罩层 let big = document.querySelector(".big");//装大图的盒子 let bigImg = big.children[0]//大图片 small.addEventListener("mouseover", function () { z.style.display = "block"; big.style.display = "block"; }); small.addEventListener("mouseout", function () { z.style.display = "none"; big.style.display = "none"; }); small.addEventListener("mousemove", function (e) { let x = e.pageX - this.offsetLeft; let y = e.pageY - this.offsetTop; let maskX = x - z.offsetWidth / 2; let maskY = y - z.offsetHeight / 2; // 遮挡层最大移动距离 let maskMaxX = this.offsetWidth - z.offsetWidth; let maskMaxY = this.offsetHeight - z.offsetHeight; if (maskX <= 0) { maskX = 0; } else if (maskX >= maskMaxX) { maskX = maskMaxX; } if (maskY <= 0) { maskY = 0; } else if (maskY >= maskMaxY) { maskY = maskMaxY; } z.style.left = maskX + "px"; z.style.top = maskY + "px"; // 大图片移动距离 = 遮挡层移动距离 * 大图片最大移动距离 / 遮挡层最大移动距离 let bigMaxX = bigImg.offsetWidth - big.offsetWidth; let bigMaxY = bigImg.offsetHeight - big.offsetHeight; let bigImgX = (maskX * bigMaxX) / maskMaxX; let bigImgY = (maskY * bigMaxY) / maskMaxY; bigImg.style.left = -bigImgX + "px"; bigImg.style.top = -bigImgY + "px"; }); } }
19.slot的基本使用
pass
20.JS遍历数组的方法
-
every():遍历数组,每一个元素 都满足条件 则返回 true,否则返回 false
-
some():遍历数组,有一个元素以上 都满足条件 则返回 true,否则返回 false
21.Promise.all()
-
将一个promise数组传给Promise.all(),返回一个promise状态,如果数组里的promise状态都为成功,那么Promise.all()返回的状态则为成功,只要有一个状态为失败,那返回的为失败
changeAllChecked({dispatch,getters},checked){ let promiseAll = [] getters.shopCartList.cartInfoList.forEach(value => { let promis = dispatch('updateCheck',{skuID:value.skuId,checkedState:checked}) promisAll.push(promis) }) console.log(promiseAll); return Promise.all(promiseAll) }
22.注册与登录
-
身份验证:
-
token:用户登录之后会不会直接返回数据,而是返回一个token
-
用户需要在发请求时将token放在请求头,才能返回真正的数据
-
23.路由守卫
-
AI的回答
Vue路由守卫可以让你在用户路由到某个页面前触发一些逻辑,可以用来检查用户是否登录、检查权限等操作。Vue提供了三种不同的路由守卫,分别是全局守卫、路由守卫和组件守卫。
-
全局守卫
全局守卫是在一个Vue应用程序中定义的,它们会对该应用程序中的所有路由生效。以下是你可以使用的三个全局守卫:
-
router.beforeEach:在进入路由之前触发,可以用来检查用户是否登录、检查权限等等。
-
router.afterEach:在路由完成跳转之后触发,可以用来进行埋点、记录日志等操作。
-
router.beforeResolve:在组件内的异步路由被解析之后触发,可以用来进行路由的处理。
-
路由守卫
在单个路由上定义的守卫称作路由守卫,你可以在路由定义的时候添加以下属性:
-
beforEnter:这个是在一个路由被激活之前触发守卫。这种方式只能检查路由对应组件的状态,无法对应用程序状态进行检查。
-
组件守卫
组件守卫是和路由守卫类似的,不同的是它是在组件内部定义的。以下是四个组件级别的守卫:
-
beforeCreate:在实例被创建之前立刻调用,此时所有的option都会被合并,生命周期钩子函数以及事件配置已经都开始初始化了。
-
created:在实例后完成调用,此时实例已经完成了数据观测,computed属性,watcher/event监听以及第一轮组件的挂载。
-
beforeMount:在挂载之前调用,即$el属性挂载到实例之前。
-
mounted:组件挂载后调用,此时已经完成了DOM的创建和挂载。
使用路由守卫能够使你方便的控制页面之间的跳转行为,从而带来更好的用户体验和更高的安全性。
-
-
具体使用(AI的回答)
Vue路由守卫的具体使用方式如下:
-
全局守卫
在全局守卫中,可以定义三个钩子函数,分别是
beforeEach
、afterEach
和beforeResolve
。const router = new VueRouter({ routes: [...], }); router.beforeEach((to, from, next) => { // to: Route: 即将要进入的目标路由对象 // from: Route: 当前导航正要离开的路由 // next: Function: 一定要调用该方法来 resolve 这个钩子。 // next():如果钩子成功执行完毕,可以正常进入路由。 // next(false):如果钩子执行失败,停留在原地。 // next('/') 跳转到一个不同的地址 next(); }); router.afterEach((to, from) => { // to: Route: 当前导航正要进入的目标路由对象 // from: Route: 当前导航正要离开的路由 }); router.beforeResolve((to, from, next) => { // 在解析组件内部的异步路由之后触发 next() });
-
路由守卫
路由守卫的使用方式和全局守卫类似,不同的是它是在单个路由上定义的,可以通过
beforeEnter
属性来定义:const router = new VueRouter({ routes: [ { path: '/foo', component: Foo, beforeEnter: (to, from, next) => { // ... next(); }, }, ], });
-
组件守卫
在组件内部定义守卫非常灵活,可以根据组件需要的场景进行定义:
export default { beforeCreate() {}, created() {}, beforeMount() {}, mounted() {}, };
通过路由守卫,我们可以轻松地进行路由拦截、登录验证、权限控制等操作。这可以给我们带来更好的用户体验和更高的安全性。
-
24.路由懒加载
25.图片懒加载
-
安装:
$ npm i vue-lazyload@1.3.3 -D
-
引入:
import VueLazyload from 'vue-lazyload'
-
注册:
Vue.use(VueLazyLoad, { preLoad: 1, error:require('./assets/img/error.jpg'), loading:require('./assets/img/homePage_top.jpg'), attempt: 2, })
-
使用:在需要图片懒加载的地方将
src
替换为v-lazy