Vue3笔记

一.前端工程化
   webpack   
      代码压缩、解决js 的浏览器兼容性问题、性能优化

      webpack.config.js文件下:
        mode 节点:
          1.development
            开发时用,不代码压缩和性能优化,打包速度块
          2.production
            发布时用,代码压缩和性能优化,打包速度慢

      webpack.config.js文件是webpack 的配置文件,打包前先读取这个配置文件,从而基于给定的配置,对项目打包
      
      默认的打包入口文件 src -> index.js
        通过entry 节点指定打包的入口文件
      默认的输出文件路径 dist -> main.js
        通过uotput 节点指定打包的出口文件

      npm run dev 进行打包

      插件
        1.webpack-dev-server
          每当修改了代码,webpack自动进行项目的打包和构建
          打包生成的文件不再放到dist 文件夹下,而是存放到了内存中,默认是放到了项目的根目录中
        2.html-webpack-plugin
          自定制index.html 页面的内容,并赋值一份index.html放到项目根目录下
          </body>之前自动注入了打包的bundle.js            

      节点:
        mode 节点:
          1.development
            开发时用,不代码压缩和性能优化,打包速度块
          2.production
            发布时用,代码压缩和性能优化,打包速度慢
        entry      指定打包的入口文件
        output      指定打包的出口文件
  ***  plugin     挂载插件的实例对象
        devServer
          自动打开浏览器,指定主机地址,指定端口号
        module
          配置第三方文件模块的规则
   ***    loader
            打包非.js后缀名结尾的模块
            style-loader
            css-loader         .css
            less-loader       .less
            babel-loader   打包webpack无法处理的高级JS语法
            url-loader file-loader   url路径相关的文件
        devtool
          保证运行时报错的行数与源代码的行数保持一致  

        为什么要打包发布:
          开发环境下,打包生成的文件存放于内存中,无法获取最终打包生成的文件
          开发环境下,打包生成的文件不会进行代码压缩和性能优化   

  ***   Source Map 文件
          文件中存储着代码压缩混淆前后的对应关系,可以定位到错误行并显示对应的源码
          可以配置devtool 保证运行时报错的行数与源代码的行数保持一致                   
        
