Vue知识网2021

尤雨溪:一直呆在舒适区往往就得不到提升,程序员能力的提升往往都发生在尝试解决一个从没解决过的问题之后进行反思的过程中。


在这里插入图片描述

一、MVVM

不管是 React 还是 Vue,它们都不是 MVVM 框架,只是有借鉴 MVVM 的思路

MVVM ( Model-View-ViewModel ) 是一种软件架构思想。
在这里插入图片描述

  • Model 层:数据模型,是代码中主要操作的部分。
  • View 层:视图模板,对应要显示的标签结构。
  • ViewModel 层:用于处理业务逻辑,对于可输入元素(输入框、文本域等)可将 View 变化自动更新到 Model ,并将 Model 变化自动更新到 View,实现双向数据绑定。



二、模板语法

1、Vue 实例

Vue 实例,代表的是MVVM 中的 ViewModel,是 Vue 应用的基础。

创建 Vue 实例后,通过对选项对象进行配置,来设置不同的 Vue 功能。

var vm = new Vue({
   // 选项对象 
});

2、el

实例选项 el 代表 MVVM 中 View 的容器,用于选择要被 Vue 操作的元素。

  • el 可以为 选择器字符串 或 DOM 对象 ,代表一个元素,但不能为 htmlbody
  • 可通过 vm.$el 访问
  • 为设置 elvue 实例,也可以通过 vm.$mount() 进行挂载,参数形式与 el 规则相同
var vm = new Vue({
    el: '#app'
});

3、data

实例选项 data 代表 MVVM 中的 Model, 用于存储数据。

数据可通过 vm.$data.数据vm.数据 访问。

var vm = new Vue({
    el: '#app',
    data: {
        title: '标题文本',
        content: '内容文本'
    }
});
console.log(vm.$data.title);
console.log(vm.title);

data 中存在数组时,索引操作与 length 操作无法自动更新视图,这时可以借助 Vue.set() 方法替代操作。

Vue.set() 参数1为要设置的数据(例如数组),参数2为键(例如索引),参数3为新值。

var vm = new Vue({
  el: '#app',
  data: {
    contentArr: ['内容1', '内容2', '内容3']
  }
});

// 在控制台中测试代码:
vm.contentArr.length = 0; // 无效
vm.contentArr[1] = '新内容'; // 无效
Vue.set(vm.contentArr, 1, '生效的新内容'); // 生效v

4、插值表达式

插值表达式用于在 View 中进行模板语法操作,可在标签 {{ }}内容中插入数据或操作代码。

内部可以书写表达式,但不能书写语句。

<div id="app">
    <ul>
        <li>计算结果为:{{ 1 + 2 + 3 }}</li>
        <li>比较结果为:{{ 2 > 1 ? 2 : 1 }}</li>
    </ul>
</div>

5、methods

methods 用于封装需要在实例中使用的函数。

methods 中的函数内的 this 代表 vm

var vm = new Vue({
    el: '#app',
    data: {
        title: '标题文本',
        content: '内容文本'
    },
    methods: {
        output () {
            console.log('标题为' + this.title + ',' + '内容为' + this.content);
        }
    }
});



三、生命周期

Vue.js 生命周期指的是 Vue 实例从创建到执行,再到销毁的过程。

Vue.js 提供了用于在生命周期不同阶段 执行的 生命周期函数用来在特点阶段设置操作。生命周期函数也称为生命周期钩子。

1、生命周期阶段

在这里插入图片描述

2、生命周期函数

生命周期函数均直接设置在实例的选项对象中。

  • 创建阶段:(每个实例只能执行一次)
  1. beforeCreate:实例初始化之前调用,此时 data 和 methods 都功能尚未初始化。
  2. created:data 和 methods 等功能已经创建好,可以被访问了,数据操作通常在这个阶段。
  3. beforeMount:实例挂载开始之前调用,模板内容还没有被添加到 DOM 中
  4. mounted:实例挂载完毕,可以通过 vm.$el 访问挂载元素,DOM 操作通常在这个阶段。
  • 运行阶段:(按需调用,可执行任意次数)
  1. beforeUpdate:数据更新时调用,Vue.js 内部还未进行视图更新处理,可以访问更新前的 DOM。
  2. updated:DOM 更新完毕,视图和数据都是最新的。
  • 销毁阶段:(每个实例只能执行一次)
  1. beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。
  2. destroyed:实例销毁后调用,实例的所有功能都已移除销毁。



四、指令

在 Vue.js 中,指令的就是以 v- 开头的 html 自定义属性,不同的指令用于设置不同功能。


1、内容指令

⑴、v-once 指令

用于让元素内部的插值表达式只生效一次。

<div id="app">
    <p>此内容会随数据变化自动更改:{{content}}</p>
    <p v-once>此内容不会随数据变化自动更改:{{content}}</p>
</div>

⑵、v-text 指令

用于给标签设置纯文本内容,设置后原内容被覆盖。

<div id="app">
    <p v-text="content"></p>
</div>

⑶、v-html 指令

用于给标签设置结构文本,设置后原内容被覆盖。

<div id="app">
    <p v-html="content"></p>
</div>


2、属性指令

⑴、v-bind 指令

如果要动态设置属性,需要使用 v-bind 指令。

v-bind:属性 简写为 :属性

<div id="app">
  <p v-bind:title="title1">这是一段文本内容</p>
  <p :title="title2">这是一段文本内容</p>
</div>
var vm = new Vue({
  el: '#app',
  data: {
    title1: '这是第一个p标签',
    title2: '这是第二个p标签'
  }
});

除了可以单个绑定,还可以通过对象同时绑定多个属性

<div id="app">
  <p v-bind="attrObj">这是 p 标签的内容</p>
</div>
new Vue({
  el: '#app',
  data: {
    attrObj: {
      id: 'box',
      title: '示例内容',
      class: 'clearFix',
      'data-title': '这是 data-title 的内容'
    }
  }
});

⑵、class 绑定

由于 class 可绑定多个值,v-bind 为 class 增加了更多功能。

①、class:class / v-bind:class 可以共存
<div id="app">
  <p class="a" :class="cls"></p>
</div>
var vm = new Vue({
  el: '#app',
  data: {
    cls: 'b'
  }
});

②、:class 可以设置对象,对象的键表示类名,值为布尔类型,用于控制类名是否显示。
<div id="app">
  <!-- 带 - 的类型需要加 ' '-->
  <p :class="{ b:isB, c: isC, 'class-d': true }"></p>
</div>
var vm = new Vue({
  el: '#app',
  data: {
    cls: 'b'
  }
});

③、:class 设置数组时,可以结合前面两种功能的特点
<div id="app">
  <p :class="['a', {b: isB} ,'c']"></p>
</div>
var vm = new Vue({
  el: '#app',
  data: {
    isB: true
  }
});

