0%

Vue

Vue官网

第一个Vue程序

在教程安装里给出了多种引入方式,具体点击这里

直接</script>引入

在编辑器上输入html回车,这时候就会自动补全以下代码

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>

</body>
</html>

引入 Vue

1
2
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

在页面输出hello Vue,用浏览器打开文件并检验效果<div>hello Vue</div>

可以之后我们改造源文件让它与Vue产生关联:new 一个Vue实例

1
2
3
4
5
6
7
8
<script type="text/javascript">
new Vue({
el: '#app', //挂载到指定节点上
data: {
message: 'Hello Vue!'
}
})
</script>

所挂载的节点需要添加一个id<div id="app"></div>

最终源码

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>

<body>
<div id="app">
{{message}} {{message + message}}
<div :id="message"></div>
<ul>
<li v-for="item in list">
<span v-if="!item.del">{{item.title}}</span>
<span v-else style="text-decoration: line-through">{{item.title}}</span>
<button v-show="!item.del">删除</button>
</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
message: 'hello world',
list: [{
title: '课程1',
del: false
}, {
title: '课程2',
del: true
}],
}
})
</script>
</body>

</html>

此时用浏览器打开即可见到显示Hello Vue!

参考官方demo 链接

添加方法

添加显示

1
2
3
4
<div class="addMethod">
<input type="text" name="" value="" v-model="info">
<button type="button" name="button" @click="handleClick">add</button>
</div>

其中

  • v-model是Vue里面的双向绑定,
  • v-for是循环遍历
  • @click=”handleClick” 绑定方法
  • console.log(this.info) 打印信息,如何查看打印输出:浏览器-右键-检查-Console
  • handleClick方法生命在methods对象里面
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
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>

</body>

<div id="app">
{{ message }}
<div class="addMethod">
<input type="text" name="" value="" v-model="info">
<button type="button" name="button" @click="handleClick">add</button>
</div>
<ul>
<li v-for="item in list">{{item}}</li>
</ul>
</div>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<script type="text/javascript">
new Vue({
el: '#app', //挂载到指定节点上
data: {
message: 'Hello Vue!',
info: '',
list:[],
},
methods: {
handleClick(){
//console
this.list.push(this.info)
this.info = ''
}
}
})
</script>
</html>

使用自定义组件

定义名为 todo-item 的新组件

1
2
3
4
Vue.component('todo-item',{
props:['item'],
template: '<li class="item">{{item}}</li>'
})

创建一个 todo-item 组件的实例,并传递属性值 v-bind:item="item"或者简写成:item="item"
<todo-item v-for="item in list" :item="item"></todo-item>

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
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
<style media="screen">
.item {
color:red;
}
</style>
</head>
<body>

</body>

<div id="app">
{{ message }}
<div class="addMethod">
<input type="text" name="" value="" v-model="info">
<button type="button" name="button" @click="handleClick">add</button>
</div>
<ul>
<todo-item v-for="item in list" :item="item">{{item}}</todo-item>
</ul>
</div>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<script type="text/javascript">
Vue.component('todo-item',{
props:['item'],
template: '<li class="item">{{item}}</li>'
})
new Vue({
el: '#app', //挂载到指定节点上
data: {
message: 'Hello Vue!',
info: '',
list:[],
},
methods: {
handleClick(){
console.log(this.info)
this.list.push(this.info)//往list数组添加数据
this.info = ''//每次点击add的同时,清空输入框
}
}
})
</script>
</html>

以上操作有几个缺点

  • 全局定义:强制要求每个component中的命名不得重复
  • 字符串模版:缺乏语法高亮,在html有多行的时候,需要用\
  • 不支持CSS:意味着当html和JavaScript组件化时,CSS明显被遗漏
  • 没有构建步骤:限制只能用html和ES5 JavaScript,而不能使用预处理器,如Pug(formerly Jade)和Babel,即每次都需要手动在浏览器上刷新,没有自动热更新。

npm安装

安装Vuebash npm install vue

安装命令行工具 (CLI)Vue CLI

npm install -g @vue/cli

出现安装问题