二.Vue3
    1.过滤器:(vue 2.x 中使用  v3没有)
      常用于文本的格式化
      在filters 节点下定义过滤器函数

      格式:
        待处理的数据 | 过滤器函数
      用在两个地方:
        插值表达式 和 v-bind 属性绑定

      全局过滤器:
        调用vue 构造函数所提供的filter 方法
        Vue.filter('过滤器函数',(str)=>{
          return str.charAt(0).toUpperCase() + str.slice(1)
        })
      
      还可以连续调用多个过滤器:
        {{ message | 过滤器1 | 过滤器2 }}
    
    2.MVVM
      M  数据源data
      V  视图  el 所指向的区域
      vm 是new 出来的VUE实例对象
      
    3.组件
      1.单页面应用程序(SPA):
        缺点:
          1.首屏加载慢
            解决方法:路由懒加载、代码压缩、CDN加速、网络传输压缩
          2.不利于SEO
            解决方法:SSR服务器端渲染  
      
      2.创建vue 的SPA项目
        1.vite方法
          vue3.x,不基于webpack,运行速度块,小而巧
        2.vue-cli
          vue3.x 和 2.x,基于webpack,运行速度慢,大而全

      3.vite的使用
        npm init vite-app 项目名称
        cd 项目名称
        npm install
        npm run dev

      vue的作用:通过main.js 把App.vue 根组件的内容渲染到index.html 中      

      4.组件的基本使用
        1.全局注册组件
          1.在main.js先导入全局注册的组件
            import Swiper from './components/globalRge/Swiper.vue'
            调用app.component() 方法来全局注册组件
            app.component('my-swiper',Swiper)
          2.使用标签的形式使用全局组件
            <my-swiper></my-swiper>
        2.局部注册组件
          在组件内部导入需要注册的组件
            1.import Search from './components/globalRge/Search.vue'
            2.components:{
                'my-search':Search
              }
            3.<my-search></my-search>
        尽量使用大驼峰命名法来命名组件:MyTest

        3.scoped  和  :deep()  v3中    /deep/ v2中(父组件可以影响子组件的样式)  [属性选择器] .title

        4.props 是组件的自定义属性
          props的作用:父组件通过props 向子组件传递要展示的数据
          好处:提高了组件的复用性
          
        5.父组件向子组件传值:(props 自定义属性)
          在子组件中:
            {{title}}  {{author}}
            props: ['title','author']
          在父组件中:
            <my-article :title="info.title" :author="'post by' + info.author"></my-article>
            import MyArticle from './Article.vue'
            components:{
              MyArticle
            },
            data(){
              return {
                info{
                  title: 'a',
                  author: 'b'
                }
              }
            }
              

          1.动态的添加和移除class 类名
            <h3 class="this" :class="classObj">MyStyle 组件</h3>
            <button @click="classObj.italic=!classObj.italic">Toggle Italic</button>
            <button @click="classObj.delete=!classObj.delete">Toggle Delete</button>
            data(){
              return {
                classObj:{
                  italic:false,
                  delete:flase
                }
              }
            }
          2.以对象语法绑定内联的style
            <div :style="{color:active,fontSize:fsize+'px','background-color':bgcolor}"></div>
            data(){
              return{
                active:'red',
                fsize:30,
                bgcolor:'pink'
              }
            }

    组件总结:
      1.什么是单页面应用程序以及组件化开发
        SPA、只有一个页面、组件是对UI结构的复用
      2..vue单文件组件的组成部分
        template、script、style(scoped、lang)
      3.如何注册vue 组件
        全局注册(app.component)局部注册(components)
      4.如何声明组件的props 属性
        props 数组
      5.如何在组件中进行样式绑定
        动态绑定class、动态绑定style
      

    组件基础下:
      1.props 验证
        使用对象类型的props 节点,可以对每个prop 进行数据类型的校验
        props:['count','state']
        对比:
          props:{
            count:Number,
            state:[Number,String]     // 可以指定多个类型
            name:{
              type:String,
              default: 100      // 用户没有指定值,则默认为100
              required:true     // 当前属性的值是必填项
            }
          }

        自定义验证函数:
          props:{
            name:{
              validator(value){     // “属性的值”通过形参value 进行接收
                return ['success','warning','danger'].indexOf(value)!==-1
                // 属性的值匹配这些字符串中的一个,true 表示验证通过
              }
            }
          }

      2.计算属性
        计算属性就是一个function 函数,可以实时监听data 中数据的变化,并return一个计算后的新值,供组件渲染DOM时使用
        
        <input type="text" v-model.number="count">
        <p>{{count}} 乘以 2 的值为: {{plus}}</p>
        data(){
          return {count:1}
        },
        computed:{
          plus(){   // 计算属性:监听data 中count 值的变化,自动计算出count*2 之后的新值
            return this.count *2    
          }
        }
        注意:
          1.计算属性必须定义在computed 节点中
          2.计算属性必须是一个function 函数
          3.计算属性必须有返回值
          4.计算属性必须当做普通属性使用 如:{{plus}}

        相对于方法来说,计算属性会缓存计算的结果,只有计算属性的依赖项发生变化时,才会重新进行计算
        因此计算属性的性能更好


        <button :disabled="isDisabled">结算按钮</button>
        computed:{
          isDisabled(){
            return this.total === 0   // 0 代表禁用
          }
        }   
      

      3.自定义事件(子组件向父组件传值)
        1.
          myheader 子组件中:
            <button @click="onBtnClick"> +1 </button>  
            export default{
              emits:['change'],       // 1.声明自定义事件
              methods:{
                onBtnClick(){
                  this.$emit('change')   // 2.触发自定义事件
                }
              }
            }
          App.vue 父组件中:
            <my-header @change="getCount"></my-header>   // 3.监听自定义事件
            methods:{
              getCount(){
                console.log('监听到了 count 值的变化')
              }
            }
        

        2.
          <button @click="add"> +1 </button>   // 触发自定义事件
          export default{
            name:'MyCounter',
            emits:['countChange'],
            data(){
              return{
                count:0
              }
            },
            methods{
              add(){
                this.count++
                this.$emit('countChange')
              }
            }
          }
          父组件中:
            <my-counter @countChange="getCount"></my-counter>
            methods:{
              getCount(){
                console.log('触发了countChange 自定义事件')
              }
            }
        3.如何传参
          <button @click="add"> +1 </button>   // 触发自定义事件
          export default{
            name:'MyCounter',
            emits:['countChange'],
            data(){
              return{
                count:0
              }
            },
            methods{
              add(){
                this.count++
                this.$emit('countChange',this.count)   // 通过第二个参数传值
              }
            }
          }
          父组件中:
            <my-counter @countChange="getCount"></my-counter>
            methods:{
              getCount(val){
                console.log('触发了countChange 自定义事件',val)    // 通过形参接收
              }
            }
      
      
      4.在组件上使用v-model 实现父子组件的双向数据绑定
        父向子同步数据:
            父组件中:
              <my-counter :number="count"></my-counter>
            子组件中:
              props:['number']
        
        子向父同步数据:
            父组件中:
              <my-counter v-model:number="count"></my-counter>
            子组件中:
              <button @click="add">+1</button>
              emits:['update:number']
              methods:{
                add(){
                  this.$emit('update:number',this.number+1)
                }
              }