⑶、style 绑定

style 可以设置多个值,v-bind 为 style 增加了更多功能。

style:style 可以共存,重复时 :style 会覆盖 style, :style 的值为对象,键为属性名,值为属性值。

<div id="app">
  <p style="width:100px;" :style="styleObj">标签内容</p>
</div>
var vm = new Vue({
  el: '#app',
  data: {
    styleObj: {
      width: '200px',
      height: '100px',
      border: '1px solid #ccc'
    }
  }
});

设置数组值时,可以传入多个样式对象

<p style="width:100px;" :style="[styleObj1, styleObj2]">标签内容</p>
var vm = new Vue({
  el: '#app',
  data: {
    styleObj1: {
      height: '100px',
      width: '200px'
    },
    styleObj2: {
      border: '1px solid #ccc',
      color: 'blue'
    }
  }
});


3、渲染指令

⑴、v-for 指令

用于遍历数据生成结构,数组与对象均可操作。

①、遍历
<div id="app">
    <!-- 数组遍历 -->
    <ul>
        <li v-for="item in arr">{{item}}</li>
    </ul>
    <!-- 数组遍历:含索引 -->
    <ul>
        <li v-for="(item, index) in arr">{{item}}</li>
    </ul>
    <!-- 对象遍历 -->
    <ul>
        <li v-for="value in obj">{{value}}</li>
    </ul>
    <!-- 对象遍历:含键名 -->
    <ul>
        <li v-for="(value, key) in obj">{{value}}</li>
    </ul>
    <!-- 对象遍历:含键名、索引值 -->
    <ul>
        <li v-for="(value, key, index) in obj">{{value}}</li>
    </ul>
</div>
new Vue({
    el: '#app',
    data: {
    	arr: ['内容1', '内容2', '内容3'],
        obj: {
            content1: '内容1',
            content2: '内容2',
            content3: '内容3'
        }
    }
});

②、key 属性

使用 v-for 的同时,应始终指定唯一的 key 属性,可以提高渲染性能并避免问题

<div id="app">
    <ul>
        <li v-for="item in items" :key="item.id">{{value}}</li>
    </ul>
</div>
new Vue({
    el: '#app',
    data: {
    	items: [
            {
                id: 1,
                content: '内容1'
            },
            {
                id: 2,
                content: '内容2'
            },
            {
                id: 3,
                content: '内容3'
            }
        ]
    }
});

③、key 属性

通过 标签设置模板占位符,可以将部分元素或内容作为整体进行操作。

<div id="app">
    <template>
        <span>奇数行</span>
        <span>偶数行</span>
    </template>
</div>
④、Vue.set()

给对象添加新的属性并自动更新,则需要使用 Vue.set() 设置,普通赋值无效

<div id="app">
	<ul>
  		<li v-for="value in obj">{{value}}</li>
	</ul>
</div>
var vm = new Vue({
  el: '#app',
  data: {
    obj: {
        content1: '内容1',
        content2: '内容2',
        content3: '内容3'
    }
  }
});

// 在控制台中测试代码:
vm.obj.content4 = '内容4'; // 无效,注:将错误写法注释后再测试正确代码
Vue.set(vm.obj, 'content4', '内容4')// 生效

⑤、Vue.delete()

删除对象属性通过 delete 操作无法触发视图更新 ,需要通过 Vue.delete() 操作

var vm = new Vue({
  el: '#app',
  data: {
    obj: {
        content1: '内容11',
        content2: '内容22',
        content3: '内容33'
    }
  }
});

// 在控制台中测试代码:
delete vm.obj.content4; // 无效,注:将错误写法注释后再测试正确代码
// Vue.delete() 参数1为要操作的对象,参数2为属性名
Vue.delete(vm.obj, 'content4')// 生效

⑵、v-show 指令

用于控制元素显示与隐藏,适用于显示隐藏频繁切换时使用。

  • 值为 true 时元素显示,false 时隐藏。
  • 由于 v-show 本质为 display 控制的功能
<div id="app">
    <p v-show="true">这个元素会显示</p>
    <p v-show="false">这个元素会隐藏</p>
</div>

⑶、v-if 指令

用于控制元素的创建与移除。

<div id="app">
    <p v-if="2>11">这个元素不会创建</p>
    <p v-else-if="2>1">这个元素会创建</p>
    <p v-else>这个元素不会创建</p>
</div>
  • 可通过 v-if 可以控制多个同级元素

  • 给使用 v-if 的同类型元素添加不同的 key 属性

    由于 Vue.js 默认会尽可能高效的更新 DOM,当通过 v-if 切换两个同类型元素时并不会重新创建,而是对以存在的元素进行修补,但这种操作可能会导致问题,我们可以为元素设置不同的 key 属性。

  • v-ifv-for 一起使用时, v-for 优先级更高,为了提高执行效率,建议将 v-if 设置给父元素。

<div id="app">
    <ul v-if="bool">
        <li v-for="item in items" :key="item.id">{{value}}</li>
    </ul>
</div>


4、事件处理

v-on 指令

用于进行事件绑定。


①、语法

书写为 v-on:事件类型名 或简写为 @事件类型名,值为事件处理程序,可书写简单代码或函数

<div id="app">
	<p>{{ content }}</p>
	<button v-on:click="content='新内容1'">点击修改内容</button>
	<button @click="content='新内容2'">点击修改内容</button>
</div>

②、methods

处理程序代码较多时,可以在 methods 中设置函数,并设置为事件处理程序

<div id="app">
    <p>{{ content }}</p>
	<button @click="fn">点击修改内容</button>
</div>
var vm = new Vue({
  el: '#app',
  data: {
    content: '默认内容',
  },
  methods: {
    fn () {
      // 函数内的 this 为 Vue 实例
      this.content = '新内容';
    }
  }
});

③、事件

设置事件时传参,并手动设置事件对象

<div id="app">
    <p>{{ content }}</p>
	<button @click="fn(content, $event)">按钮</button>
</div>
var vm = new Vue({
  el: '#app',
  data: {
    content: '默认内容',
  },
  methods: {
    fn (content, event) {
      console.log(content);
      console.log(event);
    }
  }
});


5、表单输入绑定

v-model 用于给 、< textarea > 及 元素设置双向数据绑定。


⑴、文本输入框

<div id="app">
    <p>input内容为:{{ value1 }}</p>
	<input type="text" v-model="value1">
    
	<p>textarea内容为:{{ value2 }}</p>
	<textarea v-model="value2"></textarea>
</div>
var vm = new Vue({
  el: '#app',
  data: {
    value1: '',
    value2: ''
  }
});

⑵、单选按钮

<div id="app">
    <p>radio数据为: {{ value3 }}</p>
	<input type="radio" id="one" value="1" v-model="value3">
	<label for="one">选项一</label>
	<input type="radio" id="two" value="2" v-model="value3">
	<label for="two">选项二</label>