1
2
3
4
5
6
7
8
npm WARN deprecated joi@14.3.1: This module has moved and is now available at @hapi/joi. Please update your dependencies as this version is no longer maintained an may contain bugs and security issues.
npm WARN deprecated topo@3.0.3: This module has moved and is now available at @hapi/topo. Please update your dependencies as this version is no longer maintained an may contain bugs and security issues.
npm WARN deprecated hoek@6.1.3: This module has moved and is now available at @hapi/hoek. Please update your dependencies as this version is no longer maintained an may contain bugs and security issues.
npm WARN deprecated cross-spawn-async@2.2.5: cross-spawn no longer requires a build toolchain, use it instead
npm ERR! Unexpected end of JSON input while parsing near '...TGOVzYcDOP1jLScCp0ACN'

npm ERR! A complete log of this run can be found in:
npm ERR! /Users/sam/.npm/_logs/2019-04-29T01_23_19_163Z-debug.log

清楚一下缓存npm cache clean --force

运行vue --version出现版本信息则说明安装成功

创建新项目vue create my-app

使用默认安装

1
2
3
4
Vue CLI v3.7.0
? Please pick a preset: (Use arrow keys)
❯ default (babel, eslint)
Manually select features

启动项目

1
2
cd my-app
npm run serve
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
69
70
71
72
73
74
> my-app@0.1.0 serve /Users/sam/Documents/studyUp/my-app
> vue-cli-service serve

INFO Starting development server...
98% after emitting CopyPlugin .
DONE Compiled successfully in 6412ms 上午9:53:14


App running at:
- Local: http://localhost:8080/
- Network: http://192.168.43.116:8080/

Note that the development build is not optimized.
To create a production build, run npm run build.
```



打开localhost:8080即可见到效果。


## 目录介绍

![image.png](https://upload-images.jianshu.io/upload_images/3850802-8fcdd44c56b23d35.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


其中

1 node_modules 整个项目的依赖

2 pubic ico 图标

3 pubic index.html 整个项目的载体<div id="app"></div> 跟上面👆直接`</script>`引入的div一样

4 src 整个项目的源代码

5 Main.js 项目入口文件

6 babel.config.js babel配置

7 package.json 依赖包版本

8 说明


将上面的直接</script>引入的demo改成单文件形式👇三个模块独立

- 模版 template
- 逻辑 script
- 样式 style


<style scoped>样式作用域只在该文件内有效


在App.vue文件的模版中替换`div`内容,新建主件TodoItem.vue,在App.vue引入并使用


```bash
<template>
<li class="item">{{item}}</li>
</template>

<script>
export default {
props: ['item'],
}
</script>

<style scoped>
.item {
color: red;
}
</style>
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
<template>
<div id="app">
{{ message }}
<div class="addMethod">
<input type="text" name="" value="" v-model="info">
<button type="button" name="button" @click="handleClick">add</button>
</div>
<ul>
<todo-item v-for="item in list" :key="item" :item="item">{{item}}</todo-item>
</ul>
</div>
</template>

<script>
import TodoItem from './components/TodoItem.vue' //引入TodoItem
export default {
name: 'app',
data() {
return {
message: 'hello vue',
info: '',
list: [],
}
},

methods: {
handleClick() {
this.list.push(this.info)
this.info = ''
}
},

components: {
TodoItem, //注册TodoItem
}

}
</script>

<style>
</style>
1
<todo-item v-for="item in list" :key="item" :item="item">{{item}}</todo-item>

上面👆是通过属性来传递item,下面👇改成插槽的方式

1
<span style="font-size:20px">{{item}}</span>

此时解析由<li class="item"></li>改成

1
2
3
<li class="item">
<slot></slot>
</li>

或者给插槽一个名字(具名插槽)

1
2
3
4
5
<todo-item v-for="item in list" :key="item">
<template id="" v-slot:item >
<span style="font-size:20px">{{item}}</span>
</template>
</todo-item>
1
2
3
4
5
<template>
<li class="item">
<slot name="item"></slot>
</li>
</template>

作用域插槽(使用场景:由子控件传递值给父控件,供父控件使用修改显示,选中由红变蓝)

1
2
3
4
5
<todo-item v-for="item in list" :key="item">
<template id="" v-slot:item="itemProps"> 获取checked的值
<span :style="{fontSize:'20px', color: itemProps.checked ? 'red' : 'blue' }">{{item}}</span>
</template>
</todo-item>
1
2
3
4
5
6
<template>
<li class="item">
<input type="checkbox" v-model="checked">
<slot name="item" v-bind="{{checked}}"></slot> //将checked 传递出去
</li>
</template>

动手复制代码跑来看看效果吧 👇👇👇👇👇👇👇👇👇

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<li class="item">
<input type="checkbox" v-model="checked">
<slot name="item" v-bind="{checked}"></slot>
</li>
</template>