1.watch 侦听器
    1.
      watch 侦听器允许开发者监视数据的变化,从而针对数据的变化做特定的操作
        export default{
          data(){
            return {username:''}
          },
          watch:{
            username(newVal,oldVal){
              console.log(newVal,oldVal)
            }
          }
        }
    
      await 和 async 简化异步操作,从而得到真正的数据对象
    
    2.
      监听username 值的变化,并使用axios 发起Ajax 请求,检测当前输入的用户名是否可用:
        import axios from 'axios'
        export default{
          data(){
            return { username: ''}
          },
          watch:{
            async username(newVal,oldVal){           // username 是一个方法
              const {data:res} = await axios.get('https://www.escook.cm/api/finduser/'+newVal)
              console.log(res)
            }
          }
        }

    3.immediate选项
      默认情况下,组件在初次加载完毕后不会调用watch 侦听器,如果想让watch 侦听器立即被调用,则需要使用immediate选项
        watch:{
          // 1.监听username 值的变化
          username:{               // username 指向一个对象
            // 2.handler 属性是固定写法,当username 变化时,调用handler
            async handler(newVal,oldVal){
              const {data:res} = await axios.get('https://escook.cn/api/finduser/${newVal}')
              console.log(res)
            },
            // 3.表示组件加载完毕后立即调用一次当前的watch 侦听器
            immediate: true      
          }
        }
    
    4.deep 选项
      当watch 侦听的是一个对象,如果对象中的属性发生了变化,则无法被监听到,此时需要使用deep 选项
        data(){
          return{
            info:{
              username:'zs',
              age: 20
            }
          }
        },
        watch:{
          info:{
            async handler(newVal,oldVal){
              const{ data: res } = await axios.get('https://www.escook.cn/api/finduser/'+newVal.username)
              console.log(res)
            },
            deep:true    
            // 监听info 对象中每个属性的变化,只要有属性值发生了变化,就执行watch
          }
        }
    
    5.监听对象中单个属性的变化
      data(){
        return{
          info: {username:'admin',password:''}
        }
      },
      watch:{
        'info.username':{    // 指向监听info.username 属性值的变化
          async handler(newVal,oldVal){
            const{ data:res } = await axios.get('http://www.escook.cn/api/finduser/'+newVal.username)
            console.log(res)
          }
        }
      }

    计算属性和侦听器的区别:
      计算属性侧重于监听多个值的变化,最终计算并放回一个新值
      侦听器侧重于监听单个数据的变化,最终执行特定的业务处理,不需要有任何返回值


2.组件的生命周期

  组件的运行过程:
    开始 - import导入组件 - components注册组件 - 以标签形式使用组件 - 在内存中创建组件的实例对象 - 把创建的组件实例渲染到页面上 - 组件切换时销毁需要被隐藏的组件 - 结束
  组件的生命周期:指的是组件的 创建 - 运行 - 销毁 的整个过程,强调的是一个时间段
 

  组件的生命周期函数:
    beforeCreate     // 在内存中开始创建组件之前, 创建阶段, 唯一一次
    created()      // 组件在内存中被创建完毕了
                    // 创建阶段   唯一一次   最早的发ajax 请求初始数据

    beforeMount     // 在组件初次渲染到页面之前,创建阶段,唯一一次
    mounted()       // 组件第一次被成功渲染到了页面上
                    // 创建阶段   唯一一次   最早的操作DOM 元素

    beforeUpdate      // 在组件被重新渲染之前 运行阶段,0或多次
    updated()        // 组件被重新渲染完毕了   
                    // 当组件的data 数据更新之后,vue 会自动重新渲染组件的DOM 结构,从而保证View 视图展示的数据和Model 数据源保持一致
                    // 当组件被重新渲染完毕之后,会自动调用updated 生命周期函数
                    // 运行阶段  0或多次    

    beforeUnmount    // 在组件被销毁之前 销毁阶段,唯一一次
    unmounted()     // 组件被销毁完毕了
                    // 销毁阶段  唯一一次

  为什么不在beforeCreate 中发起ajax 请求初始数据?
    在拿到ajax 返回回来的数据之后,需要存到data 里面去,才能够在组件渲染的时候访问到data里面的数据
    但是在beforeCreate 中是无法访问到data 里面的数据的,即使请求了数据,但是请求的数据无法挂载到data中,共组件使用
    因此最早只能在created 里面发起ajax 请求

  能否在beforeMount 里面操作DOM 元素?
    不可以,因为在mounted 执行的时候,才第一次把当前组件渲染到页面中
    


