Vue总结篇中 (看完你就学会整个Vue)

这篇博客全面总结了Vue.js的关键知识点,包括插槽、生命周期钩子、组件缓存、mixin、过滤器、自定义指令、$nextTick、axios的使用、路由配置与导航守卫等,是学习Vue的宝贵资料。

1.插槽

1.   允许 子组件在  父组件中使用 通过子组件(双标签) 嵌套 html结构,这个结构可以在子组件中通过<slot/>组件接收
2. 具名插槽  <slot name="a"></slot> 父组件里面的子组件在给上名
3. 作用域插槽  可以使用子组件数据
<script>
    // 定义子组件对象
    const Child = {
      template: `
        <div>
          <h3>我是子组件头部</h3>  
          <slot :msg="msg" :arr="arr" a="静态值"></slot>
          <h3>子组件的底部</h3>
        </div>
      `,
      data(){
        return {
          msg: '子组件的数据1',
          arr: ['香蕉','菠萝','榴莲','葡萄']
        }
      }
    }
    // 定义父组件 组件对象
    const Home = {
      template: `
        <div>
          <h2>我是父组件</h2>  
          <child>
            <div slot-scope="obj">
              <button>{{obj.msg}}</button>
              <ul>
                <li v-for="item in obj.arr" :key="item">
                  {{item}}  
                </li>
              </ul>
            </div>
          </child>
        </div>
      `,
      components: {
        Child
      }
    }
</script>

2.生命周期

一个组件从初始化(<组件/>),到组件销毁的整个过程
导致组件销毁:
  1, 条件 v-if 让组件 禁用
  2, 路由组件,切换路由的时候,切除这个路由组件会销毁
  3, 组件调用 this.$destroyed() 很少用 自杀
组件一旦销毁:组件实例就会从内存中移除
生命周期的钩子函数:
  实例定义了一些方法 这些方法 会在组件的 生命周期不同阶段会自动调用(mounted)

1. new vue
2. beforeCreate  (实例初始化完成之前)
3. 初始化组件属性方法 原型链 数据响应 遍历data转化getter和sett并创建watcher这个时候实例已经初始化完成了会触发created这个钩子函数
4. 编译真实DOM(mount渲染)
 (1) 将template或者el绑定的元素,扔到render函数中,生成新的虚拟dom 完成后 触发 beforeMount钩子函数
 (2) 将render生成的虚拟dom 编译成真实dom
     完成后触发 mounted (在组件初始化 获取dom只能在mounted)
5.  数据更新阶段
    更新前触发 beforeUpdate钩子函数
    更新后触发  updated
6.  组件销毁阶段(实例卸载)内存中将组件实例这个对象 移除(释放内存)
    移除之前 触发 beforeDestroy 钩子函数
    移除之后,触发 destroyed

整理钩子函数
  初始化 (挂载阶段)
    beforeCreate()
    created  *()
    beforeMount
    mounted *()
  数据更新阶段
    beforeUpdate
    updated *
  组件销毁阶段(组件卸载阶段)
    beforeDestroy *()
    destroyed

初始化阶段:一个组件 可以显示出来(此时不做任何数据修改) 前面四个钩子函数会触发

2.1钩子函数使用场景

钩子函数使用场景
  + 初始化阶段
    created
    调用当前需要 需要 用到数据的ajax的请求函数(model函数),拿到数据后赋值
    ```js
      {
        data(){
          return {
            items: []
          }
        },
        created(){
          fetchItems({page: 1,pageSize: 10}).then(res=> {
            if(res.data.code === 0) {
              this.items = res.data.data
            }
          })
        }
      }
    ```
    mounted 我们才能获取真实dom
    使用初始化 时 需要获取dom的操作只能在mounted中触发
    (大部分情况 都是 一些 插件初始化 时 需要传入 dom作为容器)
    ```js
      // swiper                 这样也是获取了dom
      const swiper1= new Swiper('.swiper-container') 
      // 富文本编辑器
      const editor = new E('#content');

      {
        data(){
          return {
            swiper1: null
          }
        },
        mounted(){
          this.swiper1  = new Swiper('.swiper-container');
        }
      }
    ```
  + 更新阶段
    updated
    在这里可以获取更新后最新的dom
     console.log(this.$refs.oul.children,222);
  + 卸载阶段
    beforeDestroy
    初始化时:
      如果在全局window定义了一些属性方法或者绑定了一些事件以及定时器等,组件销毁之后,这些还在
    为什么:
      将组件实例对象从内存中移除,所以window是一直存在的,window上的属性方法以及事件绑定会一直在
    怎么解决:
      组件卸载之前 手动解除 window上定义写绑定
             比如 清除定时器
         clearInterval(this.timer);  
           需要在父组件data里面挂载   timer:null
            注销全局的事件绑定
             window.onscroll = null;

