vue项目的实用性总结

news2025/3/14 10:52:21

1、mockjs 基本使用 ★

  1. 安装:npm i mockjs

  2. src/mock/index.js内容如下:

    import Mock from 'mockjs'
    //制订拦截规则
    Mock.mock('http://www.0313.com','get','你好啊')
    
  3. 记得在main.js中引入一下,让其参与整个项目的运行。

  4. 只要发出去的是get类型的ajax请求,地址只要是http://www.0313.com就能拦截了。

  5. 备注:使用mockjs后,浏览器的网络选项卡中一定看不到被拦截的请求。

  6. Test组件中发送一个请求测试一下。

    async handleTest(){
      const result = await axios.get('http://www.0313.com')
      console.log(result)
    }
    

2、处理 vuex 假报错 ★

  1. 问题描述:从undefined上读取属性会报错,代码演示如下:

    //例如vuex中数据格式如下:
    const state = {
      a:[],
      x:{}
    }
    
    <!--下写法均会报错-->
    <h1>{{a[0].id}}</h1>
    <h1>{{x.y.z}}</h1>
    
  2. 如何解决?

    • 第一种方法:在最初就设计好数据层次 —— 不推荐。

      const state = {
        a:[{}],
        x:{y:{}}
      }
      
    • 第二种方法:使用数据时加判断。

      <h1 v-if="a[0]">{{a[0].id}}</h1>
      <h1 v-if="x.y">{{x.y.z}}</h1>
      
    • 第三种方法:使用问号 —— 最推荐。

      <h1>{{a[0]?.id}}</h1>
      <h1>{{x.y?.z}}</h1>
      
    • 第四种方法:若模板中恰巧使用了v-for遍历,那么问题自动消失。

      <h1 v-for="s in carouselList" :key="s.id">{{s.imgUrl}}</h1>
      
    • 第五种方法:使用&&短路

      <h1>{{a[0] && a[0].id}}</h1>
      <h1>{{x.y && x.y.z}}</h1>
      

3、vue-awesome-swiper 的使用 ★

  1. Swiper是专门做轮播图的库,在:原生项目、Vue项目、React项目中,均可使用。

  2. Vue项目中,可以使用vue-awesome-swiper来更方便的实现轮播图。

  3. 安装:npm i vue-awesome-swiper@4.1.1

  4. Test组件中实现一个简单的轮播

    <template>
      <swiper class="swiper" :options="swiperOption">
        <swiper-slide class="item">Slide 1</swiper-slide>
        <swiper-slide class="item">Slide 2</swiper-slide>
        <swiper-slide class="item">Slide 3</swiper-slide>
        <swiper-slide class="item">Slide 4</swiper-slide>
        <div class="swiper-pagination" slot="pagination"></div>
        <div class="swiper-button-prev" slot="button-prev"></div>
        <div class="swiper-button-next" slot="button-next"></div>
      </swiper>
    </template>
    
    <script>
      import {Swiper, SwiperSlide} from 'vue-awesome-swiper'
      import 'swiper/css/swiper.css'
    
      export default {
        name: 'Test',
        components: {Swiper,SwiperSlide},
        data() {
          return {
            //轮播图配置对象
            swiperOption: {
                slidesPerView: 1, //同时展示几屏
                spaceBetween: 30, //每屏间隔
                loop: true, //是否循环轮播
                speed: 1000, //切换速度
                //自动轮播
                autoplay: {
                  delay: 2000,//轮播间隔
                  disableOnInteraction: false //鼠标点击后,是否禁止自动轮播
                },
                //分页器配置(小圆点)
                pagination: {
                  el: '.swiper-pagination', //分页器元素
                  clickable: true //小圆点是否可以点击
                },
                //导航按钮(左箭头、右箭头)
                navigation: {
                  nextEl: '.swiper-button-next',
                  prevEl: '.swiper-button-prev'
                }
            }
          }
        }
      }
    </script>
    
    <style lang="less" scoped>
      .item {
        line-height: 200px;
        text-align: center;
        font-size: 40px;
        background-image: linear-gradient(45deg,red,yellow,green);
        height: 200px;
      }
    </style>
    

4、整合搜索参数 ★

  1. 目标:无论是点击分类,还是点击搜索Search组件都需要接收路由参数。

  2. 使用watch监听$route

    watch:{	
      $route:{		
        //立即监视,目的是为了让第一次搜索的时候,可以拿到参数。
        immediate:true,
        //此处的value是谁? —— 是$route的新值(监视的是谁,handler得到的就是谁的新值)
        handler(value){	
          console.log(value.query)		
        }	
      }
    }
    
  3. 具体思路:使用watch监听$route,只要当前路由信息变化,就存储搜索参数。

    watch:{
    	$route:{
            //追加立即监视,为了初次挂载拿到参数
    		immediate:true,
            //注意此处的query是谁?—— 路由传递过来的query参数   
    		handler({query}){
                //重置之前的一级id、二级id、三级id,随后将最新的参数合并进去
    			Object.assign(this.searchParams,{
    				category1Id:'', //重置一级分类id
    				category2Id:'', //重置二级分类id
    				category3Id:'', //重置三级分类id
    			},query)
    		}
    	}
    }
    

5、对分页器的理解★

  1. 为什么要用?—— 大量数据不能一次全部展示。
  2. 分页器想工作,需要哪些数据?
    • 总数(total)
    • **页大小(pageSize)**通俗理解:每页展示几条。
    • **页码(pageNo)**通俗理解:看第几页。
    • **连续页数(continues)**一般为奇数,例如:357 等,为什么?—— 好看,通常是5
    • 总页数(totalPage) = total/pageSize(注意要向上取整)

备注:红色是必须传项,绿色的不用传,可以根据红色算出来。

6、分页器_ 连续页_特殊情况一 ★

  • 连续页数continues > totalPage

  • 具体编码:

      export default {      
        name: "Pagination",   
        props:['total','pageSize','pageNo','continues'],
        computed:{
          // 计算总页数
          totalPage(){
            return Math.ceil(this.total / this.pageSize)
          },
          // 计算连续页的起始页、结束页
          startEnd(){
            // 获取外部传入的各种参数
            const {continues,pageNo,totalPage} = this
            let start = 0 //起始页初始值
            let end = 0 //结束页初始值
            // 判断一下目前的情况是不是一个“大变态”
            //(你要求的连续页数,比我倾家荡产的总页数都要多,推都不推,都给你)
            if(continues > totalPage){
              start = 1
              end = totalPage
            }else{
              // 标准情况
              start = pageNo - (continues - 1)/2
              end = pageNo + (continues - 1)/2
            }
            return {start,end}
          }
        }
      };
    

