梦入琼楼寒有月,行过石树冻无烟

📖 earlier posts 📖

Vue.js V-bind at class and style 绑定

在 Vue.js 中,v-bind 指令提供了 :class/style 参数进行处理,并对此针对性的进行了增强表达式类型除了字符串之外,还可以是对象或是数组。

Class 绑定

通常我们可以使用 :class 对象以动态的切换 class,但这个 class 的使用与否取决与属性 isActive 的 Truthy

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
<style>
.active {
padding: 1.5em;
border: 1px solid #e4e4e4;
}
.err {
border: 1px solid red;
color: red;
padding: 1.5em;
}
</style>
<div id="app">
<p v-bind:class="{active: isActive, err: isError}">{{message}}</p>
</div>
<script>
const app = Vue.createApp ({
data() {
return {
message: 'This is v-bind:class',
isActive: true,
isError: false
}
}
})
const vm = app.mount('#app')
</script>

Truthy(真值),在 JavaScript 的定义中,值得是布尔值的上下文转换后的值为 true,除下述意外皆为真值。也就是说当我们在 :class 中使用的是 :class="{active: true}" 才会使用该样式。

name
false
0
“”
null
undefined
Nan

对于上下文 (context) 是一个任务中不不可少的一组数据,当数据任务中断时,这次之后仍然可以在同一个位置继续执行。

任务可以是进程或线程

数组

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
<style>
.active {
padding: 1.5em;
border: 1px solid #e4e4e4;
}
.err {
border: 1px solid red;
color: red;
padding: 1.5em;
}
</style>
<div id="app">
<p v-bind:class="[headerActive, paddingError]">{{message}}</p>
</div>
<script>
const app = Vue.createApp ({
data() {
return {
message: 'This is v-bind:class',
headerActive: 'active',
paddingError: false
}
}
})
const vm = app.mount('#app')
</script>

三元表达式

在下述的例子中,假设 isActive 的 Truthy 为 false,则直接使用 paddingError 作为样式,反之使用 headerActive 作为其 Class,这提供了两种写法:

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
<style>
.active {
padding: 1.5em;
border: 1px solid #e4e4e4;
}
.err {
border: 1px solid red;
color: red;
padding: 1.5em;
}
</style>
<div id="app">
<p v-bind:class="[isActive ? headerActive : '', paddingError]">{{message}}</p>
<!-- <p v-bind:class="[{headerActive: isActive},paddingError]">{{message}}</p>-->
</div>
<script>
const app = Vue.createApp ({
data() {
return {
message: 'This is v-bind:class',
headerActive: 'active',
paddingError: 'err',
isActive: true
}
}
})
const vm = app.mount('#app')
</script>

Style 对象语法

v-bind 的 style 语法非常的类似于 class ,需要注意的是 CSS 属性名可以使用驼峰式(camelCase)或短横线分割(kebab-case)的进行命名:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div id="app">
<p v-bind:style="{border: borderBold, padding: '1.5em'}">{{message}}</p>
</div>
<script>
const app = Vue.createApp ({
data() {
return {
message: 'This is v-bind:style',
borderBold: '1px solid #e4e4e4'
}
}
})
const vm = app.mount('#app')
</script>

数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id="app">
<p v-bind:style="[borderStyle, paddingStyle]">{{message}}</p>
</div>
<script>
const app = Vue.createApp ({
data() {
return {
message: 'This is v-bind:style',
borderStyle: 'border:1px solid #e4e4e4',
paddingStyle: 'padding:1em'
}
}
})
const vm = app.mount('#app')
</script>

Vue.js computed at watch

计算属性 (computed)

getter

Vue 提供了 computed 和 watch 属性,分别实现了计算属性和侦听器,其中计算属性为模板提供了一种家但便捷的表达式虽然设计他们的初衷是用于实现简单的运算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div id="app">
<p>{{projectMessage}}</p>
</div>
<script>
const app = Vue.createApp ({
data() {
return {
project : {
name: [
'Linux system',
'Git'
]
}
}
},
computed: {
projectMessage() {
return this.project.name.length > 0 ? 'Welcome you to join' : 'Sorry you can’t enter the community, please participate or create a project'
}
}
})
const vm = app.mount('#app')
</script>

如上述的例子中,主要声明了一个 computed 属性 projectMessage,假设 project >0 即可加入社区,而 < 0 的 User 将会被拒绝。

projectMessage 所依赖的是 data()project 数组的值,当 vm.project.name 发生更改时 projectMessage 也会对应的进行改变。

setter

setter 主要的作用就是当改变 projectMessge 时来重新进行调整,也就是你可以在该函数下写一些你想重新改变的东西,比如添加或者关联等:

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">
<p>{{projectMessage}}</p>
</div>
<script>
const app = Vue.createApp ({
data() {
return {
project : 'Vue',
author: 'Evan You'
}
},
computed: {
projectMessage: {
get() {
return this.project + ' ' + this.author
},
set(newValue) {
alert("当前名称是:" + newValue);
const names = newValue.split(' ')
this.project = names[0]
this.author = names + 'Vue!'
}
}
}
})
const vm = app.mount('#app')
</script>

侦听器 (watch)

