尚品汇前端项目总结1(P1-P30)

目录

Vue-cli脚手架文件组成

自带文件夹:

后期设置的文件夹:

项目其他配置:

eslint校验关闭:

src文件夹配置别名:

路由分析

全局组件:

非路由组件的业务:

设置footer组件的隐藏和显示

路由组件业务:

vue-router的使用

路由重定向

 路由跳转

路由传参 

重写push和replace(项目中遇到的问题)

接口相关

postman测试接口

axios二次封装

接口统一管理

跨域问题

nprogress进度条的使用 

Vuex

使用

Vuex模块开发

typeNav三级联动

数据展示 

添加动态背景

二三级分类显示与隐藏

给三级联动加上防抖与节流 

三级联动路由跳转

三级联动的显示与隐藏

三级分类的过渡动画

防抖与节流 

效果

原理

防抖的应用场景

实现:

方法一、使用lodash插件,其中包含防抖与节流业务

方法二、自己写防抖节流函数


Vue-cli脚手架文件组成

自带文件夹:

node_modules:放置项目的依赖

public:一般放置静态资源,放在public中的静态资源在打包的时候会原封不动的打包到dist文件中

src:程序员源代码文件夹

        assests:放置静态资源(一般放置多个文件共用的静态资源)webpack打包的时候会把这 里的静态资源当成一个模块打包到JS文件里面

        components:放置非路由组件和全局组件

        App.vue:唯一的根组件

        main.js:入口组件,也是整个组件最先执行的文件

babel.config.js:配置文件(babel相关)

package.json文件:记录项目叫做什么,项目中有哪些依赖以及相应的版本

package-lock.json:缓存性文件

README.md:说明文件(怎么安装依赖,项目怎么运行,怎么打包)

后期设置的文件夹:

vue.config.js:配置了eslint校验关闭

jsconfig.js配置了src文件别名

pages:放置路由组件

router:路由文件夹

api:放置api相关

store:Vuex相关

mock:mock数据

utils:存放一些功能函数例如获取uuid

项目其他配置:

eslint校验关闭:

在根目录下创建一个vue.config.js然后配置

module.exports = defineConfig({
    lintOnSave: false,//关闭eslint
})

为什么要关闭?声明变量但是没有使用时,eslint检验工具会报错

src文件夹配置别名:

创建jsconfig.json然后配置

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

因为项目大的时候src(源代码文件夹):里面目录会很多,找文件不方便,设置src文件夹的别名的好处,找文件会方便一些

路由分析

分为上中下结构,上下结构不会变,设置为非路由组件,中间部分会改变设置为非路由组件

分析路由组件以及非路由组件

路由组件:home,search,login,register

非路由组件:footer,header

全局组件:在多个组件中都出现的组件注册为全局组件,放在components中例如三级联动以及分页器

全局组件:

在main.js中import引入组件

注册全局组件:Vue.compoent(组件名,组件)

非路由组件的业务:

步骤:引入组件、注册组件、引入样式

设置footer组件的隐藏和显示

v-if或v-show这里用v-show,因为切换得比较频繁

方法一:通过$route获取路径,判断路径,不建议,会很麻烦

方法二:利用路由元信息meta,通过footerShow来决定是否显示footer,通过$router.meta.来获取字段

{
            path: '/register',
            //路由懒加载
            component: () =>
                import ('@/pages/Register'),
            meta: {
                footerShow: false,
            }

        }
    

路由组件业务:

vue-router的使用

安装vue-router,新建router文件夹,import引入vue-router,Vue.use(VueRouter)使用

在router中引入路由组件并配置路由组件的路径

最后在main.js中引入router并注册

路由组件引用的时候用router-view,非路由组件直接使用标签

路由组件组件一般放在pages中非路由组件放在compoents中

$route:一般用来获取路由信息(路由身上的一些属性吧)例如path\query\params

$router:一般进行编程式导航进行路由跳转(使用路由身上的一些方法)例如push\replace

注册完路由之后,不论是路由组件还是非路由组件身上都会有这两种属性

路由重定向