</div>
var vm = new Vue({
  el: '#app',
  data: {
    value3: ''
  }
});

⑶、复选框

  • 单个复选框绑定时,数据为布尔类型
  • 多个复选框绑定时,数组为数组类型,用于接收多个选项数据。
<div id="app">
	<p>单个checkbox选中的数据为: {{ value4 }}</p>
	<input type="checkbox" id="item" value="选项内容" v-model="value4">
	<label for="item">选项</label>
    
	<p>多个checkbox选中的数据为: {{ value5 }}</p>
	<input type="checkbox" id="one" value="选项一内容" v-model="value5">
	<label for="one">选项一</label>
	<input type="checkbox" id="two" value="选项二内容" v-model="value5">
	<label for="two">选项二</label>
</div>
var vm = new Vue({
  el: '#app',
  data: {
    value4: '',
    value5: []
  }
});

⑷、选择框

<div id="app">
    <p>单选select数据为: {{ value6 }}</p>
    <select v-model="value6">
      <option value="">请选择</option>
      <option value="1">选项一</option>
      <option value="2">选项二</option>
      <option value="3">选项三</option>
    </select>
    <p>多选select数据为: {{ value7 }}</p>
    <select v-model="value7" multiple>
      <option value="1">选项一</option>
      <option value="2">选项二</option>
      <option value="3">选项三</option>
    </select>
</div>
var vm = new Vue({
  el: '#app',
  data: {
    value6: '',
    value7: []
  }
});


6、修饰符

修饰符是以点开头的指令后缀,用于给当前指令设置特殊操作。


⑴、事件修饰符

事件修饰符用于修饰 v-on 指令

  • .prevent:用于阻止默认事件行为,相当于 event.preventDefault()
  • .stop:用于阻止事件传播,相当于 event.stopPropagation()
  • .once:用于设置事件只会触发一次
  • 修饰符可以连写:例如 @click.prevent.once

⑵、按键修饰符

按键修饰符用于键盘事件(例如 keyup )可以由哪种案件触发

  • .enter 点击回车键
  • .esc 点击 esc 键

⑶、系统修饰符

用于实现事件在点击系统按钮时触发。

  • .ctrl
  • .alt
  • .shift

⑷、鼠标修饰符

用于实现事件在点击鼠标指定按钮时触发。

  • .left 点击鼠标左键
  • .right 点击鼠标右键
  • .middle 点击鼠标中键

⑸、v-model 修饰符

用于实现事件在点击系统按钮时触发。

  • .lazy 用于将 v-model 的触发方式由 input 事件触发更改为 change 事件触发。
  • .number 用于自动将用户输入的值转换为数值类型,如无法被 parseFloat() 转换,则返回原始内容。
  • .trim 用于自动过滤用户输入首尾两端空格



7、自定义指令

⑴、自定义 全局 指令

自定义 全局 指令可以被任意 Vue 实例或组件使用。

Vue.directive() 用于设置 全局 自定义指令

  • 参数1: 指令名称,无需书写 v- , 在视图中使用时添加 v- 使用
  • 参数2: 配置对象,用于设置自定义指令的功能
<div id="app">
    <input type="text" v-focus>
</div>
Vue.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时执行 inserted
  inserted: function (el) {
    // el 为设置指令的 DOM 元素
    // 通过 DOM 语法设置功能即可,例如设置输入框自动获取焦点
    el.focus();
  }
});

⑵、自定义 局部 指令

自定义 局部 指令只能在当前 Vue 实例或组件内部使用。

new Vue({
    // ...省略其他代码
    directives: {
        focus: {
    		inserted (el) {
				el.focus();
        	}
		}
    }
});



五、Vue.js 基础

1、过滤器

Vue.js 允许自定义过滤器,可被用于对插值表达式与 v-bind 中的文本进行处理。


⑴、全局 过滤器

全局过滤器可以被任意 Vue 实例或组件使用。

过滤器本质为函数,数据自动传入到函数中处理,最终展示的数据为返回值。

Vue.filter('过滤器名称', function (value) {
    // value 为传入到过滤器中的数据
	return '处理结果';
});
<div id="app">
	<!-- 在 v-bind 中 -->
	<div v-bind:id="id | filterId"></div>
    
    <!-- 在插值表达式中 -->
	<div>{{ content | filterContent }}</div>
    
    <!-- 同时设置多个过滤器: 数据会从左到右依次进行过滤 -->
	<div>{{ content | filterA | filterB }}</div>
    
    <!-- 给过滤器传入多个参数 -->
	<div>{{ content | filterContent(par1, par2) }}</div>
</div>

⑵、局部 过滤器

局部过滤器只能在当前 Vue 实例或组件内部使用。

new Vue({
    // ...省略其他代码
    filters: {
        过滤器名称: function (value) {
        	return '处理结果';
    	}
    }
});

2、计算属性

Vue.js 的视图不建议书写复杂逻辑,这样不利于维护,所以提供了计算属性来进行处理。


⑴、语法

计算属性本质本函数,在视图模板中访问函数名时函数会自动调用。

var vm = new Vue({
  el: '#app',
  data: {
    arr: [1, 2, 3, 4, 5]
  },
  computed: {
    result () {
      var arr = this.arr;
      var sum = 0;
      for (var i = 0; i < arr.length; i++) {
        sum += arr[i];
      }
      return sum;
    }
  }
});
<div id="app">
    <p>{{ result }}</p>
    <p>{{ result }}</p>
    <p>{{ result }}</p>
</div>

⑵、computed 与 methods 的区别

  • computed 用于计算操作具有缓存性,初次计算后的结果会被缓存,只有当内部依赖的数据改变后,下次获取 computed 时才会重新执行计算;否则,多次获取 computed 值只会读取缓存的结果,而不会重新执行。
  • methods 无缓存性,每次调用都会执行,常用于处理非计算操作

⑶、常见操作

通过计算属性进行处理,并结合 v-for 遍历计算属性结果,用于避免 v-ifv-for 结合使用。

<div id="app">
    <ul>
	    <li v-for="item in result" :key="item">
            数值为:{{ item }}
        </li>
	</ul>
</div>
var vm = new Vue({
  el: '#app',
  data: {
    arr: [1, 2, 3, 4, 5, 6, 7, 8]
  },
  computed: {
    result () {
      // 筛选出偶数
      return this.arr.filter((item) => item % 2 === 0);
    }
  }
});

⑷、计算属性的 setter

计算属性无法像 methods 一样进行传参,那我们要如何针对不同数据进行操作呢?

var vm = new Vue({
   computed: {
     getResult: {
       // getter
       get: function () {
         // 逻辑代码
       },
       // setter
       set: function (newValue) {
          // 逻辑代码
       }
     }
   } 
});

3、侦听器