侦听器主要通过 watch选项来提供一个通用的方法来进行响应数据的变化,当数据变化或开销较大的时候这种方法最为有用。

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
<div id="app">
<input type="text" v-model="id">
<p>{{message}}</p>
</div>
<script>
const app = Vue.createApp ({
data() {
return {
id: '0',
message: 'Your id'
}
},
watch: {
id(newId) {
if (newId >= 0) {
this.getId()
}
}
},
methods: {
getId() {
this.message = 'Thinking...'
if (this.id <= 712)
this.message = 'true'
else
this.message = 'false'
}
}
})
const vm = app.mount('#app')
</script>

Vue.js createApp and RootComponent at DataProperty

RootComponent

在 Vue 3.0 中,每个应用的创建都是通过 createApp 函数进行创建一个新的应用:

1
2
3
const app = Vue.createApp({
// code
})

上述这种方法可以更加明显的表示出 createApp 函数的作用,但 Vue3.0 中还有另一种方法更加遵循 MVVM 模型。

尽管 Vue 的设计没有完全遵循,但设计收到了他的启发,因此在文档中也会使用 vm 来作为 ViewModel 的缩写来表示组件实例。

在大多数真实应用中,这些语法都是层层嵌套的,为此为了一个可重用的组件树,还提供了 根组件 的写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="app">
{{message.hey}}
</div>
<script>
const RootComponent = {
data() {
return {
message: {
hey: 'hey,world!'
}
}
}
}
const app = Vue.createApp(RootComponent)
const vm = app.mount('#app')
</script>

MVVM 架构的核心是通过数据进行驱动,即 ViewModel,通过 ViewModel 将 View 和 Model 进行关联映射,更加通俗的说法 ViewModel 就是一个负责转换 Model 中的数据对象,使得数据对象更加便与管理和使用,也就是双向绑定。

当用户操作 View 时,ViewModel 就会监听到改变后通知 Model 发生了改变,如果 Model 改变则 View 也会改变。

生命周期

当使用 createApp 时,会初始化事件和生命周期,之后创建将模板渲染函数并将 app.$el 添加到 el 安装,当数据发生变化时重新渲染并更新,最后当 app.unmount() 被调用时将会卸载该应用。

在初始化事件和生命周期之前还会初始化注射(ingections)以及响应式(reactivify)

Data Property

组件中的 data 函数在 Vue 创建新的实例的过程中将会被调用,他返回的是一个对象,之后 Vue 会通过相应性系统将其包裹,并以 $data 的形式存储在组件实例中,该函数的任何属性也会直接通过他来暴露出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="app">
{{message}}
</div>
<script>
const app = Vue.createApp ({
data() {
return {
message: 'hey,world!',
}
}
})
const vm = app.mount('#app')
console.log(vm.message) // => hey,world!
vm.message = 'hey,message!'
console.log(vm.$data.message) // => hey,message!
</script>

这些属性在实例创建时进行添加,所以需要保证他们在 data 返回的对象中,他还支持属性使用 null 以及 undefined 或其他站位值。

Vue 使用的是 $ 前缀来通过组件实例所暴露自己的内置 API,对于内部还提供了 _ 前缀。

直接将不包含 data 中的属性添加到实例中是可行的,但由于睡醒那个不再背后的响应式,因此 Vue 相应性系统不会自动跟踪,也就是说他无法自动更新。

methods

methods 选项向组件实例添加方法,主要用于包含所需方法对象,Vue 自动将 methods 绑定为 this,以便于他始终指向组件实例。

需要注意的是在定义 methods 时应避免使用箭头函数,这回阻止 Vue 绑定恰当的 this 指向。

通常 this 指向该实例的本身

在模板指令中通常 方法(methods) 会和其他的属性在组件模板中被访问,这通常是事件监听,如 v-on 指令 (他有很多的 methods)

Lodash

Lodash 是遵循 MIT 开源协议所发布的,一致性、模块化、高性能的 JavaScript 实用工具库。

本文主要演示下 _.debounce(func,[wait=0],[options={}]) 函数以及 Vue Data Property 的 “相应性” 的使用。

_.debounce 函数主要的作用就是延迟 functionwait 自上次调用 debounced 函数以来经过多少毫秒后。

可参考下 Lodash doc: https://www.lodashjs.com/docs/lodash.debounce

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div id="app">
<button @click="toclick">up go</button>
<p>{{message}}</p>
</div>
<script src="https://unpkg.com/lodash@4.17.20/lodash.min.js"></script>
<script>
const app = Vue.createApp ({
data() {
return {
toclick: _.debounce(function () {
vm.message = 'hey,lodash'
}, 1000),
message: 'hey,world!'
}
}
})
const vm = app.mount('#app')
</script>

如上述的例子中主要通过 lodash 所提供的 _.debounce 函数延迟 1ms 来改变 message 属性的值。当数据发生变化时 DOM 将会重新渲染并更新。

Vue.js 模板语法

Vue.js主要使用了基于HTML的模板语法,允许开发者声明式的将DOM绑定至底层的Vue实例数据,由于Vue.js基于HTML模板语法,所以所编写的自然而然能被浏览器所解析(除了远古浏览器)。

插值

插值(interpolation) 在数学领域中表示了一个有限的数值函数其中自变量的值,而在 Vue 中可以表示在元素中所使用的自变量,根据这些子变量可以通过双向绑定来实现其最终效果。

Mustache


Mustache(小胡子) 是一个模板系统(template system),支持了如 C++、Java、JavaScript、PHP、Python 等众多主流语言。