3.组件之间的数据共享
    父子关系,兄弟关系,后代关系
    
    1.父子组件之间的数据共享
      1.父组件向子组件共享数据  属性绑定
        父组件通过v-bind 属性绑定向子组件共享数据,同时,子组件需要通过props 接收数据
          父组件中:
            <mt-test :msg="message" :user="userinfo"></mt-test>
            data(){
              return {
                message: 'hello vue',
                userinfo:{name:'zs',age:20}
              }
            }
          子组件中:  
            <p>{{msg}}</p>
            <p>{{user}}</p>
            export default{
              props:['msg','user']
            }
      2.子组件向父子间共享数据
        子组件通过自定义事件的方式向父组件共享数据
          子组件中:
            export default{
              emits:['n1change'],  // 1.声明自定义事件
              data(){
                return {n1:0}
              },
              methods:{
                addN1(){
                  this.n1++,
                  // 2.数据变化,并触发自定义事件
                  this.$emit('n1change',this.n1)
                }
              }
            }
          父组件中:  
            // 1.监听子组件的自定义事件 n1change
            <me-test @n1change="getn1"></me-test>
            export default{
              data(){return { n1FromSon:0 }},
              methods:{
                getn1(n1){  // 2.通过形参,接收子组件传递过来的数据
                  this.n1FromSon = n1
                }
              }
            }
      3.父子组件之间的数据双向同步
        父组件中:  
          <my-son v-model:num="count"></my-son>
          data(){
            return {
              count: 0
            }
          }
        子组件中:  
          <button @click="add">+1</button>
          props:['num'],
          emits:['update:num'],
          methods:{
            add(){
              this.$emit('update:num',this.num+1)
            }
          }
   
    2.兄弟组件之间的数据共享
      兄弟组件之间实现数据共享的方案是EventBus,可以借助第三方的包mitt 来创建eventBus 对象
        npm i mitt@2.1.0 -S
        
        //创建一个eventBus.js
          // 导入mitt 包
          import mitt from 'mitt'
          // 创建EventBus 的实例对象
          const bus = mitt()
          // 将EventBus 的实例对象共享出去
          export default bus
        
        数据接收方:
          import bus from './eventBus.js'
          created(){
            bus.on('countChange',(count) => {
              this.num = count
            })
          }
        数据发送方:
          <button @click="add">+1</button>
          import bus from './eventBus.js'
          data(){
            return {
              count: 0
            }
          }
          methods:{
            add(){
              this.count++
              bus.emit('countChange',this.count)
            }
          }
    
    3.后代组件之间的数据共享
      指的是父节点的组件向其子孙组件共享数据
      可以使用provide 和inject 实现后代关系组件之间的数据共享
      
        父节点的组件可以通过provide 方法,对其子孙组件共享数据
          export default{
            data(){
              return { color:red }
            },
            provide(){
              return {
                color: this.color
              }
            }
          }
        子孙节点的组件可以通过inject 数组,接收父级节点向下共享的数据
          <h5>{{ color }}</h5>
          export default{
            // 子孙组件,使用inject 接收父节点向下共享的color 数据,并在页面上使用
            inject:['color']
          }
        
      父节点向外共享响应式的数据:
        结合computed 函数向下共享响应是的数据:
        如果父组件共享的是响应式的数据,则子孙节点必须以.value 的形式进行使用
        
        父组件中:
          import { computed } from 'vue'
          export default{
            data(){
              return { color: 'red' }
            },
            provide(){
              return{
                // 使用computed 函数,可以把要共享的数据包装成 响应式的数据
                color: computed(() => this.color)
              }
            }
          }
        
        子孙组件中:
          <h5>{{ color.value }}</h5>
          export default{
            inject: ['color']
          }
          
    4.vuex
      vuex 是组件之间实现数据共享的方案,可以让组件之间的数据共享变得高效、清晰、且易于维护