用于监听数据变化并执行指定操作。

new Vue({
	el: '#app',
    data: {
        value: ''
    },
    watch: {
    	// 侦听器名称为要监视的 data 中的属性名
        value (newValue, oldValue) {
    		// newValue 为新数据
    		// oldValue 为旧数据
		}
    }
});

4、案例:toDoList

Git地址:https://gitee.com/yuan0_0/to-do-list.git

在这里插入图片描述

需求分析

  • 事项列表展示
  • 状态栏展示
  • 事项状态切换
  • 事项新增
  • 事项删除
  • 事项编辑
  • 事项筛选
  • 事项数据持久化



六、Vue.js 组件

组件是可复用的 Vue 实例,内部封装组件的结构、样式、逻辑代码,可通过自定义的组件名称进行复用,形式为自定义 HTML 标签。


1、组件注册

⑴、全局注册

全局注册的组件在注册之后可以用在任意实例或组件中。

// 全局注册必须设置在根 Vue 实例创建之前
Vue.component('组件名', { /* 选项对象 */ });

⑵、组件基础

由于组件是可复用的 Vue 实例,所以它们可与 new Vue 接收相同的选项,例如 datacomputedwatchmethods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。


①、组件命名规则

组件命名有两种方式,但无论采用哪种方式定义,在 DOM 中都只有 kebab-case 可以使用。

  • kebab-case:‘my-component-name’
  • PascalCase:‘MyComponentName’
Vue.component('my-component-a', { /* 选项对象 */ });
Vue.component('my-component-b', { /* 选项对象 */ });
Vue.component('MyComponentC', { /* 选项对象 */ });
Vue.component('MyComponentD', { /* 选项对象 */ });

②、template 选项

template 选项用于设置组件结构,最终会被根实例或其他组件引入。

示例中使用了模板字符串,考虑兼容性时应改为普通字符串。

// 子组件
Vue.component('MyComponentA', {
  template: `
    <div>
      <h3>组件 A 的标题内容</h3>
    </div>`,
});

注意:组件必须只有一个根元素,否则会报错。

③、data 选项

data 选项必须为函数,而不是像根实例一样设置为对象。

这种实现方式是为了确保每个组件实例可以维护一份被返回对象的独立的拷贝,不会相互影响。

Vue.component('MyComA', {
  template: `
	<h3>{{ title }}</h3>
  `,
  data: function () {
    return {
      title: '示例内容'
    }
  }
})

⑶、局部注册

局部注册的组件只能用在当前实例或组件中。

new Vue({
    ...
    components: {
        'my-component-a': {
            template: '<h3>{{ title }}</h3>',
            data () {
                return { title: 'a 组件示例内容' }
            }
        },
        'my-component-b': {
            template: '<h3>{{ title }}</h3>',
            data () {
                return { title: 'b 组件示例内容' }
            }
        }
    }
});

上面的方式将根实例与多个组件的选项对象书写在一起非常不清晰,所以我们通常会将组件的选项对象单独书写,再设置到某个实例的 components 属性中。

var MyComponentA = { /* ... */ }
var MyComponentB = { /* ... */ }

new Vue({
  el: '#app',
  components: {
    'my-component-a': MyComponentA,
    'my-component-b': MyComponentB
  }
});

或者采用 ES6 的对象属性简写方式

new Vue({
  el: '#app',
  components: {
    MyComponentA,
    MyComponentB
  }
});


2、组件通信

当我们将页面功能设置为多个子组件后,这些子组件如何使用根实例中的数据呢?子组件如何将操作状态反馈给父实例呢?这时就需要组件间进行传值,这种操作称为组件通信。


⑴、父组件向子组件传值

父向子传值需要通过给子组件设置 props 选项实现。


①、Props

props 选项为数组,子组件通过 props 定义任意属性后,父组件再给子组件设置对应属性即可。

props 的属性可以像 data 的属性一样可以在子组件中通过 this 访问或在视图模板中通过名称使用,所以 props 不要与 data 存在同名属性。

// 子组件
Vue.component('my-component', {
  props: ['title'],
  template: '<h3>{{ title }}</h3>'
});

设置方式有2种:静态属性值设置方式与 v-bind 动态绑定 方式,静态属性只能为字符串类型,而动态绑定可以为任意类型。

<!-- 父组件 -->
<div id="app">
    <my-component-a title="示例内容1"></my-component-a>
	<my-component-a :title="'示例内容2'"></my-component-a>
	<my-component-a :title="item.title"></my-component-a>
</div>
new Vue({
  el: '#app',
  data: {
    item: {
      title: '父组件中的数据'
    }
  }
});

Props 命名推荐使用 camelCase,但由于 HTML 属性不区分大小写,所以 props 中的 camelCase 属性在被父组件设置时需要使用对应的 kebab-case

<!-- 父组件 -->
<div id="app">
	<my-component-a my-title="示例内容1"></my-component-a>
	<my-component-a :my-title="'示例内容2'"></my-component-a>
	<my-component-a :my-title="item.title"></my-component-a>
</div>
// 子组件
Vue.component('my-component', {
  props: ['myTitle'],
  template: '<h3>{{ myTitle }}</h3>'
});

②、单向数据流

父子组件间的所有 Props 都是 单向下行绑定 的。

每次父级组件发生变更时,子组件所有的 prop 都会刷新为最新值,但反过来则不行。这样可以防止子组件意外变更父级组件的状态,从而导致应用的数据流向难以理解。

如果子组件需要对父组件传递的数据进行处理,应当定义本地的 data 属性以保存需要处理的 props。

// 子组件
Vue.component('my-component', {
  props: ['initialTitle'],
  template: '<h3>{{ myTitle }}</h3>',
  data () {
    return {
      myTitle: this.initialTitle
    }
  }
});

需要特别注意的是,由于对象和数组是通过引用传入的,所以对于一个数组或对象类型的 props 来说,在子组件中变更这个对象或数组内部的数据 将会 影响到父组件的状态。

③、Props 类型

Props 可以设置类型检查,这时需要将 Props 更改为一个带有验证需求的对象。

类型检查的设置方式有两种:

第一种:指定单个类型,设置为对应的构造函数即可,无需验证时设置 nullundefined

// 子组件
Vue.component('MyComponentA', {
  props: {
    parStr: String,
    parNum: Number,
    parArr: Array,
    parObj: Object,
    parAny: null // parAny: undefined
  },
  template: `
    <div>
      {{ parStr }}
      {{ parNum }}
      {{ parArr }}
      {{ parObj }}
      {{ parAny }}
    </div>
  `
});

// 父组件
var vm = new Vue({
  el: '#app',
  data: {
    str: '示例内容',
    num: 100,
    arr: [1, 2, 3],
    obj: {
      content1: '父组件示例示例内容1',
      content2: '父组件示例示例内容2'
    },
    any: '任意类型均可'
  }
});
<!-- 父组件 -->
<!-- 父组件 -->
<div id="app">
  <my-component-a 
    :par-str="str"
    :par-num="num"
    :par-arr="arr"
    :par-obj="obj"
    :par-any="any"
  ></my-component-a>