Available in Ruby, JavaScript, Python, Erlang, Elixir, PHP, Perl, Raku, Objective-C, Java, C#/.NET, Android, C++, CFEngine, Go, Lua, ooc, ActionScript, ColdFusion, Scala, Clojure[Script], Clojure, Fantom, CoffeeScript, D, Haskell, XQuery, ASP, Io, Dart, Haxe, Delphi, Racket, Rust, OCaml, Swift, Bash, Julia, R, Crystal, Common Lisp, Nim, Pharo, Tcl, C, ABAP, Elm, Kotlin, and for SQL

Works great with TextMate, Vim, Emacs, Coda, and Atom

他也被描述为 “无逻辑” 系统,因为缺少任何明确的控制流语句,这主要取决与他是一个大量使用的符号 “**{}**” ,类似与一个横向的小胡子因此而得名。

Vue 为此支持了这种方法,Mustache 标签将会替代对应组件中的属性值,但无论如何绑定组件上的属性发生了改变,所以插值处还是会被更新。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index</title>
<script src="https://unpkg.com/vue@next"></script></head>
<body>
<div id="app">
<span>{{message}}</span>
</div>
<script>
var App = {
data() {
return {
message: 'hey,world!'
}
}
}
Vue.createApp(App).mount("#app")
</script>
</body>
</html>

v-once

为此、Vue 还提供了 v-once 指令,该指令类似于 Object.freeze() 阻止修改的方法,但主要作用就是只渲染元素和组件一次,随后元素/组件及其子节点将会被视为静态内容并跳过:

Vue 渐进式主要体现在其文档中,通常 Vue 文档写一个实例会出现你认知以外的方法,因此你需要阅读之后的文档在折回来学习目前的实例才可理解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div id="app">
<p v-once>{{message}}</p>
<input type="button" value="upgrate message" v-on:click="message ='hey,v-on'"/>
</div>
<script>
var App = {
data() {
return {
message: 'hey,world!'
}
}
}
Vue.createApp(App).mount("#app")
</script>

上述的例子中,主要通过使用 v-once 来阻止 v-on 指令来进行修改,从而整个 v-on 事件失败。

v-html


需要注意的是,Mustache 将会被 Vue 解析为普通的文本,并不是正常的 HTML 代码,因此如果需要输出 HTML 则需要使用 v-html 指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div id="app">
<p>{{message}}</p>
<p v-html="message">{{message}}</p>
</div>
<script>
var App = {
data() {
return {
message: '<h1>Hello,world!</h1>'
}
}
}
Vue.createApp(App).mount("#app")
</script>

JavaScript 表达式

需要注意的是虽然在 Vue 模板中使绑定的一直都是属性插值,但实际上对于所有的数据绑定(Data Bigings) 都完全的支持了 JavaScript 表达式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id="app">
<p>{{'This is ' + message}}</p>
<p>{{gender ? '我是女孩!' : '我不是女孩'}}</p>
</div>
<script>
var App = {
data() {
return {
message: 'Hello,world!',
gender: false
}
}
}
Vue.createApp(App).mount("#app")
</script>

但 在 Mustache 的限制下每个绑定的都只能包含单个表达式,所以向类似与 Lareavel 那种 Mustache 是不会生效的,如:

1
{{ if (ok) { return message } }}

Attribute


属性(Attribute) 语法是一个在元素层级之下,通常被指令包含在其中的都会被称之为 Attribute。

v-bind


v-bind 是一个富有很多 Attribute 的指令之一,他最为常用的就是 src\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
<style type="text/css">
.logo-size {
width: 3em;
}
.logo-layout {
border: 1px solid #e3e3e3;
}
</style>
<div id="app">
<p>{{message}}</p>
<img v-bind:src="logo" :class="[logo_size,logo_layout]"/>
<img :src="title" :style="{ width: title_size + 'em' }"/>
<a :href="home">to Jiangxue Team Home</a>
</div>
<script>
var App = {
data() {
return {
message: 'Hello,world!',
logo: 'logo: 'https://gitee.com/analysis-of-river-snow/drawing-bed/raw/master/20210918011632.png',
title: 'https://gitee.com/analysis-of-river-snow/drawing-bed/raw/master/20210918002526.png',
logo_size: 'logo-size',
logo_layout: 'logo-layout',
title_size: '5'
home: 'https://www.jiangxue.team/'
}
}
}
Vue.createApp(App).mount("#app")
</script>

需要注意的是 DOM 模板 v-bind property 支持陀峰命名

当元素上是指一个绑定的时候 Vue 将会默认通过 in 操作检测元素是否有一个被定义的属性的 key,如果属性被定义了则个值也就被设置为一个 DOM Propetry 而不是单纯的 Attribute。

指令

指令(Directives) 指的是 Vue 中带有 v- 前缀的特殊 Attribute,指令是预期的单个 JavaScript 表达式。主要的作用是当表达式的值发生改变的时候,同样会响应式的作用与 DOM 。

通常指令的 attribute 预期值是单个的 JavaScript 表达式,但 v-forv-on 是特殊的情况

v-if

指令的 DOM 传递可直接通过 v-if 来根据表达式 Attribute 的值来决定是否出现这个元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id="app">
<p>{{'This is ' + message}}</p>
<p v-if="gender">This is true</p>
</div>
<script>
var App = {
data() {
return {
message: 'Hello,world!',
gender: true
}
}
}
Vue.createApp(App).mount("#app")
</script>

参数