<script>
export default {
props: ['item'],
data() {
return {
checked:false
}
}
}
</script>

<style scoped>
.item {
color: red;
}
</style>
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
<template>
<div id="app">
{{ message }}
<div class="addMethod">
<input type="text" name="" value="" v-model="info">
<button type="button" name="button" @click="handleClick">add</button>
</div>
<ul>
<todo-item v-for="item in list" :key="item">
<template id="" v-slot:item="itemProps">
<span :style="{fontSize:'20px', color: itemProps.checked ? 'red' : 'blue' }">{{item}}</span>
</template>
</todo-item>
</ul>
</div>
</template>

<script>
import TodoItem from './components/TodoItem.vue' //引入TodoItem
export default {
name: 'app',
data() {
return {
message: 'hello vue',
info: '',
list: [],
}
},

methods: {
handleClick() {
this.list.push(this.info)
this.info = ''
}
},

components: {
TodoItem, //注册TodoItem
}

}
</script>

<style>
</style>

条件渲染

条件渲染官方文档地址
v-if
v-else
v-show
v-for

组件基础&组件注册

组件基础

组件注册

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
69
70
71
72
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>

<body>
<div id="app">
{{message}} {{message + message}}
<div :id="message"></div>
<!-- <ul>
<todo-item v-for="item in list" :title="item.title" :del="item.del"></todo-item>
</ul> -->
<todo-list></todo-list>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('todo-item', {
props: {
title: String,
del: {
type: Boolean,
default: false,
},
},
template: `
<li>
<span v-if="!del">{{title}}</span>
<span v-else style="text-decoration: line-through">{{title}}</span>
<button v-show="!del">删除</button>
</li>
`,
data: function() {
return {}
},
methods: {

},
})
Vue.component('todo-list', {
template: `
<ul>
<todo-item v-for="item in list" :title="item.title" :del="item.del"></todo-item>
</ul>
`,
data: function() {
return {
list: [{
title: '课程1',
del: false
}, {
title: '课程2',
del: true
}],
}
}
})
var vm = new Vue({
el: '#app',
data: {
message: 'hello world',

}
})
</script>
</body>

</html>

在Vue中,组件:小型的,一个个独立,可以复用的UI模块。它有三大概念:属性、事件、插槽

属性

Prop

  • 自定义属性props
    • 组件props中声明的属性
  • 原生属性attrs
    • 没有生命的属性,默认自动挂载到组件根元素上
    • 设置inheritAttrs 为false可以关闭自动挂载
  • 特殊属性 class、style
    • 挂载到组件根元素上
    • 支持字符串、对象、数组等多种语法
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
<template>
<div>
name: {{ name }}
<br />
type: {{ type }}
<br />
list: {{ list }}
<br />
isVisible: {{ isVisible }}
<br />
<button @click="handleClick">change type</button>
</div>
</template>

<script>
export default {
name: "PropsDemo",
// inheritAttrs: false,
// props: ['name', 'type', 'list', 'isVisible'],
props: {
name: String,
type: {
validator: function(value) {
// 这个值必须匹配下列字符串中的一个
return ["success", "warning", "danger"].includes(value);
}
},
list: {
type: Array,
// 对象或数组默认值必须从一个工厂函数获取
default: () => []
},
isVisible: {
type: Boolean,
default: false
},
onChange: {
type: Function,
default: () => {}
}
},
methods: {
handleClick() {
// 不要这么做、不要这么做、不要这么做
// this.type = "warning";
// 可以,还可以更好
this.onChange(this.type === "success" ? "warning" : "success");
}
}
};
</script>

事件

事件处理

  • 普通时间

    • @click,@input,@change,@xxx等事件
    • 通过this.$emit(‘xxx’,…)触发
  • 修饰符事件

    • @input.trim,@click.stop,@submit.prevent 等
    • 一般用于原生html元素,自定义组件需要自行开发支持
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
<template>
<div>
name: {{ name || "--" }}
<br />
<input :value="name" @change="handleChange" />
<br />
<br />
<div @click="handleDivClick">
<button @click="handleClick">重置成功</button>&nbsp;&nbsp;&nbsp;
<button @click.stop="handleClick">重置失败</button>
</div>
</div>
</template>