7. 分页器_ 连续页_特殊情况二★

  • 算连续页start的时候,往左推,推多了,出现 start < 1 了。

  • 处理方式如下:

    export default {      
        name: "Pagination",   
        props:['total','pageSize','pageNo','continues'],
        computed:{
          // 计算总页数
          totalPage(){
            return Math.ceil(this.total / this.pageSize)
          },
          // 计算连续页的起始页、结束页
          startEnd(){
            // 获取外部传入的各种参数
            const {continues,pageNo,totalPage} = this
            let start = 0 //起始页初始值
            let end = 0 //结束页初始值
    
            // 判断一下目前的情况是不是一个“大变态”
            //(你要求的连续页数,比我倾家荡产的总页数都要多,推都不推,都给你)
            if(continues > totalPage){
              start = 1
              end = totalPage
            }else{
              // 标准情况(正常左推,右推)
              start = pageNo - (continues - 1)/2
              end = pageNo + (continues - 1)/2
              // 判断左边是否“冒了”(start是否小于1)
              if(start < 1){ 
                start = 1
                end = continues
              }
            }
            return {start,end}
          }
        }
      };
    

8. 分页器_ 连续页_特殊情况三★

  • 往右推,算结束页码的时候,推多了,出现end > totalPage 了。

    >

  • 处理方式如下:

    export default {      
        name: "Pagination",   
        props:['total','pageSize','pageNo','continues'],
        computed:{
          // 计算总页数
          totalPage(){
            return Math.ceil(this.total / this.pageSize)
          },
          // 计算连续页的起始页、结束页
          startEnd(){
            // 获取外部传入的各种参数
            const {continues,pageNo,totalPage} = this
            let start = 0 //起始页初始值
            let end = 0 //结束页初始值
    
            // 判断一下目前的情况是不是一个“大变态”
            //(你要求的连续页数,比我倾家荡产的总页数都要多,推都不推,都给你)
            if(continues > totalPage){
              start = 1
              end = totalPage
            }else{
              // 标准情况(正常左推,右推)
              start = pageNo - (continues - 1)/2
              end = pageNo + (continues - 1)/2
              // 判断左边是否“冒了”(start是否小于1)
              if(start < 1){ 
                start = 1
                end = continues
              }
              // 判断右边是否“冒了”(end是否大于totalPage)
              if(end > totalPage){ 
                start = totalPage - continues + 1
                end = totalPage
              }
            }
            return {start,end}
          }
        }
      };
    

9. 分页器_整体显示

  • 分页器整体的显示,逻辑如下:

    • 当前已经是第一页了,那么【上一页】就不能点了。
    • 当前已经是最后一页了,那么【下一页】就不能点了。
    • 两个数中间,有其他数,再出现三个点。
    	<div class="pagination">
    		<button :disabled="pageNo === 1">上一页</button>
    		<button v-if="startEnd.start !== 1">1</button>
    		<span v-show="startEnd.start > 2">···</span> 
        
    		<!-- 连续页区域 -->
        	<button 
    			v-for="(number,index) in (startEnd.end - startEnd.start + 1)" 
    			:key="index" 
    			:class="{active: (index + startEnd.start) === pageNo }"
    		>
    			{{index + startEnd.start}}
    		</button>
    	
    		<span v-show="startEnd.end < totalPage - 1">···</span>
    		<button v-show="startEnd.end !== totalPage">{{totalPage}}</button>
    		<button :disabled="pageNo === totalPage">下一页</button>
    		<span>共 {{total}} 条</span> 
      </div>
    

10.分页器_整体交互

  • 第一步:对接真实数据,把:totalpageNopageSize,传给Pagination组件。

    注意1:total是服务器返回给我们的,要去searchResInfo中拿。

    注意2:pageNopageSize是我们传给服务器的,要去searchParams中拿。

    注意3:continues是连续页数,是我们自己指定的。

    <!-- 分页器 -->
    <Pagination 
    	:total="searchResInfo.total" 
    	:pageNo="searchParams.pageNo" 
    	:pageSize="searchParams.pageSize" 
    	:continues="5"
    />
    
  • 第二步:传递页码,Pagination组件将用户点击的页码,传给Search组件,Search组件接收参数,随后重新请求数据。

    1. Saecrh组件中:

      <template>	
        <!-- 分页器 -->    
        <Pagination         
          :total="searchResInfo.total" 
      	:pageSize="searchParams.pageSize" 
      	:pageNo="searchParams.pageNo" 
      	:continues="5"
      	:changePageNo="changePageNo" 
        />
      </template>
      
      <script>	
        //......    
        methods:{        
          //用于接收分页器传递过来的页码(pageNo)       
          changePageNo(n){            
            //更新参数            
            this.searchParams.pageNo = n
          }    
        }    
        //......
      </script>
      
    2. Pagination组件中:

      <template>	
      	<div class="pagination">
          <!-- 此处给所有能点击的东西绑定上click事件,事件的回调为sendPageNo -->
          <button @click="changePageNo(????)">????</button>
        </div>
      </<template>
      
      <script>   
        export default {        
          name: "Pagination",        
          props:['total','pageNo','pageSize','continues','changePageNo'],        
        }; 
      </script>
      

11、 详情组件 _ 放大镜★

  • 思路:鼠标动,遮罩动,大图区域跟着动。

  • 具体代码:

    详情组件,给放大镜组件传递图片url

    <Zoom :imgurl="info.skuInfo.skuDefaultImg"/>
    

    放大镜组件:

    <template>
      <div class="spec-preview">
        <img :src="imgurl" />
        <div class="event" @mousemove="move"></div>
        <div class="big">
          <img :src="imgurl" ref="bigImg"/>
        </div>
        <div class="mask" ref="mask"></div>
      </div>
    </template>
    
    <script>
      export default {
        name: "Zoom",
        props:['imgurl'],
        methods: {
          move(event){
            // 获取鼠标位置
            let mouseX = event.offsetX
            let mouseY = event.offsetY
    
            // 获取遮罩DOM元素
            let {mask,bigImg} = this.$refs
    
            // 计算出遮罩位置
            let maskX = mouseX - mask.offsetWidth/2
            let maskY = mouseY - mask.offsetHeight/2
    
            // 限制遮罩不要超出父容器
            if(maskX < 0){
              maskX = 0
            }else if(maskX > mask.offsetWidth){
              maskX = mask.offsetWidth
            }
            if(maskY < 0){
              maskY = 0
            }else if(maskY > mask.offsetHeight){
              maskY = mask.offsetHeight
            }
    
            // 给遮罩应用位置
          mask.style.left = maskX + 'px'
            mask.style.top = maskY + 'px'
    
            // 给大图应用位置
            bigImg.style.left = -2*maskX + 'px'
            bigImg.style.top = -2*maskY + 'px'
          }
        },
      }
    </script>
    

    用第三方库实现:vue-photo-zoom-pro

