Vue 项目

1.项目的其他配置


创建项目 vue create name --packageManager npm

  1. eslint 验证关闭

    lintOnSave:false //在vue.config.js文件中加入这行代码
  2. 重启时打开浏览器

    • 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
            }
        ]
    })

  • 使用路由

    1. 在入口文件main.js引入路由,然后注册路由

    import router from '@/router'
    ​
    new Vue({
      render: h => h(App),
      router
    }).$mount('#app')
    1. 路由出口:展示路由组件的地方

      <template>
        <div>
          <Header></Header>
          <!-- 路由出口 -->
          <router-view></router-view>
          <Footer></Footer>
        </div>
      </template>
      • <router-view></router-view> 就是展示路由组件的入口

  1. 重定向路由

     {
        path:'*',
        redirect:'/home'
      }
  2. 路由的跳转

    • 声明式跳转 router-link

      1. 本质是 a 标签,只是简单跳转的时候可以用

        <router-link to="/login">登录</router-link>
        <router-link to="/register">注册</router-link>
        • to 属性:跳转到哪个组件

    • 编程式跳转 push|replace

      1. 需要其他业务逻辑的时候,比如传参给其他路由

        • 在需要跳转的地方定义 事件

          <button  type="button" @click="goSearch">搜索</button>
          ​
        • 调用方法

           methods:{
                  goSearch(){
                      this.$router.push('/search')
                  }
              }
          • 当我们注册路由之后 每个组件都有$router 属性 里面的 push | replace 方法可以帮助我们跳转到对应的了路由

        1. 路由原信息,自己定义的东西 对象形式

          $route.meta //存放原信息
          • 里面存放的数据,非路由组件也能访问到

      • 路由传参

        • 三种形式

          1. 对象形式

             goSearch(){
                        this.$router.push({
                            name:'search',
                            params:{
                                keyword:this.keyword
                            }
                        })
                    }
            • 这种形式最好要在路由中定义name

              {
                	name:'search',
                	path:'/search/:keyword',
                	component:Search,
                	meta:{isShow:true}
              }
              1. 字符串和模板字符串形式不做演示

        • params参数

          1. 需要在路径上使用占位符

              path:'/search/:keyword',
                        component:Search,
                        meta:{isShow:true}
          2. 存放在$router.params当中

        • query参数

          1. 类似于ajax中的GET传参?k=v的形式

          2. 存放在$router.query当中

  • 路由几个重要的点

    1. path 跳转不能和 params一起使用

    goSearch(){
                // this.$router.push({
                //     name:'search',
                //     params:{
                //         keyword:this.keyword
                //     }
                // })
                this.$router.push({
                    path:"/search",
                    params:{
                       keyword: this.keyword
                    }
                })
    • 会报错的,必须结合name来使用

  1. 如何指定params参数可传可不传

    • 如果params已经占位了,不穿的话URL会有问题

    • 解决方法

      在占位的时候在后面加一个问号

      {
          name:'search',
          path:'/search/:keyword?',
          component:Search,
          meta:{isShow:true}
       }
  2. 如果传一个空字符的解决方法

    goSearch(){
                this.$router.push({
                    name:'search',
                    params:{
                        keyword:this.keyword || undefined
                    }
                })
            }
    • undefined解决

  3. 路由组件也可以传props

    • 有三种写法,因为不常用所以不写了

  4. 重写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()函数来联系 mutations

      const 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=])
    • 参数详解

      1. func (Function): 要节流的函数。

      2. [wait=0] (number): 需要节流的毫秒。

      3. [options=] (Object): 选项对象。

8.获取自定义属性


  • 当使用事件委托时,不知道对应标签,即可使用这个方法(自定义属性)

  • goSearch(event){
            // 传ID和标题
            let element = event.target
            console.log(element,element.dataset);
    
           }
    • element.dataset 即可获取自定义属性

9.过度动画


  1. 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:  整个运动状态(强调运动的结果,结束状态)
  2. 配合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.组件通信

  1. props :用于父子组件通信

  2. @on | @emit 可以实现子给父通信 (自定义事件)

  3. 全局事件总线 $bus 全能

  4. pubsub-js :发布订阅模式 全能

  5. 插槽 3种方式

  6. 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)
    1. 三个参数时:删除从i开始,删除j个元素,并且插入元素k

    2. 两个参数时:删除从i开始,删除j个元素

    3. 一个参数时:删除从i包括i后面的元素

