前言
前端开发发展历程,目的都是为了提高开发效率:
- 原生JS
- Jquery之类的类库(解决不同浏览器兼容性问题)
- 前端模板引擎(避免频繁操作DOM元素,调用前端模板引擎提供的方法自动渲染页面)
- Vue.js / Angular.js 等(直接用框架提供的指令把数据渲染到页面上,不需要进行DOM操作,提高渲染效率)
三大主流框架:主流的 MVVM 框架有 Angular, React 和 Vue.js。
双向数据绑定:通过框架提供的指令直接绑定数据,只关注数据的业务逻辑,不再操作DOM元素。
什么是 Vue.js
- Vue.js(读音 /vju/, 类似于 view),是一套构建用户界面的前端框架(视图层)。
- 借助 Weex 也可以使用 Vue 语法进行手机APP开发。
Vue框架的好处:数据绑定,指令,轻量级,插件化。
官网:https://cn.vuejs.org
框架和库的区别
- 框架:是一套完整的解决方案,对项目的侵入性较大,更换麻烦。
- 库(插件):只是某一个小功能,对项目的侵入性较小,容易更换。
MVVM设计模式
MVVM是Model-View-ViewModel的简写。它本质上是MVC的改进版。MVVM就是将其中的View的状态和行为抽象化,让我们将视图UI和业务逻辑分开。
MVVM模式和MVC模式一样,主要目的是分离视图(View)和模型(Model)。
Vue.js是一个提供了MVVM风格的双向数据绑定的JavaScript库,专注于View层。它的核心是MVVM中的VM(ViewModel)。ViewModel负责连接View和Model,保证视图和数据的一致性,这种轻量级的架构让前端开发更加高效、便捷。
传统写法:红框中的是逻辑部分,灰框中的是赋值部分(逻辑和赋值在一起)。