12、限制购买数量_思路★

购买商品时,输入的最大购买数量,有两种选择:200 最大库存量,大部分电商的输入限制是200

  • 必须是1200的正整数。
  • 若输入的是1-200之间的小数,则取其整数部分。
  • 若输入小于1,则重置为1
  • 若输入大于200,则重置为200
  • 若输入的是其他值,则重置为1

13、购物车_理解临时标识★

明确几个点:

  • 每个用户都有自己的购物车,不可能大家共用一个。

  • 如何区分不同的用户?(服务器如何知道你是谁?)

    办法一:登录系统 (最正式的做法)

    办法二:使用临时标识(其实就是:随机生成的一个UUID,不能真正下单,只是为了临时支撑购物车)。

  • 为什么有的网站,不登录也有购物车?

    尽可能引导用户消费,或者说增强用户体验。

  • 为什么有的网站,必须登录才有购物车,甚至必须登录才能搜索?

    ①用户量大(不愁用户)②网站业务逻辑复杂,登录以后有些逻辑好处理。

14、 购物车_实现临时标识★

  1. 使用uuidjs生成,安装uuid:npm i uuid

  2. 为确保生成的userTempId不丢失,需要存到localStorage中。

  3. /src/utils/auth.js中创建一个getUserTempId的方法,用于提供userTempId

    整体思路为:

    • 如果localStorage中有,就返回。
    • 没有就生成一个,随后存到localStorage中,再返回。
    /* 
        getUserTempId的功能是:
            方法会从localStorage中读取用户临时标识,
                1.如果有就返回,随后就用。
                2.如果没有
                	第一件事:生成一个新的临时标识,
                    第二件事:存入localStorage  ——  为了以后使用
                    第三件事:返回 ——  为了这次使用
      */
    import {v4} from 'uuid';
    
    export function getUserTempId(){
        // 尝试从localStorage中读取临时标识
        const userTempId = localStorage.getItem('userTempId')
        // 判断标识是否存在
        if(userTempId){
            // 返回之前存入的标识
            return userTempId
        }else {
            // 生成一个新的临时标识
            const newUserTempId = v4()
            // 存入localStorage
            localStorage.setItem('userTempId',newUserTempId)
            // 返回
            return newUserTempId
        }
    }
    
  4. 编辑/src/api/myAxios.js,在axios请求拦截器中,让所有请求头,都携带userTempId

    import {getUserTempId} from '@/utils/auth'
    
    //请求拦截器
    myAxios.interceptors.request.use((config)=>{
    	//进度条走起
    	nprogress.start()
    	//向请求头中添加userTempId
    	config.headers.userTempId = getUserTempId()
    	//返回本次请求的配置信息
    	return config
    })
    

15.购物车勾选_prevent一个小优化★

当我们勾选一个商品时,先阻止掉默认行为,即:不让checkbox立刻勾选上。因为这样checkbox的勾选才是完全由服务器决定,操作很简单:加上一个prevent修饰符。

<input 
	type="checkbox" name="chk_list" 
	:checked="cartInfo.isChecked" 
	@click.prevent="checkOne(cartInfo)"
>

16.函数防抖、函数节流 ★

16.1 函数防抖

  1. 标准概念:事件触发n秒后再执行逻辑,若这n秒内事件又被触发,则重新计时n秒,之前的逻辑不执行。

  2. 通俗理解:要做的事,总是改来改去,那么就等你下发指令后n秒,我再做,免得你再改。

  3. 目的:防止复杂逻辑频繁触发(例如:复杂的运算、网络请求等)。

  4. 生活中的例子:调节空调温度时,按下【+】或【-】,等1秒空调才有反应。

    最简洁的记法:就要最后那一下。

  5. 最具代表性的应用场景:实时搜索,但要注意:像百度这种专业做搜索的网站,为了用户的丝滑体验,没有做防抖,但像boss直聘这种主营业务不是搜索,但有搜索功能的网站,都会做防抖。

  6. 例子如下:

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="UTF-8" />
        <title>1.函数防抖</title>
        <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
      </head>
      <body>
        <input type="text" placeholder="请输入关键词" id="input">
    
        <script type="text/javascript" >
            const input = document.getElementById('input')
    
            // 靠程序员自己去用定时器实现防抖
            /* let timer
            input.addEventListener('keyup',(e)=>{
                if(timer){
                    clearTimeout(timer)
                }
                timer = setTimeout(()=>{
                    console.log('发请求了',e.target.value)
                },300)
            }) */
    
            // 靠lodash去实现防抖
            input.addEventListener('keyup',_.debounce((e)=>{
                console.log('发请求了',e.target.value)
            },300))
    
        </script>
    
      </body>
    </html>
    

16.2 函数节流

  1. 标准概念:在n秒内,无论触发事件多少次,逻辑只执行一次。

  2. 通俗理解:你催的再急,也没用,我的速度是有极限的。

  3. 目的:在一定的时间内,防止复杂逻辑频繁触发(例如:复杂的运算、网络请求等)。

  4. 生活中的例子:

    • 就算火车要开了,我3秒内也只能吃一口面。
    • 玩游戏时候的技能冷却时间。

    通俗理解:别催,催也没有用,我就是这个速度。

  5. 最具代表性的应用场景:秒杀按钮。

  6. 关于lodash节流的配置

    <!DOCTYPE html>
    <html>
    	<head>
    		<meta charset="UTF-8" />
    		<title>2.函数节流</title>
    		<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
    	</head>
    	<body>
    		<button id="btn">立即购买</button>
    
    		<script type="text/javascript" >
    			const btn = document.getElementById('btn')
    
    			let canExecute = true //用于标识是否可以执行逻辑
    
    			/* 靠程序员,用定时器去实现节流,不推荐 */
    			/* btn.onclick = ()=>{
    				if(canExecute){
    					canExecute = false
    					console.log('发起网络请求~~')
    					setTimeout(()=>canExecute = true,2000)
    				}
    			} */
    
    			btn.onclick = _.throttle(()=>{
    				console.log('发起网络请求~~')
    			},2000,{trailing:false})
                
    		</script>
    	</body>
    </html>
    