3.组件缓存

组件停用时,不销毁不初始化 可继续使用 但是谨慎使用会使内存堆积
 <div id="app">
    <button @click="showHide">{{isShow?'消失':'显示'}}</button>
    <keep-alive>   <!-- 组件缓存 -->
      <common-head v-if="isShow"></common-head>
    </keep-alive>
  </div>
局部刷新: 可以配合 1.activated钩子函数 被缓存的组件 再一次使用时触发
2.组件初始化 定义 全局属性方法 和 事件 也应该在这里定义
3.deactivaed 被缓存的组件 停用时触发    用来清除一些全局事件

3.1组件状态切换

<div id="app">
    <button @click="showHide">{{isShow?'隐藏':'显示'}}</button>
    <transition name="ani1"><!-- 状态切换元素 -->
      <div id="box" v-if="isShow"></div>
    </transition>
  </div> 
   这些都是style样式
- .ani1-enter 定义入场一瞬间状态
  .ani1-leave-to 定义出场状态
  .ani1-enter-active,.ani1-leave-active 入场出场中间状态(过度)
+  transition组件控制单组件 (只能包裹一个元素) 多个需要加v-if
    <body>
  <div id="app">
    <button @click="showHide">{{isShow?'隐藏':'显示'}}</button>
    <transition name="ani1" mode="out-in">
      <div id="box" key="1" v-if="isShow">111</div>
      <div id="box2" key="2" v-else>222</div><!-- 因为虚拟dom会比较所以这里加key -->
    </transition>
  </div>
+  transition-group控制列表动画
    结合动画 只需要 两个类即可
入场
.name-enter-active 使用动画
出场
.name-leave-active 使用动画
 @keyframes enter  定义入场动画
 @keyframes leave    定义出场动画

4.mixin

+ 公用组件 谁用注册一下就好
const mixin = {}
const father = {   <!-- 优先用自己的数据方法 -->
    template: ` `
    mixins: [mixin]
}

5.过滤器

 <div id="app">
    {{ num | currency }}
    <common-head></common-head>
  </div>
  <script src="./vue.js"></script>
  <script> 
    // 定义一个全局过滤器 过滤器名 v固定参数
    Vue.filter('currency', (v) => {
      return '¥'+v
    })
    Vue.component('CommonHead',{
      template: `
        <div>
          <h2>这是一个组件</h2>
          {{ num2 |  currency }}
          
        </div>
      `,
      data(){
        return {
          num2: 10
        }
      }
    })
    const vm = new Vue({
      el: '#app',
      data: {
        num:50
      }
    })
  </script>

6.自定义指令 directive

<div id="app">
    <button @click="showHide">{{isShow?'隐藏':'显示'}}</button>
    <input v-if="isShow" type="text" v-focus="100">
  </div>
  <script src="./vue.js"></script>
  <script>
    // 定义一个全局组件
    Vue.directive('focus', {
      inserted(el, binding){
        // console.log(el, 'el')绑定的对象;
        // console.log(binding, 'binding')组件的配置信息;
        el.focus();/* 自动获取焦点 */
        
        // 全局事件
        window.onscroll = () => {
          console.log('滚动事件触发了');
        }
      },
      unbind(){
        // 卸载
        console.log('指令卸载了');
        window.onscroll = null;/* 清除全局事件 */
      }
    })
    const vm = new Vue({
      el: '#app',
      data: {
        isShow: true
      },
      methods: {
        showHide(){
          this.isShow = !this.isShow
        }
      }
    })
  </script>

7.$nextTick