参数通常在指令的冒号之后表示(:),在 Vue 中最为常用到参数的指令就是 v-bind ,如下述 code 中的 :href 就是一个参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id="app">
<p>{{'This is ' + message}}</p>
<a :href="home">to Jiangxue Team Home</a>
</div>
<script>
var App = {
data() {
return {
message: 'Hello,world!',
home: 'https://www.jiangxue.team/'
}
}
}
Vue.createApp(App).mount("#app")
</script>

修饰符

修饰符(Access modifiers) 是一个面向对象语言中的关键字,用于设置类或方法和其他成员的可访问性,在 Vue 中主要使用到修饰符的指令是 v-on

v-on

v-on 的缩写为 @,其参数为 event即监听对应的事件是否被触发,这主要用法就是绑定事件监听器,事件的类型都由修饰符进行指定。

ID DA FA
click.stop 调用 event.stopPropagation() 阻止捕获和冒泡阶段中当前事件的进一步传播
click.prevent 调用 event.preventDefault() 阻止默认的点击事件执行
.capture 添加事件侦听器时使用capture(捕获) 模式
.self 只当事件从侦听器绑定元素本身触发时才进行回调
```.{keyCode keyAlias}``` 只当事件从特定键触发时才进行回调
.once 只触发一次回调
.left 只当点击鼠标左键时触发
.right 只当鼠标点击右键时触发
.middle 当鼠标中键时触发
.passive 以 { passive: true } 模式添加侦听器

我们主要以 prevent 为例,监听其 submit 元素是否被触发,并组织默认的单击事件执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="app">
<form v-on:click.prevent="doThis">
<a :href="home">{{message}}</a>
</form>
</div>
<script>
var App = {
data() {
return {
message: 'Go Jiangxue Team',
home: 'https://www.jiangxue.team/'
}
}
}
Vue.createApp(App).mount("#app")
</script>

或者通过 v-on 的简写 @ 来实现没有表达式的阻止默认行为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="app">
<form @click.prevent>
<a :href="home">{{message}}</a>
</form>
</div>
<script>
var App = {
data() {
return {
message: 'Go Jiangxue Team',
home: 'https://www.jiangxue.team/'
}
}
}
Vue.createApp(App).mount("#app")
</script>

可参考下 Vue 官方文档中所提供的实例:

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
<!-- 方法处理器 -->
<button v-on:click="doThis"></button>

<!-- 动态事件 -->
<button v-on:[event]="doThis"></button>

<!-- 内联语句 -->
<button v-on:click="doThat('hello', $event)"></button>

<!-- 缩写 -->
<button @click="doThis"></button>

<!-- 动态事件缩写 -->
<button @[event]="doThis"></button>

<!-- 停止冒泡 -->
<button @click.stop="doThis"></button>

<!-- 阻止默认行为 -->
<form @submit.prevent></form> / <form @click.prevent></form>

<!-- 阻止默认行为,没有表达式 -->
<form @click.prevent></form>

<!-- 串联修饰符 -->
<button @click.stop.prevent="doThis"></button>

<!-- 键修饰符,键别名 -->
<input @keyup.enter="onEnter" />

<!-- 点击回调只会触发一次 -->
<button v-on:click.once="doThis"></button>

<!-- 对象语法 -->
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>

Vue.js component

组件(component) 是 Vue.js 中最为核心的功能,在前端应用程序中可以采用模块化以及实现可重用、可扩展的 Vue 实例,通常使用 template 作为方法进行注册组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div id="app">
<button-counter></button-counter>
</div>
<script>
const app = Vue.createApp({})
app.component('button-counter', {
data() {
return {
count: 0
}
},
template: `
<button @click="count++">
This is click {{count}}
</button>
`
}).mount('#app')
</script>

当然我们也可以重复使用各个独立的 <button-counter> 组件,但前提我们的组件需要高度解藕。

点击按钮的时候,上述的 flag 将会被证实,就因为每个组件都会各自独立并维护他的 count,每次使用 <button-conters> 都会有一个新的实例会被新建。

1
2
3
4
5
<div id="app">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>


或者可以选择组件使用同一个 count 来进行同时的数据绑定与渲染:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div id="app">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
<script>
const app = Vue.createApp({})
var buttonCounter = {
count: 0
}
app.component('button-counter', {
data() {
return buttonCounter
},
template: `
<button @click="count++">
This is click {{count}}
</button>
`
}).mount('#app')
</script>

Prop

Prop 也被直译为 “支柱”,通常他可以为组建提供一个属性用于传递数据并建立链接,这也方便了数据的获取和各个组件之间的解藕:

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
<style>
.header {
border: 1px solid #eaeaea;
padding: 2em;
width: 40%;
}
.header_item {
border-left: 10px solid #1488ff;
padding-left: 14px;
}
.header_txt {
text-indent: 1em;
}
</style>
<div id="app">
<item-main title="Hey,world" text="当然我们也可以重复使用各个独立的 <button-counter> 组件,但前提我们的组件需要高度解藕。点击按钮的时候,上述的 flag 将会被证实,就因为每个组件都会各自独立并维护他的 count,每次使用 <button-conters> 都会有一个新的实例会被新建。"></item-main>
</div>
<script>
const app = Vue.createApp({})
app.component('item-main', {
data() {
return {
}
},
props: ['title', 'text'],

template: `
<div class="header">
<h1 class="header_item">{{title}}</h1>
<p class="header_txt">{{text}}</p>
</div>
`
}).mount('#app')
</script>