17、element-ui_按需引入(推荐)★

  1. 第一步:安装,命令为 npm i element-ui

  2. 第二步:安装一个包,npm install babel-plugin-component -D

  3. 第三步:修改babel.config.js内容如下:

    module.exports = {
      presets: [
        '@vue/cli-plugin-babel/preset'
      ],
      plugins: [
        [
          "component",
          {
            "libraryName": "element-ui",
            "styleLibraryName": "theme-chalk"
          }
        ]
      ]
    }
    
  4. 第三步:去main.js中引入并注册,你想用的组件 或 使用插件

    import {Button} from 'element-ui';
    
    // 第一种方式:全局注册按钮组件
    // Vue.component(Button.name, Button);
    
    // 第二种方式:使用Button插件
    Vue.use(Button)
    
  5. 第四步:随便在任何一个组件里,去使用上一步注册的组件即可

18、. element-ui_提示类组件★

  • main.js中引入所有提示相关组件,并放在Vue原型上。

    import {Message} from 'element-ui';
    
    Vue.prototype.$message = MessageBox;
    
  • 所有用到提示的地方,直接复制官网上的代码即可,例如:

    this.$message.info('一些消息');
    this.$message.success('一些成功消息');
    this.$message.warning('一些警告消息');
    this.$message.error('一些错误消息');
    
    this.$message({
        type:'success',
        message:'恭喜支付成功!',
        showClose:true,
        duration:1000,
    })
    

19、对 token 的理解 ★

  1. 只有登录成功,服务器才会为该用户生成一个token

  2. token是用户的真正标识,根据token,可以去服务器那查询到该用户的所有信息。

  3. 浏览器端需要保存一份token,哪里保存? —— localStorage中。

    为什么? —— 为了做自动登录(即:刷新页面、重开浏览器,依然是登录的状态)

  4. 我们的服务器在登录成功后,会返回:用户昵称、用户真实姓名、token等等,但我们只存token

    为什么? —— 根据token可以找服务器获取到更多的用户信息。

    大多数情况,登录成功后,服务器不会给任何多余的信息,只给一个token

  5. 若服务器不允许多端登录,则服务器要在用户登录后,把之前的token销毁,再生成一个新的token

    这个得看具体需求,目前我们的尚品汇服务器允许多端登录。

  6. token不是永久有效的,通常都有一个有效期,短的2小时,长的30天,具体时间看项目需求。

  7. 服务器那边什么情况需要销毁token?—— 以下几个场景:

    • 到了token的过期时间,服务器销毁了token
    • 若不允许多端登录,用户在其他终端登录了系统,服务器销毁了之前的token
    • 若允许多端登录,但达到了上限,服务器会销毁最开始的token
    • 用户触发退出登录,服务器会销毁该用户的token

20、token 持久化存储+携带 ★

  1. 明确:登录成功后,token要存入localStorage中。

  2. 编写src/utils/auth.js追加三个方法,如下:

    //用于将token存入localStorage
    export function saveToken(token){
    	localStorage.setItem('token',token)
    }
    
    //用于从localStorage中读取token
    export function readToken(){
    	return localStorage.getItem('token')
    }
    
    //从localStoragre删除用户token
    export function deleteToken(){  
       localStorage.removeItem('token')
    }
    
  3. Login组件中,登录成功后,将token存入localStorage

    methods: {
      async login() {
          // 获取登录需要的参数
          const { phone, password } = this;
          // 请求登录
          let { code, data, message } = await reqLogin({ phone, password });
          if (code === 200) {
            // 登录成功给个提示
            this.$message.success("登录成功!");
            //token存入本地
            saveToken(data.token)
            // 跳转到主页
            this.$router.push("/home");
          } else {
            // 登录失败提示原因
            this.$message.warning(message);
          }
        },
    },
    
  4. 修改src/api/ajax.js每次请求,都要携带token

    import {getUserTempId,readToken} from '@/utils/auth'
    
    //请求拦截器
    request.interceptors.request.use((config)=>{
        /*****/
        // 携带token
        config.headers.token = readToken()
        /*****/
    	return config
    })
    

21、 获取用户信息存入 vuex ★

  1. 明确:根据token获取到的用户信息,存在vuex中,方便其他组件使用。

  2. 编写接口请求函数,根据token获取用户信息。

    // 此函数专门用于获取用户信息
    export const reqUserInfo = () => request.get('/api/user/passport/auth/getUserInfo')
    
  3. 操作文件:src/store/modules/user.js,编写vuex代码,存储用户信息。

    import {reqUserInfo} from '@/api'
    import {Message} from 'element-ui'
    
    const actions = {
    	async getUserInfo({commit}){
    		//发送请求获取用户信息
    		const {code,message,data} = await reqUserInfo()
    		//判断是否获取成功
    		if(code === 200){
    			commit('SAVE_USER_INFO',data)
    		}else{
    			Message.error(message)
    		}
    	}
    }
    
    const mutations = {
    	// 保存用户数据
    	SAVE_USER_INFO(state,info){
    		state.info = info
    	}
    }
    
    const state = {
    	info:{} //初始化用户信息
    }
    
    export default {
      namespaced:true,
      actions,
      mutations,
      state
    }
    
  4. Login组件中,登录成功后,dispatch一个getUserInfo获取用户信息。

    async login(){
      // 获取用户的输入
      const {phone,password} = this
      // 请求登录
      const result = await reqLogin({phone,password})
      // 判断登录是否成功
      if(result.code === 200){
        // 第一步:提示信息
        this.$message.success('登录成功')
        // 第二步:存储token
        saveToken(result.data.token)
        // 第三步:获取用户信息
        this.$store.dispatch('user/getUserInfo')
        // 第四步跳转到主页
        this.$router.push('/home')
      }else{
        // 若登录失败,提示原因
        this.$message.error(result.message)
      }
    }
    

22. 用户信息如何不丢失 ★