数据更新后,dom更新同步 $nextTick  
methods: {
        addLi(){
          this.$nextTick(()=>{/* 实例调用方法 每一次dom数据更新触发 */
            this.$refs.oul.lastElementChild.style.background="red";
            /* this.$refs.定义ref的值来获取 dom对象或者组件实例 */
          })
    }

8.axios

 Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
axios({
  url:'',
  method: 'get/post'
  params: {},// get请求参数
  data: {}, // post请求参数
})


 <div id="app">
    <ul>
      <li v-for="cate in cates" :key="cate.id">
        <h2>{{cate.name}}</h2>
        <img :src="cate.icon" alt="">
      </li>
    </ul>
    <hr>
    <ul>
      <li v-for="item in items" :key="item.id">
      <h5> 商品名称:{{item.name}}</h5>
      <img :src="item.pic" alt="">

      </li>
    </ul>
  </div>
  <script src="./vue.js"></script>
  <script src="./axios.js"></script>
  <script>
    /* 定义默认源地址 */
    axios.defaults.baseURL = 'https://api.it120.cc/conner'/* 源 */
    const vm = new Vue({
      el: '#app',
      data: {
        cates: [],
        items: []
      },
      created(){
        this.fetchCates();
        this.fetchItems();
      },
      methods: {
        fetchCates(){
          axios({
            url: '/shop/goods/category/all',/* path 路径 */
            method: 'get',
            params: {   // get请求参数有params
              page:1,
              pageSize: 10
            }
          }).then(res=> {
            if(res.data.code === 0){
              this.cates = res.data.data
            }
          })
        },
        fetchItems (){
          axios({
            url:'/shop/goods/list/v2',
            method: 'post',  
            data: {          //传参用data属性 post请求参数有data
              a: 10,
              b: 20
            }
          }).then(res=> {
            if(res.data.code ===0) {
              this.items = res.data.data.result
            }
          })
        } 
      }
    })

  </script>

8.1自定义默认配置

/* 创建 axios 实例来定义 默认配置 跟axios用法一模一样*/
    const request = axios.create({
      baseURL: 'https://api.it120.cc/conner'/*baseURL后面3个大写  */
    })


 methods: {
        fetchCates(){
          request({/* 这里吧axios改成request就好了 */
            url: '/shop/goods/category/all',
            method: 'get',
            params: {
              page:1,
              pageSize: 10
            }
          }).then(res=> {
            if(res.data.code === 0){
              this.cates = res.data.data
            }
          })
        },

8.2axiaos拦截器

<!--  添加请求的拦截 拦截 所有的请求 -->

 axios.interceptors.request.use(function (config) {
        config.xxxx      请求配置
     return config  把上面请求放行发送给后端
      },function (error) {
      // 请求错误的拦截 .catch执行不了 
      return Promise.reject(error);
 })
     // 响应的拦截
    axios.interceptors.response.use(function (response) {
      console.log('得到数据了');
      console.log(response);
      return response;
    }, function (error) {
      // http状态码 不是  2xx 就会走报错
      return Promise.reject(error);
    });

9.路由

1.基础路由

原理: 一个路由地址对应一个组件
  功能:创建前后端分离的单页面应用
  优点: 大大提交网页的切换性能(网页打开速度变快)
         开发效率提交(不会出现前后端扯皮情况
  缺点:首页打开速度会变慢(可以解决的 html代码量过大)
        seo(搜索引擎优化) 不利于seo


<body>
  <div id="app">
    <router-link to="/Sakura" tag="button">樱花</router-link>
    <router-link to="/apple" tag="button">苹果</router-link>
    <router-link to="/game" tag="button">游戏</router-link>
    <router-view></router-view>   <!-- 第三步 配置路由出口 -->
  </div>
  <script src="./vue.js"></script>  <!-- 第一件事现引入 在配置vm -->
  <script src="./vue-router.js"></script>
  <script>
    Vue.config.productionTip = false
     
    const Sakura = {       /* 这里和下面 component组件对应 */
      template: `
              <div>
          <h1>樱花页</h1>  
              </div>
              `
    }
    
    const Apple = {
      template: `
              <div>
          <h1>苹果页</h1>  
              </div>
              `
    }

    const  Game = {
      template: `
              <div>
          <h1>游戏</h1>  
              </div>
              `
    }
    const  NotFound ={
      template: `
              <div>
          <h1>404啦  NotFound</h1>  
              </div>
              `
    }
 
    
      const routes = [       /* 这里是下面的routes拿到外面单独定义 */
      {
        path:'/sakura',     /* 地址 */
        component:Sakura   /* 这是注册路由组件 通过component来注册组件不要字符串 */
      },
      {
        path:'/apple',    
        component:Apple  
      },
      {
        path:'/game',     
        component:Game  
      },

      {
        path: '/',
        redirect: '/game'     /* redirect 默认打开/ 路径 变成/game
          */
      },
      {
        path: '/404',       /* 输入错误的会自动跳转404 */
        component: NotFound
      },
      {
        path: '*',        /* 优先级最低的) */
        redirect: '/404'
      }
       

   ]
  
    const router = new VueRouter({     // 第二步:创建路由对象
      routes                           // 路由规则 一个路由地址 对应什么组件
    })


     // 第三步步:将路由对象 挂载到实例上
     const vm = new Vue({
      el: '#app',
      router            /* 2.5步 挂载到实例上 */
    });
  </script>

1地址组成

ttp://  www.xxx.com  :8080  /a/b/c  ?a=10&b=20           #aaa

协议名   主机名       端口    path     search(不要?query)   hash (网址后面指向那个)

2.动态路由

概念:路由的地址 某些path是可变的
  使用场景:
  用于 路由跳转传参 (列表页 进入详情页路由 一般需要传id)

{
path: '/news/:id1/a/:id2', // /:参数名 定义 动态的path 相当于定义了一个变量叫id
  component: News   在news中增加watch:{$route(to,from)}侦听动态变化
}


// 跳转 地址 和 path保持一致
 <router-link to="/news/2/a/5" ></router-link>



 <style>
    .router-link-active{/* 一级路由使用高亮类 */
      background-color: red;/* 二级使用 */
    }
    .news .router-link-exact-active{ /* news 就是子路由的class */
      background-color: rgb(20, 129, 111);
    }
  </style>

3.嵌套路由

在 父级路由的 路由规则 新增children属性 定义 子级路由


  {
        path: '/news',
        component: News,
        children: [          /* 给父路由增加一个属性 */
          {
            path: '/news',    /* 设置默认为n想·ative */
            redirect: '/news/native'
          },
          {
            path: '/news/native',   /* 子路由嵌套父路由里面 然后对应上面父news中注册 */
            component: NativeNews
          },
          {
            path: '/news/abroad',
            component: AbroadNews
          }
        ]
      }


在父级路由 路由组件的template新增router-view 作为 子级路由的出口

    const  News = {
      template: `
        <div>
          <h1>新闻页</h1>  
          <router-link to="/news/native">国内新闻</router-link>
          <router-link to="/news/abroad">国外新闻</router-link>
          <!--定义子级路由的出口-->
          <router-view></router-view>
        </div>
      `
    }

4.命名路由

给路由配置name属性  const routes=[{ path:'',name:'首页'}]
  <router-link :to="{name: '首页'}" tag="button">首页</router-link>


{
        path: '/home',
        name: '首页',
        component: Home
      },

10.编程式导航

1. $route 获取路由信息(获取动态路由跳转传参数)
   $router 其实就是 new VueRouter返回的router实例  
2. this.$router.addRoutes() 动态添加路由
3. getRoutes() // 获取当前路由的 routes属性 获取路由规则
go(n) // 0刷新 1 前进一步 -1 后退一步
push() // 参数和router-link的to属性一样,跳转路由
replace() // 参数和router-link的to属性一样,跳转路由 replace跳转路由不会新增新的历史记录 而会覆盖当前历史记录



  <button @click="addRoutes">动态添加一个路由</button>
  // 路由对象挂载到实例上 新增router属性
    const vm = new Vue({
      el: '#app',
      methods: {        /* 方法 */
        addRoutes(){
          // 动态添加路由
          this.$router.addRoutes([   /* 和上面button对应 */
            {
              path: '/itemList',  
              component: ItemList
            }
          ])
        }
      },
      router
    })
  </script>


  <button @click="$router.go(0)">刷新</button><!-- 直接调用· -->
 <button @click="$router.replace('/about')">到关于我们</button><!-- 这里不会新加历史记录 

    找不到去404页面 
     {
        path: '/404',
        component: NotFound
      },
      {
        path: '*',
        redirect: '/404'
      }

11. 路由跳转传参

1.query在url中是指:url?a=10&b=20  ?a=10&b=20叫search   a=10&b=20 是query
 <router-link :to="{         
      path:'/news',
      query: {
        a: 10,
        b: 20
      }
    }" tag="button">新闻</router-link>
      对应的news下方法 mounted(){
        console.log(this.$route.query); /* 保存的路由相关参数 */
      }
2. 特点: 在地址栏显示 参数刷新不丢失
3.  params传参  隐式传参 1.没有在地址栏体现 2 刷新后数据丢失

12.路由模式

1. 1 hash 模式 
  体现:改变了 地址栏 url hash值(url#hash),动态控制路由组件的显示和卸载
    利用window.onhashchange = function(){

    }
优点:
 hash值改变 不会 改变 url 指向的那个html,网页不会刷新  
 组件切换速度 比较快
缺点:
  丑 (url多个#)

2. history(黑丝特瑞)模式 
  当你使用 history 模式时,URL 就像正常的 url,例如 http://yoursite.com/user/id,也好看!
原理:(工作 薪资 9k以上)
  h5 给js history新增了两个api history.pushState() 和 history.replaceState
缺点:
  没有# 直接改变整个地址栏的地址
  原来:
    www.xxx.com/index.html
    // 跳转到新闻页 /news
    立即变成/news

问题:
  使用 history 如果 服务器 不 做配置的情况下 会产生一个404
服务器支持:
  想要使用history模式 一定要放到服务器中,服务器要做 配置
  配置原理:当我们一个请求匹配不到任何资源(产生404了),那我们永远让你重定向到 index.html

/ 路径如果放到 url最前面 称为 根目录
  根目录:
        服务器环境下 指向 服务器静态资源根目录
        非服务器 指向 盘符
img src="/imgs/a.jpg"

13.导航守卫

1.全局前置守卫

功能:
  拦截 或者 监听 路由 变化
+ 全局 (拦截的监听所有的路由变化)
  - 全局前置守卫   *** 拦截所有路由
    ```js
      router.beforeEach((to, from, next)=> {
        /* 
        to 即将 进入的 路由的参数对象 (跟实例上 $route一样)
        from 离开的 那个路由参数对象
        next 放行
          next() 调用时  不加参数 直接 放行到 to这个路由
          next('/login')还可以传参 参数 和route-link的to属性一样,还可以指定跳转
         */
      })

2.登录鉴权

 注意:
      beforeEach回调函数,从上往下 next只能出现一次(写分支 )
    使用场景:
      一般用于  鉴权
        1 登录鉴权 
        2 后台管理中的角色鉴权
      前后端分离token作用
        从哪来,用于登录成功后,后端返回这个token
          1 前端缓存起来,访问特定路由时(个人中心),判断用于是否登录
          2 后端 有些接口 需要 登录才能拿到数据(购物车数据),前端在请求时一般需要携带token



   methods: {
        doLogin() {
          setTimeout(() => {
            const token = 'hfieirehgire';
            // 将token缓存起来
            localStorage.setItem('token', token);
            // 跳转回来
            if (this.$route.query.from) {
              this.$router.replace(this.$route.query.from)
            } else {
              this.$router.replace('/home')
            }
          }, 1000)
        }
      }
    }

_________________________________________________________________ 
  {
        path: '/news',
        component: News,
        meta: {                  路由元信息我们可以在 路由 规则中 添加 meta属性,在这个属性(对象)中 定义一些 属性,
          needLogin: true   /* 根据,meta来判断需不需要登录 */
        }
      },

_________________________________________________________________ 
 // 创建路由对象
    const router = new VueRouter({
      routes
    })
    // 全局前置守卫
    /* 
      拦截所有的路由变化
    */
    router.beforeEach((to, from, next) => {
      /* 
       取路由元信息中 needLogin属性
       判断
        true 需要登录 
        false 不需要登录
      */
      if (to.meta.needLogin) {
        // 需要登录
        const token = localStorage.getItem('token');
        if(token) {
          next();
        }else{
          next({
            path: '/login',
            query: {
              from: to.path
            }
          })
        }
      } else {
        // 不需要登录
        next();
      }

    })
    const vm = new Vue({
      el: '#app',
      router
    })
  </script>

3.全局后置守卫

 只能监听路由 不能拦截,(因为 已经渲染目标路由组件)
   
      router.afterEach((to, from)=>{

      })
    

4.路由独享守卫

 {
      path: '/about',
      component: About,
      beforeEnter: (to, from, next) => {

      }
    }

另外两种
  beforeRouteEnter(to, from, next) {
      /* 
      渲染目标路由组件之前触发
      不能获取组件实例
      使用场景:
        某些路由 需要用户确认信息 之后才能进入 可以使用这个组件
       */
      if(confirm('您18了吗')){
        next();
      }else{
        next('其他路由')
      }
    },
    beforeRouteUpdate(to, from, next) {
      /* 
        路由发生改变时触发
        使用场景是:多个路由地址 匹配同一个组件
        具体干啥:  可以用来监听动态路由参数的变化
      */
    },
    beforeRouteLeave(to, from, next) {
      /* 
        某些路由组件 希望用于 多停留一段时间,当离开时,弹出确认框
        询问用户是否真的离开
      */
      if(confirm('您确认这么狠心离开我啦')){
        next();
      }
    }

面试题:

  如何监听动态路由参数变化

  watch $route可以

  组件内的 导航守卫 beforeRouteUpdate

5.路由切换的过渡动效

<transition name="ani" mode="out-in"> <!-- 路由切换的过渡动效 mode现出场在入场-->
      <router-view></router-view>
    </transition>


  <style>
    html{
      overflow-x: hidden;/* 去除滚动条 */
    }
    .ani-enter{
      transform: translateX(-100%);
      opacity: 0;
    }
    .ani-leave-to{
      transform: translateX(100%);
      opacity: 0;
    }
    .ani-enter-active,.ani-leave-active{
      transition: all 300ms;
    }
  </style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值