一、Vue.js的有关概念
Vue.js
(读音 /vjuː/,类似于view
) 是一套用于构建用户界面的渐进式(Progressive
)框架。与其它大型框架不同的是,Vue
被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
简单小巧的核心,渐进式技术栈,足以应付任何规模的应用。简单小巧是指Vue.js
压缩后大小仅有17KB。所谓渐进式,就是您可以(step by step
)一步一步、有阶段性地来使用Vue .j s ,不必一开始就使用所有的东西。
使用Vue.js
可以让Web
开发变得简单,它提供了现代Web
开发中常见的高级功能:
- 解辑视图与数据;
- 可复用的组件;
- 前端路由;
- 状态管理;
- 虚拟DOM。
假设您已了解关于 HTML、CSS 和 JavaScript 的中级水平的知识,就可以开始学习了。如果您刚开始学习前端开发,将框架作为你的第一步可能不是最好的主意——掌握好基础知识再来吧!
1.1 安装
① 兼容性:
Vue 不支持 IE8 及以下版本,因为 Vue 使用了 IE8 无法模拟的 ECMAScript 5 特性。但它支持所有兼容 ECMAScript 5 的浏览器。
② 直接用 <script>
引入:
直接下载并用 <script>
标签引入,Vue
会被注册为一个全局变量。
对于制作原型或学习,可以这样使用最新版本:
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
对于生产环境,推荐链接到一个明确的版本号和构建文件,以避免新版本造成的不可预期的破坏:
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
如果使用原生 ES Modules,这里也有一个兼容 ES Module 的构建文件:
<script type="module">
import Vue from 'https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.esm.browser.js '
</script>
可以在 cdn.jsdelivr.net/npm/vue 浏览 NPM 包的源代码。
Vue 也可以在 unpkg 和 cdnjs 上获取 (cdnjs 的版本更新可能略滞后)。
请确认了解不同构建版本并在你发布的站点中使用生产环境版本,把 vue.js 换成 vue.min.js。这是一个更小的构建,可以带来比开发环境下更快的速度体验。
③ NPM
在用 Vue
构建大型应用时推荐使用 NPM 安装。NPM
能很好地和诸如 webpack 或 Browserify 模块打包器配合使用。同时 Vue
也提供配套工具来开发单文件组件 。
$ npm install vue # 最新稳定版
④ 命令行工具 (CLI)
Vue
提供了一个官方的 CLI,为单页面应用 (SPA
) 快速搭建繁杂的脚手架。它为现代前端工作流提供了 开箱即用
(batteries-included) 的构建设置。只需要几分钟的时间就可以运行起来并带有热重载、保存时 lint
校验,以及生产环境可用的构建版本。更多详情可查阅 Vue CLI 的文档。
Vue CLI
是用于快速Vue.js
开发的完整系统,它提供:
- 交互项目脚手架通过
@vue/cli
实现; - 零配置快速原型制作通过
@vue/cli + @vue/cli-service-global
实现; - 运行时依赖项通过
@vue/cli-service
实现,这个是:可升级;建立在webpack之上,具有合理的默认值;可通过项目内配置文件进行配置; - 丰富的官方插件集合,集成了前端生态系统中的最佳工具;
- 完整的图形用户界面,用于创建和管理Vue.js项目。
Vue CLI
的目标是成为Vue
生态系统的标准工具基线。 它可以确保各种构建工具与合理的默认设置一起顺利运行,因此可以专注于编写应用程序,而不必花费大量时间进行配置。 同时,它仍然可以灵活地调整每个工具的配置,而无需弹出。
Vue CLI
有几个移动的部分,其中包含许多单独发布的软件包。
CLI
CLI
(@vue/cli
)是全局安装的npm软件包,并在终端中提供vue
命令。 它提供了通过vue create
快速搭建新项目的能力,或通过vue serve
立即为新创意制作原型的功能。 还可以通过vue ui
使用图形用户界面来管理项目。
安装:
推荐先下淘宝镜像:npm install -g cnpm --registry=https://registry.npm.taobao.org
//下好了淘宝镜像之后下面有npm的代码都可以换成cnpm,下载速度会加快
npm install -g @vue/cli
创建一个项目:
vue create mypro
# 或
vue ui
CLI Service
CLI Service
(@vue/cli-service
)是开发依赖项。 这是一个安装在本地的由@vue/cli
创建的每个项目中的npm
软件包。
CLI Plugins
CLI Plugins
是npm软件包,可为Vue CLI项目提供可选功能,例如Babel / TypeScript编译,ESLint集成,单元测试和端到端测试。
1.2 M V V M 模式
Vue.js
在设计上使用MVVM
模式 , MVVM是Model-View-ViewModel的简写,即模型-视图-视图模型。【模型】指的是后端传递来的数据;【视图】指的是所展示的页面;【视图模型】为MVVM
模式的核心,它是连接View和Model的桥梁。它有两个方向:一是将【模型】转化成【视图】,即将后端传递的数据转化成所展示的页面。实现的方式是:数据绑定
。二是将【视图】转化成【模型】,即将所展示的页面转化成后端需要的数据。实现的方式是:DOM 事件监听。这两个方向所实现的即为数据的双向绑定。
在MVVM
的模式下View
和Model
是不能直接通信的,它们通过ViewModel
来通信。ViewModel通常要实现一个observer
观察者,当Model
数据发生变化,ViewModel
能够监听到数据的这种变化,然后通知到对应的View
做自动更新。而当用户操作View
,ViewModel也能监听到View
的变化,然后通知Model
做数据改动,这实际上就实现了数据的双向绑定。
MVVM
模式是由经典的软件架构MVC
衍生来的。当View
(视图层)变化时,会由ViewModel
(视图模型)监听到并自动更新到Model
(模型),反之亦然。View
与ViewModel
和 ViewModel
与Model
之间通过双向绑定(data-binding )建立联系。如图1所示:
图 1. M V V M关系
Vue
就是基于MVVM模式实现的一套框架。在Vue中,Model
指的是JavaScript中的数据
,如对象、数组
等等;View
指的是页面视图
;ViewModel
指的是Vue的实例化对象
。
Vue.js操作DOM:
① 在清单1中实现ES5操作DOM
清单 1. ECMAScript 5操作DOM
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ES5操作DOM</title>
</head>
<body>
<div id="app"></div>
<script>
var app = document.getElementById("app")
var showBtn = true;
function handleButtonClick() {
if(showBtn) {
console.log('已点击了我!')
var hone = document.createElement("h1");
hone.innerHTML = '已点击了我!';
app.appendChild(hone);
showBtn = false;
}
}
var btn = document.createElement("button");
btn.innerHTML = "点击我";
btn.onclick = handleButtonClick;
app.appendChild(btn);
</script>
</body>
</html>
清单1中这段代码不难理解,操作的内容也不复杂,不过这样让我们的视图代码和业务逻辑紧藕合在一起,随着功能不断增加,直接操作DOM 会使得代码越来越难以维护。
② 在清单2中实现Vue.js操作DOM
Vue.js 通过MVVM 的模式拆分为视图
与数据
两部分,并将其分离。因此,只要关注数据
即可, DOM的事情Vue 会自动搞定。上面的示例清单1用Vue.js 可以改写为清单2:
清单 2. Vue.js操作DOM
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Vue操作DOM</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app">
<button v-if="showBtn" v-on:click="handleClick">点击我</button>
<h1>{{clickme}}</h1>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
showBtn: true,
clickme:''
},
methods: {
handleClick: function() {
console.log('已点击了我!');
this.clickme = '已点击了我';
}
}
})
</script>
</body>
</html>
清单 2中的代码,当执行 new Vue() 时,Vue 就进入了初始化阶段,一方面,Vue 会遍历 data 选项中的属性,Observer
数据监听器,把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用Object.defineProperty()方法把这些属性全部转成setter、getter方法。当data中对象的某个属性被访问时,则会调用getter方法,当data中对象的属性被改变时,则会调用setter方法。实现数据变化监听功能;另一方面,Vue 的指令编译器Compile
对元素节点的指令进行解析,替换模板数据,初始化视图。并使得订阅者Watcher
绑定对应的更新函数Updater
以更新视图。 此时Watcher
会将自己添加到消息订阅器中(Dep
),初始化完毕。
当数据发生变化时,Observer
中的 setter 方法被触发,setter 会立即调用Dep.notify()
,Dep 开始遍历所有的订阅者,并调用订阅者的 update
方法,订阅者收到通知后对视图进行相应的更新。
其中的el
用于外部的模板template
或者将HTML作为模板编译。用于指定一个页面中己存在的DOM 元素来挂载Vue实例。
图 2. Vue执行流程
1.3 传统的前端开发模式与Vue.js的开发模式的比较
如今的前端开发己不再是写个HTML 和css 那样简单了,新的概念不断涌现,已有像ECMAScript 6
、Node.js
、NPM
、前端工程化
等。它们正在不断优化现有的开发模式,改变传统的编程思想。
曾经以jQuery框架技术为核心的技术栈被许多商业项目用于生产环境:
jQuery + RequireJS ( SeaJS ) + artTemplate ( doT ) + Gulp ( Grunt)
这套技术栈能兼容绝大部分浏览器,有的的客户很可能还在用IE 8
及以下浏览器;使用RequireJS 或SeaJS 进行模块化开发可以解决代码依赖混乱的问题,同时便于维护及团队协作;使用轻量级的前端模板(如doT )可以将数据与HTML模板分离;最后,使用自动化构建工具(如Gulp )可以合并压缩代码,如果采用写Less
、Sass
以及现在流行的ES 6
,也可以进行预编译。
由于以jQuery框架技术为核心的技术栈的简单、高效、实用, 至今仍有不少开发者在使用,这样一套前端解决方案就称为传统前端开发模式
。其核心是直接操作DOM的思想,jQuery是使用选择器($
)选取DOM对象,对其进行赋值、取值、事件绑定等操作,其实和原生的HTML的区别只在于可以更方便的选取和操作DOM对象,而数据和界面是在一起的。
不过随着项目的扩大和时间的推移,出现了更复杂的业务场景,比如SPA (单页面富应用〉、组件解耦等。为了提升开发效率,降低维护成本,传统的前端开发模式己不能完全满足项目的需求,这时就出现了如Vue.js
、Angular
、React
等框架技术为代表的现代Web前端开发模式
。
Vue.js
是一个渐进式的JavaScript框架,根据项目需求,可选择从不同的维度来使用之。如果开发简单的HTML5页面应用,可以通过<script>
直接加载Vue.js
独立文件,并不需要jQuery
等另外的库文件 :
<!--自动识别最新稳定版本的Vue.j s -->
<script src = "https://unpkg.com/vue/dist/vue.min.js"></script>
<!--指定某个具体版本的Vue.j s -->
<script src = "https://unpkg.com/vue@2.6.11/dist/vue.min.js"></script>
引入Vue.js
框架后,在页面元素标记<body>
的底部使用new Vue()
的方式创建一个实例,其表示的是ViewModel
,它将数据Model
和界面视图View
完全分离开来了,对数据进行操作不再需要引用相应的DOM对象,可以说数据和视图是分离的,他们通过Vue对象
实现相互的绑定。这就是Vue.js
最基本的开发模式,即MVVM模式
。清单2中给出Vue
实现完整代码,而清单3给出jQuery
实现完整代码:
清单 2. Vue 示例:用户列表展示
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Vue 示例:展示用户列表</title>
</head>
<body>
<div id="app">
<ul>
<li v-for="u in users">{{u.name}}</li>
</ul>
</div>
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
users: [
{name : '张三' },
{name : '李四' },
{name : '王麻子' }
]
}
})
</script>
</body>
</html>
清单 3. jQuery 示例:用户列表展示
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>jQuery 示例:展示用户列表</title>
</head>
<body>
<div id="app">
<ul>
</ul>
</div>
<script src="https://code.jquery.com/jquery-3.5.0.min.js"></script>
<script>
$(document).ready(function() {
$("#app").children("ul").append("<li>张三</li>")
$("#app").children("ul").append("<li>李四</li>")
$("#app").children("ul").append("<li>王麻子</li>")
});
</script>
</body>
</html>
1.4 声明式渲染
Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统:
<div id="app-1">
<h1>
hello {{ nameMsg }}
</h1>
</div>
var vm1 = new Vue({
el: '#app-1',
data: {
nameMsg: 'world!'
}
})
通过Vue 实例的data 选项,可以声明应用内需要双向绑定的数据,data 选项中的name 字段, 就是Vue 需要绑定的数据。而Vue实例本身也代理了data 对象里的所有属性。看起来这跟渲染一个字符串模板非常类似,但是 Vue 在背后做了大量工作。现在数据和 DOM 已经被建立了关联,所有东西都是响应式的。要怎么确认呢?在浏览器运行之后,打开浏览器的 JavaScript 控制台 (就在这个页面打开),并修改 vm1.nameMsg = '张三'
,改变其值,将看到上例相应地更新。
请注意:不再和 HTML 直接交互了。一个 Vue 应用会将其挂载到一个 DOM 元素上 (对于这个例子是 #app-1) 然后对其进行完全控制。那个 HTML 是入口,但其余都会发生在新创建的 Vue 实例内部。
除了文本插值,还可以像这样来绑定元素属性
:
<div id="app-2">
<span v-bind:title="message">
鼠标悬停几秒钟查看此处动态绑定的提示信息!
</span>
</div>
var vm2 = new Vue({
el: '#app-2',
data: {
message: '页面加载于 ' + new Date().toLocaleString()
}
})
这里遇到了一点新内容。看到的 v-bind
被称为指令。指令带有前缀 v-
,以表示它们是 Vue 提供的特殊属性
。它们会在渲染的 DOM 上应用特殊的响应式行为。在这里,该指令的意思是:“将这个元素节点的title
属性和 Vue 实例的 message
属性保持一致”。
如果再次打开浏览器的 JavaScript 控制台,输入 vm2.message = '新消息'
,就会再一次看到这个绑定了 title 属性的 HTML 已经进行了更新。
1.5 条件与循环
控制切换一个元素是否显示:
<div id="app-3">
<p v-if="seen">现在你看到我了</p>
</div>
var vm3 = new Vue({
el: '#app-3',
data: {
seen: true
}
})
继续在控制台输入 vm3.seen = false
,会发现之前显示的消息消失了。
这个例子演示了不仅可以把数据绑定到 DOM 文本或元素属性,还可以绑定到 DOM 结构,此处用到了v-if
指令。此外,Vue 也提供一个强大的过渡效果系统,可以在 Vue 插入
/ 更新
/移除
元素时自动应用过渡效果。
还有其它很多指令,每个都有特殊的功能,随着学习的逐渐深入,将会了解和掌握。再了解一个v-for
指令可以绑定数组或集合对象的数据来渲染一个项目列表:
<div id="app-4">
<ol>
<li v-for="book in books">
{{ book.text }}
</li>
</ol>
</div>
var vm4 = new Vue({
el: '#app-4',
data: {
books: [{
text: 'JavaScript程序设计'
},
{
text: 'Vue前端框架技术'
},
{
text: 'Flask后端框架技术'
}]
}
})
在控制台里,输入 vm4.books.push({ text: 'Java程序设计' })
,将会发现列表最后添加了一个新项目。
1.6 处理用户输入
为了让用户和应用进行交互,可以用 v-on
指令添加一个事件监听器
,通过它调用在 Vue 实例中定义的方法reverseMessage
:
<div id="app-5">
<p>{{ message }}</p>
<button v-on:click="reverseMessage">反转消息</button>
</div>
var vm5 = new Vue({
el: '#app-5',
data: {
message: 'Hello 张三!'
},
methods: {
reverseMessage: function () {
this.message = this.message.split('').reverse().join('')
}
}
})
注意:在 reverseMessage
方法中,更新了应用的数据状态message
,但没有触碰 DOM——所有的 DOM 操作都由 Vue 来处理,编写的代码只需要关注逻辑层面即可。
Vue 还提供了 v-model
指令,它能轻松实现表单输入
和数据状态message
之间的双向绑定。
<div id="app-6">
<p>{{ message }}</p>
<input v-model="message">
</div>
var vm6 = new Vue({
el: '#app-6',
data: {
message: 'Hello 王麻子!'
}
})
二、Vue 实例与生命周期
每个 Vue 应用都是通过构造函数Vue
创建一个Vue 的根实例,并启用多个选项开始:
var vm = new Vue({
// 选项属性
})
变量vm表示对这个Vue 实例的引用,代表了 MVVM 模型中的ViewModel,这个变量名vm
表示了 Vue 实例。当创建一个 Vue 实例时,可以传入一个包含多个选项属性的对象。使用这些选项属性来创建想要的行为
。
2.1 el选项
传入Vue构造函数的对象参数,其中必不可少的一个选项属性就是el
,其值为一个字符串形式的选择器,或者是DOM的元素对象。若为字符串,则是与css 选择器
相同的表示方式,若是DOM的元素对象,则使用document.getElementById()
或其它获取元素对象的方式。el
提供一个在页面上已存在的DOM元素作为Vue实例的挂载目标:
<div id="app"></div>
var vm = new Vue({
el: '#app'
})
// 或者
var vm = new Vue({
el: document.getElementById('app')
})
在实例挂载成功后,可以通过vm.$el
来访问该元素。Vue 提供了很多常用的实例属性与方法,都以$
开头。如果在实例化时存在这个选项,实例将立即进入编译过程,否则,需要显示调用这个vm.$mount()
手动开启编译。
提供的元素只能作为挂载点,所有的挂载元素会被Vue生成的DOM替换。因此不推荐挂载根实例到<html>
或<body>
上。如果render
函数和template
属性都不存在,挂载DOM元素的HTML会被提取出来用作模板,此时,必须使用独立构建(编译器 + 运行时)的Vue库。进一步的解释就是:如果存在render
函数或template
属性,则挂载元素会被Vue
生成的DOM
替换;否则,挂载元素所在的HTML
会被提取出来用作模版。
vue实例中的el属性
、template属性
、render函数
都是vue对象对应的HTML元素(DOM对象),它们存在时有一个优先级的顺序:el < template < render
,Vue会选择其中优先级高的作为HTML输出:
<div id="app">
<p>我是{{fname}},通过el出来的。</p>
</div>
var vm = new Vue({
el:"#app",
template: "<div><p>我是{{fname}},通过template出来的。</p></div>",
data: {
fname: '张三'
},
render:function(h){
// 不能使用“双大括号”语法 ,因为Vue只是把render函数的返回值放在HTML里,而不进行再次的绑定。
// Vue 在调用 render 方法时,会传入一个 createElement 函数作为参数,这里的 h 的实参是 createElement 函数
return h('h1', '我是'+this.fname+',通过render出来的。')
}
});
以上会使用render函数的内容,因为它的优先级高。render函数发挥了 JavaScript 最大的编程能力。
2.2 数据与方法
当一个 Vue 实例被创建时,它将data
选项属性的对象中的所有的property
特性加入到 Vue 的响应式系统中。当这些property
特性的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。通过Vue 实例的data
选项属性,可以声明应用内需要双向绑定的数据。所有会用到的数据都应该预先在data
内声明,这样不至于将数据散落在业务逻辑中。
// 数据对象
var mydata = { msg: 3 }
// 该对象被加入到一个 Vue 实例中
var vm = new Vue({
data: mydata
})
// 获得这个实例上的 property与返回源数据中对应的字段作比对。
// 它们之间默认建立了双向绑定,当修改其中任意一个时,另一个也会一起变化。
// 切换到Console:
> vm.msg == mydata.msg
< true // =>true
// 设置 property 也会影响到原始数据
> vm.msg = 5
< 5
> mydata.msg
< 5 // => 5
// ……反之亦然
> datamy.msg = 6
< 6
> vm.msg
< 6 // => 6
当这些数据改变时,视图会进行重渲染。值得注意的是只有当实例被创建时就已经存在于 data
选项中的 property 才是响应式的。也就是说如果你添加一个新的 property,例如:
> vm.myname = '张三'
那么对 myname
的改动将不会触发任何视图的更新。若是晚些时候需要一个 property,不过一开始它的值为空或不存在,则仅需要设置一些初始值。例如:
data: {
newStudent: {
name: '',
gender: '',
age: 18,
tel: ''
},
visitCount: 0,
hideCompletedStudent: false,
students: [],
error: null
}
这里有一个方法使用 Object.freeze()
,它会阻止修改现有的 property,即响应系统无法再追踪变化。
var obj = {
msg: 'hello'
}
Object.freeze(obj) //使用Object.freeze()固化obj对象
new Vue({
el: '#app',
data: obj
})
<div id="app">
<p>{{ msg }}</p>
<!-- 这里的 msg 不会更新! -->
<button v-on:click="msg = 'world'">更改</button>
</div>
除了数据 property,Vue 实例还有一些有用的实例特性与方法
。它们都有前缀 $
,以便与用户定义的 property 区分开来。例如:
var mydata = { val: 1 }
var vm = new Vue({
el: '#app',
data: mydata
})
//Console
> vm.$data === mydata // => true
< true
> vm.$el === document.getElementById('app') // => true
< true
// $watch 是一个实例方法
vm.$watch('val', function (newValue, oldValue) {
// 这个回调将在 `vm.val` 改变后调用
})
2.3 生命周期
每个Vue 实例创建时,都会经历一系列的初始化过程,同时也会调用相应的生命周期钩子,可以利用这些钩子,在合适的时机执行相应的业务逻辑。
图 2. Vue生命周期