问题描述:刷新会导致vuex中的用户信息丢失。

如何解决?

  • 第一种方式:登录成功后dispatch一下,在App组件中mounted的时候再dispatch一下

    import { readToken } from './utils/auth'
    
    export default {
        name:'App',
        components:{Header,Footer},
        mounted() {
            if(readToken()){
                this.$store.dispatch('user/getUserInfo')
            }
        },
    }
    
  • 第二种方式:靠导航守卫去维护用户信息。

    token存入localStorage,根据token获取的用户信息存入vuex,每次刷新页面时,靠导航守卫去维护用户信息。

23. 导航守卫 ★

  1. 概念:路由跳转时,在特定的时刻,执行的一些函数。

  2. 作用:可以在路由跳转时,追加特殊逻辑(路由鉴权、获取用户信息等)。

  3. 技巧:守卫越早使用越好。

  4. 分类:

    • 全局守卫(很常用)====> 宏福科技园门口的大爷
    • 路由独享守卫 =========> 宏福科技园A座楼的保安
    • 组件内守卫 ===========> 宏福科技园A座楼3027室的保安
  5. 我们的项目为什么使用导航守卫?

    • 每次路由跳转、刷新页面时,都要获取用户信息。
    • 对某些敏感路由做出访问限制。
  6. 严重注意:导航守卫一定一定要有出口!—— 什么是出口?next()就是。

  7. 导航守卫小案例:

    import Vue from 'vue'
    import VueRouter from 'vue-router'
    import Home from '../pages/Home'
    import Hot from '../pages/Hot'
    import Like from '../pages/Like'
    import Near from '../pages/Near'
    
     Vue.use(VueRouter)
    
    //暴露路由器
    const router =  new VueRouter({
    	mode:'history',
    	routes:[
    		{
    			path:'/home',
    			component:Home
    		},
    		{
    			path:'/hot',
    			component:Hot
    		},
    		{
    			path:'/like',
    			component:Like
    		},
    		{
    			path:'/near',
    			component:Near
    		},
    	]
    })
    
    //需求:只有尊贵的vip用户,才能访问: /like   /near
    
    // 用于标识是否为vip
    let isVIP = false
    // 设计一个鉴权名单
    let authPath = ['/like','/near']
    
    // beforeEach所指定的函数就是全局守卫
    // 全局守卫何时调用?—— 1.整个应用初始化的时候调用(一上来)2.每次路由跳转的时候
    router.beforeEach((to,from,next)=>{
    	// 判断是否为尊贵的VIP
    	if(isVIP){
    		console.log('你是尊贵的VIP,你随便去哪,我直接放行')
    		next()
    	}else{
    		console.log('你不是尊贵的VIP,我需要进一步看看,你要去哪?')
    		if(authPath.includes(to.path)){
    			console.log('想得美,还看VIP专属路由,放行到/home')
    			next('/home')
    		}else{
    			console.log('还好,你看的是那些随便看的,直接放行')
    			next()
    		}
    	}
    })
    
    export default router
    

24. dispatch 的返回值★

  1. this.$store.dispatch('xxx')的返回值是一个Promise实例。

  2. 返回的这个Promise实例,成功还是失败,要看xxx函数的返回值,规则同then方法。

  3. 可以去Test组件中做一个测试

  • src\store\index.js

    import Vue from 'vue'
    import Vuex,{Store} from 'vuex'
    import user from './modules/user'
    import home from './modules/home'
    
    Vue.use(Vuex)
    
    // actions中的方法用于响应组件中的动作 —— 服务员
    const actions = {
      jia({state,commit},value){
        if(state.a < 10){
          commit('JIA',value)
          // return ''
        }else{
          // alert('不能再加了,因为不能超过10')
          return Promise.reject('不能大于10')
        }
      }
    }
    
    // mutations中的方法用于修改状态 —— 厨师
    const mutations = {
      JIA(state,value){
        state.a += value
      }
    }
    
    // state用于指定初始的数据 —— 原材料
    const state = {
      a:1
    }
    
    export default new Store({
      actions,
      mutations,
      state,
      modules:{
        user,
        home
      }
    })
    
  • src\pages\Test\index.vue

    async test() {
      //第一种写法
      /* const x = this.$store.dispatch("jia");
      x.then(
        () => {console.log("加成了")},
        () => {console.log("没加成")}
      ) */
    
      //第二种写法
      try {
        let x = await this.$store.dispatch("jia", 1);
        console.log(x);
      } catch (error) {
        console.log(error);
      }
    },
    

25. 维护用户信息 _思路 ★

26. 维护用户信息 _实现

  1. 去:src\store\user.js里,找到:getUserInfo,让getUserInfo可以反馈自己的工作成果,同时修改一下获取信息失败的提示,换成一个比较好的提示 —— 【身份过期,请重新登录!】
const actions = {
	// 根据token获取用户信息(token已经在请求头中携带了)
	async getUserInfo({commit}){
		/**********/
		if(code === 200){
      		//如果用户信息获取成功,则不写return,相当于return了undefined,
      		//那么dispatch('getUserInfo')得到的值就是一个成功的Promise实例
			/**********/
		}else{
			/**********/
      		//如果用户信息获取失败,则return一个失败的Promise实例 
      		//那么dispatch('getUserInfo')得到的值就是一个失败的Promise实例
            Message.warning('身份过期,请重新登录!')
			return Promise.reject() 
		}
	}
}
  1. 守卫中的逻辑:
router.beforeEach(async(to,from,next)=>{
  // 从localStorage中读取token
  const token = readToken()
  // 判断token是否存在
  if(token){
    console.log('你登录过,我要继续从vuex中读取用户信息')
    // 从vuex中读取用户信息
    const {info} = store.state.user
    // 判断用户信息是否为空
    if(info.id){
      console.log('要token有token,要信息有信息,直接放行')
      // 放行
      next()
    }else{
      console.log('虽然有token,但没信息,拿着token去要信息')
      try {
        // 获取用户信息
        await store.dispatch('user/getUserInfo')
        console.log('拿着你的token获取到了用户信息,漂亮!放行!')
        next()
      } catch (error) {
        console.log('拿着你的token没有获取到用户信息,你那个破token失效了,我帮你删了,放行到登录')
        // 删除已经不靠谱的token
        // deleteToken()
        next('/login')
      }
    }
  }else{
    console.log('你没有登录,所以有些敏感路由,你不能去了,但现在没有啥路由是敏感的,所以暂时饶你一命,想去哪就去哪')
    next()
  }
})
  1. 备注:逻辑写完了如何去测试?
  • localStorage中删除token,不登录,随便点一点,所有路由都能看才正常。
  • 去登录,然后到各种地方去刷新,看看vuex里有没有用户信息,一直有才正常。
  • localStorage中,故意把token改错,刷新,打回登录才正常。

