Skip to content

Vue 2.0

Vue.js 是一个用于构建用户界面的渐进式JavaScript框架,它采用组件化和响应式数据绑定的方式,简化了Web应用程序的开发。

一、Vue核心

1、Object.defineProperty

Object.defineProperty() 静态方法会直接在一个对象上定义一个新属性,或修改其现有属性,并返回此对象。这是Vue核心底层逻辑。

语法

javascript
Object.defineProperty(obj, prop, descriptor)

示例

javascript
let num = 18;
let person = {
  name: 'zs',
  sex: 'male'
};

Object.defineProperty(person, 'age', {
  value: 18,  //添加age属性的value值
  enumerbale: true,  //控制属性是否可以枚举,默认为false
  writeable: true,  //控制属性是否可以被修改,默认为false
  configurable: true,  //控制属性是否可以被删除,默认为false
  
  //当读取person的age属性时,get函数(getter)就会被调用,且返回值 就是age的值
  get(){
    return num;
  },
  //当修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值
  set(value){
    num =value;
  }
});

TIP

定义或修改多个属性:

javascript
Object.defineProperties(obj, props)

2、模板语法

插值语法

最基本的数据绑定形式是文本插值,它使用的是“Mustache”语法 (即双大括号):

  • 功能:用过解析标签体内容
  • 语法:,xxx是 js 表达式,且可以直接读取到 data 中的所有属性
html
<span>Message: {{ msg }}</span>

指令语法

v-bind:href = 'xxxx' 或 :href='xxxx' ,xxxx是 js 表达式被解析,且可以直接读取到 data 中的所有属性

html
<a v-bind:href="url"> ... </a>

<!-- 简写 -->
<a :href="url"> ... </a>

3、数据绑定

单向数据绑定

  • 语法:v-bind:href ="xxx" 或简写为 :href="xxx"
  • 特点:数据只能从 data 流向页面

双向数据绑定

  • 语法:v-mode:value="xxx" 或简写为 v-model="xxx"
  • 特点:数据不仅能从 data 流向页面,还能从页面流向 data,且只能应用在表单类元素(即value值)

v-model修饰符

  • .lazy:懒加载
  • .trim:去除前后空格
  • .number:将字符串数字转换为数值

4、el和data的两种写法

html
<div id = "root">
  <h1>您好,{{name}}</h1>
</div>

<script type="text/javascript">
Vue.config.productionTip = false;
//写法一
new Vue({
  el: '#root',
  data:{
    name: 'kite'
  }
});
//写法二:推荐
const vm = new Vue({ 
  data(){  //组件内必须这种写法
    return {
      name:'kite'
    }
  }
});
vm.$mount('#root');
</script>

5、事件处理

事件基本使用

  • v-on:xxx="函数名"
  • @xxx="函数名"
  • @xxx="函数名(参数)"
  • 默认事件形参:event
  • 隐含属性对象:$event

TIP

  • xxx为事件名,如change、click、mouseover、mouseout、keydown、keyup、load、scorll、wheel等;
  • 事件的回调需要配置在methods对象中,最终会在vm上;
  • methods中配置的函数,不要用箭头函数!否则this指向windows对象而不是vm或组件实例对象;
  • @click="demo"和@click="demo($event)"效果是一样的,但后者可以传参;
html
<div id="root">
  <h2>您好,{{name}}</h2>
  <button v-on:click="showInfo1">点我(不传参)</button>
  <button @click="showInfo2(88)">点我(传参)</button>
</div>

<script type="text/javascript">
new Vue({
  el:'root',
  data:{
    name: 'kite'
  },
  methods:{
    showInfo1(event){
      console.log(event.target);
    },
    showInfo2(num){
      console.log(num);
    }
  }
});
</script>

事件修饰符

  • prevent:阻止默认事件
  • stop:阻止事件冒泡
  • once:事件只触发一次
  • capture:使用事件的捕获模式
  • self:只有event.target是当前操作的元素时才触发事件
  • passive:事件的默认行为为立即执行,无需等待事件回调执行完毕
html
<div id="root">
  <!-- 第一种写法 -->
  <a href="https://www.baidu.com" @click.prevent="showInfo">点我干嘛</a>
</div>

<script type="text/javascript">
new Vue({
  el: '#root',
  methods: {
    showInfo(e){
      //e.preventDefault(); //第二种写法
      console.log("滚开");
    }
  }
})
</script>

按键修饰符

默认按键别名
  • enter:回车
  • delete:删除
  • esc:退出
  • space:空格
  • tab:换行
  • up:上
  • down:下
  • left:左
  • right:右
  • caps-lock:上一页
html
<div id="root">
i  <input type="text" @click.enter="showInfo">
</div>

<script type="text/javascript">
new Vue({
  el:'root',
  methods: {
    showInfo(e){
      conosle.log(e.keyCode, e.key)
    }
  }
})
</script>

TIP

ctrl/alt/shift/win特殊用法:

  • 配合keyup使用:按键的同时,再按下其他键,随后释放其他键,事件方可触发;
  • 配合keydown使用:正常触发事件;
自定义按键别名
html
<div id="root">
  <input type="text" @click.huiche="showInfo">
</div>

<script type="text/javascript">
  Vue.config.keyCodes.huiche = 13;  //自定义按键别名
  new Vue({
    el:'root',
    methods: {
      showInfo(e){
        conosle.log(e.keyCode, e.key)
      }
    }
  })
  
</script>

6、computed计算属性

定义

要显示的数据不存在,要通过计算得来,且在 computed 对象中定义计算属性,在页面中使用来显示计算的结果。

原理

底层借助了Object.defineproperty方法提供的getter和setter。

getter和setter调用

  • get函数:
    • 初次读取时会调用一次
      • 当依赖的数据发生改变时会被再次调用
  • set函数
    • 计算属性被修改时会调用

与methods区别

  • computed:内部有缓存机制(复用)
  • methods:内部无缓存机制
html
<div id="root">
  姓:<input type="text" v-model="fistName">
  名:<input type="text" v-mode="lastName">
  全名:{{fullName}}
</div>