<script>
export default {
name: "EventDemo",
props: {
name: String
},
methods: {
handleChange(e) {
this.$emit("change", e.target.value);
},
handleDivClick() {
this.$emit("change", "");
},
handleClick(e) {
// 都会失败
//e.stopPropagation();
}
}
};
</script>
```




```bash
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>

<body>
<div id="app">
{{message}} {{message + message}}
<div :id="message"></div>
<!-- <ul>
<todo-item v-for="item in list" :title="item.title" :del="item.del"></todo-item>
</ul> -->
<todo-list></todo-list>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('todo-item', {
props: {
title: String,
del: {
type: Boolean,
default: false,
},
},
template: `
<li>
<span v-if="!del">{{title}}</span>
<span v-else style="text-decoration: line-through">{{title}}</span>
<button v-show="!del" @click="handleClick">删除</button>
</li>
`,
data: function() {
return {}
},
methods: {
handleClick(e) {
console.log('点击删除按钮')
this.$emit('delete', this.title)
}
},
})
Vue.component('todo-list', {
template: `
<ul>
<todo-item @delete="handleDelete" v-for="item in list" :title="item.title" :del="item.del"></todo-item>
</ul>
`,
data: function() {
return {
list: [{
title: '课程1',
del: false
}, {
title: '课程2',
del: true
}],
}
},
methods: {
handleDelete(val) {
console.log('handleDelete', val)
}
}
})
var vm = new Vue({
el: '#app',
data: {
message: 'hello world',

}
})
</script>
</body>

</html>

插槽

插槽

  • 普通插槽
    • <template slot="xxx">...</template>
    • <template v-slot:xxx>...</template>
  • 作用域插槽
    • <template slot="xxx" slot-scope="props">...</template>
    • <template v-slot:xxx="props">...</template>
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
<template>
<div>
<slot />
<slot name="title" />
<slot name="item" v-bind="{ value: 'vue' }" />
</div>
</template>

<script>
export default {
name: "SlotDemo"
};
</script>
```

我们可以理解为万物皆“属性”,即它们都是父组件传递给子组件,然后子组件根据传递的内容来执行对应的行为。请看demo

```bash
<template>
<div>
{{ name }}
<br />
<button @click="handleChange">change name</button>
<br />
<!-- {{ slotDefault }} -->
<VNodes :vnodes="slotDefault" />
<br />
<VNodes :vnodes="slotTitle" />
<br />
<VNodes :vnodes="slotScopeItem({ value: 'vue' })" />
</div>
</template>

<script>
export default {
name: "BigProps",
components: {
VNodes: {
functional: true,
render: (h, ctx) => ctx.props.vnodes
}
},
props: {
name: String,
onChange: {
type: Function,
default: () => {}
},
slotDefault: Array,
slotTitle: Array,
slotScopeItem: {
type: Function,
default: () => {}
}
},
methods: {
handleChange() {
this.onChange("Hello vue!");
}
}
};
</script>

```



```bash
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>

<body>
<div id="app">
{{message}} {{message + message}}
<div :id="message"></div>
<!-- <ul>
<todo-item v-for="item in list" :title="item.title" :del="item.del"></todo-item>
</ul> -->
<todo-list>
<todo-item @delete="handleDelete" v-for="item in list" :title="item.title" :del="item.del">
<template v-slot:pre-icon="{value}">
<span>前置图标 {{value}}</span>
</template>

</todo-item>
</todo-list>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('todo-item', {
props: {
title: String,
del: {
type: Boolean,
default: false,
},
},
template: `
<li>
<slot name="pre-icon" :value="value"></slot>
<span v-if="!del">{{title}}</span>
<span v-else style="text-decoration: line-through">{{title}}</span>
<slot name="suf-icon">😄</slot>
<button v-show="!del" @click="handleClick">删除</button>
</li>
`,
data: function() {
return {
value: Math.random()
}
},
methods: {
handleClick(e) {
console.log('点击删除按钮')
this.$emit('delete', this.title)
}
},
})
Vue.component('todo-list', {
template: `
<ul>
<slot></slot>
</ul>
`,
data: function() {
return {

}
},
})
var vm = new Vue({
el: '#app',
data: {
message: 'hello world',
list: [{
title: '课程1',
del: false
}, {
title: '课程2',
del: true
}],
},
methods: {
handleDelete(val) {
console.log('handleDelete', val)
}
}
})
</script>
</body>

</html>

理解单文件组件

意思是通过Vue CLI构建整个项目,而不是单个html文件。

双向绑定和单向数据流不冲突