27、支付路由_弹窗 ★

使用element-ui实现付款弹窗

<a class="btn" @click="pay">立即支付</a>

<script>
  pay(){
      // 弹窗中的html内容
      const htmlStr = '<img style="width:200px" src="http://49.232.112.44/images/hot.jpg">'
      // 弹窗的具体配置
      const options = {
        dangerouslyUseHTMLString: true, //用于支持html字符串的解析
        center:true,
        showCancelButton:true,
        cancelButtonText:'支付遇到问题',
        confirmButtonText	:'已完成支付',
        showClose:false,
        title:'微信扫码支付'
      }
      // 弹窗弹起来!
      this.$alert(htmlStr,{
        // 弹窗的配置
        ...options,
        // 点击弹窗中的确定或关闭按钮的回调
        callback(type){
          if(type === 'confirm'){
            console.log('你点了确定按钮')
          }else{
            console.log('你点了关闭按钮')
          }
        }
      });
    }
</script>

28. qrcode 的使用★

<template>
  <div>
    <button @click="test">点我测试一下qrcode</button>
    <br>
    <img :src="url" alt="">
  </div>
</template>

<script>
  import QRCode from 'qrcode'

  export default {
    name:'Test',
    data() {
      return {
        url:'http://49.232.112.44/images/hot.jpg'
      }
    },
    methods: {
      test(){
        // 将“泰裤辣”转为二维码
        const x = QRCode.toDataURL('泰裤辣,草莓,蓝莓、蔓越莓、今天你想我了没')
        x.then(
          value => {
            console.log('转换二维码成功',value)
            this.url = value
          },
          reason => {
            console.log('转换二维码失败',reason)
          }
        )
      }
    },
  }
</script>

<style lang="less" scoped>
  
</style>

29. 支付路由_生成二维码

  • 安装qrcode:npm i qrcode

  • 代码如下:

    <script>
      import {reqPayInfo} from "@/api";
      import QRCode from "qrcode";
    
      export default {
        methods: {
            async pay(){
              try {
                // 调用QRCode将codeUrl转为二维码
                const url = await QRCode.toDataURL(this.payInfo.codeUrl)
                // 弹窗中的html内容
                const htmlStr = `<img style="width:200px" src="${url}">`
                // 弹窗的具体配置
                const options = {
                  dangerouslyUseHTMLString: true, //用于支持html字符串的解析
                  center:true, //居中布局
                  showCancelButton:true, //显示取消按钮
                  cancelButtonText:'支付遇到问题',
                  confirmButtonText	:'已完成支付',
                  showClose:false, //隐藏右上角的x
                  title:'微信扫码支付'
                }
                // 弹窗弹起来!
                this.$alert(htmlStr,{
                  // 弹窗的配置
                  ...options,
                  // 点击弹窗中的确定或关闭按钮的回调
                  callback(type){
                    if(type === 'confirm'){
                      console.log('你点了确定按钮')
                    }else{
                      console.log('你点了关闭按钮')
                    }
                  }
                });
              } catch (error) {
                // 若转换二维码失败,提示一下
                this.$message.warning('支付二维码显示失败,请联系客服!')
              }
            }
        }
    };
    </script>
    

30、图片懒加载★

  1. 安装vue-lazyloadnpm i vue-lazyload@1

    注意:vue-lazyload这包,默认版本是3的内测版,所以请安装时,加上版本限制

  2. 提前准备好一个过渡图片:/assets/images/loading.gif

  3. main.js

    import VueLazyload from 'vue-lazyload'
    import picture from '@/assets/images/loading.gif'
    //....
    Vue.use(VueLazyload,{
    	loading:picture
    })
    
  4. 使用图片的地方改为

    <img v-lazy="goods.defaultImg" />
    

31. 路由懒加载 ★

  1. 什么是懒加载:其实就是延迟加载,即当需要用到的时候再去加载。

  2. Vue中路由的懒加载:

    //原来的引入方式
    /* 
        import Home from '@/pages/Home'
        import Login from '@/pages/Login'
        import Register from '@/pages/Register'
        import Search from '@/pages/Search'
        import Detail from '@/pages/Detail'
        import AddCartSuccess from '@/pages/AddCartSuccess'
        import Cart from '@/pages/Cart'
        import Trade from '@/pages/Trade'
        import Pay from '@/pages/Pay'
        import PaySuccess from '@/pages/PaySuccess' 
    */
    
    //懒加载的引入方式
    export default [
      {
        path:'/home',
        component:() => import('@/pages/Home'),
      },
      {
        path:'/login',
        component:() => import('@/pages/Login'),
        meta:{isHideFooter:true}
      },
      {
        path:'/register',
        component:() => import('@/pages/Register'),
        meta:{isHideFooter:true}
      },
      {
        path:'/search',
        component:() => import('@/pages/Search'),
      },
      {
        path:'/test',
        component:() => import('@/pages/Test'),
      },
      {
        name:'detail',
        path:'/detail/:id',
        component:() => import('@/pages/Detail'),
      },
      {
        path:'/addcart_success',
        component:() => import('@/pages/AddCartSuccess'),
      },
      {
        path:'/shopcart',
        component:() => import('@/pages/ShopCart'),
      },
      {
        path:'/trade',
        component:() => import('@/pages/Trade'),
      },
      {
        path:'/pay',
        component:() => import('@/pages/Pay'),
      },
      {
        path:'/paysuccess',
        component:() => import('@/pages/PaySuccess'),
      },
      {
        path:'/',
        redirect:'/home',
      }
    ]
    