<script type="text/javascript">
new Vue({
  el:'#root',
  data:{
    fistName:'张',
    lastName:'三'
  },
  methods:{
    demo(){
      //....
    }
  },
  computed:{
    //完整写法:可读可写
    fullName:{
      get(){
        return this.firstName + '-' + this.lastName;
      },
      set(value){
       const arr = value.split('-');
       this.fisrtName = arr[0];
       this.lastName = arr[1];
      }
    }
    //简写:仅可读,不可写
    fullName(){
      return this.firstName + '-' + this.lastName;
    }
  }
})
</script>

7、watch监听属性

通过通过 vm 对象的$watch()或 watch 配置来监视指定的属性,当属性变化时, 回调函数自动调用, 在函数内部进行计算。

html
<div id="root">
  <h1>今天的天气很{{info}}</h2>
  <button @click="change"></button>
</div>

<script type="text/javascript">
  const vm = new Vue({
    el: '#root',
    data:{
      isHot:true
    },
    methods:{
      change(){
        this.isHot=!this.isHot;
      }
    },
    computed:{
      info(){
        return this.isHot?'凉爽':'严热';
      }
    },
    //第一种写法
    watch:{
      //完整写法
      isHot:{  //监听data属性
        immediate:true,  //初始化时让handler调用一下
        handler(newValue,oldValue){
          console.log('isHot被修改了',newValue,oldValue);
        }
      },
      info:{  //监听computed属性
        deep:true,  //开启深度监听,可以监听对象内部值的改变
        handler(newValue,oldValue){
          console.log('info被修改了',newValue,oldValue);
        }
      }
      //简写:仅回调函数存在,没有其他配置项
      isHot(newValue,oldValue){
        console.log('isHot被修改了',newValue,oldValue);
      }
    }
  })
  //第二种写法
  vm.$watch('isHot',{
    immediate:true,
    handler(newValue,oldValue){
      console.log('info被修改了',newValue,oldValue);
    }
  })
</script>

TIP

watch与computed之间的区别

  • computed能完成的功能,watch都可以完成;
  • watch能完成的功能,computed不一定能完成;

8、class与style绑定

语法:v-bind:class='xxx'/v-bind:style='xxx' 或 :class='xxx'/:style='xxx'

  • xxx是字符串: 'classA' ,适用于类名不确定且需要动态指定
  • xxx是对象: {classA:isA, classB: isB},适用于类名确定、类名数量确定且动态指定
  • xxx是数组: ['classA', 'classB'],适用于类名不确定、类名数量不确定
html
<style>
  .base {...}
  .mood {...}
</style>

<body>
  <div id="root">
    <div class="basic" :class="mood">{{name}}</div>
    <div class="basic" :class="classArr" :style="styleArr">{{name}}</div>
    <div class="basic" :class="classObj" :style="styleObj">{{name}}</div>
  </div>
</body>

<scirpt type="text/javascirpt">
  new Vue({
    el:'#root',
    data:{
      name:'Vue学生',
      mood:'happy',
      classArr:['happy','phone','money'],
      classObj:{
        happy:true,
        phone:false,
        money:true
      },
      styleArr:[
        {
          fontSize:40px,
          color:pink
        },
        {
          backgroundColor: hotpink
        }
      ],
      styleObj:{
        fontSize:30px,
        color:red
      }
    }
  })
</script>

9、条件渲染

html
<div id="app">
    <h2 v-show="true">猜猜我是谁</h2>

    <h2 v-if="false">猜猜你是谁</h2>
    <h2 v-else-if="false">要你管</h2>
    <h2 v-else>再见</h2>
</div>

10、列表渲染

html
<div id="root">
  <ul>
  //:key不指定,默认为:key="index",即索引号
    <li v-for="p in persons" :key="p.id">
      {{index}}:{{p.name}}-{{p.age}}
    </li>
  </ul>
</div>

<script type="text/javascript">
  new Vue({
    el:'#root',
    data:{
      persons:[
        {id:'001',name:'小明',age:19},
        {id:'002',name:'小红',age:20},
        {id:'003',name:'小虎',age:17}
      ]
    }
  })
</script>

列表过滤与排序

html
<div id="root">
  <input type="text" placeholder="请输入名字" v-model="keywords">
  <button @click="sortType = 0">原顺序</button>
  <button @click="sortType = 1">年龄升序</button>
  <button @click="sortType = 2">年龄降序</button>
  <ul>
    <li v-for="(p,index) of filterPersons" :key="index">
      {{p.name}}-{{p.age}}-{{p.sex}}
    </li>
  </ul>
</div>

<script type="text/javascript">
  new Vue({
    el:'#root',
    data:{
      keywords:'',
      sortType:0,
      persons:[
      {id:'1',name:'马冬梅',age:30,sex: '女'},
      {id:'2',name:'周冬雨',age:27,sex:'女'},
      {id:'3',name:'周杰伦',age:36,sex:'男'},
      {id:'4',name:'温兆伦',age:38,sex:'男'}
      ]
    },
    computed:{
      filterPersons(){
        const personsArr = this.persons.filter((p)=>{
          return p.name.indexOf(this.keywords) !== -1;
        })
        if(this.sortType){
          personsArr.sort((a,b)=>{
            return this.sortType ===1 ? b.age - a.age : a.age - b.age;
          })
        }
        return personsArr;
      }
    }
  })
</script>

数据监听

  • vue会监听data中所有层次的数据。
  • vue如何监听对象中的数据:
    • 通过setter实现监听,且要在new Vue时就传入要监听的数据 *对象中后追加的属性,Vue默认不做响应式处理 * 如需给后添加的属性做响应式处理,请使用如下API *Vue.set(target, propertyName/index, value) * vm.$set(target, propertyName/index, value)
  • 如何监听数组中的数据
    • 通过包裹组更新元素的方法实现,本质就是做了2件事 *调用原生对应的方法对数据进行更新 * 重新解析模板,进而更新页面
  • 在Vue修改数组中的某个元素一定要用如下方法
    • 使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
      • Vue.set(target, propertyName/index, value)
      • vm.$set(target, propertyName/index, value)

TIP

Vue.set()和vm$set()不能给vm或vm的根数据对象添加属性。

11、 收集表单数据