16.分页器


  • 分页器展示,需要哪些条件

    1. 当前页数 : pageNo

    2. 每页展示的数据 : pageSize

    3. 一共要展示的数据 : total

    4. 一共有多少页 : totalPage

    5. 连续页码数 : 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

    1. event.offsetX :鼠标在当前元素的位置

    2. element.offsetLeft :元素距离带定位的父元素的偏移量

  • 等比例移动

    1. 大图片移动距离 = 遮挡层移动距离 * 大图片最大移动距离 / 遮挡层最大移动距离

  • 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遍历数组的方法


  1. every():遍历数组,每一个元素 都满足条件 则返回 true,否则返回 false

  2. 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.注册与登录


  • 身份验证:

    1. token:用户登录之后会不会直接返回数据,而是返回一个token

    2. 用户需要在发请求时将token放在请求头,才能返回真正的数据

23.路由守卫


  • AI的回答

    Vue路由守卫可以让你在用户路由到某个页面前触发一些逻辑,可以用来检查用户是否登录、检查权限等操作。Vue提供了三种不同的路由守卫,分别是全局守卫、路由守卫和组件守卫。

    1. 全局守卫

    全局守卫是在一个Vue应用程序中定义的,它们会对该应用程序中的所有路由生效。以下是你可以使用的三个全局守卫:

    • router.beforeEach:在进入路由之前触发,可以用来检查用户是否登录、检查权限等等。

    • router.afterEach:在路由完成跳转之后触发,可以用来进行埋点、记录日志等操作。

    • router.beforeResolve:在组件内的异步路由被解析之后触发,可以用来进行路由的处理。

    1. 路由守卫

    在单个路由上定义的守卫称作路由守卫,你可以在路由定义的时候添加以下属性:

    • beforEnter:这个是在一个路由被激活之前触发守卫。这种方式只能检查路由对应组件的状态,无法对应用程序状态进行检查。

    1. 组件守卫

    组件守卫是和路由守卫类似的,不同的是它是在组件内部定义的。以下是四个组件级别的守卫:

    • beforeCreate:在实例被创建之前立刻调用,此时所有的option都会被合并,生命周期钩子函数以及事件配置已经都开始初始化了。

    • created:在实例后完成调用,此时实例已经完成了数据观测,computed属性,watcher/event监听以及第一轮组件的挂载。

    • beforeMount:在挂载之前调用,即$el属性挂载到实例之前。

    • mounted:组件挂载后调用,此时已经完成了DOM的创建和挂载。

    使用路由守卫能够使你方便的控制页面之间的跳转行为,从而带来更好的用户体验和更高的安全性。

  • 具体使用(AI的回答)

    Vue路由守卫的具体使用方式如下:

    1. 全局守卫

    在全局守卫中,可以定义三个钩子函数,分别是beforeEachafterEachbeforeResolve

    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()
    });
    1. 路由守卫

    路由守卫的使用方式和全局守卫类似,不同的是它是在单个路由上定义的,可以通过beforeEnter属性来定义:

    const router = new VueRouter({
      routes: [
        {
          path: '/foo',
          component: Foo,
          beforeEnter: (to, from, next) => {
            // ...
            next();
          },
        },
      ],
    });
    1. 组件守卫

    在组件内部定义守卫非常灵活,可以根据组件需要的场景进行定义:

    export default {
      beforeCreate() {},
      created() {},
      beforeMount() {},
      mounted() {},
    };

    通过路由守卫,我们可以轻松地进行路由拦截、登录验证、权限控制等操作。这可以给我们带来更好的用户体验和更高的安全性。

24.路由懒加载


25.图片懒加载


  1. 安装:$ npm i vue-lazyload@1.3.3 -D

  2. 引入: import VueLazyload from 'vue-lazyload'

  3. 注册:

    Vue.use(VueLazyLoad, {  
        preLoad: 1,  
        error:require('./assets/img/error.jpg'), 
        loading:require('./assets/img/homePage_top.jpg'),
        attempt: 2, 
    })	
  4. 使用:在需要图片懒加载的地方将 src 替换为 v-lazy

26.自定义插件


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值