目录
1 了解
1.1 为什么要学习流行框架?
- 提高开发效率的发展历程:原生JS -> Jquery之类的类库 -> 前端模板引擎 -> Angular.js / Vue.js(能够帮助我们减少不必要的DOM操作;提高渲染效率;双向数据绑定的概念【通过框架提供的指令,我们前端程序员只需要关心数据的业务逻辑,不再关心DOM是如何渲染的了】)
- 在Vue中,一个核心的概念,就是让用户不再操作DOM元素,解放了用户的双手,让程序员可以更多的时间去关注业务逻辑;
1.2 框架和库的区别
- 框架:是一套完整的解决方案;对项目的侵入性较大,项目如果需要更换框架,则需要重新架构整个项目。
- node 中的 express;
- 库(插件):提供某一个小功能,对项目的侵入性较小,如果某个库无法完成某些需求,可以很容易切换到其它库实现需求。
- 从Jquery 切换到 Zepto
- 从 EJS 切换到 art-template
1.3 Node(后端)中的 MVC 与 前端中的 MVVM 之间的区别
- MVC 是后端的分层开发概念;
- MVVM是前端视图层的概念,主要关注于 视图层分离,也就是说:MVVM把前端的视图层,分为了 三部分 Model, View , VM ViewModel
- 为什么有了MVC还要有MVVM
1.4 Vue 扩展插件
vue-cli:vue 脚手架
vue-resource(axios):ajax 请求
vue-router:路由
vuex:状态管理(它是 vue 的插件但是没有用 vue-xxx 的命名规则)
vue-lazyload:图片懒加载
vue-scroller:页面滑动相关
mint-ui:基于 vue 的 UI 组件库(移动端)
element-ui:基于 vue 的 UI 组件库(PC 端)
2 Vue简介
-
Vue.js 是目前最火的一个前端框架,React是最流行的一个前端框架(React除了开发网站,还可以开发手机App, Vue语法也是可以用于进行手机App开发的,需要借助于Weex)
-
Vue.js 是前端的主流框架之一,和Angular.js、React.js 一起,并成为前端三大主流框架!
-
Vue.js 是一套构建用户界面的框架,只关注视图层,它不仅易于上手,还便于与第三方库或既有项目整合。(Vue有配套的第三方类库,可以整合起来做大型项目的开发)
-
前端的主要工作?主要负责MVC中的V这一层;主要工作就是和界面打交道,来制作前端页面效果;
2.1 vue 的两个特性
2.1.1 数据驱动视图
- 数据的变化会驱动视图自动更新
- 好处:程序员只管把数据维护好,那么页面结构会被 vue 自动渲染出来!单向的数据绑定。
2.1.2 双向数据绑定
在网页中,form 表单负责采集数据,Ajax 负责提交数据。
- (1) js 数据的变化,会被自动渲染到页面上
- (2) 页面上表单采集的数据发生变化的时候,会被 vue 自动获取到,并更新到 js 数据中,不再需要手动操作DOM元素来获取表单元素最新的值。 注意:数据驱动视图和双向数据绑定的底层原理是 MVVM(Mode 数据源、View 视图、ViewModel 就是 vue 的实例)
2.2 MVVM
(1) MVVM 是 vue 实现数据驱动视图和双向数据绑定的核心原理。MVVM 指的是 Model、View 和 ViewModel,
它把每个 HTML 页面都拆分成了这三个部分,在 MVVM 概念中:
- Model 表示当前页面渲染时所依赖的数据源。
- View 表示当前页面所渲染的 DOM 结构,即 我们的HTML页面。
- ViewModel 表示 vue 的实例,它是 MVVM 的核心,控制器将数据和视图层建立联系 , vm 即 Vue 的实例
(2)ViewModel 作为 MVVM 的核心,是它把当前页面的数据源(Model)和页面的结构(View)连接在了一起。
- 当数据源发生变化时,会被 ViewModel 监听到,VM 会根据最新的数据源自动更新页面的结构
- 当表单元素的值发生变化时,也会被 VM 监听到,VM 会把变化过后最新的值自动同步到 Model 数据源中
3 Vue基本使用
3.1 基本使用步骤
- ① 导入 vue.js 的 script 脚本文件
- ② 在页面中声明一个将要被 vue 所控制的 DOM 区域
- ③ 创建 vm 实例对象(vue 实例对象):el指定根element(选择器); data初始化数据(页面可以访问)
基本代码与 MVVM 的对应关系:
3.1.1 el和data的两种写法
- el有2种写法
(1) new Vue时候配置el属性。
const v = new Vue({
el:'#root', //第一种写法
data:{
name:'时倾'
}
})
(2) 先创建Vue实例,随后再通过vm.$mount(’#root’)指定el的值。
const v = new Vue({
data:{
name:'时倾'
}
})
v.$mount('#root') //第二种写法 */
- data有2种写法
(1) 对象式
data:{
name:'时倾'
}
(2) 函数式
data(){
console.log('@@@',this) //此处的this是Vue实例对象
return{
name:'时倾'
}
}
如何选择:目前哪种写法都可以,以后学习到组件时,data必须使用函数式,否则会报错。
一个重要的原则:由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了。
3.2 调试工具vue-devtools
- 安装 vue-devtools 调试工具,vue 官方提供的 vue-devtools 调试工具,扩展迷链接进行搜索下载,方便开发者对 vue 项目进行调试与开发。
- 下载完成后,点击 Chrome 浏览器右上角的 按钮,选择更多工具 -> 扩展程序–>右上角打开开发者模式–>拖拽即可。
- 配置 Chrome 浏览器中的 vue-devtools,选择更多工具 -> 扩展程序 -> Vue.js devtools 详细信息,并勾选如下的两个选项:
修改完配置项,须重启浏览器才能生效! - 使用 vue-devtools 调试 vue 页面
在浏览器中访问一个使用了 vue 的页面,打开浏览器的开发者工具,切换到 Vue 面板,即可使用 vue-devtools
调试当前的页面。
4 vue指令
- 指令是带有 v- 前缀的特殊属性。是 vue 为开发者提供的模板语法,用于辅助开发者渲染页面的基本结构。
- 指令用于在表达式的值改变时,将某些行为应用到 DOM 上。
4.1 六大常见指令
4.1.1 内容渲染指令 {{ }}
v-text
指令的缺点:会覆盖元素内部原有的内容!
用法示例:
{{ }}
插值表达式:在实际开发中用的最多,只是内容的占位符,不会覆盖原有的内容!
v-html
指令的作用:可以把带有标签的字符串,渲染成真正的 HTML 内容!
注意:v-html有安全性问题!!!!
(1).在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。
(2).一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!
4.1.2 属性绑定指令 v-bind
注意:插值表达式只能用在元素的内容节点中,不能用在元素的属性节点中!
-
在 vue 中,可以使用
v-bind:
指令,为元素的属性动态绑定值; 简写是英文的:
-
在使用 v-bind 属性绑定期间,如果绑定内容需要进行动态拼接,则字符串的外面应该包裹单引号,例如:
<div :title="'box' + index">这是一个 div</div>
4.1.3 事件绑定 v-on
-
v-on:
简写是@
辅助程序员为 DOM 元素绑定事件监听,原生 DOM 对象有 onclick、oninput、onkeyup 等原生事件,替换为 vue 的事件绑定形式后,分别为:v-on:click、v-on:input、v-on:keyup -
语法格式为:
<button @click="add"></button> methods: { add() { // 如果在方法中要修改 data 中的数据,可以通过 this 访问到 this.count += 1 } }
-
事件参数对象
在原生的 DOM 事件绑定中,可以在事件处理函数的形参处,接收事件参数对象 event。同理,在 v-on 指令(简写为 @ )所绑定的事件处理函数中,同样可以接收到事件参数对象 event,示例代码如下:
-
绑定事件并传参
在使用 v-on 指令绑定事件时,可以使用 ( ) 进行传参,示例代码如下:
-
$event
是 vue 提供的特殊变量,用来表示原生的事件参数对象 event。 可以解决事件参数对象 event 被覆盖的问题。$event
的应用场景:如果默认的事件对象 e 被覆盖了,则可以手动传递一个 $event。例如:
<button @click="add(3, $event)"></button> methods: { add(n, e) { // 如果在方法中要修改 data 中的数据,可以通过 this 访问到 this.count += 1 } }
-
事件修饰符stop和prevent:
在事件处理函数中调用 event.preventDefault() 或 event.stopPropagation() 是非常常见的需求。因此,
vue 提供了事件修饰符的概念,来辅助程序员更方便的对事件的触发进行控制。常用的 5 个事件修饰符如下:
事件修饰符 | 说明 |
---|---|
.prevent | 阻止默认行为(例如:阻止 a 连接的跳转、阻止表单的提交等) |
.stop | 阻止事件冒泡 |
.capture | 以捕获模式触发当前的事件处理函数 |
.once | 绑定的事件只触发1次 |
.self | 只有在 event.target 是当前元素自身时触发事件处理函数 |
// 使用 .stop 阻止冒泡
<!-- <div class="inner" @click="div1Handler">
<input type="button" value="戳他" @click.stop="btnHandler">
</div>
-->
// 使用 .prevent 阻止默认行为
<a href="http://www.baidu.com" @click.prevent="linkClick">有问题,先去百度</a> //提交事件不再重载页面
<form v-on:submit.prevent="onSubmit"></form> // 修饰符可以串联
<a v-on:click.stop.prevent="doThat"></a> //只有修饰符
<form v-on:submit.prevent></form> // 使用 .capture 实现捕获触发事件的机制 ,首先触发该元素
<div class="inner" @click.capture="div1Handler">
<input type="button" value="戳他" @click="btnHandler">
</div>
// 使用 .self 实现只有点击当前元素时候,才会触发事件处理函数
<div class="inner" @click="div1Handler">
<input type="button" value="戳他" @click="btnHandler">
</div>
// 使用 .once 只触发一次事件处理函数
<a href="http://www.baidu.com" @click.prevent.once="linkClick">有问题,先去百度</a>
7.按键修饰符
在做项目中有时会用到键盘事件,在监听键盘事件时,我们经常需要检查详细的按键。Vue 允许为 v-on
在监听键盘事件时添加按键修饰符。
// 只有在 `keyCode` 是 13 时调用 `vm.submit()`
<input v-on:keyup.13="submit">
// 当点击enter 时调用 `vm.submit()`
<input v-on:keyup.enter="submit">
// 当点击enter或者space时 时调用 `vm.alertMe()`
<input type="text" v-on:keyup.enter.space="alertMe" >
/*常用的按键修饰符
.enter => enter键
.tab => tab键
.delete (捕获“删除”和“退格”按键) => 删除键
.esc => 取消键
.space => 空格键
.up => 上
.down => 下
.left => 左
.right => 右
*/
<script>
var vm = new Vue({
el:"#app",
methods: {
submit:function(){},
alertMe:function(){},
}
})
</script>
自定义按键修饰符别名
<div id="app">
// 预先定义了keycode 116(即F5)的别名为f5,因此在文字输入框中按下F5,会触发prompt方法
<input type="text" v-on:keydown.f5="prompt()">
</div>
<script>
Vue.config.keyCodes.f5 = 116;
let app = new Vue({
el: '#app',
methods: {
prompt: function() {
alert('我是 F5!');
}
}
});
</script>
4.1.4 双向绑定v-model 指令
- 辅助开发者在不操作 DOM 的前提下,快速获取表单的数据。
(1). input 输入框:type=“radio”; type=“checkbox”; type=“xxxx”
(2). textarea
(3). select
<body>
<div id="app">
<h4>{{ msg }}</h4>
/* v-bind 只能实现数据的单向绑定,从 M 自动绑定到 V, 无法实现数据的双向绑定
<input type="text" v-bind:value="msg" style="width:100%;">
使用 v-model 指令,可以实现 表单元素和 Model 中数据的双向数据绑定
注意: v-model 只能运用在 表单元素中
*/
// input(radio, text, address, email....) select checkbox textarea -->
<input type="text" style="width:100%;" v-model="msg">
</div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
msg: '大家都是好学生,爱敲代码,爱学习,爱思考,简直是完美,没瑕疵!'
},
methods: {
}
});
</script>
</body>
- 1.输入框:实例中演示了 input 和 textarea 元素中使用 v-model 实现双向数据绑定:
<div id="app">
<p>input 元素:</p>
<input v-model="message" placeholder="编辑我……">
<p>消息是: {{ message }}</p>
<p>textarea 元素:</p>
<p style="white-space: pre">{{ message2 }}</p>
<textarea v-model="message2" placeholder="多行文本输入……"></textarea>
</div>
<script>
new Vue({
el: '#app',
data: {
message: 'Runoob',
message2: '菜鸟教程\r\nhttp://www.runoob.com'
}
})
</script>
- 2.复选框:如果是一个为逻辑值,如果是多个则绑定到同一个数组:
以下实例中演示了复选框的双向数据绑定:
<div id="app">
<p>单个复选框:</p>
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>
<p>多个复选框:</p>
<input type="checkbox" id="runoob" value="Runoob" v-model="checkedNames">
<label for="runoob">Runoob</label>
<input type="checkbox" id="google" value="Google" v-model="checkedNames">
<label for="google">Google</label>
<input type="checkbox" id="taobao" value="Taobao" v-model="checkedNames">
<label for="taobao">taobao</label>
<br>
<span>选择的值为: {{ checkedNames }}</span>
</div>
<script>
new Vue({
el: '#app',
data: {
checked : false,
checkedNames: []
}
})
</script>
实例中勾选复选框效果如下所示:
- 3.单选按钮
<div id="app">
<input type="radio" id="runoob" value="Runoob" v-model="picked">
<label for="runoob">Runoob</label>
<br>
<input type="radio" id="google" value="Google" v-model="picked">
<label for="google">Google</label>
<br>
<span>选中值为: {{ picked }}</span>
</div>
<script>
new Vue({
el: '#app',
data: {
picked : 'Runoob'
}
})
</script>
选中后,效果如下图所示:
- 4.select 列表:下拉列表的双向数据绑定:
<div id="app">
<select v-model="selected" name="fruit">
<option value="">选择一个网站</option>
<option value="www.runoob.com">Runoob</option>
<option value="www.google.com">Google</option>
</select>
<div id="output">
选择的网站是: {{selected}}
</div>
</div>
<script>
new Vue({
el: '#app',
data: {
selected: ''
}
})
</script>
选取 Runoob,输出效果如下所示:
2. v-model 指令的修饰符
为了方便对用户输入的内容进行处理,vue 为 v-model 指令提供了 3 个修饰符,分别是:
修饰符 | 作用 | 示例 |
---|---|---|
.number | 自动将用户的输入值转为数值类型 | <input v-model.number=“age” /> |
.trim | 自动过滤用户输入的首尾空白字符 | <input v-model.trim=“msg” /> |
.lazy | 在“change”时而非“input”时更新 | <input v-model.lazy=“msg” /> |
-
.lazy
在默认情况下, v-model 在 input 事件中同步输入框的值与数据,但你可以添加一个修饰符 lazy ,从而转变为在 change 事件中同步:在 "change" 而不是 "input" 事件中更新 <input v-model.lazy="msg" >
-
.number
如果想自动将用户的输入值转为 Number 类型(如果原值的转换结果为 NaN 则返回原值),可以添加一个修饰符 number 给 v-model 来处理输入值,这通常很有用,因为在 type=“number” 时 HTML 中输入的值也总是会返回字符串类型。:<input v-model.number="age" type="number">
-
.trim
如果要自动过滤用户输入的首尾空格,可以添加 trim 修饰符到 v-model 上过滤输入:<input v-model.trim="msg">
4.1.5 条件渲染指令v-if、v-show
-
v-if 和 v-show 的区别:
(1)实现原理不同:v-if 指令会动态地创建或移除 DOM 元素,从而控制元素在页面上的显示与隐藏;
v-show 指令会动态为元素添加或移除 style=“display: none;” 样式,从而控制元素的显示与隐藏;(2)性能消耗不同:
v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此:
如果需要非常频繁地切换,则使用 v-show 较好
如果在运行时条件很少改变,则使用 v-if 较好(3)在实际开发中,绝大多数情况,不用考虑性能问题,直接使用 v-if 就好了!!!v-if 可以单独使用,或配合 v-else 指令一起使用。
-
v-if 指令在使用的时候,有两种方式:
(1)直接给定一个布尔值 true 或 false<p v-if="true">被 v-if 控制的元素</p>
(2) 给 v-if 提供一个判断条件,根据判断的结果是 true 或 false,来控制元素的显示和隐藏
<div id="app">
<div v-if="type === 'A'"> A </div>
<div v-else-if="type === 'B'"> B </div>
<div v-else-if="type === 'C'"> C </div>
<div v-else> Not A/B/C </div>
</div>
<script>
new Vue({
el: '#app',
data: {
type: 'C'
}
})
</script>
4.1.6 列表渲染指令v-for
vue 提供了 v-for 列表渲染指令,用来辅助开发者基于一个数组来循环渲染一个列表结构。v-for 指令需要使用 item in items 形式的特殊语法,其中:items 是待循环的数组, item 是被循环的每一项
- in 后面我们放过 普通数组,对象数组,对象, 还可以放数字
- v-for循环对象,此时遍历的是一个对象,对象中有多个键值对,遍历时有三个值:val,key,索引i。
- v-for 可以绑定数据到数组来渲染一个列表:
<div id="app">
<ol>
<li v-for="item in items "> {{ item.name }} </li>
</ol>
</div>
<script>
new Vue({
el: '#app',
data: {
items: [
{ name: 'Runoob' },
{ name: 'Google' },
{ name: 'Taobao' }
]
}
})
</script>
- 可以添加第二个的参数为键名,第三个参数为索引:
<div id="app">
<ul>
<li v-for="(value, key, index) in object">
{{ index }}. {{ key }} : {{ value }}
</li>
</ul>
</div>
- 使用 key 维护列表的状态
- (1)当列表的数据变化时,默认情况下,vue 会尽可能的复用已存在的 DOM 元素,从而提升渲染的性能。但这种默认的性能优化策略,会导致有状态的列表无法被正确更新。
- (2)为了给 vue 一个提示,以便它能跟踪每个节点的身份,从而在保证有状态的列表被正确更新的前提下,提升渲染的性能。此时,需要为每项提供一个唯一的 key 属性:
- (3)v-for 默认行为试着不改变整体,而是替换元素。迫使其重新排序的元素,你需要提供一个 key 的特殊属性:
<div v-for="item in items" :key="item.id"> {{ item.text }}</div>
注意: v-for 循环的时候,key 属性只能使用 number获取string
注意: key 在使用的时候,必须使用 v-bind 属性绑定的形式,指定 key 的值 ,
key来给每个节点做一个唯一标识,key的作用主要是为了高效的更新虚拟DOM
在组件中,必须 在使用 v-for 的同时,指定 唯一的 字符串/数字 类型 :key 值 -->
key 的注意事项
① key 的值只能是字符串或数字类型
② key 的值必须具有唯一性(即:key 的值不能重复)
③ 建议把数据项 id 属性的值作为 key 的值(因为 id 属性的值具有唯一性)
④ 使用 index 的值当作 key 的值没有任何意义(因为 index 的值不具有唯一性),不能使用index 作为key,因为当列表的内容新增或者删除时index都会发生变化,这就导致了不能很好的复用没有发生改变的元素,大大降低了渲染的效率。
⑤ 建议使用 v-for 指令时一定要指定 key 的值(既提升性能、又防止列表状态紊乱)
4.2 拓展指令、v-pre、v-once
4.2.1 v-cloak
- Vue 数据绑定过程
- 会先将未绑定数据的界面展示给用户;
- 然后再根据模型中的数据和控制的区域生成绑定数据之后的 HTML 代码
- 最后再将绑定数据之后的 HTML 渲染到界面上
正是在最终的 HTML 被生成渲染之前会先显示模板内容,所以如果用户网络比较慢或者网页性能较差时,用户会先看到模板内容。
-
v-cloak:避免闪烁问题:html页面加载过程中会先显示{{msg}},加载后才会导入数据,肉眼可见。
a. 默认 v-text 是没有闪烁问题的,但是用插值表达式的时候 ,必须用双花括号
b. 使用 v-cloak 能够解决 插值表达式闪烁的问题,默认先隐藏未渲染的界面;等到生成 HTML 渲染之后再重新显示
<p v-cloak>++++++++ dafd{{ msg }} ----------</p> /*需要添加style样式*/
<style>
[v-cloak] {
display: none;
}
</style>
4.2.2 v-pre
- 显示原始信息跳过编译过程
- 跳过这个元素和它的子元素的编译过程。
- 一些静态的内容不需要编译加这个指令可以加快渲染
<span v-pre>{{ this will not be compiled }}</span>
<!-- 显示的是{{ this will not be compiled }} -->
<span v-pre>{{msg}}</span>
<!-- 即使data里面定义了msg这里仍然是显示的{{msg}} -->
<script>
new Vue({
el: '#app',
data: {
msg: 'Hello Vue.js'
}
});
</script>
4.2.3 v-once
- v-once:执行一次性的插值【当数据改变时,插值处的内容不会继续更新】
<!-- 即使data里面定义了msg 后期我们修改了 仍然显示的是第一次data里面存储的数据即 Hello Vue.js -->
<span v-once>{{ msg}}</span>
<script>
new Vue({
el: '#app',
data: {
msg: 'Hello Vue.js'
}
});
</script>
4.3 数据绑定
4.3.1 绑定class类样式
四种方法,都需要 v-bind做数据绑定,简写:
:
<body>
<div id="app">
<!-- <h1 class="red thin">这是一个很大很大的H1,大到你无法想象!!!</h1> -->
//第一种使用方式,直接传递一个数组,注意: 这里的 class 需要使用 v-bind 做数据绑定
<!-- <h1 :class="['thin', 'italic']">这是一个很大很大的H1,大到你无法想象!!!</h1> -->
// 在数组中使用三元表达式
<!-- <h1 :class="['thin', 'italic', flag?'active':'']">这是一个很大很大的H1,大到你无法想象!!!</h1> -->
// 在数组中使用 对象来代替三元表达式,提高代码的可读性
<!-- <h1 :class="['thin', 'italic', {'active':flag} ]">这是一个很大很大的H1,大到你无法想象!!!</h1> -->
// 在为 class 使用 v-bind 绑定 对象的时候,对象的属性是类名,由于 对象的属性可带引号,也可不带引号,所以 这里我没写引号; 属性的值 是一个标识符
<h1 :class="classObj">这是一个很大很大的H1,大到你无法想象!!!</h1>
</div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
flag: true,
classObj: { red: true, thin: true, italic: false, active: false }
},
methods: {}
});
</script>
</body>
4.3.2 绑定style行内样式
<body>
<div id="app">
// 对象就是无序键值对的集合
<!-- <h1 :style="styleObj1">这是一个h1</h1> -->
<h1 :style="[ styleObj1, styleObj2 ]">这是一个h1</h1>
</div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
styleObj1: { color: 'red', 'font-weight': 200 },
styleObj2: { 'font-style': 'italic' }
},
methods: {}
});
</script>
</body>
4.4 自定义指令
- 内置指令不能满足我们特殊的需求
- Vue允许我们自定义指令
4.4.1 Vue.directive 注册全局指令
- 除了核心功能默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令。注意,在 Vue2.0 中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。举个聚焦输入框的例子。
- 当页面加载时,该元素将获得焦点 (注意:autofocus 在移动版 Safari 上不工作)。事实上,只要你在打开这个页面后还没点击过任何内容,这个输入框就应当还是处于聚焦状态。现在让我们用指令来实现这个功能:
// 注册一个全局自定义指令
Vue.directive('自定义指令名称', {
声明周期名称: function (el) {
指令业务逻辑代码
}
})
- 指令声明周期方法
自定义指令时一定要明确指令的业务逻辑代码更适合在哪个阶段执行
例如:指令业务逻辑代码中没有用到元素事件,那么可以在bind阶段执行
例如:指令业务逻辑代码中用到了元素事件,那么就需要在inserted阶段执行
4.4.2 Vue.directive 注册局部指令
- 如果想注册局部指令,组件中也接受一个 directives 的选项:
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}
4.4.3 Vue.directive 使用
在使用自定义指令时,需要加上v-前缀。你可以在模板中任何元素上使用新的 v-focus 属性,如下:
<input v-focus>
- 为自定义指令动态绑定参数值
在template 结构中使用自定义指令时,可以通过等号(=)的方式,为当前指令动态绑定参数值:
- 通过binding获取指令的参数值
在声明自定义指令时,可以通过形参中的第二个参数,来接收指令的参数值:
- update 函数
bind 函数只调用1 次:当指令第一次绑定到元素时调用,当DOM 更新时bind函数不会被触发。update 函数会在每次DOM 更新时被调用。示例代码如下:
- 函数简写
如果insert和update函数中的逻辑完全相同,则对象格式的自定义指令可以简写成函数格式:
5 过滤器
- Vue.js允许自定义过滤器,可被用于一些常见的文本格式化。
- 过滤器可以用在两个地方:双花括号插值和v-bind表达式。
- 过滤器应该被添加在JavaScript表达式的尾部,由“管道”符号指示
- 支持级联操作
- 过滤器不改变真正的
data
,而只是改变渲染的结果,并返回过滤后的版本 - 全局注册时是filter,没有s的。而局部过滤器是filters,是有s的
5.1 定义过滤器
在创建 vue 实例期间,可以在 filters 节点中定义过滤器,示例代码如下:
5.2 局部过滤器
- 1.HTML元素
<td>{{item.ctime | dataFormat('yyyy-mm-dd')}}</td>
- 2.私有
filters
定义方式:- 在 filters 节点下定义的过滤器,称为“私有过滤器”,因为它只能在当前 vm 实例所控制的 el 区域内使用。
- 本地过滤器被注册到一个Vue组件中。下面这个示例展示了本地过滤器是如何创建的。这个过滤器的功能是将字母变成大写:
- 本地过滤器存储在Vue组件中,作过filters属性中的函数。这个时候你想注册多少个就能注册多少个:
let app = new Vue({
el: '#app',
data () {
return {
name: 'w3cplus.com'
}
},
// 声明一个本地的过滤器
filters: {
Upper: function (value) {
return value.toUpperCase()
},
Lower: function (value) {
return value.toLowerCase()
}
}
})
<div id="app"> <h1>{{ name | Upper }}</h1> </div>
5.3 全局过滤器
<body>
<div id="app">
<p>{{ msg | msgFormat('漂亮+1', '美丽') | test }}</p>
</div>
<script>
// 定义一个 Vue 全局的过滤器,名字叫做 msgFormat,第一个参数:全局过滤器名字;第二个参数:全局过滤器的处理函数。
Vue.filter('msgFormat', function (msg, arg, arg2) {
// 字符串的 replace 方法,第一个参数,除了可写一个 字符串之外,还可以定义一个正则
return msg.replace(/可爱/g, arg + arg2)
})
Vue.filter('test', function (msg) {
return msg + '========'
})
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
msg: '你是可爱,可爱的'
},
methods: {}
});
</script>
</body>
- 结果:你是漂亮+1美丽,漂亮+1美丽的========
5.4 连续调用多个过滤器
过滤器可以串联地进行调用,例如
5.5 过滤器传参
5.6 常用:过滤时间
<h2>{{ time | dateFormat("yyyy-MM-dd") }}</h2>
</div>
/* 1. 创建 vue 实例对象 */
let vm = new Vue({
// 2. 声明vue的实例对象控制页面的哪个区域
el: '#app',
// 3. 声明vue控制区域可以使用的数据
data: {
time: Date.now()
}
})
/*
注意点:在使用过滤器的时候,可以在过滤器名称后面加上() 传递参数 value-->time format-->dateFormat
过滤器调用的时候可以传递参数,在处理函数的第二个参数接收
*/
Vue.filter("dateFormat", function (value, format){
let date = new Date(value)
let year = date.getFullYear()
let month = date.getMonth() + 1 + ""
let day = date.getDay() + ""
let hour = date.getHours() + ""
let minute = date.getMinutes() + ""
let second = date.getSeconds() + ""
if (format && format === "yyyy-MM-dd"){
return `${year}-${month.padStart(2, "0")}-${day.padStart(2, "0")}`
}
return `${year}-${month.padStart(2, "0")}-${day.padStart(2, "0")} ${hour.padStart(2, "0")}:${minute.padStart(2, "0")}:${second.padStart(2, "0")}`
})
/* padStart(number, String) 方法接收两个参数,由字符串调用
当字符串的长度不足 第一个参数 的长度时,会在前面填充 第二个参数 的值
*/
6.总结
- ① 能够知道 vue 的基本使用步骤:
导入 vue.js 文件–> new Vue() 构造函数,得到 vm 实例对象–>声明 el 和 data 数据节点–> MVVM 的对应关系 - ② 掌握 vue 中常见指令的基本用法:插值表达式、v-bind、v-on、v-if 和 v-else 、 v-for 和 :key、v-model
- ③ 掌握 vue 中过滤器的基本用法:全局过滤器 Vue.filter(‘过滤器名称’, function)、私有过滤器 filters 节点
7 watch侦听器
7.1 什么是watch侦听器
watch 侦听器允许开发者监视数据的变化,从而针对数据的变化做特定的操作。
<div id = "app">
<p style = "font-size:25px;">计数器: {{ counter }}</p>
<button @click = "counter++" style = "font-size:25px;">点我</button>
</div>
<script type = "text/javascript">
var vm = new Vue({
el: '#app',
data: {
counter: 1
}
});
vm.$watch('counter', function(nval, oval) {
alert('计数器值的变化 :' + oval + ' 变为 ' + nval + '!');
});
</script>
7.2 使用 watch 检测用户名是否可用
监听 username 值的变化,并使用 axios 发起 Ajax 请求,检测当前输入的用户名是否可用:
watch: {
// 监听 username 值的变化
async username(newVal) {
if (newVal === '') return
// 使用 axios 发起请求,判断用户名是否可用
const { data: res } = await axios.get('https://www.escook.cn/api/finduser/' + newVal)
console.log(res)
}
}
7.3 侦听器的格式
方法格式的侦听器
- 缺点1:无法在刚进入页面的时候,自动触发!!!
- 缺点2:如果侦听的是一个对象,如果对象中的属性发生了变化,不会触发侦听器!!!
对象格式的侦听器
- 好处1:可以通过 immediate 选项,让侦听器自动触发!!!
- 好处2:可以通过 deep 选项,让侦听器深度监听对象中每个属性的变化!!!
7.3.1 immediate 选项
默认情况下,组件在初次加载完毕后不会调用 watch 侦听器。如果想让 watch 侦听器立即被调用,则需要使用 immediate 选项。示例代码如下:
watch: {
username: {
// handler 是固定写法,表示当 username 的值变化时,自动调用 handler 处理函数
handler: async function (newVal) {
if (newVal === '') return
const { data: res } = await axios.get('https://www.escook.cn/api/finduser/' + newVal)
console.log(res)
},
// 表示页面初次渲染好之后,就立即触发当前的 watch 侦听器
immediate: true
}
}
7.3.1 deep 选项
如果 watch 侦听的是一个对象,如果对象中的属性值发生了变化,则无法被监听到。此时需要使用 deep 选 项,代码示例如下:
7.4 监听对象单个属性的变化
如果只想监听对象中单个属性的变化,则可以按照如下的方式定义 watch 侦听器
8 计算属性computed
计算属性指的是通过一系列运算之后,最终得到一个属性值。
这个动态计算出来的属性值可以被模板结构或 methods 方法使用。
8.1 计算属性的特点
① 虽然计算属性在声明的时候被定义为方法,但是计算属性的本质是一个属性
② 计算属性会缓存计算的结果,只有计算属性依赖的数据变化时,才会重新进行运算
- 注意点:定义的时候是通过一个函数返回数据,使用的时候不能加括号。因为其实属性,不是方法
- 计算属性和函数的区别
函数 :每次调用都会执行;数据经常发生变化的时候使用该函数
计算属性:只要返回的结果没发生变化,就只会执行一次;由于会将返回的结果进行缓存,如果返回的数据不经常发生变化,使用计算属性的性能比使用函数高
8.2 案例
反转字符串的例子:
- 实例一:
<div id="app">
{{ message.split('').reverse().join('') }}
</div>
- 实例二:声明了一个计算属性 reversedMessage 。提供的函数将用作属性 vm.reversedMessage 的 getter 。
vm.reversedMessage 依赖于 vm.message,在 vm.message 发生改变时,vm.reversedMessage 也会更新。
<div id="app">
<p>原始字符串: {{ message }}</p>
<p>计算后反转字符串: {{ reversedMessage }}</p>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
message: 'Runoob!'
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})
</script>
computed vs methods
- 实例三:
methods: {
reversedMessage2: function () {
return this.message.split('').reverse().join('')
}
}
1.首先最明显的不同 就是调用的时候,methods要加上()
2.我们可以使用 methods 来替代 computed,效果上两个都是一样的,但是 computed 是基于它的依赖缓存,只有相关依赖发生改变时才会重新取值。而使用 methods ,在重新渲染的时候,函数总会重新调用执行。
3.可以说使用 computed 性能会更好,但是如果你不希望缓存,你可以使用 methods 属性。
9 vue组件
9.1 vue组件化开发
- 组件化开发指的是:根据封装的思想,把页面上可重用的 UI 结构封装为组件,从而方便项目的开发和维护。
- vue 是一个支持组件化开发的前端框架。vue 中规定:组件的后缀名是 .vue。之前接触到的 App.vue 文件本质上就是一个 vue 的组件。
- 每个 .vue 组件都由 3 部分构成,分别是:
(1) template -> 组件的模板结构
(2) script -> 组件的 JavaScript 行为
(3) style -> 组件的样式
其中,每个组件中必须包含 template 模板结构,而 script 行为和 style 样式是可选的组成部分。
9.1.1 template
- vue 规定:每个组件对应的模板结构,需要定义到 节点中.
- 注意:
(1) template 是 vue 提供的容器标签,只起到包裹性质的作用,它不会被渲染为真正的 DOM 元素
(2) template 中只能包含唯一的根节点
9.1.2 script
vue 规定:
- 开发者可以在
<script>
节点中封装组件的 JavaScript 业务逻辑。
<script >
节点的基本结构如下:
9.1.3 style
vue 规定:组件内的<style>
节点是可选的,开发者可以在
9.2 组件使用
组件在被封装好之后,彼此之间是相互独立的,不存在父子关系 在使用组件的时候,根据彼此的嵌套关系,形成了父子关系、兄弟关系。
3.2.1 使用步骤
通过 components 注册的是私有子组件:在组件 A 的 components 节点下,注册了组件 F。则组件 F 只能用在组件 A 中;不能被用在组件 C 中。
9.2.2 注册全局组件
在 vue 项目的 main.js 入口文件中,通过 Vue.component() 方法,可以注册全局组件。示例代码如下:
- 第一种
<body>
<div id="app">
<!-- 如果要使用组件,直接,把组件的名称,以 HTML 标签的形式,引入到页面中,即可 -->
<!-- 注意:创建组件指定组件模板的时候,模板只能有一个根元素(一般是div) -->
<mycom1></mycom1>
</div>
<script>
/* 1.1 使用 Vue.extend 来创建全局的Vue组件
var com1 = Vue.extend({
template: '<h3>这是使用 Vue.extend 创建的组件</h3>' // 通过 template 属性,指定了组件要展示的HTML结构
})
*/
/* 1.2 使用 Vue.component('组件的名称', 创建出来的组件模板对象)注意组件只能有一个根元素,可以用一个div括起来。
Vue.component('myCom1', com1)
如果使用 Vue.component 定义全局组件的时候,组件名称使用了 驼峰命名,则在引用组件的时候,需要把 大写的驼峰改为小写的字母,同时,两个单词之前,使用 - 链接,要变成xx-yyy;如果不使用驼峰,则直接拿名称来使用即可;
Vue.component 第一个参数:组件的名称,将来在引用组件的时候,就是一个 标签形式 来引入 它的
第二个参数: Vue.extend 创建的组件 ,其中 template 就是组件将来要展示的HTML内容
*/
Vue.component('mycom1', Vue.extend({
template: '<h3>这是使用 Vue.extend 创建的组件</h3>'
}))
// 注意:不论是哪种方式创建出来的组件,组件的 template 属性指向的模板内容,必须有且只能有唯一的一个根元素
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {}
});
</script>
- 第二种,创建obj 或者将内容直接放入 Vue.component 第二个参数
let obj = {
template: `
<div>
<img width="200px" src="images/bg.jpg"/>
<p>this is a description.</p>
</div>`
}
// 这里会自动调用 extend 方法
Vue.component("component-obj", obj)
- 第三种 会有代码提示,不专业
<script id="info" type="text/html">
<div>
<img width="200px" src="images/fm.jpg"/>
<p>this is a description.</p>
</div>
</script>
<script>
Vue.component("component-script", {
template: '#info'
})
</script>
- 第四种:Vue提供了template标签–推荐写法
<template id="main">
<div>
<img width="200px" src="images/bg.jpg"/>
<p>this is a description.</p>
</div>
</template>
<script>
Vue.component("component-script", {
template: '#main'
})
</script>
9.2.3 组件的props
props 是组件的自定义属性,在封装通用组件的时候,合理地使用 props 可以极大的提高组件的复用性!
- props 是只读的:组件中封装的自定义属性是只读的,程序员不能直接修改 props 的值。否则会直接报错;要想修改 props 的值,可以把 props 的值转存到 data 中,因为 data 中的数据都是可读可写的!
- 在声明自定义属性时,可以通过 default 来定义属性的默认值。
- 在声明自定义属性时,可以通过 type 来定义属性的值类型。示例代码如下:
- 在声明自定义属性时,可以通过 required 选项,
required:true
将属性设置为必填项,强制用户必须传递属性的值。
9.2.4 组件之间的样式冲突问题
默认情况下,写在 .vue 组件中的样式会全局生效,因此很容易造成多个组件之间的样式冲突问题。
导致组件之间样式冲突的根本原因是:
① 单页面应用程序中,所有组件的 DOM 结构,都是基于唯一的 index.html 页面进行呈现的
② 每个组件中的样式,都会影响整个 index.html 页面中的 DOM 元素
解决方法:
- 为每个组件分配唯一的自定义属性,在编写组件样式时,通过属性选择器来控制样式的作用域,示例代码如下:
- 为了提高开发效率和开发体验,vue 为 style 节点提供了 scoped 属性,从而防止组件之间的样式冲突问题:
- 如果给当前组件的 style 节点添加了 scoped 属性,则当前组件的样式对其子组件是不生效的。如果想让某些样式对子组件生效,可以使用 /deep/ 深度选择器
9.2.5 定义在外面-代码示例
<div id="app2">
<mycom3></mycom3>
<login></login>
</div>
<!-- 在 被控制的 #app 外面,使用 template 元素,定义组件的HTML模板结构 -->
<template id="tmpl">
<div>
<h1>这是通过 template 元素,在外部定义的组件结构,这个方式,有代码的只能提示和高亮</h1>
<h4>好用,不错!</h4>
</div>
</template>
<script>
Vue.component('mycom3', {
template: '#tmpl'
})
</script>
3.2.6 定义私有的-代码示例
- 只能在自定义的那个Vue实例控制的区域中可以使用
- 在vue实例中新增components: {},注意:多了个s,在{}中通过key-value的形式注册组件
<body>
<div id="app2">
<login></login>
</div>
<template id="tmpl2">
<h1>这是私有的 login 组件</h1>
</template>
<script>
var vm2 = new Vue({
el: '#app2',
data: {},
methods: {},
filters: {},
directives: {},
components: { // 定义实例内部私有组件的
login: {
template: '#tmpl2'
}
}
})
</script>
</body>
9.2.7 组件中的data方法
- Vue实例控制的区域相当于一个大的组件,在大组件中我们可以使用data和methods
而我们自定义的组件也属于组件,在自定义组件中也可以使用data和methods - vue 组件中的 data 必须是一个函数,不能直接指向一个数据对象。因此在组件中定义 data 数据节点时,下面的方式是错误的,会导致多个组件实例共用同一份数据的问题,请参考官方给出的示例:https://cn.vuejs.org/v2/guide/components.html#data-必须是一个函数
- 自定义组件的data为什么是一个函数
因为组件可以被复用,为了保证复用时每个组件的数据都是独立的,所以必须是一个函数.如果组件中的data是通过函数返回的,则每创建一个新的组件,都会调用一次该方法,将这个方法返回的数据和当前创建的组件绑定在一起,这样就有效的避免了数据混乱
<script>
// 1. 组件可以有自己的 data 数据
// 2. 组件的 data 和 实例的 data 有点不一样,实例中的 data 可以为一个对象,但是 组件中的 data 必须是一个方法
// 3. 组件中的 data 除了必须为一个方法之外,这个方法内部,还必须返回一个对象才行;
// 4. 组件中 的data 数据,使用方式,和实例中的 data 使用方式完全一样!!!
Vue.component('mycom1', {
template: '<h1>这是全局组件 --- {{msg}}</h1>',
data: function () {
return {
msg: '这是组件的中data定义的数据'
}
}
})
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {},
methods: {}
});
</script>
9 数组
9.1 数组变异方法
- 在 Vue 中,直接修改对象属性的值无法触发响应式。当你直接修改了对象属性的值,你会发现,只有数据改了,但是页面内容并没有改变
- 变异数组方法即保持数组方法原有功能不变的前提下对其进行功能拓展
push() | 往数组最后面添加一个元素,成功返回当前数组的长度 |
---|---|
pop() | 删除数组的最后一个元素,成功返回删除元素的值 |
shift() | 删除数组的第一个元素,成功返回删除元素的值 |
unshift() | 往数组最前面添加一个元素,成功返回当前数组的长度 |
splice() | 有三个参数,第一个是想要删除的元素的下标(必选),第二个是想要删除的个数(必选),第三个是删除 后想要在原位置替换的值 |
sort() | sort() 使数组按照字符编码默认从小到大排序,成功返回排序后的数组 |
reverse() | reverse() 将数组倒序,成功返回倒序后的数组 |
9.2 替换数组
- 不会改变原始数组,但总是返回一个新数组
filter | filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。 |
---|---|
concat | concat() 方法用于连接两个或多个数组。该方法不会改变现有的数组 |
slice | slice() 方法可从已有的数组中返回选定的元素。该方法并不会修改数组,而是返回一个子数组 |
9.3 动态数组响应式数据
- Vue.set(a,b,c) 让 触发视图重新更新一遍,数据动态起来
- a是要更改的数据 、 b是数据的第几项、 c是更改后的数据