4.v3 中全局配置axios
    在main.js 入口文件中,通过app.config.globalProperties 全局挂载axios

    1.安装axios
      npm i axios -S
    2.main.js 中
      import axios from 'axios'
      const app = createApp(App)
      // 挂载请求的根路径
      axios.defaults.baseURL = 'https://www.esbook.cn'
      // 全局挂载自定义属性 $http
      axios.config.globalProperties.$http = axios
      app.mount('#app')
    3.发起post 请求
      methods:{
        async postInfo(){
          const {data:res} = await this.$http.post('/api/post',{name:'zs',age:20})
          console.log(res)
        }
      }
    4.发起get 请求
      methods:{
        async getInfo(){
          cosnt {data:res} = await this.$http.get('/api/get',{
            params:{
              name: 'ls',
              age: 33
            }
          })
          console.log(res)
        }
      }


5.购物车案例
      


1.ref 引用

  ref 是用来辅助开发者在不依赖于jQuery 的情况下,获取DOM 元素或组件的引用
  每个vue 的组件实例上,都包含一个$refs 对象,里面存储着对应的DOM 元素或组件的引用
  默认情况下,组件的$refs 指向一个空对象
    示例:
      <h3 ref="myh3">App 根组件</h3>
      <button @click="getRefs">获取$refs 引用</button>
      methods:{
        getRefs(){
          console.log(this.$refs.myh3)    // this.$refs.myh3 就指向<h3></h3>
        }
      }

    可以通过为子组件设置ref,调用该组件内部的方法
      父组件中:
        <my-counter ref="counterRef"></my-counter>
        <button @click="getRefs">获取$refs 引用</button>
        methods:{
          getRefs(){
            this.$refs.counterRef.reset()
          }
        }
      子组件中:
        methods:{
          reset(){
            this.count = 0
          }
        }


    组件的DOM 元素是异步的进行更新渲染
    而代码是同步执行的,当需要的DOM 元素没有被及时渲染时,ref 获取的是undefined
    
  2.this.$nextTick(cb)方法
    组件的$nextTick(cb) 方法,会把cb 会调推迟到下一个DOM 更新周期之后执行
    等组件的DOM 异步地重新渲染完成后,再执行cb回调函数,从而能保证cb 回调函数可以操作到最新的DOM 元素
    示例:
      <input type="text" v-if="inputVisible" ref="ipt">
      <button v-else @click="showInput">展示input 输入框</button>
      methods:{
        showInput(){
          this.inputVisible = true
          // 把对 input 文本框的操作,推迟到下次DOM 更新之后,否则页面上根本不存在文本框元素
          this.$nextTick(() => {
            this.$refs.ipt.focus()
          })
        }
      }


2.动态组件
  动态组件指的是动态切换组件的显示与隐藏
  vue 中提供了一个内置的 <component> 组件,专门用来实现组件的动态渲染

    1.<component> 是组件的占位符
    2.通过is 属性动态指定要渲染的组件名称
    3.<component is="要渲染的组件的名称"></component>

  示例:App 根组件中有Home 和Movie 两个组件
      <button @click="comName="MyHome>首页</button>
      <button @click="comName="MyMovie>电影</button>
      // <my-home></my-home>
      // <my-movie></my-movie>
      <component :is="comName"></component>
      data(){
        return{
          comName:'MyHome'
        }
      },
      components:{
        MyHome,
        MyMovie
      }
 
  动态组件切换时,默认情况下,不会记录组件中数值的变化
  因为动态组件在切换时,是对组件的重新创建和销毁
    <keep-alive>
      <component :is="comName"></component>
    </keep-alive>
 
      


