文章目录
概念
基本语法
声明式渲染
表单输入绑定
数据绑定中进行数据处理
Class 与 Style 的数据绑定
条件与循环
数据更新检测
事件监听器
修饰符
指令的缩写
组件
注册组件
组件名和Prop名
局部注册
在模块系统中局部注册
自动化全局注册
Prop
组件的数据
组件的事件
is特性和动态组件
异步组件
组件构建应用
vue实例
实例生命周期的钩子
模板语法
高级语法
组件相关的边界情况
过渡 & 动画
混入
自定义指令
渲染函数 & JSX
插件
过滤器
工具
单文件组件
单元测试
TypeScript 支持
生产环境部署
深入响应式原理
热点问题
风格指南
在 VS Code 中调试
客户端存储
Devtools
Vue CLI
Vue Loader
Vue Router
基本介绍
路由匹配
编程式导航
重定向和别名
HTML5 History 模式
Vuex
Vue SSR
axios
介绍
发送请求
请求配置
响应结构
错误处理
使用 application/x-www-form-urlencoded format
其他API和功能特性
Element
vue-element-admin
介绍
中文
布局
路由和侧边栏
权限验证
快捷导航(标签栏导航)
新增一个页面
样式
和服务端进行交互
Mock Data
构建,发布和环境变量
其他
相关概念
HTML DOM
Node.js
NPM
Modern JavaScript with ES2015/16
AMD规范,commonJS规范,和CMD规范
JSX和React
Flux和Redux
TypeScript
webpack
Babel
REPL
SSR
Git Hooks
Promise
概念
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
Vue 不支持 IE8 及以下版本,因为 Vue 使用了 IE8 无法模拟的 ECMAScript 5 特性。但它支持所有兼容 ECMAScript 5 的浏览器。
索引
官网:https://vuejs.org 中文官网:https://cn.vuejs.org API:https://cn.vuejs.org/v2/api/
安装vue
在线使用:
安装教程:https://cn.vuejs.org/v2/guide/installation.html
可以直接下载并使用
动态参数
从 2.6.0 开始,可以用方括号括起来的 JavaScript 表达式作为一个指令的参数:
...
表单输入绑定
v-model 指令,它能轻松实现表单输入和应用状态之间的双向绑定。
v-model 会忽略所有表单元素的 value、checked、selected 特性的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data 选项中声明初始值。
v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:
text 和 textarea 元素使用 value 属性和 input 事件;
checkbox 和 radio 使用 checked 属性和 change 事件;(单个复选框,绑定到布尔值;多个复选框,绑定到同一个数组)
select 字段将 value 作为 prop 并将 change 作为事件。
自定义输入组件也可以使用v-model。
model可以使用.lazy,.number,.trim修饰符。
数据绑定中进行数据处理
使用JS表达式
对于所有的数据绑定,Vue.js 都提供了完全的 JavaScript 表达式支持:
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
{{ var a = 1 }}
{{ if (ok) { return message } }}
计算属性
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。对于任何复杂逻辑,你都应当使用计算属性。
Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。然而,通常更好的做法是使用计算属性而不是命令式的 watch 回调。
Original message: "{{ message }}"
Computed reversed message: "{{ reversedMessage }}"
我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。 然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。 相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。
Reversed message: "{{ reversedMessage() }}"
计算属性默认只有 getter ,不过在需要时你也可以提供一个 setter:
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter,现在再运行 vm.fullName = 'John Doe' 时,setter 会被调用,vm.firstName 和 vm.lastName 也会相应地被更新。
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
侦听器
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。
当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
Class 与 Style 的数据绑定
在将 v-bind 用于 class 和 style 时,Vue.js 做了专门的增强。表达式结果的类型除了字符串之外,还可以是对象或数组。
Class的数据绑定
我们可以传给 v-bind:class 一个对象,以动态地切换 class。
可以在这个对象中传入更多属性来动态切换多个 class。
v-bind:class 指令也可以与普通的 class 属性共存。
绑定的数据对象不必内联定义在模板里。
也可以在v-bind:class 指令中绑定一个返回对象的计算属性。
我们可以把一个数组传给 v-bind:class,以应用一个 class 列表。
可以在数组中,结合使用对象语法。
当在一个自定义组件上使用 class 属性时,这些 class 将被添加到该组件的根元素上面。这个元素上已经存在的 class 不会被覆盖。
Style的数据绑定
v-bind:style 的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象。
绑定的数据对象不必内联定义在模板里。
对象语法常常结合返回对象的计算属性使用。
v-bind:style 的数组语法可以将多个样式对象应用到同一个元素上。
当 v-bind:style 使用需要添加浏览器引擎前缀的 CSS 属性时,如 transform,Vue.js 会自动侦测并添加相应的前缀。
从 2.3.0 起你可以为 style 绑定中的属性提供一个包含多个值的数组,常用于提供多个带前缀的值。
条件与循环
条件渲染
v-if指令
条件使用v-if指令: (注:永远不要把 v-if 和 v-for 同时用在同一个元素上。)
因为 v-if 是一个指令,所以必须将它添加到一个元素上。如果想切换多个元素,可以把一个 元素当做不可见的包裹元素,并在上面使用 v-if。最终的渲染结果将不包含 元素。
Title
Paragraph 1
Paragraph 2
可以使用 v-else 指令来表示 v-if 的“else 块”。 2.1.0 新增 v-else-if 。
Now you see me
Now you don't
Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。 Vue 为你提供了一种方式来表达“这两个元素是完全独立的,不要复用它们”。只需添加一个具有唯一值的 key 属性即可:
Username
Email
v-show指令
v-show:带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS 属性 display。 v-show 不支持 元素。
Hello!
对比 vi-if:
v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。
列表(数组)渲染
循环使用v-for指令。 我们可以用 v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名。
v-for 还支持一个可选的第二个参数,即当前项的索引。
...
也可以用 of 替代 in 作为分隔符,因为它更接近 JavaScript 迭代器的语法:
也可以用 v-for 来遍历一个对象的属性。 在遍历对象时,会按 Object.keys() 的结果遍历,但是不能保证它的结果在不同的 JavaScript 引擎下都一致。
建议尽可能在使用 v-for 时提供 key attribute,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素。 如果不指定key,默认使用“就地更新”的策略。如果数组中数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素。
我们想要显示一个数组经过过滤或排序后的版本,而不实际改变或重置原始数据。在这种情况下,可以在 v-for 中使用计算属性或者方法。例如:n in computedVar/method()
v-for 也可以接受整数。在这种情况下,它会把模板重复对应次数。例如:v-for="n in 10"
可以利用带有 v-for 的 来循环渲染一段包含多个元素的内容。
在自定义组件上,你可以像在任何普通元素上一样使用 v-for 。2.2.0+ 的版本里,当在组件上使用 v-for 时,key 现在是必须的。 然而,任何数据都不会被自动传递到组件里,因为组件有自己独立的作用域。为了把迭代数据传递到组件里,我们要使用 prop:
数据更新检测
Vue 将被侦听的数组的变异方法(mutation method,变异方法,顾名思义,会改变调用了这些方法的原始数组,例如:push(), pop(), shift(), splice(), unshift(), sort(), reverse() )进行了包裹,所以它们也将会触发视图更新。 非变异 (non-mutating method) 方法,例如 filter()、concat() 和 slice() 。它们不会改变原始数组,而总是返回一个新数组。当使用非变异方法时,可以用新数组替换旧数组。Vue 为了使得 DOM 元素得到最大范围的重用而实现了一些智能的启发式方法,所以用一个含有相同元素的数组去替换原来的数组是非常高效的操作。
注意:当你利用索引直接设置一个数组项时,或者当你修改数组的长度时,Vue 并不能检测到数组的变动:
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x' // 不是响应性的,使用 Vue.set(vm.items, indexOfItem, newValue) 或者 vm.items.splice(indexOfItem, 1, newValue) 代替,也可以使用 vm.$set 实例方法,该方法是全局方法 Vue.set 的一个别名:vm.$set(vm.items, indexOfItem, newValue)
vm.items.length = 2 // 不是响应性的,使用 vm.items.splice(newLength) 代替
Vue 不能检测对象属性的添加或删除:
var vm = new Vue({
data: {
a: 1,
userProfile: {
name: 'Anika'
}
}
})
// `vm.a` 现在是响应式的
vm.b = 2
// `vm.b` 不是响应式的
// 对于已经创建的实例,Vue 不允许动态添加根级别的响应式属性,例如上例中的b。但是,可以使用 Vue.set(object, propertyName, value) 方法向嵌套对象添加响应式属性。
// 还可以使用 vm.$set 实例方法,它只是全局 Vue.set 的别名:vm.$set(vm.userProfile, 'age', 27)
Vue.set(vm.userProfile, 'age', 27)
// 为已有对象赋值多个新属性,应该用两个对象的属性创建一个新的对象
vm.userProfile = Object.assign({}, vm.userProfile, {
sex: 'female',
favoriteColor: 'Vue Green'
})
事件监听器
用 v-on 指令添加一个事件监听器,通过它调用在 Vue 实例中定义的方法:
{{ message }}
反转消息
Greet
Submit
所有的 Vue.js 事件处理方法和表达式都严格绑定在当前视图的 ViewModel 上,它不会导致任何维护上的困难。使用 v-on 有几个好处:
扫一眼 HTML 模板便能轻松定位在 JavaScript 代码里对应的方法。
因为你无须在 JavaScript 里手动绑定事件,你的 ViewModel 代码可以是非常纯粹的逻辑,和 DOM 完全解耦,更易于测试。
当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除。你无须担心如何清理它们。
修饰符
修饰符 (modifier) 是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。
事件修饰符
Vue.js 为 v-on 提供了事件修饰符,专门去处理 DOM 事件细节。 支持:.stop, .prevent, .capture, .self, .once(2.1.4 新增), .passive(2.3.0 新增)。 .once 修饰符还能被用到自定义的组件事件上,其它只能对原生的 DOM 事件起作用。
...
...
...
按键修饰符
Vue 允许为 v-on 在监听键盘事件时添加按键修饰符。其中按键名称参见:https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
按键码
为了在必要的情况下支持旧浏览器,Vue 提供了绝大多数常用的按键码的别名:.enter,.tab,.delete (捕获“删除”和“退格”键),.esc,.space,.up,.down,.left,.right。推荐优先使用别名。 你还可以通过全局 config.keyCodes 对象自定义按键修饰符别名:
// 可以使用 `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112
系统修饰键
可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器:.ctrl,.alt,.shift,.meta(在 Mac 系统键盘上,meta 对应 command 键 (⌘)。在 Windows 系统键盘 meta 对应 Windows 徽标键 (⊞)。)(前面均为2.1.0 新增),.exact(2.5.0 新增),.left,.right,.middle(2.2.0 新增)
Do something
A
A
A
A
指令的缩写
Vue 为 v-bind 和 v-on 这两个最常用的指令,提供了特定简写:
...
...
...
...
组件
在 Vue 里,一个组件本质上是一个拥有预定义选项的一个 Vue 实例。每用一次组件,就会有一个它的新实例被创建。
组件系统是 Vue 的另一个重要概念,因为它是一种抽象,允许我们使用小型、独立和通常可复用的组件构建大型应用。
Vue的组件和原生自定义组件并不相同,具体参见:https://cn.vuejs.org/v2/guide/index.html#与自定义元素的关系
注册组件
在 Vue 中注册组件很简单:
为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别。这里有两种组件的注册类型:全局注册和局部注册。 全局注册的组件可以用在其被注册之后的任何 (通过 new Vue) 新创建的 Vue 根实例,也包括其组件树中的所有子组件的模板中。
// 全局注册。
Vue.component('component-a', { /* ... */ })
Vue.component('component-b', { /* ... */ })
Vue.component('component-c', { /* ... */ })
// 其后的vue实例,其对应的组件树中所有子组件都可以使用
new Vue({ el: '#app' })
可以使用 JavaScript 的模板字符串来让多行的模板更易读。 模板字符串使用反引号 (
) 来代替普通字符串中的用双引号和单引号。在模版字符串内使用反引号(`)时,需要在它前面加转义符(\)。 模板字符串在 IE 下并没有被支持,所以如果你需要在不 (经过 Babel 或 TypeScript 之类的工具) 编译的情况下支持 IE,请使用折行转义字符取而代之。
Vue.component('blog-post', {
props: ['post'],
template: `
`
})
组件名和Prop名
组件名,推荐遵循 W3C 规范中的自定义组件名 (字母全小写且必须包含一个连字符)。这会帮助你避免和当前以及未来的 HTML 元素相冲突。
推荐使用 kebab-case (短横线分隔命名) 定义一个组件,例如 。 当使用 PascalCase (首字母大写命名) 定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用。也就是说 和 都是可接受的。注意,尽管如此,直接在 DOM (即非字符串的模板) 中使用时只有 kebab-case 是有效的。
Prop名也是一样。HTML 中的特性名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名。
局部注册
全局注册所有的组件意味着即便你已经不再使用一个组件了,它仍然会被包含在你最终的构建结果中。这造成了用户下载的 JavaScript 的无谓的增加。
局部注册方式:
// 定义组件
var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
var ComponentC = { /* ... */ }
new Vue({
el: '#app',
components: {
// 对于 components 对象中的每个属性来说,其属性名就是自定义元素的名字,其属性值就是这个组件的选项对象
'component-a': ComponentA,
'component-b': ComponentB
}
})
注意,局部注册的组件在其子组件中不可用。
// 如果希望 ComponentA 在 ComponentB 中可用:
var ComponentA = { /* ... */ }
var ComponentB = {
components: {
'component-a': ComponentA
},
// ...
}
在模块系统中局部注册
我们推荐创建一个 components 目录,并将每个组件放置在其各自的文件中。然后,在局部注册之前导入每个你想使用的组件。
// ComponentB.vue 文件
import ComponentA from './ComponentA'
import ComponentC from './ComponentC'
export default {
components: {
// 让 ComponentA 和 ComponentC 都可以在 ComponentB 的模板中使用:
ComponentA,
ComponentC
},
// ...
}
自动化全局注册
示例代码:
import Vue from 'vue'
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'
const requireComponent = require.context(
// 其组件目录的相对路径
'./components',
// 是否查询其子目录
false,
// 匹配基础组件文件名的正则表达式
/Base[A-Z]\w+\.(vue|js)$/
)
requireComponent.keys().forEach(fileName => {
// 获取组件配置
const componentConfig = requireComponent(fileName)
// 获取组件的 PascalCase 命名
const componentName = upperFirst(
camelCase(
// 获取和目录深度无关的文件名
fileName
.split('/')
.pop()
.replace(/\.\w+$/, '')
)
)
// 全局注册组件:全局注册的行为必须在根 Vue 实例 (通过 new Vue) 创建之前发生
Vue.component(
componentName,
// 如果这个组件选项是通过 `export default` 导出的,
// 那么就会优先使用 `.default`,
// 否则回退到使用模块的根。
componentConfig.default || componentConfig
)
})
Prop
可以以对象形式列出 prop,这些属性的名称和值分别是 prop 各自的名称和类型:
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // or any other constructor
}
使用Prop时也可以传入任何类型:
post: {
id: 1,
title: 'My Journey with Vue'
}
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。 额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。 这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。注意,在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变这个对象或数组本身将会影响到父组件的状态。
如果确实需要在子组件使用父组件的Prop属性,有2个建议的方法:
// 这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。在这种情况下,最好定义一个本地的 data 属性并将这个 prop 用作其初始值:
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
// 这个 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个计算属性:
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
可以为组件的 prop 指定验证要求,为了定制 prop 的验证方式,你可以为 props 中的值提供一个带有验证需求的对象,而不是一个字符串数组。 当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。
Vue.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
}
}
}
})
组件可以接受任意的特性,而这些特性会被添加到这个组件的根元素上。 对于绝大多数特性来说,从外部提供给组件的值会替换掉组件内部设置好的值。 class 和 style 特性会稍微智能一些,即两边的值会被合并起来:
如果你不希望组件的根元素继承特性,你可以在组件的选项中设置 inheritAttrs: false。 注意, inheritAttrs: false 选项不会影响 style 和 class 的绑定。
Vue.component('base-input', {
inheritAttrs: false,
props: ['label', 'value'],
template: `
{{ label }}
`
})
组件的数据
一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:
// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: 'You clicked me {{ count }} times. '
})
v-model
自定义事件也可以用于创建支持 v-model 的自定义输入组件。需要组件内的 满足以下条件:
将其 value 特性绑定到一个名叫 value 的 prop 上
在其 input 事件被触发时,将新的值通过自定义的 input 事件抛出
等价于:
等价于:
所以,组件要支持:
Vue.component('custom-input', {
props: ['value'],
template: `
`
})
一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像单选框、复选框等类型的输入控件可能会将 value 特性用于不同的目的。model 选项可以用来避免这样的冲突:
Vue.component('base-checkbox', {
model: {
// 必须声明 checked 这个 prop
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean
},
template: `
`
})
.async
使用 .sync 修饰符对一个 prop 进行“双向绑定”。类似 v-model,带有 .sync 修饰符的 v-bind 不能和表达式一起使用,只能提供你想要绑定的属性名。
插槽
通过插槽方式使用数据,插槽内可以包含任何模板代码,包括 HTML。
Something bad happened.
Vue.component('alert-box', {
template: `
Error!
`
})
Submit
...
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。 插槽跟模板的其它地方一样可以访问相同的实例属性 (也就是相同的“作用域”),而不能访问所属组件的作用域。
为了让 属性 在父级的插槽内容中可用,我们可以将 该属性 作为 元素的一个特性绑定上去。绑定在 元素上的特性被称为插槽 prop。
Clicking here will send you to: {{ url }}
{{ user.lastName }}
{{ slotProps.user.firstName }}
{{ user.firstName }}
通过具名插槽的方式来使用多个插槽。 v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 #。例如 v-slot:header 可以被重写为 #header。 同理,v-slot:default=“slotProps” 可以缩写为 #default=“slotProps”。
Here might be a page title
A paragraph for the main content.
And another one.
Here's some contact info
组件的事件
子组件可以通过调用内建的 $emit 方法 并传入事件名称来触发一个事件; 注:事件名不存在任何自动化的大小写转换。而是触发的事件名需要完全匹配监听这个事件所用的名称。并且, v-on 事件监听器在 DOM 模板中会被自动转换为全小写 (因为 HTML 是大小写不敏感的),所以 v-on:myEvent 将会变成 v-on:myevent——导致 myEvent 不可能被监听到。因此,推荐你始终使用 kebab-case 的事件名。 父级组件可以像处理 native DOM 事件一样通过 v-on 监听子组件实例的任意事件。
Vue.component('blog-post', {
props: ['post'],
template: `
{{ post.title }}
Enlarge text
`
})
可以使用 $emit 的第二个参数来使用事件抛出一个值; 当在父级组件监听这个事件的时候,可以通过 $event 访问到被抛出的这个值。
Enlarge text
想要在一个组件的根元素上直接监听一个原生事件。这时,你可以使用 v-on 的 .native 修饰符:
Vue 提供了一个 $listeners 属性,它是一个对象,里面包含了作用在这个组件上的所有原生事件的监听器。
Vue.component('base-input', {
inheritAttrs: false,
props: ['label', 'value'],
computed: {
inputListeners: function () {
var vm = this
// `Object.assign` 将所有的对象合并为一个新对象
return Object.assign({},
// 我们从父级添加所有的监听器
this.$listeners,
// 然后我们添加自定义监听器,
// 或覆写一些监听器的行为
{
// 这里确保组件配合 `v-model` 的工作
input: function (event) {
vm.$emit('input', event.target.value)
}
}
)
}
},
template: `
{{ label }}
`
})
is特性和动态组件
使用is特性可以在不同组件之间进行动态切换:
有些元素,诸如
、 和 ,只能出现在其它某些特定的元素内部。 例外,从以下来源使用模板的话,这条限制是不存在的:
字符串 (例如:template: ‘…’)
单文件组件 (.vue)
异步组件
Vue 允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。 工厂函数会收到一个 resolve 回调,这个回调函数会在你从服务器得到组件定义的时候被调用。你也可以调用 reject(reason) 来表示加载失败。 一个推荐的做法是将异步组件和 webpack 的 code-splitting 功能一起配合使用。
Vue.component('async-webpack-example', function (resolve) {
// 这个特殊的 `require` 语法将会告诉 webpack
// 自动将你的构建代码切割成多个包,这些包
// 会通过 Ajax 请求加载
require(['./my-async-component'], resolve)
})
// 你也可以在工厂函数中返回一个 Promise,所以把 webpack 2 和 ES2015 语法加在一起,我们可以写成这样:
Vue.component(
'async-webpack-example',
// 这个 `import` 函数会返回一个 `Promise` 对象。
() => import('./my-async-component')
)
异步组件工厂函数也可以返回一个如下格式的对象,以支持处理加载状态等:
const AsyncComponent = () => ({
// 需要加载的组件 (应该是一个 `Promise` 对象)
component: import('./MyComponent.vue'),
// 异步组件加载时使用的组件
loading: LoadingComponent,
// 加载失败时使用的组件
error: ErrorComponent,
// 展示加载时组件的延时时间。默认值是 200 (毫秒)
delay: 200,
// 如果提供了超时时间且组件加载也超时了,
// 则使用加载失败时使用的组件。默认值是:`Infinity`
timeout: 3000
})
组件构建应用
在一个大型应用中,有必要将整个应用程序划分为组件,以使开发更易管理。这里有一个 布局Layout 例子,以展示使用了组件的应用模板是什么样的:
vue实例
每个 Vue 应用都是通过用 Vue 函数创建一个新的 Vue 实例开始的:new Vue({ // 选项 })
。
一个 Vue 应用由一个通过 new Vue 创建的根 Vue 实例,以及可选的嵌套的、可复用的组件树组成。
当一个 Vue 实例被创建时,它将 data 对象中的所有的属性加入到 Vue 的响应式系统中。当这些属性的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。
值得注意的是只有当实例被创建时就已经存在于 data 中的属性才是响应式的;后来添加一个新的属性,这个属性的改动将不会触发任何视图的更新。
使用 Object.freeze(),这会阻止修改现有的属性,也意味着响应系统无法再追踪变化。
除了数据属性,Vue 实例还暴露了一些有用的实例属性与方法。它们都有前缀 $,以便与用户定义的属性区分开来。例如 vue.$data来引用实例vue的data对象。
实例生命周期的钩子
每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。
例如:
new Vue({
data: {
a: 1
},
// created就是一个钩子
created: function () {
// `this` 指向 vm 实例
console.log('a is: ' + this.a)
}
})
更多的钩子函数和被调用时间参见 生命周期图示:
模板语法
Vue.js 使用了基于 HTML 的模板语法,所有 Vue.js 的模板都是合法的 HTML ,所以能被遵循规范的浏览器和 HTML 解析器解析。
高级语法
组件相关的边界情况
参见:https://cn.vuejs.org/v2/guide/components-edge-cases.html
【不推荐】在每个 new Vue 实例的子组件中,其根实例可以通过 $root 属性进行访问。
【不推荐】$parent 属性可以用来从一个子组件访问父组件的实例。
【不推荐】可以通过 ref 特性为一个子组件赋予一个 ID 引用,在 JavaScript 里直接访问这个子组件。
【不推荐】依赖注入:provide 选项允许我们指定我们想要提供给后代组件的数据/方法。然后,在任何后代组件里,我们都可以使用 inject 选项来接收指定的我们想要添加在这个实例上的属性。
程序化的事件侦听器:通过 $on(eventName, eventHandler) 侦听一个事件;通过 $once(eventName, eventHandler) 一次性侦听一个事件;通过 $off(eventName, eventHandler) 停止侦听一个事件。
递归组件:组件是可以在它们自己的模板中调用自身的。它们通过 name 属性选项来做这件事。
组件之间的循环引用,在本地注册组件的时候,你可以使用 webpack 的异步 import 来避免解析依赖报错。
【不推荐】内联模板:当 inline-template 这个特殊的特性出现在一个子组件上时,这个组件将会使用其里面的内容作为模板,而不是将其作为被分发的内容。
【不推荐】另一个定义模板的方式是在一个 。
发送请求
通过向 axios 传递相关配置来创建请求:
// 获取远端图片
axios({
method:'get',
url:'http://bit.ly/2mTM3nY',
responseType:'stream'
})
.then(function(response) {
response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'))
});
使用别名方法进行发送请求,无需在配置中指定 method 等属性:
axios.request(config)
axios.get(url[, config])
axios.delete(url[, config])
axios.head(url[, config])
axios.options(url[, config])
axios.post(url[, data[, config]])
axios.put(url[, data[, config]])
axios.patch(url[, data[, config]])
// 执行 POST 请求
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
请求配置
创建请求时可以用的配置选项:
{
// `url` 是用于请求的服务器 URL
url: '/user',
// `method` 是创建请求时使用的方法
method: 'get', // default
// `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。
// 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
baseURL: 'https://some-domain.com/api/',
// `transformRequest` 允许在向服务器发送前,修改请求数据
// 只能用在 'PUT', 'POST' 和 'PATCH' 这几个请求方法
// 后面数组中的函数必须返回一个字符串,或 ArrayBuffer,或 Stream
transformRequest: [function (data, headers) {
// 对 data 进行任意转换处理
return data;
}],
// `transformResponse` 在传递给 then/catch 前,允许修改响应数据
transformResponse: [function (data) {
// 对 data 进行任意转换处理
return data;
}],
// `headers` 是即将被发送的自定义请求头
headers: {'X-Requested-With': 'XMLHttpRequest'},
// `params` 是即将与请求一起发送的 URL 参数
// 必须是一个无格式对象(plain object)或 URLSearchParams 对象
params: {
ID: 12345
},
// `paramsSerializer` 是一个负责 `params` 序列化的函数
// (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
paramsSerializer: function(params) {
return Qs.stringify(params, {arrayFormat: 'brackets'})
},
// `data` 是作为请求主体被发送的数据
// 只适用于这些请求方法 'PUT', 'POST', 和 'PATCH'
// 在没有设置 `transformRequest` 时,必须是以下类型之一:
// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
// - 浏览器专属:FormData, File, Blob
// - Node 专属: Stream
data: {
firstName: 'Fred'
},
// `timeout` 指定请求超时的毫秒数(0 表示无超时时间)
// 如果请求话费了超过 `timeout` 的时间,请求将被中断
timeout: 1000,
// `withCredentials` 表示跨域请求时是否需要使用凭证
withCredentials: false, // default
// `adapter` 允许自定义处理请求,以使测试更轻松
// 返回一个 promise 并应用一个有效的响应 (查阅 [response docs](#response-api)).
adapter: function (config) {
/* ... */
},
// `auth` 表示应该使用 HTTP 基础验证,并提供凭据
// 这将设置一个 `Authorization` 头,覆写掉现有的任意使用 `headers` 设置的自定义 `Authorization`头
auth: {
username: 'janedoe',
password: 's00pers3cret'
},
// `responseType` 表示服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
responseType: 'json', // default
// `responseEncoding` indicates encoding to use for decoding responses
// Note: Ignored for `responseType` of 'stream' or client-side requests
responseEncoding: 'utf8', // default
// `xsrfCookieName` 是用作 xsrf token 的值的cookie的名称
xsrfCookieName: 'XSRF-TOKEN', // default
// `xsrfHeaderName` is the name of the http header that carries the xsrf token value
xsrfHeaderName: 'X-XSRF-TOKEN', // default
// `onUploadProgress` 允许为上传处理进度事件
onUploadProgress: function (progressEvent) {
// Do whatever you want with the native progress event
},
// `onDownloadProgress` 允许为下载处理进度事件
onDownloadProgress: function (progressEvent) {
// 对原生进度事件的处理
},
// `maxContentLength` 定义允许的响应内容的最大尺寸
maxContentLength: 2000,
// `validateStatus` 定义对于给定的HTTP 响应状态码是 resolve 或 reject promise 。如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`),promise 将被 resolve; 否则,promise 将被 rejecte
validateStatus: function (status) {
return status >= 200 && status < 300; // default
},
// `maxRedirects` 定义在 node.js 中 follow 的最大重定向数目
// 如果设置为0,将不会 follow 任何重定向
maxRedirects: 5, // default
// `socketPath` defines a UNIX Socket to be used in node.js.
// e.g. '/var/run/docker.sock' to send requests to the docker daemon.
// Only either `socketPath` or `proxy` can be specified.
// If both are specified, `socketPath` is used.
socketPath: null, // default
// `httpAgent` 和 `httpsAgent` 分别在 node.js 中用于定义在执行 http 和 https 时使用的自定义代理。允许像这样配置选项:
// `keepAlive` 默认没有启用
httpAgent: new http.Agent({ keepAlive: true }),
httpsAgent: new https.Agent({ keepAlive: true }),
// 'proxy' 定义代理服务器的主机名称和端口
// `auth` 表示 HTTP 基础验证应当用于连接代理,并提供凭据
// 这将会设置一个 `Proxy-Authorization` 头,覆写掉已有的通过使用 `header` 设置的自定义 `Proxy-Authorization` 头。
proxy: {
host: '127.0.0.1',
port: 9000,
auth: {
username: 'mikeymike',
password: 'rapunz3l'
}
},
// `cancelToken` 指定用于取消请求的 cancel token
// (查看后面的 Cancellation 这节了解更多)
cancelToken: new CancelToken(function (cancel) {
})
}
响应结构
某个请求的 then 响应包含以下信息:
{
// `data` 由服务器提供的响应
data: {},
// `status` 来自服务器响应的 HTTP 状态码
status: 200,
// `statusText` 来自服务器响应的 HTTP 状态信息
statusText: 'OK',
// `headers` 服务器响应的头
headers: {},
// `config` 是为请求提供的配置信息
config: {},
// 'request'
// `request` is the request that generated this response
// It is the last ClientRequest instance in node.js (in redirects)
// and an XMLHttpRequest instance the browser
request: {}
}
// 实例:
axios.get('/user/12345')
.then(function(response) {
console.log(response.data);
console.log(response.status);
console.log(response.statusText);
console.log(response.headers);
console.log(response.config);
});
错误处理
某个请求的 catch 响应包含以下信息:
axios.get('/user/12345')
.catch(function (error) {
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.log(error.request);
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message);
}
console.log(error.config);
});
使用 application/x-www-form-urlencoded format
默认情况下,axios将JavaScript对象序列化为JSON。要以application / x-www-form-urlencoded格式发送数据可以:
// 浏览器中
const qs = require('qs');
axios.post('/foo', qs.stringify({ 'bar': 123 }));
// Node.js中
const querystring = require('querystring');
axios.post('http://something.com/', querystring.stringify({ foo: 'bar' }));
其他API和功能特性
处理并发请求的助手函数: axios.all(iterable) 和 axios.spread(callback) ;
创建一个 axios 实例: axios.create([config]) ,然后可以使用该实例的.post等方法;
在请求或响应被 then 或 catch 处理前使用拦截器拦截它们: axios.interceptors.request.use() 和 axios.interceptors.response.use() ;
使用 cancel token 取消请求;
Element
Element,一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库。
可以通过 unpkg.com/element-ui 获取到最新版本的资源,在页面上引入 js 和 css 文件即可开始使用:
推荐使用 npm 的方式安装,它能更好地和 webpack 打包工具配合使用: npm i element-ui -S
。
完整引入 Element :
import Vue from 'vue';
import ElementUI from 'element-ui';
// 样式文件需要单独引入
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';
Vue.use(ElementUI);
new Vue({
el: '#app',
render: h => h(App)
});
借助 babel-plugin-component ,我们可以只引入需要的组件,以达到减小项目体积的目的。
在引入 Element 时,可以传入一个全局配置对象。
import Vue from 'vue';
import Element from 'element-ui';
// size 用于改变组件的默认尺寸,zIndex 设置弹框的初始 z-index(默认值:2000)
Vue.use(Element, { size: 'small', zIndex: 3000 });
主题定制的方法有:使用在线主题编辑器,在项目中改变 SCSS 变量,使用主题生成工具来安装自定义主题。 具体参见:https://element.eleme.cn/#/zh-CN/component/custom-theme。
Element 内应用在部分组件的过渡动画,也可以直接使用。 具体参见:https://element.eleme.cn/#/zh-CN/component/transition
Element UI 的组件在以下网页查找,注意使用“搜索文档”功能时不能以标签名的代码作为目标去搜索。 参见:https://element.eleme.cn/#/zh-CN/component/layout
vue-element-admin
vue-element-admin 是一个后台前端解决方案,它基于 vue 和 element-ui实现。它使用了最新的前端技术栈(vue/vuex/vue-router/element等),内置了 i18 国际化解决方案,动态路由,权限验证,提炼了典型的业务模型,提供了丰富的功能组件,它可以帮助你快速搭建企业级中后台产品原型。
可以把 vue-element-admin 当做工具箱或者集成方案仓库,在 vue-admin-template 的基础上进行二次开发,想要什么功能或者组件就去 vue-element-admin 那里复制过来。
element-ui 官方对自己的定位是桌面端后台框架,而且对于管理后台这种重交互的项目来说是不能通过简单的适配来满足桌面端和移动端两端不同的交互,所以 vue-element-admin 项目也不会适配移动端。
介绍
模块组成
集成方案: vue-element-admin
基础模板: vue-admin-template
桌面终端: electron-vue-admin
Typescript 版: vue-typescript-admin-template (鸣谢: @Armour)
Others: awesome-project
功能介绍
登录 / 注销
权限验证
多环境发布
全局功能
国际化多语言
多种动态换肤
动态侧边栏(支持多级路由嵌套)
动态面包屑
快捷导航(标签页)
Svg Sprite 图标
本地/后端 mock 数据
Screenfull全屏
自适应收缩侧边栏
编辑器
Excel
导出excel
导入excel
前端可视化excel
导出zip
表格
错误页面
組件
头像上传
返回顶部
拖拽Dialog
拖拽Select
拖拽看板
列表拖拽
SplitPane
Dropzone
Sticky
CountTo
综合实例
错误日志
Dashboard
引导页
ECharts 图表
Clipboard(剪贴复制)
Markdown2html
项目的目录结构
├── build # 构建相关
├── mock # 项目mock 模拟数据
├── plop-templates # 基本模板
├── public # 静态资源
│ │── favicon.ico # favicon图标
│ └── index.html # html模板
├── src # 源代码
│ ├── api # 所有请求
│ ├── assets # 主题 字体等静态资源
│ ├── components # 全局公用组件
│ ├── directive # 全局指令
│ ├── filters # 全局 filter
│ ├── icons # 项目所有 svg icons
│ ├── lang # 国际化 language
│ ├── layout # 全局 layout
│ ├── router # 路由
│ ├── store # 全局 store管理
│ ├── styles # 全局样式
│ ├── utils # 全局公用方法
│ ├── vendor # 公用vendor
│ ├── views # views 所有页面
│ ├── App.vue # 入口页面
│ ├── main.js # 入口文件 加载组件 初始化等
│ └── permission.js # 权限管理
├── tests # 测试
├── .env.xxx # 环境变量配置
├── .eslintrc.js # eslint 配置项
├── .babelrc # babel-loader 配置
├── .travis.yml # 自动化CI配置
├── vue.config.js # vue-cli 配置
├── postcss.config.js # postcss 配置
└── package.json # package.json
安装
# 克隆项目
git clone https://github.com/PanJiaChen/vue-element-admin.git
# 进入项目目录
cd vue-element-admin
# 安装依赖
npm install
# 建议不要用 cnpm 安装 会有各种诡异的bug 可以通过如下操作解决 npm 下载速度慢的问题
npm install --registry=https://registry.npm.taobao.org
# 本地开发 启动项目,启动完成后会自动打开浏览器访问 http://localhost:9527
npm run dev
中文
在v4.1.0+版本之后,默认 master 将不再提供国际化。如果你有国际化需求的请使用 i18n 分支,它与 master 同步更新。
修改 @/lang/index.js 文件,将默认语言由 en 改为 zh。
布局
Layout 对应代码:@/layout
app-main(主体区域)对应代码:@/layout/components/AppMain
这里在 app-main 外部包了一层 keep-alive 主要是为了缓存 的,配合页面的 tabs-view 标签导航使用。
路由和侧边栏
本项目侧边栏和路由是绑定在一起的,只要在 @/router/index.js 下面配置对应的路由,侧边栏就能动态的生成了。
配置路由时的配置项:
//当设置 true 的时候该路由不会再侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1
hidden: true // (默认 false)
//当设置 noRedirect 的时候该路由在面包屑导航中不可被点击
redirect: 'noRedirect'
//当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面
//只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面
//若你想不管路由下面的 children 声明的个数都显示你的根路由
//你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由
alwaysShow: true
name: 'router-name' //设定路由的名字,一定要填写不然使用时会出现各种问题
meta: {
roles: ['admin', 'editor'] //设置该路由进入的权限,支持多个权限叠加
title: 'title' //设置该路由在侧边栏和面包屑中展示的名字
icon: 'svg-name' //设置该路由的图标
noCache: true //如果设置为true,则不会被 缓存(默认 false)
breadcrumb: false // 如果设置为false,则不会在breadcrumb面包屑中显示
}
路由配置的完整示例:
{
// 你也可以在侧边栏中配置一个外链,只要你在 path 中填写了合法的 url 路径,当你点击侧边栏的时候就会帮你新开这个页面。
path: '/permission',
component: Layout,
redirect: '/permission/index', //重定向地址,在面包屑中点击会重定向去的地址
hidden: true, // 不在侧边栏线上
alwaysShow: true, //一直显示根路由
meta: { roles: ['admin','editor'] }, //你可以在根路由设置权限,这样它下面所以的子路由都继承了这个权限
children: [{
path: 'index',
component: ()=>import('permission/index'),
name: 'permission',
meta: {
title: 'permission',
icon: 'lock', //图标
role: ['admin','editor'], //或者你可以给每一个子路由设置自己的权限
noCache: true // 不会被 缓存
}
}]
}
你可以在Sidebar/index.vue中设置unique-opened来控制侧边栏,是否只保持一个子菜单的展开。
原则上有多少级路由嵌套就需要多少个。如有三级路由嵌套的情况下,不要忘记还要手动在二级目录的根文件下添加一个 。
本项目中也封装了一个面包屑导航,它也是和路由是绑定在一起的,是通过 watch $route 变化动态生成的。
权限验证
该项目中权限的实现方式是:通过获取当前用户的权限去比对路由表,生成当前用户具的权限可访问的路由表,通过 router.addRoutes 动态挂载到 router 上。
现在路由层面权限的控制代码都在 @/permission.js 中。
可以使用指令权限 v-permission
来简单快速的实现按钮级别的权限判断。实例:
admin
editor
Both admin or editor can see this
可以使用全局权限判断函数 checkPermission() 。实例:
Admin can see this
Editor can see this
Both admin or editor can see this
快捷导航(标签栏导航)
由于目前 keep-alive 和 router-view 是强耦合的, keep-alive 的 include 默认是优先匹配组件的 name ,所以在编写路由 router 和路由对应的 view component 的时候一定要确保 两者的 name 是完全一致的。
当在声明路由时添加了 Affix 属性,则当前tag会被固定在 tags-view中(不可被删除)。实例:
{
path: '',
component: Layout,
redirect: 'dashboard',
children: [
{
path: 'dashboard',
component: () => import('@/views/dashboard/index'),
name: 'Dashboard',
meta: {
title: 'dashboard',
icon: 'dashboard',
noCache: true,
affix: true
}
}
]
}
新增一个页面
在 @/router/index.js 中增加你需要添加的路由。
在 @/views 文件下 创建对应的文件夹,一般性一个路由对应一个文件(该模块下的功能组件或者方法就建议在本文件夹下创建一个utils或components文件夹,各个功能模块维护自己的utils或components组件)。
在 @/api 文件夹下创建本模块对应的 api 服务。
新增组件:在全局的 @/components 只会写一些全局的组件;每个页面或者模块特定的业务组件则会写在当前 views 下面。
新增样式:全局的 @/style 放置一下全局公用的样式,每一个页面的样式就写在当前 views下面,请记住加上scoped 或者命名空间,避免造成全局的样式污染。
样式
CSS 文件中的选择器是全局生效的,存在全局污染问题。vue 提供了 scoped 可以很方便的解决这个问题:只要加上