为了使用的方便我们还可以直接使用 Prop 数组来提供数据的绑定和输出,这会更加的方便和简洁

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
<style>
.header {
border: 1px solid #eaeaea;
padding: 2em;
width: 40%;
}
.header_item {
border-left: 10px solid #1488ff;
padding-left: 14px;
}
.header_txt {
text-indent: 1em;
}
</style>
<div id="app">
<item-main
v-for="post in posts"
:key="post.id"
:title="post.title"
:txt="post.txt"
></item-main>
</div>
<script>
const App = {
data() {
return {
posts: [
{ id: 1, title: 'Vue', txt: '一个优秀的渐进式框架'},
{ id: 2, title: 'Jquery', txt: 'JavaScript 组件库,集成了多种实用方法'},
{ id: 3, title: 'D3', txt: '专业的数据可视化框架'}
]
}
}
}
const app = Vue.createApp(App)

app.component('item-main', {
props: ['title', 'txt'],
template: `
<div class="header">
<h1 class="header_item">{{title}}</h1>
<p class="header_txt">{{txt}}</p>
</div>
`
})

app.mount('#app')
</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
54
55
56
57
58
59
60
61
62
63
64
<style>
.header {
border: 1px solid #eaeaea;
padding: 2em;
width: 40%;
}
.header_item {
border-left: 10px solid #1488ff;
padding-left: 14px;
}
.header_txt {
text-indent: 1em;
}
.txt_button {
float: left;
display: -webkit-inline-box;
}
</style>
<div id="app">
<item-main title="Hey,world" text="当然我们也可以重复使用各个独立的 <button-counter> 组件,但前提我们的组件需要高度解藕。点击按钮的时候,上述的 flag 将会被证实,就因为每个组件都会各自独立并维护他的 count,每次使用 <button-conters> 都会有一个新的实例会被新建。"></item-main>
<div class="txt_button">
<button-addcounter></button-addcounter>
<button-redcounter></button-redcounter>
</div>
</div>
<script>
const app = Vue.createApp({})
var txtSize = {
size: '15'
}
app.component('item-main', {
data() {
return txtSize
},
props: ['title', 'text'],
template: `
<div class="header">
<h1 class="header_item">{{title}}</h1>
<p class="header_txt" :style="{fontSize: size+'px'}">{{text}}</p>
</div>
`
})
app.component('button-redcounter', {
data() {
return txtSize;
},
template: `
<button @click="size--">
Reduce to fontSize - 1
</button>
`
})
app.component('button-addcounter', {
data() {
return txtSize;
},
template: `
<button @click="size++">
Add to fontSize + 1
</button>
`
})
app.mount('#app')
</script>

插槽


组件通常通过元素进行使用,并以属性作为内容,而元素本身的内容将会被覆盖,因此 Vue 提供了 <slot> 元素来解决这一问题,此外 slot 也可被理解为组件之间的一种传递方式或占位符。

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
<style>
.header {
border: 1px solid #eaeaea;
padding: 2em;
width: 40%;
}
.header_item {
border-left: 10px solid #1488ff;
padding-left: 14px;
}
.header_txt {
text-indent: 1em;
}
</style>
<div id="app">
<item-main>和 HTML 元素一样,我们经常需要向一个组件传递内容</item-main>
</div>
<script>
const app = Vue.createApp({})
app.component('item-main', {
data() {
return {
title: 'This is <slot>'
}
},
template: `
<div class="header">
<h1 class="header_item">{{title}}</h1>
<slot></slot>
</div>
`
}).mount('#app')
</script>

动态组件

在通常情况下,不同组件之间的动态切换可以让页面变得更加的拥有趣味,且可以减少一些不必要的从页面占用。动态组建可以用于构建一些非动态的填写,而如果需要满足一些OAuth等切换或登录的场景,则需要使用 Vue 动态组件中的异步组件。

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
<div id="app">
<button
v-for="nav in navs"
:key="nav"
@click="currentTab = nav">{{nav}}</button>
<component :is="currentTabDev"></component>
</div>
<script>
const app = Vue.createApp({
data() {
return {
currentTab: 'Home',
navs: ['home', 'arch', 'about']
}
},
computed: {
currentTabDev() {
return 'nav-' + this.currentTab.toLowerCase()
}
}
})

app.component('nav-home', {
template: `
<div>Home</div>
`
})
app.component('nav-arch', {
template: `
<div>Arch</div>
`
})
app.component('nav-about', {
template: `
<div>About</div>
`
})
app.mount('#app')
</script>

上述 code 中,currentTabDev 可以是一个组件的名字或一个选项对象,通过 :is 属性来作为一个自定义的内置元素进行转译。

很多时候我们所使用的 :key 只是用于进行排序,有相同父元素的子元素必须有唯一的 key。重复的 key 会造成渲染错误,因此需要使用属性

也就是说当 currentTabDev 发生改变的时候,组件也就会发生变化,这是 Vue 3.x 中所新增的特殊属性。

keep-alive

Vue创建一个新的 ```currentTabDev``` 实例,也就是说只要通过使用``````元素进行包裹,即可让他们在第一次创建时会被进行**缓存**,即保存刚刚切换后的数据,为了更加的体现```< keep-alive>```效果,我们可以使用表单来进行验证:
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