3.插槽
  插槽(Slot) 是vue 为组件的封装者提供的能力,允许开发者在封装组件时,把不确定的、希望由用户指定的部分定义为插槽
  可以把插槽认为是组件封装期间,为用户预留的内容的占位符
  没有预留插槽的内容会被丢弃
  示例:
    子组件中:
      <p>111</p>
      <slot>默认显示内容</slot>
      <p>333</p>
    父组件中:
      <my-son>
        <p>222</p>
      </my-son>
    
  2.具名插槽
    v-slot:   可以简写成#
    子组件中:
      <slot name="header"></slot>   // name 默认是default
      <slot name="footer"></slot>
    父组件中:
      <my-son>
        <template v-slot:header> 标题</template>
        // <template #header> 标题</template>
        <template v-slot:footer> 尾部</template>
        // <template #footer> 尾部</template>
        <p>默认</p>
      </my-son>
  3.作用域插槽
    在封装组件的过程中,可以为预留的<slot>插槽绑定props 数据,这种带有props 数据的插槽叫做“作用于插槽”
    <slot :info="infomation"></slot>

    <my-test>
      <template #default="{info }">
        {{ info  }}    // scope 是自定义的,是用来接收数据的,是一个大对象,里面包含了插槽传过来的数据
      </template>
    </my-test>

4.自定义指令
  1.私有自定义指令
    在每个vue组件中,可以在directives 节点下声明私有自定义指令
    <input v-focus>
    directives:{
      // 自定义一个私有指令
      focus:{
              // 当被绑定的元素插入到DOM 中时,自动触发mounted 函数
              mounted(el){          // el 表示当前指令所绑定到的DOM 元素
                el.focus()  // 让被绑定的元素自动获得焦点
              }
      }
    }

  mounted()函数,  表示被绑定到的DOM 元素渲染到页面中时,自动触发该函数
 
  2.全局自定义指令
    在main.js 中
      const app = createApp(App)
      app.directives('focus',{
        mounted(el){
          el.focus()
        }
      })
      app.mount('#app')
 
  3.updated 函数
    mounted 函数只在元素第一次插入DOM 时被调用,当DOM 更新时mounted 函数不会被触发
    updated 函数会在每次DOM 更新完成后被调用
    app.directives('focus',{
        mounted(el){      // 第一次插入DOM 时触发这个函数
          el.focus()
        },
        updated(el){     // 每次DOM 更新都会触发 updated 函数
          el.focus()
        }
      })
  注意点:
    1.
      v3 中的mounted  在v2 中是bind
      v3 中的updated  在v2 中是update
    2.当mounted 和updated 函数中的代码一样时可以简写
      app.directives('focus',(el) => {
        // 在mounted 和updated 时都会触发相同的业务处理
        el.focus()
      })
 
  4.指令的参数值
    在绑定指令时,可以通过“等号”的形式为指令绑定具体的参数值
    示例:
      <input v-color="'red'">
      app.directives('color',(el,binding) => {
        // binding.value 就是通过"等号"为指令绑定的值
        el.style.color = binding.value
      })


    

5.Table 案例