32、vee-validate_基本使用★

  • 第一步:安装 npm i vee-validate@3

    Vue2中,我们要使用vee-validate2版本、或3版本,vee-validate4版本是给Vue3用的

  • 第二步:在main.js中引入:ValidationProviderextend,并注册ValidationProvider

    import {ValidationProvider,extend} from 'vee-validate';
    
    Vue.component('ValidationProvider',ValidationProvider)
    
  • 第三步:使用ValidationProvider包裹表单元素,并指定具体规则。

    <ValidationProvider rules="shouji" v-slot="{errors}">
      <input type="text" placeholder="请输入你的手机号" v-model="phone">
      <span class="error-msg">{{errors[0]}}</span>
    </ValidationProvider>
    
  • 第四步:在main.js中,创建一个手机的验1证规则

    const phoneReg = /^(0|86|17951)?(13[0-9]|15[012356789]|166|17[3678]|18[0-9]|14[57])[0-9]{8}$/
    
    extend('shouji',{
      validate:(value)=>phoneReg.test(value),
      message:'手机号格式不合法'
    })
    

33. vee-validate_进阶使用★

分析:很多的输入项需要多种规则,例如手机号的规则是:必填项、格式要合法。

  • 第一步:在main.js中,引入内置的验必填项规则

    import {required} from 'vee-validate/dist/rules'
    //extend('required',required)
    
  • 第二步:在内置规则基础上,自定义一下必填项规则

    extend('bixu',{
      ...required,
      message:'{_field_}必须填写',
    })
    
  • 第三步:组件中使用规则

    <ValidationProvider name="手机" rules="shouji|bixu" v-slot="{errors}">
        <input type="text" placeholder="请输入你的手机号" v-model="phone">
        <span class="error-msg">{{errors[0]}}</span>
    </ValidationProvider>
    

随后完成:验证码、密码、重复密码、用户协议的校验,且要注意重复密码需要传参

  • 补充其他规则:
const codeReg = /^\d{6}$/
const passwordReg = /^\w{6,21}$/

extend('code',{
  validate:(value)=> codeReg.test(value),
  message:'验证码必须为6位数字',
})

extend('pwd',{
  validate:(value)=> passwordReg.test(value),
  message:'密码必须为:数字、字母、下划线',
})

extend('agree',{
  validate: value => value,
  message:'请同意协议',
})

extend('repwd',{
  validate:(value,[params])=> {
    console.log(params)
    return value === params
  },
  message:'确认密码必须与密码一致'
})
  • 组件中使用
<ValidationProvider name="验证码" rules="code|bixu" v-slot="{errors}">
    <input type="text" placeholder="请输入验证码" v-model="code">
    <button class="getcode" @click="getCode">获取验证码</button>
    <span class="error-msg">{{errors[0]}}</span>
</ValidationProvider>

<ValidationProvider name="密码" rules="pwd|bixu" v-slot="{errors}">  
    <input type="password" placeholder="请输入你的登录密码" v-model="password">
    <span class="error-msg">{{errors[0]}}</span>
</ValidationProvider>

<ValidationProvider name="确认密码" :rules="`repwd:${password}|bixu`" v-slot="{errors}">  
    <input type="password" placeholder="请输入确认密码" v-model="rePassword">
    <span class="error-msg">{{errors[0]}}</span>
</ValidationProvider>

<ValidationProvider name="用户协议" rules="agree" v-slot="{errors}"> 
    <input name="m1" type="checkbox" v-model="agree">
    <span>同意协议并注册《尚品汇用户协议》</span>
    <span class="error-msg">{{errors[0]}}</span>
</ValidationProvider>

34. 统一校验表单★

  • 操作Register组件

    <ValidationObserver ref="register">
      <!-- 所有输入项 -->
    </ValidationObserver>
    
    <script>
        async register(){
          const result = await this.$refs.registerForm.validate()
          if(result){
            // 获取用户输入的东西
            const {phone,password,code} = this
            // 发请求去注册
            const {code:c1,message,data} = await reqRegister({phone,password,code})
            if(c1 === 200){
              this.$message.success(`注册成功`)
              this.$router.push('/login')
            }else{
              this.$message.warning(`注册失败:${message}`)
            }
          }else{
            this.$message.warning('请检查输入项的合法性')
          }
        }
    </script>
    

34. 完善路由鉴权★

1. 基本鉴权

  1. 明确:有些路由只有登录了,才能看。—— 使用全局守卫去做。

  2. 他们分别是:/trade/pay/paysuccess/order

  3. 在全局导航守卫中追加几个规则,具体编码如下:

    // 以下路由必须登录后才能看
    const authPath = ['/trade','/pay','/paysuccess','/order']
    

2. 细化鉴权

  1. 明确:只有在特定路由,才能去特定路由。

  2. 使用【路由独享守卫】细化鉴权规则,配置写在路由规则中(src/router/routes.js)。

    	//只有从【详情】页面才能跳到【添加购物车成功】页面
    	{
    		path:'/addcart_success',
    		component:AddCartSuccess,
    		beforeEnter: (to, from, next) => {
    			if(from.path.slice(0,7) === '/detail') next()
    			else next('/home')
    		}
    	},
        
      	//只有从【交易】页面才能跳到【支付】页面
    	{
    		path:'/pay',
    		component:Pay,
    		beforeEnter: (to, from, next) => {
    			if(from.path === '/trade') next()
    			else next('/home')
    		}
    	},
    
  3. 使用【组件内守卫】细化鉴权规则,配置写在组件中(.vue文件)

    export default {
    	name: 'PaySuccess',
        //只有从【支付】页面,才能去【支付成功】页面
    	beforeRouteEnter(to, from, next) {
    		if(from.path === '/pay'){
    			next()
    		}else{
    			next('/home')
    		}
    	}
    }
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/883358.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

印度货代专线【我国到印度专线有哪些方式】

随着全球贸易的不断发展&#xff0c;我国与印度之间的贸易往来也日益频繁。作为两个人口最多的国家之一&#xff0c;中国和印度之间的货物运输需求不断增长。为了满足这一需求&#xff0c;印度货代专线应运而生&#xff0c;为进出口商提供高效、可靠的货物运输服务。本文将探索…

零零信安:暗网分析报告——Part 4 商业黑客组织,“流星街”的原住

暗网&#xff0c;作为互联网的一部分&#xff0c;充满了神秘而又复杂的活动。更重要的是&#xff0c;其背后的主要参与者——商业黑客和各种有组织的犯罪集团&#xff0c;揭示了这是一个怎样的世界。本报告将试图带您了解这些原住民的身份、行为方式、商业逻辑、受害者以及他们…

JMeter接口自动化测试实例—JMeter引用javaScript