</div>

第二种:指定多个类型,可以将 props 对应类型设置为数组。

// 子组件
Vue.component('MyComponentA', {
  props: {
    parData: [String, Number]
  },
  template: `
    <div>
      {{ parData }}
    </div>
  `
});

④、Props 验证

如果需要给一个 Props 设置多种规则,可以将某个 Props 的值设置为选项对象

Props 值设置为选项对象后,类型检测通过 type 选项进行设置。

Ⅰ、type

Props 值设置为选项对象后,类型检测通过 type 选项进行设置。

// 子组件
Vue.component('MyComponentA', {
  props: {
    parNum: {
      type: Number
    },
    parData: {
      type: [String, Boolean]
    }
  },
  template: `
    <div>
	  {{ parNum }}
      {{ parData }}
    </div>
  `
});

Ⅱ、required

Required 用于设置数据为必填项

// 子组件
Vue.component('MyComponentA', {
  props: {
    parNum: {
      type: Number,
      required: true
    }
  },
  template: `
    <div>
      {{ parNum }}
    </div>
  `
});

Ⅲ、default

Default 用于给可选项指定默认值,当父组件未传递数据时生效。

// 子组件
Vue.component('MyComponentA', {
  props: {
    parNum: {
      type: Number,
      default: 100
    }
  },
  template: `
    <div>
      {{ parNum }}
    </div>
  `
});

Ⅳ、工厂函数

当默认值为数组或对象时,必须为工厂函数返回的形式(为了确保多个实例数据独立)。

// 子组件
Vue.component('MyComponentA', {
  props: {
    parArr: {
      type: Array,
      default: function () {
        return [1, 2, 3];
      }
    }
  },
  template: `
    <div>
      {{ parArr }}
    </div>
  `
});

Ⅴ、validator

Validator 用于给传入的 props 设置校验函数,return 值为 falseVue.js 会发出警告。

// 子组件
Vue.component('MyComponentA', {
  props: {
    parStr: {
      type: String,
      validator: function (value) {
        return value.startsWith('lagou');
      }
    }
  },
  template: `
    <div>
      {{ parStr }}
    </div>
  `
});

由于 prop 会在实例创建前进行验证,所以 default 与 validator 无法使用例如 data、methods 等功能。

⑤、非 Prop 属性

当父组件给子组件设置了属性,但此属性在 props 中不存在,这时会自动绑定到子组件的根元素上。

<!-- 父组件 -->
<div id="app">
  <my-component-a
    demo-attr="示例属性"
    title="示例title"
    style="height: 200px"
    class="colorBlue"
  ></my-component-a>
</div>
<!-- 子组件 -->
Vue.component('MyComponentA', {
  template: `<p>子组件内容</p>`
});

如果组件根元素已经存在了对应属性,则父组件的提供的值会替换组件内部的值。但 class 与 style 例外,当内外都设置时,属性会自动合并。

/* 
	修改子组件为以下代码后:
	title 被覆盖,class 与 style 则自动合并。
*/
Vue.component('MyComponentA', {
  template: `
    <p title="原始title" class="fl" style="width: 200px;">子组件内容</p>
  `
});

如果不希望继承父组件设置的属性,可以设置 inheritAttrs: false,但 class 与 style 不受影响。

Vue.component('MyComponentA', {
  inheritAttrs: false,
  template: `
    <p title="原始title" class="fl" style="width: 200px;">子组件内容</p>
  `
});


⑵、子组件向父组件传值

①、自定义事件

子向父传值需要通过给子组件设置自定义事件来实现。

例如电商网站的购物车功能中,每个商品的个数需要在购物车中进行统计,如果将商品看作子组件,购物车为父组件,那数据的传递过程就是在通过子组件向父组件传值。

<!-- 父组件 -->
<div id="app">
  <h3>购物车</h3>
  <product-item 
    v-for="product in products"
    :title="product.title"
    :key="product.id"
  ></product-item>
  <p>总数为:{{ totalCount }}</p>
</div>
var vm = new Vue({
  el: '#app',
  data: {
    products: [
      { id: 1, title: '苹果1斤' },
      { id: 2, title: '橙子2个' },
      { id: 3, title: '香蕉3根' }
    ],
    totalCount: 0
  }
});
Vue.component('product-item', {
  props: ['title'],
  template: `
    <div>
      <span>商品名称:{{ title }},商品个数: {{ count }}</span>
      <button @click="countIns">+1</button>
    </div>
  `,
  data () {
    return { count: 0 }
  },
  methods: {
    countIns () {
      this.count++;
    }
  }
});

当商品个数变化时,我们应该将数据传递给父组件,这时可以通过触发自定义事件来实现。

方法 $emit() 用于触发指定的事件,父组件可以通过监听子组件的事件来得知子组件的状态变化。

this.$emit('事件名');

注意,为确保事件名在组件与 DOM 模板内均得以生效,推荐始终使用 kebab-case 的事件名。

// 子组件
Vue.component('product-item', {
  ...
  methods: {
    countIns () {
      this.$emit('count-change');
      this.count++;
    }
  }
});

父组件监听子组件事件,并设置处理代码。

<div id="app">
  ...
  <product-item 
	...
    @count-change="totalCount++"
  ></product-item>
  ...
</div>

②、事件传值

现在添加一个+5 按钮,如果 +1 和 +5 使用 count-change 事件,就需要准确告知父组件个数的变化,这时就需要通过事件进行传值。

this.$emit('事件名', '事件传值');

给两个按钮分别设置事件,并给 count-change 传递不同值。

Vue.component('product-item', {
  props: ['title'],
  template: `
    <div>
      <span>商品名称:{{ title }},商品个数: {{ count }}</span>
      <button @click="countIns1">+1</button>
      <button @click="countIns5">+5</button>
    </div>
  `,
  ...
  methods: {
    countIns1 () {
      this.$emit('count-change', 1);
      this.count++;
    },
    countIns5 () {
      this.$emit('count-change', 5);
      this.count+= 5;
    }
  }
});

父组件需要接受事件传值并进行相应处理。

<div id="app">
  ...
  <product-item
    ...
    @count-change="totalCount += $event"></product-item>
  ...
</div>

<div id="app">
  <h3>购物车</h3>
  <product-item
    ...
    @count-change="onCountChange"
  ></product-item>
  ...
</div>
var vm = new Vue({
  ...
  methods: {
    onCountChange (productCount) {
      this.totalCount += productCount;
    }
  }
});

③、组件与 v-model

v-model 用于双向数据绑定,但用于组件时,双向绑定的过程需要通过 props 与自定义事件实现。