一.路由
  路由就是对应关系
  前端路由 指的是: Hash 地址与组件之间的对应关系
  v2 用的是vue-router 3.x  
  v3 用的是vue-router 4.x
 
  vue-router
    步骤:
      1.在项目中安装vue-router
        npm i vue-router@next -S
      2.定义路由组件
      3.声明路由链接和占位符
        声明路由链接:<router-link>
        路由占位符:<router-view>
      4.创建路由模板

        1.创建router.js 路由模块
        2.import { createRouter,createWebHashHistory } from 'vue-router'
        import Home from './MyHome.vue'
        import Movie from './MyMovie.vue'
        import About from './MyAbout.vue'
        // 3.创建路由实例对象
        const router = createRouter({
          // 3.1通过history 属性指定路由的工作模式
          history: createWebHashHistory(),
          // 3.2 通过routes 数组,指定路由规则
          routes: [
            // path 是hash 地址,component 是要展示的组件
            {path:'/home',component:Home},
            {path:'/movie',component:Moive},
            {path:'/about',component:About},
          ]
        })
        export default router
      5.导入并挂载路由模块
        在main.js 中
          import router from './router.js'
          const app = createApp(App)
          // 挂载路由模块
          app.use(router)
          app.mount('#app')  
 
  vue-router 的高级用法:
    1.路由重定向
      const router = createRouter({
          history: createWebHashHistory(),
          routes: [

            {path:'/',redirect:'/home'},

            {path:'/home',component:Home},
            {path:'/movie',component:Moive},
            {path:'/about',component:About},
          ]
        })
    2.高亮路由
      1.默认的高亮class 类
        被激活的路由链接,默认会应用一个叫做router-link-active 类名
        可以使用此类名选择器,为激活的路由链接设置高亮的样式
          // 在index.css 全局样式表中
          .router-link-active{
            background-color: red;
            color:white;
            font-weight:bold;
          }
      2.自定义路由高亮的calss 类
        在创建路由的实例对象时,可以基于linkActiveClass 属性,自定义路由链接被激活时所应用的类名
        const router = createRouter({
          history: createWebHashHistory(),

          // 指定被激活的路由链接,会应用router-avtive 这个类名
          // 默认的router-link-active 类名会被覆盖掉
          linkActibeClass: 'active-router',

          routes: [
            {path:'/',redirect:'/home'},
            {path:'/home',component:Home},
            {path:'/movie',component:Moive},
            {path:'/about',component:About},
          ]
        })

    3.嵌套路由
      步骤:
        1.在router.js 模块中,导入需要的组件,并使用children 属性声明子路由规则
          import Tab1 from './components/tabs/MyTab1.vue'
          import Tab2 from './components/tabs/MyTab2.vue'
          const router = createRouter({
            routes:[
              {
                path:'/about',
                conponent:About,
                redirect: '/about/tab1',     // 嵌套路由的重定向
                children:[
                  {path:'tab1',component:Tab1},
                  {path:'tab2',component:Tab2},
                ]
              }
            ]
          })
        2.在about.vue 组件中
          <router-link to="/about/tab1">tab1</router-link>
          <router-link to="/about/tab2">tab2</router-link>
          // 路由占位符
          <router-view></router-view>
    
    4.动态路由匹配
      动态路由指的是:把Hash 地址中可变的部分定义为参数项,从而提高路由规则的复用性,
      在vue-router 中使用英文的冒号 : 来定义路由的参数项
      
      示例:
        // 路由中的动态参数以 : 进行声明,冒号后面的是动态参数的名称
        { path:'/movie/:id',component:Movie }

        // 将以下三个路由规则,合成为一个,提高了路由规则的复用性
        { path:'/movie/1',component:Movie }
        { path:'/movie/2',component:Movie }
        { path:'/movie/3',component:Movie }
    5.获取动态路由参数值
      1.使用$router.params 获取参数值
         示例:
        { path:'/movie/:id',component:Movie }
        $router.params.id
      2.使用props 接收路由参数
        { path:'/movie/:id',component:Movie,props: true}   // 为当前组件开启了动态路由传参
        
        {{ id }}
        export default{
          props: ['id']
        }
     6.编程式导航
      通过调用API 实现导航的方式,叫做编程时导航
      通过点击链接实现导航的方式,叫做声明式导航
      例如:
        通过点击<a>链接、vue项目中点击<router-link>都属于声明式导航
        调用location.href 跳转到新页面的方式,属于编程式导航
      
      vue-router 中的编程式导航API
        1.this.$router.push('hash地址')
          跳转到指定Hash 地址,从而展示对应的组件
          示例:
            <button @click="goToMovie(3)">到Movie 页面</button>
            methods:{
              goToMovie(id){
                this.$router.push('/movie/'+id)
              }
            }
        2.this.$router.go(数值 n)
          实现导航历史的前进、后退
            示例:
              <button @click="goBack">后退</button>
              methods:{
                goBack(){
                  this.$router.go(-1)  // 后退到上一个组件页面
                }
              }
    7.命名路由
      通过name 属性为路由规则定义名称的方式,叫做命名路由
      注意:命名路由的name 值不能重复,必须保持唯一性
        
      1. 使用命名路由实现声明式导航
        {
          path:'movie/:id',
          // 使用name 属性为当前的路由规则定义一个"名称"
          name:'mov',
          component:Movie,
          props:true
        }

        <router-link :to="{ name:'mov',params:{id:3} }"> go to movie </router-link>

      2. 使用命名路由实现编程式导航
            <button @click="goToMovie(3)">到Movie 页面</button>
            methods:{
              goToMovie(id){
                this.$router.push({ name:'mov', paramas: { id: 3 }})
              }
            }
    8.导航守卫
      1.全局导航守卫
        全局导航守卫会拦截每个路由规则,从而对每个路由进行访问权限的控制
        示例:
          // 创建路由实例对象
          const router = createRouter({...})
          
          // 调用路由实例对象的beforeEach 函数,声明“全局前置守卫”
          // fn 必须是一个函数,每次拦截到路由的请求,都会调用fn 进行处理
          // 因此 fn 叫做“守卫方法”
          router.beforeEach(fn)
        
      2.全局导航守卫的三个形参
        router.beforeEach((to,from,next) => {
          // to 目标路由对象
          // from 当前导航正要离开的路由对象
          // next 是一个函数,表示放行
        })
        注意:
          1.在守卫方法中,如果不声明next 形参,则默认允许用户访问每一个路由
          2.在守卫方法中,如果声明了next 形参,则必须调用next()函数,否则不允许用户访问任何一个路由

        next 函数的三种调用方式:
          1.直接放行 next()
            router.beforeEach((to,from,next) => {
              if(to.path === '/main'){
                // 证明用户要访问后台主页
                next()
              }else{
                // 访问的不是后台主页
                next()
              }
            })
          2.强制其停留在当前页面:next(false)
            router.beforeEach((to,from,next) => {
              if(to.path === '/main'){
                // 停留在/main
                next(false)
              }else{
                next()
              }
            })
          3.强制其跳转到指定的页面: next('/login')
            router.beforeEach((to,from,next) => {
              if(to.path === '/main'){
                // 强制访问登录页面
                next('/login')
              }else{
                next()
              }
            })
      3.结合token 控制后台主页的访问权限
        // 全局路由导航守卫
          router.before((to,from,next) => {
            const tokenStr = localStorage.getItem('token')
            if(to.path === '/main' && !tokenStr){
              next('/login')
            }else{
              next()
            }
          })

