Vue脚手架学习笔记

发布时间 2023-03-27 15:20:14作者: maxTUNI
title: Vue脚手架
date: 2023-03-06 15:10:21
tags: Vue

一、 脚手架文件结构

|--node_modules
|--public
|  |--favicon.ico:页签图标
|  |--index.html:主页面
|--src
|  |--assets:存放静态资源
|  |  |--logo.png
|  |--component:存放组件
|  |  |--Helloworld.vue
|  |--App.vue: 汇总所有组件
|  |--main.js:入口文件
|--.gitignore: git版本管制忽略的配置
|--babel.config-js: babel的配置文件
|--package.json:应用包配置文件
|--README.md:应用描述文件
|--package-lock.json:包版本控制文件

二、关于不同版本的Vue:

  • vue.js与vue.runtime.xxx.js的区别:
    • vue.js是完整版,包含核心功能+模板解析器
    • vue.runtime.xxx.js是运行版的Vue,只包含核心功能
  • 因为使用runtime版本的vue可以提高效率但是不能解析模板,不能配置template配置对象,需要使用render函数去createElement创建元素

三、vue.config.js配置文件

  • 可以使用vue inspect > output.js查看Vue脚手架的默认配置
  • 使用vue.config.js可以对脚手架进行个性化定制,详情见: https://cli.vuejs.org/zh

四、ref属性

  • 作用:被用来给元素或子组件注册引用信息(id的替代者),应用在html标签上获取的是真实的dom元素,应用在组件标签上获取的是组件实例对象(vc)
  • 使用方式
    • 打标识

    • 或者
    • 获取: this.$refs.xxx

五、配置项props

功能:让组件接收外部传过来的数据

  • 传递数据:

  • 接收数据:

    1. 第一种方式(只接收): props:['name']

    2. 第二种方式(限制类型):

      props: {
      	name: type
      }
      
    3. 第三种方式(限制类型,是否必要,默认值)

      props: {
      name: {
      		type: 
      		required:
      		default: 
      	}
      }
      

备注:

  • props是只读的,如果修改会被警告,当业务要求需要修改时,可以复制props的属性值到data中

  • props解析优先于data

六、mixin(混入)

  • 功能:把多个组件共用的方法,配置,钩子等提取成一个混入对象

  • 使用方式:

    1. 定义混入:

      /*mixin.js*/
      export const hunhe = {
          methods: {
              showName() {
                  alert(this.name)
              },
              mounted() {
                  console.log('你好')
              }
          }
      }
      
    2. 使用混入:

      Vue.mixin(name)全局混入
      mixins:['name']局部混入
      

    注意:

    • 如果混合的data或methods发生冲突,那就以引入混合的文件为主

    • 但是如果是生命周期钩子,就和本文件的合并,混合的钩子运行在前

七、插件

  • 功能:用于增强Vue定义的js功能库

  • 本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。

  • 定义插件:

    /*plugin.js*/
    export default{
        install(Vue, options) {
            //添加过滤器
            Vue.filter()
            //添加全局指令
            Vue.directive()
            //配置全局混入
            Vue.mixin
            //添加实例方法
            Vue.prototype.$myMethod = function() {...}
            Vue.prototype.$myProperty = xxx
    	}
    }
    
  • 使用插件:

    //引入插件
    import plugins from './plugins'
    //使用插件
    Vue.use(plugins)
    //然后插件定义的过滤器,指令等等就可以直接使用了
    