<div id="app">
  <p>输入内容为:{{ iptValue }}</p>
  <com-input v-model="iptValue"></com-input>
</div>
var ComInput = {
  props: ['value'],
  template: `
    <input 
      type="text" 
      :value="value" 
      @input="$emit('input', $event.target.value)">`
};
new Vue({
  el: '#app',
  data: {
    iptValue: ''
  },
  components: {
    ComInput
  }
})


⑶、非父子组件传值

非父子组件,例如一个父组件下的两个兄弟组件或两个没有任何关联的组件。


①、兄弟组件传值

兄弟组件可以将数据通过父组件进行中转,从而实现传值。

<div id="app">
  <com-a 
    @value-change="value = $event"
  ></com-a>
  <com-b
    :value="value"
  ></com-b>
</div>
Vue.component('ComA', {
  template: `
    <div>
      组件A的内容:{{ value }}
      <button 
        @click="$emit('value-change', value)"
      >发送</button>
    </div>`,
  data () {
    return {
      value: '示例内容'
    }
  }
});
    
Vue.component('ComB', {
  props: ['value'],
  template: `
    <div>
      组件B接收到:{{ value }}
    </div>`,
});
    
// 根实例
new Vue({
  el: '#app',
  data: {
    value: ''
  }
});

但如果不是兄弟呢?这时再逐层传递数据会十分繁琐,且执行效率很低。

②、EventBus

EventBus(事件总线)是一个独立的事件中心,用来对不同组件间的传值操作进行管理。

EventBus 通过一个独立的 Vue 实例(bus)来管理组件传值,不同组件传值时通过给 bus 设置事件、触发事件以达到数据传递的目的。总的来说,bus 中存储的只有用来传值的事件,而不存储数据。

③、操作方式

数据的传递过程一定会存在 发送方 与 接收方两个部分,接收方组件给 注册事件,发送方组件触发 bus 的对应事件,即可实现组件传值。

首先创建一个新的 Vue 实例作为 EventBus 的事件中心,通常设置为独立文件。

// EventBus.js
var bus = new Vue();

功能中,商品组件会进行个数操作,属于发送方,个数操作同时触发 bus 事件并传值。

// 商品组件
Vue.component('product-item', {
  template: `
    <div>
      <span>商品名称:苹果,商品个数: {{ count }}</span>
      <button @click="countIns">+1</button>
    </div>`,
  data () {
    return { count: 0 }
  },
  methods: {
    countIns () {
      bus.$emit('countChange', 1);
      this.count++;
    },
  }
});

计数组件需要统计个数,属于接收方,当组件实例创建后给 bus 注册对应事件接收数据,在 created() 中通过 $on() 进行设置。

// 计数组件
Vue.component('product-total', {
  template: `
    <p>总个数为:{{ totalCount }}</p>
  `,
  data () {
    return { totalCount: 0 }
  },
  created () {
    bus.$on('countChange', (productCount) => {
      this.totalCount += productCount;
    });
  }
});

最后创建根实例。

// 根实例
new Vue({
  el: '#app',
});


⑷、其他通信方式

下面的方式并不是推荐方式 ,只适合开发简单的 Vue 应用中使用,复杂应用使用可能会导致数据混乱难以维护,非必要情况不建议使用。这里作为补充讲解。


①、$root

$root 用于访问当前组件树根实例,设置简单的 Vue 应用时可以通过此方式进行组件传值

<!-- 父组件 -->
<div id="app">
  <p>父组件数据: {{ count }}</p>
  <com-a></com-a>
  <com-b></com-b>
</div>
// 父组件
new Vue({
  el: '#app',
  data: {
    count: 0
  },
  components: {
    ComA,
    ComB
  }
});
// 组件 A: 要书写在根实例创建之前
var ComA = {
  template: `
    <div>
      组件 A : {{ $root.count }}
      <button @click="clickFn">+1</button>
    </div>
  `,
  methods: {
    clickFn () {
      this.$root.count++;
    }
  }
};
// 组件 B: 要书写在根实例创建之前
var ComB = {
  template: `
    <div>
      组件 B : {{ $root.count }}
      <button @click="clickFn">+1</button>
    </div>
  `,
  methods: {
    clickFn () {
      this.$root.count++;
    }
  }
};

另外,Vue.js 中还提供了 parentchildren 用于进行父子组件便捷访问。

②、$refs

$refs 用于获取设置了 ref 属性的 HTML 标签或子组件。

给普通 HTML 标签设置 ref 属性,可以通过 $refs 获取 DOM 对象。

<div id="app">
  <input type="text" ref="inp">
  <button @click="fn">按钮</button>
</div>
new Vue({
  el: '#app',
  methods: {
    fn () {
      this.$refs.inp.focus();
    }
  }
});

给子组件设置 ref 属性,通过 $refs 获取到的是子组件实例,需要在子组件渲染完毕后才能获取。

<!-- 父组件 -->
<div id="app">
  <com-a ref="comA"></com-a>
</div>
// 父组件
var vm = new Vue({
  el: '#app',    
  components: {
    ComA
  },
  mounted () {
    console.log(this.$refs);
    this.$refs.comA.value = '修改子组件数据';
  },
});
// 子组件
var ComA = {
  template: `<p>组件 A:{{ value }}</p>`,
  data () {
    return {
      value: '这是组件A的数据'
    }
  }
};

$refs 只会在组件渲染完成之后生效,且不是响应式数据,所以不要在模板或计算属性中使用。



3、组件插槽

组件插槽可以便捷的设置组件内容,让组件可以给 HTML 标签一样设置内容。


⑴、单个插槽


⑵、具名插槽


⑴、作用域插槽



4、内置组件

⑴、动态组件

动态组件 适用于多个组件需要频繁切换的场景。


⑵、keep-alive 组件

主要用于保留组件状态或避免组件重新渲染。


⑶、过渡组件

用于在 Vue 插入、更新或者移除 DOM 时,提供多种不同方式的应用过渡、动画效果。




七、Vue Router

Vue Router 是 Vue.js 的官方插件,用来快速实现单页应用。


1、单页应用

SPA(Single Page Application)单页面应用程序,简称单页应用。单页应用是网站功能的一种组织形式,与传统多页面网站不同,单页应用指的是网站的 “所有” 功能都在单个页面中进行呈现。


通过观察示例网站可以发现 SPA 具有以下特点:

  • 优点:
  1. 前后端分离开发,提高了开发效率。
  2. 业务场景切换时,局部更新结构。(无需刷新,可实现跳转动画)
  3. 用户体验好,更加接近本地应用。
  • 缺点:
  1. 不利于 SEO。
  2. 初次首屏加载速度较慢。需要单独进行加载时间优化。
  3. 但多页共用的资源只需要加载一次
  4. 功能集中在单页上,页面复杂度比较高。