Jmeter提供了JSR223 PreProcessor前置处理器&#xff0c;通过该工具融合了Java 8 Nashorn 脚本引擎&#xff0c;可以执行js脚本以便对脚本进行前置处理。其中比较典型的应用就是通过执行js脚本对前端数据进行rsa加密&#xff0c;如登录密码加密。但在这里我就简单的应用javaScr…

No view found for id 0x7f0901c3 for fragment解决以及线上bug排查技巧

情景再现 开发这么久&#xff0c;不知道你们是否也经历过这样的情况&#xff0c;测试或者用户&#xff0c;反馈app闪退&#xff0c;结果你自己打开开发工具&#xff0c;去调试&#xff0c;一切正常&#xff0c;然后闪退还是存在&#xff0c;只是在开发环境中不能重现。这种情况…

11 - git stash 开发中临时加塞了紧急任务怎么处理

查看所有文章链接&#xff1a;&#xff08;更新中&#xff09;GIT常用场景- 目录 文章目录 开发中临时加塞了紧急任务怎么处理 开发中临时加塞了紧急任务怎么处理 当你此时工作区已经修改了 Readme 文件&#xff0c;然后突然需要解决其他问题&#xff08;紧急问题、新任务&…

ML-fairness-gym入门教学

1、ML-fairness-gym简介 ML-fairness-gym是一个探索机器学习系统长期影响的工具。可以用于评估机器学习系统的公平性和评估静态数据集上针对各种输入的误差度量的差异。开源网站&#xff1a;GitHub - google/ml-fairness-gym 2、安装ML-fairness-gym&#xff08;Windows&…

【贪心】CF1841 D

Codeforces 题意&#xff1a; 思路&#xff1a; 首先模拟一下样例 并没有发现什么 那么就去考虑特殊情况&#xff0c;看看有没有什么启发 考虑一个大区间包含所有小区间的情形&#xff0c;这种情况就是在这么多区间中找出两个区间 换句话说&#xff0c;这么多区间组成一个…

容器虚拟化基础之cgroups/LXC

"你真的&#xff0c;自由了~" 容器虚拟化基础之Cgroups: (1) 什么是cgroups cgroups是 linux 内核提供的一种机制&#xff0c; 这种机制可以根据需求把一系列系统任务及其子任务整合(或分隔)到按资源划分等级的不同组内&#xff0c;从而为系统资源管理提供一个统一…

嵌入式电火花线切割控制系统总体设计

2.1 电火花线切割机床的特点与结构 电火花线切割加工&#xff08; Wire Cut EDM &#xff09;是特种加工中电火花加工方式的一种&#xff0c;是 直接利用电能或热能进行加工的工艺方法。加工基本原理是利用在导丝架固定的轨 道上连续移动电极丝&#xff08;钼丝 / 铜丝&…

Spring框架【IOC详解】

目录 一、前言 1.1.Spring简介 1.2.使用Spring的优点 1.3.Spring组成 二、Spring之IOC详解 2.1.IOC理论推导 2.1.1.IOC分析实现 2.1.2.IOC本质 2.2.Maven项目导入Jar包 2.3.依赖注入 2.3.1.Set注入&#xff08;重点&#xff09; 2.3.2.构造注入 无参构造创建对象 …

STM32F4X-GPIO输入功能使用

STM32F4 GPIO输入模式配置 上一节讲GPIO的时候说到了将GPIO设置成输出模式&#xff0c;并通过将GPIO的电平拉高拉低控制LED灯的例程。GPIO除了用作输出功能之外&#xff0c;还可以用作输入功能。最常用的就是检测按键的输入电平。 硬件设计 本章的硬件是基于正点原子的探索者…

UI设计师个人工作总结范文精选

UI设计师个人工作总结范文(一) 在忙忙碌碌中&#xff0c;2019年又将过去了&#xff0c;在这一年当中&#xff0c;设计部无论是在运作模式、设计产值、还是人员结构&#xff0c;各方面的变化都比较大。 设计部的运作模式是从7月底开始进行调整的&#xff0c;以独立承包制的运营方…

Linux / Ubuntu磁盘扩容

测试时遇到了shell脚本执行错误的问题&#xff0c;找到脚本编写的楼哥&#xff0c;才发现自己给虚拟机的磁盘已经满了&#xff0c;没想到啊&#xff0c;业务的解压操作&#xff0c;这么费磁盘&#xff0c;那就需要进行磁盘的扩展&#xff0c;记录一下 1、首先停掉虚拟机&#…

只需四步,让Vscode连接远程服务器中的docker容器进行开发

0. 前提条件 本地windows或其他环境中安装了Vscode&#xff0c;Vscode中安装了Remote-SSH拓展&#xff08;用于利用SSH连接docker容器&#xff09;远程服务器中安装了docker&#xff0c;并且拉取了自己需要的镜像&#xff08;image&#xff09;有root权限&#xff0c;能使用su…

【环境配置】Windows10终端和VSCode下能够直接打开Anaconda-Prompt

很多小伙伴在 Windows 下做深度学习开发的时候&#xff0c;遇到终端没有在 Linux 那么方便&#xff0c;那么我们现在就可以来设置一下&#xff1b;这样我们也可以在文件夹内部右键打开终端&#xff0c;也可以在 VS Code 里面新建一个虚拟环境的控制台&#xff1b;这里主要是针对…

HCIP-OpenStack组件介绍

OpenStack排错思路&#xff1a; OpenStack查询日志&#xff0c;所有日志都在/var/log/模块名称下面。 OpenStack修改配置&#xff0c;所有配置文件都在/etc/模块名称下面。 openstack把这些组件服务都集成到httpd服务中了&#xff0c;目的是为了提升性能。登入不了openstack在…

Spark MLlib机器学习库(一)决策树和随机森林案例详解

Spark MLlib机器学习库(一)决策树和随机森林案例详解 1 决策树预测森林植被 1.1 Covtype数据集 数据集的下载地址&#xff1a; https://www.kaggle.com/datasets/uciml/forest-cover-type-dataset 该数据集记录了美国科罗拉多州不同地块的森林植被类型&#xff0c;每个样本…

Git 如何使用TortoiseGit 操作本地仓库

初始化仓库 方法一: 新建一个文件夹,进入文件夹内部操作 1、右键--> 在这里创建Git 版本库 注意: 不要直接在桌面上操作,否则桌面就是一个仓库 方法二: 1、右键-->Git GUI here 方法三: 命令行模式 1、 git init 创建完毕仓库,我们发现,此时我们创建的文件夹下…