v-model创建双向绑定

双向绑定:数据更新时,视图同步更新;反过来视图更新时,数据也会更新。可以通过v-model来创建双向绑定。

v-model

文档在表单输入绑定有说明:

v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:

  • text 和 textarea 元素使用 value 属性和 input 事件;
  • checkbox 和 radio 使用 checked 属性和 change 事件;
  • select 字段将 value 作为 prop 并将 change 作为事件。

自定义组件的 v-model

.sync修饰符 创建双向绑定

.sync 修饰符

单向数据流

单向数据流

总结

  • Vue是单向数据流,不是双向绑定
  • Vue的双向绑定不过是语法糖
  • Object.defineProperty是用来做响应式更新的,和双向绑定没关系
  • v-model来实现双向绑定的实质是:属性的传递和事件的回调来做一个数据更新。所以说它仅仅是语法糖而已。

如何触发组件的更新

数据来源(单向的)

  • 来自父类元素的属性
  • 来自组件自身的状态 data
  • 来自状态管理器

状态data 和 属性props 区别

  • 状态是组件自身的数据
  • 属性来自父组件的数据
  • 状态或者属性的改变未必会让视图更新(只有做了响应才会更新,即放在了return里面)

合理应用计算属性和侦听器

 
计算属性computed

  • 减少模版中的计算逻辑
  • 数据缓存
  • 以来固定的数据类型(响应式编程)

侦听器watch中可以执行任何逻辑,如函数节流,Ajax异步获取数据,甚至操作com

总之,computed能做的,watch都能做,反之则不行;能用computed的尽量用computed;

生命周期的应用场景和函数式组件

生命周期图示

函数式组件

  • functional:true 可以将它看作一个方法
  • 无状态、无实力、没有this上下文、无生命周期

指令的本质是什么

源码演示

provide & inject

provide / inject

源码演示

解决跨组件间的属性传递问题。

获取跨层级组件实例

源码演示

template VS JSX

源码演示

Vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

  • state,驱动应用的数据源;
  • view,以声明方式将 state 映射到视图;
  • actions,响应在 view 上的用户输入导致的状态变化。  

    在Vue中使用Vuex

源码演示

Vuex原理

  • State提供一个响应式数据
  • Getter借助Vue的计算属性computed来实现缓存
  • Mutation更改state方法
  • Action触发mutation方法
  • ModuleVue.set动态添加state到响应式数据中

Vue Router

Vue Router

源码演示

Nuxt

Nuxt

开发插件

vs code插件:Vetur:代码块功能

ESLint:代码规范、错误检查、

Prettier:代码规范提示

单元测试

三种方式:

  • jest或者mocha
  • @vue/test-utils
  • sinon
1
2
3
4
5
6
vue create XX
Manually select features
除了默认之外还需要勾选 Unit Testing
选用ESLint + Standard config
Lint on save
JEST

demo

实战

创建工程

1
➜  Vue_app git:(master) vue create  vue-app

手动选择

1
2
3
4
5
6
7
8
9
 ◉ Babel
◯ TypeScript
◯ Progressive Web App (PWA) Support
◉ Router
◉ Vuex
◉ CSS Pre-processors
◉ Linter / Formatter
❯◉ Unit Testing
◯ E2E Testing

其他选项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 ? Use history mode for router? (Requires proper server setup for index fallback in produc
tion) Yes
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by defaul
t): Less
? Pick a linter / formatter config: Prettier
? Pick additional lint features:
◉ Lint on save
❯◉ Lint and fix on commit
? Pick additional lint features: Lint on save, Lint and fix on commit
? Pick a unit testing solution: Jest
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? Yes
? Save preset as: vue-app
? Pick the package manager to use when installing dependencies: Yarn

启动

1
2
$ cd vue-app
$ yarn serve

安装两个依赖包(上面步骤创建工程时选择了Yarn构建,现在通过npm安装依赖包也是可以的原来)

1
npm i ant-design-vue moment

更改构建方式

文档说明

1
2
3
vi ~/.vuerc 
修改: "packageManager": "yarn", #或者是npm
source ~/.vuerc

我自己也重新操作了一下,项目构建成功之后的提示为

1
2
$ cd vue-app
$ npm run serve

webpack配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 
ERROR Failed to compile with 1 errors 7:20:06 PM

error in ./node_modules/ant-design-vue/dist/antd.less

Module build failed (from ./node_modules/less-loader/dist/cjs.js):