2、前端路由

后端路由指的是 URL 与处理函数间的映射关系。前端路由与之类似,指的是 URL 与内容间的映射关系。


前端路由的组成部分:

  • n 个 URL
  • n 段内容(Vue 中为 n 个组件)
  • 映射关系(对应关系)


3、前端路由的实现

前端路由的原生实现有 Hash 与 History 两种方式。


⑴、Hash 方式

Hash 方式是通过 hashchange 事件监听 hash 变化,从而实现网页功能更新。明显的标志是 URL 中带有 #。

设置如下结构:

<body>
  <div>
    <a href="#/">首页</a>
    <a href="#/category">分类页</a>
    <a href="#/user">用户页</a>
  </div>
  <div id="container">
    这是首页功能
  </div>
</body>

通过 onhashchange 事件监听 hash 变化,并进行页面内容更新。

window.onhashchange = function () {
  var hash = location.hash.replace('#', '');
  var str = ''
  switch (hash) {
    case '/':
      str = '这是首页的功能';
      break;
    case '/category':
      str = '这是分类的功能';
      break;
    case '/user':
      str = '这是用户的功能';
      break;
  }
  document.getElementById('container').innerHTML = str;
};

对功能进行封装

var router = {
  // 路由存储位置
  routes: {},
  // 定义路由
  route: function  (path, callback) {
    this.routes[path] = callback;
  },
  // 初始化路由
  init: function () {
    var that = this;
    window.onhashchange = function () {
      var hash = location.hash.replace('#', '');
      that.routes[hash] && that.routes[hash]();
    };
  }
};
var container = document.getElementById('container');
// 定义路由
router.route('/', function () {
  container.innerHTML = '这是首页的功能';
});
router.route('/category', function () {
  container.innerHTML = '这是分类的功能';
});
router.route('/user', function () {
  container.innerHTML = '这是用户的功能';
});   
// 初始化路由
router.init();

以上为基础功能,不包含前进后退,分析后可得知前进后退的实现较为繁琐。

总结 Hash 方式特点:

  • Hash 方式兼容性好。
  • 地址中具有 #,不太美观。
  • 前进后退功能较为繁琐。

⑵、History 方式

History 方式实现为 HTML5 提供的新功能,是前端路由实现的新标准。

<body>
  <div>
    <a href="/">首页</a>
    <a href="/category">分类页</a>
    <a href="/user">用户页</a>
  </div>
  <div id="container">
    这是首页功能
  </div>
</body>

由于 History 不是 a 标签的默认行为,所以在操作时需要通过 history.pushState() 变更 URL

pushState() 参数如下:

  • state :与指定路径相关的状态对象,popstate 事件触发时,可通过事件对象的 state 接收副本。
  • 不需要时填 null。
  • title:浏览器还不支持,填 ‘’ (空字符串)即可。
  • URL:新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个网址。
var router = {
  // 存储路由
  routes: {},
  // 定义路由
  route: function (path, callback) {
    this.routes[path] = callback;
  },
  // 触发指定路由规则
  go: function (path) {
    history.pushState(null, null, path);
    this.routes[path] && this.routes[path]();
  }
}

设置测试功能,注意要阻止 a 标签默认跳转。

// 使用测试
var links = document.querySelectorAll('a');
var container = document.querySelector('#container');    
links.forEach(function (ele) {
  ele.onclick = function (e) {
    var path = e.target.getAttribute('href');
    // 调用路由
    router.go(path);
    return false; // 阻止跳转
  }
}); 

// 定义路由规则
router.route('/', function () {
  container.innerHTML = '这是首页的功能';
});
router.route('/category', function () {
  container.innerHTML = '这是分类的功能';
});
router.route('/user', function () {
  container.innerHTML = '这是用户的功能';
});  

实现前进后退功能时,需要通过 pushState()state 传递路由标记,并通过 popstate 事件的事件对象访问 state 来获取路由标记,并触发对应功能。

go: function (path) {
  history.pushState({ path: path }, null, path);
  ...
}

设置 init 方法,监听前进后退的触发。

init: function () {
  var that = this;
  window.addEventListener('popstate', function (e) {
    var path = e.state ? e.state.path: '/';
    that.routes[path] && that.routes[path]();
  });
}

进行初始调用

...
router.init();
...

这里总结 History 方式特点:

  • 实现方式简洁。
  • 在 URL 显示上更符合前端路由格式。
  • pushState() 可存储序列化后最大 640k 的数据,hash 方式为 2k。
  • 兼容性差,ie10 开始支持。
  • 刷新时会访问对应路径,需要后端配合处理。


4、Vue Router

Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。


⑴、基本使用

Vue Router 提供了用于进行路由设置的组件 <router-link><router-view>

  • 使用 <router-link> 组件来导航,用 to 属性指定链接,组件默认会渲染为 <a> 标签,可通过 tag 属性设置为其他标签。
  • Vue Router 的路由是以组件的单位的,<router-view> 用来显示路由匹配到的组件。
<div id="app">
  <router-link to="/">首页</router-link>
  <router-link to="/category">分类</router-link>
  <router-link to="/user">用户</router-link>
  <router-view></router-view>
</div>

提前定义路由中的组件(配置对象)。

var Index = {
  template: `<div>这是首页的功能</div>`
};
var Category = {
  template: `<div>这是分类的功能</div>`
};
var User = {
  template: `<div>这是用户的功能</div>`
};

定义路由规则

var routes = [
  { path: '/', component: Index },
  { path: '/category', component: Category },
  { path: '/user', component: User }
];

创建 Vue Router 实例,通过 routes 属性配置路由

var router = new VueRouter({
  routes: routes
});

创建 Vue 实例,通过 router 属性注入路由

var vm = new Vue({
  el: '#app',
  router: router
});

输出 Vue 实例,观察 route与router

命名视图

如果导航后,希望同时在同级展示多个视图(组件),这时就需要进行命名视图。

多个视图通过多个 表示,并设置不同的 name 属性;如果未设置,默认为 default。

<div id="app">
  <router-link to="/">首页</router-link>
  <router-link to="/user">用户</router-link>
    
  <router-view name="sidebar"></router-view>
  <router-view></router-view>
</div>

设置命名视图后,路由中通过 components 属性进行设置(注意不是 component,而是 components)。

var SideBar = {
  template: `<div>这是侧边栏功能</div>`
};
// 定义路由规则
var routes = [
  { 
    path: '/', 
    components: {
      sidebar: SideBar,
      default: Index
    }
  },
  {
    path: '/user', 
    components: {
      sidebar: SideBar,
      default: User
    }
  }
];

⑵、动态路由

当我们需要将某一类 URL 都映射到同一个组件,就需要使用动态路由。

定义路由规则时,将路径中的某个部分使用 : 进行标记,即可设置为动态路由。