html
<div id="root">
  <form @submit.prevent="demo">、
  <!-- trim:删除前后空格 -->
  用户名:<input type="text" v-model.trim="userInfo.account">
  密码:<input type="password" v-model="userInfo.password">
  <!-- number:将字符转换为数字 -->
  年龄:<input type="number" v-model.number="userInfo.age">
  性别:
  <input type="radio" name="sex" v-model="userInfo.sex" value="male">男
  <input type="radio" name="sex" v-model="userInfo.sex" value="female">女
  爱好:
  <input type="checkbox" v-model="userInfo.hobby" value="study">学习
  <input type="checkbox" v-model="userInfo.hobby" value="game">游戏
  <input type="checkbox" v-model="userInfo.hobby" value="music">音乐
  所在地区:
  <select v-model="userInfo.city">
    <option value="">请选择校区</option>
    <option value="beijing">北京</option>
    <option value="shanghai">上海</option>
    <option value="nanjing">南京</option>
    <option value="wuhan">武汉</option>
  </select>
  内容:
  <!-- lazy:失去焦点时触发 -->
  <textarea v-model.lazy="userInfo.other"></textarea>
  <input type="checkbox" v-model="userInfo.isAgree">阅读并同意<a href="javascript:;"《用户协议》</a>
  <input type="submit" value="提交">
  </form>
</div>

<script type="text/javascript">
  Vue.config.productionTip=false;
  new Vue({
    el:'#root',
    data:{
      userInfo:{
        account:'',
        password:'',
        age:'',
        sex:'female',
        hobby:[],
        city:'nanjing',
        content:'',
        isAgree:false
      }
    }
    methods:{
      demo(){
        console.log(this.userInfo);
      }
    }
  })
</script>

12、过滤器

html
<div id="root">
  <h2>北京时间:{{time | timeFormater}}</h2>
  <h2>北京时间:{{time | timeFormater('YYYY-MM-DD')}}</h2>
  <h2>北京时间:{{time | timeFormater('YYYY-MM-DD') | mySlice}}</h2>
  <h3 :hi="msg | mySlice">世界</h3>
</div>

<div id="app">
  <h2>{{msg | mySlice}}</h2>
</div>

<script type="text/javascript">
  Vue.config.productionTip=false;
  //全局过滤器
  Vue.filter('mySlice', function(value){
    return value.slice(0,4);
  });
  
  new Vue({
    el:'#root',
    data:{
      time:1621561377603,
      msg:'你们好呀'
    },
    filters:{
      timeFormater(value,str='YYYY年MM月DD日 HH:mm:ss'){
      //需要引入第三方库day.js
        return dayjs(value).format(str);
      }
    }
  });
  
  new Vue({
    el:'#app',
    data:{
      msg:'hello'
    }
  })
</script>

13、指令

内置指令

  • v-text : 更新元素的 innerText
  • v-html : 更新元素的 innerHTML
  • v-if : 如果为 true, 当前标签才会输出到页面
  • v-else: 如果为 false, 当前标签才会输出到页面
  • v-show : 通过控制 display 样式来控制显示/隐藏
  • v-for : 遍历数组/对象
  • v-on : 绑定事件监听, 一般简写为@
  • v-bind : 绑定解析表达式, 可以省略 v-bind
  • v-model : 双向数据绑定
  • v-cloak : 无赋值,防止闪现, 与 css 配合: [v-cloak]
  • v-once:无赋值,执行一次后变为静态值
  • v-pre:无赋值,跳过Vue编译过程

自定义指令

局部自定义指令
javascript
//使用指令
v-my-directive='xxx'

directives:{
  //注册函数式指令:指令与元素成功绑定时执行,指令所在的模板被重新解析时执行
  'my-directive'(el, binding){
    el.innerHTML = binding.value.toupperCase();
  },
  //注册对象式指令
  'my-directive':{
    //指令与元素成功绑定时执行
    bind(el, binding){
      el.innerHTML = binding.value.toupperCase();
    },
    //指令所在元素被插入页面时执行
    inserted(el, binding){
      el.innerHTML = binding.value.toupperCase();
    },
    //指令所在的模板被重新解析时执行
    update(el, binding){
      el.innerHTML = binding.value.toupperCase();
    }    
  }
}
全部自定义指令
javascript
//使用指令
v-my-directive='xxx'

//注册函数式指令
Vue.directive('my-directive', function(el, binding){
  el.innerHTML = binding.value.toupperCase();
})

//注册对象式指令
Vue.directive('my-directive',{
    bind(el, binding){
      el.innerHTML = binding.value.toupperCase();
    },
    inserted(el, binding){
      el.innerHTML = binding.value.toupperCase();
    },
    update(el, binding){
      el.innerHTML = binding.value.toupperCase();
    }
  })

14、生命周期函数

创建阶段

  • beforeCreate:数据创建完成之前
  • created():数据创建完成之后

挂载阶段

  • beforeMount:模板渲染完成之前
  • mounted:模板渲染完成之后

更新阶段

  • beforeUpdate:数据更新完成之前,模板未渲染
  • updated:数据更新完成之后,模板已渲染

销毁阶段

  • beforeDestory:模板卸载完成之前
  • destoryed:模板卸载完成之后

TIP

  • mounted:发送ajax请求、启动定时器、绑定自定义事件、订阅消息等;
  • beforeDestroy:清除定时器、解绑自定义事件、取消订阅消息等;
  • 销毁后借助Vue开发者工具看不到任何信息;
  • 销毁后自定义事件支失效,但原生DOM事件依然有效;
  • 一般不会在beforeDestroy操作数据,因为即使操作数据,也不会再触发更新流程;

二、Vue组件

在 Vue 组件中,为了保证每个组件实例,维护独立的一份数据对象,data 必须是一个函数形式。每次创建新的组件实例,都会新执行一次 data 函数,得到一个新对象。

1、非单文件组件

注册局部组件

html
<div id="root">
    <h1>{{msg}}</h1>
    <!--
* 第三步:使用组件
* <school></school>:双标签可以编译多次
* <school/>:单标签且不使用脚手架时,会导致后续组件无法编译
* <my-school></my-school>:配合脚手架使用,否则无法编译
* <MySchool></MySchool>:配合脚手架使用,否则无法编译
-->
    <school></school>
    <school />
    <my-school></my-school>
    <MySchool></MySchool>
