我正在参加「掘金·启航计划」
1. Vue框架部分
1.1 vue的指令
# vue指令及含义
v-cloak: 防止网络不好时出现{{msg}}这种现象
v-text: 数据绑定,插值相当于{{msg}}
v-pre: 显示原始 Mustache 标签 即{{msg}},跳过编译过程
v-slot: 插槽
v-on: 事件监听函数 v-on:click="hadleClick"(@click="handleClick")
v-model: 数据双向绑定 v-model='name'(常用于input输入框)
v-once: 数据加载一次之后就不在改变
v-html: 将html字符串标签解析为html(不建议用于表单提交,会造成xss工具)
v-bind: 属性绑定 v-bind:title="'这是一张图片'"(:title="'这是一张图片'")
v-if: 分支切换(不满足dom则会被删除) 控制元素是否渲染到页面
v-else-if: 分支切换,不满足v-if则会走到v-else-if这里(不满足dom则会被删除)
v-else: 以上都不满足则会走到这里(不满足dom则会被删除)
v-show: 切换显示/隐藏(v-show是将样式display设置为none) 控制元素是否显示(已经渲染到了页面)
v-for: 循环遍历 需要绑定key进一步提高性能
1.2 事件修饰符
# 事件修饰符 v-on:click === @click
<button v-on:[event]="doThis"></button> 动态事件
<form @submit.prevent></form> 阻止默认行为没有表达式
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button> 对象语法绑定事件
@click.stop="handle" 阻止冒泡
@click.prevent="handle" 阻止默认事件
@click.stop.prevent="handle" 阻止冒泡同时也阻止默认事件
@click.self="handle" 只有点击元素自身才会触发
@click.capture 添加事件侦听器时使用capture模式
@click.native 监听组件根元素的原生事件
@click.once 只触发一次回调 (version > 2.1.4)
@click.passive 滚动事件的默认行为 (即滚动行为) 将会立即触发以{ passive:true }模式添加侦听器(version>2.3.0).passive 修饰符尤其能够提升移动端的性能。
使用修饰符时,顺序很重要,相应的代码会以同样的顺序产生.因此,用
v-on:click.prevent.self会阻止所有的点击, 而
v-on:click.self.prevent只会阻止对自身元素的点击不要把
.passive和.prevent一起使用,因为.prevent将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive会告诉浏览器你不想阻止事件的默认行为。
用法:
绑定事件监听器。事件类型由参数指定。表达式可以是一个方法的名字或一个内联语句,如果没有修饰符也可以省略。
用在普通元素上时,只能监听原生 DOM 事件。用在自定义元素组件上时,也可以监听子组件触发的自定义事件。
#触发事件时,传递参数
@click="handle" // 不传参 handle(val){console.log(val)} // 此时val为$event事件对象
@click="handle(item)" // 传参
@cick="handle(item,$event)" // 传参,传事件对象
1.3 按键修饰符
@click.{keyCode | keyAlias} 事件是从特定键触发时才触发回调
Vue.config.keyCodes.aaa = 65 // 自定义全局按键修饰符
@click.left 只点击鼠标左键时触发(version>2.2.0)
@click.right 点击鼠标右键时触发(version>2.2.0)
@click.middle 点击鼠标中键时触发(version>2.2.0)
<button @click.ctrl.exact="onCtrlClick">A</button> // 有且只有ctrl被按下的时候才触发
1.4 表单修饰符
表单域修饰符
v-model.number: 转换为数值
v-model.trim: 去掉开始和结尾的空格
v-model.lazy: 将input时间转换为change事件
1.5 样式绑定
1.5.1 class动态绑定
<template>
<div :class="{active: isActive,bgc:bgc}"></div> // 对象语法绑定类名
<div :class="[activeClass,bgcClass]"></div> // 数组语法绑定类名
<div :class="[activeClass,{active: isActive]"></div> // 对象和数组结合使用
<div :class="myClass"></div> //数组简化类名
<div :calss="objClass"></div> // 对象简化用法
<div class="base" :calss="objClass"></div> // 静态class和动态class结合
</template>
<script>
export default {
data() {
return {
isActive: false, // 对象类型
bgc: false, // 对象类型
activeClass: 'active', // 数组类型, 去除这个类名时直接赋值为''字符串
bgcClass: 'bgc', // 数组类型, 去除这个类名时直接赋值为''字符串
myClass: ['active','bgc'], // 数组简化
objClass: { // 对象类名简化
active: true,
bgc: true
}
}
}
}
</script>
<style>
.active{
color: red
}
.bgc{
background-color: #000;
}
</style>
1.5.2 style动态绑定
<template>
<div :style="{border:borderStyle,width: widthStyle}"></div> // 对象语法绑定样式
</template>
<script>
export default {
data() {
return {
borderStyle: '1px solid red',
widthStyle: '200px'
}
}
}
</script>
<style>
</style>
1.6 Vue之自定义指令
// 全局定义指令
Vue.directive('focus',{
// el: 指令所绑定的元素,可以用来直接操作dom
/* binding: 一个对象,包含一下property:
* name: 指令名,不包括v- 前缀
* value: 指令绑定的值, 例如 v-my-directive="1+1", 绑定值为2
* oldValue: 指令绑定的前一个值, 仅在update和componentUpdated钩子中可用,无论值是否改变 * 都可用
* expression: 字符串形式的指令表达式,例如:v-my-directive="1+1", 表达式为: '1+1'
* arg: 传给指令的参数.可选.例如:v-my-directive:foo, 参数为"foo"
* modifiers: 一个包含修饰符的对象,如v-my-directive.foo.bar,修饰符为{foo:true,bar:true}
*/
// vnode: Vue编译生成的虚拟节点
//oldVnode: 上一个虚拟节点,仅在update和componentUpdated钩子中可用
inserted: function(el,binding,vnode,oldVnone){ // 被绑定元素插入父节点时调用(仅保证父节点存在,但不一定被插入文档中)
// el表示指令所绑定的元素
el.focus()
},
bind: function(el,binding,vnode,oldVnone){
// 该方法只调用一次,指令第一次绑定到元素时调用,在这里可以进行一次性的初始化设置
},
update: function(el,binding,vnode,oldVnone){
//所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新
},
componentUpdated: function(el,binding,vnode,oldVnone){
// 指令所在组件的 VNode 及其子 VNode 全部更新后调用。
},
unbind: function(el,binding,vnode,oldVnone){
// 只调用一次,指令与元素解绑时调用。
}
})
// 使用自定义指令
<input v-focus type="text">
// 在组件中定义局部指令,组件中也接受一个directives的选项:
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}
1.7 计算属性
我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。
Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch——特别是如果你之前使用过 AngularJS。然而,通常更好的做法是使用计算属性而不是命令式的 watch 回调.
使用 watch 选项允许我们执行异步操作 (访问一个 API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
<div id="example">
<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})
1.8 过滤器
Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值和 v-bind 表达式 (后者从 2.1.0+ 开始支持)。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示:
<template>
<!-- 在双花括号中 -->
{{ message | capitalize }}
<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>
<!-- 过滤器可以串联 -->
{{ message | filterA | filterB }}
<!-- 过滤器是 JavaScript 函数,因此可以接收参数: 参数1: message, 参数2: 字符串'arg1',参数3:表达式arg -->
{{ message | filterA('arg1', arg2) }}
</template>
<script>
export default {
// 局部过滤器
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
}
</script>
// 全局过滤器
Vue.filter('capitalize', function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
new Vue({
// ...
})
**注:**当全局过滤器和局部过滤器重名时,会采用局部过滤器
1.9 Vue组件的生命周期
beforeCreate( )// 该钩子函数执行时,组件实例还未创建.
created()//组件初始化完毕,各种数据可以使用,可以使用ajax发送异步请求获取数据
beforeMounted()// 未执行渲染,更新,虚拟DOM完成,真实DOM未创建
mounted()// 初始化阶段结束,真实DOM已经创建,可以发送异步请求获取数据,也可以访问dom元素
beforeUpdate()//更新前,可用于获取更新前各种状态数据
updated()//更新后执行该钩子函数,所有的状态数据是最新的。
beforeDestroy() // 销毁前执行,可以用于一些定时器的清除。
destroyed()//组件已经销毁,事件监听器被移除,所有的子实例也会被销毁。
1.10 数组更新检测
- 变更方法(修改原有数组)
Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:
- 变异数组方法,会修改原有数组
- push() 向数组的末尾添加一个或多个元素,并返回新的数组
- pop() 删除数组的最后一个元素并返回删除的元素。
- shift() 把数组的第一个元素从其中删除,并返回第一个元素的值。
- unshift() 将新项添加到数组的开头,并返回新的数组。
- splice() 用于添加或删除数组中的元素,返回被删除的元素数组,没删除返回空数组
- sort() 方法用于对数组的元素进行排序。参数接受一个函数
- reverse() 用于颠倒数组中元素的顺序。
- 替换数组(生成新的数组)
变更方法,顾名思义,会变更调用了这些方法的原始数组。相比之下,也有非变更方法,例如 `filter()`、`concat()` 和 `slice()`。它们不会变更原始数组,而**总是返回一个新数组**。当使用非变更方法时,可以用新数组替换旧数组:
example1.items = example1.items.filter(function (item) {
return item.message.match(/Foo/)
})
1.11 组件化开发
1.11.1 组件注册
-
全局组件注册
记住全局注册的行为必须在根
Vue实例 (通过new Vue) 创建之前发生。直接在 DOM (即非字符串的模板) 中使用时只有 kebab-case 是有效的。
# 短横线模式
Vue.component('my-component-name', { /* ... */ })
# 驼峰命名
Vue.component('MyComponentName', { /* ... */ })
-
局部组件注册
局部组件只能在注册它的父组件中使用
var ComponentA = { /* ... */ }
new Vue({
el: '#app',
components: {
'component-a': ComponentA,
}
})
组件注册注意事项:
- data必须是一个函数
- 组件模板根内容必须是单个元素
- 组件模板内容可以是模板字符串
1.11.2 父子组件传值
props属性名规则:
- 在props中使用驼峰形式,在模板中使用短横线形式
- 字符串形式的模板中没有这个限制
props值类型
- Number 数值
- String 字符串
- Boolean 布尔值
- Array 数组
- Object 对象
<template>
<div>
<h1>父组件</h1>
<son v-bind:fData="data1" :fMessage="data2"></son>
</div>
</template>
<script>
export default {
data () {
return {
data1: '父组件数据data1',
data2: '父组件数据data2',
};
},
components: {
Son
}
}
</script>
# 子组件
<template>
<div>
<h1>子组件</h1>
<p>下面是父组件传过来的数据</p>
<p>第一个数据:{{fData}}</p>
<p>第二个数据:{{fMessage}}</p>
</div>
</template>
<script>
export default {
name: 'son'
props: ['fData', 'fMessage'],
data () {
return {
};
}
}
</script>
1.11.3 子组件向父组件传值
- 父组件在组件上定义了一个自定义事件
childFn,事件名为parentFn用于接受子组件传过来的message值。
<!-- 父组件 -->
<template>
<div class="test">
<test-com @childFn="parentFn"></test-com>
<br/>
子组件传来的值 : {{message}}
</div>
</template>
<script>
export default {
// ...
data() {
return {
message: ''
}
},
methods: {
parentFn(payload) {
this.message = payload;
}
}
}
</script>
- 子组件是一个
buttton按钮,并为其添加了一个click事件,当点击的时候使用$emit()触发事件,把message传给父组件。
<!-- 子组件 -->
<template>
<div class="testCom">
<input type="text" v-model="message" />
<button @click="click">Send</button>
</div>
</template>
<script>
export default {
// ...
data() {
return {
// 默认
message: '我是来自子组件的消息'
}
},
methods: {
click() {
this.$emit('childFn', this.message);
}
}
}
</script>
1.11.4 兄弟组件通信
// 定义事件中心
const eventBus = new Vue()
<!-- ParentCard -->
<template>
<div class="card">
<div class="card-header">
<h5 v-text="theCardTitle"></h5>
</div>
<div class="card-body">
<brother-card></brother-card>
<sister-card></sister-card>
</div>
</div>
</template>
<script>
import BrotherCard from "./BrotherCard";
import SisterCard from "./SisterCard";
export default {
name: "ParentCard",
data: () => ({
theCardTitle: "Parent Card"
}),
components: {
BrotherCard,
SisterCard
}
};
</script>
<!-- SisterCard.vue -->
<template>
<div class="message">
<div class="message-header">
<h5 v-text="theCardTitle"></h5>
</div>
<div class="message-body">
<p class="message-text">我是Sister组件</p>
<button @click="messageBrother" class="btn">给哥哥发消息</button>
<div v-if="fromBrother" class="alert" v-html="fromBrother"></div>
</div>
</div>
</template>
<script>
import { eventBus } from "../main";
export default {
name: "SisterCard",
data: () => ({
theCardTitle: "Sister Card",
fromBrother: ""
}),
methods: {
messageBrother() {
eventBus.$emit("sisterSaid", "妈妈说,该做作业了!(^_^)!!!");
}
},
created() {
eventBus.$on("brotherSaid", message => {
this.fromBrother = message;
});
}
};
</script>
<!-- BrotherCard.vue -->
<template>
<div class="message">
<div class="message-header">
<h5 v-text="theCardTitle"></h5>
</div>
<div class="message-body">
<p class="message-text">我是Brother组件</p>
<button @click="messageSister" class="btn">给妹妹发消息</button>
<div v-if="fromSister" class="alert" v-html="fromSister"></div>
</div>
</div>
</template>
<script>
import { eventBus } from "../main.js";
export default {
name: "BrotherCard",
data: () => ({
theCardTitle: "Brother Card",
fromSister: ""
}),
methods: {
messageSister() {
eventBus.$emit("brotherSaid", "妈妈说,该做作业了!(^_^)!!!");
}
},
created() {
eventBus.$on("sisterSaid", message => {
this.fromSister = message;
});
}
};
</script>
this.$off('xxx') // 销毁事件
1.11.5 插槽
<navigation-link url="/profile"> // 父组件传递内容
Your Profile
</navigation-link>
然后你在 <navigation-link> 的模板中可能会写为:
<a
v-bind:href="url"
class="nav-link"
>
<slot></slot> // 子组件在这里接收父组件传递的内容
</a>
当组件渲染的时候,<slot></slot> 将会被替换为“Your Profile”。插槽内可以包含任何模板代码,包括 HTML:
<navigation-link url="/profile">
<!-- 添加一个 Font Awesome 图标 -->
<span class="fa fa-user"></span>
Your Profile
</navigation-link>
<navigation-link url="/profile">
Clicking here will send you to: {{ url }}
<!--
这里的 `url` 会是 undefined,因为其 (指该插槽的) 内容是
_传递给_ <navigation-link> 的而不是
在 <navigation-link> 组件*内部*定义的。
-->
</navigation-link>
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
有时我们需要多个插槽。例如对于一个带有如下模板的 <base-layout> 组件:
<div class="container">
<header>
<!-- 我们希望把页头放这里 -->
</header>
<main>
<!-- 我们希望把主要内容放这里 -->
</main>
<footer>
<!-- 我们希望把页脚放这里 -->
</footer>
</div>
对于这样的情况,<slot> 元素有一个特殊的 attribute:name。这个 attribute 可以用来定义额外的插槽:
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
一个不带 name 的 <slot> 出口会带有隐含的名字“default”。
在向具名插槽提供内容的时候,我们可以在一个 <template> 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称:
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
现在 <template> 元素中的所有内容都将会被传入相应的插槽。任何没有被包裹在带有 v-slot 的 <template> 中的内容都会被视为默认插槽的内容。
作用域插槽:
有时让插槽内容能够访问子组件中才有的数据是很有用的。例如,设想一个带有如下模板的 <current-user> 组件:
<span>
<slot>{{ user.lastName }}</slot>
</span>
我们可能想换掉备用内容,用名而非姓来显示。如下:
<current-user>
{{ user.firstName }}
</current-user>
然而上述代码不会正常工作,因为只有 <current-user> 组件可以访问到 user,而我们提供的内容是在父级渲染的。
为了让 user 在父级的插槽内容中可用,我们可以将 user 作为 <slot> 元素的一个 attribute 绑定上去:
<span>
<slot v-bind:user="user">
{{ user.lastName }}
</slot>
</span>
绑定在 <slot> 元素上的 attribute 被称为插槽 prop。现在在父级作用域中,我们可以使用带值的 v-slot 来定义我们提供的插槽 prop 的名字:
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
</current-user>
在这个例子中,我们选择将包含所有插槽 prop 的对象命名为 slotProps,但你也可以使用任意你喜欢的名字。
1.12 Vue路由
1.12.1 基本使用步骤
# 安装 vue-router
npm install vue-router
1.12.2 动态路由props传参
routes = [
{
path: '/my/:id'
name: 'My',
component: My,
props: true, // 开启路由props传参
}
// props 可以为函数类型 props: route =>({uname: '123', id: route.params.id })
// props 可以返回一个静态数据 props: {uname: 'lisi', age: 20}
]
// 组件内访问路由的id
export default {
props: ['id']
}
1.12.3 命名路由
// name: 表示命名路由,此时可以通过 router.push({name: 'My',params: {id:123 }})跳转
routes = [
{
path: '/my/:id'
name: 'My',
component: My,
props: true, // 开启路由props传参
}
// props 可以为函数类型 props: route =>({uname: '123', id: route.params.id })
// props 可以返回一个静态数据 props: {uname: 'lisi', age: 20}
]
1.12.4 编程式导航
this.$router.push('地址')
this.$router.go(n) // n 为数值,正数代表前进,负数表示后退
1.13 this.$nextTick()的作用
$nextTick方法的作用,就是当页面上元素被重新渲染之后,才会执行回调函数中的代码
1.14 完整的导航解析流程
/*
* 1: 导航被触发;
* 2: 在失活的组件里调用离开守卫;
* 3: 调用全局的beforeEach守卫;
* 4: 在重用的组件里调用beforeRouteUpdate守卫;
* 5: 在路由配置里面调用beforeEnter;
* 6: 解析异步路由组件;
* 7: 在被激活的组件里调用beforeRouteEnter;
* 8: 调用全局的beforeResolve守卫;
* 9: 导航被确认;
* 10: 调用全局的afterEnter钩子;
* 11: 触发Dom更新;
* 12: 用创建好的实例调用beforeRouterEnter守卫中传给next的回调函数
*
* */