Vue介绍
Webpack+Vue+vue-Router前端工程搭建
1 Vue是什么
1.1 定义
- Vue 是一套用于构建用户界面的渐进式框架
- 使用Vue框架,可以完全在浏览器端渲染页面,服务端只提供数据
- 使用Vue框架可以非常方便的构建 单页面应用 (SPA)
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式JavaScript框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,方便与第三方库或既有项目整合。
1.2 关于作者
- 国人 尤雨溪
- 百科介绍: https://baike.baidu.com/item/%E5%B0%A4%E9%9B%A8%E6%BA%AA/2281470?fr=aladdin
- 微博: https://weibo.com/arttechdesign?is_hot=1#1528176039582
1.3 相关网站
- 官方网站: https://cn.vuejs.org/
- GitHub: https://github.com/vuejs/vue
2 前端框架
2.1 三足鼎立
- React
- Angular
- Vue
2.2 Angular、Vue、React的区别
Vue与React
- React与Vue 都采用虚拟DOM
- 核心功能都在核心库中,其他类似路由这样的功能则由其他库进行处理
- React的生态系统更庞大,由ReactNative来进行混合App开发; Vue更轻量
- React由独特的JSX语法; Vue是基于传统的Web计数进行扩展(HTML、CSS、JavaScript),更容易学习
Vue与Angular
-
Angular1和Angular2以后的版本 是完全不同的两个框架; 一般Angular1被称作Angular.js, Angular之后的版本被称作 Angular
-
Vue与Angular的语法非常相似
-
Vue没有像Angular一样深入开发,只保证了基本功能。 Angular过于笨重
-
Vue的运行速度比Angular快得多
-
Angular的脏检查机制带来诸多性能问题
2.3 MVVM
- Model
- View
- ViewModel
2.4 Vue的优点
- 不存在依赖
- 轻便(25k min)
- 适用范围广(大中小型项目、PC、移动端、混合开发)
- 本土框架,社区非常活跃
- 语法简单、学习成本低
- 双向数据绑定(所见即所得)
2.5 使用框架开展一个项目的时候,需要考虑哪些方面?
1.性能
如果一个网站在性能方面存在问题,它将会损失超过一半以上的用户。
对于框架性能,你可以在网上查询到各类测试,你可以了解框架的代码结构、逻辑处理,判断是否能够满足你对性能的需求。
2.扩展性
对于一个需要长期维护的项目而言,经常会有各种各样的功能添加进来,这时扩展性就显得尤为重要,如果你在前期选择了一款满足前期的框架,但后期你需要使用某个插件来完成某个功能,或者基于什么完成一个功能,这时候你发现网上并没有检索到相关内容,内心是否充满了心塞。
3.维护性
一个项目的生命周期不是三天两天,而前端的发展则是爆炸式的。在你选择框架的时候是否考虑过官方在后续的一段时间是否会一直对框架进行更新维护?如果不确定,是否已经有了官方放弃维护后的解决方案?
4.兼容性
这里的兼容性指的不是浏览器兼容,而是框架与其他框架及工具的兼容,使用这个框架对于你的开发环境是否有影响,对于你的开发 IDE 是否有影响。
Vue.js 适用具有以下性质的项目:
- 对浏览器兼容要求不高,不需要兼容至IE6-8;
- SPA开发;
- 对性能较高要求;
- 组件化。
总的来说,如果你是一个 MVVM 框架新手,那么 Vue.js 就是你最好的进阶工具,如果你是一个已经掌握了其他 MVVM 框架的老手,那你会发现 Vue.js 更加简单轻便。
3 多页面应用和单页面应用
3.1 多页面应用(MultiPage Application,MPA)
多页面跳转刷新所有资源,每个公共资源(js、css等)需选择性重新加载,常用于 app 或 客户端等
3.2 单页面应用(SinglePage Web Application,SPA)
只有一张Web页面的应用,是一种从Web服务器加载的富客户端,单页面跳转仅刷新局部资源 ,公共资源(js、css等)仅需加载一次,常用于PC端官网、购物等网站
3.3 两者对比
单页面应用 | 多页面应用 | |
---|---|---|
组成 | 一个外壳页面和多个页面片段组成 | 多个完整页面构成 |
资源公用(css,js) | 共用,只需在外壳部分加载 | 不共用,每个页面都需要加载 |
刷新方式 | 页面局部刷新或更改 | 整页刷新 |
url 模式 | a.com/#/pagetwo a.com/#/pagetwo | a.com/pageone.html a.com/pagetwo.html |
用户体验 | 页面片段间的切换快,用户体验良好 | 页面切换加载缓慢,流畅度不够,用户体验比较差 |
转场动画 | 容易实现 | 无法实现 |
数据传递 | 容易 | 依赖 url传参、或者cookie 、localStorage等 |
搜索引擎优化(SEO) | 需要单独方案、实现较为困难、适用于追求高度支持搜索引擎的应用 | 实现方法简易 |
试用范围 | 高要求的体验度、追求界面流畅的应用 | 适用于追求高度支持搜索引擎的应用 |
开发成本 | 较高,常需借助专业的框架 | 较低 ,但页面重复代码多 |
维护成本 | 相对容易 | 相对复杂 |
4 Vue入门
4.1 安装
直接<script>
引入
下载地址
- 开发环境版本 https://vuejs.org/js/vue.js 包含完整的警告和调试模式
- 生成环境版本 https://vuejs.org/js/vue.min.js 删除了警告、进行了压缩 #####CDN
https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.min.js
https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js
# 以手动指定版本号
NPM
在用 Vue 构建大型应用时推荐使用 NPM 安装[1]。NPM 能很好地和诸如 webpack 或 Browserify 模块打包器配合使用。同时 Vue 也提供配套工具来开发单文件组件。
npm install vue
构建工具 (CLI)
npm install -g @vue/cli
vue create my-project
4.2 Vue基本演示
创建实例
var app = new Vue({
el: '#app',
})
添加数据
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
模板渲染(文本差值)
<div id="app">
</div>
或者
<div id="app" v-text="message">
</div>
####绑定属性的值
<span v-bind:title="message">
鼠标悬停几秒钟查看此处动态绑定的提示信息!
</span>
双向数据绑定
<p></p>
<input v-model="message">
事件
<div id="app">
<p></p>
<button v-on:click="reverseMessage">逆转消息</button>
</div>
var app = new Vue({
el: '#app-5',
data: {
message: 'Hello Vue.js!'
},
methods: {
reverseMessage: function () {
this.message = this.message.split('').reverse().join('')
}
}
})
循环
<ol>
<li v-for="todo in todos">
</li>
</ol>
条件
<p v-if="seen">现在你看到我了</p>
Vue组件化
// 定义名为 todo-item 的新组件
Vue.component('todo-item', {
template: '<li>这是个待办项</li>'
})
<ol>
<!-- 创建一个 todo-item 组件的实例 -->
<todo-item></todo-item>
</ol>
Vue实例
1 创建实例
var vm = new Vue({
// 选项
})
- 每个 Vue 应用都是通过用 Vue 函数创建一个新的 Vue 实例开始的
- 一个 Vue 应用由一个通过 new Vue 创建的根 Vue 实例,以及可选的嵌套的、可复用的组件树组成。
2 数据与方法
2.1 数据
- 当一个 Vue 实例被创建时,它向 Vue 的响应式系统中加入了其 data 对象中能找到的所有的属性。当这些属性的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。
- 只有当实例被创建时 data 中存在的属性才是响应式的
- 如果你知道你会在晚些时候需要一个属性,但是一开始它为空或不存在,那么你仅需要设置一些初始值
2.2 实例方法
Vue 实例还暴露了一些有用的实例属性与方法。它们都有前缀 $,以便与用户定义的属性区分开来
- vm.$el
- vm.$data
- vm.$watch(dataAttr, fn)
3 计算属性和侦听器
3.1 methods
methods用来装载可以调用的函数,你可以直接通过 Vue 实例访问这些方法,或者在指令表达式中使用。方法中的 this 自动绑定为 Vue 实例。
注意,不应该使用箭头函数来定义 methods 函数(例如 plus: () => this.a++)。理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,this.a 将是 undefined。示例代码如下。
如果你要通过对 DOM 的操作来触发这些函数,那么应该使用 v-on 对操作和事件进行绑定
var vm = new Vue({
data: { a: 1 },
methods: {
plus: function () {
this.a++
}
}
})
vm.plus()
vm.a // 2
3.2 computed 计算属性
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护,这时候应该使用计算属性
<div id="example">
</div>
<!--以下是计算属性的用法-->
<div id="example">
<p>Original message: ""</p>
<p>Computed reversed message: ""</p>
</div>
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})
3.3 watch 监听器
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的
var vm = new Vue({
data: {
question: ''
},
watch: {
// 如果 `question` 发生改变,这个函数就会运行
question: function (newQuestion, oldQuestion) {
this.answer = 'Waiting for you to stop typing...'
this.debouncedGetAnswer()
}
},
})
3.4 三者区别
它们三者都是以函数为主体,但是它们之间却各有区别。
计算属行与方法
我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖发生改变时才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。
相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。
计算属性与侦听属性
- watch擅长处理的场景:一个数据影响多个数据
- computed擅长处理的场景:一个数据受多个数据影响
4 生命周期
4.1 生命周期钩子函数
**1.beforeCreate **
在实例初始化之后,数据观测(data observer)和 event/watcher 事件配置之前被调用。
2.created
在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测(data observer)、属性和方法的运算、watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。
3.beforeMount
在挂载开始之前被调用,相关的 render 函数将首次被调用。
注意:该钩子在服务器端渲染期间不被调用。
4.mounted
el 被新创建的 vm.el替换,并挂载到实例上去之后调用该钩子。如果root实例挂载了一个文档内元素,当mounted被调用时vm.el 也在文档内。页面渲染完成后初始化的处理都可以放在这里。
注意:mounted 不会承诺所有的子组件也都一起被挂载。
5.beforeUpdate
数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。
你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。
6.updated
由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或 watcher 取而代之。
注意:updated 不会承诺所有的子组件也都一起被重绘。
7.activated
keep-alive 组件激活时调用。
8.deactivated
keep-alive 组件停用时调用。
9.beforeDestroy
实例销毁之前调用。在这一步,实例仍然完全可用。
10.destroyed
Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
4.2 生命周期图示
Vue 视图
1. 基本模板语法
1.1 插值
文本
- 数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值
v-text
指令也可以用于数据绑定,如果要更新部分的 textContent ,需要使用 插值。- 通过使用
v-once
指令,你也能执行一次性地插值,当数据改变时,插值处的内容不会更新
原始HTML
双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用 v-html 指令
你的站点上动态渲染的任意 HTML 可能会非常危险,因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值,绝不要对用户提供的内容使用插值。
1.2 指令
指令
- 指令 (Directives) 是带有 v- 前缀的特殊属性。
- 指令特性的值预期是单个 JavaScript 表达式
- 指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。
指令列表
- v-text
- v-html
- v-show
- v-if
- v-else
- v-else-if
- v-for
- v-on 绑定事件2
- v-bind 绑定属性1
- v-model 双向绑定3
- v-pre
- v-cloak
- v-once
缩写
v-bind缩写
<!-- 完整语法 -->
<a v-bind:href="url">...</a>
<!-- 缩写 -->
<a :href="url">...</a>
v-on缩写
<!-- 完整语法 -->
<a v-on:click="doSomething">...</a>
<!-- 缩写 -->
<a @click="doSomething">...</a>
2. 条件渲染与列表渲染
2.1 条件渲染
相关指令
- v-if
- v-else
- v-else-if
控制多个元素
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
复用已有元素
- Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染
- 要想每次都重新渲染,只需添加一个具有唯一值的 key 属性
v-show
<h1 v-show="ok">Hello!</h1>
与 v-if 的区别
v-if
是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。v-show
就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。- 一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。
2.2 列表渲染
遍历数组
<ul id="example-1">
<li v-for="item in items">
</li>
</ul>
v-for 还支持一个可选的第二个参数为当前项的索引
<ul id="example-2">
<li v-for="(item, index) in items">
-
</li>
</ul>
遍历对象
<!-- 只取值 -->
<li v-for="value in object">
</li>
<!-- 值、属性名 -->
<div v-for="(value, key) in object">
:
</div>
<!--值、属性名、索引-->
<div v-for="(value, key, index) in object">
. :
</div>
key
- 当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。
- 这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 的列表渲染输出。
- 为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性。因为在遍历,需要用 v-bind 来绑定动态值
- 建议尽可能在使用 v-for 时提供 key,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。
更新检测
**数组 **
由于 JavaScript 的限制,Vue 不能检测以下变动的数组:
当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue
当你修改数组的长度时,例如:vm.items.length = newLength
对象
还是由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除
方法
Vue.set()
vm.$set()
遍历数字
<div>
<span v-for="n in 10"> </span>
</div>
v-for
on <tmplate>
<ul>
<template v-for="item in items">
<li></li>
<li class="divider" role="presentation"></li>
</template>
</ul>
v-for
和 v-if
当它们处于同一节点,v-for 的优先级比 v-if 更高,这意味着 v-if 将分别重复运行于每个 v-for 循环中。
<li v-for="todo in todos" v-if="!todo.isComplete">
</li>
3 样式
3.1 绑定HTML Class
对象语法
<div v-bind:class="{ active: isActive }"></div>
<div class="static"
v-bind:class="{ active: isActive, 'text-danger': hasError }">
</div>
<div v-bind:class="classObject"></div>
数组语法
<div v-bind:class="[activeClass, errorClass]"></div>
3.2 绑定内联样式
对象语法
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
<div v-bind:style="styleObject"></div>
数组语法
数组语法可以将多个样式对象应用到同一个元素上
div v-bind:style="[baseStyles, overridingStyles]"></div>
自动添加前缀
当 v-bind:style
使用需要添加浏览器引擎前缀的 CSS 属性时,如 transform,Vue.js 会自动侦测并添加相应的前缀。
多重值
从 2.3.0 起你可以为 style 绑定中的属性提供一个包含多个值的数组,常用于提供多个带前缀的值
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
这样写只会渲染数组中最后一个被浏览器支持的值。在本例中,如果浏览器支持不带浏览器前缀的 flexbox,那么就只会渲染 display: flex。
4. 事件
4.1 事件绑定
监听事件
<div id="example-1">
<button v-on:click="counter += 1">Add 1</button>
<p>The button above has been clicked times.</p>
</div>
事件处理方法
<div id="example-2">
<!-- `greet` 是在下面定义的方法名 -->
<button v-on:click="greet">Greet</button>
</div>
内联调用方法
<div id="example-3">
<button v-on:click="say('hi')">Say hi</button>
<button v-on:click="say('what')">Say what</button>
</div>
有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event 把它传入方法
<button v-on:click="warn('Form cannot be submitted yet.', $event)">
Submit
</button>
4.2 事件修饰符
.stop
.prevent
.capture
.self
.once
.passive
<!-- 阻止单击事件继续传播 阻止事件冒泡-->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 阻止默认事件-->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
<!-- 点击事件将只会触发一次 2.1.4新增-->
<a v-on:click.once="doThis"></a>
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 2.3.0新增-->
<!-- 而不会等待 `onScroll` 完成 -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<!--.passive 修饰符尤其能够提升移动端的性能。-->
<div v-on:scroll.passive="onScroll">...</div>
4.3 按键修饰符
数字
<!-- 只有在 `keyCode` 是 13 时调用 `vm.submit()` -->
<input v-on:keyup.13="submit">
按键别名
<!-- 回车键 -->
<input v-on:keyup.enter="submit">
- .enter
- .tab
- .delete (捕获“删除”和“退格”键)
- .esc
- .space
- .up
- .down
- .left
- .right
可以通过全局 config.keyCodes 对象自定义按键修饰符别名:
// 可以使用 `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112
4.4 系统修饰键
可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器。
- .ctrl
- .alt
- .shift
- .meta
注意:在 Mac 系统键盘上,meta 对应 command 键 (⌘)。在 Windows 系统键盘 meta 对应 Windows 徽标键 (⊞)。在 Sun 操作系统键盘上,meta 对应实心宝石键 (◆)。在其他特定键盘上,尤其在 MIT 和 Lisp 机器的键盘、以及其后继产品,比如 Knight 键盘、space-cadet 键盘,meta 被标记为“META”。在 Symbolics 键盘上,meta 被标记为“META”或者“Meta”。
<!-- Alt + C -->
<input @keyup.alt.67="clear">
<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>
5 表单输入绑定
你可以用 v-model
指令在表单 <input>
及 <textarea>
元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。
v-model
会忽略所有表单元素的 value、checked、selected 特性的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data 选项中声明初始值。
5.1 基础用法
文本
<input v-model="message" placeholder="edit me">
<textarea v-model="message" placeholder="add multiple lines"></textarea>
复选框
单个复选框,绑定到布尔值:
<input type="checkbox" id="checkbox" v-model="checked">
<input
type="checkbox"
v-model="toggle"
true-value="yes"
false-value="no"
>
多个复选框,绑定到同一个数组
<div id='example'>
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<br>
<span>Checked names: </span>
</div>
单选按钮
绑定value对应的字符串
<div id="example">
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<br>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<br>
<span>Picked: </span>
</div>
选择框
单选,绑定对应所选的值
<div id="example">
<select v-model="selected">
<option disabled value="">请选择</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<span>Selected: </span>
</div>
多选时 ,绑定到一个数组
<div id="example">
<select v-model="selected" multiple style="width: 50px;">
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<br>
<span>Selected: </span>
</div>
5.2 修饰符
####.lazy
在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步
添加 lazy 修饰符,从而转变为使用 change 事件进行同步
<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="msg" >
.number
如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符
<input v-model.number="age" type="number">
.trim
自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符
<input v-model.trim="msg">
Vue组件
1 组件基础
1.1 什么是组件
组件的概念
组件(Component)是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能。在有些情况下,组件也可以表现为用 is 特性进行了扩展的原生 HTML 元素。
所有的 Vue 组件同时也都是 Vue 的实例,所以可接受相同的选项对象(除了一些根级特有的选项)并提供相同的生命周期钩子。
如何理解组件
简单理解,组件其实就是一个独立的 HTML,它的内部可能有各种结构、样式、逻辑,某些地方来说有些像 iframe,它都是在页面中引入之后展现另一个页面的内容,但实际上它与 iframe 又完全不同,iframe 是一个独立封闭的内容,而组件既是一个独立的内容,还是一个受引入页面控制的内容。
通常一个应用会以一棵嵌套的组件树的形式来组织:
例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。
为什么要使用组件
举个简单的列子,最近我的项目中有一个日历模块,多个页面都要用这个日历,而每个页面的日历都存在一些差别,如果不使用组件,我要完成这个项目,做到各个页面的日历大体一致,而部分地方存在差异,我可能就需要写几套日历代码了。
而使用组件呢?一套代码,一个标签,然后分别在不同地方引用,根据不同的需求进行差异控制即可。
<calendar></calendar>
我可以通过给 calendar 传递值实现在本页面对日历的控制,让它满足我这个页面的某些单独需求。
有人会问,你 calendar 标签是什么鬼?前面有这么一句话,组件是自定义元素。calendar 就是我自定义的元素,它就是一个组件。所以在项目中,你会发现有各种五花八门的标签名,他们就是一个个组件。
####
1.2 创建组件
注册组件
我们把创建一个组件称为注册组件,如果你把组件理解成为变量,那么注册组件你就可以理解为声明变量。我们通过 Vue.component 来注册一个全局组件
Vue.component(componentName, {
//选项
})
对于自定义组件的命名,Vue.js 不强制遵循 W3C 规则(小写,并且包含一个短杠),尽管这被认为是最佳实践。
组件的选项
-
与创建Vue示例时的选项相同(除了一些根级特有的选项)
-
一个组件的 data 选项必须是一个函数 (每个组件实例具有自己的作用域,组件复用不会互相影响)
// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me 8 times.</button>'
})
组件的使用
<div id="components-demo">
<button-counter></button-counter>
</div>
组件可以复用
<div id="components-demo">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
组件模板
每个组件模板必须只有一个根元素
模板的形式:
-
template 选项指定字符串 (模板字符串)
-
单文件组件(.vue)
-
内联模板 (不推荐)
<my-component inline-template> <div> <p>These are compiled as the component's own template.</p> <p>Not parent's transclusion content.</p> </div> </my-component>
-
X-Templates
<script type="text/x-template" id="hello-world-template"> <p>Hello hello hello</p> </script>
Vue.component('hello-world', { template: '#hello-world-template' })
全局组件和局部组件
使用 Vue.component()
注册的组件都是全局组件
我们可以定义局部组件
var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
var ComponentC = { /* ... */ }
new Vue({
el: '#app'
components: {
'component-a': ComponentA,
'component-b': ComponentB
}
})
2. 组件之间的嵌套使用和互相通信
组件设计初衷就是要配合使用的,最常见的就是形成父子组件的关系:组件 A 在它的模板中使用了组件 B。它们之间必然需要相互通信:父组件可能要给子组件下发数据,子组件则可能要将它内部发生的事情告知父组件。
每个组件的作用域都是独立的,所以在组件嵌套使用的时候子组件不能直接使用父组件中的数据。
2.1 通过Prop向子组件传递数据
基本使用
在子组件中声明 prop,然后添加一个 message
Vue.component('child', {
// 声明 props
props: ['message'],
// 就像 data 一样,prop 也可以在模板中使用
// 同样也可以在 vm 实例中通过 this.message 来使用
template: '<span></span>'
})
一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。我们能够在组件实例中访问这个值,
然后直接传入值就可以在子组件中使用 message。
<child message="hello!"></child>
####
Prop 的大小写
HTML 中的特性名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名
传入一个对象的所有属性
<blog-post v-bind="post"></blog-post>
等价于
<blog-post
v-bind:id="post.id"
v-bind:title="post.title"
></blog-post>
Prop验证
我们可以为组件的 prop 指定验证要求
Vue.component('my-component', {
props: {
// 基础的类型检查 (`null` 匹配任何类型)
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
}
}
}
})
类型列表:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbol
- 自定义的构造函数
2.2 通过事件向父级组件发送消息
on(eventName)+on(eventName)+emit(eventName) 实现通讯
在父组件中使用 on(eventName)监听事件,然后在子组件中使用on(eventName)监听事件,然后在子组件中使用emit(eventName) 触发事件,这样就能实现子组件向父组件传值。
Vue.component('button-counter', {
template: '<button v-on:click="incrementCounter"></button>',
data: function () {
return {
counter: 0
}
},
methods: {
incrementCounter: function () {
this.counter += 1
this.$emit('increment')
}
},
})
new Vue({
el: '#counter-event-example',
data: {
total: 0
},
methods: {
incrementTotal: function () {
this.total += 1
console.log('第'+this.total+'次点击')
}
}
})
<div id="counter-event-example">
<p></p>
<button-counter v-on:increment="incrementTotal"></button-counter>
<button-counter v-on:increment="incrementTotal"></button-counter>
</div>
使用事件抛出一个值
有的时候用一个事件来抛出一个特定的值是非常有用的。这时可以使用 $emit
的第二个参数来提供这个值
incrementCounter: function () {
this.counter += 1
this.$emit('increment', this.counter)
}
然后当在父级组件监听这个事件的时候,我们可以通过 $event
访问到被抛出的这个值
<button-counter v-on:increment="postFontSize + $event"></button-counter>
或者,如果这个事件处理函数是一个方法:那么这个值将会作为第一个参数传入这个方法:
<button-counter v-on:increment="incrementTotal"></button-counter>
methods: {
incrementTotal: function (enlargeAmount) {
this.postFontSize += enlargeAmount
}
}
在组件上使用 v-model
组件内input需要满足条件:
- 将其
value
特性绑定到一个名叫value
的 prop 上 - 在其
input
事件被触发时,将新的值通过自定义的input
事件抛出
Vue.component('custom-input', {
props: ['value'],
template: `
<input
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
`
})
v-model
在组件上的使用
<custom-input v-model="searchText"></custom-input>
<!-- 上面的写法 等价于 下面的写法 -->
<custom-input
v-bind:value="searchText"
v-on:input="searchText = $event"
></custom-input>
3 插槽 slot
3.1 通过插槽分发内容
Vue.component('alert-box', {
template: `
<div class="demo-alert-box">
<strong>Error!</strong>
<slot></slot>
</div>
`
})
<alert-box>
Something bad happened.
</alert-box>
·Something bad happened.· 会替换掉 slot标签
3.2 模板中多个插槽
组件模板
<div class="container">
<header>
<!-- 我们希望把页头放这里 -->
</header>
<main>
<!-- 我们希望把主要内容放这里 -->
</main>
<footer>
<!-- 我们希望把页脚放这里 -->
</footer>
</div>
调用组件
<base-layout>
<template slot="header">
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<p slot="footer">Here's some contact info</p>
</base-layout>
3.3 插槽默认内容
<button type="submit">
<slot>Submit</slot>
</button>
4. 动态组件
4.1 实现动态组件
在不同组件之间进行动态切换
<component is="组件名" class="tab"></component>
实现选项卡案例
4.2 在动态组件上使用 keep-alive
包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们
主要用于保留组件状态或避免重新渲染
<!-- 基本 -->
<keep-alive>
<component :is="view"></component>
</keep-alive>
<!-- 多个条件判断的子组件 -->
<keep-alive>
<comp-a v-if="a > 1"></comp-a>
<comp-b v-else></comp-b>
</keep-alive>
4.3 绑定组件选项对象
动态组件可以绑定 组件选项对象(有component属性的对象),而不是已注册组件名的示例
var tabs = [
{
name: 'Home',
component: {
template: '<div>Home component</div>'
}
},
{
name: 'Posts',
component: {
template: '<div>Posts component</div>'
}
},
{
name: 'Archive',
component: {
template: '<div>Archive component</div>',
}
}
]
new Vue({
el: '#dynamic-component-demo',
data: {
tabs: tabs,
currentTab: tabs[0]
}
})
<component
v-bind:is="currentTab.component"
class="tab"
></component>
5 组件的其他特性
5.1 解析 DOM 模板时的注意事项
有些 HTML 元素,诸如 <ul>、<ol>、<table> 和
<table>
<blog-post-row></blog-post-row>
</table>
上面的写法,渲染效果会不甚理想,可以采用以下写法
<table>
<tr is="blog-post-row"></tr>
</table>
需要注意的是如果我们从以下来源使用模板的话,这条限制是不存在的:
- 字符串 (例如:template: ‘…’)
- 单文件组件 (.vue)
<script type="text/x-template">
5.2 Prop的一些问题
Prop的属性名问题
HTML 中的特性名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名
如果你使用字符串模板,那么这个限制就不存在了。
非Prop属性
组件上定义的非Prop属性 会传递到 组件模板的根元素上
class 和 style 特性会非常智能,即两边的值会被合并起来
对prop重新赋值
子组件中,对prop重新赋值,会报警告
5.3 组件事件的相关问题
将原生事件绑定到组件
想要在一个组件的根元素上直接监听一个原生事件。这时,你可以使用 v-on 的 .native 修饰符
<base-input v-on:focus.native="onFocus"></base-input>
.sync 修饰符
在有些情况下,我们可能需要对一个 prop 进行“双向绑定”
推荐以 update:my-prop-name 的模式触发事件
//子组件中
this.$emit('update:title', newTitle)
<!-- 上级组件 模板中 -->
<text-document
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
></text-document>
以上写法可以换成下列写法
<text-document v-bind:title.sync="doc.title"></text-document>
5.4 官方文档-组件
深入了解组件
网址: https://cn.vuejs.org/v2/guide/components-registration.html
定时器
setInterval() 间隔指定的毫秒数不停地执行指定的代码
https://www.runoob.com/js/js-timing.html
Ajax
每30s执行一次ajax
function reloadView(){
$.ajax({
url:'${oneway}/index?event=reloadView',
type:'POST',
async:true, //或false,是否异步
success:function(result){
//eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码。
//在这里是将String转化为数组形式
var datas= eval(result);
var accessCountDiv = document.getElementById("accessCount"); //获取某一个div元素
accessCountDiv.innerHTML = ""; //将该Div元素的原有内容清空
var accessCount = datas[0]; //获取数组第一个内容
for(i=0; i<accessCount.length; i++){ //遍历该数组
var div = document.createElement("div"); //创建一个div元素
div.className = "lishi0"; //为该div元素指定class
var img = document.createElement("img"); //创建一个img元素
img.className = "lishi3-1";
img.src = "img/lishi1.png"; //为该img元素指定src属性
var p = document.createElement("p"); //创建一个p元素
p.className = "lishi3-2";
var txt = document.createTextNode(accessCount[i]); //创建一个文本内容
p.appendChild(txt); //将该文本内容插入到p元素中
div.appendChild(img); //将img元素插入到div元素中
div.appendChild(p);
accessCountDiv.appendChild(div); //将这些元素插入到获取的div元素中
}
},
error: function (XMLHttpRequest, txtStatus, errorThrown)
{
//alert(XMLHttpRequest + "<br>" + txtStatus + "<br>" + errorThrown);
}
});
}
/**
* 设置定时执行
* setTimeout(表达式,延时时间)在执行时,是在载入后延迟指定时间后,去执行一次表达式,记住,次数是一次
* setInterval(表达式,交互时间)则不一样,它从载入后,每隔指定的时间就执行一次表达式
*/
setInterval('reloadView()',15000); //每15秒刷新一次页面下边显示的数据
jQuery Ajax 操作函数
https://www.w3school.com.cn/jquery/jquery_ref_ajax.asp
jQuery 库拥有完整的 Ajax 兼容套件。其中的函数和方法允许我们在不刷新浏览器的情况下从服务器加载数据。
函数 | 描述 |
---|---|
jQuery.ajax() | 执行异步 HTTP (Ajax) 请求。 |
.ajaxComplete() | 当 Ajax 请求完成时注册要调用的处理程序。这是一个 Ajax 事件。 |
.ajaxError() | 当 Ajax 请求完成且出现错误时注册要调用的处理程序。这是一个 Ajax 事件。 |
.ajaxSend() | 在 Ajax 请求发送之前显示一条消息。 |
jQuery.ajaxSetup() | 设置将来的 Ajax 请求的默认值。 |
.ajaxStart() | 当首个 Ajax 请求完成开始时注册要调用的处理程序。这是一个 Ajax 事件。 |
.ajaxStop() | 当所有 Ajax 请求完成时注册要调用的处理程序。这是一个 Ajax 事件。 |
.ajaxSuccess() | 当 Ajax 请求成功完成时显示一条消息。 |
jQuery.get() | 使用 HTTP GET 请求从服务器加载数据。 |
jQuery.getJSON() | 使用 HTTP GET 请求从服务器加载 JSON 编码数据。 |
jQuery.getScript() | 使用 HTTP GET 请求从服务器加载 JavaScript 文件,然后执行该文件。 |
.load() | 从服务器加载数据,然后把返回到 HTML 放入匹配元素。 |
jQuery.param() | 创建数组或对象的序列化表示,适合在 URL 查询字符串或 Ajax 请求中使用。 |
jQuery.post() | 使用 HTTP POST 请求从服务器加载数据。 |
.serialize() | 将表单内容序列化为字符串。 |
.serializeArray() | 序列化表单元素,返回 JSON 数据结构数据。 |
参考文献i
cloning克隆
https://github.com/vuejs/vue-hackernews
Vue.js官网
https://cn.vuejs.org/v2/guide/#Vue-js-%E6%98%AF%E4%BB%80%E4%B9%88
vue demo 留言板
https://kenberkeley.github.io/vue-demo/dist/#!/msg/list
介绍Vue.js
http://doc.vue-js.com/v2/guide/