</div>

<div id="wrap">
    <app></app>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script type='text/javascript'>
    Vue.config.productionTip = false;

    //第一步:创建局部组件
    const school = Vue.extend({
        name: 'School',
        data() {
            return {
                schoolName: '和仁小学',
                address: '和仁付'
            }
        },
        template: `
<div>
<h2>学校名称:{{schoolName}}</h2>
<h3>学校地址:{{address}}</h3>
</div>
`
    })
    const student = Vue.extend({
        template: `
            <div>
                <h3>学生姓名:{{studentName}}</h3>
                <h3>学生年龄:{{age}}</h3>
            </div>
        `,
        data() {
            return {
                studentName: '李四',
                age: 17
            }
        }
    })
    const sdt = Vue.extend({
        name: 'Sdt',
        data() {
            return {
                studentName: '张三',
                age: 18
            }
        },
        template: `
        <div>
            <h2>学生名称:{{studentName}}</h2>
            <h3>学生年龄:{{age}}</h3>
        </div>
        `
    })

    const app = Vue.extend({
        name: 'App',
        template: `
<student></student >
`,
        components: {
            student: sdt
        }
    })

    //第二步:注册局部组件
    new Vue({
        el: '#root',
        data: {
            msg: '欢迎学习Vue!'
        },
        components: {
            school,
            'my-school': school,
            MySchool: school
        }
    })

    new Vue({
        el: '#wrap',
        components: {
            app
        }
    })
</script>

注册全局组件

html
<div id="main">
    <!-- 第三步:使用全局组件 -->
    <Hello></Hello>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script type='text/javascript'>
    Vue.config.productionTip = false;

    //第一步:创建全局组件
    const Hello = Vue.extend({
        name: 'coency',  //Vue开发者工具呈现名称
        data() {  //组件必须使用函数式
            return { slogan: 'Hello World!' }
        },
        template: `
        <h2>slogan:{{slogan}}</h2>
    `
    })

    // 第二步:注册全局组件
    Vue.component('Hello', Hello)

    // 挂载
    new Vue({
        el: '#main'
    })
</script>

TIP

VueComponent

  • school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的。
  • 我们只需要写<school/><school></school>,Vue解析时会帮我们创建school组件的实例对象,即vue帮我们执行的:new VueComponent(options).
  • 特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent! !!!
  • 关于this指向:
    • 组件配置中:data函数、methods中的函数、watch中的函数、computed中的函数它们的this均是【VueComponent实例对象】
    • new Vue(options)配置中:data函数、methods中的函数、watch中的函数、computed中的函数它们的this均是【Vue实例对象】
  • VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象),Vue的实例对象,以后简称vm
  • 一个重要的内置关系:VueComponent.prototype.proto == Vue.prototype

2、单文件组件

.vue 文件组成(3 个部分)

HTML模板页面
html
<template>
  <!-- 页面模板 -->
</template>
JS 模块对象
javascript
<script>
export default {
  name:'',
  data() {
    return {}
  },
  methods: {},
  computed: {},
  components: {}
}
</script>
Style样式
html
<style>
  /* 样式定义 */
</style>

基本使用

  • 引入组件
  • 映射成标签
  • 使用组件标签

3、动态组件

html
<template>
  <div id="app">
    <component :is="componentId"></component>
    <button @click="bool = !bool">修改bool</button>
  </div>
</template>

<script>
import Song from "@/components/song.vue";
import Singer from "@/components/singer.vue";
export default {
  name: "App",
  data() {
    return {
      bool: true,
    };
  },
  computed: {
    componentId() {
      return this.bool ? "Singer" : "Song";
    },
  },
  components: { Song, Singer },
};
</script>

4、缓存动态组件

html
 <!-- 默认缓存所有的动态组件  -->
<keep-alive>
  <component :is="componentId"></component>
</keep-alive>



 <!-- 只缓存 Song 组件  -->
<keep-alive include="Song">
  <component :is="componentId"></component>
</keep-alive>



<!-- 不缓存Song的组件  -->
<keep-alive exclude="Song">
  <component :is="componentId"></component>
</keep-alive>

三、Vue脚手架

1、安装脚手架

第一步(仅第一次执行):全局安装@vue/cli。

javascript
npm install -g @vue/cli

第二步:切换到你要创建项目的目录,然后使用命令创建项目

javascript
vue create 项目文件夹

第三步:启动项目

javascript
npm run serve

2、render函数

javascript
new Vue({
  el:'#root',
  //简写
  render: h => h(App)
  /**
  * 完整写法
  * render: render(createElement){
  *   return createElement('h1','你好')
  * }
  **/
})

TIP

vue.js与vue.runtime.xxx.js区别

  • vue.js是完整版的vue,包含核心功能+模板解析器
  • vue.runtime.xxx.js是运行版的vue,只包含核心功能,没有模板解析器,所有不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容。

3、配置文件

查看Vue脚手架默认配置文件

javascript
vue inspect > output.js

修改Vue脚手架默认配置文件

新建一个vue.config.js文件

javascript
module.exports = {
  pages: {
    index: {
      // page 的入口
      entry: 'src/index/main.js',
      // 模板来源
      template: 'public/index.html',
      // 在 dist/index.html 的输出
      filename: 'index.html',
      // 当使用 title 选项时,
      // template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>
      title: 'Index Page',
      // 在这个页面中包含的块,默认情况下会包含
      // 提取出来的通用 chunk 和 vendor chunk。
      chunks: ['chunk-vendors', 'chunk-common', 'index']
    },
    // 当使用只有入口的字符串格式时,
    // 模板会被推导为 `public/subpage.html`
    // 并且如果找不到的话,就回退到 `public/index.html`。
    // 输出文件名会被推导为 `subpage.html`。
    subpage: 'src/subpage/main.js'
  }
}

路径写法

html
<!-- 相对路径 -->
<img src="./assets/logo.png" alt="">

<!-- 根路径 -->
<img src="@/assets/logo.png" alt="">

<!-- 动态绑定路径 -->
<img src="srcURL" alt="">

<script>
    export default {
        data() {
            return {
                srcURL: require('@/assets/logo.png')
            }
        }
    }
</script>

4、ref属性

用于给节点打标识。