Vue解决的问题是:逻辑部分和视图部分进行分离。
Vue 的基本使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<!-- 1. 导入Vue的包 -->
<script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
<!-- 将来 new 的Vue实例,会控制这个 元素中的所有内容 -->
<div id="app">
<p>{{ msg }}</p>
</div>
<script>
// 2. 创建一个Vue的实例
// 当我们导入包之后,在浏览器的内存中,就多了一个 Vue 构造函数
// 注意:我们 new 出来的这个 app 对象,就是我们 MVVM中的 VM调度者
var app = new Vue({
// 指定控制区域(由 Vue 接管 id 为 app 的区域)
el: '#app', // 表示,当前我们 new 的这个 Vue 实例,要控制页面上的哪个区域
// 这里的 data 就是 MVVM中的 M,专门用来保存 每个页面的数据的
data: { // data 属性中,存放的是 el 中要用到的数据
msg: '欢迎学习Vue' // 通过 Vue 提供的指令,很方便的就能把数据渲染到页面上,程序员不再手动操作DOM元素了【前端的Vue之类的框架,不提倡我们去手动操作DOM元素了】
}
})
</script>
</body>
</html>
Vue 指令
-
{{ }}:插值表达式
-
绑定数据(渲染值)指令:
<!-- 1.插值表达式:使用 v-cloak 能够解决 "{{ msg }}" 显示闪烁的问题 -->
<p v-cloak>{{ msg }}</p>
<!-- 2.使用 v-text 指令:默认没有闪烁问题,会覆盖元素中原本的内容 -->
<p v-text="msg">内容</p>
<!-- 3.使用 v-html 指令:可以输出带 HTML 标签的文本(赋值内容为富文本) -->
<p v-html="msg">内容</p>
- 绑定属性指令
v-bind:属性名
:
<!-- 这里的 mytitle 是普通文本 -->
<input type="button" value="按钮" title="mytitle">
<!-- v-bind:绑定 title 属性,告诉这里的 mytitle 是变量,即会当JS代码去执行 -->
<!-- v-bind 指令的意思是:将这个元素节点的 title attribute 和 Vue 实例的 mytitle property 保持一致 -->
<input type="button" value="按钮" v-bind:title="mytitle">
<!-- "v-bind:" 可以缩写成 ":" -->
<input type="button" value="按钮" :title="mytitle">
<!-- v-bind 只能实现数据的单向绑定,从 M 自动绑定到 V, 无法实现数据的双向绑定 -->
- 绑定事件指令
v-on:事件名
:
<!-- 绑定点击事件 -->
<input type="button" value="按钮" v-on:click="show">
<!-- 绑定鼠标移动覆盖事件 -->
<input type="button" value="按钮" v-on:mouseover="show">
<!-- "v-on:" 可以缩写成 "@" -->
<input type="button" value="按钮" @click="show">
<!-- 方法后面可以传参数, prevent 是阻止默认行为(即不执行href的跳转) -->
<a href="" @click.prevent="delete(item.id)">删除</a>
<script>
var app = new Vue({
el: '#app',
data: {
msg: '123',
mytitle: '这是一个自己定义的title'
},
methods: { // 这个 methods属性中定义了当前Vue实例所有可用的方法
show: function () {
alert('Hello')
}
}
})
/*
document.getElementById('btn').onclick = function(){
alert('Hello')
}
*/
</script>
// 注意1:在 app 实例中,如果想要获取 data 上的数据,或者 想要调用 methods 中的 方法,必须通过 this.数据属性名 或 this.方法名 来进行访问,这里的this,就表示 我们 new 出来的 app 实例对象
// 注意2:app 实例,会监听自己身上 data 中所有数据的改变,只要数据一发生变化,就会自动把 最新的数据,从data 上同步到页面中去;【好处:程序员只需要关心数据,不需要考虑如何重新渲染DOM页面】
- 列表渲染
v-for
<!-- 遍历数组:list 是数据源数据,item 是数组的每一项 -->
<p v-for="item in list">{{ item }}</p>
<!-- 遍历数组:list 是数据源数据,item 是数组的每一项,i 是索引 -->
<p v-for="(item, i) in list">索引值:{{ i }} , 每一项:{{ item }}</p>
<!-- 数组的每一项也可以用其它名称 -->
<p v-for="obj in list">{{ obj.name }}</p>
<!-- 遍历对象 -->
<p v-for="(val, key) in obj">值:{{ val }} ;键:{{ key }}</p>
<p v-for="(val, key, i) in obj">值:{{ val }} ;键:{{ key }};索引:{{ i }}</p>
<!-- in 后面可以是数组,对象,或数字 -->
<!-- 注意:如果使用 v-for 迭代数字的话,前面的 count 值从 1 开始 -->
<p v-for="count in 10">这是第 {{ count }} 次循环</p>
<!-- 在2.2.0+版本里,key是必须设置的(数字/字符串类型),便于跟踪每个节点的唯一性 -->
<p v-for="item in list" :key="item.id">{{ item.name }}</p>
- 条件渲染
<!-- v-if 的特点:每次都会重新删除或创建元素 -->
<!-- v-show 的特点:每次不会重新进行DOM的删除和创建操作,只是切换了元素的 display:none 样式 -->
<h1 v-if="flag">这是用v-if控制的元素</h1>
<h1 v-show="flag">这是用v-show控制的元素</h1>
定时器的使用
// 开启一个定时器
var interval = setInterval(() => {
// 执行定时任务
}, 500);
// 停止定时器
clearInterval(interval);
interval = null;
事件修饰符
- .stop 阻止事件冒泡
<!-- 默认是事件冒泡(从里往外执行):即点击按钮先执行 btnClick,后执行 divClick -->
<div class="inner" @click="divClick">
<input type="button" value="按钮" @click.stop="btnClick">
</div>
- .prevent 阻止默认行为
<!-- 阻止默认行为,即不执行href的跳转 -->
<a href="http://www.baidu.com" @click.prevent="linkClick">有问题,先去百度</a>
- .capture 实现捕获触发事件的机制
<!-- 从外往里执行:即点击按钮先执行 divClick,后执行btnClick -->
<div class="inner" @click.capture="divClick">
<input type="button" value="按钮" @click="btnClick">
</div>
- .self 实现只有点击当前元素时候,才会触发事件
<!-- 只有当点击 div 时才执行 divClick,其它情况(如:冒泡、捕获等)都不执行 -->
<div class="inner" @click.self="divClick">
<input type="button" value="按钮" @click="btnClick">
</div>
- .once 只触发一次事件
<!-- 事件只执行一次,事件修饰符可组合使用 -->
<a href="http://www.baidu.com" @click.prevent.once="linkClick">有问题,先去百度</a>
Vue中双向数据绑定
-
v-model
指令:可以实现 表单元素和 Model 中数据的双向数据绑定
<!-- v-bind 只能实现数据的单向绑定,从 M 自动绑定到 V, 无法实现数据的双向绑定 -->
<input type="text" v-bind:value="msg" style="width:100%;">
<!-- 注意: v-model 只能运用在 表单元素中 -->
<!-- input(radio, text, address, email....) select checkbox textarea -->
<input type="text" style="width:100%;" v-model="msg">
Vue中使用样式
<!-- 一般用法 -->
<h1 class="red thin">我是一个很长很长的标题!!!</h1>
<!-- Vue中用法:使用数组 -->
<h1 v-bind:class="['red', 'thin']">我是一个很长很长的标题!!!</h1>
<h1 :class="['red', 'thin', isActive ? 'active' : '']">我是一个很长很长的标题!!!</h1>
<h1 :class="['red', 'thin', {'active': isActive}]">我是一个很长很长的标题!!!</h1>
<!-- Vue中用法:直接使用对象 -->
<h1 :class="{red: true, thin: true, active: true}">我是一个很长很长的标题!!!</h1>
<h1 :class="h1StyleObj">我是一个很长很长的标题!!!</h1>
<h1 :style="{'color': 'red', 'font-size': '40px'}">我是一个很长很长的标题!!!</h1>
过滤器
过滤器就是数据在输出前做最后一层的处理,即格式化输出。
常用在两个地方:{{ }} 或 v-bind: 中,用管道符("|")指示
- 过滤器的定义语法:
Vue.filter('过滤器的名称', function(){})
- 过滤器调用时候的格式:
{{ name | 过滤器的名称 }}
- 自定义 Vue 全局的过滤器
// 过滤器1:返回有效字符串(字符串格式化)
Vue.filter("ValidString", function(str, msg = "") {
if (str == null || typeof str == "undefined" || str == "") {
return msg;
}
// 去掉字符串头尾空格
if (str.replace(/(^\s*)|(\s*$)/g, "") == "") {
return msg;
}
return str;
}));
// 过滤器2:返回年月日字符串(时间格式化)
// format 给一个默认值,防止不传这个参数时 format = "undefined" 而报错
Vue.filter("dateFormatYMD", function(dateStr, format = "") {
if (dateStr.length < 10) {
return dateStr;
}
// 根据时间字符串,得到时间
var date = new Date(dateStr);
var y = date.getFullYear().toString().padStart(4, '0');
var m = (date.getMonth() + 1).toString().padStart(2, '0');
var d = date.getDate().toString().padStart(2, '0');
if (format.toLowerCase() === 'yyyy-mm-dd') {
// return y + '-' + m + '-' + d;
return `${y}-${m}-${d}`; // 模板字符串
} else {
var h = dt.getHours().toString().padStart(2, '0');
var m = dt.getMinutes().toString().padStart(2, '0');
var s = dt.getSeconds().toString().padStart(2, '0');
return `${y}-${m}-${d} ${h}:${m}:${s}`;
}
}));
<!-- 2.过滤器的使用 -->
{{ name | ValidString("暂无姓名") }}
{{ date | ValidString("暂无") | dateFormatYMD() }}
- 自定义 Vue 私有的过滤器
// 全局过滤器( app 和 app2 都能使用):返回有效字符串
Vue.filter("ValidString", function(str, msg = "") {
if (str == null || typeof str == "undefined" || str == "") {
return msg;
}
// 去掉字符串头尾空格
if (str.replace(/(^\s*)|(\s*$)/g, "") == "") {
return msg;
}
return str;
}));
// 创建 Vue 实例
var app = new Vue({
el: '#app',
data: {
msg: ""
},
methods: {
show () {
console.log("show方法");
},
dismiss () {
console.log("dismiss方法");
}
}
})
// 创建 Vue 实例
var app2 = new Vue({
el: '#app2',
data: {
msg: ""
},
methods: {
show () {
console.log("show方法");
},
dismiss () {
console.log("dismiss方法");
}
},
filters: { // 定义私有过滤器(只能 app2 使用)
ValidString: function (str, msg = "") {
if (str == null || typeof str == "undefined" || str == "") {
return msg;
}
// 去掉字符串头尾空格
if (str.replace(/(^\s*)|(\s*$)/g, "") == "") {
return msg;
}
return str;
}
}
})
<!-- 过滤器的调用:采用的是就近原则,这里调用的是私有的 ValidString -->
{{ name | ValidString("暂无姓名") }}
监听输入框键盘回车事件
<!-- 监听键盘(所有键)抬起事件 -->
<input type="text" class="form-control" v-model="name" @keyup="nameEnter">
<!-- 监听“Enter键”抬起事件(可通过 按键修饰符 来控制) -->
<input type="text" class="form-control" v-model="name" @keyup.enter="nameEnter">
- 按键修饰符
<!-- keyCode == 13(键盘码:键盘上每个按键都对应一个键盘码)时,调用 submit 方法 -->
<input type="text" @keyup.13="submit">
// Vue框架内定义的按键修饰符
<input type="text" @keyup.enter="submit">
<input type="text" @keyup.tab="submit">
<input type="text" @keyup.delete="submit">
<input type="text" @keyup.esc="submit">
... ...
// 自定义按键修饰符(即给键盘码取别名)
Vue.config.keyCodes.f2 = 113;
<!-- 监听“F2键”抬起事件 -->
<input type="text" @keyup.113="submit">
<input type="text" @keyup.f2="submit">
- 自定义指令(如:v-focus、v-color):输入框自动获取焦点
// 1.DOM操作实现
document.getElementById('search').focus();
<!-- 2.通过自定义指令来实现:v-focus (默认Vue没有帮我们实现这个指令,需要我们自己去实现)-->
<input type="text" id="search" v-focus v-color="'blue'">
// 使用 Vue.directive() 定义全局的指令 v-focus
// 参数1 :指令的名称(定义时不需要加 v- 前缀,调用的时候需要加)
// 参数2: 是一个对象,这个对象身上,有一些指令相关的函数,这些函数可以在特定的阶段,执行相关的操作
Vue.directive('focus', {
// 和JS行为相关的操作,最好在钩子函数(时机函数) inserted 中去执行,防止JS行为不生效
// 当绑定元素插入到DOM中执行
inserted: function (el) {
// 聚焦元素
el.focus();
}
});
// 自定义一个设置字体颜色的指令(支持传参)
Vue.directive('color', {
// 和样式相关的操作,一般都在钩子函数(时机函数) bind 中去执行
bind: function (el, binding) {
el.style.color = binding.value;
}
});
也可以定义局部指令,组件中接收一个directives
的选项
// 定义私有(局部)指令
directives: {
'focus': {
// 指令的定义
inserted: function (el) {
// 聚焦元素
el.focus();
}
}
}
常用鼠标、键盘事件
click
:鼠标单击
dbclick
:鼠标双击
mouseover
:鼠标经过
mouseout
:鼠标移开
keydown
:键盘按下
keyup
:键盘弹起
网友评论