Vue.js 2.0 手把手入门笔记
栏目: JavaScript · 发布时间: 5年前
内容简介:是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。框架是库的升级版这里使用cnd方便测试
是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。
2 特点:
- 核心只关注视图层(view)
- 灵活、轻量、灵活的特点
- 适用于移动端项目
- 渐进式框架
3 什么是库,什么是框架?
- 库是将代码集合成一个产品,库是我们调用库中的方法实现自己的功能
- 框架则是为解决一类问题而开发的产品,框架是我们在指定的位置编写代码,框架帮我们调用。
框架是库的升级版
4 渐进式
- 声明式渲染(无需关心如何实现)
- 组件系统
- 客户端路由(vue-router)
- 大规模状态管理(vuex)
- 构建工具(vue-cli)
5 Vue的两个核心点
- 响应的数据变化
- 当数据发生改变->视图的自动更新
- 组合的视图组件
- ui页面映射为组件树
- 划分组件可维护、可复用、可测试
6 MVC(backbone,react)
- model 数据
- view 视图
- controller 控制器
7 MVVM(angular,vue) 双向
- model 数据
- view 视图
- viewModel视图模型
8 Object.defineProperty(es5)没有替代方案
- 不支持ie8<=
2 vue基础指令
2.1 安装vue
- cnd方式
- npm 方式
2.2 简单的尝试
这里使用cnd方便测试
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <body> <div id="content"> <!-- moustache 小胡子语法 表达式 可以放赋值 取值 三元--> {{ msg }} </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <!-- 使用官网的vue地址 --> <script> // 引用vue后会给一个vue构造函数 var vm = new Vue({ // vm === viewModel el: '#content', // 告诉vue管理哪一部分,querySelector "document.querySelector("#content")" data: { // data中的数据会被vm所代理 msg: 'Hello Vue!' // 可以通过vm.msg获取对应的呢日用 } })// Object.defineProperty vm.msg = "wjw" // 修改视图 </script> </html> 复制代码
2.3 模板语法
综上所属得出了一套模板语法
2.3.1 文本
<span>Message:{{msg}}</span> 复制代码
2.3.2 表达式
{{number + 1}} {{ok?'YES':'NO'}} {{message.split('').reverse().join('')}} 复制代码
但是vue的表单元素 input checkbox textarea radio select 非文本处理
vue的指令 directive 只是dom上的行间属性,vue给这类属性赋予了一些意义,来实现特殊功能所有指令都以v-开头value属性默认情况下回vue忽略掉 selected checked 都没有意义
2.3.3表单输入
v-model 会将msg赋予输入框,输入框的值改变会影响数据
<input v-model="msg"> <input type="checkbox" v-model="msg1" value="爬山"> 复制代码
2.3.4 原始HTML
<p>Using mustache:<span v-html='rawHtml'></spn></p> 复制代码
2.3.5 指令
<p v-if='seen'>现在看到我了</p> 复制代码
2.3.6 特性
<div v-bind:id='dynamicld'></div> 复制代码
2.4 Object.defineProperty原理
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <body> <div id="content"></div> <input type="text" id="input"> </body> <script> let obj = {} let temp = {}; document.getElementById("content").innerHTML = obj.name // 'name' 代表属性 Object.defineProperty(obj,'name',{ configurable:false, //是否可删除 // writable:true,// 是否可赋值(如果使用set方法,则不能使用) enumerable:true, // 是否可枚举,也是就for..in.. // value:1,// 值(如果使用get方法,则不能使用) get(){ // 取obj的name会触发get方法 return temp['name'] }, set(val){// 给obj赋值会触发get方法 // console.log(val); temp['name'] = val // 改变temp的结果 input.value = val // 将值赋值给输入框 } }); input.value = obj.name // 页面一加载,会将调用get方法 input.addEventListener('input',function(){ // 等待输入框的变化 obj.name = this.value // 当值变化时会调用set方法 document.getElementById("content").innerHTML = obj.name }) </script> </html> 复制代码
最后可以实现双向绑定的雏形
3 数据响应的变化
vue会循环data中的数据(数据劫持) 依次的增加getter和setter
let vm = new Vue({ el:'#content', data:{ a:{} } }) 复制代码
但是这时候我想添加一个school方法,发现没有产生getter和setter
1.1 方法一 $set
使用变量时 先要初始化,否则新加的属性不会导致页面刷新
vm.$set(vm.a,"school",'1')// 此方法可以给对象添加响应式的变化 复制代码
1.2 方法二 替换原对象
vm.a = {"school":"heihei",age:8}; 复制代码
1.3 数组问题
去改变数组中的某一项监控不到的,也不能改变数组的长度方法
let vm = new Vue({ el:'#content', data:{ a:[1,2,3,4,5,6] } }) 复制代码
错误方法
vm.a[0] =100 vm.a.length -=2 复制代码
变异方法:pop push shift unshit sort reserve splice
vm.a = vm.a.map(item=>item*3) 复制代码
4 数组的循环v-for
vue 提供了一个v-for 解决循环问题 更高效 会复用原有结构
4.1 代码
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <body> <div id="content"> <!--要循环谁就在谁身上增加v-for属性,类似于for...in..--> <!--默认是value of 数组/ (value,index) of 数组--> <li v-for="(todo,index) in todos"> <!-- 会改变原始数组的方法,为变异方法 例如push(),pop()等; 非变异方法,不会改变原始数组,但是会返回一个新数组 --> {{ todo.text }} {{index+1}} </li> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <!-- 使用官网的vue地址 --> <script> let vm = new Vue({ el:'#content', data:{ todos: [ { text: '学习 JavaScript' }, { text: '学习 Vue' }, { text: '整个牛项目' } ] } }) </script> </html> 复制代码
v-for循环数组 当用for来更新已被渲染的元素时,vue的“就地复用”机制 是不会改变数据项的顺序的。要想重新排序,需为每项添加key属性(也就是每项唯一的id)
想要改变
会改变原始数组的方法,为变异方法 例如push(),pop()等; 非变异方法,不会改变原始数组,但是会返回一个新数组
4.2 为什么v-for一定要有key
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <body> <div id="app"> <div> <input type="text" v-model="name"> <button @click="add">添加</button> </div> <ul> <li v-for="(item, i) in list"> <input type="checkbox"> {{item.name}} </li> </ul> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <!-- 使用官网的vue地址 --> <script> // 创建 Vue 实例,得到 ViewModel var vm = new Vue({ el: '#app', data: { name: '', newId: 3, list: [ { id: 1, name: '蔬菜' }, { id: 2, name: '奶酪' }, { id: 3, name: '肉' } ] }, methods: { add() { //注意这里是unshift this.list.unshift({ id: ++this.newId, name: this.name }) this.name = '' } } }); </script> </div> </html> 复制代码
当你输入汤时
就会变成这个样子 =>
但是当你换成了key
可以简单的这样理解:加了key(一定要具有唯一性) id的checkbox跟内容进行了一个关联。是我们想达到的效果
vue和react的虚拟DOM的Diff算法大致相同,其核心是基于两个简单的假设
首先讲一下diff算法的处理方法,对操作前后的dom树同一层的节点进行对比,一层一层对比,如下图:
当某一层有很多相同的节点时,也就是列表节点时,Diff算法的更新过程默认情况下也是遵循以上原则。
比如一下这个情况:
我们希望可以在B和C之间加一个F,Diff算法默认执行起来是这样的:
即把C更新成F,D更新成C,E更新成D,最后再插入E,是不是很没有效率?
所以我们需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点,找到正确的位置区插入新的节点。
vue中列表循环需加:key="唯一标识" 唯一标识可以是item里面id index等,因为vue组件高度复用增加Key可以标识组件的唯一性,为了更好地区别各个组件 key的作用主要是为了高效的更新虚拟DOM
5 事件
5.1 定义&缩写
事件定义以及缩写
<div id="app"> <button @click="msg"></button> <button @mousedown="add"></button> <!--如果不传递参数,则不要写括号会自动传入事件源,如果写括号了,要手动传入$event属性--> </div> let vm = new Vue({ el:"#app", methods:{ msg(){ console.log(Math.random()); } } }) 复制代码
methods和data中的数据会全部放在vm上,而且名字不能冲突,冲突会报错,methods中的this指向的都是实例
5.2 mousedown
当鼠标指针移动到元素上方,并按下鼠标按键(左、右键均可)时,会发生 mousedown 事件。
与 click 事件不同,mousedown 事件仅需要按键被按下,而不需要松开即可发生。
5.3 mouseup
当在元素上松开鼠标按键(左、右键均可)时,会发生 mouseup 事件。
与 click 事件不同,mouseup 事件仅需要松开按钮。当鼠标指针位于元素上方时,放松鼠标按钮就会触发该事件。
5.4 click
当鼠标指针停留在元素上方,然后按下并松开鼠标左键时,就会发生一次 click 事件。
注意:触发click事件的条件是按下并松开鼠标左键!,按下并松开鼠标右键并不会触发click事件。
三个事件的触发顺序
5.5 总结
若在同一个元素上按下并松开鼠标左键,会依次触发mousedown、mouseup、click,前一个事件执行完毕才会执行下一个事件
若在同一个元素上按下并松开鼠标右键,会依次触发mousedown、mouseup,前一个事件执行完毕才会执行下一个事件,不会触发click事件
6 事件修饰符的使用
1 事件处理
如果需要在内联语句处理器中访问原生DOM事件。可以使用特殊变量 $event
,把它传入到 methods
中的方法中。
在Vue中, 事件修饰符 处理了许多DOM事件的细节,让我们不再需要花大量的时间去处理这些烦恼的事情,而能有更多的精力专注于程序的逻辑处理。在Vue中事件修饰符主要有:
-
.stop
:等同于JavaScript中的event.stopPropagation()
,防止事件冒泡 -
.prevent
:等同于JavaScript中的event.preventDefault()
,防止执行预设的行为(如果事件可取消,则取消该事件,而不停止事件的进一步传播) -
.capture
:与事件冒泡的方向相反,事件捕获由外到内 -
.self
:只会触发自己范围内的事件,不包含子元素 -
.once
:只会触发一次
1.1 stop 防止事件冒泡
冒泡事件:嵌套两三层父子关系,然后所有都有点击事件,点击子节点,就会触发从内至外 子节点-》父节点的点击事件
<!-- HTML --> <div id="app"> <div class="outeer" @click="outer"> <div class="middle" @click="middle"> <button @click="inner">点击我(^_^)</button> </div> </div> <p>{{ message }}</p> </div> let app = new Vue({ el: '#app', data () { return { message: '测试冒泡事件' } }, methods: { inner: function () { this.message = 'inner: 这是最里面的Button' }, middle: function () { this.message = 'middle: 这是中间的Div' }, outer: function () { this.message = 'outer: 这是外面的Div' } } }) 复制代码
防止冒泡事件的写法是:在点击上加上.stop相当于在每个方法中调用了等同于 event.stopPropagation(),点击子节点不会捕获到父节点的事件
<!-- HTML --> <div id="app"> <div class="outeer" @click.stop="outer"> <div class="middle" @click.stop="middle"> <button @click.stop="inner">点击我(^_^)</button> </div> </div> </div> 复制代码
1.2 prevent取消默认事件
.prevent
等同于JavaScript的 event.preventDefault()
,用于取消默认事件。比如我们页面的 <a href="#">
标签,当用户点击时,通常在浏览器的网址列出 #
:
1.3 .capture 捕获事件
捕获事件:嵌套两三层父子关系,然后所有都有点击事件,点击子节点,就会触发从外至内 父节点-》子节点的点击事件
<!-- HTML --> <div id="app"> <div class="outeer" @click.capture="outer"> <div class="middle" @click.capture="middle"> <button @click.capture="inner">点击我(^_^)</button> </div> </div> </div> 复制代码
1.4 .self
修饰符 .self
只会触发自己范围内的事件,不会包含子元素。
<!-- HTML --> <div id="app"> <div class="outeer" @click.self="outer"> <div class="middle" @click.self="middle"> <button @click.stop="inner">点击我(^_^)</button> </div> </div> </div> 复制代码
1.5 .once 只执行一次点击
如果我们在 @click
事件上添加 .once
修饰符,只要点击按钮只会执行一次。
2 键盘修饰符
在JavaScript事件中除了前面所说的事件,还有键盘事件,也经常需要监测常见的键值。在Vue中允许 v-on
在监听键盘事件时添加关键修饰符。记住所有的 keyCode
比较困难,所以Vue为最常用的键盘事件提供了别名:
-
.enter
:回车键 -
.tab
:制表键 -
.delete
:含delete
和backspace
键 -
.esc
:返回键 -
.space
: 空格键 -
.up
:向上键 -
.down
:向下键 -
.left
:向左键 -
.right
:向右键
3 鼠标修饰符
鼠标修饰符用来限制处理程序监听特定的滑鼠按键。常见的有:
-
.left
:鼠标左键 -
.middle
:鼠标中间滚轮 -
.right
:鼠标右键
4 修饰键
可以用如下修饰符开启鼠标或键盘事件监听,使在按键按下时发生响应:
-
.ctrl
-
.alt
-
.shift
-
.meta
5 自定义按键修饰符别名
在Vue中可以通过 config.keyCodes
自定义按键修饰符别名。例如,由于预先定义了 keycode 116
(即 F5
)的别名为 f5
,因此在文字输入框中按下 F5
,会触发 prompt
方法,出现 alert
。
<!-- HTML --> <div id="app"> <input type="text" v-on:keydown.f5="prompt()"> </div> Vue.config.keyCodes.f5 = 116; let app = new Vue({ el: '#app', methods: { prompt: function() { alert('我是 F5!'); } } }); 复制代码
6 总结
在Vue中,使用 v-on
来给元素绑定事件,而为了更好的处理逻辑方面的事物,Vue提供了一个 methods
。在 methods
中定义一些方法,这些方法可以帮助我们处理一些逻辑方面的事情。而在这篇文章中,我们主要介绍了一些事件的修饰符,比如常见的阻止事件冒泡,键盘修饰符等。除此之外,还提供了 config.keyCodes
提供自定义按键修饰符别名。
7 缩写
7.1 指令缩写
<a v-bind:href='url'></a> <a :href='url'></a> <a v-on:click='doSomething'></a> <a @click='doSomething'></a> 复制代码
7.2 函数缩写
缩写后
8 组件化管理
1.组件化开发
我们可以很直观的将一个复杂的页面分割成若干个独立组件,每个组件包含组件的逻辑和样式,再将这些独立组件完成一个复杂的页面。这样既减少了逻辑复杂度,又实现了代码的重用。页面是组件的容器,组件自动组合形成完整的界面,当不需要某个组件时,或者想要替换某个组件时,可以随时进行替换和删除,而不影响整个应用的运行。
2、组件化开发的好处
- 提高开发效率
- 方便重复使用
- 便于协同开发
- 更容易被管理和维护
在vue中例如div、span都可以看做一个组件
3、全局组件
- 全局组件:可以声明一次在任何地方使用
- 局部组件:必须告诉这个组件属于谁
一般写插件的时候全局组件使用的多一些
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <body> <div id="app"> <my-handsom></my-handsom> <my-handsom></my-handsom> <my-handsom></my-handsom> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> Vue.component("my-handsom",{ //一个对象可以看成一个组件 data: function () { return { count: 0 } }, template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>' }) var vm = new Vue({ el: '#app' }) </script> </html> 复制代码
- 组件名不要带大写,多组件使用 -
- 只要组件和定义相同是可以的(首字母可以大写)
- html采用短横线隔开命名法js中转驼峰也是可以的
深入了解组件
props
组件的参数传递
slot
插槽在组件抽象设计中的应用
自定义事件
父子组件的通信方式
9 全局api- Vue.extend
使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象。
data
选项是特例,需要注意 - 在 Vue.extend()
中它必须是函数
<div id="mount-point"></div> 复制代码
// 创建构造器 var demo = Vue.extend({ template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>', data: function () { return { firstName: 'Walter', lastName: 'White', alias: 'Heisenberg' } } }) // 创建 Profile 实例,并挂载到一个元素上。 new demo().$mount('#mount-point') 复制代码
10 全局api-nextTick
官方说明
参数:
{Function} [callback] {Object} [context]
用法:
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
// 修改数据 vm.msg = 'Hello' // DOM 还没有更新 Vue.nextTick(function () { // DOM 更新了 }) // 作为一个 Promise 使用 (2.1.0 起新增,详见接下来的提示) Vue.nextTick() .then(function () { // DOM 更新了 }) 复制代码
2.1.0 起新增:如果没有提供回调且在支持 Promise 的环境中,则返回一个 Promise。请注意 Vue 不自带 Promise 的 polyfill,所以如果你的目标浏览器不原生支持 Promise (IE:你们都看我干嘛),你得自己提供 polyfill。
示例
先来一个示例了解下关于Vue中的DOM更新以及 nextTick
的作用。
模板
<div class="app"> <div ref="msgDiv">{{msg}}</div> <div v-if="msg1">Message got outside $nextTick: {{msg1}}</div> <div v-if="msg2">Message got inside $nextTick: {{msg2}}</div> <div v-if="msg3">Message got outside $nextTick: {{msg3}}</div> <button @click="changeMsg"> Change the Message </button> </div> 复制代码
Vue实例
**
new Vue({ el: '.app', data: { msg: 'Hello Vue.', msg1: '', msg2: '', msg3: '' }, methods: { changeMsg() { this.msg = "Hello world." this.msg1 = this.$refs.msgDiv.innerHTML this.$nextTick(() => { this.msg2 = this.$refs.msgDiv.innerHTML }) this.msg3 = this.$refs.msgDiv.innerHTML } } }) 复制代码
点击前
点击后
从图中可以得知:msg1和msg3显示的内容还是变换之前的,而msg2显示的内容是变换之后的。其根本原因是因为Vue中DOM更新是异步的(详细解释在后面)。
应用场景
下面了解下 nextTick
的主要应用的场景及原因。
- 在Vue生命周期的
created()
钩子函数进行的DOM操作一定要放在Vue.nextTick()
的回调函数中
在 created()
钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js代码放进 Vue.nextTick()
的回调函数中。与之对应的就是 mounted()
钩子函数,因为该钩子函数执行时所有的DOM挂载和渲染都已完成,此时在该钩子函数中进行任何DOM操作都不会有问题 。
- 在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的DOM结构的时候,这个操作都应该放进
Vue.nextTick()
的回调函数中。
具体原因在Vue的官方文档中详细解释:
Vue 异步执行 DOM 更新。只要观察到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作上非常重要。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。Vue 在内部尝试对异步队列使用原生的 Promise.then
和 MessageChannel
,如果执行环境不支持,会采用 setTimeout(fn, 0)
代替。 例如,当你设置 vm.someData = 'new value'
,该组件不会立即重新渲染。当刷新队列时,组件会在事件循环队列清空时的下一个“tick”更新。多数情况我们不需要关心这个过程,但是如果你想在 DOM 状态更新后做点什么,这就可能会有些棘手。虽然 Vue.js 通常鼓励开发人员沿着“数据驱动”的方式思考,避免直接接触 DOM,但是有时我们确实要这么做。为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback)
。这样回调函数在 DOM 更新完成后就会调用。
11 全局api-set
官网说明
Vue.set( target, propertyName/index, value )
- 参数 :
{Object | Array} target {string | number} propertyName/index {any} value
- 返回值 :设置的值。
- 用法 :
向响应式对象中添加一个属性,并确保这个新属性同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新属性,因为 Vue 无法探测普通的新增属性 (比如this.myObject.newProperty = 'hi'
)
注意对象不能是 Vue 实例,或者 Vue 实例的根数据对象。
示例
<div id="div"> <p >{{items}}</p> </div> <script> var vm = new Vue({ el:"#div", data: { items: ['a', 'b', 'c'] } }); Vue.set(vm.items,2,"ling") </script> 复制代码
1 设置数组元素
Vue.set(vm.items,2,"ling") : 表示 把vm.items 这个数组的下标为2 的元素,改为"ling"
把数组 ["a","b","c"] 修改 后是 ["a","b","ling"]
2 向响应式对象添加属性
<div id="div"> <p>{{person}}</p> </div> <script> var vm = new Vue({ el:"#div", data: { person:{ name:"ling", job:"engineer" } }, created:function(){ alert(this.person.age) } }); Vue.set(vm.person,"age","26") </script> 复制代码
注意:person 是data 里面的子对象,所以可以使用 Vue.set( ) 方法。data 这个根对象就不能使用 set 方法
说明:控制台可以在person 里找到age 这个属性,说明添加成功 (响应式)
**
对比非响应式方法
vm.food="chocolate"
alert(vm.food)
控制台和网页上的 {{person}} 都没有显示food 这个属性,说明food 这个属性没有被添加 (非响应式)
**
12 全局api-delete
Vue.delete( target, propertyName/index )
- 参数 :
{Object | Array} target {string | number} propertyName/index
仅在 2.2.0+ 版本中支持 Array + index 用法。
-
用法:
删除对象的属性。如果对象是响应式的,确保删除能触发更新视图。这个方法主要用于避开 Vue 不能检测到属性被删除的限制,但是你应该很少会使用它。
在 2.2.0+ 中同样支持在数组上工作。
- 目标对象不能是一个 Vue 实例或 Vue 实例的根数据对象。
data:{ namelist : { id : 1, name : '叶落森' } } 复制代码
// 删除name delete this.namelist.name;//js方法 Vue.delete(this.namelist,'name');//vue方法 复制代码
13 全局api-fifer过滤器
8.1 介绍
允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方: 双花括号插值和 v-bind
表达式 (后者从 2.1.0+ 开始支持)。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示
8.2 优势
1、在Vue中使用过滤器(Filters)来渲染数据是一种很有趣的方式。
2、首先我们要知道,Vue中的过滤器不能替代Vue中的 methods
、 computed
或者 watch
,
3、过滤器不改变真正的 data
,而只是改变渲染的结果,并返回过滤后的版本。
4、在很多不同的情况下,过滤器都是有用的,比如尽可能保持API响应的干净,并在前端处理数据的格式。
5、在你希望避免重复和连接的情况下,它们也可以有效地封装成可重用代码块背后的所有逻辑。
8.3 过滤器例子
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <body> <div id="app"> <div>{{ message | capitalize }}</div> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> var vm= new Vue({ el: '#app', data: { message: 'world' }, filters: { // 可以有好多的自定义过滤器 capitalize(value) { // 这里的this指向的window if (!value) return '' value = value.toString() return value.charAt(0).toUpperCase() + value.slice(1) } } }); </script> </html> 复制代码
8.4 过滤器串连
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <body> <div id="app"> {{ message | filterA | filterB }} </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> var vm= new Vue({ el: '#app', data: { message: 'world' }, filters: { // 可以有好多的自定义过滤器 filterA(value){ return value.split('').reverse().join(''); }, filterB(value){ return value.charAt(0).toUpperCase() + value.slice(1) } } }); </script> </html> 复制代码
8.5 过滤器传参
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <body> <div id="app"> {{ message | filterA('hello',hi) }} </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> var vm= new Vue({ el: '#app', data: { hi:'!', message: 'world' }, filters: { // 可以有好多的自定义过滤器 filterA(value1,value2,value3){ return `${value2} ${value1} ${value3}`; } } }); </script> </html> 复制代码
这里,filterA 被定义为接收三个参数的过滤器函数。其中 message 的值作为第一个参数,普通字符串 'hello' 作为第二个参数,表达式 hi 的值作为第三个参数。
14 插槽-slot
老版本vue
模板中只能有一个根元素
HTML内容模板(template)元素是一种用于保存客户端内容机制,该内容在加载页面时不会呈现,但随后可以在运行时使用JavaScript实例化。
<div id="app"> <modal></modal> </div> <template id="modal"> <div> <h1>是否删除</h1> </div> </template> 复制代码
let modal = { template:"#modal" } const app = new Vue({ el:'#app', components:{ modal }, data:{ } }) 复制代码
我们通常是想把h1的值动态放入,所以就要用到插槽
单个插槽 | 默认插槽 | 匿名插槽
首先是单个插槽, 单个插槽 是vue的官方叫法,但是其实也可以叫它默认插槽,或者与具名插槽相对,我们可以叫它匿名插槽。因为它不用设置name属性。 单个插槽可以放置在组件的任意位置,但是就像它的名字一样,一个组件中只能有一个该类插槽。相对应的,具名插槽就可以有很多个,只要名字(name属性)不同就可以了。
<div id="app"> <modal> <h1>插入成功</h1> </modal> </div> <template id="modal"> <div> <slot></slot> </div> </template> 复制代码
当我们看到插入成功的时候,匿名插入就实现了
具名插槽
匿名插槽没有name属性,所以是匿名插槽,那么,插槽加了name属性,就变成了具名插槽。具名插槽可以在一个组件中出现N次,出现在不同的位置。下面的例子,就是一个有两个 具名插槽 和 单个插槽 的组件,这三个插槽被父组件用同一套css样式显示了出来,不同的是内容上略有区别。
简单的来说,就是,我们可能遇到一个问题 我们想插入不同的插槽内的内容不一样
在 2.6.0+ 中已弃用
<div id="app"> <modal> <h1>插入成功</h1> <h2 slot="title">标题</h2> <h2 slot="content">内容</h2> </modal> </div> <template id="modal"> <div> <slot name="default"></slot> <slot name="title"></slot> <slot name="content"></slot> </div> </template> 复制代码
我们可以发现没有name的情况下,默认就是default
作用域插槽 | 带数据的插槽
最后,就是我们的作用域插槽。这个稍微难理解一点。官方叫它作用域插槽,实际上,对比前面两种插槽,我们可以叫它带数据的插槽。什么意思呢,就是前面两种,都是在组件的template里面写
在 2.6.0+ 中已弃用
```html
{{users[props.$index].id}} {{users[props.$index].name}} {{users[props.$index].age}} 编辑 删除
```
这种写法,习惯了element-ui的朋友一定就很熟悉了。
总结:
1 . 使用slot可以在自定义组件内插入原生HTML元素,需要搭配使用name和slot属性,否则多个slot可能会返回重复的HTML元素。
2 . 使用slot-scope可以将slot内部的作用域指向该子组件,否则默认作用域指向调用slot的父组件。
新版本的 v-slot
从 vue@2.6.x 开始,Vue 为具名和范围插槽引入了一个全新的语法,即我们今天要讲的主角: v-slot
指令。目的就是想统一 slot
和 scope-slot
语法,使代码更加规范和清晰。既然有新的语法上位,很明显, slot
和 scope-slot
也将会在 vue@3.0.x
中彻底的跟我们说拜拜了。而从 vue@2.6.0
开始,官方推荐我们使用 v-slot
来替代后两者。
#### 具名插槽 > 实例化一个vue
// 组件 Vue.component('lv-hello', { template: ` <div> <slot name="header"></slot> <h1>我的天呀</h1> </div>` }) new Vue({ el: '#app1', data: { } }); 复制代码
老版本
<div id="app1"> <!-- 老版本使用具名插槽 --> <lv-hello> <p slot="header">我是头部</p> </lv-hello> </div> 复制代码
新版本的变化
<!-- 新版本使用具名插槽 --> <lv-hello> <!-- 注意:这块的 v-slot 指令只能写在 template 标签上面,而不能放置到 p 标签上 --> <template v-slot:header> <p>我是头部</p> </template> </lv-hello> </div> 复制代码
具名插槽的缩写
将 v-slot:
替换成 #
号
<div id="app"> <lv-hello> <template #header> <p>我是头部</p> </template> <!-- 注意: #号后面必须有参数,否则会报错。即便是默认插槽,也需要写成 #default --> <template #default> <p>我是默认插槽</p> </template> </lv-hello> </div> 复制代码
作用域插槽
所谓作用域插槽,就是让插槽的内容能够访问子组件中才有的数据。
Vue.component('lv-hello', { data: function () { return { firstName: '张', lastName: '三' } }, template: ` <div> <slot name="header" :firstName="firstName" :lastName="lastName"></slot> <h1>我的天呀</h1> </div> ` }) 复制代码
<div id="app"> <!-- 老版本使用具名插槽 --> <lv-hello> <p slot="header" slot-scope="hh">我是头部 {{ hh.firstName }} {{ hh.lastName }}</p> </lv-hello> <!-- 新版本使用具名插槽 --> <lv-hello> <!-- 注意:这块的 v-slot 指令只能写在 template 标签上面,而不能放置到 p 标签上 --> <template v-slot:header="hh"> <p>我是头部 {{ hh.firstName }} {{ hh.lastName }}</p> </template> </lv-hello> </div> 复制代码
15 动态绑定样式-v-bind
13.1 对象语法
:class 绑定的样式和class绑定的不冲突
13.1.1 直接绑定一个data
<div v-bind:class="{ active: isActive }"></div> 复制代码
active
这个 class 存在与否将取决于数据属性 isActive
的 布尔值
<div class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }"></div> 复制代码
13.1.2 data中使用一个对象绑定
data: { classObject: { active: true, 'text-danger': false } } 复制代码
13.1.3 计算属性中绑定
data: { isActive: true, error: null }, computed: { classObject: function () { return { active: this.isActive && !this.error, 'text-danger': this.error && this.error.type === 'fatal' } } } 复制代码
13.2 数组语法
<div v-bind:class="[activeClass, errorClass]"></div> 复制代码
13.2.1 直接动态绑定一个class
data: { activeClass: 'active', errorClass: 'text-danger' } 复制代码
13.2.2 三元表达式
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div> 复制代码
不过,当有多个条件 class 时这样写有些繁琐。所以在数组语法中也可以使用对象语法:
<div v-bind:class="[{ active: isActive }, errorClass]"></div> 复制代码
16 数据-计算属性(computed)
1 什么是计算属性
模板内的表达式非常便利,但是设计它们的初衷是用于 简单运算的 。在模板中放入太多的逻辑会让模板过重且难以维护。例如:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <body> <div id="app"> <div id="example"> {{ message.split('').reverse().join('') }} </div> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> var vm = new Vue({ el: '#app', data: { message: 'Hello' } }); </script> </div> </html> 复制代码
这里的表达式包含3个操作,并不是很清晰,所以遇到复杂逻辑时应该使用Vue特带的计算属性computed来进行处理。
2 计算属性的用法
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <body> <div id="app"> <div id="example"> {{getMessage}} </div> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> var vm = new Vue({ el: '#app', data: { message: 'Hello' }, computed: { // 放在computed中最后也会放在vm上,不能和methods与data重名 getMessage() { return this.message.split('').reverse().join('') } } }); </script> </div> </html> 复制代码
3 计算属性使用技巧
计算属性可以依赖其他计算属性
计算属性不仅可以依赖当前Vue 实例的数据,还可以依赖其他实例的数据
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <body> <div id="app1"></div> <div id="app2"> {{getMessage}} </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> var vm= new Vue({ el: '#app1', data: { message: 'World' } }); var vm2 = new Vue({ el: '#app2', data: { message: 'Hello' }, computed: { getMessage() { return `${this.message} ${vm.message}` } } }); </script> </div> </html> 复制代码
4 getter和setter
每一个计算属性都包含一个getter 和一个setter ,我们上面的两个示例都是计算属性的默认用法, 只是利用了getter 来读取。
在你需要时,也可以提供一个setter 函数, 当手动修改计算属性的值就像修改一个普通数据那样时,就会触发setter 函数,执行一些自定义的操作
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <body> <div id="app"> <input type="text" v-model="getMessage"> <--模拟修改--!> {{getMessage}} </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> var vm= new Vue({ el: '#app', data: { hi:'Hello', message: 'World' }, computed:{ getMessage:{ //get,set方法 // getter get(){ return this.hi + ' ' + this.message }, // setter set(newValue){ console.log('===================================='); console.log(newValue); console.log('===================================='); var names = newValue.split(' '); this.hi = names[0]; this.message = names[names.length - 1]; } } } }); </script> </html> 复制代码
绝大多数情况下,我们只会用默认的getter 方法来读取一个计算属性,在业务中很少用到setter,所以在声明一个计算属性时,可以直接使用默认的写法,不必将getter 和setter 都声明。
5 质疑什么不直接用methods
我们可以将同一函数定义为一个方法而不是一个计算属性,两种方式的最终结果确实是完全相同的。只是一个使用getMessage()取值,一个使用getMessage取值。
然而,不同的是 计算属性是基于它们的依赖进行缓存的 。计算属性只有在它的相关依赖发生改变时才会重新求值。
这就意味着只要 hi还没有发生改变,多次访问 getMessage计算属性会立即返回之前的计算结果,而不必再次执行函数。
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <body> <div id="app"> <div>{{getMessage}}</div> <div> {{getMessage1()}}</div> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> var vm= new Vue({ el: '#app', data: { hi:'Hello', message: 'World' }, computed:{ getMessage(){ //get,set方法 return this.hi + ' ' + this.message //而使用计算属性,只要title没变,页面渲染是不会重新进这里来计算的,而是使用了缓存。 } }, methods:{ getMessage1(){ return this.hi + ' ' + this.message //进这个方法,再次计算。不是刷新,而是只要页面渲染,就会进方法里重新计算。 } } }); </script> </html> 复制代码
17 数据-观察(watch)
一个对象,键是需要观察的表达式,值是对应回调函数。值也可以是方法名,或者包含选项的对象。Vue 实例将会在实例化时调用 $watch()
,遍历 watch 对象的每一个属性。
为什么一定要有watch,不用可以吗?我们已经有了computed,能不能不去使用?
1 watch的出现
做一个实验
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <body> <div id="app"> <input type="text" v-model="a"> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> var vm = new Vue({ el:'#app', data:{ a:"1" }, computed: { a(){ setTimeout(() => { this.a=1; }, 500); } } }) </script> </html> 复制代码
不难发现在_异步的情况下就不好使用了_
2 代码实现
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <body> <div id="app"> <input type="text" v-model="a"> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> var vm = new Vue({ el:'#app', data:{ a:"" }, watch: { // 只有值变化的时候才会触发 支持异步了,其他情况我们更善于使用 a(newVal,oldVal){ // watch的属性名字要和观察的人的名字一致 console.log(newVal); console.log(oldVal); } }, }) </script> </html> 复制代码
3 computed与watch的区别
Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动: 侦听属性 。当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <body> <div id="app"> {{ fullName }} </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> </html> 复制代码
var vm = new Vue({ el: '#app', data: { firstName: 'Foo', lastName: 'Bar', fullName: 'Foo Bar' }, watch: { firstName: function (val) { this.fullName = val + ' ' + this.lastName }, lastName: function (val) { this.fullName = this.firstName + ' ' + val } } }) 复制代码
上面代码是命令式且重复的。将它与计算属性的版本进行比较:
var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar' }, computed: { fullName: function () { return this.firstName + ' ' + this.lastName } } }) 复制代码
是不是感觉优雅很多
4 侦听器
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <body> <div id="app"> <input type="text" v-model="something"> {{somethingShow}} </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> var vm = new Vue({ el: '#app', data: { something: '', somethingShow:'' }, watch: { something(val){ this.somethingShow = "loading" this.getSomething() } }, methods:{ getSomething(){ setTimeout(() => { this.somethingShow = "hello" }, 1000);// 我们使用延迟模拟一个网络请求 } } }) </script> </html> 复制代码
5 vm.$watch
vm.$watch( expOrFn, callback, [options] )
观察 Vue 实例变化的一个表达式或计算属性函数。回调函数得到的参数为新值和旧值。表达式只接受监督的键路径。对于更复杂的表达式,用一个函数取代。
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <body> <div id="app"> <input type="text" v-model="something"> {{somethingShow}} </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> var vm = new Vue({ el: '#app', data: { something: '', somethingShow:'' } }) vm.$watch('something',(newVal,oldVal)=>{// watch的属性名要和观察的人名字一致 vm.somethingShow = "loading" console.log('===================================='); console.log(newVal); console.log('===================================='); vm.somethingShow = newVal }) </script> </html> 复制代码
18 数据-属性(props)
组件接受的选项之一 props 是 Vue 中非常重要的一个选项。父子组件的关系可以总结为: props down, events up 父组件通过 props 向下传递数据给子组件;子组件通过 events 给父组件发送消息。
父子级组件
比如我们需要创建两个组件 parent 和 child。需要保证每个组件可以在相对隔离的环境中书写,这样也能提高组件的可维护性。
这里我们先定义父子两个组件和一个 Vue 对象
var childNode = { template: ` <div>childNode</div> ` }; var parentNode = { template: ` <div> <child></child> <child></child> </div> `, components: { child: childNode } }; new Vue({ el: "#example", components: { parent: parentNode } }); 复制代码
<div id="example"> <parent></parent> </div> 复制代码
这里的 childNode 定义的 template 是一个 div,并且内容是"childNode"字符串。 而在 parentNode 的 template 中定义了 div 的 class 名叫 parent 并且包含了两个 child 组件。
静态 props
组件实例的作用域是孤立的。这意味着不能(也不应该)在子组件的模板中直接引用父组件的数据。要让子组件使用父组件的数据,需要通过子组件的 props 选项。 父组件向子组件传递数据分为两种方式:动态和静态,这里先介绍静态方式。 子组件要显示的用 props 声明它期望获得的数据 修改上例中的代码,给 childNode 添加一个 props 选项和需要的 forChildMsg
数据; 然后在父组件中的占位符添加特性的方式来传递数据。
var childNode = { template: ` <div> {{forChildMsg}} </div> `, props: ["for-child-msg"] // 直接把参数作为数组放进去 }; var parentNode = { template: ` <div> <p>parentNode</p> <child for-child-msg="aaa"></child> <child for-child-msg="bbb"></child> </div> `, components: { child: childNode } }; 复制代码
命名规范
**
对于 props 声明的属性,在父组件的 template 模板中,属性名需要使用中划线写法; 子组件 props 属性声明时,使用小驼峰或者中划线写法都可以;而子组件的模板使用从父组件传来的变量时,需要使用对应的小驼峰写法。别担心,Vue 能够正确识别出小驼峰和下划线命名法混用的变量,如这里的 forChildMsg
和 for-child-msg
是同一值。
动态props
原则上很简单,for-child-msg作为一个变量
var parentNode = { template: ` <div> <p>parentNode</p> <child :for-child-msg="childMsg1"></child> <child :for-child-msg="childMsg2"></child> </div> `, components: { child: childNode }, data: function() { return { childMsg1: "child-1", childMsg2: "child-2" }; } }; 复制代码
在父组件的 data 的 return 数据中的 childMsg1 和 childMsg2 会被传入子组件中
props 验证
验证传入的 props 参数的数据规格,如果不符合数据规格,Vue 会发出警告。
能判断的所有种类(也就是 type 值)有: String, Number, Boolean, Function, Object, Array, Symbol
Vue.component("example", { props: { // 基础类型检测, null意味着任何类型都行 propA: Number, // 多种类型 propB: [String, Number], // 必传且是String propC: { type: String, required: true }, // 数字有默认值 propD: { type: Number, default: 101 }, // 数组、默认值是一个工厂函数返回对象 propE: { type: Object, default: function() { console.log("propE default invoked."); return { message: "I am from propE." }; } }, // 自定义验证函数 propF: { isValid: function(value) { return value > 100; } } } }); let childNode = { template: "<div>{{forChildMsg}}</div>", props: { "for-child-msg": Number } }; let parentNode = { template: ` <div class="parent"> <child :for-child-msg="msg"></child> </div>`, components: { child: childNode }, data() { return { // 当这里是字符串 "123456"时会报错 msg: 123456 }; } }; 复制代码
还可以在 props 定义的数据中加入自定义验证函数,当函数返回 false 时,输出警告。 比如我们把上述例子中的 childNode 的 for-child-msg
修改成一个对象,并包含一个名叫 validator
的函数,该命名是规定叫 validator
的,自定义函数名不会生效
let childNode = { template: "<div>{{forChildMsg}}</div>", props: { "for-child-msg": { validator: function(value) { return value > 100; } } } }; 复制代码
在这里我们给 for-child-msg
变量设置了 validator
函数,并且要求传入的值必须大于 100,否则报出警告。
单向数据流
props 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是不会反过来。这是为了防止子组件五一修改父组件的状态。
所以不应该在子组件中修改 props 中的值,Vue 会报出警告。
let childNode = { template: `<div class="child"> <div> <span>子组件数据</span> <input v-model="forChildMsg"/> </div> <p>{{forChildMsg}}</p> </div>`, props: { "for-child-msg": String } }; let parentNode = { template: ` <div class="parent"> <div> <span>父组件数据</span> <input v-model="msg"/> </div> <p>{{msg}}</p> <child :for-child-msg="msg"></child> </div>`, components: { child: childNode }, data() { return { msg: "default string." }; } }; 复制代码
传递的过程将短横分割命名,转成驼峰命名法即可
这里我们给父组件和子组件都有一个输入框,并且显示出父组件数据和子组件的数据。当我们在父组件的输入框输入新数据时,同步的子组件数据也被修改了;这就是 props 的向子组件传递数据。而当我们修改子组件的输入框时,浏览器的控制台则报出错误警告
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "forChildMsg"
修改 props 数据
通常有两种原因:
-
prop 作为初始值传入后,子组件想把它当做局部数据来用
-
prop 作为初始值传入后,由子组件处理成其他数据输出
-
定义一个局部变量,并用 prop 的值初始化它
但是由于定义的 ownChildMsg 只能接受 forChildMsg 的初始值,当父组件要传递的值变化发生时,ownChildMsg 无法收到更新。
let childNode = { template: ` <div class="child"> <div> <span>子组件数据</span> <input v-model="forChildMsg"/> </div> <p>{{forChildMsg}}</p> <p>ownChildMsg : {{ownChildMsg}}</p> </div>`, props: { "for-child-msg": String }, data() { return { ownChildMsg: this.forChildMsg }; } }; 复制代码
这里我们加了一个
用于查看 ownChildMsg 数据是否变化,结果发现只有默认值传递给了 ownChildMsg,父组件改变只会变化到 forChildMsg,不会修改 ownChildMsg。
- 定义一个计算属性,处理 prop 的值并返回
由于是计算属性,所以只能显示值,不能设置值。我们这里设置的是一旦从父组件修改了 forChildMsg 数据,我们就把 forChildMsg 加上一个字符串"---ownChildMsg",然后显示在屏幕上。这时是可以每当父组件修改了新数据,都会更新 ownChildMsg 数据的。
let childNode = { template: ` <div class="child"> <div> <span>子组件数据</span> <input v-model="forChildMsg"/> </div> <p>{{forChildMsg}}</p> <p>ownChildMsg : {{ownChildMsg}}</p> </div>`, props: { "for-child-msg": String }, computed: { ownChildMsg() { return this.forChildMsg + "---ownChildMsg"; } } }; 复制代码
- 更加妥帖的方式是使用变量存储 prop 的初始值,并用 watch 来观察 prop 值得变化。发生变化时,更新变量的值。
let childNode = { template: ` <div class="child"> <div> <span>子组件数据</span> <input v-model="forChildMsg"/> </div> <p>{{forChildMsg}}</p> <p>ownChildMsg : {{ownChildMsg}}</p> </div>`, props: { "for-child-msg": String }, data() { return { ownChildMsg: this.forChildMsg }; }, watch: { forChildMsg() { this.ownChildMsg = this.forChildMsg; } } }; 复制代码
19 生命周期
1 vue生命周期简介
2 生命周期探究
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <body> <div id="app">{{message}}</div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> var app = new Vue({ el: '#app', data: { message: "hello is world" }, beforeCreate() { console.group('beforeCreate 创建前状态===============》'); console.log("%c%s", "color:red", "el : " + this.$el); //undefined console.log("%c%s", "color:red", "data : " + this.$data); //undefined console.log("%c%s", "color:red", "message: " + this.message) }, created() { console.group('created 创建完毕状态===============》'); console.log("%c%s", "color:red", "el : " + this.$el); //undefined console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化 console.log("%c%s", "color:red", "message: " + this.message); //已被初始化 }, beforeMount() { console.group('beforeMount 挂载前状态===============》'); console.log("%c%s", "color:red", "el : " + (this.$el)); //已被初始化 console.log(this.$el); console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化 console.log("%c%s", "color:red", "message: " + this.message); //已被初始化 }, mounted() { console.group('mounted 挂载结束状态===============》'); console.log("%c%s", "color:red", "el : " + this.$el); //已被初始化 console.log(this.$el); console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化 console.log("%c%s", "color:red", "message: " + this.message); //已被初始化 }, beforeUpdate() { console.group('beforeUpdate 更新前状态===============》'); console.log("%c%s", "color:red", "el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red", "data : " + this.$data); console.log("%c%s", "color:red", "message: " + this.message); }, updated() { console.group('updated 更新完成状态===============》'); console.log("%c%s", "color:red", "el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red", "data : " + this.$data); console.log("%c%s", "color:red", "message: " + this.message); }, beforeDestroy() { console.group('beforeDestroy 销毁前状态===============》'); console.log("%c%s", "color:red", "el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red", "data : " + this.$data); console.log("%c%s", "color:red", "message: " + this.message); }, destroyed() { console.group('destroyed 销毁完成状态===============》'); console.log("%c%s", "color:red", "el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red", "data : " + this.$data); console.log("%c%s", "color:red", "message: " + this.message) } }) </script> </html> 复制代码
chrome
浏览器里打开, F12
看 console
就能发现
3 beforecreated
el 和 data 并未初始化
4 created
完成了 data 数据的初始化,el没有
5 beforeMount
完成了 el 和 data 初始化
6 mounted
完成挂载
7 update
在console控制台中输入
app.message= 'hello!!'; 复制代码
8 destroy
我们在console里执行下命令对 vue实例进行销毁。销毁完成后,我们再重新改变message的值,vue不再对此动作进行响应了。但是原先生成的dom元素还存在,可以这么理解,执行了destroy操作,后续就不再受vue控制了。
app.$destroy(); 复制代码
9 生命周期总结
9.1 beforecreate
可以在这加个loading事件,加载的动画
9.2 created
在这结束loading,还做一些初始化,实现函数自执行
9.3 mounted
在这发起后端请求,拿回数据,配合路由钩子做一些事情
9.4 beforeDestroy
你确认删除XX吗? destroyed :当前组件已被删除,清空相关内容
20 指令-条件判断(v-if&v-show)
1 v-if&v-show
v-if v-show
if操作的是dom show 操作的样式 如果频繁切换dom使用v-show,当数据一开时就确定下来使用v-if更好一些,如果if通过内部指令不会执行了 只有dom从显示到隐藏 或者隐藏到显示 才能使用vue的动画
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <body> <div id="app"> <span v-if="flag">你看的见我</span> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> var vm = new Vue({ el:'#app', data:{ flag:true } }) </script> </html> 复制代码
2 区别总结
- v-show:操作的是元素的display属性
- v-if:操作的是元素的创建和插入
- 相比较而言v-show的性能要高
21 内置组件-动画(transition)
1 组件的过渡
Vue 提供了 transition
的封装组件,在下列情形中,可以给任何元素和组件添加进入/离开过渡
在进入/离开的过渡中,会有 6 个 class 切换。
-
v-enter
:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。 -
v-enter-active
:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。 -
v-enter-to
: 2.1.8版及以上 定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时v-enter
被移除),在过渡/动画完成之后移除。 -
v-leave
: 定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。 -
v-leave-active
:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。 -
v-leave-to
: 2.1.8版及以上 定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时v-leave
被删除),在过渡/动画完成之后移除。
1.1 初步代码实现
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <style> div>div{ width:100px;height: 100px;background: red; } .v-enter{ opacity: 1; } /* 激活的时候 */ .v-enter-avtive{ opacity: 0; transition: 1s linear; } /* 离开 */ .v-leave-active{ opacity: 0; background: black; transition: 1s linear; } </style> <body> <div id="app"> <button @click="flag=!flag">切换</button> <!-- vue自定义的组件 --> <transition> <div v-show="flag"></div> </transition> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> var vm = new Vue({ el:'#app', data:{ flag:true } }) </script> </html> 复制代码
1.2 多个transition
遇上了多个transition的时候,同一个class肯定是会冲突的,那么如何处理呢
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <style> div>div{ width:100px;height: 100px;background: red; } .jw-enter-active { transition: all .3s ease; } .jw-leave-active { transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0); } .jw-enter, .jw-leave-to { transform: translateX(10px); opacity: 0; } </style> <body> <div id="app"> <button @click="flag=!flag">切换</button> <!-- vue自定义的组件 --> <transition name="jw"> <div v-show="flag"></div> </transition> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> var vm = new Vue({ el:'#app', data:{ flag:true } }) </script> </html> 复制代码
简单的理解就是就 transition有一个name属性
在css中name-状态即可调用
22 自定义指令-directives
1 介绍
Vue 也允许注册自定义指令。注意,在 Vue2.0 中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。
举一个栗子:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <body> <div id="app"> <div v-color='flag'>123</div> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> var vm = new Vue({ directives:{ color(el,bindings){ //el值指代的是button按钮 console.log(arguments); el.style.background = bindings.value; } }, el: '#app', data: { flag: 'red' }, methods:{ getSomething(){ return "hello" } } }) </script> </html> 复制代码
出现如图情况
再来个栗子
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <style> .a{ position: absolute;width: 100px;height: 100px;background: red; } </style> <body> <div id="app"> <div class="a" v-drag></div> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> var vm = new Vue({ directives:{ drag(el){ el.onmousedown = function (e) { var disx = e.pageX - el.offsetLeft; var disy = e.pageY - el.offsetTop; document.onmousemove = function (e) { el.style.left = e.pageX - disx +'px'; el.style.top = e.pageX - disy + 'px'; } document.onmouseup = function (e) { document.onmousemove = document.onmousemove = null; } e.preventDefault(); } } }, el: '#app', data: { flag: 'red' }, methods:{ getSomething(){ return "hello" } } }) </script> </html> 复制代码
可以拖动
2 钩子函数
一个指令定义对象可以提供如下几个钩子函数 (均为可选):
bind
:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
inserted
:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
update
:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
3 钩子函数参数
-
el
:指令所绑定的元素,可以用来直接操作 DOM 。 -
binding
:一个对象,包含以下属性:-
name
:指令名,不包括v-
前缀。 -
value
:指令的绑定值,例如:v-my-directive="1 + 1"
中,绑定值为2
。 -
oldValue
:指令绑定的前一个值,仅在update
和componentUpdated
钩子中可用。无论值是否改变都可用。 -
expression
:字符串形式的指令表达式。例如v-my-directive="1 + 1"
中,表达式为"1 + 1"
。 -
arg
:传给指令的参数,可选。例如v-my-directive:foo
中,参数为"foo"
。 -
modifiers
:一个包含修饰符的对象。例如:v-my-directive.foo.bar
中,修饰符对象为{ foo: true, bar: true }
。
-
-
oldVnode
:上一个虚拟节点,仅在update
和componentUpdated
钩子中可用。
23 实例属性-$ref
官网针对-ref的解释
- 预期 :
string
ref
被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的$refs
对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例:
<!-- `vm.$refs.p` will be the DOM node --> <p ref="p">hello</p> <!-- `vm.$refs.child` will be the child component instance --> <child-component ref="child"></child-component> 复制代码
-
当
v-for
用于元素或组件的时候,引用信息将是包含 DOM 节点或组件实例的数组。关于 ref 注册时间的重要说明:因为 ref 本身是作为渲染结果被创建的,在初始渲染的时候你不能访问它们 - 它们还不存在!
$refs
也不是响应式的,因此你不应该试图用它在模板中做数据绑定。
操作dom
如果我们用jQuery的话,一般性都可以操作dom
$("#id").text('xxx') // 使用Jquery document.getElementById("id") // 使用原生Dom 复制代码
现在我们牛逼了,我们用vue。那vue中,如果我要获取Dom,该怎么做?
这就进入本文的主题ref, $refs,官网解释:
<div id="app"> <div>{{msg}}</div> </div> 复制代码
在JavaScript中我们习惯了使用document.getElementsByTagName
```javascript var vm = new Vue({ el: '#app', data:{ msg:'hello' }, mounted() { console.log(document.getElementsByTagName("div")[0].innerHTML); } }) ```
vue操作dom
那么我们在vue中呢
<div id="app"> <div ref="msg">{{msg}}</div> </div> 复制代码
var vm = new Vue({ el: '#app', data:{ msg:'hello' }, mounted() { // console.log(document.getElementsByTagName("div")[0].innerHTML); console.log('===================================='); console.log(this.$refs.msg); console.log('===================================='); } }) 复制代码
以上所述就是小编给大家介绍的《Vue.js 2.0 手把手入门笔记》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
计算机网络(第6版)
[美] James F.Kurose、[美] Keith W.Ross / 陈鸣 / 机械工业出版社 / 2014-10 / 79.00元
《计算机网络:自顶向下方法(原书第6版)》第1版于12年前出版,首创采用自顶向下的方法讲解计算机网络的原理和协议,出版以来已被几百所大学和学院选用,是业界最经典的计算机网络教材之一。 《计算机网络:自顶向下方法(原书第6版)》第6版继续保持了以前版本的特色,为计算机网络教学提供了一种新颖和与时俱进的方法,同时也进行了相当多的修订和更新:第1章更多地关注时下,更新了接入网的论述;第2章用pyt......一起来看看 《计算机网络(第6版)》 这本书的介绍吧!