html
<template>
  <div>
    <h1 v-text="msg" ref="title"></h1>
    <buttom ref="btn" @click="showDom">点击显示DOM元素</buttom>
    <School ref="sch"/>
  </div>
</template>

<script>
   import School from './components/School'
   export default {
     name:'App',
     components:{School},
     data(){
       return {msg:'欢迎学习vue'}
     },
     methods:{
       showDom(){
         console.log(this.$refs.title);
         console.log(this.$refs.btn);
         console.log(this.$refs.sch)
       }
     }
   }
</script>

TIP

  • 被用来给元素或子组件注册引用信息(id的替代者)
  • 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
  • 使用方式:
    • 标识:<h1 ref="xxx">.....</h1><School ref="xxx"></School>
    • 获取:this.$refs.xxx

5、mixin混入属性

可以把多个组件共用的配置提出成一个混入对象。

html
<!-- School.vue -->
<template>
  <div>
    <h2 @click="showName">学校名称:{{name}}</h2>
    <h2>学校地址:{{city}}</h2>
  </div>
</template>

<script>
  import {mixin} from './mixin'
  export default {
    name:'School',
    data(){
      return {
        name:'南京大学',
        city:'南京'
      }
    },
    mixins:[mixin]  //局部混入
  }
</script>

<!-- Student.vue -->
<template>
  <div>
    <h2 @click="showName">学生姓名:{{name}}</h2>
    <h2>学生年龄:{{age}}</h2>
  </div>
</template>

<script>
  import {mixin} from './mixin'
  export default {
    name:'Student',
    data(){
      return {
        name:'张三',
        age:18
      }
    },
    mixins:[mixin]  //局部混入
  }
</script>

<!-- mixin.js -->
<script>
  export const mixin = {
    methods:{
      showName(){
        console.log(this.name)
      }
    }
  }
  
  // main.js全局混入
  import Vue from 'vue'
  import App from './App'
  import {mixin} from './mixin'
  Vue.mixin(mixin)
  
  new Vue({
    render: h => h(App)
  }).$mount('#root')
</script>

6、plugins插件属性

javascript
//plugins.js
export default {
  install(Vue, params){
    Vue.prototype.demo = () => {console.log('123')}
}

//main.js
import plugins from './plugins.js'
Vue.use(plugins, params)  //使用插件

TIP

一般把通用型的方法写在 utils 文件夹内,如 密码验证函数、数组去重、日期格式化函数等。

7、scoped样式

让css样式只在局部作用域生效,防止类名冲突。

html
<style scoped>
</style>

8、props属性

用于父组件给子组件传递数据。

html
<!-- App.vue -->
<template>
  <div>
    <Student name="张三" sex="男" :age="18"/>
  </div>
</template>

<script>
  import Student from './component/Student'
  export default {
    name:'App',
    components:{Student}
  }
</script>

<!-- Student.vue -->
<template>
  <div>
    <h1>{{msg}}</h1>
    <h2>学生姓名:{{name}}</h2>
    <h2>学生性别:{{sex}}</h2>
    <h2>学生年龄:{{age+1}}</h2>
  </div>
</template>

<script>
  export default {
    name:'Student',
    data(){
      msg:'学生信息表',
    },
    //方式一:数组写法,仅指定名称
    props:['name','sex','age']
    //方式二:对象写法,指定名称 /类型
    props:{
      name:String,
      sex:String,
      age:Number
    }
    //方式二:对象写法,指定名称 /类型 /必要性 /默认值 /自定义
    props:{
      name:{
        type:String,  //字符串型
        required:true  //必填
      },
      sex:{
        type:String,  //字符型
        default:20  //不填,默认为20
      },
      age:{
        type:Number,  //数值型
        validator(value){ //自定义
          if(value >= 18){
            return true
          }else {
            return false
          }
        }
      }
    }
  }
</script>

TIP

  • 所有的 props 都遵循着单向绑定原则,props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递。这避免了子组件意外修改父组件的状态的情况,不然应用的数据流将很容易变得混乱而难以理解。
  • props是只读,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告;如果业务需要修改,则复制props的内容到data中,然后去修改data中的数据。
  • props 没有接收的标签属性,会出现在 根节点和 vc 的 $attrs 上。

9、组件自定义事件

绑定自定义事件

html
<!-- 方式一 -->
<Student @addTodo="addTodo"></Student>
<script>
this.$emit('addTodo',this.addTodo)
</script>
<!-- 方式二 -->
<Student ref="list"></Student>
<script>
this.$refs.list.$on('addTodo',this.addTodo)
</script>

解绑自定义事件

javascript
//解绑单个自定义事件
this.$off('addTodo')
//解绑多个自定义事件
this.$off(['addTodo','updateTodo'])

TIP

  • —种组件间通信的方式,适用于子组件向父组件传数据,子组件给父组件传数据,要在父组件中给子组件绑定自定义事件且事件的回调在父组件中;
  • 要自定义事件只能触发一次,需要使用once修饰符或$once方法;
  • 组件上要绑定原生DOM事件,需要使用native修饰符;
  • this.$refs.xxx.$on('eventName', callback)绑定自定义事件时,回调要么配置在methods中,要么使用箭头函数,否则this指向有问题;

10、组件 v-mode

.sync 修饰符

11、全局事件总线

javascript
//第一 步:在main.js中把vm对象挂载到Vue原型上
new Vue({
   .....
  beforeCreate(){
    Vue.prototype.$bus = this;
  },
  .....
})

//第二步:给vm注册上自定义事件(需要被改变的组件 需要被接收值的组件),如App.vue
new Vue({
  ......
  methods(){
    vcName(params){......}
  }
  ......
  mounted(){
    this.$bus.$on('vcName',this.vcName);//接收数据
  },
  beforeDestroy(){
    this.$bus.$off('vcName');
  }
})
//第三步:触发vm上的自定义事件(需要传值给别人的组件),如Student.vue
new Vue({
  .....
  methods(){
    handleName(params){
      this.$globalEventBus.$emit('vcName',params); //提供数据
    }
  }
})

TIP

  • 一种组件间通信的方式,适用于任意组件间通信
  • 最好在beforeDestroy钩子中,用$off解绑当前组件所用到的事件

12、$nextTick

html
<div>
  <button @click="showIpt">点击显示 input框</button>
  <input type="text" v-if="bool" ref="ipt" />
</div>

<script>
  new Vue({
    .....
    showIpt() {
      this.bool = true;
      console.log(this.$refs);
      //使得input具有焦点 得等input渲染完毕之后 再执行
    
      //dom更新完毕 调用的钩子函数
      this.$nextTick(() => {
        this.$refs.ipt.focus();
      });
    }
  })
</script>

TIP

  • 在下一次DOM更新结束后执行其指定的回调
  • 当改变数据后,要基于更新后的新DOM进行操作时,要在nextTick所指定的回调函数中执行

13、动画

html
<template>
  <!-- name不写时,动画默认为一样,appear不写时,默认进入无动画 -->
  <transition name="todo" appear>
    <div v-show="isShow">Hello World!</div>
  </transition>
</tempalte>

<script>
  new Vue({
    el:'#root',
    data:{
      isShow:'true'
    }
  })
</script>

<style>
  /* todo为template的name,若name不一样,可设置不同的动画*/
  .todo-enter-active {
    transition: atguigu 1s linear;
  }
  .todo-leave-active {
    transition: atguigu 1s linear reverse
  }
  @keyframes atguigu {
    from {
      transform: translateX(100%)
    }
    to {
      transform: translateX(0)
    }
  }
</style>

14、过渡

单个元素过渡

html
<template>
  <transition name="todo" appear>
    <div v-show="isShow">Hello World!</div>
  </transition>
</tempalte>

<script>
  new Vue({
     el:'#root',
    data:{
      isShow:'true'
    }
  })
</script>

<style>
.todo-enter, .todo-leave-to {
  transform: translateX(100%);
}
.todo-leave, .todo-enter-to{
  transform: translateX(0);
}
.todo-enter-active, .todo-leave-active {
  transition: 1s linear;
}
</style>

多个元素过渡

html
<template>
  <transition-group name="todo" appear>
    <!-- 必须要具有 key值 -->
    <div v-show="!isShow" key="1">Hello World!</div>
    <div v-show="isShow" key="2">你好,世界!</div>
  </transition-group>
</template>

<script>
  new Vue({
    el:'#root',
    data:{
      isShow:'true'
    }
  })
</script>

<style>
.todo-enter, .todo-leave-to {
  transform: translateX(100%);
}
.todo-leave, .todo-enter-to{
  transform: translateX(0);
}
.todo-enter-active, .todo-leave-active {
  transition: 1s linear;
}
</style>

四、Vue之ajax

1、解决跨域

方法一

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

javascript
module.exports = {
  devServer: {
    proxy: 'http://localhost:4000'
  }
}

TIP

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

方法二

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

javascript
module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: '<url>',
        ws: true,
        changeOrigin: true
      },
      '/foo': {
        target: '<other_url>'
      }
    }
  }
}

