Vue进阶
组件
组件可以扩展 HTML 元素,封装可重用的代码,类似于模板
template
组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界面都可以抽象为一个组件树
语法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21// 注册一个全局组件
// tagName 为组件名,options 为配置选项
Vue.component(tagName, options)
// 调用
<tagName></tagName>
// 也可以先使用Vue.extend 来创建全局的Vue组件模板对象,通过 template 属性,指定了组件要展示的HTML内容,使用变量接收即可
var com1 = Vue.extend({
template: '<h3>这是使用 Vue.extend 创建的组件</h3>'
})
Vue.component('myCom1', com1)
// 定义全局组件的时候,如果组件名称使用了驼峰命名,则在引用组件的时候,需要把大写的驼峰改为小写的字母,同时,两个单词之前,使用 - 链接
// 组合简写
// 组件的 template 属性指向的模板内容,必须有且仅有一个根元素
Vue.component('mycom1', Vue.extend({
template: '<h3>这是使用 Vue.extend 创建的组件</h3>'
}))
// 一般情况下,我们按如下方式全局注册组件- 全局组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14<div id="app">
<runoob></runoob>
</div>
<script>
// 全局注册
Vue.component('runoob', {
})
// 创建根实例
new Vue({
el: '#app'
})
</script>- 局部组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20<div id="app">
<runoob></runoob>
</div>
<script>
// 模板对象
var Child = {
}
// 创建根实例
new Vue({
el: '#app',
// 局部注册
components: {
// <runoob> 将只在父模板可用
'runoob': Child
}
})
</script>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34<div id="app2">
<mycom3></mycom3>
<login></login>
</div>
<template id="tmpl">
<div>
<h1>这是通过 template 元素,在外部定义的组件结构,这个方式,有代码的只能提示和高亮</h1>
<h4>好用,不错!</h4>
</div>
</template>
<template id="tmpl2">
<h1>这是私有的 login 组件</h1>
</template>
<script>
Vue.component('mycom3', {
template: '#tmpl'
})
var vm = new Vue({
el: '#app'
});
var vm2 = new Vue({
el: '#app2',
components: { // 定义实例内部私有组件的
login: {
template: '#tmpl2'
}
}
})
</script>:is
属性,可以用来指定要展示的组件的名称
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27<div id="app">
<a href="" @click.prevent="comName='login'">登录</a>
<a href="" @click.prevent="comName='register'">注册</a>
<!-- Vue提供了 component ,来展示对应名称的组件 -->
<!-- component 是一个占位符 -->
<component :is="comName"></component>
</div>
<script>
// 组件名称是 字符串
Vue.component('login', {
})
Vue.component('register', {
})
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
comName: 'login' // 当前 component 中的 :is 绑定的组件的名称
}
});
</script>prop
父模板的数据需要通过 props 传给子组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18<div id="app">
<!--message变量可以自定义-->
<child message="hello!"></child>
</div>
<script>
// 注册
Vue.component('child', {
// 子组件需要显式地用 props 选项声明
props: ['message'],
// 同样也可以在 vm 实例中像 "this.message" 这样使用
})
// 创建根实例
new Vue({
el: '#app'
})
</script>- 也可以用 v-bind 动态绑定 props 的值
- 注:prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是不会反过来
- 也就是说
v-bind
可以作为一种方法来传导数据,但是具有动态效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25<div id="app">
<div>
<input v-model="parentMsg">
<br>
<!-- 当父组件(模板)的数据变化时,该变化也会传导给子组件 -->
<child v-bind:message="parentMsg"></child>
</div>
</div>
<script>
// 注册
Vue.component('child', {
// 声明 props
props: ['message'],
// 同样也可以在 vm 实例中像 "this.message" 这样使用
})
// 创建根实例
new Vue({
el: '#app',
data: {
parentMsg: '父组件内容'
}
})
</script>- 如下,我们利用组件设计一个带有验证功能的模板;可以为 props 中的值提供一个带有验证需求的对象,而不是一个字符串数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34Vue.component('my-component', {
props: {
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
})
// type 也可以是一个自定义构造器,使用 instanceof 检测- 父组件是使用 props 传递数据给子组件,但如果子组件要把数据传递回去,就需要使用自定义事件!
- 使用 v-on 绑定自定义事件, 每个 Vue 实例都实现了事件接口(Events interface),即:
- 使用
$on(eventName)
监听事件 - 使用
$emit(eventName)
触发事件
- 使用
- 通过触发或者监听事件,达到子组件传递数据的效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36<div id="app">
<div id="counter-event-example">
<p>{{ total }}</p>
<button-counter v-on:increment="incrementTotal"></button-counter>
<button-counter v-on:increment="incrementTotal"></button-counter>
</div>
</div>
<script>
Vue.component('button-counter', {
data: function () {
return {
counter: 0
}
},
methods: {
incrementHandler: function () {
this.counter += 1
// 通过 $emit(increment) 出发父组件中绑定的 incrementTotal 方法
this.$emit('increment')
}
},
})
new Vue({
el: '#counter-event-example',
data: {
total: 0
},
methods: {
incrementTotal: function () {
this.total += 1
}
}
})
</script>注:上面的
data
必须是一个函数,而不是一个对象
自定义指令
类似于注册组件
1
2// 注册一个全局指令
Vue.directive(comName, options)全局指令 v-focus, 该指令的功能是在页面加载时,input 元素自动获取焦点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21<div id="app">
<p>页面载入时,input 元素自动获取焦点:</p>
<input v-focus>
</div>
<script>
// 注册一个全局自定义指令 v-focus
Vue.directive('focus', {
// 当绑定元素插入到 DOM 中
// el指绑定的命令,可以直接操作DOM
// js教程:https://www.w3school.com.cn/js/index.asp
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
// 创建根实例
new Vue({
el: '#app'
})
</script>同样,可以注册 局部指令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21<div id="app">
<p>页面载入时,input 元素自动获取焦点:</p>
<input v-focus>
</div>
<script>
// 创建根实例
new Vue({
el: '#app',
directives: {
// 注册一个局部的自定义指令 v-focus
focus: {
// 指令的定义
inserted: function (el) {
// 聚焦元素
el.focus()
}
}
}
})
</script>上例中使用的
inserted
属于指令定义的钩子函数,如下bind
: 只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作。inserted
: 被绑定元素(指令)插入父节点(模板标签)时调用(父节点存在即可调用,不必存在于 document 中)。update
: 被绑定元素所在的模板更新时调用,而不论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新(详细的钩子函数参数见下)。componentUpdated
: 被绑定元素所在模板完成一次更新周期时调用。unbind
: 只调用一次, 指令与元素解绑时调用。
钩子函数的参数有:
- el: 指令所绑定的元素,可以用来直接操作 DOM 。
- binding:一个对象,包含以下属性:
- name: 指令名,不包括
v-
前缀。 - value: 指令的绑定值, 例如:
v-my-directive="1 + 1"
, value 的值是2
。 - oldValue: 指令绑定的前一个值,仅在
update
和componentUpdated
钩子中可用。无论值是否改变都可用。 - expression: 绑定值的表达式或变量名。 例如
v-my-directive="1 + 1"
, expression 的值是"1 + 1"
。 - arg: 传给指令的参数。例如
v-my-directive:foo
, arg 的值是"foo"
。 - modifiers: 一个包含修饰符的对象。 例如:
v-my-directive.foo.bar
, 修饰符对象 modifiers 的值是{ foo: true, bar: true }
。
- name: 指令名,不包括
- vnode: Vue 编译生成的虚拟节点。
- oldVnode: 上一个虚拟节点,仅在
update
和componentUpdated
钩子中可用。
有时候我们不需要其他钩子函数,我们可以简写函数:
1
2
3
4Vue.directive('runoob', function (el, binding) {
// 设置指令的背景颜色
el.style.backgroundColor = binding.value.color
})
路由
路由允许我们通过不同的 URL 访问不同的内容
路由需要载入 vue-router 库,中文文档地址:vue-router文档
首先介绍一下获取DOM元素和组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40<div id="app">
<input type="button" value="获取元素" @click="getElement">
<!-- 使用ref即可将DOM节点添加到$refs变量 -->
<h3 id="myh3" ref="h3">今天天气太好了!!!</h3>
<hr>
<login ref="mylogin"></login>
</div>
<script>
var login = {
data() {
return {
msg: 'son msg'
}
},
methods: {
show() {
console.log('调用了子组件的方法')
}
}
}
var vm = new Vue({
el: '#app',
methods: {
getElement() {
// 不建议使用JS直接操作DOM元素
console.log(document.getElementById('myh3').innerText)
// 在浏览器中查看Vue实例,有一个$refs变量
console.log(this.$refs.h3.innerText)
console.log(this.$refs.mylogin.msg)
this.$refs.mylogin.show()
}
},
components: {
login // login:login
}
});
</script>什么是路由
1. 后端路由:对于普通的网站,所有的超链接都是URL地址,所有的URL地址都对应服务器上对应的资源 2. 前端路由:对于单页面应用程序来说,主要通过URL中的hash(#号)来实现不同页面之间的切换,同时,hash有一个特点:HTTP请求中不会包含hash相关的内容;所以,单页面程序中的页面跳转主要用hash实现 3. 在单页面应用程序中,这种通过hash改变来切换页面的方式,称作前端路由(区别于后端路由),也就是说,旨在前端的静态页面之间跳转
vue-router.js
文件应该放在vue.js
之后- 创建一个路由对象类似于创建Vue实例,最后需要在Vue实例中使用
router
注册
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31.myactive{ /* 组件模板自定义样式 */
color: red;
font-weight: 800;
font-style: italic;
font-size: 80px;
text-decoration: underline;
background-color: green;
}
.v-enter,
.v-leave-to { /* <router-view>样式,淡入淡出 */
opacity: 0;
transform: translateX(140px);
}
.v-enter-active,
.v-leave-active {
transition: all 0.5s ease;
}
<!-- 路由规则匹配到的组件,就会展示到 router-view 中去 -->
<transition mode="out-in">
<router-view></router-view>
</transition>
<!-- 推荐使用a标签改变路由地址,可以传参;Vue提供了router-link标签 -->
<a href="#/login?id=1&name=roy">登录</a>
<a href="#/register">注册</a>
<!-- router-link 默认渲染为一个a 标签 -->
<router-link to="/login" tag="span">登录</router-link>
<router-link to="/register">注册</router-link>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26<script>
// 组件的模板对象
var login = { template: '<h1>登录组件</h1>' }
var register = { template: '<h1>注册组件</h1>' }
// 创建一个路由对象,当导入 vue-router 包之后,在 window 全局对象中就有了一个路由的构造函数 VueRouter
// 在 new 路由对象的时候,可以为构造函数传递一个配置对象
var routerObj = new VueRouter({
// routes // 这个配置对象中的 routes 表示 【路由匹配规则】 的意思
routes: [ // 路由匹配规则
// 每个路由规则,都是一个对象,这个规则对象有两个必须的属性:
// 属性1 是 path, 表示监听哪个路由链接地址;
// 属性2 是 component,表示如果路由是前面匹配到的 path 则展示 component 属性对应的那个组件
// 注意: component 的属性值必须是一个组件的模板对象, 不能是组件的引用名称;
{ path: '/', redirect: '/login' },// 前端页面跳转,默认显示登录组件
{ path: '/login', component: login },
{ path: '/register', component: register }
],
linkActiveClass: 'myactive' //自定义类样式名称
})
var vm = new Vue({
el: '#app',
router: routerObj // 将路由规则对象,注册到 vm 实例上用来监听 URL 地址的变化,然后展示对应的组件
});
</script>- 路由参数的获取
- 在Vue实例中的
$route
变量内 - 可以通过路由匹配规则的
path
属性,通过$params
变量获取
- 在Vue实例中的
1
2
3
4
5
6
7
8
9var login = {
template: '<h1>登录--- {{ this.$route.query.id }}
--- {{ this.$route.query.name }} </h1>',
data(){ return { msg: '123' } },
created(){ // 组件的生命周期钩子函数
// console.log(this.$route)
// console.log(this.$route.query.id)
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18var login = {
template: '<h1>登录 --- {{ $route.params.id }}
--- {{ $route.params.name }}</h1>',
data(){
return {
msg: '123'
}
},
created(){ // 组件的生命周期钩子函数
console.log(this.$route.params.id)
}
}
var router = new VueRouter({
routes: [
{ path: '/login/:id/:name', component: login },
{ path: '/register', component: register }
]
})路由的嵌套
- 使用
children
属性实现子路由 - 子路由可以保留根路由的内容不被刷新掉
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38<div id="app">
<router-link to="/account">Account</router-link>
<router-view></router-view>
</div>
<template id="tmpl">
<div>
<h1>这是 Account 组件</h1>
<router-link to="/account/login">登录</router-link>
<router-link to="/account/register">注册</router-link>
<router-view></router-view>
</div>
</template>
<script>
// 组件的模板对象
var account = { template: '#tmpl' }
var login = { template: '<h3>登录</h3>' }
var register = { template: '<h3>注册</h3>' }
var router = new VueRouter({
routes: [
{
path: '/account',
component: account,
children: [
{ path: 'login', component: login },
{ path: 'register', component: register }
]
},
// 使用 children 属性实现子路由,子路由的 path 前面,不要带 / ,否则永远以根路径开始请求,这样不方便我们用户去理解URL地址
]
})
var vm = new Vue({
el: '#app',
router
});
</script>使用命名视图实现经典布局
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53.container {
display: flex;
height: 300px;
}
.left {
background-color: lightgreen;
flex: 2;
}
.main {
background-color: lightpink;
flex: 8;
}
<div id="app">
<router-view></router-view>
<div class="container">
<!--这就叫命名视图,name对应路由模板components-->
<router-view name="left"></router-view>
<router-view name="main"></router-view>
</div>
</div>
<script>
// 创建路由对象
var router = new VueRouter({
routes: [
// { path: '/', component: header },
// { path: '/left', component: leftBox },
// { path: '/main', component: mainBox }
{
path: '/',
// 使用components,表示多个组件模板
components: {
'default': header,
'left': leftBox,
'main': mainBox
}
}
]
})
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
router
});
</script>Ajax(axios)
- 推荐使用 axios 来完成 ajax 请求,Axios 是一个基于 Promise 的 HTTP 库
- 使用github地址将
axios.min.js
文件下载下来即可使用
或者在项目中使用npm install axios --save
或者在线使用<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
- vue2.x时广泛应用
- 这部分是核心关键,但具体用法因人而异比较灵活
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68// GET方法
new Vue({
el: '#app',
data () {
return {
info: null
}
},
mounted () {
axios
.get('https://www.runoob.com/try/ajax/json_demo.json')// 链式操作
.then(response => (this.info = response)) // 请求到信息response
.catch(function (error) { // 请求失败处理,使用catch捕获
console.log(error);
});
}
})
// 使用 response.data 读取 JSON 数据
<div id="app">
<h1>网站列表</h1>
<div
v-for="site in info"
>
{{ site.name }}
</div>
</div>
<script type = "text/javascript">
new Vue({
el: '#app',
data () {
return {
info: null
}
},
mounted () {
axios
.get('https://www.runoob.com/try/ajax/json_demo.json')
.then(response => (this.info = response.data.sites))// 箭头函数
.catch(function (error) { // 请求失败处理
console.log(error);
});
}
})
</script>
// GET 方法传递参数格式如下
// 直接在 URL 上添加参数 ID=12345
axios.get('/user?ID=12345')
.then(function (response) {// 匿名函数
console.log(response);
})
.catch(function (error) {
console.log(error);
});
// 也可以通过 params 设置参数:比较推荐这种方式
axios.get('/user', {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});- 请求就是要传递参数到服务器获取数据嘛
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29// POST 方法
new Vue({
el: '#app',
data () {
return {
info: null
}
},
mounted () {
axios
.post('https://www.runoob.com/try/ajax/demo_axios_post.php')
.then(response => (this.info = response))
.catch(function (error) { // 请求失败处理
console.log(error);
});
}
})
// 传递参数格式如下
axios.post('/user', { // 不需要使用params
firstName: 'Fred', // 参数 firstName
lastName: 'Flintstone' // 参数 lastName
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});- 也可以用配置形式来创建请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32axios(config)
// 发送 POST 请求
axios({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
});
// GET 请求远程图片
axios({
method:'get',
url:'http://bit.ly/2mTM3nY',
responseType:'stream' // get方法还是要使用params
})
.then(function(response) {
response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'))
});
// 发送 GET 请求(默认的方法)
axios('/user/12345');
// 请求方法的别名
axios.request(config)
axios.get(url[, config])
axios.delete(url[, config])
axios.head(url[, config])
axios.post(url[, data[, config]])
axios.put(url[, data[, config]])
axios.patch(url[, data[, config]])1
2
3
4
5
6
7
8
9
10
11
12// 执行多个并发请求
function getUserAccount() {
return axios.get('/user/12345');
}
function getUserPermissions() {
return axios.get('/user/12345/permissions');
}
axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (acct, perms) {
// 两个请求现在都执行完成
}));- 请求配置项
- 拦截器:在被 then 或者 catch 处理之前截取请求或响应
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//添加请求拦截器
axios.interceptors.request.use(function(config){
//在发送请求之前做点事
return config;
},function(error){
//请求错误时做点事
return Promise.reject(error);
});
//添加响应拦截器
axios.interceptors.response.use(function(response){
//对响应数据做点事
return response;
},function(error){
//请求错误时做些事
return Promise.reject(error);
});- 如果有问题可以仔细看一下官方文档
http://www.axios-js.com/zh-cn/docs/
Ajax(vue-resource)
- 一款vue插件,用于处理ajax请求,vue1.x时广泛应用,现不被维护
- 所以就先不学了