Vue 基础
Vue 概述
vue2 中文官方文档
渐进式 JavaScript 框架
graph LR
声明式渲染-->组件系统
组件系统-->客户端路由
客户端路由-->集中式状态管理
集中式状态管理-->项目构建
特点:
- 易用:熟悉 HTML、CSS、JavaScript 知识后,可快速上手 Vue
- 灵活:在一个库和一套完整框架之间自如伸缩
- 高效:20kB 运行大小,超快虚拟 DOM
Hello Vue
| <div id="app">{{msg}}</div>
<script src="js/vue.js"></script> <script> var vm = new Vue({ el: '#app', data: { msg: 'Hello Vue' } }) </script>
|
参数 |
数据类型 |
说明 |
el |
css 选择器(String)或 DOM 元素(Element) |
元素的挂载位置 |
data |
Object |
模型数据 |
指令
指令的本质就是自定义属性。
Vue 的指令格式为:v-
开头(例如v-cloak
)
v-cloak
指令解决插值表达式存在的闪动
v-cloak
指令会保持在元素上直到关联实例结束编译。
| [v-cloak]{ display: none; }
|
| <div id="app" v-cloak></div>
|
数据绑定指令
v-text
填充文本
更新元素的文本内容。
| <div>{{msg}}</div>
<div v-text="msg"></div>
|
v-html
填充 HTML 片段
更新元素的innerHTML
。内容按普通的 HTML 插入——不会作为 Vue 模板进行编译。
在网站上动态渲染任意 HTML 是非常危险的,因为容易导致 XSS 攻击。只在可信内容上使用 v-html,永不用在用户提交的内容上。
| <div v-html="html"></div>
|
v-pre
填充原始信息
显示原始信息,跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签(花括号包裹的这些模板语法)。跳过大量没有指令的节点会加快编译。
| <div v-pre>{{ this will not be compiled }}</div>
|
v-once
只填充一次
只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。
| <span v-once>This will never change: {{msg}}</span>
<div v-once> <h1>comment</h1> <p>{{msg}}</p> </div>
<my-component v-once :comment="msg"></my-component>
<ul> <li v-for="i in list" v-once>{{i}}</li> </ul>
|
样式绑定
class
| <div v-bind:class="{active: isActive}"></div>
|
| <div v-bind:class="[activeClass, errorClass]"></div>
|
style
| <div v-bind:style="{color: activeColor, fontSize: fontSize}"></div>
|
| <div v-bind:style="[baseStyles, overridingStyles]"></div>
|
条件渲染
v-if
v-else
v-else-if
v-show
不推荐同时使用v-if
和v-for
。
当v-if
与v-for
一起使用时,v-for
具有比v-if
更高的优先级。请查阅列表渲染指南以获取详细信息。
v-if
vs v-show
v-if
是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if
也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show
就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
一般来说,v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show
较好;如果在运行时条件很少改变,则使用 v-if
较好。`
循环渲染
| v-for="(item, index) in list"
v-for="(value, name, index) in object"
|
参数说明:
- 第一个参数为循环的当前值
- 第二个参数为循环的当前 property 名称 (也就是键名)
- 第三个参数为索引
:key
建议尽可能在使用 v-for 时提供 key attribute,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。
因为它是 Vue 识别节点的一个通用机制,key 并不仅与 v-for 特别关联。后面我们将在指南中看到,它还具有其它用途。
>不要使用对象或数组之类的非基本类型值作为 v-for 的 key。请用字符串或数值类型的值。
计算属性
| <div id="app"> {{reverse}} </div>
|
| var vm = new Vue({ el: "#app", data: { msg: "Hello", } computed: { reverse: function(){ return this.msg.split('').reverse().join(""); } } })
|
计算属性与方法的区别
- 计算属性是是根据依赖进行缓存的。
- 方法不存在缓存。
监听器
用于数据变化时执行异步或开销较大(耗时)的操作。
| watch: { firstName: function(val){ this.fullName = val + this.lastName; } lastName: function(val){ this.fullName = this.firstName + val; } }
|
过滤器
语法
两种定义方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| Vue.filter('lower', function (val) { return val.charAt(0).toLowerCase() + val.slice(1); })
var vm = new Vue({ el: "#app", data: { msg: "", time: new Date() }, filters: { upper: function (val) { return val.charAt(0).toUpperCase() + val.slice(1); }, } })
|
使用时:
- 可级联操作会按顺序应用过滤器。
- 可属性值(
v-bind
)绑定.
- 可带参数。
| <input type="text" v-model="msg">
<div>首字母大写:{{msg | upper}}</div> <div>首字母小写:{{msg | lower}}</div>
|
综合例子
| <input type="text" v-model="msg">
<div>首字母大写:{{msg | upper}}</div> <div>首字母小写:{{msg | lower}}</div> <div :abc="msg | upper">属性值绑定</div>
<div>{{time | format("yyyy-MM-dd")}}</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
|
Vue.filter('lower', function (val) { return val.charAt(0).toLowerCase() + val.slice(1); }) var vm = new Vue({ el: "#app", data: { msg: "", time: new Date() }, filters: { upper: function (val) { return val.charAt(0).toUpperCase() + val.slice(1); }, format: function (val, arg) { if (arg == "yyyy-MM-dd") { return `${val.getFullYear()}-${(val.getMonth() + 1)}-${val.getDate()}`; } } } })
|
生命周期
- 挂载
beforeCreate
: 在实例初始化之后,数据观测和事件配置之前调用。
created
: 在实例创建完成洪湖被立即调用。
beforeMount
: 在挂载开始前被调用。
mounted
: 被新创建的vm.$el
替换,并挂载到实例上去之后调用。
- 更新
beforeUpdate
:数据更新时调用,发生在虚拟 DOM 打补丁之前。
updated
:数据更新时调用,发生在虚拟 DOM 打补丁之后。
- 销毁
beforeDestroy
:实例销毁之前调用。
destroyed
:实例销毁之后调用。
修改响应式数据
| Vue.set(vm.item, indexOfItem, newValue);
vm.$set(vm.item, indexOfItem, newValue);
|
参数 |
说明 |
vm.item |
要处理的数组名称 |
indexOfItem |
要处理的数组索引 |
newValue |
要处理的数组的值 |
组件
组件化规范 Web Components
规范文档
目前并未完全被浏览器支持。
组件注册
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <!-- 全局组件 --> Vue.compoent(组件名称,{ data: 组件数据, template: 组件模版内容 }) <!-- 局部组件 只能在注册它的父组件中使用 --> new Vue({ el: "#app", components: { 'component-a': { data: 组件数据, template: 组件模版内容 }, 'component-b': { data: 组件数据, template: 组件模版内容 } } })
|
注意事项:
data
必须是一个函数返回的对象。
- 组件模版内容必须是单个根元素
- 组件模版内容可以是模板字符串(需要浏览器支持 ES6 语法)
组件命名方式
短横线方式
| Vue.component('my-component',{})
|
驼峰方式(该命名放式只能在字符串的模板里使用,否则得转换成短横线方式再使用)
| Vue.component('MyComponent',{})
|
组件之间的数据交互
父组件向子组件传值
- 组件内部通过
props
接收传递过来的值。
| Vue.component('menu-item',{ props: ['title'], template: '<div>{{ title }}</div>' })
|
- 父组件通过属性将值传给子组件。
| <menu-item title="来自父组件的数据"></menu-item> <menu-item :title="title"></menu-item>
|
- props 属性名规则
- 在
props
中使用驼峰形式,在模版中需要使用短横线的形式
- 在字符串的模版中可以不用转换成短横线的形式。
| Vue.component('menu-item',{ props: ['menuTitle'], template: '<div>{{ menuTitle }}</div>' }) <!-- 在html中是短横线方式的 --> <menu-item menu-title="nihao"></menu-item>
|
- props 属性值类型
- 字符串
String
- 数值
Number
- 布尔值
Boolean
- 数组
Array
- 对象
Object
| <menu-item :num1="12" num2="12"></menu-item>
Vue.component('menu-item', { props: ["num1", "num2"], template: `<div> <div>{{ typeof num1}}</div> <div>{{ typeof num2}}</div> </div>`, })
// 输出 // Number // String
|
*注意区分v-bind:
的属性与 html 自定义属性的区别。自定义属性的值都是String
类型。
子组件向父组件传值
- 子组件通过自定义事件向父组件传递信息
| <button v-on:click="$emit("enlarge-text")"> 扩大字体 </button>
|
- 父组件监听子组件的事件
| <menu-item v-on:enlarge-text="fontSize += 0.1"></menu-item>
|
- 子组件通过自定义事件向父组件传递信息
| <button v-on:click="$emit(enlarge-text, 0.1)">扩大字体</button>
|
- 父组件监听子组件的事件
| <menu-item v-on:elarge-text="fontSize += $event"></menu-item>
|
*$event
的名字是固定的
非父子组件之间的传值
- 单独的是中心管理组件间的通信
- 监听事件与销毁事件
| eventHub.$on('add-todo', addTodo)
eventHub.$off(‘add-todo’)
|
- 触发事件
| eventHhub.$emit('add-todo', id)
|
组件插槽
一般用法
- 在组件模板中添加
slot
(里面可以填写默认内容)
| Vue.component('alert-box', { template: `<div> <strong>Error:</strong> <slot>默认内容</slot> </div>`, })
|
- 在使用组件标签中添加内容
| <alert-box>有bug发生</alert-box> <alert-box></alert-box>
|
具名插槽
- 插槽定义
| <div> <header> <slot name="header"></slot> </header> <main> <slot></slot> </main> <footer> <slot name="footer"></slot> </footer> </div>
|
- 插槽使用
| <h1 slot="header">标题内容</h1> <p>主要内容1</p> <p>主要内容2</p> <p slot="footer">底部内容</p>
|
*使用<template>
标签可以插入多个标签到一个插槽位置。
| <template slot="header"> <p>标题信息1</p> <p>标题信息2</p> </template> <p>主要内容1</p> <p>主要内容2</p> <template slot="footer"> <p>标题信息1</p> <p>标题信息2</p> </template>
|
作用域插槽
作用:父组件对子组件的内容进行加工处理。
插槽声明:
| Vue.component('fruit-list', { props: ['list'], template: `<ul> <li v-for="item in list" :key="item.id"> <slot :info="item">{{item.name}}</slot> </li> </ul>`, })
|
插槽使用:
| <fruit-list :list="list"> <template slot-scope="slotProps"> <strong>{{slotProps.info.name}}</strong> </template> </fruit-list>
|
其中slot-scope
为获取插槽属性列表,上面代码将列表命名为slotProps
,使用列表获取插槽绑定的info
属性即循环的内的item
。