var User = {
  template: `<div>这是用户的功能</div>`
};
var routes = [
  { path: '/user/:id', component: User }
];

设置为动态路由后,动态部分为任意内容均跳转到同一组件。(示例中为用户名或用户 ID)

<div id="app">
  <router-link to="/user/1">用户1</router-link>
  <router-link to="/user/2">用户2</router-link>
  <router-link to="/user/3">用户3</router-link>
  <router-view></router-view>
</div>

带有 : 的部分对应的信息称为 路径参数,存储在 vm.$route.params 中。

var User = {
  template: `
	<div>
	  这是用户 {{ $route.params.id }} 的功能
	</div>`
};

①、侦听路由参数

使用动态路由时,如果仅为路由参数变化,组件实例会被复用而不是销毁后再重新创建。这意味着组件的生命周期钩子不会再调用。

如果希望在路由参数变化时作出响应,可以通过 watch 监听 $route

var User = {
  template: `<div>这是 {{ $route.params.id }} 功能</div>`,
  watch: {
    $route (route) {
      console.log(route);
    }
  }
};

②、路由传参处理

在组件中使用 $route 会让组件与路由高度耦合,如果希望组件可以在其他路由中使用,需要进行解耦。

通过路由的 props 设置数据,并通过组件 props 接收,从而将组件与路由解耦。本质上是将 $route.params 的值传递给组件 props。

var routes = [
  { 
    path: '/user/:id',
    component: User
  },
  { 
    path: '/category/:id', 
    component: Category,
    // props: true 表示将路径参数设置为组件属性(而组件属性可以通过组件的 props 接收)
    props: true
  },
];
var User = {
  template: `<div>这是 {{ $route.params.id }} 的功能</div>`
};
var Category = {
  props: ['id'],
  template: `<div>这是 {{ id }} 功能</div>`
};

如果路由包含多个命名视图,则需要将路由的 props 设置为对象,以给每个视图添加 props 选项。

{ 
  path: '/category/:id', 
  components: {
    default: Category,
    sidebar: SideBar
  },
  props: {
    default: true,
    sidebar: false 
  }
}
var SideBar = {
  template: `<div>这是侧边栏功能</div>`
};

如果希望设置静态数据,可将 props 中的某个组件对应的选项设置为对象,内部属性会绑定给组件的 props。

{ 
  path: '/category/:id', 
  components: {
    default: Category,
    sidebar: SideBar,
    sidebar2: SideBar2
  },
  props: {
    default: true,
    sidebar: false,
    sidebar2: { a: '状态1',b: '状态2'}
  }
},
var SideBar2 = {
  props: ['a', 'b', 'c', 'id'],
  template: `
    <div>
      这是右侧侧边栏功能: {{ a }} {{ b }} {{ c }}
    </div>
  `
};

⑶、嵌套路由

实例场景中,路由通常由多层嵌套的组件组合而成,这时需要使用嵌套路由配置。

使用 children 来进行嵌套路由中的子路由设置。

var routes = [
  { 
    path: '/user', 
    component: User,
    children: [
      { 
        path: 'hobby', 
        component: UserHobby
      },
      { 
        path: 'info', 
        component: UserInfo,
        children: [
          { path: 'age', component: UserInfoAge },
          { path: 'school', component: UserInfoSchool },
        ]
      }
    ]
  }
];

⑷、编程式导航

除了可以通过 来定义声明式导航,还可以通过 router 实例方法设置编程式导航。

router.push() 用来导航到一个新 URL

参数为路径信息,可以为字符串或对象

vm.$router.push('/user');
vm.$router.push({path: '/user'});
vm.$router.push({path: '/user/123'});

可以将 的导航设置在 :to 属性中,点击时内部也会调用 push()。

<router-link :to="{ path: '/user/10' }">用户10</router-link>

除了 push 外,Vue Router 还提供了 replace()、go()、forward()、back() 方法。

命名路由

有时候,通过名称来表示一个路由更加方便,尤其是在导航到一些较长的路由的时候(如嵌套路由时)。

设置路由时添加 name 属性。

var School = {
  template: `<div>School 组件:{{ $route.params }}</div>`
};
var routes = [
  {
    path: '/user/:id/info/school',
    name: 'school',
    component: School
  }
];

在 push() 中通过 name 导航到对应路由,参数通过 params 对象设置。(编程式导航)
params 只能与 name 搭配使用,如果使用 path 则 params 无效。

vm.$router.push({ name: 'school', params: { id: 20, demo: '其他数据' }});

也可以在 中使用。(声明式导航)

<router-link :to="{ name: 'school', params: { id: 1 } }">用户学校</router-link>
<router-link :to="{ name: 'school', params: { id: 2 } }">用户学校</router-link>
<router-link :to="{ name: 'school', params: { id: 3 } }">用户学校</router-link>

⑸、其他功能

①、重定向

用户访问 URL1 时,实际被替换为了 URL2 并匹配了对应路由,这就是重定向。

例如,设置路由 /category/:id 用来访问某个分类功能,但如果 URL 为 /category 则没有任何意义,这时可以进行重定向。

 var routes = [
   { path: '/', component: Index},
   { path: '/category/:id', component: Category },
   { path: '/category', redirect: '/' }
 ];

②、别名

别名是一种美化路由的方式。

别名指的是用户访问 URL1 时,实际上匹配了 URL2 对应的路由,但 URL 还保持为 URL1。假定有路由 /user/:id/info/school/intro/:date 这时 URL 看起来十分复杂,我们可以设置为别名为例如 /10/20200910。

var routes = [
  {
    path: '/user/:id/info/school/intro/:date',
    name: 'school',
    component: School,
    alias: '/:id/:date'
  }
];
<router-link :to="{ name: 'school', params: { id: 1, date: 0101 } }">用户学校</router-link>
<router-link to="/10/0612">用户学校</router-link>

③、导航守卫

导航守卫用于在路由发生改变时,通过跳转或取消的方式来守卫导航。

例如,网站的用户页,如果用户没有登录,则无法访问,这时就可以给 router 使用导航守卫。

router.beforeEach(function (to, from, next) {
  console.log(to, from);
  next();
});
  • next() 用来继续执行后续功能
  • next(false) 用于阻止本次导航,
  • next(’/’) 者 next({ path: ‘/’ }) 用于中止本次导航,并进行一个新的导航。
  • 必须确保 next 在任何导航守卫中都被严格调用一次(最多一次,最少也一次)。

④、History 模式

vue-router 默认 hash 模式,如果需要,可通过实例属性设置为 History 模式。

需要通过 Vue Router 实例的 mode 选项来设置,这样 URL 会更加美观,但同样需要后端支持避免问题。

var router = new VueRouter({
  mode: 'history',
  routes: [
    ...
  ]
});



八、Vue CLI

Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统,称为脚手架工具。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

后海 0_o

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值