// https://github.com/ant-design/ant-motion/issues/44
.bezierEasingMixin();
^
Inline JavaScript is not enabled. Is it set in your options?
in /Users/samtake/Documents/GitHub/Vue_app/vue-app/node_modules/ant-design-vue/lib/style/color/bezierEa
sing.less (line 110, column 0)

@ ./node_modules/ant-design-vue/dist/antd.less 4:14-188 14:3-18:5 15:22-196
@ ./src/main.js
@ multi (webpack)-dev-server/client?http://192.168.0.103:8086/sockjs-node (webpack)/hot/dev-server.js ./src/
main.js

css.loaderOptions

新建一个vue.config.js文件

1
2
3
4
5
6
7
8
9
10
module.exports = {
css: {
loaderOptions: {
less: {
// 这里的选项会传递给 css-loader
javascriptEnabled:true
}
}
}
}

重新启动构建npm run serve

按需加载

文档

配置babel.config.js文件

1
2
3
4
5
6
7
8
9
"plugins": [
["import", { "libraryName": "ant-design-vue", "libraryDirectory": "es", "style": true }] // `style: true` 会加载 less 文件
]

import {Button} from 'ant-design-vue';



npm i --save-dev babel-plugin-import

路由

同时刷新URL路径的插件

1
npm i nprogress

有两种加载方式,推荐这种:

1
component: { render: h => h("router-view") },

App.vue

1
2
3
4
5
6
7
8
9
10
11
<template>
<div id="app">
<div id="nav">
<router-link to="/dashboard/analysis">dashboard</router-link> |
<router-link to="/form">form</router-link>
</div>
<router-view />
</div>
</template>

<style lang="less"></style>

BasicLayout.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<div>
<Header />
<SiderMenu />
<router-view></router-view>
<Footer />
</div>
</template>

<script>
import Header from "./Header";
import Footer from "./Footer";
import SiderMenu from "./SiderMenu";
export default {
components: {
Header,
Footer,
SiderMenu
}
};
</script>

<style></style>

布局

Layout 布局

菜单和路由的结合

layouts文件夹是菜单的布局

router.js

  • 有name的才会渲染到菜单
  • 不渲染到菜单,可以添加标志位hideInMenu: true,
  • 如果不想让菜单的子路由渲染出来,可以添加标志位:hideChildrenMenu: true,

如何使用echarts第三方库

  • npm i XX
  • 在component文件夹中进行封装
  • 第三方库resize-detector的作用是监听空间大小变化
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
<template>
<div ref="chartDom"></div>
</template>

<script>
import echarts from "echarts";
import debounce from "lodash/debounce";
import { addListener, removeListener } from "resize-detector";
export default {
props: {
option: {
type: Object,
default: () => {}
}
},
watch: {
option(val) {
this.chart.setOption(val);
}
// option: {
// handler(val) {
// this.chart.setOption(val);
// },
// deep: true
// }
},
created() {
this.resize = debounce(this.resize, 300);
},
mounted() {
this.renderChart();
addListener(this.$refs.chartDom, this.resize);
},
beforeDestroy() {
removeListener(this.$refs.chartDom, this.resize);
this.chart.dispose();
this.chart = null;
},
methods: {
resize() {
console.log("resize");
this.chart.resize();
},
renderChart() {
// 基于准备好的dom,初始化echarts实例
this.chart = echarts.init(this.$refs.chartDom);
this.chart.setOption(this.option);
}
}
};
</script>

<style></style>

mock的使用

1.在mock文件夹创建对应的js文件
2.在vue.config.js配置对应的代理
3.axios像正常那样请求数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
devServer: {
proxy: {
"/api": {
target: "http://localhost:3000",
bypass: function(req, res) {
if (req.headers.accept.indexOf("html") !== -1) {
console.log("Skipping proxy for browser request.");
return "/index.html";
} else if (process.env.MOCK !== "none") {
const name = req.path
.split("/api/")[1]
.split("/")
.join("_");
const mock = require(`./mock/${name}`);
const result = mock(req.method);
delete require.cache[require.resolve(`./mock/${name}`)];
return res.send(result);
}
}
}
}

axios的使用

  • 切换环境的时候需要执行npm run serve:no-mock才会请求真实服务器
  • 一般都会对axios进行封装

表单的使用

表单的自动校验

分步表单(Vuex、Vue-router)

  • store-module-form.js

自己封装一个表单

components-ReceiverAccount.vue