TIP

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

2、slot插槽

用于父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式。

默认插槽

html
<!-- 父组件 -->
<Category>
   <div>html结构1</div>
</Category>

<!-- 子组件Category -->
<template>
    <div>
       <!-- 定义插槽 -->
       <slot>插槽默认内容...</slot>
    </div>
</template>

具名插槽

父组件指明放入子组件的哪个插槽slot="footer",如果是template可以写成v-slot:footer

html
<!-- 父组件 -->
<Category>
    <template slot="center">
      <div>html结构1</div>
    </template>

    <template v-slot:footer>
       <div>html结构2</div>
    </template>
</Category>

<!-- 子组件Category -->
<template>
    <div>
       <!-- 定义插槽 -->
       <slot name="center">插槽默认内容...</slot>
       <slot name="footer">插槽默认内容...</slot>
    </div>
</template>

作用域插槽

用于父组件往子组件插槽放的html 结构接收子组件的数据scope="scopeData"。

理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。

html
<!-- 父组件 -->
<Category>
    <template scope="scopeData">
        <ul>
          <li v-for="g in scopeData.gamesDate" :key="g">{{g}}</li>
        </ul>
    </template>
</Category>

<Category>
    <!-- 解构赋值方式 -->
    <template scope="{gamesData}">
        <ul>
          <li v-for="g in gamesData" :key="g">{{g}}</li>
        </ul>
    </template>
</Category>

<Category>
    <!-- 普通节点方式:不能直接用scope,得用slot-scope -->
    <ul slot-scope="{gamesDate}">
      <li v-for="g in gamesDate" :key="g">{{g}}</li>
    </ul>
</Category>

<!-- 子组件Category -->
<template>
    <div>
        <slot :gamesDate="games"></slot>
    </div>
</template>

<script>
    export default {
        name:'Category',
        //数据在子组件自身
        data() {
          return {
            games: [
              { id: 0, name: "zhuque" },
              { id: 1, name: "wanzi" },
              { id: 2, name: "yingtao" },
              { id: 3, name: "afei" },
            ]
          }
        }
    }
</script>

五、Vuex状态管理

1、安装

bash
npm install vuex@next --save

2、创建store

创建src/store/index.js该文件用于创建Vuex中最为核心的store

javascript
import Vue from 'vue'
import Vuex from 'vuex' // 引入Vuex

Vue.use(Vuex) // 应用Vuex插件

// 创建并暴露store
export default new Vuex.Store({
  actions:{    // 准备actions——用于响应组件中的动作
    /** 绕过Action时,不需要再写
     add(context,value){
    context.commit('add',value)
   },
      cut(context,value){
    context.commit('cut',value)
   },
    */
   addOdd(context,value){ // context 相当于精简版的 $store
    if(context.state.sum % 2){
     context.commit('add',value)
    }
   },
   addWait(context,value){
    setTimeout(()=>{
     context.commit('add',value)
    },500)
   }
  }
  
  mutations:{ // 准备mutations——用于操作数据(state)
    add(state,value){
    state.sum += value
   },
    cut(state,value){
    state.sum -= value
   }
  }
  
  state:{   // 准备state——用于存储数据
    sum:0 //当前的和
  }
})

3、配置store

在src/main.js中创建vm时传入store配置项

javascript
import Vue from 'vue'
import App from './App.vue'
import store from './store' // 引入store