将路由定义到自己想定义的页面

 //一进入页面就到home组件里面
        {
            path: '*',
            redirect: '/home'

        },

 路由跳转

声明式导航:router-link,一定要有to属性设置路径

   我的订单 

编程式导航:$router.push,$router.replace 声明式导航能做的,编程式导航都能做,编程式导航还能够处理一些业务逻辑

a标签中的target='_blank'代表打开一个新网页进行跳转

路由传参 

params参数 :属于路径中的一部分,在配置路由的时候需要事先占位,(占位方式:在设置路由的时候,路径后面加/:KEY),用对象写法的时候不能用path否则会崩溃

query参数:不属于路由的一部分,/home?k=v&a=b,不需要占位

路由传参方式

1、字符串形式:直接在跳转的路径后面加

this.$router.push("/search/"+this.params+"?k="+this.query)

2、模板字符串

this.$router.push(`/search/${this.params}?k=+${this.query}`)

3、对象写法

this.$router.push({name:'home',params:{keyword:this.keyword},query:{k:this.k})

路由传参相关面试题:

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

     不可以:不能这样书写,程序会崩掉,要用name

2:如何指定params参数可传可不传?

     如果占位了但是没有传递params那么url会出现问题(会导致原来要跳转的路径消失)

     可以在占位时在后面加上一个问号代表这个参数可传可不传

3、params参数可以传递也可以不传递,但是如果传递是空串,如何解决?

     如果传递的是空串,url同样会出现问题

 用undefined解决   params:{k:''||undefined}

this.$router.push({name:'home',params:{keyword:''||undefined},query:{k:this.k})

4、 路由组件能不能传递props数据?能,有三种写法,布尔值,函数,对象

this.$router.push({ name:'home',
                    params:{keyword:''||undefined},
                    query:{k:this.k},
                    //1、布尔值形式可以让params参数成为组件的参数
                    //接收组件直接用props就可以接收
                    props:true,

                    //2、对象写法:额外给路由组件传递参数
                    props:{a:1,b:2},

                    //3、函数写法:可以将params参数和query参数成为组件的参数
                    props:{($route)=>{return                                 
                    {keyword:$route.params.keyword,k:$route.query.k}
                    }}
})

重写push和replace(项目中遇到的问题)

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

原因:vue-router3.1.0之后, 引入了promise的语法,$router.push和$router.replace如果没有通过参数指定成功或者失败回调函数就返回一个promise来指定成功/失败的回调,且内部会判断如果要跳转的路径和参数都没有变化, 会抛出一个失败的promise

解决方法:

1、在每次调用的时候都传入成功或失败回调,或者catch捕获错误

 this.$router.push({
        name: "search",
        params: { keyword: this.keyword || undefined},
        query: { keyword1: this.keyword.toUpperCase() },
      }).catch(() => {});

但是每次使用的时候都要写,治标不治本 

2、直接修正Vue原型上的push和replace方法(推荐)

在router文件中,首先保存原来的方法,然后修改VueRouter原型上的push和replace方法,传递三个参数location:原先的参数(路径和参数) ;resolved:成功回调;rejected:失败回调。如果有成功和失败回调传入,就直接传入原先的push和replace,如果没有就手动传入两个回调()=>{}。

注意,在调用原方法的时候要用call改变上下文,因为要让组件实例来调用这个方法,如果不用call的话就这个函数的上下文就变成window了

 在组件中调用this.$router.push的时候this指的是组件实例,$router和$route都是在注册路由的时候添加在组件上的属性

push和replace是VueRouter原型上的方法

//将原有的push方法地址,保存起来,后期还能拿到原来的
const originPush = VueRouter.prototype.push   
const originReplace = VueRouter.prototype.replace   
//可以大胆的去修改原型的push,让原型的push指向另外一个函数
VueRouter.prototype.push = function(location,onResolved,onRejected){
  //location就是我们调用 this.$router.push,传递过来的对象
  if(onResolved === undefined && onRejected === undefined){
    //证明调用的时候只传递了个匹配路由对象,没有传递成功或者失败的回调
    return originPush.call(this,location).catch(() => {})
  }else{
    //证明调用的时候传递了成功或者失败的回调,或者都有
    return originPush.call(this,location,onResolved,onRejected)
  }
}
 
 
VueRouter.prototype.replace = function(location,onResolved,onRejected){
  //location就是我们调用 this.$router.replace,传递过来的对象
 
  if(onResolved === undefined && onRejected === undefined){
    //证明调用的时候只传递了个匹配路由对象,没有传递成功或者失败的回调
    return originReplace.call(this,location).catch(() => {})
  }else{
    //证明调用的时候传递了成功或者失败的回调,或者都有
    return originReplace.call(this,location,onResolved,onRejected)
  }
}

接口相关

postman测试接口

postman可以测试接口能不能正常使用,选择请求方式并输入接口地址就能测试接口能否正常使用,如果返回的code字段是200则表示成功

axios二次封装

向服务器发请求的方式:XMLHttpRequest,fetch,JQ,axios 

项目当中常用api文件夹放置api相关的东西,创建request.js重新配置axios

//这个request其实就是axios
const request=axios.create({
    //配置基础路径
    baseURL:'/api',
    //配置请求超时的时间
    timeout:5000,
})

为什么要二次封装?为了实现请求拦截器和响应拦截器

请求拦截器:在发送请求之前可以做一些处理

//请求拦截器可以在请求发出之前检测到,并进行一些处理
requests.interceptors.request.use((config) => {
        //config是配置对象,身上有个属性很重要,headers请求头
        return config;
    })

响应拦截器:数据返回之后进行一些处理

    //响应拦截器
    requests.interceptors.response.use((res) => {
    //成功的回调函数
    return res.data;
}, (error) => {
    //失败的回调函数
    return Promise.reject(new Error('faile'));
});

 记得对外暴露

接口统一管理

 项目小的时候:可以在组件的生命周期函数中发送请求

项目大的时候:在api文件夹下创建index.js对项目接口进行统一管理

export const reqSubmitOrder = ((tradeNo, data) => requests({ url: `/order/auth/submitOrder?tradeNo=${tradeNo}`, method: 'post', data }))

跨域问题

处理方式:配置代理服务器

在vue.config.js文件中配置

    //代理跨域
    devServer: {
        proxy: {
            //只要路径中有api就去找target的服务器要东西
            '/api': {
                target: 'http://gmall-h5-api.atguigu.cn',
            }
        }
    }

 原理:

当浏览器向服务器发起请求时,即使存在跨域问题,服务器依旧是会响应请求,并返回数据给浏览器,但当浏览器拿到数据后发现存在跨域问题了,这时候浏览器就不会将数据给页面,相当于把数据给扣留了。而代理服务器与前端页面同源,由他返回的数据就不存在跨域的问题,如图。 (备注:代理服务器与服务器之间是非同源,但不存在跨域问题,是因为服务器之间采用的是http请求,而不是ajax技术

nginx (相对繁琐,且需要一定的后端知识)

proxy:(相对简单,熟悉一定的webpack配置即可) ,可以配置单个或多个代理

尚品汇前端项目总结1(P1-P30)_第1张图片

nprogress进度条的使用 

安装nprogress,在api文件夹下的request.js中import引入,引入进度条样式

//引入进度条
import nprogress from "nprogress";
//引入进度条样式,可以在这个文件中改变样式
import 'nprogress/nprogress.css'

在请求拦截器和响应拦截器中使用,start进度条开始,done进度条开始

requests.interceptors.request.use((config) => {
        //进度条开始动
        nprogress.start();
        //config配置对象,包含header请求头
        return config;
    })
    //响应拦截器
    requests.interceptors.response.use((res) => {
    //成功的回调函数
    //进度条结束
    nprogress.done();
    return res.data;
}, (error) => {
    //失败的回调函数
    return Promise.reject(new Error('faile'));
});

Vuex

什么是Vuex:官方提供的一个插件,状态管理库,集中管理项目公共的数据 ,

项目比较大的时候才用,项目小完全不需要

使用

安装vuex(记得@3.6.2),新建store文件夹,引入Vue(依赖于Vue所以要引入Vue)和Vuex,使用Vuex插件,在main.js中引入store,引入后组件身上会多一个$store属性

获取store中的数据,直接$store获取或者用map(数组、对象【value是函数的形式】)

Vuex模块开发

将大仓库拆分成小module ,记得打开命名空间

import trade from './trade'
export default new Vuex.Store({
  modules: {
        trade
    }
})

typeNav三级联动

数据展示 

就是三连环:在mounted里面dispatch,在actions中拿调用api文件中设置的接口函数,向服务器发送请求得到返回的数据,并进行处理,返回成功则提交mutation修改state中的数据(state中的数据默认初始值应该根据返回数据的类型来设置),在组件中获取数据,最后展示数据,展示数据的时候常用v-for循环

添加动态背景

方法一::hover

方法二、data中设置一个值x,鼠标没有在任何一个一级标签的时候设置为-1,鼠标在哪个一级标签上就赋值为哪个标签的index,添加动态类名,当x==index的时候就加上这个类,当鼠标移开的时候x=-1

要求:当鼠标离开‘全部商品分类’的时候背景颜色才消失

使用事件的委派【在三级联动路由跳转的时候也有用到】,在一级列表和全部商品分类外面再套一个盒子,然后把鼠标移除事件放在外面的父盒子里面

事件委派的定义就是,把原来加给子元素身上的事件绑定在父元素身上,就是把事件委派给父元素。换句话说,就是把一个本来让下属干的事情交给上司做了。这样做的好处就是不在进行查询,减少了DOM操作,极大地改善了代码性能,它既可以为动态添加的元素附上效果,也可以改善代码性能

二三级分类显示与隐藏

方法一、:hover,鼠标移上就display:block否则display:none

方法二、js动态添加样式

 

给三级联动加上防抖与节流 

 防止出现卡顿,使用lodash,Vue2框架自带lodash,直接引入就行。lodash对外暴露的是一个下划线_,直接import _ from 'lodash'是全部引入,最好按需引入import throttle from 'lodash/throttle'

//节流
changBackground: throttle(function(index){
                this.currentIndex=index;
                },50)

三级联动路由跳转

 在路由跳转的时候会把选择产品的信息进行传递

因为router-link是一个组件,当服务器数据返回之后循环出很多的router-link组件,很耗内存,会出现卡顿,最好用编程式导航 但是如果直接在a标签上面用编程式导航也会导致大量绑定,所以可以用事件委派将事件绑定在父盒子上面。

事件委派存在的问题:如何识别a标签?如何获取参数?使用自定义属性,在所以的a标签中都设置一个:data-categoryName="c1.categoryName",一级标签为c1二级c2三级c3,判断元素身上是否有categoryname【注意小写】属性就可以判断是否为a标签,再添加一个自定义标签categoryId来分辨一级二级三级标签

{{c1.categoryName}}

自定义属性设置 :HTML5中我们可以使用data-前缀设置我们需要的自定义属性,来进行一些数据的存放。这里的data-前缀就被称为data属性 ;我们可以通过dataset来获取data属性在dataset中属性名会自动变为小写

三级路由跳转使用编程式导航+事件委派+自定义标签的形式实现

三级联动的显示与隐藏

 在home页面会一直显示,但是search中不会,所以在typenav挂载完时判断是在哪个路由下,如果在search那就隐藏typenav,并且鼠标移到商品时才显示,鼠标移出的时候又隐藏

三级分类的过渡动画

动画效果:在要用动画效果的一个元素外面套上transition标签,并且元素上一定要有v-show或者v-if标签

V-enter 进入的起点动画效果

V-enter-to 进入的终点动画效果

V-leave 离开的起点动画效果

V-leave-to 离开的终点动画效果

V-enter-active 进入时激活的样式

V-leave-active 离开时激活的样式

当有多个动画效果时可以给元素取name,然后把v换成name

当要包含多个元素的时候要用transition-group标签

还可以使用animate库

 
        
   .sort-enter,.sort-leave-to{
      height: 0px;
    }
    .sort-leave,.sort-enter-to{
      height: 461px;
    }
    .sort-enter-active{
      transition: 0.1s linear;    
    }
    .sort-leave-active{
      transition: 0.1s linear;    
    }
  }

防抖与节流 

用户行为过快会导致浏览器反应不过来,如果当前回调函数中有一些大量的业务,有可能会出现卡顿现象,因此需要防抖和节流 

效果

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

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

原理

防抖是维护一个计时器,规定在delay时间后触发函数,但是在delay时间内再次触发的话,都会清除当前的 timer 然后重新设置超时调用,即重新计时。这样一来,只有最后一次操作能被触发。

节流是通过判断是否到达一定时间来触发函数,若没到规定时间则使用计时器延后,而下一次事件则会重新设定计时器。

防抖的应用场景

( 1 ) 用户在输入框中连续输入一串字符,只会在输入完成后取最终输入的内容。然后发送ajax请求,这样可以有效地减少请求次数,节约请求资源。

( 2 ) window的resize、scroll事件,不断地调整浏览器的窗口大小、或者滚动时会触发对应事件,防抖函数让其只触发一次。

节流的应用场景

( 1 ) 鼠标连续不断地触发某事件(如点击),在单位时间内只触发一次。

( 2 ) 在页面的无限加载场景下,需要用户在滚动页面时,每隔一段时间发一次 ajax 请求,而不是在用户停下滚动页面操作时才去请求数据。

( 3 ) 监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断

实现:

方法一、使用lodash插件,其中包含防抖与节流业务

防抖:_.debounce(function(){},1000);其中function(){}为要防抖的函数,1s后执行,用户操作频繁但是只执行一次

节流:_.throttle(function(){},1000) 用户操作频繁,把频繁操作变为少量操作

Vue2框架自带lodash直接引入就可以

方法二、自己写防抖节流函数

防抖:分为立即执行版本和非立即执行版本

防抖函数中获得argument并使用apply改变this是为了让 debounce 函数最终返回的函数 this 指向不变(否则this会指向window)以及依旧能接受到 event 参数(这里接收的arguments其实就是event参数)。

非立即执行:触发事件后不会立即执行而是在n秒后执行

 
let input = document.querySelector('input')
    let a = 'a';
    input.oninput = debounce(function(a) {
        console.log(1)
    }, 1000)

    function debounce(fn, delay) {
        let time = null;
        function _debounce() {
            let args = [...arguments];
            clearTimeout(time);
            time = setTimeout(() => {
                fn.apply(this, args)
            }, delay)
        }
        return _debounce
    }

立即执行,触发后会先立即执行一次

  let input = document.querySelector('input')
    let a = 'a';
    input.oninput = debounce(function(a) {
        console.log(1)
    }, 1000, true)
function debounce(fn, delay, isImmediate = true) {
        let time = null;
        let isFirst = true

        function _debounce() {
            let args = [...arguments]
            if (time) {
                clearTimeout(time)
            }
            if (isFirst && isImmediate) {
                fn.apply(this, args);
                isFirst = false;
            }
            time = setTimeout(() => {
                fn.apply(this, args)
                isFirst = true;
            }, delay)
        }
        return _debounce
    }

节流:有两个版本,时间戳和定时器

时间戳版本

function throttle(fn, wait) {
        let lastTime = 0;
        function _throttle() {
            let nowTime = Date.now();
            let args = [...arguments]
            if (nowTime - lastTime > wait) {
                fn.apply(this, args);
                lastTime = nowTime;
            }
        }
        return _throttle
    }

定时器

function throttle(fn, wait) {
        let lastTime = 0;

        function _throttle() {
            let nowTime = Date.now();
            let args = [...arguments]
            if (nowTime - lastTime > wait) {
                fn.apply(this, args);
                lastTime = nowTime;
            }
        }
        return _throttle
    }

时间戳版本是在触发时立马就执行,而定时器版本是在触发一段时间后再执行

 参考内容

你可能感兴趣的:(尚品汇项目总结,前端,vue.js,javascript)