二.综合案例
  1.vue-cli
    vue 脚手架,是vue 官方提供的、快速生成vue 工程化项目的工具
    
    特点:
      1.开箱急用
      2.基于webpack
      3.功能丰富且易于扩展
      4.支持创建vue2和v3的项目
    安装:
      npm i -g @vue/cli
      vue create my-project

    
    项目解构:
      node_modules        存放的是第三方的依赖包
      index.html        是项目唯一的首页
      src               是程序的源代码
        assets         存放静态资源
        components          存放组件
        App.vue         是项目的根组件
        main.js         是项目的入口文件
      package.json      是包管理配置文件
      

      在v2 中使用3.x 的路由:
        1.npm i vue-router@3.4.9 -S
        2.在components 下 新建 Home.vue 和 movie.vue
        3.在 src -> router -> index.js 中创建路由模块
          import Vue from 'vue'                               // 1.导入vue2 的构造函数
          import VueRouter from 'vue-router'                  // 2.导入3.x 路由的构造函数
          
          import Home from '@/components/Home.vue'            // 3.导入需要使用路由切换的组件
          import Movie from '@/components/Movie.vue'
          // @ 就代表src 这层目录
          Vue.use(VueRouter)                                  // 4.调用Vue.use() 函数,把路由配置为Vue 的插件

          const router = new VueRouter({                       // 5.创建路由对象
            routes: [                                           // 6.声明路由规则
              {path:'/',redirect:'/home'},
              {path:'/home',component: Home},
              {path:'/movie',component:Movie}
            ]
          })

          export default router                                // 7.向外共享路由对象

        4.在main.js 中导入路由模块
          import Vue from 'vue'
          import App from './App.vue'
          // 1.导入路由模块
          import router from './router'
          
          Vue.config.productionTip = false
          
          new Vue({
            render: h => h(App),
            // 2.挂载路由模块
            // router : router
            router,
          }).$mount('#app')

  2.vue 组件库
    pc端:
      element ui
      View ui
    移动端:
      Mint ui
      Vant

    1.element UI
      1.V2中使用的是   element ui
        npm i element-ui -S
        // 在main.js 中
          import ElementUI from 'element-ui'
          import 'element-ui/lis/theme-chalk/index.css'
          Vue.use(ElementUI)        // 把ElementUI 注册为 vue 的插件
          

      2.v3中使用的是   element plus
        
  3.proxy 跨域代理
    如果后端没有开启CORS 跨域资源共享的情况下
    通过代理解决接口的跨域问题
      如:  
        vue 项目运行在 http://localhost:8080
        API 接口项目 在 https://www.escook.cn
        
      1.把axios 的请求根路径设置为vue 项目的运行地址  
        axios.defaults.baseURL = 'http://localhost:8080'
      2.vue 项目发现请求的接口不存在,把请求转交给proxy 代理
      
      3.代理把请求根路径替换为derServer.proxy 属性的值,发起真正的数据请求
        代理把请求到的数据转发给axios
        
      this.$http.get('/api/users')

      proxy 代理:
        module.exports ={
          devServer:{
            proxy: 'https://www.escook.cn'
          }
        }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值