概要
1. 组件
2. 脚手架vue cli
3. vue的生命周期
4. vue路由 vue router
5. axios
6. vuex
1. 组件
1. 全局组件
语法:
- 格式:
Vue.component(tagName, options) - 参数:
tagName=> 组件名称options=> 配置项data=> 数据,是个函数template=> 模板,内容为html结构(必须参数)
解析
- 这里的Vue是一个对象,在引入的vue.js库中,每个Vue应用都是通过用Vue函数创建一个新的Vue实例开始的,这个Vue实际上就是一个构造函数。
- 在Vue的构造函数(实例)中,它有一个component方法,Vue构造函数下面的这个component方法就是用来声明全局组件的。
- 这个component方法接收两个参数,tagName(组件名称)和options(组件配置)。
- tagName:当一个组件声明之后需要使用,使用的时候需要在html文件里面写标签对,而这标签对的名称,就是这个组件声明时候的名称。
- options:针对这个组件我们需要给它很多设置。它是一个对象,可以放很多参数,最基本的有两个,一个是数据(data),一个是html模板(template)。
示例1:
全局组件的基本用法
html:
<!-- 用来放vue的容器 -->
<div id="app">
<!-- 全局组件的名称(tagName) -->
<counter></counter>
</div>
<!-- js s -->
引入vue.js
...
<!-- js e -->
js:
/**
* vue对象注册全局组件的方法
* Vue.component(tagName, options)
* 组件名称,配置项(data, template...)
* 将组件名(tagName)放到html中
* options是一个对象,表示组件中的选项
*/
Vue.component("counter", {
// template中所有的标签需要包含在一个父级当中
template: '<button>按钮</button>',
});
// 实例化Vue对象
new Vue({
// 用来放vue的容器,声明的这个vue对象,要在页面中放在id为app的div中展示。
el: "#app",
});
示例2:
全局组件关联template
html:
<!-- 用来放vue的容器 -->
<div id="app">
<counter></counter>
</div>
<!--
用来放vue模板的容器
1. template标签它是放在body当中,并一定要与上面的div平级
2. 需要给一个id用于关联
3. 同样需要一个父级
-->
<template id="btn">
<!-- 需要被包含在一个容器当中 -->
<div>
<button>按钮</button>
<span>按照平常的html写法就可以了</span>
</div>
</template>
<!-- js s -->
引入vue.js
...
<!-- js e -->
js
// vue对象,调用component注册全局组件
Vue.component("counter", {
// 使用在body中vue模板(template)的id
template: '#btn',
});
new Vue({
el: "#app",
});
示例3:
全局组件关联中data的用法
html:
<!-- 用来放vue的容器 -->
<div id="app">
<counter></counter>
</div>
<!-- 用来放vue模板的容器 -->
<template id="btn">
<div>
<button>按钮</button>
<!-- 在data中的数据可以使用模板字符串的形式使用 -->
<span>{{ count }}</span>
</div>
</template>
<!-- js s -->
引入vue.js
...
<!-- js e -->
js
// vue中component注册全局组件的方法中的参数options是一个对象,里面有若干属性和方法,data是其中一个表示组件中数据的方法
Vue.component("counter", {
template: '#btn',
// 表示vue组件中的数据,是一个方法
// 在es6中,在对象中写一个函数的方法
data() {
// 在data对象中需要有一个return,这个return是一个对象,在这个对象里面可以存储这个组件所需要的数据
return{
count: 0,
}
}
});
new Vue({
el: "#app",
});
示例4:
全局组件中定义方法
html:
<!-- 用来放vue的容器 -->
<div id="app">
<counter></counter>
</div>
<!-- 用来放vue模板的容器 -->
<template id="btn">
<div>
<!-- 在vue中使用方法用 @事件名=“方法名” -->
<button @click="add">按钮</button>
<span>{{ count }}</span>
</div>
</template>
<!-- js s -->
引入vue.js
...
<!-- js e -->
js
Vue.component("counter", {
template: '#btn',
data() {
return{
count: 0,
}
},
// 在全局组件中定义方法
methods: {
add() {
// 取到组件中的数据使用this.数据
this.count++;
}
}
});
new Vue({
el: "#app",
});
注意:
- 每个Vue应用都是通过用Vue函数创建一个新的Vue实例开始的。
- 组件就是具体的一个个功能。一个实际的功能里面会有很多结构。结构都放在template中,就是一个模板。
- template中所有的标签需要包含在一个父级当中。
- 每一个全局组件之间相互独立,即每次实例化的Vue相互不关联。
- 所有的组件名称不能使用驼峰命名(标签不能用大写字母),要使用-,比如说你的一个组件名称叫counterA,你在标签里面使用的话只能用
2. 局部组件
- 语法:
- 格式:
new Vue({ components: { tagName: options, tagName: options, ... } })- 注意:
- 参数与全局组件一样
- 全局组件是通过
vue component生成的(没有s),局部组件用的是components(带s)。
解析
- 局部组件相当于局部变量
- 和原生一样,原生不建议使用全局变量,组件也是,尽量不使用全局组件。
- 和全局组件不一样,全局组件是在原生的vue对象上使用component方法,
Vue.component,而局部组件是new了一个Vue实例。 - 这个被创建的vue实例放在components中,比注册全局组件的方法多了一个s。
- 在components中的语法和全局组件中的语法一样。
- 局部组件只能在当前实例的vue中使用。
示例1:
局部组件的基本用法
html:
<!-- 用来放vue的容器 -->
<div id="app">
<!-- navBar组件 -->
<!-- js中组件名定义的是驼峰命名,在标签中不允许驼峰,故用-连接 -->
<nav-bar></nav-bar>
</div>
<!-- navBar组件的模板 -->
<navTemplate>
<ul>
<li>食材1</li>
<li>食材2</li>
</ul>
</navTemplate>
<!-- js s -->
引入vue.js
...
<!-- js e -->
js:
new Vue({
el: "#app",
// 局部组件直接在实例化后的vue中使用compontents,在这里可以放很多个组件
components: {
// 组件名
navBar: {
template: "#navTemplate",
},
// 组件名
navList: {
},
// .....
}
});
示例2:
在局部组件中存放数据,并定义方法
html:
<!-- 用来放vue的容器 -->
<div id="app">
<!-- navBar组件 -->
<nav-bar></nav-bar>
</div>
<!-- navBar组件的模板 -->
<navTemplate>
<!-- 用模板字符串使用数据 -->
<!--<ul>
<li>{{ text[0] }}</li>
<li>{{ text[1] }}</li>
</ul>-->
<!-- 使用v-for遍历 -->
<ul>
<li v-for="(item, index) in text" :key="index" @click="log">{{ item }}</li>
</ul>
</navTemplate>
<!-- js s -->
引入vue.js
...
<!-- js e -->
js:
new Vue({
el: "#app",
components: {
// 组件名
navBar: {
// 定义模板
template: "#navTemplate",
// 数据
data() {
return {
text: ["食材1", "食材2"],
}
},
// 方法
methods: {
log() {
console.log("点击");
}
}
},
}
});
示例3:
局部组件有作用域
html:
<!-- 用来放vue的容器 -->
<div id="app">
<!-- navBar组件 -->
<nav-bar></nav-bar>
<!-- navList组件 -->
<nav-list></nav-list>
</div>
<!-- 用来放vue的容器 -->
<!-- 局部组件无法应用,有作用域,只能在app中使用 -->
<div id="box">
<!-- 组件模板2 -->
<nav-list></nav-list>
</div>
<!-- navBar组件的模板 -->
<navTemplate>
<ul>
<li>食材1</li>
<li>食材2</li>
</ul>
</navTemplate>
<!-- navList组件模板 -->
<template id="navListTemplate">
<span>navList233333</span>
</template>
<!-- js s -->
引入vue.js
...
<!-- js e -->
js:
new Vue({
el: "#app",
components: {
// 组件名
navBar: {
// 定义模板
template: "#navTemplate",
},
// 组件名
navList: {
template: "#navListTemplate",
},
}
});
new Vue({
el: "#box",
});
关于全局组件和之间的区别
- 全局组件和局部组件与js中的全局变量和局部变量非常相似,它们身上所具有的特性,在组件中也能够体现出来。
3. Props
定义:
props:在组件上定义一些属性用来传递数据(传递给组件里的结构使用)
语法:
- 组件里:props:['属性名', '属性名', ...]
- 模板里:{{ 属性名 }}
- 标签里:<组件 属性名="值" 属性名="值"></组件>
解析
- 用来传递数据
- 和形参与实参差不多
- html结构相同,但数据不同,组件传递数据
示例1:
props的基本用法
html:
<!-- 用来放vue的容器 -->
<div id="app">
<!-- 每个li是一个组件,外面用ul包起来 -->
<ul>
<!-- boxList组件 -->
<!-- 组件结构相同,数据不同,给每个组件的props中定义的属性赋值 -->
<box-list title="可乐" price="112"></box-list>
<box-list title="雪碧" price="333"></box-list>
</ul>
</div>
<!-- boxList组件模板 -->
<template id="boxListTemplate">
<li>
<!-- 使用模板字符串的方式渲染props传递过来的值 -->
<span>{{ title }}</span>
<span>{{ price }}</span>
</li>
</template>
<!-- js s -->
引入vue.js
...
<!-- js e -->
js:
new Vue({
el: "#app",
components: {
boxList: {
template: "#boxListTemplate",
// props 在组件上定义一些属性用来传递数据
props: ["title", "price"]
}
}
});
3. Slot
定义:
slot:插槽
语法:
- 插槽(slot):用于分发内容
- template里定义:
- 组件标签对里使用(标签对里的标签能被解析)
- 注意:插槽只能写一个,多写会报错
- 具名插槽:给slot添加name属性后就叫具名插槽
- template里定义:
- 组件标签对里使用:
解析
- 在使用props传递数据的时候,在使用组件的标签对中,数据都当做属性传递进来。这种方法传递的数据比较少比较清楚,但数据量一多就显得累赘。
- 在vue中,组件和标签是一样的,为了更像html标签,组件中传递数据可以用slot这种形式传递,结构会更清晰。
- 举个例子,就像一台电脑,它有各种插槽用来放显卡,内存等等,还有usb接口等等,这些插槽,就相当于vue中的slot。
- 在组件标签对中不会显示里面的标签,组件定义好之后,就相当于在内部已经成为一个完全封闭的状态了。
示例1:
slot的基本用法(传递数据)
html:
<!-- 用来放vue的容器 -->
<div id="app">
<!-- boxList组件 -->
<box-list>
<!-- 组件标签对中的内容不显示 -->
<!-- p标签中若有数据,使用slot分发内容,在组件的标签对里面放的都是数据 -->
<p>使用slot才能显示....</p>
<p>template里面使用一个slot就会全部显示</p>
...
</box-list>
</div>
<!-- boxList组件模板 -->
<template id="boxListTemplate">
<!-- 插槽在template中定义,相当于划一道口,想在哪里显示,slot插槽就放在哪里 -->
<div>
<h1>test</h1>
<!-- 组件标签对中的内容在h1下面显示,它这里是显示组件标签对中所有的数据 -->
<slot></slot>
</div>
</template>
<!-- js s -->
引入vue.js
...
<!-- js e -->
js:
new Vue({
el: "#app",
components: {
boxList: {
template: "#boxListTemplate",
}
}
});
示例2:
slot的基本用法(具名插槽)
html:
<!-- 用来放vue的容器 -->
<div id="app">
<!-- boxList组件 -->
<box-list>
<!-- 使用具名插槽 -->
<span slot="cc1">插槽1</span>
<span slot="cc2">插槽2</span>
</box-list>
</div>
<!-- boxList组件模板 -->
<template id="boxListTemplate">
<!-- 插槽在template中定义,相当于每把刀上有一个名字,刀每划一道口,有特定的含义,使用的时候根据刀名字使用 -->
<div>
<h1>test</h1>
<!-- 可以用名称定义显示部分(具名插槽) -->
<slot name="cc1"></slot>
<slot name="cc2"></slot>
</div>
</template>
<!-- js s -->
引入vue.js
...
<!-- js e -->
js:
new Vue({
el: "#app",
components: {
boxList: {
template: "#boxListTemplate",
}
}
});
示例3:
slot传递数据
html:
<!-- 用来放vue的容器 -->
<div id="app">
<ul>
<!-- boxList组件 -->
<!-- 在组件标签对中使用具名插槽,并可以按照传统html写法把数据添加上去,一个标签对就是一个li,只要把组件模板中的插槽拿过来就可以了 -->
<box-list>
<span slot="title">可乐</span>
<span slot="price">22</span>
</box-list>
<!-- boxList组件 -->
<box-list>
<span slot="title">雪碧</span>
<span slot="price">33</span>
</box-list>
</ul>
</div>
<!-- boxList组件模板 -->
<template id="boxListTemplate">
<!-- 在template中定义具名插槽,这里是具体的html结构,在li结构中 -->
<li>
<a href="#">
<slot name="title"></slot>
<slot name="price"></slot>
<span>食品介绍<span>
</a>
</li>
</template>
<!-- js s -->
引入vue.js
...
<!-- js e -->
js:
new Vue({
el: "#app",
components: {
boxList: {
template: "#boxListTemplate",
}
}
});
4. 父子组件
4-1. 父子组件的定义
语法:
new Vue({
// vue容器
el: "#app",
components: {
父组件名: {
template: "父组件template对应的id",
data() {
// 父组件数据
},
// 子组件
components: {
子组件名: {
template: "子组件template对应的id",
},
...
}
}
}
});
解析:
- 一个组件就是一个功能里面的结构,这个结构可能包含在其他的结构当中,而这个结构就是外面那个结构的子组件,包围子组件的那个结构就是父组件。
示例1:
基本的父子组件
html:
<!-- 用来放vue的容器 -->
<div id="app">
<!-- boxList组件(父组件) -->
<box-list></box-list>
</div>
<!-- boxList组件(父组件)模板 -->
<!-- 父组件模板使用ul标签 -->
<template id="boxListTemplate">
<ul>
<!-- 在父组件中使用子组件 -->
<!-- 子组件使用插槽 -->
<!-- 子组件只能在父组件模板里面调用 -->
<!-- 定义所有插槽的值(定义数据) -->
<item>
<span slot="title">可乐</span>
<span slot="price">44</span>
</item>
<item>
<span slot="title">雪碧</span>
<span slot="price">33</span>
</item>
</ul>
</template>
<!-- item组件(子组件)模板 -->
<!-- 子组件模板就是一个个li标签 -->
<template id="itemTemplate">
<!-- html结构,值用插槽代替 -->
<li>
<a href="#">
<slot name="title"></slot>
<slot name="price"></slot>
<span>食品介绍<span>
</a>
</li>
</template>
<!-- js s -->
引入vue.js
...
<!-- js e -->
js:
new Vue({
el: "#app",
// 父组件
components: {
boxList: {
// 父组件模板
template: "#boxListTemplate",
// 子组件
components: {
item: {
// 子组件模板
template: "#itemTemplate",
}
}
}
}
});
关于组件间如何传递数据
- 父组件向子组件传递数据(自动传)
- 使用props属性传递数据
- 子组件向父组件传递数据(主动传,事件触发)
- 使用自定义事件
关于自定义事件
- 在发数据组件的methods里定义一个方法,方法内容如下:
this.$emit('event', value)- event:自定义事件名称
- value:要传递的数据(可选参数)
- 在发数据组件的标签里
<component @自定义事件名称="函数名"></component>
- 在接收数据组件的methods里
函数名(val) {
// val就是接收到的数据
}
4-2. 父组件给子组件传递数据
语法:
- 父组件向子组件传递数据(自动传)
- 使用props属性
解析
- 父亲可以给儿子零花钱,即父组件可以给子组件传递数据,单向数据流
- 在子组件标签里通过v-bind绑定一条属性,属性的值为父组件的数据名称
- 在子组件里利用props属性把已经绑定的属性写进去
- 在子组件的template里就可以使用数据了
示例1:
父组件给子组件传递数据(外->里)
html:
<!-- 用来放vue的容器 -->
<div id="app">
<!-- 父组件 -->
<father></father>
</div>
<!-- father组件(父组件)模板 -->
<template id="fatherTemplate">
<div>
<h1>父组件</h1>
<span>父组件的数据{{ name }}</span>
<span>把"{{ name }}"传给子组件</span>
<!--
使用props传递数据给子组件
1. 在子组件定义一个props用来接收父组件传递过来的数据,props是一个数据
2. 在父组件中引用子组件,并给该子组件标签里通过v-bind绑定上在子组件上已经定义的props属性名
3. 绑定的props属性名的值,就是父组件要传过来给子组件的值,这里是name,即取到父组件的数据名称name值
4. 在子组件的template使用数据
-->
<!-- 子组件只能在父组件中调用 -->
<son :fatherdata="name"></son>
</div>
</template>
<!-- son组件(子组件)模板 -->
<template id="sonTemplate">
<div>
<h1>子组件</h1>
<span>这是儿子</span>
<!-- 此时已经将父组件的值通过props传过来了,要使用父组件中的值,就只需要使用在子组件中定义的props属性,并把定义的props属性名用模板字符串的方式在子组件模板中表现出来就行了 -->
<!-- 父组件和子组件拥有独立的作用域,如果没有使用props绑定直接使用,会报错 -->
<p>{{ fatherdata }}</p>
</div>
</template>
<!-- js s -->
引入vue.js
...
<!-- js e -->
js:
new Vue({
el: "#app",
// 父组件
components: {
father: {
template: "#fatherTemplate",
// 父组件数据
data() {
return {
name: "老王",
// 用来存放子组件的数据
sonData: "",
}
},
// 子组件
components: {
son: {
template: "#sonTemplate",
// 父组件向子组件传递数据,子组件需要定义一个props,用来接收父组件的数据
props: ['fatherdata'],
}
}
}
}
});
4-3. 子组件给父组件传递数据
语法:
- 子组件向父组件传递数据(主动传,事件触发)
// 在发数据组件的methods里定义一个方法
this.$emit('event', value)
...
// 在发数据组件的标签里
<component @自定义事件名称="函数名"></component>
...
// 在接收数据组件的methods里
函数名(val) {
// val就是接收到的数据
}
解析
- 儿子可以孝敬父亲,双向数据流。
示例1:
子组件向父组件传递数据((里->外))
html:
<!-- 用来放vue的容器 -->
<div id="app">
<!-- 父组件 -->
<father></father>
</div>
<!-- 父组件模板 -->
<template id="fatherTemplate">
<div>
<h1>父组件</h1>
<span>子组件向父组件传数据</span>
<!-- 使用子组件给父组件传递过来的数据,点击时候才会有 -->
<p>{{ sondata }}</p>
<!-- getdata用来接收数据 -->
<!-- 在发数据组件的标签里,使用自定义事件 -->
<!-- 自定义事件等于事件的名字,这个函数名是用来接收数据的 -->
<son @getdata="getval"></son>
</div>
</template>
<!-- 子组件模板 -->
<template id="sonTemplate">
<div>
<h1>子组件</h1>
<span>这是子组件</span>
<span>子组件要向父组件传数据</span>
<!-- 给父组件传数据 -->
<button @click="send">给父组件传数据</button>
</div>
</template>
<!-- js s -->
引入vue.js
...
<!-- js e -->
js:
new Vue({
el: "#app",
// 父组件
components: {
father: {
template: "#fatherTemplate",
// 父组件数据
data() {
return {
// 父组件接收子组件传递过来的值
sondata: "",
}
},
// 父组件方法
methods: {
// 在接收数据组件的methods里
// 拿到子组件给父组件传递的值
// 把刚刚接收过来的函数名定义一下
getval(val) {
// 父组件的数据
this.sondata = val;
}
},
// 子组件
components: {
son: {
template: "#sonTemplate",
// 子组件数据
data () {
return {
// 要传给父组件的数据
message: "你好,我是小王~~~~~",
}
},
methods: {
// 给父组件传数据,只能通过事件给父组件传数据(自定义事件)
// 在发数据的组件(子组件) 定义一个方法
send() {
// alert();
// 触发自定义事件
// $emit("事件名称", 传递的数据)
this.$emit("getdata", this.message);
}
}
}
}
}
}
});
2. 脚手架vue cli
注意:
- 脚手架是通过webpack搭建的一种vue开发环境
搭建脚手架步骤
- 安装nodejs之后,打开终端
- 使用
npm install -g @vue/cli - 创建项目
- 把终端里的路径移到要创建项目的路径里
vue create 项目名称
vue相关常用命令介绍
Vue -V=> 查看版本Vue -h=> 查看命令帮助Vue -create=> 创建一个项目Vue -add=> 增加一个插件Vue -invoke=> 在创建好的项目里调用插件Vue -inspect=> 检查webpack的配置Vue -serve=> 启动一个项目Vue -build=> 打包项目Vue -ui=> 打开ui库(少用)Vue -init=> 声明一个项目
脚手架目录及文件介绍
- node_modules目录:表示nodejs里的模块
- public目录:入口文件
- 里面的index为模板文件,最终内容会渲染到这里面
<div id="app"> - favicon.icon:网页的icon
- 里面的index为模板文件,最终内容会渲染到这里面
- src:开发目录
- assets目录:静态资源文件夹(图片、外部js、外部css、字体)
- components目录:公用组件文件夹
- views目录:非公用组件文件夹
- App.vue:整个项目的根组件(主组件),所有组件都会被引入这里面,最终,这里面的组件会被渲染到public目录下的index.html里面
- main.js:放js的逻辑处理,就是webpack打包的时候的入口文件,先进到这里对里面逻辑进行分析,看你引入了哪些东西,需要的东西都会在里面找到。
- 里面有实例化Vue对象,在下面的
new Vue({}).$mount("#app")其实就是new Vue({el: "#app"})
- 里面有实例化Vue对象,在下面的
- router.js:安装环境的时候的router,所有路由相关的信息都放在这里
- store.js:安装环境的时候的vuex,用来存储数据(状态管理),集中管理数据,所有的数据在这个文件里面进行管理。
- .gitignore:git版本控制忽略上传
- babel.config.js:用来做babel(打包)的配置
- package-lock.json:package.json锁,在安装的时候不下载新版本
- package.json:webpack配置都在这里面
- README.md:文档
- vue.config.js:vue cli3配置文件
vue执行过程
- App.vue是根组件,vue会默认把组件中的内容渲染到index中
- 渲染到index中之后,接下来它会去找main.js,因为它是入口文件,其实index.html也会引入main.js,只是它最终不叫main.js,它变成了app.js,这是打包之后的
- main.js中包含了整个项目的逻辑,如果页面需要路由或者vuex,都要在main.js里面引入(用es6的语法import)。
- main.js引入了路由,页面它还需要其他的页面,其他的页面都放在路由当中引入。
关于引入
import .. from ...和export default{}是es6中模块化的语法,export是用来导出,import是用来导入- 在vue中,一个功能或者一个页面,可以认为是一个组件,一个组件就可以认为是一个模块。
- 我们在声明的时候,*.vue就是一个组件,即一个模块,在开发完之后,将他用export导出去,导出之后,在你需要用到的时候,直接导入进来
- export是用来导出,举个例子
// 导出一个对象
export var kaivo = {...}
// 在需要用到的地方导入这个对象
import {kaivo} form "..."
或者另外一种形式
// 当他导入这个对象的时候,并没有加变量名,
// 直接导出对象
export {...}
// 这时候就不需要放在括号当中了,
// 这其实就是es6中对象的解构,去取其中的名字
import Vue from "vue"
vue组件
- vue cli中*.vue这种文件就是组件
在脚手架里使用组件
创建一个MenuList模块,不使用router.js
-
MenuList.vue:相当于子组件
<template> <li> <a href="#"> <slot name="title"></slot> <slot name="price"></slot> <span>食品介绍<span> </a> </li> </template> <script> export default { name: 'Menu-list', } </script> <style scoped lang="less"> </style> -
app.vue:相当于父组件
<template> <ul> <!-- 调用组件,使用插槽 --> <Menu-list> <span slot="title">可乐</span> <span slot="price">22</span> </Menu-list> <Menu-list> <span slot="title">雪碧</span> <span slot="price">33</span> </Menu-list> </ul> </template> <script> // 在一个组件里使用另一个组件,需要通过import导入 import MenuList from './components/MenuList' // 但凡设置js都需要供出,相当于父组件 export default { // 局部组件 components: { // 子组件 // 导入进来的组件要放在components中, // 在es6中如果key与value相同,就可以省略 MenuList, } } </script> <style lang="less"> </style>
3. vue的生命周期
vue生命周期图示
vue生命周期详解
- 开始之前,先
new Vue,接下来它就需要去做一些初始化,初始化一些事件,以及初始化一个生命周期,这个生命周期里面有很多方法都需要初始化一下。接下来触发钩子函数。
- beforeCreate 创建实例前触发
- 创建一个实例前就要被触发的,这时候实例并没有被创建。
- 比如做一个预加载的loading,就可以放在这个钩子函数中了。
- 当他触发完成之后,再次初始化,这时候初始化的是组件里的一些数据(data方法中),或者说是methods里面的事件等等,在这一步走完之后,这实例才算是被创建了。接下来触发钩子函数
- created 创建实例后触发
- 这个钩子函数发生之后,这个实例就被创建完了,这个时候就可以使用数据和事件了,但是虽然实例创建完了,但在dom中还没有被创建,这时候页面中什么都没有
- 在这里可以做,比如在后端请求数据,页面还没有展示,先请求数据,页面中dom创建完了,再把数据塞进去。
- 在这时候可以把loading结束了。
- 接下来开始渲染dom了
- 先检查有没有
el这个东西(vue的容器),如果没有,就会去找有没有vm.$mount(el),[要渲染dom的话首先要知道渲染到哪里,这是是要渲染到vue的容器里] - 然后去找有没有“template”这个属性,如果有的话,就会按照你给的这个“template”渲染,如果没有的话,就会安装"el"这个标签渲染,即直接把el标签渲染成dom元素,在这一步操作是在虚拟dom里面编译的,也就是说他做了很多事情,我们看不到,dom没有显示出来,这就是虚拟dom
- 接下里触发钩子函数
- beforeMount 渲染dom前触发
- 此时在虚拟dom中的模板,其实已经渲染完成了,接下来就是在真实的dom中渲染dom了
- 接下来,在这个事件发生完成之后,就开始真正渲染dom了,这个时候,就会把el标签中的内容替换了,此时,页面中就有内容了,页面中就是一个真实的标签了。
- 接下来触发钩子函数
- mounted 渲染dom后触发
- 你如果需要在页面显示后去做一些事情,就在这个钩子函数中去处理。
- 这个事件发生之后,接下来,并不是说这个东西显示出来就完事了,你的数据显示完成了,后面肯定需要去修改。
- 如果这个数据有修改,那么在修改前它就会触发"beforeUpdate"钩子函数,在修改后又会触发"updated"钩子函数,这个修改也是在虚拟dom中修改的。
- [虚拟dom]=> 为了提升页面性能
- 虚拟dom会根据diff算法,和之前不一样才去渲染,一样的话就不会渲染,渲染完成触发"updated"钩子函数,触发之后,就到了后面的一个阶段(钩子函数)了,销毁阶段
- beforeUpdate 数据更改前后触发
- updated 数据更改后触发
- dom已经渲染完成不需要修改了,这时候就需要销毁了,就会执行以下钩子函数。
- beforeDestroy 实例销毁前触发
- 销毁中,清除数据,清除事件等等,有定时器的话,还要删掉定时器
- destroyed 实例销毁后触发
- 这个钩子函数触发之后,整个组件的生命周期就接收了
注意:
- 当new了一个vue的时候,生命周期就开始了,这里面的方法就是钩子函数
- 总共有8个方法(事件)
new Vue就是实例化了vue对象
4. vue路由 vue router
路由:根据URL锚点路径,在容器中加载不同的模块
配置方式一
- 引入vue-router.js库
- 定义路由,并对应router配置参数
router: new VueRouter({
routers: [
{
path: "路径",
component: "此路径对应的组件名"
}
]
});
- 使用
<router-link>组件来导航,并通过to属性指定链接 - 定义路由出口:
<router-view></router-view>
示例:
路由配置方式一
html:
<div id="app">
<!-- 使用路由 -->
<!-- 它需要使用自己的标签进行跳转 -->
<router-link to="/home">首页</router-link>
<router-link to="/news">新闻</router-link>
<router-link to="/hot">热点</router-link>
<!-- 需要指定一个地方去显示路由的内容 -->
<!-- 给路由定义一个出口,而不是整个页面都显示,这就是单页应用(spa)的一个核心 -->
<!-- 此时需要在点击首页路由的时候,在以下div中显示home组件中的路由 -->
<div class="box">
<router-view></router-view>
</div>
</div>
<!-- js s -->
引入vue.js
引入router.js
...
<!-- js e -->
js:
new Vue({
el: "#app",
// 构造函数(实例化router对象)
// 有若干与路由相关的参数
router: new VueRouter({
// 第一个参数是routers,它的值是一个数组,放所有的路由
routers: [
{
// 路径
// /表示根目录
path: "/home",
// 此路径对应的组件名,不带s,即只能放一个组件,即相当于一个页面
component: {
template: "<h2>首页<h2>",
}
},
{
path: "/news",
component: {
template: "<h2>新闻<h2>",
}
},
{
path: "/hot",
component: {
template: "<h2>热点<h2>",
}
},
]
}),
// 创建实例前触发,钩子函数
beforeCreate() {
// 在创建实例前,将路由初始化到首页
// 即每次刷新之后到首页
this.$router.push("/home");
}
});
配置方式二
- 引入vue-router.js库
- 定义(路由)组件
- 定义路由
- 创建router实例(路由对象),并对router配置参数
- 使用
<router-link>组件来导航,并通过to属性指定链接 - 定义路由出口:
<router-view></router-view>
示例:
路由配置方式二
html:
<!-- 热点模板 -->
<template id="hot">
<h2>热点</h2>
</template>
<div id="app">
<!-- 使用路由 -->
<router-link to="/home">首页</router-link>
<router-link to="/news">新闻</router-link>
<router-link to="/hot">热点</router-link>
<div class="box">
<router-view></router-view>
</div>
</div>
<!-- js s -->
引入vue.js
引入router.js
...
<!-- js e -->
js:
// 定义路由的组件
const Home = {template: "<h2>首页</h2>"}
// 第二个方法,vue中用来注册组件的方法,是一个全局的方法
const News = Vue.extend({
template: "<h2>新闻</h2>"
});
const Hot = {template: "#hot"}
// 定义路由
const routers = [
{
path: "/home",
compontent: Home
},
{
path: "/news",
compontent: News
},
{
path: "/hot",
compontent: Hot
}
],
// 创建router对象
const router = new VueRouter({
// 把上面创建的路由对象放进来
// 原来是routers: routers,es6里面可以简写成一个
routers
});
// 接下来需要new一个vue,并把创建的路由对象放入实例化后的vue对象中
new Vue({
el: "#app",
// 上面创建的router对象
router,
/*
// 第一个路由重定向方法
// 创建实例前触发,钩子函数
beforeCreate() {
// 在创建实例前,将路由初始化到首页
// 即每次刷新之后到首页
this.$router.push("/home");
}
*/
});
// 第二个路由重定向方法
// router就是一个路由对象,可以直接调用
router.push('/home');
注意:
- 路由的重定向:
this.$router.push("路径")- 其中
this.$router为路由对象
- 其中
嵌套路由
语法
{
path: "父级路由地址", compontent: "父级路由组件", children: [
{
path: "子级路由地址", compontent: "子级路由组件"
},
...
]
}
示例:
嵌套路由
html:
<div id="app">
<!-- 使用路由 -->
<router-link to="/home">首页</router-link>
<router-link to="/news">新闻</router-link>
<router-link to="/hot">热点</router-link>
<div class="box">
<router-view></router-view>
</div>
</div>
<!-- 热点模板 -->
<template id="hot">
<h2>热点</h2>
<ul>
<li>
// 二级路由对应的链接
<router-link to="/hot/movie">电影</router-link>
</li>
<li>
<router-link to="/hot/music">音乐</router-link>
</li>
</ul>
<!-- 二级路由显示的地方 -->
<div class="box">
<router-view></router-view>
</div>
</template>
<!-- hot子路由模板(二级路由对应的模板) -->
<template id="movie">
<h2>电影</h2>
<p>喜羊羊</p>
</template>
<template id="music">
<h2>音乐</h2>
<p>音乐111</p>
</template>
<!-- js s -->
引入vue.js
引入router.js
...
<!-- js e -->
js:
const Home = {template: "<h2>首页</h2>"};
const News = Vue.extend({
template: "<h2>新闻</h2>"
});
const Hot = {template: "#hot"};
// hot子路由
const Movie = {template: "#movie"};
const Music = {template: "#music"};
const routers = [
{path: "/home", compontent: Home},
{path: "/news", compontent: News},
{path: "/hot", compontent: Hot, children: [
// 这里的path,不需要加 /
{path: "movie", compontent: Movie},
{path: "music", compontent: Music}
]}
],
const router = new VueRouter({
routers
});
new Vue({
el: "#app",
router,
});
// 第二个路由重定向方法
// router就是一个路由对象,可以直接调用
router.push('/home');
动态路由匹配
示例:
动态路由匹配
html:
<div id="app">
<ul>
<li>
<router-link to="/home">首页</router-link>
</li>
<li>
<router-link to="/news">新闻</router-link>
</li>
</ul>
// 路由出口
<div class="box">
<router-view></router-view>
</div>
</div>
<!-- 新闻模板 -->
<template id="news">
<span>新闻列表</span>
<ul>
<li>
<router-link to="/news/1">新闻1</router-link>
</li>
<li>
<router-link to="/news/2">新闻1</router-link>
</li>
</ul>
</template>
<!-- 新闻最终页模板 -->
<template id="new_list">
<span>新闻最终页</span>
<p>这是传递过来的参数(id):{{ this.$route.params }}</p>
</template>
<!-- js s -->
引入vue.js
引入router.js
...
<!-- js e -->
js:
const Home = {template: "<h2>首页</h2>"};
const News = {template: "#new"};
// 因为点击某个新闻要跳转到一个新的页面,所以也需要一个组件
const NewList {template: "#new_list"};
const routes = [
{path: "/home", compontent: Home},
{path: "/news", compontent: News},
// 要匹配一个动态路由的话,就用:id,动态路径的参数
// 这个设置完了之后,参数值会被设置到this.$route.params
{path: "/news/:id", compontent: NewList},
]
const router = new VueRouter({
routes
});
new Vue({
el: "app",
router,
});
router.push("/home");
脚手架里配置路由
示例:
脚手架里配置路由
main.js:如果npm安装了router会自动引入
import router from './router'
new Vue({
router,
...
}).$mount('#app')
router.js
// 引入路由
import Home from './components/Home'
import Manage from './components/Manage'
import Order from './components/Order'
import Dishes from './components/Dishes'
import About from './components/about/About'
// 引入二级路由
import Intro from './components/about/Intro'
import Join from './components/about/Join'
import Contact from './components/about/Contact'
// 引入三级路由
import Wechat from './components/about/contact/Wechat'
import QQ from './components/about/contact/QQ'
import Email from './components/about/contact/Email'
Vue.use(Router)
const router = new Router({
...
routes: [
// 重定向(当用户输入一个错误的地址后跳转)
{path: '*', redirect: "/home"},
// 根目录
{path: '/home', component: Home},
{path: '/dishes',name: 'dishes',component: Dishes},
{path: '/manage', component: Manage},
{path: '/order', component: Order},
{path: '/about', component: About,
// 要展示的路由
redirect: '/about/contact',
// 二级路由
children: [
// 要加父路由
{path: '/about/intro', component: Intro},
{path: '/about/join', component: Join},
{path: '/about/contact', component: Contact,
// 要展示的路由
redirect: '/about/contact/qq',
// 三级路由
children: [
{path: '/about/contact/wechat', component: Wechat},
{path: '/about/contact/qq', component: QQ},
{path: '/about/contact/email', component: Email},
]},
]},
]
})
Home.vue
一级目录
<template>
<div>
<h6>首页</h6>
<router-link to="/home">首页</router-link>
<router-link to="/manage">管理</router-link>
<router-link to="/dishes">食品</router-link>
<router-link to="/order">订单</router-link>
<router-link to="/about">关于</router-link>
<button @click="back">回退</button>
<button @click="goTo">跳转</button>
</div>
</template>
<script>
export default {
name: "home",
methods: {
back() {
this.$router.go(-1); // 回退
},
goTo() {
this.$router.replace("/manage");
this.$router.replace({name: 'dishes'});
this.$router.push("/order");
}
}
}
</script>
<style scoped>
</style>
Manage.vue
一级目录
<template>
<div>管理</div>
</template>
<script>
export default {
name: "Manage",
}
</script>
<style scoped lang="less">
</style>
Dishes.vue
一级目录,结构与上雷同,
<div>食品</div>
Order.vue
一级目录,结构与上雷同,
<div>订单</div>
- about目录(有三级目录)
- Intro组件
- Join组件
- Contact组件
About.vue
一级目录
<template>
<div>
<h6>关于我们</h6>
<router-link to="/about/intro">简介</router-link>
<router-link to="/about/join">加入</router-link>
<router-link to="/about/contact">联系</router-link>
<!--<nav>
<ul>
<li>
<router-link to="/about/intro">简介</router-link>
</li>
<li>
<router-link to="/about/join">加入</router-link>
</li>
<li>
<router-link to="/about/contact">联系</router-link>
</li>
</ul>
</nav>-->
<div class="box">
<router-view />
</div>
</div>
</template>
<script>
export default {
name: "about"
}
</script>
<style scoped lang="less">
</style>
Intro.vue
二级目录
<template>
<div>简介</div>
</template>
<script>
export default {
name: "Intro"
}
</script>
<style scoped lang="less">
</style>
Join.vue
二级目录,结构与上雷同,
<div>加入</div>
Contact.vue
二级目录
<template>
<div>
<h2>联系我们</h2>
<nav>
<ul>
<li>
<router-link to="/about/contact/wechat" tag="h3">微信</router-link>
</li>
<li>
<router-link to="/about/contact/qq" tag="h3">qq</router-link>
</li>
<li>
<router-link to="/about/contact/email" tag="h3">email</router-link>
</li>
</ul>
</nav>
<div class="box">
<router-view />
</div>
</div>
</template>
<script>
export default {
name: "Contect"
}
</script>
<style scoped lang="less">
</style>
Email.vue
三级目录
<template>
<div>Email</div>
</template>
<script>
export default {
name: "Email"
}
</script>
<style scoped lang="less">
</style>
QQ.vue
二级目录,结构与上雷同,
<div>QQ</div>
Wechat.vue
二级目录,结构与上雷同,
<div>微信</div>
路由其他知识点
1. 去掉导航里的#
解决方法
mode: "history"
示例:
router.js
...
export default = new Router({
mode: 'hash', // 去掉#用history
...
});
2. 指定激活的class
解决方法
mode: "linkActiveClass: "active"
示例:
router.js
...
export default = new Router({
linkActiveClass: 'active', // 选中默认样式
...
});
注意:
- 如果此时home连接一直被选中,说明有一个bug。
- 在router.js的home下有一个根目录
{path: '/', component: Home},它没有一个自己的标识,所以没办法被判断。 - 只要改成
{path: '/home', component: Home}。 - 并且在链接上也改一下,
<router-link to="/home">首页</router-link>
3. 指定跳转标签类型
解决方法
mode: "<router-link target="div"></router-link>"
示例:
<!-- 不需要a标签,指定一个跳转标签 -->
<router-link to="/manage" tag="span">管理</router-link>
/**
* 就会被渲染成<span>管理</span>
*/
4. 用属性作为路由地址
解决方法
:to="属性名"
mode: "<router-link :to="homeLink"></router-link>"
示例:
根据数据绑定跳转
<!-- 此时不是定值,需要一个绑定属性,所以用v-bind -->
<router-link :to=orderLink>订单</router-link>
<script>
export default {
name: "...",
// 是一个函数
data () {
return {
orderLink: "/order",
}
}
}
</script>
5. 给路由起名字
解决方法
{path: '/dishes',name: 'dishes',component: Dishes}:to="{name: 'dishes'}"
示例2:
根据路由名字跳转
router.js
...
{path: '/dishes',name: 'dishes',component: Dishes},
...
*.vue
<!-- 根据路由名字跳转 -->
<router-link :to="{name: 'dishes'}">食谱</router-link>
6. 路由跳转方法
- 回退
this.$router.go(-1);
- 跳转到指定路由
this.$router.replace("/manage");- 根据路由名字跳转
this.$router.replace(name: "dishes");
this.$router.push("/manage");- 根据路由名字跳转(常用)
this.$router.push(name: "dishes");
示例:
Home.vue
<template>
<div>
<h6>首页</h6>
<button @click="back">回退</button>
<button @click="goTo">跳转</button>
</div>
</template>
<script>
export default {
name: "home",
methods: {
back() {
this.$router.go(-1); // 回退
},
goTo() {
this.$router.replace("/manage");
// 根据路由名字跳转
this.$router.replace({name: 'dishes'});
this.$router.push("/manage");
// 根据路由名字跳转(常用)
this.$router.push(name: "dishes
}
}
}
</script>
<style scoped lang="less">
</style>
7. 错误路由的重定向
示例:
错误路由的重定向
router.js
...
// 重定向(当用户输入一个错误的地址后跳转)
{path: '*', redirect: "/home"},
...
路由守卫
1. 全局守卫(在所有路由展示前触发)
// 在引入router组件的页面中使用main.js
router.before.Each((to, from, next) => {
// 即将要进入到的路由,值为路由
to ...
// 离开的路由(从哪个路由离开),值为路由
from ...
// 值为函数,这个函数决定你接下来要展示的路由页面
next ...
})
示例:
全局守卫
router.js
...
const router = new Router({
...
});
// 全局守卫,点击路由就会触发
router.beforeEach((to, from, next) => {
// 如果点击的是登陆的那个路由,直接跳转
if (to.path == 'login') {
// 如果是继续执行
next();
} else { // 点击的不是登陆的路由,跳到登陆
next("/login");
}
});
export default router;
2. 后置钩子(在所有路由展示后触发)
// 在引入router组件的页面中使用main.js
router.after.Each((to, from) => {
// 即将要进入到的路由,值为路由
to ...
// 离开的路由(从哪个路由离开),值为路由
from ...
})
3. 路由独享守卫(在当前路由展示前触发)
// 在路由内部使用
beforeEnter(to, from, next) {
// 即将要进入到的路由,值为路由
to ...
// 离开的路由(从哪个路由离开),值为路由
from ...
// 值为函数,这个函数决定你接下来要展示的路由页面
next ...
}
3. 组件内的守卫
// 在路由组件内部使用
beforeRouterEnter(to, from, next) {
// 在当前路由被展示前调用
...
}
beforeRouterUpdate(to, from, next) {
// 在当前路由改变时调用
...
}
beforeRouterLeave(to, from, next) {
// 在离开当前路由时调用
...
}
未完待续...
5. axios
6. vuex
~End~