![未使用 keep-alive](https://49812933408852955071488026628034-1301075051.cos.ap-nanjing.myqcloud.com/20210924014746.gif)


![使用 keep-alive](https://49812933408852955071488026628034-1301075051.cos.ap-nanjing.myqcloud.com/20210924014955.gif)


```js
<div id="app">
<button
v-for="nav in navs"
:key="nav"
@click="currentTab = nav">{{nav}}</button>
<keep-alive>
<component :is="currentTabDev"></component>
</keep-alive>
</div>
<script>
const app = Vue.createApp({
data() {
return {
currentTab: 'Home',
navs: ['home', 'arch', 'about']
}
},
computed: {
currentTabDev() {
return 'nav-' + this.currentTab.toLowerCase()
}
}
})

app.component('nav-home', {
template: `
<div><input type="text" /></div>
`
})
app.component('nav-arch', {
template: `
<div>Arch</div>
`
})
app.component('nav-about', {
template: `
<div>About</div>
`
})
app.mount('#app')
</script>

Vue.js MVVM at ViewModel

Vue(/vju:/,类似与 view),是由尤雨溪所开发的一种前端js框架,以vue为中心的vue 库最为出名,其中知名的有vue-data、vue-table……分别运用在数据大屏以及表格处理方面,其中vue主要用于构建用户界面的渐进式框架,核心库只支持前端显示。

而渐进式框架主要是:“一开始不需要完全掌握全部功能,到后面会慢慢的提升”,就以vue-data为例,即使光通过官方文档也可以快速上手,这与vue.js的成功密切相关。

Vue 的部分设计理念遵守了 MVVM(Model-view-viewmodel) 软件架构模式,用于构建用户界面的渐进式框架,是被一个设计自低向上逐层应用。

通过 MVVM 有助于将图形用户界面的开发和业务逻辑的开发分离开来,因此 Vue 也是一个用于构建用户界面的渐进式框架

虽然并没有完全遵守 MVVM 模型,但 Vue 在设计上收到了启发,因此对于 vm,ViewModel 通常用于表示组件的实例。

vue.js 的官方文档是写的最为详细的框架文档之一,这与其vue.js发展之出的Laravel 社区文档出现了对比,如果按照通俗的话来讲就是“教你造车但不教你开车”,这也是vue.js如此成功的关键因素之一,其二就是由于vue.js是由国人所开发的,这也增加了vue.js在国内的受欢迎程度不同类框架第一影响更好。

对于学习我们可以直接使用最新的 Vue CDN 进行引入:<script src="https://unpkg.com/vue@next"></script>

MVVM

MVVM(Model-view-viewmodel)即表示 **view(视图)\viewModel(视图模型)\Model(模型)**,是一种软件的架构模式,其中 View 的主要作用就是向用户显示数据上下文中可用的数据,并与用户进行数据交互。

而之后的 ViewModel 是用于作为视图(VIew)和模型(Model)之间的桥梁,在 MVC 的软家架构模式中可以表示为 Controller 控制器,,MVVM 模式试图通过 MVC 所提供的数据绑定的优势通过框架尽可能的接近于应用程序模型,他使用绑定器以及视图模型。

MVVM 不同于 MVC,在MVVM 模式中将 ViewModel 层绑定到 View 曾后基本上不会使用点击事件,而是使用命令(Command)来控制,并通过 Binding 来绑定相关数据。

对于 Model 是指所需数据的对象建模,这通常用于数据库建模并直接进行映射到数据中,类似与 Laravel 以及 Sequelize 中的 Model

ViewModel

ViewModel 是 Vue 的核心框架也是一个 Vue 实例,作用与某一个 HTML 元素之上,通常为指定的 ID 元素。当创建了一个 ViewModel 后,最为核心的就是双向绑定,主要以 DOM Listeners(DOM 侦听器)和 Data Bindings(数据绑定) 为主。DOM Listeners 会检测 View 上的元素变化,如有变化则会更改 Model 中的数据; 当更改 Model 数据时, DataBingdings 则会更新页面中的 DOM 元素。因此最后的 app 元素下所绑定的 {{message}} 则会返回 hey,world 来将此渲染进 DOM 系统。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index</title>
<script src="https://unpkg.com/vue@next"></script></head>
<body>
<div id="app">
{{message}}
</div>
<script>
var App = {
data() {
return {
message: 'hey,world!'
}
}
}
Vue.createApp(App).mount("#app")
</script>
</body>
</html>

在 Vue3.0 版本中,Vue 应用都是通过 createApp 函数创建一个新的应用实例,如:

1
2
3
4
5
6
7
8
var App = {
data() {
return {
message: 'hey,world!'
}
}
}
Vue.createApp(App).mount("#app")

但上述这种只是一种非常简单的写法,还有另一种写法是类似与更加符合 MVVM 模型的,Vue 的设计收到此启发但在文档中也会使用 vm(ViewModel) 进行缩写。

1
2
3
4
5
const RootComponent = { 
/* 选项 */
}
const app = Vue.createApp(RootComponent)
const vm = app.mount('#app')

Vue-cli

Vue CLI 是一个由vue.js官方所发布的一个快速构建 vue 项目的脚手架,通过使用 vue-cli可以构建一个标准的 vue 项目。

安装

npm

安装 Vue 我们主要通过脚手架来进行,vue 为此提供了两种可需方案,分别为nmp install -g vue-cli 或使用cnpm install -g vue-cli来进行安装,我们可以配置下全局安装 cnpm

cnpm

1
npm install cnpm -g --registry=https://registry.npm.taobao.org

当安装完成之后可以使用 vue --version来查看是否安装成功,如安装成功之后则进行下一步。

构建

在vue-cli中,分别有两种方式,可使用vue create project-name来构建一个 vue-cli项目,也可以通过使用 vue ui及 web gui 的形式来进行构建。

运行

当构建完成一个 vue 项目之后,我们可以通过npm脚手架来进行运行服务也可以通过使用 vue 进行,两者之间的区别就是通过使用 npm run serve脚手架运行不需要安装@vue/cli-service-global依赖,而 通过使用 vue serve来运行会经过漫长的等待来安装依赖,但等待并不是没有用的,vue 的用户体验是很棒的。

目录

ID DA FA
build 项目构建时的相关代码 项目构建
build.js 生产环境构建代码
check-version.js 用于检查 node,npm 版本
utils.js 构建工具
vue-loader.conf.js loader 配置
webpack.base.conf.js webpack 基础配置
webpack.dev.conf.js webpack 开发环境配置,用于构建本地服务器
webpack.prod.conf.js webpack 生产环境配置
config 项目开发环境配置 项目配置
dev.env.js 开发环境变量
index.js 项目配置变量
prod.env.js 生产环境变量
src 源码目录 工作空间
components 公共组件
router 路由管理
App.vue 页面入口文件
main.js 程序入口文件
static 静态文件资源存放
.babelrc ES6 语法编译配置
.editorconfig 定义代码格式
.gitignore git 上传需要忽略的文件格式
.postcssrc postcss 配置文件
README.md 说明文件
index.html 入口文件
package.json 项目基本信息 ,包依赖信息……

Vue 路由

vue 中的路由并不同等与上述章节内容,如需要在Vue中使用路由需要引入或通过脚手架来安装 vue.js 所提供的官方CDN或是 vue-router,同样的本文依然使用 CDN 的方式进行演示:

https://unpkg.com/vue-router@2.0.0/dist/vue-router.js

Vue 组件基础

创建组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="button-counts">
<button-conters></button-conters>
</div>
<script>
Vue.component('button-conters', {
data: function() {
return {
count: 0
}
},
template: '<button v-on:click="count += 1">增加 count {{ count }}</button>'
})
new Vue({
el: '#button-counts'
})
</script>

当创建完成组建之后,我们可以无限次的进行引用,而组件中通常会带有一个名字,就比如上述创建的组件名称就是<button-conters>并根据创建一个创建Vue根实例化将组件作为自定义元素进行使用

1
2
3
new Vue({
el: '#button-counts'
})


之后直接在HTML中直接引入刚刚创建的组件即可作为HTML元素进行使用如:

1
2
3
<div id="button-counts">
<button-conters></button-conters>
</div>

复用组件


当组件创建完成之后我们可以在上述的基础上进行多次复用且独立,如:

1
2
3
4
5
6
<div id="button-counts">
<button-conters></button-conters>
<button-conters></button-conters>
<button-conters></button-conters>
<button-conters></button-conters>
</div>

当点击按钮的时候,上述的flag将会被证实,就因为每个组件都会各自独立并维护他的count,每次使用<button-conters>都会有一个新的实例会被新建。

同步


当以往常一样通过data直接定义且不通过data 函数则可返回对象的独立访问,如:

1
2
3
4
5
6
7
8
9
10
11
12
var buttonRepeat =  {
count: 0
}
Vue.component('button-conters', {
data: function() {
return buttonRepeat
},
template: '<button v-on:click="count += 1">增加 count {{ count }}</button>'
})
new Vue({
el: '#button-counts'
})


由于每个count是共享的所以当点击一个组件的同时,所有复用的组件都会被增加,所以正确的方式是通过使用独立函数进行编写组件,来不影响组件之间的复用:

1
2
3
4
5
6
7
8
9
10
11
Vue.component('button-conters', {
data: function() {
return {
count: 0
}
},
template: '<button v-on:click="count += 1">增加 count {{ count }}</button>'
})
new Vue({
el: '#button-counts'
})

组件的组织


通常一个应用会一一棵嵌套的组建树形式来组织,在vue当中,组件的注册需要让vue能够进行识别,所以注册主键一共分为两种类型,分别为全局注册以及局部注册,vue中所有的组件都是通过Vue.component来进行注册。

而局部注册主要通过使用var ComponentOne {}来进行注册,全局注册主要通过使用Vue.component('组件名', {})来进行注册,这里两者的区别就是命名方式的不同

全局注册组件格式

1
2
3
Vue.component('组件名称' {
// ……
})

局部注册

1
2
3
var ComponentOne = {
// ……
}

Prop 向子组件传递数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

![](https://49812933408852955071488026628034-1301075051.cos.ap-nanjing.myqcloud.com/1615400560_20210310232123160_1093282140.png)
在官方文档中,Prop向子组件传递数据主要用于**向这个组件传递一个标题或者内容之类的展示数据**,Prop是你可以在组建上注册一些自定义的**属性**,当一个**值传递给一个 prop 属性**。

```vue
<div id="data-title">
<data-title title="Hello,world"></data-title>
<data-title title="Hello,Vue"></data-title>
</div>
<script>
Vue.component('data-title', {
props: ['title'],
template: '<h1>{{title}}</h1>'
})
new Vue({el: '#data-title'})
</script>


除此之外我们还可以使用动态赋予一个变量的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div id="data-title">
<data-title
v-for="post in posts"
v-bind:key="post.id"
v-bind:title="post.title"></data-title>
</div>
<script>
new Vue({
el: '#data-title',
data: {
posts: [
{ id: 1, title: 'Hello,world!'},
{ id: 2, title: 'Hello,Vue!'},
{ id: 3, title: 'Hello,Laravel!'},
]
}
})
Vue.component('data-title', {
props: ['title'],
template: '<h4>{{title}}</h4>'
})
new Vue({el: '#data-title'})
</script>

监听子组件事件


在开发<text-post组件的时候,就会涉及到父级组件的协调来实现一个文字的放大效果以及默认字体大小。首先我们在父组件,即#text-posts-events中添加一个postFontSize数据属性来设置默认的字号。

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
<div id="text-posts-events">
<div :style="{ fontSize: postFontSize + 'em' }">
<text-post
v-for="post in posts"
v-bind:key="post.id"
v-bind:post="post"
v-on:enlarge-text="postFontSize += 0.1"
>
</text-post>
</div>
</div>
<script>
Vue.component('text-post', {
props: ['post'],
template: '\
<div class="text-post">\
<h3>{{post.title}}\
<button v-on:click="$emit(\'enlarge-text\')">\
字体放大\
</button>\
<div v-html="post.content"></div>\
</div>\
'
})
new Vue({
el: '#text-posts-events',
data: {
posts: [
{id:1, title: 'Hello,vue!', content:'vue'},
{id:2, title: 'Hello,Laravel!', content:'laravel'}
],
postFontSize: 1
}
})
</script>

Vue 表单处理

用法

vue 的表单处理主要使用v-model指令,且仅仅限制于在<input>、<select>、<textarea>等HTML表单元素中使用。v-model可以根据HTML控件的类型来自动选取正确的方法来进行更新。

需要值得注意的是v-model指令会忽略所有的表单value、checked、selected等属性的初始值,所以需在js内的data选项中声明初始值,其中元素所对应的属性对应为:

ID DA
text & textarea 使用value 属性和input 事件
checkbox & radio 使用 checked 属性 和 change 事件
select 将以value 作为属性并和 change 事件

input

1
2
3
4
5
6
7
8
9
10
11
12
<div id="app">
<input v-model="message" placeholder="输入">
<p>当前输入的是: {{message}}</p>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
message: 0
}
})
</script>

