此笔记是我在拉勾学习课程过程中的总结,文字原创,笔记里很多技巧和知识是老师总结的,如果有侵权,请联系本人!
组件化开发、Vue Router、VueCLI
Vue.js组件
用于将每个区域进行封装,将结构、样式、逻辑代码封装为整体。
提高功能复用性和可维护性。
组件使用时为自定义的html标签,通过组件作名为自定义标签名
组件的注册
全局注册
全局注册的组件可以用于任意实例或组件中Vue.component('组件名',{选项对象})
注:
全局注册必须在根Vue实例之前创建,需要在根实例之内使用
<div id="app">
<p>这是p标签</p>
<my-compoment></my-compoment>
</div>
<script src="lib/vue.js"></script>
<script>
// 全局组件
Vue.component('my-compoment',{
template: '<div>这是第一个我们自己的组件</div>'
});
// 根实例
new Vue({
el:'#app',
data: {
}
});
</script>
组件基础
组件是可服用的Vue实例,可以与 new Vue接收相同的选项,例如data,methods以及生命周期钩子等。
但是 el 这种跟实例特有的选项不行
组件命名规则:
· kebab-case:"my-component"
· PascalCase:"MyComponent"
注意:
无论哪种命名方式,dom 中只有kebab-case方法命名可用-
template选项
· 用于设置组件结构,最终被引入根实例或者其他组件中· template只能有一个根元素,不可在根元素同级设置根元素
-
data选项
· data用于存储数据,但是组件的data必须为函数,数据设置在返回值对象中
· 这种实现方式是为了确保每个组件实例可以维护一份被返回的对象独立的拷贝,不会相互影响
局部注册
- 只用于当前实例或组件中
new Vue({
el: '#app',
data: {
},
// 组件
components: {
// 组件a
'my-com-a': {
template:`
<div>
<h3>{{title}}</h3>
<p>{{content}}</p>
</div>
`,
data () {
return {
title:'组件 A 标题',
content:'组件 A 内容'
}
}
},
// 组件b
'my-com-b': {
template:`
<div>
<h3>{{title}}</h3>
<p>{{content}}</p>
</div>
`,
data () {
return {
title:'组件 B 标题',
content:'组件 B 内容'
}
}
}
}
});
- 单独配置组件的选项对象
<div id="app">
<my-com-a></my-com-a>
<my-com-b></my-com-b>
</div>
<script src="lib/vue.js"></script>
<script>
// 组件a的选项对象
var MyComponentA = {
// 组件a
template:`
<div>
<h3>{{title}}</h3>
<p>{{content}}</p>
</div>
`,
data () {
return {
title:'组件 A 标题',
content:'组件 A 内容'
}
}
};
var MyComponentB = {
template:`
<div>
<h3>{{title}}</h3>
<p>{{content}}</p>
</div>
`,
data () {
return {
title:'组件 B 标题',
content:'组件 B 内容'
}
}
}
new Vue({
el: '#app',
data: {
},
// 组件
components: {
//组件a
'my-com-a': MyComponentA, //两种书写方式均可,但是第二种跟更简洁
// 组件b
MyComponentB
}
});
</script>
组件通信
父组件向子组件传值
通过子组件的 props 选项接收父组件的传值。
props不要与data存在同名属性
<!-- 通过 v-for 遍历数据 items,创建组件并生成内容 -->
<div id="app">
<!-- 这里的key是绑定给v-for来帮助渲染的 -->
<demo-item
v-for="item in items"
:key="item.title"
:item-title="item.title"
:item-content="item.content"
></demo-item>
</div>
<script src="lib/vue.js"></script>
<script>
Vue.component('demoItem',{
props: ["itemTitle", "itemContent"],
template: `
<div>
<h3>{{ itemTitle }}</h3>
<p>{{ itemContent }}</p>
</div>
`
})
new Vue({
el: '#app',
data: {
// 给子组件准备的数据
items:[
{
title:'这是标题1',
content:'这是内容1'
},
{
title:'这是标题2',
content:'这是内容2'
},
{
title:'这是标题3',
content:'这是内容3'
}
]
}
})
</script>
- props命名规则:建议使用驼峰命名,父组件绑定时用烤串命名
<my-component
:item-title="item.title"
:item-content="item.content"></my-component>
</div>
<script src="lib/vue.js"></script>
<script>
Vue.component('my-component',{
props:['itemTitle','itemContent'],
- 单向数据流
父子组件间所有的prop都是单项下行绑定的,(只能向子组件传,不能反向影响父组件)
如果子组件要处理prop数据,应当存储在data中后操作。
如果prop为数组或对象,,传递进来的是引用,子组件操作将影响到父组件的状态。
<div id="app">
<my-component
:initial-title="title"
></my-component>
</div>
<script src="lib/vue.js"></script>
<script>
Vue.component('my-component',{
props:["initialTitle"],//接收父组件的data,传给data()处理
template: `
<div>
{{ title }}
<button @click="fn">按钮</button>
</div>
`,
data () {
return{
title: this.initialTitle//处理完了给赋值表达式
}
},
method: {
fn () {
// this.title = "这是新标题";//父组件数据变化,引起子组件数据变化
// this.initialTitle = "这是新标题";//子组件数据变化,不引起父组件变化
this.obj.name = 'jack';//根元素和原来数据都会发生变化
}
}
})
new Vue({
el: '#app',
data: {
title:'这是示例内容',
obj : {
name:'william',
age:18
}
}
});
</script>
props类型
· 如果需要prop类型检查,就需要进行规则设置,如设置成一个对象解构,通过 键值对 对应。
· props中的某一个prop可以同时制定多种类型,可以写成数组类型props验证
· 当prop需要设置多种规则时,可以将prop设置为选项对象
· 之前类型检测可以通过type选项设置
· required 用于设置数据为必填项
· default 用于给可选项设置默认值,当父组件未传值时生效。
· default为数组或对象,必须为工厂函数返回的形式
· validator 用于传入prop设置校验函数,return值为 false 时发出警告
· 验证函数中无法使用实例data、methods功能,无法使用this
<div id="app">
<my-component
:par-str="str"
:par-num="num"
:par-num2="2000"
:par-arr="arr"
par-content="lagou: hello world"
></my-component>
</div>
<script src="lib/vue.js"></script>
<script>
Vue.component('MyComponent', {
props: {
parStr: {
// 当prop需要设置多种规则时,可以将prop设置为选项对象
// 之前类型检测可以通过type选项设置
type: String
},
parData: {
type: [Array, Object]
},
parNum: {
type: Number,
//required表示此处必须传值,不传报错
required: true
},
parNum2: {
type: Number,
// 默认值为100,如果传值了,就为传的值
default: 100
},
parArr: {
type: Array,
// 如果传的值是数组或对象,必须为工厂函数返回的形式
default () {
return [1, 2, 3];
}
},
parContent: {
type: String,
// 用于传入prop设置校验函数,return值为false时发出警告,value是传入的值,是html元素中设置的对应项的值
//验证函数中无法使用实例data、methods功能,无法使用this
validator (value) {
console.log(this);
return value.startsWith('lagou');
}
}
},
template: `<div></div>`
})
new Vue({
el: '#app',
data: {
str: '示例内容',
num: 100,
arr: [10, 20, 30],
obj: {
content1: '内容1',
content2: '内容2'
}
}
});
</script>
当父组件给子组件设置了属性,但此属性在props中不存在,会自动绑定到子组件的根元素上
如果根元素已经存在了对应属性,则会被组件内属性替换,class和style例外,会进行合并。
如果不希望继承父组件属性,可以设置inheritAttrs:false,但是只适用于普通属性,class与style不受影响
<div id="app">
<!-- 自定义属性也可以绑定 -->
<my-component
:class="colorRed"
style="background-color:red;"
:title="'示例标题内容'"
data-index = "3"
></my-component>
</div>
<script src="lib/vue.js"></script>
<script>
Vue.component('my-component', {
// 如果不希望继承父组件属性,可以设置inheritAttrs:false
inheritAttr:false,
//class和style属性会合并,其他属性会被组件内属性代替
template:`
<div data-index="6"
title="旧的title"
class="abc"
style="width: 200px;">
<p>这是组件内容</p></div>
`
})
new Vue({
el: '#app',
data: {
}
});
</script>
子组件向父组件传值
- 子组件数据变化时,通过$emit()触发自定义事件,自定义事件名称需要用 kebab-case
- 父组件监听子组件事件,并设置处理程序
<h3>购物车</h3>
<!-- @count-increase接收一个值或者一个功能函数 -->
<div id="app">
<product-item
v-for="item in product"
:key="product.id"
:title="product.title"
@count-increase="totalCount++"
></product-item>
<div>购物车总数:{{ totalCount }}</div>
</div>
<script src="lib/vue.js"></script>
<script>
Vue.component('product-item',{
props:['title'],
template:`
<div>
<span>商品名称:{{title}},商品个数:{{count}}</span>
<button @click="Increase">+1</button>
</div>
`,
data() {
return {
count:0
}
},
methods:{
Increase() {
//$emit向父级传递数据
this.$emit('count-increase')
this.count++;
}
}
})
new Vue({
el: '#app',
data: {
product: [
{ id:1, title:'橘子一斤' },
{ id:2,title:"香蕉一斤"},
{id:3,title:"苹果一斤"}
],
totalCount:0
}
});
</script>
- 自定义事件传值
子组件触发事件向父组件传值,父组件在监听事件时接收子组件传递的数据
<div id="app">
<h3>购物车</h3>
<product-item
v-for="product in products"
:key="product.id"
:title="product.title"
@count-change="onCountChange"
></product-item>
<p>商品总个数为:{{ totalCount }}</p>
</div>
<script src="lib/vue.js"></script>
<script>
// 子组件
Vue.component('ProductItem', {
props: ['title'],
template: `
<div>
<span>商品名称: {{ title }}, 商品个数: {{ count }}</span>
<button @click="countIns1">+1</button>
<button @click="countIns5">+5</button>
</div>
`,
data () {
return {
count: 0
}
},
methods: {
countIns1 () {
this.$emit('count-change', 1);
this.count++;
},
countIns5 () {
this.$emit('count-change', 5);
this.count += 5;
}
}
});
// 父组件
new Vue({
el: '#app',
data: {
products: [
{
id: 1,
title: '苹果一斤'
},
{
id: 2,
title: '香蕉一根'
},
{
id: 3,
title: '橙子一个'
}
],
totalCount: 0
},
methods: {
onCountChange(productZongshu) {//这是模板传回来的参数
// console.log(productZongshu);
this.totalCount += productZongshu;
}
}
});
</script>
- 组件与v-model
v-model用于组件时,需要通过props与自定义事件实现
<div id="app">
<p>输入的内容是:{{ inputValue }}</p>
<com-input v-model="inputValue"></com-input>
</div>
<script src="lib/vue.js"></script>
<script>
var ComInput = {
props:['value'],
template:`
<input
type="text"
:value="value"
@input='onInput'>`,
methods: {
onInput (event) {
this.$emit('input', event.target.value)
}
}
}
// 根实例
new Vue({
el: '#app',
data: {
inputValue:''
},
components: {
ComInput
}
});
</script>
非父子组件传值
- 兄弟组件传值,通过父组件中转
<div id="app">
<!-- 父组件接收子组件a的数据 -->
<com-a
@value-change="value = $event"
></com-a>
<!-- 父组件将数据传递给子组件b -->
<com-b
:value = "value"
></com-b>
</div>
<script src="lib/vue.js"></script>
<script>
// 子组件a: 发送数据
Vue.component('com-a',{
template:`
<div>
组件a的内容:{{ value }}
<button @click="$emit('value-change',value)"></button>
</div>
`,
// 通过数据声明,向外部传递数据
data () {
return {
value:'这是组件a中的内容'
}
}
});
// 子组件b:接收数据
Vue.component('com-b',{
props:['value'],
template:`
<div>
组件b接收的数据:{{ value }}
</div>
`
});
// 根实例(父组件)
new Vue({
el: '#app',
data: {
// 用于数据中专
value:''
}
})
</script>
-
EventBus
EventBus(事件总线)是一个独立的事件中心,用于管理不同组件之间的传值。
EventBus通过一个新的Vue实例来管理组件传值操作,组件通过给实例注册事件、调用事件实现数据传递。
操作步骤:
发送数据的组件触发bus事件,接收的组件给bus注册对应事件
给bus注册对应事件通过$on()操作。
// 商品组件
Vue.component('product-item',{
template:`
<div>
<span>商品名称:苹果,商品个数:{{ count }}</span>
<button
@click="countIns"
>+1</button>
</div>`,
data() {
return {
count:0
}
},
methods: {
countIns () {
// 给bus触发自定义事件,传递数据
bus.$emit('countChange', 1)
this.count++;
}
}
})
// 计数组件
Vue.component('product-total',{
template:`
<p>商品总数是:{{ totalCount }}</p>
`,
data () {
return {
totalCount:0
}
},
created () {
// 给 bus 注册事件,并接受数据,接受函数名,并调用回调函数
bus.$on('countChange', (productCount) => {
this.totalCount += productCount;
})
// 实例创建完毕,可以使用 data 等功能
}
})
-
其他通信
了解即可 $root $refs不建议在功能中使用,会操作其他组件内部数据,出现问题时,不容易找出问题所在
$root 用于访问当前树根实例,设置简单的Vue应用
<div id="app">
<com-a></com-a>
</div>
<script src="lib/vue.js"></script>
<script>
// 根实例的子组件A的子组件B
var ComB = {
template: `
<div>
组件B: {{ $root.count }}
<button @click="clickFn">按钮</button>
</div>
`,
methods: {
clickFn () {
this.$root.count = 200;
}
}
};
// 子组件A
var ComA = {
template: `
<div>
组件A: {{ $root.count }}
<button @click="clickFn">按钮</button>
<com-b></com-b>
</div>
`,
methods: {
clickFn () {
this.$root.count = 100;
}
},
components: {
ComB
}
};
// 根实例
new Vue({
el: '#app',
data: {
count: 0
},
components: {
ComA
}
});
</script>
- $refs用于获取设置了ref属性的HTML标签或子组件。
基本操作方式:
给普通HTML标签设置ref属性,$refs可以获取DOM对象 访问含有ref属性的元素
给组件设置 ref 属性,获取的是DOM对象结构,渲染可以通过 $refs 获取子组件实例
<div id="app">
<!-- 给 HTML 标签设置 ref 属性 -->
<input type="text" ref="inp">
<button @click="fn">按钮</button>
<!-- 给子组件设置 ref 属性 -->
<com-a ref="comA"></com-a>
</div>
<script src="lib/vue.js"></script>
<script>
var ComA = {
template: `<div>组件A的内容:{{ value }}</div>`,
data () {
return {
value: '示例内容'
}
}
}
var vm = new Vue({
el: '#app',
methods: {
fn () {
// 点击后修改 HTML 标签焦点状态
this.$refs.inp.focus();
this.$refs.comA.value = '新的内容';
}
},
components: {
ComA
},
mounted () {
console.log(this.$refs);
this.$refs.comA.value = "修改后的内容";
},
});
</script>
组件插槽
单个插槽
用来像HTML一样在标签之间书写内容
- 通过<slot>进行插槽设置。需要注意模板内容渲染位置:
父组件的数据都是在父组件中编译执行的,
子组件的数据都是在子组件中编译执行的。
插槽可以设置默认值,成为后备内容
<div id="app">
<com-a>这是组件的内容</com-a>
<com-a>
这是第二个组件的内容:
<span>这是span的内容</span>
</com-a>
<com-a>
这里是父组件的视图模板,只能使用父组件的数据:
{{ parValue }}
</com-a>
</div>
<script src="lib/vue.js"></script>
<script>
Vue.component('ComA', {
//插槽中没有内容时,会采用<slot>中间的默认内容
template: `
<div>
<h3>组件标题</h3>
<slot>
这是插槽的默认内容
</slot>
</div>
`,
data() {
return {
value: '子组件的数据'
}
}
});
new Vue({
el: '#app',
data: {
parValue: '这是父组件的数据'
}
})
</script>
具名插槽
- 组建中有多个位置需要设置插槽,根据需要,给<slot>设置name,成为具名插槽。
插入一张slot图片
<div id="app">
<com-a>
<!-- 这里记得是对象书写形式,不是属性书写方式 -->
<template v-slot:header>
<h3>组件头部内容</h3>
</template>
<!-- <template #default>
<p>组件主体内容1</p>
<p>组件主体内容2</p>
</template> -->
<p>组件主体内容1</p>
<p>组件主体内容2</p>
<!-- #footer是v-slot:footer的简写方式 -->
<template #footer>
<p>组件底部内容</p>
</template>
</com-a>
</div>
<script src="lib/vue.js"></script>
<script>
// 子组件
Vue.component('ComA',{
template:`
<div>
<header>
// 这里正常书写name='header'
<slot name='header'></slot>
</header>
<main>
<slot></slot>
</main>
<footer >
<slot name="footer"></slot>
</footer>
</div>
`
});
new Vue({
el: '#app',
data: {
}
});
</script>
作用域插槽
用于让插槽可以使用子组件的数据,
组件需要使用的数据通过v-bind绑定给<slot>,
这种用于给插槽传递数据的属性成为插槽prop组件绑定数据后,通过v-slot接受数据
可以通过es6的解构操作进行数据接收 <com-a v-slot:default="{ value }">
<div id="app">
<!-- 多个插槽作用域插槽的书写方式 -->
<com-a>
<template v-slot:default="dataObj">
{{ dataObj.value }}
{{ dataObj.num }}
</template>
<template v-slot:footer="dataObj">
{{ dataObj.value }}
</template>
</com-a>
<!-- 只具有默认插槽的作用域插槽的书写方式 -->
<!-- <com-b v-slot="dataObj"> -->
<com-b v-slot="dataObj">
{{ value }}
</com-b>
<!-- 通过es6 的解构操作接收作用域插槽的数据 -->
<com-b #default="{value,num}">
{{value}}
{{num}}
</com-b>
</div>
<script src="lib/vue.js"></script>
<script>
// 子组件b
var ComB = {
template:`
<div>
<p>组件b的内容:</p>
<slot
:value="value"
:num="num"
></slot>
<slot name = "footer"
:value="value"></slot>
</div>
`,
data() {
return {//返回的就是 dataObj 数据对象
value :'这是组件a内的数据',
num:200
}
}
}
// 子组件a
var ComA = {
template:`
<div>
<p>组件a的内容:</p>
<slot
v-bind:value="value"
:num="num"
></slot>
<slot name = "footer"
:value="value"></slot>
</div>
`,
data() {
return {//返回的就是 dataObj 数据对象
value :'这是组件a内的数据',
num:100
}
}
}
new Vue({
el: '#app',
components: {
ComA,ComB
}
});
</script>
内置组件
动态组件
组件切换处理操作
<component> 用于将一个‘元组件’渲染为动态组件,以 is 属性决定渲染哪个组件
用于实现多个组件的快速切换,例如选项卡效果
is 属性会在每次切换组件时,创建一个新的组件实例
<div id="app">
<!-- 按钮代表选项卡的标题功能 -->
<button
v-for="title in titles"
:key="title"
@click="currentCom = title"
>{{ title }}</button>
<!-- component 设置动态组件 -->
<component :is="currentCom"></component>
</div>
<script src="lib/vue.js"></script>
<script>
// 这是要切换的子组件的选项对象
var comA = {
template:`<p>这是组件a的内容:<input type="text"></p>`
};
var comB = {
template:`<p>这是组件b的内容:<input type="text"></p>`
};
var comC = {
template:`<p>这是组件c的内容:<input type="text"></p>`
};
new Vue({
el: '#app',
data:{
// 所有组件名称
titles: ["comA","comB","comC"],
// 当前组件名称
currentCom:'comA'
},
components: {
// 注册组件
comA,comB,comC
}
});
</script>
keep-alive组件
组件动态切换的缓存处理
- 主要用于保留组件状态或避免组件重新渲染
利用 <keep-alive max="2">
<component :is="currentCom"></component>
</keep-alive>
包裹,保证组件切换中进行缓存,不会被移除
keep-alive需要记住的属性:
- include属性用于指定哪些组件会被缓存,具有多种设置方式
<!-- <keep-alive include="ComA,ComB,ComC"> -->
<!-- <keep-alive :include="['ComA', 'ComB', 'ComC']"> -->
<!-- <keep-alive :include="/Com[ABC]/"> -->
- exclude 指定哪些组件不会被缓存
<!-- <keep-alive exclude="ComD"> -->
<!-- <keep-alive :exclude="['ComD']"> -->
<!-- <keep-alive :exclude="/ComD/"> -->
- max 属性用于设置最大缓存个数:
距离最近的max个被缓存
<div id="app">
<button
v-for="title in titles"
:key="title"
@click="currentCom = title"
>
{{title}}
</button>
<keep-alive max="2">
<!-- 动态组件 -->
<component :is="currentCom"></component>
</keep-alive>
</div>
过渡组件
控制切换效果和动画效果
transition组件
-
用于给元素进入或离开过渡
· 条件渲染 v-if
· 条件展示 v-show
· 动态组件<component>
· 组件根节点
提供了6个class,用于设置过渡的具体效果
进入类名: v-enter(入场前的样式)
v-enter-to(入场完毕之后样式)一般不做设置
v-enter-active(入场过渡)
离开类名:v-leave(离场前)一般不做设置
v-leave-to(离场后)
v-leave-active(离场过渡)
属性
给组件设置name属性,用于给多个元素、组件设置不同的过渡效果,这时需要将 v- 改为对应的 name- 形式
通过 appear 属性,可以让组件在初始渲染时实现过渡
<style>
/* 第一组过渡效果设置 */
.v-enter, .v-leave-to {
opacity: 0;
}
.v-enter-active, .v-leave-active {
transition: opacity .5s;
}
/* 第二组过渡效果设置 */
.demo-enter, .demo-leave-to {
opacity: 0;
transform: translateX(200px);
}
.demo-enter-active, .demo-leave-active {
transition: all .5s;
}
</style>
</head>
<body>
<div id="app">
<button @click="show = !show">切换1</button>
<!-- 没有设置name命名的 transition 组件,style中类名以 v-开头 -->
<transition appear>
<p v-if="show">这是要切换的元素1</p>
</transition>
<button @click="showDemo = !showDemo">切换2</button>
<!-- 设置了name 的transition 组件,类名需要将 v- 修改为 demo- -->
<!-- 通过 appear 属性,可以让组件在初始渲染时实现过渡 -->
<transition
name="demo"
appear>
<p v-if="showDemo">这是要切换的元素2</p>
</transition>
</div>
<script src="./lib/vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
show: true,
showDemo: true
}
});
自定义过渡类名
自定义类名比普通类名优先级高,才能替代第三方动画库
-
用于自定义过渡类名的属性
· enter-class 入场初始状态· enter-active-class 入场过程
· enter-to-class 入场最终效果
· leave-class 入场初始状态
· leave-active-class 入场过程
· leave-to-class 入场最终效果
-
用于初始过渡类名的属性:
· appear-class· appear-to-class
· appear-active-class
.test {
transition: all 3s;
}
</style>
</head>
<body>
<div id="app">
<button @click="show = !show">切换</button>
<!-- 自定义类名的优先级更高 -->
<transition
enter-active-class="test"
leave-active-class="test"
>
<p v-if="show">这是 p 标签</p>
</transition>
</div>
-
Animate.css 是一个第三方动画库,通过设置类名给元素添加效果
注意点
· animate__ 前缀与compat版本· 基础类名 animate__animated 设置
<link
rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"
/>
<!-- 不需要添加 animate__ 的兼容版本,但是官方建议使用完整版本 -->
<!-- "https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.0.0/animate.compat.css" -->
</head>
<body>
<div id="app">
<button @click="show = !show">按钮</button>
<!-- 通过自定义过渡类名设置,给组件添加第三方动画库的类名效果 -->
<transition
enter-active-class="animate__bounceInDown"
leave-active-class="animate__bounceOutDown"
>
<!-- 必须给要使用动画的元素设置基础类名 animate__animated -->
<p
v-if="show"
class="animate__animated"
>hello world</p>
</transition>
</div>
transition-group组件
-
<transition-group> 用于给列表统一设置过渡动画
· tag 属性用于设置容器元素,默认为<span>
· 过渡应用于内部元素,不是容器
· 子节点必须有独立的key,动画才能正常工作
· 当列表元素变更导致元素位移,可以通过 .v-move 类名设置移动效果
<style>
ul {
position: relative;
}
.v-enter, .v-leave-to {
opacity: 0;
transform: translateX(100px);
}
.v-enter-active, .v-leave-active {
transition: all .5s;
}
/* 让元素在离场的过程中脱离标准流 */
.v-leave-active {
position: absolute;
}
.v-move {
transition: all .5s;
}
</style>
</head>
<body>
<div id="app">
<input type="text"
v-model="newTitle"
@keyup.enter="addItem"
>
<transition-group
tag="ul"
>
<li
v-for="item in items"
:key="item.id"
@click="removeItem(item)"
>
{{ item.title }}
</li>
</transition-group>
</div>
<script src="./lib/vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
items: [
{ id: 1, title: '示例内容1'},
{ id: 2, title: '示例内容2'},
{ id: 3, title: '示例内容3'},
{ id: 4, title: '示例内容4'},
{ id: 5, title: '示例内容5'},
],
newTitle: '',
latestId: 5
},
methods: {
addItem () {
this.items.push({
id: this.latestId + 1,
title: this.newTitle
});
this.latestId++;
this.newTitle = '';
},
removeItem (item) {
var i = this.items.indexOf(item);
this.items.splice(i, 1);
}
}
});
</script>
Vue Router
Vue Router是Vue.js的官方插件,用来快速实现单页应用
单页应用
SPA(Single Page Application)单页面应用程序,简称单页应用。指的是网站的 “所有” 功能都在单个页面
中进行呈现。
- 后台管理系统、移动端、小程序等适合单页面应用。
优点:
· 前后端分离开发,提高开发效率
· 业务场景切换时,局部更新结构
· 用户体验好,接近本地应用
缺点:
· 不利于SEO
· 初次加载速度慢
· 页面复杂度高
前端路由
-
URL 与内容之间的映射关系
前端路由的必备条件: URL、内容、映射关系
实现方式
Hash方式实现前端路由
通过hashchange 事件监听hash变化,进行网页内容更新
通过 onhashchange 事件监听 hash 变化,进行网页内容更新
- 封装hash函数以备复用
总结:
· hash兼容性好
· 地址中具有 # ,不太美观
· 前进后退功能较繁琐
<div>
<a href="#/">首页</a>
<a href="#/category">分类页</a>
<a href="#/user">用户页</a>
</div>
<div id="container">
这是首页功能
</div>
<script>
// 准备对象,用于封装 hash 功能
var router = {
// 路由存储位置:保存了 url 与 内容处理函数的对应关系
routes: {},
// 定义路由规则的方法
route: function (path, callback) {//path相当于键,callback相当于值
this.routes[path] = callback;
},
// 初始化路由的方法
init: function () {
window.onhashchange = function () {
// 将外部的router传进来
var that = this;
// 当 hash 改变,需要得到当前新的 hash
var hash = location.hash.replace('#','');
// 根据hash 触发 routes 对象中的对应 callback
// 利用逻辑与运算,前面成立,则执行 && 后面的内容,如果前面路由是真,则执行回调函数
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();
</script>
History方式实现前端路由
History 方式采用 html5 提供的新功能实现前端路由
在操作时通过 history.pushState() 变更url执行对应操作。
实现前进后退功能,首先需要再更改url时保存路由标记;通过popstate事件监听前进后退按钮操作,并检测 state;调用初始化方法监听操作。
<body>
<div>
<a href="/">首页</a>
<a href="/category">分类页</a>
<a href="/user">用户页</a>
</div>
<div id="container">
这是首页功能
</div>
<script>
var router = {
// 存储路由的对象
routes: {},
// 定义路由的方法
route (path, callback) {
this.routes[path] = callback;
},
// go 用于 触发 指定的路由操作
go (path) {
// 更改url,前进后退功能,需要pushstate方法将更改url时的标记存储在第一个参数中,方便后面popstate调用
history.pushState({path : path}, null, path);//第一个参数是与数据有关的,第二个参数浏览器目前不支持
// 触发路由对应的回调函数
this.routes[path] && this.routes[path]();
// console.log(this.routes[path]());
},
init() {
var that = this;
window.addEventListener('popstate', function(e) {//监听前进后退操作,并检测state
var path = e.state ? e.state.path : '/';
// 触发路由对应的回调函数
that.routes[path] && that.routes[path]();
});
}
};
// 初始化操作
router.init();
// 设置 a 标签 功能
var links = document.querySelectorAll('a');
var container = document.querySelector('#container');
links.forEach(function (ele) {
ele.addEventListener ('click', function (event) {
router.go(this.getAttribute('href'));
event.preventDefault();//防止跳转
});
});
// 定义路由规则
router.route("/", function () {
container.innerHTML = '这是首页功能';
});
router.route("/category", function () {
container.innerHTML = '这是分类功能';
});
router.route("/user", function () {
container.innerHTML = '这是用户功能';
});
</script>
总结:
history可以实现前进后退
url没有#,更加美观
hash传递数据最大2k,history的pushstate可以存储640k;
history只兼容HTML5
VueRouter
Vue.js官方路由管理器,让构建单页面应用更简单
基本使用
安装
- 直接下载 / CDN
• 最新版本:https://unpkg.com/vue-router/dist/vue-router.js
• 指定版本:https://unpkg.com/vue-router@3.4.9/dist/vue-router.js - npm
• npm install vue-router
基本使用
- Vue Router 提供了 <router-link>和 <router-view> 组件来进行路由设置
<router-link> 链接跳转,默认就是a标签,如果希望改变,可以用tag属性进行改变
<router-view> 进行视图区域变换,用来显示路由匹配到的组件
<router-link>使用
- 定义路由需要的组件,进行规则设置,
- 创建vue Router实例,通过 Routes属性配置路由
- 创建vue 实例,通过 router 属性注入路由。
<div id="app">
<!-- 设置用于进行路由操作的组件 -->
<router-link to="/">首页</router-link>
<router-link to="/user">用户</router-link>
<router-link to="/category">分类</router-link>
<!-- 用于切换显示组件 -->
<router-view></router-view>
</div>
<script src="lib/vue.js"></script>
<script src="lib/vue-router.js"></script>
<script>
console.log(VueRouter);
// 2.定义组件信息
var Index = {
template:`<div>这是首页信息</div>`
};
var User = {
template:`<div>这是用户信息</div>`
};
var Category = {
template:`<div>这是分类信息</div>`
};
// 1.定义路由规则
var routes1 = [
{ path: '/', component: Index},
{ path: '/user', component: User},
{ path: '/category', component: Category},
];
// 3. 创建 Vue Router实例
var router = new VueRouter({
// 配置对象属性routes,将配置好的路由规则加入进来
routes:[
{ path: '/', component: Index},
{ path: '/user', component: User},
{ path: '/category', component: Category},
]
});
// 4.创建vue 实例,注入router
new Vue({
el:"#app",
router
});
</script>
<router-view>基本使用
- 导航(跳转)后,希望统计展示多个视图(组件),这是就需要进行命名视图。
- 路由中通过 components 属性进行设置不同视图的对应组件。
// 侧边栏结构
var SideBar1 = {
template:`<div>侧边栏1功能</div>`
};
var SideBar2 = {
template:`<div>侧边栏2功能</div>`
};
var Index = {
template:`<div>首页功能</div>`
};
var User = {
template:`<div>用户功能</div>`
};
// 定义路由规则
var rules = [
{
path:'/',
components: {
// router-view 的name: 组件配置对象
default: Index,
sidebar: SideBar1
}
},
{
path:'/user',
components: {
// router-view 的name: 组件配置对象
default: User,
sidebar: SideBar2
}
}
];
// 创建 Vue Router 实例
var router = new VueRouter({
routes: rules
});
// 创建 Vue 实例
new Vue({
el: '#app',
router
});
</script>
动态路由处理
多个url对应一个组件的方式
应用场景:
需要将一类URL都映射到一个组件,就需要使用动态路由使用方法
· 定义动态路由规则时,将路径中的某个部分使用:
冒号进行标记,即可设置为动态路由
· 设置动态路由后,动态部分为任意内容均跳转到同一组件
· 冒号:部分对应的信息成为路径参数,存储在 vm.$route.params 中侦听路由参数
· 如果要相应路由参数变化,可以通过watch监听 $route
<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>
<script src="lib/vue.js"></script>
<script src="lib/vue-router.js"></script>
<script>
// 设置组件
var User = {
template: `
<div>
这是用户 {{ $route.params.id }} 的功能
<input type="text">
</div>`,
// 由于组件没有重新创建,所以生命周期钩子只能执行一次
/* created () {
console.log('创建了组件的实例');//只输出了一次
} */
// 监听路由参数
watch: {
$route (route) {
console.log($route.params.id);
}
}
};
// 设置路由规则
var routes = [
{
path: '/user/:id', component: User
}
];
var router = new VueRouter({ routes });
var vm = new Vue({
el: '#app',
router
});
</script>
- 路由传参处理
· 通过路由的props设置数据,并通过组件 porps 接收
<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-link to="/category/1">分类1</router-link>
<router-link to="/category/2">分类2</router-link>
<router-link to="/category/3">分类3</router-link>
<router-view></router-view>
<router-view name="sidebar"></router-view>
<router-view name="sidebar2"></router-view>
</div>
<script src="lib/vue.js"></script>
<script src="lib/vue-router.js"></script>
<script>
// 组件的配置对象
var User = {
template: `<div>这是用户 {{ $route.params.id }} 功能</div>`
};
var Category = {
props: ['id'],
template: `<div>这是分类 {{ id }} 功能</div>`
};
var SideBar = {
template: `<div>侧边栏功能</div>`
};
var SideBar2 = {
props: ['a', 'b'],
template: `
<div>
侧边栏2功能: {{ a }} {{ b }}
</div>`
};
// 设置路由规则
var routes = [
{
path: '/user/:id',
component: User
},
{
path: '/category/:id',
components: {
default: Category,
sidebar: SideBar,
sidebar2: SideBar2
},
props: {
default: true,
sidebar: false,
sidebar2: {
a: '状态1',
b: '状态2'
}
}
}
];
var router = new VueRouter({ routes });
var vm = new Vue({
el: '#app',
router
});
</script>
- 路由传参其他方式
· 包含多个命名视图时,需要将路由的 props 设置为对象
· 如果希望设置静态数据,可以将 props 中的额某个组件对应的选项设置为对象,内部属性会绑定给 props
<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-link to="/category/1">分类1</router-link>
<router-link to="/category/2">分类2</router-link>
<router-link to="/category/3">分类3</router-link>
<router-view></router-view>
<router-view name="sidebar"></router-view>
<router-view name="sidebar2"></router-view>
</div>
<script src="lib/vue.js"></script>
<script src="lib/vue-router.js"></script>
<script>
// 组件的配置对象
var User = {
template: `<div>这是用户 {{ $route.params.id }} 功能</div>`
};
var Category = {
props: ['id'],
template: `<div>这是分类 {{ id }} 功能</div>`
};
var SideBar = {
template: `<div>侧边栏功能</div>`
};
var SideBar2 = {
props: ['a', 'b'],
template: `
<div>
侧边栏2功能: {{ a }} {{ b }}
</div>`
};
// 设置路由规则
var routes = [
{
path: '/user/:id',
component: User
},
{
path: '/category/:id',
components: {
default: Category,
sidebar: SideBar,
sidebar2: SideBar2
},
// 多个视图时,需要将props改为对象,并设置加载方式
props: {
default: true,
sidebar: false,
sidebar2: {
a: '状态1',
b: '状态2'
}
}
}
];
var router = new VueRouter({ routes });
var vm = new Vue({
el: '#app',
router
});
</script>
嵌套路由
路由通常由多层嵌套的组件组合而成,这是需要使用嵌套路由配置。
· 使用 children 来进行嵌套路由中的子路由设置。
<div id="app">
<router-link to="/user">用户功能</router-link>
<router-view></router-view>
</div>
<script src="lib/vue.js"></script>
<script src="lib/vue-router.js"></script>
<script>
var User = {
template:`
<div>
<h3>这是 User 组件的功能</h3>
<router-link to="/user/hobby">爱好功能</router-link>
<router-link to="/user/info">用户信息</router-link>
<router-view></router-view>
</div>
`
};
// 爱好
var UserHobby = {
template: `<div> UserHobby 组件</div>`
};
// info
var UserInfo = {
template: `
<div>
UserInfo 组件
<router-link to="/user/info/school">学校信息</router-link>
<router-view></router-view>
</div>`
};
var UserInfoSchool = {
template:`<div> UserInfoSchool 组件</div>`
};
// 路由规则设置
var routes = [
{
path: '/user',
component: User,
children: [
{
path: 'hobby',
component: UserHobby
},
{
path: 'info',
component: UserInfo,
children: [
{
path: 'school',
component: UserInfoSchool
}
]
}
]
}
];
var router = new VueRouter({ routes });
var vm = new Vue({
el:'#app',
router
});
</script>
编程式导航
指的是通过方法设置导航。
router.push() 用来导航一个新的URL
<router-link> 的 to 属性 使用绑定方式时也可以是 属性对象结构
<div id="app">
<!-- 声明式导航 -->
<!-- <router-link to="/user/200">用户200</router-link> -->
<!-- 编程式导航 -->
<router-link :to="{ path: '/user/700' }">用户700</router-link>
<router-view></router-view>
</div>
<script src="./lib/vue.js"></script>
<script src="./lib/vue-router.js"></script>
<script>
var User = {
template: `<div> 这是用户 {{ $route.params.id }} </div>`
};
// 设置路由规则
var routes = [
{
path: '/user/:id',
component: User
}
];
var router = new VueRouter({ routes });
var vm = new Vue({
el: '#app',
router
});
</script>
命名路由
设置路由时添加 name 属性,根据name处理路由
在 push() 中设置 name 导航到对应路由,参数通过 params 设置
<div id="app">
<!-- <router-link :to="{ name: 'school', params: { id: 10 } }">学校10</router-link> -->
<!-- 通过name属性寻找对应的组件 -->
<router-link :to="{ name: 'school', params: { id :10 }}">学校10</router-link>
<router-view></router-view>
</div>
<script src="lib/vue.js"></script>
<script src="lib/vue-router.js"></script>
<script>
var School = {
template: `
<div>school 组件的功能: {{ $route.params }}</div>`
};
var routes = [
{
path:'/user/:id/info/school',
name: 'school',
component: School
}
];
var router = new VueRouter({ routes });
var vm = new Vue({
el: '#app',
router
});
</script>
重定向
访问不合理的URL时,被重新指定到了一个位置
<div id="app">
<router-link to="/">首页</router-link>
<router-link to="/category/2">分类2</router-link>
<router-link to="/category"> /category 错误演示 </router-link>
<router-view></router-view>
</div>
<script src="./lib/vue.js"></script>
<script src="./lib/vue-router.js"></script>
<script>
var Index = {
template: `<div>首页功能</div>`
};
var Category = {
template: `<div>分类 {{ $route.params.id }} 功能</div>`
};
var router = new VueRouter({
routes: [
{
path: '/',
component: Index
},
{
path: '/category/:id',
component: Category
},
// 如果id没有书写的话,无法访问到对应的category,那我们就需要重新定向
{
path: '/category',
redirect: '/'
}
]
});
var vm = new Vue({
el: '#app',
router
});
</script>
别名功能
<div id="app">
<!-- 命名式路由 -->
<router-link :to="{ name: 'school', params: { id: 10, date: '0612'} }">学校信息</router-link>
<!-- 导航别名 -->
<router-link to="/20/1234">学校信息2</router-link>
<router-view></router-view>
</div>
<script src="lib/vue.js"></script>
<script src="lib/vue-router.js"></script>
<script>
// 组件
var School = {
template: `
<div>School 组件</div>
`
};
// 路由规则
var router = new VueRouter({
routes: [
{
path: '/user/:id/info/school/:date',
name: 'school',
component: School,
// 设置别名,此处绑定id和日期,为了传参使用
alias: '/:id/:date'
}
]
});
var vm = new Vue({
el: '#app',
router
});
</script>
导航守卫
- 某些特定网址可能需要登录,所以需要使用导航守卫进行跳转
- to到哪里去,from来自哪里,需要守卫时调用next,调用且调用一次
- next可以传入false组织本次导航,无法往后执行
- 传入参数跳转到登录页面,传入参数和router-link中的参数传入的是相同的
router.beforeEach(function (to, from, next){
函数体
next();
});
<script>
var Index = {
template: `<div>这是首页功能</div>`
};
var User = {
template: `<div>这是用户功能</div>`
};
var Category = {
template: `<div>这是分类功能</div>`
};
// 建立导航对象 router
var router = new VueRouter({
routes: [
{ path: '/', component: Index },
{ path: '/user', component: User },
{ path: '/category', component: Category },
]
});
// 通过上面建立的导航对象router,设置导航守卫
router.beforeEach(function (to ,from, next) {
console.log(to,from);
//阻止了导航继续操作
// next(false);
// 按条件跳转
if(to.path === '/user') {
next('/category');
}else {
next();
}
});
var vm = new Vue({
el: '#app',
router
})
</script>
history 模式
vue router默认使用的hash模式设置
也可以提供history模式,需要Vue Router实例的mode 选项设置,这样可以是 URL更加美观,需要后端支持避免出现问题
var router = new VueRouter({
mode: 'history',
routes: [
{ path: '/', component: Index },
{ path: '/user', component: User },
{ path: '/category', component: Category },
]
});
Vue CLI
基于 Vue.js 进行快速开发的完整系统,称为为脚手架工具,
统一架构风格,
初始化配置项目依赖。
提供单文件组件功能。
Vue CLI安装
• 安装
• npm install –g @vue/cli
• 升级
• npm update –g @vue/cli
项目搭建
• 创建项目:
vue create project-demo
• 选择 Preset:
• 选择包管理器:
- 运行项目:
• npm run serve
目录与文件
• 文件目录介绍:
└─ 根目录
├─ public 预览文件目录
└─ src
├─ assets 静态资源目录
└─ components 项目组件目录
└─ App.vue 根组件
└─ main.js 入口文件
- 单文件组件
可以将组件的功能统一保存在以.vue为扩展名的文件中。
打包与部署
打包
• 打包就是将 Vue CLI 项目编译为浏览器可识别的文件。
• 命令:
npm run build
部署
• 部署指的是将 Vue 项目dist 目录部署到服务器上。
• 安装静态文件服务器:
- npm install -g serve
• 在 dist 下通过 serve 命令部署
先到dist目录下:cd dist
再运行服务器:serve