Vue.config.productionTip = false

new Vue({
 el: '#app',
 render: h => h(App),
 store,  // 配置项添加store
 beforeCreate() {
  Vue.prototype.$bus = this
 }
})

4、调用store

在src/components/Count.vue中创建Count.vue子组件调用store

html
<template>
    <div>
        <h1>当前求和为:{{ $store.state.sum }}</h1>
        <select v-model.number="num">
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
        </select>
        <button @click="add">+</button>
        <button @click="cut">-</button>
        <button @click="addOdd">当前求和为奇数再加</button>
        <button @click="addWait">等一等再加</button>
    </div>
</template>

<script>
    export default {
        name: 'Count',
        data() {
            return {
                num: 1, //用户选择的数字
            }
        },
        methods: {
            add() {  //绕过Action,直接对接Commit
                this.$store.commit('add', this.num)
            },
            cut() {  //绕过Action,直接对接Commit
                this.$store.commit('cut', this.num)
            },
            addOdd() {
                this.$store.dispatch('addOdd', this.num)
            },
            addWait() {
                this.$store.dispatch('addWait', this.num)
            },
        }
    }
</script>

6、getters配置项

在store.js中追加getters配置

javascript
......
const getters = {
 bigSum(state){
  return state.sum * 10
 }
}

// 创建并暴露store
export default new Vuex.Store({
 ......
 getters
})

组件中读取数据$store.getters.bigSum

6、四个map方法使用

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

javascript
computed: {
   // 借助mapState生成计算属性:sum、school、subject(对象写法一)
   ...mapState({
    sum:'sum',
    school:'school',
    subject:'subject'
    }),

   // 借助mapState生成计算属性:sum、school、subject(数组写法二)
   ...mapState(['sum','school','subject']),
}

mapGetters方法:用于帮助映射getters中的数据为计算属性

javascript
computed: {
    //借助mapGetters生成计算属性:bigSum(对象写法一)
    ...mapGetters({bigSum:'bigSum'}),

    //借助mapGetters生成计算属性:bigSum(数组写法二)
    ...mapGetters(['bigSum'])
}

mapActions方法:用于帮助生成与actions对话的方法,即包含$store.dispatch(xxx)的函数

javascript
methods:{
    //靠mapActions生成:incrementOdd、incrementWait(对象形式)
    ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})

    //靠mapActions生成:incrementOdd、incrementWait(数组形式)
    ...mapActions(['jiaOdd','jiaWait'])
}

mapMutations方法:用于帮助生成与mutations对话的方法,即包含$store.commit(xxx)的函数

javascript
methods:{
    //靠mapActions生成:increment、decrement(对象形式)
    ...mapMutations({increment:'JIA',decrement:'JIAN'}),
    
    //靠mapMutations生成:JIA、JIAN(对象形式)
    ...mapMutations(['JIA','JIAN']),
}

7、模块化+命名空间

修改store.js

javascript
const countAbout = {
  namespaced: true, // 开启命名空间
  actions: { ... },
  mutations: { ... },
  state: {x:1},
  getters: {
    bigSum(state){ return state.sum * 10 }
  }
}

const personAbout = {
  namespaced: true, // 开启命名空间
  actions: { ... }
  mutations: { ... },
  state: { ... },
  getters: { ... }
}

const store = new Vuex.Store({
  modules: {
    countAbout,
    personAbout
  }
})

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

javascript
// 方式一:自己直接读取
this.$store.state.personAbout.list
// 方式二:借助mapState读取:
...mapState('countAbout',['sum','school','subject'])

开启命名空间后,组件中读取getters数据

javascript
//方式一:自己直接读取
this.$store.getters['personAbout/firstPersonName']
//方式二:借助mapGetters读取:
...mapGetters('countAbout',['bigSum'])

开启命名空间后,组件中调用dispatch

javascript
//方式一:自己直接dispatch
this.$store.dispatch('personAbout/addPersonWang',person)
//方式二:借助mapActions:
...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})

开启命名空间后,组件中调用commit

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

六、Router 路由

1、基本路由使用

src/router/index.js

javascript
import VueRouter from 'vue-router'
// 引入组件
import About from '../pages/About'
import Home from '../pages/Home'

// 创建并暴露一个路由器
export default new VueRouter({
 routes:[
  {
   path:'/about',
   component:About
  },
  {
   path:'/home',
   component:Home
  }
 ]
})

src/main.js

javascript
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router' // 引入VueRouter
import router from './router'    // 引入路由器

Vue.config.productionTip = false

Vue.use(VueRouter) // 应用插件

new Vue({
 el:'#app',
 render: h => h(App),
 router
})

src/App.vue

html
<!-- Vue中借助router-link标签实现路由的切换 -->
<router-link class="item" active-class="active" to="/about">About</router-link>
<router-link class="item" active-class="active" to="/home">Home</router-link>

<!-- 指定组件的呈现位置 -->
<router-view></router-view>

TIP

  • 路由组件通常存放在pages或views文件夹,一般组件通常存放在components文件夹
  • 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载
  • 每个组件都有自己的**$route**属性,里面存储着自己的路由信息
  • 整个应用只有一个router,可以通过组件的**$router**属性获取到

2、嵌套路由

配置路由规则,使用children配置项

javascript
routes:[
 {
  path:'/about',
  component:About,
 },
 {
  path:'/home',
  component:Home,
  children:[      // 通过children配置子级路由
   {
    path:'news',   // 此处一定不要带斜杠,写成 /news
    component:News
   },
   {
    path:'message', // 此处一定不要写成 /message
    component:Message
   }
  ]
 }
]

跳转(要写完整路径)

javascript
<router-link to="/home/news">News</router-link>

3、路由 query 参数

传递参数

javascript
// 跳转并携带query参数,to的字符串写法
<router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">跳转</router-link>
    
// 跳转并携带query参数,to的对象写法(推荐)
<router-link 
 :to="{
  path:'/home/message/detail',
  query:{
     id: m.id,
           title: m.title
  }
 }"
>跳转</router-link>

接收参数

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

4、路由 params 参数

配置路由,声明接收params参数