textarea

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<style>
.br {
white-space: pre-line;
}
</style>
<div id="app">
<textarea v-model="message" placeholder="输入"></textarea>
<p class="br">当前输入的是: {{message}}</p>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
message: ''
}
})
</script>

在上述的演示当中,<p>标签是不会根据<textarea>标签中并不会进行换行,所以我们需要使用style的white-space属性。

checkbox

1
2
3
4
5
6
7
8
9
10
11
12
<div id="app">
<input type="checkbox" id="message" v-model="message">
<label for="message">{{message}}</label>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
message: false
}
})
</script>

复选框绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<div id="app">
<h2>Vue</h2>
<input type="checkbox" id="vue" value="vue" v-model="message"><label>vue</label>
<input type="checkbox" id="vue" value="vue" v-model="message"><label>vue</label>
<h2>Laravel</h2>
<input type="checkbox" id="laravel" value="laravel" v-model="message"><label>laravel</label>
<h2>Less</h2>
<input type="checkbox" id="less" value="less" v-model="message"><label>less</label>
<hr/>
<br>
<label>共选择了 {{message}}</label>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
message: []
}
})
</script>

radio

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div id="app">
<input type="radio" id="vue" value="Vue" v-model="message">
<label for="vue">Vue</label>
<input type="radio" id="laravel" value="Laravel" v-model="message">
<label for="vue">Laravel</label>
<input type="radio" id="ejs" value="Ejs" v-model="message">
<label for="vue">Ejs</label>
<hr/>
<br>
<label>选择了 {{message}}</label>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
message: ''
}
})
</script>