八、scoped样式

  • 作用: 让样式在局部生效,防止组件样式同名发生冲突

  • 写法: 使用less格式的样式(需要安装less-loader)

    总结TodoList案例

    说明: 本案例不涉及总线,消息订阅,状态仓库

    image-20230303135156630

    • 组件化编码流程:

      1. 拆分静态组件:组件要按照功能点拆分,命名不要和html元素冲突

      2. 实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用

        • 如果是一个组件在用:放在组件本身即可

        • 如果是一些组件在用:放在他们共同的父组件上(状态提升)

      3. 实现交互:从绑定事件开始,数据在哪改变数据的事件就在哪

    • props适用于

      • 父组件 ==> 子组件通信
      • 子组件 ==> 父组件通信 (要求父组件先传递给子一个函数)

      注意:

      • 使用v-model时不能绑定props传递过来的值,因为props不可修改

      • props传过来的如果是对象,修改对象里的属性Vue不会报错,但不推荐这么做

    九、webStorage

    包括:localStoragesessionStorage

    1. 存储大内容大小一般支持5MB左右,不同浏览器可能不同

    2. 浏览器端通过Windows.sessionStorageWindows.localStorage属性来实现本地存储机制,存到硬盘上

    3. 相关API:(sessionStorage和localStorage身上的API一样,前几个字母就用xxx代替)

      • xxxxStorage.setItem('key', 'value'):

      • xxxxStorage.getItem('key')

      • xxxxStorage.removeItem('key')

      • xxxxStorage.clear()

    备注:

    • SessionStorage存储的内容会随浏览器窗口关闭而消失
    • LocalStorage存储的内容,需要手动清除才会消失
    • xxxxStorage.getItem('key')不存在相应的Key那么返回值是null
    • JSON.parse(null)的值依然是null

    十、组件的自定义事件

    1. 一种组件间通信的方式,适用于==子组件 > 父组件传递数据

      父==>子 使用props最方便

    2. 使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)

    3. 绑定自下定义事件:

      • 第一种方式:在父组件中 <Demo @atguigu="callback/>"<Demo v-on:Demo="callback"/>

      • 第二种方式:在父组件中

        <Son ref="demo" /> 给子组件打上标识
        ...
        mounted() {
        	this.$refs.demo.$on('atguigu', this.callback)
        }
        
      • 如果想让自定义事件触发一次可以用@atguigu.once$once方法

    4. 触发自定义事件:this.$emit('atguigu', 数据)

    5. 解绑自定义事件:this.$off('atguigu') this.$off(['atguigu', 'demo']) this.$off()

    6. 组件上也可以绑定原生的DOM事件,需要加上native修饰符

    注意:通过this.$refs.xxx.$on('atguigu', callback)绑定自定义事件时,callback回调函数要么事先定义在methods中,要么用箭头函数,会有this指向问题

    十一、全局事件总线(GlobalEventBus)

    1. 一种组件间通信的方式,适用于任意组件间通信

    2. 安装全局事件总线

      new Vue({
      	...
      	beforeCreate() {
      		Vue.prototype.$bus = this  //安装全局事件总线,$bus的值就是vm
      	}
      	...
      })
      
    3. 使用事件总线:

      • 接收数据:A组件想接收数据,在A组件中给$bus绑定自定义事件,事件的callback留在A组件自身
      methods() {
      	demo(value) {
      		...
      	}
      }
      ...
      mounted() {
          // 给$bus绑定了一个xxx事件,一般是接收方去绑定事件,提供数据方去触发事件
      	this.$bus.$on('xxxx', this.demo)
      }
      beforeDestroy() {
          // 组件销毁前解绑事件
          this.$bus.$off('xxx')
      }
      
      • 提供数据:this.$bus.$emit('xxx', 数据),找$bus触发事件

    注意:最好在beforeDestroy钩子中在组件销毁前,用$off去解绑当前组件所用的到的事件,因为$bus会一直存在不会随组件销毁而消失

    十二、消息订阅与发布

    (不常用)组件间通信多用事件总线

    1. 一种组件间通信的方式,适用于任意组件间通信

    2. 使用步骤

      • 安装pubsub(第三方库): npm i pubsub-js

      • 在使用的地方引入: improt pubsub from 'pubsub-js'

      • 接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。

        methods(){
        	demoCallback(msgName, value){...}
        }
        ...
        mounted() {
        	// 每次订阅都会有独立的ID,销毁时需要用ID销毁
        	this.pid = pubsub.subscribe('xxx', this.demoCallback) //订阅消息
        }
        beforeDestory() {
            // 取消订阅
            pubsub.unsubscribe(this.pid)
        }
        
      • 提供数据:pubsub.publish('xxx', 数据)

    最好在beforeDestory取消订阅pubsub.unsubscribe(this.pid)

    十三、nextTick

    1. 语法this.$nextTick(回调函数)

    2. 作用:在下一次DOM更新结束之后执行指定的回调函数

      这里的下一次更新是指Vue在改变数据的时候并不会实时渲染模板更新DOM,但是如果想对渲染后DOM进行操作就需要在Vue渲染更新DOM后才有效

    十四、Vue脚手架配置代理proxy

    方法一

    • 在vue.config.js中添加如下配置:

      devServer: {
      	proxy: 'http://localhost:5000'
      }
      
    • 说明:

      • 优点:配置简单,请求资源时直接发给前端8080即可
      • 缺点:不能配置多个代理,不能控制是否走代理配置
      • 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器(优先匹配前端资源)

    方法二

    • 编写vue.config.js配置具体代理规则:

      module.exports = {
        devServer: {
          proxy: {
            // 请求如果带有/atguigu前缀就走代理服务器转发
            '/atguigu': {
              target: 'http://localhost:5000',
              // 将请求重写,如果不重写会带着前缀,参数的key是一个正则匹配
              pathRewrite: {'^/atguigu' : ''}
              // ws: true,  // 用于支持websocket,默认为true
              // changeOrigin: true // 用于控制请求头的中的host值,默认为true
            },
            '/demo': {
              target: 'http://localhost:5001',
              // 将请求重写,如果不重写会带着前缀,参数的key是一个正则匹配
              pathRewrite: {'^/demo' : ''}
            },
          }
        }
      }
      /*
      	changeOrigin的值为true时,服务器收到的请求头host值为:localhost:5000
      	changeOrigin的值为false时,服务器收到的请求头host值为:localhost:8080
      	它的默认值为true,为了适应服务器屏蔽某些端口
      */
      
    • 说明

      • 优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
      • 缺点:配置略微繁琐,请求资源时的url需要加前缀

    十五、插槽

    • 作用:在组件标签体当中插入HTML结构,可以让父组件可以向子组件指定位置插入HTML结构,也是一种组件间通信的方式,适用于父组件===>子组件

    • 分类:默认插槽、具名插槽、作用域插槽

    • 使用方式

      • 默认插槽

        App.vue父组件中:
        <Category>
            <div>
                html结构1
            </div>
        </Category>
        
        子组件中:
        <tempalte>
        	<div>
               	定义插槽
                <slot>插槽默认显示内容...</slot>
            </div>
        </tempalte>
        

        说明:在父组件App.vue创建组件标签,然后标签体里的HTML结构就会默认传递到子组件的位置

      • 具名插槽

        父组件中:
        <Category>
        	<template slot="center">
            	<div>
                    html结构1
                </div>
            </template>
            
            <template v-slot:footer>
            	<div>
                    html结构2
                </div>
            </template>
        </Category>
        
        子组件中:
        <tempalte>
        	<div>
                定义插槽
                <slot name="center">插槽默认内容</slot>
                <slot name="footer">插槽默认内容</slot>
            </div>
        </tempalte>
        

        给插槽起个名字,在传递结构的时候指定传递到哪个插槽

      • 作用域插槽

        • 理解:可以用来把子组件的数据传递给父组件,同时子组件的结构就由父组件来决定

        • 具体编码:

          父组件:
          <Category title="游戏分类">
                      <!--scope属性用来接收组件内传递过来的所有数据作为一个对象-->
                      <template scope="atguigu">
                          <ul>
                              <li v-for="(g, index) in atguigu.games" :key="index">{{g}}</li>
                          </ul>
                      </template>
          </Category>
          
          <Category title="游戏分类">
                      <template scope="atguigu">
                          <h4 v-for="(g,index) in atguigu.games" :key="index">{{g}}</h4>
                      </template>
          </Category>
          
          子组件:
          <template>
              <div class="category">
                  <h3>{{title}}</h3>
                  <!--定义一个插槽-->
                  <slot :games="games">默认标签体2,没有指定标签填充就会出现</slot>
              </div>
          </template>
          
          <script>
              export default {
                  name: "Category",
                  props: ['title'],
                  data() {
                      return {
                          games: ['红色警戒', '穿越火线', '超级玛丽'],
                      }
                  }
              }
          </script>
          

    说明:前两种插槽的主要作用是由组件的使用者,向组件里传递HTML元素,

    ​ 而作用域插槽是将子组件的数据传递回组件的使用者,也是作为组件间数据传递的一种方法

    十六、Vuex

    • 概念:在Vue中实现集中式状态(数据)管理的一个Vue插件,对Vue应用中的多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。

    • 使用场景:多个组件需要共享数据时

    • 搭建Vuex环境

      1. 创建文件:src/store/index.js
      //该文件用于创建Vuex中最为核心的store
      import Vue from 'vue'
      import Vuex from 'vuex'
      
      // 在这里使用vuex插件,因为脚手架对于import语句的处理
      Vue.use(Vuex)
      //准备actions--用于响应组件中的动作
      const actions = {}
      //准备mutations--用于操作数据(state)
      const mutations = {}
      //准备state--用于存储数据
      const state = {
          sum: 0
      }
      
      //创建store并暴露
      export default new Vuex.Store({
          actions,  //配置对象和值重名可以用简写形式
          mutations,
          state
      })
      
      1. main.js创建vm时传入store配置项
      import Vue from 'vue'
      import App from './App.vue'
      // 引入store
      import store from './store'
      Vue.config.productionTip = false
      
      const vm = new Vue({
          el: '#app',
          render: h => h(App),
          store: store,
          beforeCreate() {
              Vue.prototype.$bus = this
          }
      })
      console.log('vm', vm)
      

      注意:

      • 因为vue脚手架会把所有import语句按顺序放在最前面执行,所以如果在main.js文件当中import store from './store'Vue.use(Vuex)还没生效会报错

      • 在组件当中读取vuex中的数据: $store.state.xxx

      • 在组件当中修改vuex中的数据:$store.dispatch('action中的方法名', 数据)或者$store.commit('mutation中的方法名', 数据)

      • 备注:如果没有网络请求或者业务逻辑处理,组件中可以越过actions,直接commit

        有业务逻辑写到action当中,有context参数调用上下文很方便

    • getters配置项的使用

    • 概念:当state中的数据需要经过加工后再使用时,可以使用getter,主要应用在加工逻辑复杂并且需要复用的场景下

    • store.js中追加getters配置

      ....
      const getters = {
      	// 这里可以收到state参数
      	bigSum(state) {
      		// 假如这是一个复杂的逻辑
      		return state.sum * 10
      	}
      }
      ....
      //创建store并暴露
      export default new Vuex.Store({
          actions,  //配置对象和值重名可以用简写形式
          mutations,
          state,
          getters
      })
      
    • 在组件当中读取数据$store.getters.xxx

    • Vuex中四个map方法的使用

      1. mapState方法:用于帮助我们映射state中的数据为计算属性

        import {mapState, mapGetters} from 'vuex'
        ...
        computed: {
                    // 借助mapState生成计算属性,从state中读取数据填充到computed对象当中 (对象写法)
                     ...mapState({he: 'sum', xuexiao: 'school', xueke: 'subject'}),
                    // 借助mapState生成计算属性,从state中读取数据填充到computed对象当中 (数组写法)
                    ...mapState(['sum', 'school', 'subject']),
                  }
        
      2. mapGetter方法:用于帮助我们映射getters中的数据为计算属性

        import {mapState, mapGetters} from 'vuex'
        ...
        computed: {
        			// 借助mapGetters生成计算属性,从state中读取数据填充到computed对象当中 (对象写法)
                     ...mapGetters({bigSum: 'bigSum'})
                    // 借助mapGetters生成计算属性,从state中读取数据填充到computed对象当中 (数组写法)
                    ...mapGetters(['bigSum'])
                    }
        
      3. mapActions方法:用于帮助我们生成与actions对话的方法,即:包含$store.dispatch(xxx)的函数

        import {mapState, mapGetters, mapActions, mapMutations} from 'vuex'
        ...
        methods: {
        	
        	...mapActions({incrementOdd: 'jiaOdd', incrementWait: 'jiaWait'})
        	
        	...mapActions(['jiaOdd', 'jiaWait'])
        }
        
      4. mapMutations方法:用于帮助我们生成mutations对话的方法,即:包含$store.commit(xxx)的函数

        import {mapState, mapGetters, mapActions, mapMutations} from 'vuex'
        ...
        methods: {
        	
        	...mapMutations({increment: 'JIA', decrement: 'JIAN'})
        	
        	...mapMutations(['JIA', 'JIAN'])
        }
        
    • 模块化+命名空间

      1. 目的:让代码更好维护,让多种数据分类更加明确,避免所有的状态和动作都放在一起

      2. 修改store.js

        const personOptions = {
            namespaced: true, //开启命名空间,用map方法时就能识别到
         actions: {
            },
            mutations: {
            },
            state: {
            },
            getters: {
            }
        }
        
        const countAbout = {
            namespaced: true, //开启命名空间,用map方法时就能识别到
            actions: {
            },
            mutations: {
            },
            state: {
            },
            getters: {
            }
        }
        
        const store = new Vuex.Store({
            modules: {
                countAbout,
                personAbout
            }
        })
        

        提示:可以把模块化后的配置选项作为单独的js文件放到store文件夹中,再引入

      3. 开启命名空间后,组件中读取state数据:

        //方式一:直接读取
        this.$store.state.personAbout.personList
        //方式二:用mapstate读取
        ...mapState('countAbout', ['sum','school','subject']) ,
        
      4. 开启命名空间后,组件中读取getters数据:

        //方式一:直接读取 (getter的命名方式与其他配置项不太一样)
        this.$store.getters['personAbout/firstPersonName']
        //方式二:借助mapGetters读取
        ...mapGetters('countAbout', ['bigSum'])
        
      5. 开启命令空间后,组件调用dispatch

        //方式一: 自己直接dispatch
        this.$store.dispatch('personAbout/addPerson', person)
        //方式二:借助mapActions
        ...mapActions('countAbout', {incrementOdd: 'jiaOdd', incrementWait: 'jiaWait'})
        
      6. 开启命名空间后,组件调用commit

        //方式一:自己直接commit
        this.$store.commit('personAbout/ADD_PERSON', person)
        //方式二:借助mapMutations
        ...mapMutations('countAbout', {increment: 'JIA', decrement: 'JIAN'})
        

    十七、路由

    1. 理解:一个路由(route)就是一组映射关系(key-value), 多个路由需要路由器(router)进行管理
    2. 前端路由:key是路径,value就是组件。

    基本使用

    1. 安装vue-router: npm i vue-router

    2. 应用插件: Vue.use(VueRouter)

    3. 编写router配置项:

      //引入VueRouter
      import VueRouter from "vue-router";
      import About from "@/components/About";
      import Home from "@/components/Home";
      
      //创建router实例对象,去管理路由规则 
      const router = new VueRouter({
          routes: [
              {
                  path: '/about',
                  component: About
              },
              {
                  path: '/home',
                  component: Home
              }
          ]
      })
      
      export default router
      
    4. router-link标签实现触发路由切换

      <router-link class="list-group-item" active-class="active" to="/about">About</router-link>
      
    5. router-view指定组件展示位置

      <router-view></router-view>
      

    几个注意点

    • 需要被切换的路由组件通常存放在pages文件夹,静态展示的一般组件通常存放在components文件夹
    • 通过切换,”隐藏“了的路由组件,默认是被销毁掉的,需要的时候再去挂载
    • 每个组件都有款自己的$route属性,里面存储着自己的路由信息
    • 整个应用只有一个router,可以通过组件的$router属性获取
    • 每个路由组件都会有$route$router属性,前者是存储自己的路由信息,后者是同一个属性对象公共同的路由器

    多级路由

    1. 配置路由规则,使用children配置顶

      const router = new VueRouter({
          routes: [
              {
                  path: '/about',
                  component: About,
              },
              {
                  path: '/home',
                  component: Home,
                  children: [
                      {
                          path: 'news',  // 底层监测到子路由时会自动加上/
                          component: News
                      },
                      {
                          path: 'message',
                          component: Message
                      }
                  ]
              }
          ]
      })
      
    2. 跳转要写完整的路由路径

      <router-link class="list-group-item" active-class="active" to="/about">About</router-link>
      

      tip: children里配置的子路由,如果被访问那么就会被渲染到上一级组件中的标签的位置

    路由的query参数

    1. 传递参数

      <!--跳转路由并携带query参数,to的字符串写法-->
      <router-link :to="`/home/message/detail?id=${message.id}&title=${message.title}`">{{message.title}}</router-link>&nbsp;&nbsp;
      
      <!--跳转路由携带query参数,对象写法-->
      <router-link :to="{
                          path: '/home/message/detail',
                          query: {
                              id: message.id,
                              title: message.title
                          }
      }">
         {{message.title}}
      </router-link>&nbsp;&nbsp;
      
    2. 接收参数

      $route.query.id
      $route.query.title
      

    命名路由

    1. 作用:可以简化路由的跳转,在路径复杂的情况

    2. 如何使用

      • 给路由命名:

        {
             name: 'guanyu',
             path: '/about',
             component: About,
        },
        
      • 简化跳转

        简化前
        <router-link class="list-group-item" active-class="active" :to="/demo/test/about">About</router-link>
        
        简化后
        <router-link class="list-group-item" active-class="active" :to="{name: 'guanyu'}">About</router-link>
        
        <router-link
              :to="{
                   name: 'guanyu',
                   query: {
                   	id: 666,
                   	title: '你好'
                   }
             }"
        跳转<router-link/>
        

    路由的params参数

    1. 配置路由,声明接收params参数占位符

      {
                          path: 'message',
                          component: Message,
                          children: [
                              {
                                  name: 'xiangqing',
                                  path: 'detail/:id/:title',  //使用占位符声明接收params参数
                                  component: Detail
                              }
                          ]
      }
      
    2. 传递参数

      <!-- 跳转携带params参数,to的字符串写法 -->
      <router-link :to="`/home/message/detail/${message.id}/${message.title}`">{{message.title}}</router-link>&nbsp;&nbsp
      
      <!-- 跳转携带params参数,to的字符串写法 -->
       <!--跳转路由携带query参数,对象写法-->
      <router-link :to="{
                          path: '/home/message/detail',
                          // name: 'xiangqing',
                          params: {
                              id: message.id,
                              title: message.title
                          }
                      }">
                          {{message.title}}
      </router-link>&nbsp;&nbsp;
      

      注意:路由携带params参数时,如果使用to的对象写法则不能使用path配置项,必须和name配置项搭配使用

    3. 接收参数

      $route.params.id
      $route.params.title
      

    路由的props配置项

    • 作用:让路由组件更方便地收到参数,在代码逻辑当中不用去$route.query.xxx$route.params.xxx调用属性

      //props的第一种写法,值为对象,该对象中的所有key-value都会以pros形式传给Detail组件。用的很少
      props: {
              a: 1,
              b: 'hello',
      }
      
      //props的第二种写法,配置为true把该路由组件收到的所有params参数以props的形式传给Detail组件
      props: true
      
      //props的第三种写法,值为函数,可以把return的key-value值以props的形式传递给Detail组件
      props: function ($route) {
             return {
             id: $route.query.id,
             title: $route.query.title
             }
      }
      

    <route-link>的replace属性

    1. 作用:控制路由跳转时操作浏览器历史记录的模块

    2. 浏览器历史记录以栈的方式存储,有个指针指向栈顶也就是当前页面;有两种写入方式:pushreplace,push是往栈顶追加历史记录,replace是替换掉当前的记录,路由跳转时默认为push

    3. 如何开启replace模式:

      <router-link replace class="list-group-item" active-class="active" to="/home">Home</router-link>
      

    编程式路由导航

    1. 作用:不借助<router-link>标签实现路由跳转,让路由跳转更加灵活可以用别的标签再利用事件进行触发

    2. 具体编码:

      // 利用$router的两个api实现路由跳转: push,replace 
      methods: {
                  pushForward(message) {
                      this.$router.push({
                          name: 'xiangqing',
                          query: {
                              id: message.id,
                              title: message.title
                          }
                      })
                  },
                  replaceForward(message) {
                      this.$router.replace({
                          name: 'xiangqing',
                          query: {
                              id: message.id,
                              title: message.title
                          }
                      })
                  }
      }
      
      this.$router.forward()  //前进
      this.$router.back() //后退
      this.$router.go(-1) // 前进或后退几步
      

    缓存路由组件

    • 作用:让不展示的组件保持挂载不被销毁

    • 具体编码

      <keep-alive include="News">
              <router-view></router-view>
      </keep-alive>
      

      include属性控制的是展示在这个位置的组件中具体要缓存哪个组件,如果不配置就缓存所有展示到的组件

    两个新生命周期钩子

    • 作用:用于捕获路由组件的激活状态,路由组件独有,可以用在需要缓存组件的同时开启或停掉组件的一些工作
    • 具体名字
      • activated路由组件被激活时触发
      • deactivated路由组件失活时触发

    路由守卫

    1. 作用:对路由进行权限控制

    2. 分类:全局守卫、独享守卫、组件内守卫

      • 全局守卫

        // 全局前置路由守卫---初始化时被调用、每次路由切换之前被调用,拦截所有的路由切换请求
        router.beforeEach((to, from, next) => {
            console.log('前置路由守卫',to, from)
            if (to.meta.isAuth) {  //判断是否需要进行权限控制
                if (localStorage.getItem('school') === 'atguigu') { //权限控制的具体规则
                    // 满足条件放行
                    next()
                } else {
                    alert('学校名不对,无权查看')
                }
            } else {
                next()
            }
        })
        
        // 全局后置路由守卫---初始化时被调用、每次路由切换之后被调用,用的不多可以做一些收尾工作
        router.afterEach((to, from) => {
            console.log('全局后置路由守卫', to, from)
            if (to.meta.title) {
                document.title = to.meta.title  //修改网页的title
            } else {
                document.title = 'vue_test'
            }
        })
        
      • 独享守卫:组件独有的守卫配置

        // 作为一个路由组件配置项
        {
            name: 'xinwen',
            path: 'news',  // 底层监测到子路由时会自动加上/
            component: News,
            meta: {isAuth: true, title: '新闻'},
            beforeEnter: (to, from, next) => {
        	console.log('独享前置路由守卫', to, from)
        	if (to.meta.isAuth) {
        	    if (localStorage.getItem('school') === 'atguigu') {
        		// 满足条件放行
        		next()
        	    } else {
        		alert('学校名不对,无权查看')
        	    }
        	} else {
        	    next()
        	}
            }
        }
        
      • 组件内守卫

        // 进入守卫:通过路由规则,进入组件之前被调用
        beforeRouteEnter(to, from, next) {
        
        }
        // 离开守卫:通过路由规则,即将离开该组件时被调用
        beforeRouteLeave(to, from, next) {
        
        }
        /* 
         这两个守卫可以写一些该路由组件独有的逻辑
        */
        

        注意:要和前置、后置路由守卫区分开,后置路由守卫是切换进入组件之后的后置操作

    路由器的两种工作模式

    1. 路由器的hash模式:
      • 兼容性好,适配绝大多数浏览器
      • 地址中永远带着#号,不美观。
      • 若以后将地址通过第三方手机app分享,若app校验严格,地址会被标识为不合法
    2. history模式:
      • 地址干净,美观不带#号
      • 兼容性和hash模式相比略差
      • 应用部署上线时需要后端人员支持,解决刷新页面时将/资源后的字符都当成请求参数造成服务端404的问题
    1. 对于一个url来说,什么是hash值?----- #及其后面的内容就是hash值。
    2. hash值不会包含在HTTP请求中即hash值不会带给服务器。

    十八、Element UI使用

    基本使用

    • npm安装

      npm i element-ui -S

    • 引入组件库

      1. 全局引入(不推荐)

        import Vue from 'vue';
        import ElementUI from 'element-ui';
        import 'element-ui/lib/theme-chalk/index.css';
        import App from './App.vue';
        
        Vue.use(ElementUI);
        
      2. 按需引入

        借助 babel-plugin-component,我们可以只引入需要的组件,以达到减小项目体积的目的。

        • 安装babel-plugin-component

          npm install babel-plugin-component -D
          
        • 修改babel.config.js文件

          {
            "presets": [["es2015", { "modules": false }]],
            "plugins": [
              [
                "component",
                {
                  "libraryName": "element-ui",
                  "styleLibraryName": "theme-chalk"
                }
              ]
            ]
          }
          
        • 引入需要的组件

          import { Button, Select } from 'element-ui';
          import App from './App.vue';
          
          Vue.component(Button.name, Button);
          Vue.component(Select.name, Select);
          /* 或写为
           * Vue.use(Button)
           * Vue.use(Select)
           */
          

        如果在这里出现问题可以去babel-plugin-component的github找到与当前脚手架相匹配的配置信息

      3. Element UI官网复制粘贴需要的组件就能使用了

      注意:按需引入组件不需要额外引入组件的样式,相关的样式会自动导入

    修改样式

    • 修改全局样式

      1. 新建全局样式表

      新建 global.css 文件,并在 main.js 中引入。 global.css 文件一般都放在 src->assets 静态资源文件夹下的 style 文件夹下,在 main.js 的引用写法如下:

      import "./assets/style/global.css";
      

      在 global.css 文件中写的样式,无论在哪一个 vue 单页面都会覆盖 ElementUI 默认的样式。

      适合于对elementUI整体的修改,比如整体配色的修改;

    • 修改单个组件样式

      1. 使用 /deep/,前面为修改组件类名的父级组件样式类名,后面为修改组件的类名,里面是样式的修改element ui组件默认class就是标签名
      .dialogPage   /deep/  .el-dialog  {
      	margin-left: 20px  !important;
      }
      

      /deep/ 的方式可以很方便的在vue组件中修改默认样式,也不会于其他页面有冲突。

      1. 在当前的vue单页面的<style>标签后,添加一对新的<style>标签,新的<style>标签中不要添加scoped属性。这样就会覆盖element ui原来的样式
      <style scoped>
          .left {
              padding-top: 25px;
          }
      </style>
      <style>
          .left {
              background-color: red;
          }
      </style>
      

      注意:这样实际是修改了全局的样式,同class名的都被影响会引发冲突

    属性和事件

    • Element UI有很多自定的属性和事件,可以用来传递数据修改配置及事件响应
      • 属性在每个组件的下面Attributes有介绍,可以通过v-bind也就是:去把自己定义的数据传递给组件
      • 事件在每个组件的下面Events中有介绍,可以通过v-on也就是@去绑定这个事件,当触发了这个事件可以去执行相应的回调
      • 有些组件身上也有特殊的方法,比如表单form对应的就有validate方法用于校验整个表单是否符合规范,当收集到这个组件的对象时就可以使用这个方法,方法可以在Methods中找到