javascript
{
 path:'/home',
 component:Home,
 children:[
  {
   path:'news',
   component:News
  },
  {
   component:Message,
   children:[
    {
     name:'xiangqing',
     path:'detail/:id/:title', // 使用占位符声明接收params参数
     component:Detail
    }
   ]
  }
 ]
}

传递参数

html
<!-- 跳转并携带params参数,to的字符串写法 -->
<router-link :to="`/home/message/detail/${id}/${title}`">跳转</router-link>
    
<!-- 跳转并携带params参数,to的对象写法 -->
<router-link 
 :to="{
  name:'xiangqing',
  params:{
     id:666,
           title:'你好'
  }
 }"
>跳转</router-link>

TIP

路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!

接收参数

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

5、命名路由

给路由命名

javascript
{
 path:'/demo',
 component:Demo,
 children:[
  {
   path:'test',
   component:Test,
   children:[
    {
      name:'hello' // 给路由命名
      path:'welcome',
      component:Hello,
    }
   ]
  }
 ]
}

简化路由的跳转

javascript
//简化前,需要写完整的路径
<router-link to="/demo/test/welcome">跳转</router-link>

//简化后,直接通过名字跳转
<router-link :to="{name:'hello'}">跳转</router-link>

//简化写法配合传递参数
<router-link 
 :to="{
  name:'hello',  //name和path不能一起使用,只能2选1
  query:{
      id:666,
        title:'你好'
  }
 }"
>跳转</router-link>

6、路由 props 配置

让路由组件更方便的收到参数

javascript
{
 name:'xiangqing',
 path:'detail/:id',
 component:Detail,

 //第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
 props:{a:900}

 //第二种写法:props值为布尔值,只能配合params使用,不能配合query使用;为true时,则把路由收到的所有params参数通过props传给Detail组件
 props:true
 
 //第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
 props($route){
  return {
   id: $route.query.id,
   title: $route.params.title
  }
 }
}

7、路由跳转 replace 方法

浏览器的历史记录有两种写入方式:

  • push是追加历史记录
  • replace是替换当前记录,路由跳转时候默认为push方式

开启replace模式

javascript
//完整写法
<router-link :replace="true" ...>News</router-link>
//简写
<router-link replace ...>News</router-link>

8、命名视图

同时(同级)展示多个视图 router-view 设置名字 默认 default

html
path: '/about',
name: 'About',
//换成 components 接收 {}
components: {
  default: About,
  pageA: () => import('../views/Singer.vue'),
  pageB: () => import('../views/Song.vue')
}

<!-- 默认展示default -->
<router-view></router-view> //about组件在这显示
<router-view name="pageA"></router-view>
<router-view name="pageB"></router-view>

9、路由别名

可以通过别名访问到 /home路由组件

javascript
path: '/home',
name: 'Home',
component: Home,
alias: ['/home1', '/home2']

10、路由重定向

当访问根路由时跳转到 /home

javascript
path: '/',
redirect: '/home'

11、编程式路由导航

不借助<router-link>(如button)实现路由跳转,让路由跳转更加灵活

javascript
this.$router.push({
  name:'xiangqing',
  params:{
    id:xxx,
    title:xxx
  }
})

this.$router.replace({
  name:'xiangqing',
  params:{
    id:xxx,
    title:xxx
  }
})

this.$router.forward()

this.$router.back()

this.$router.go(n)

12、缓存路由组件

让不展示的路由组件保持挂载,不被销毁

javascript
// 缓存一个路由组件
<keep-alive include="News"> // include中写想要缓存的组件名,不写表示全部缓存
    <router-view></router-view>
</keep-alive>

// 缓存多个路由组件
<keep-alive :include="['News','Message']"> 
    <router-view></router-view>
</keep-alive>

activated与deactivated

activateddeactivated是路由组件所独有的两个钩子,用于捕获路由组件的激活状态

  • activated路由组件被激活时触发
  • deactivated路由组件失活时触发
html
<template>
    <ul>
        <li :style="{opacity}">欢迎学习vue</li>
        <li>news001 <input type="text"></li>
        <li>news002 <input type="text"></li>
        <li>news003 <input type="text"></li>
    </ul>
</template>

<script>
    export default {
        name:'News',
        data(){
            return{
                opacity:1
            }
        },
        activated(){
            console.log('News组件被激活了')
            this.timer = setInterval(() => {
                this.opacity -= 0.01
                if(this.opacity <= 0) this.opacity = 1
            },16)
        },
        deactivated(){
            console.log('News组件失活了')
            clearInterval(this.timer)
        }
    }
</script>

13、路由守卫

全局守卫

javascript
// 全局前置守卫:初始化时、每次路由切换前执行
router.beforeEach((to,from,next) => {
 if(to.meta.isAuth){ // 判断当前路由是否需要进行权限控制,meta写在路由规则里
  if(localStorage.getItem('school') === 'atguigu'){ // 权限控制的具体规则
   next() // 放行
  }else{
   alert('暂无权限查看')
  }
 }else{
  next() // 放行
 }
})

// 全局后置守卫:初始化时、每次路由切换后执行
router.afterEach((to,from) => {
 if(to.meta.title){ 
  document.title = to.meta.title //修改网页的title
 }else{
  document.title = 'vue_test'
 }
})

独享守卫

javascript
//写在路由规则里
beforeEnter(to,from,next){
    if(localStorage.getItem('school') === 'atguigu'){
        next()
    }else{
        alert('暂无权限查看')
    }
}

组件内守卫

javascript
//进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter(to, from, next) {... next()}

//离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave(to, from, next) {... next()}

//通过路由规则,该组件被复用时调用
beforeRouteUpdatee(to, from, next) {... next()}

14、路由器工作模式

hash模式

  • 后端不会把 /#/home/singer 识别为后端路由 (不会和后端路由发生冲突 )
  • 地址中永远带着#号,不美观
  • 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法
  • 兼容性较好

history模式

  • 再次刷新页面会把 /home/singer 当做后端路由,页面就会丢掉,可后端配合解决
  • 地址干净,美观
  • 兼容性和hash模式相比略差
  • 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题
javascript
const router =  new VueRouter({
 mode:'history',
 routes:[...]
})

export default router

TIP

若使用Node搭建本地服务器,可npm安装connect-history-api-fallback插件解决404问题。

读书、摄影、画画、弹琴、编程