当单选一个radio类型的时候,将会根据其radio类型中所设置的value进行显示。

选择框

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="app">
<select v-model="message">
<option disabled value="">类型</option>
<option value="Vue 被选择了">Vue</option>
<option value="Laravel 被选择了">Laravel</option>
</select>
<p>{{message}}</p>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
message: ''
}
})
</script>

在上述的code当中,我们主要通过使用value来设置option被选择时的属性,除此之外我们还可以直接在 vue 中更加生动的进行绑定:

value 绑定

修饰符

在 vue中,不仅仅为前面几章提供了修饰符,自然而然的也为表单处理所加入了一些较为常用的修饰符,如.lazy、.number、.trim,他们的详细用法是:

ID DA
.lazy 当输入完成时在同步信息
.number 将输入的值自动转换为 number类型
.trim 自动过滤首尾空白

.lazy


vue 所提供的.lazy修饰符的主要作用就是,当输入的时候并不会更新当前输入的内容,只有当结束输入的时候才会进行更新或同步:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<style>
.br {
white-space: pre-line;
}
</style>
<div id="app">
<textarea v-model.lazy="message" placeholder="输入"></textarea>
<p class="br">当前输入的是: {{message}}</p>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
message: ''
}
})
</script>

.number

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
```vue
<style>
.br {
white-space: pre-line;
}
</style>
<div id="app">
<input type="number" v-model.number="message" placeholder="输入"></input>
<p class="br">当前输入的是: {{message}}</p>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
message: ''
}
})
</script>

.trim

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
```vue
<style>
.br {
white-space: pre-line;
}
</style>
<div id="app">
<textarea v-model.trim="message" placeholder="输入"></textarea>
<p class="br">当前输入的是: {{message}}</p>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
message: ''
}
})
</script>
📖 more posts 📖