一、Vue核心(上篇)
热身tops:选取开发模式
①用于开发模式 我们只需要知道 我们是开发模式,开发模式他会跟你提示代码出现错误的地方以及出错原因,而生产模式比较简洁。
②用于生产模式
1.1 new Vue()实例
了解Vue:Vue 构造函数是 vue.js 框架中用于创建 Vue 实例的核心部分,而 vue.js 框架则是一个包含了这个构造函数以及许多其他功能和工具的完整系统。注:后半句话不太理解,先这样,后面理解后会更新 !
(1)关于vue实例
- 想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象;创建vue实例语法:new Vue({ 配置对象 })
- 配置对象包含很多配置项,也就是属性或者方法,常见的如:data,el,methods等。
- root容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法;如插值语法
- root容器里的代码被称为【Vue模板】
<!-- 准备好一个容器 -->
<div id="root">
<!-- 插值语法 -->
<h1>hello,{{name}}</h1>
</div>
<script type="text/javascript">
//死记硬背:开发的时候写着这个就好了 减少不必要的提示
Vue.config.productionTip = false
<!-- 创建vue实例 -->
new Vue({
el: '#root', //el用于指定当前vue实例为哪个容器服务,值通常为css选择器字符串
data: { //data用于存储数据,数据供el所指定的容器去使用,值通常是对象
name: '哈哈哈'
}
}
)
</script>
1.2 模板语法
Vue模板语法两大类:插值语法 指令语法
(1).插值语法
功能:可以解析标签体的内容(标签是html标签)
语法:{{xxxx}} xxxx是js表达式 可以直接读取data中的所有属性(意味着即使是data中的方法,也可以直接将方法属性插入到模版中,将结果直接返回);如果data出现多个相同名字的属性,使用多层,需要用点来引用
<div id="root">
<h1>插值语法</h1>
<h3>你好,{{name}}</h3>
</div>
<script>
Vue.config.productionTip = false
new Vue({
el: ' #root',
data: {
name: 'jack'
}
}) </script>
讲一下vue模版中的JS表达式
vue模版可以直接访问js表达式,这意味着即使是name属性也可是直接访问JS表达式,相当于将name当对象来用
区分js表达式和js代码(语句)在 Vue 的模板中,可以使用 JavaScript 表达式,但不能直接添加 JavaScript 代码块(如
if语句、for循环等)。
因为Vue 的模板是声明式的,意味着你告诉 Vue 你想要什么,而不是如何得到它。
***js表达式 和 js代码(语句)区分***
1.表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方:
(1). a
(2). a+b
(3). demo(1)
(4). x === y ? 'a' : 'b'
2.js代码(语句)
(1). if(){}
(2). for(){}
<!-- 准备好一个容器 -->
<div id="root">
<!-- 13:03 -->
<!-- 函数调用表达式 -->
<h1>hello,{{name.toUpperCase()}},{{address}},{{Date.now()}}</h1>
</div>
(2).指令语法
Vue中指令语法通常是 v-
功能:用于解析标签(包括:标签属性(也就是可以直接对标签属性进行操作)、标签体内容、绑定事件、、、)。
即:不再像插值语法局限标签体内容,可以直接利用data中的数据对标签属性值进行赋值,属性值还可以加上js表达式或者变量;重新给标签设置属性(注意:在data数据中设置好属性和属性值)
意思是当我使用V-bind指令时,实际上是将html属性的值给绑定在vue实例对象某个数据属性上,这样当vue实例该数据属性的值发生变化,html属性的值也相应发生变化
<div id="root">
<!--v-bind:缩写为: -->
//这里的x实际上是重新给标签设置属性(注意:在data数据中设置好属性和属性值)
<a v-bind:href='url' :x="hello">去{{school.name}}吧</a>
<!-- <a :href='school.url.toUpperCase()' :x="school.hello">去{{school.name}}吧</a> -->
</div>
<script>
Vue.config.productionTip = false
new Vue({
el: ' #root',
data: {
name: 'jack',
// 两个相同属性,不能在同一层
school: {
name: '百度',
url: 'https://www.baidu.com/',
hello: '你好'
}
}
}) </script>
单项数据绑定:<input type="text" v-bind:value="name"><br>
这里的双向数据绑定:绑定的就是inout框中value值,通过双向绑定,来实现模版视图和data中数据的交互
双向数据绑定:<input type="text" v-model:value="name"><br>
单向双向绑定案例一起写:
<!-- 准备一个容器 -->
<div id="root">
单项数据绑定:<input type="text" v-bind:value="name"><br>
双向数据绑定:<input type="text" v-model:value="name"><br>
<!-- 简写:
单项数据绑定:<input type="text" :value="name"><br>
双向数据绑定:<input type="text" v-model="name"> -->
<!-- v-model只能应用在表单元素(输入类元素)上 -->
<!-- <h2 v-bind:x="name">你好啊</h2> -->
</div>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: "#root",
data: {
name: '哈哈哈'
}
})
</script>
就会有神奇的化学反应:这里用abc代指。那就是只要b发生改变,a、c都会紧接着改变,大家会觉得奇怪,c发生改变是因为双向绑定特性,但是a为什么改变,这是因为b发生改变,因为双向绑定,c发生改变;c发生改变,因为c、a之间有单项绑定,c改变,所以a改变。
1.4 el与data的两种写法
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
// el: '#root',//指定容器 第一种写法
// 函数式 第二种写法的简写式:
// data () {
// return {
// name: '哈哈哈哈'
// }
// }
// 使用普通函数
// data: function () {
// return {
// name: '哈哈哈'
// }
// }
// 第二种写法的简写式:使用箭头函数
// 箭头函数在vue实例中一般不要添加 因为需要将this指向vue实例 而不是其他
data: () => {
console.log('@@@', this) //此处的this
return {
name: '哈哈哈'
}
}
})
// 指定容器 挂载式
vm.$mount('#root')
</script>
1.5 Vue中的MVVM
Vue是基于MVVM构成的
M模型 对应data中的数据
V视图:模板 DOM:vue模版经过解析形成的页面从而形成dom结构 所以模版也是页面结构
VM 视图模型 Vue实例对象
VM:Vue实例对象
实例对象vm的由来:通过Viewmodel 可以实现视图与数据的交互 而且通过vue实例可以获得viewmodel 所以叫vm
Vue中MVVM之间的关系:
这样:实例对象vm对象中的数据经过数据绑定就绑定在页面上的DOM上面了
DOM结构经过监听就返回到数据中了
数据经过数据绑定形成页面上的dom的页面结构
反映在代码上:
有下面的案例可以知道:
data中的属性经过vue内部一系列sao操作,可以显示vue实例对象中;而vue实例对象中的所有属性直接间接都可以被展示在页面视图中。
<!-- 容器 视图 V -->
<div id="root">
<h1>学校名称:{{name}}</h1>
<h1>学校地址:{{address}}</h1>
<h1>{{$options}}</h1>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
// Vue实例 vm
const vm = new Vue({
el: '#root',
// 数据 M
data: {
name: '尚硅谷',
address: '北京',
a: 1
}
})
console.log(vm);
1.6 回顾Object.defineProperty方法
<script>
let number = 20
let person = {
name: '张三',
sex: '男',
// age: number
}
// 访问age属性 默认会调用此函数
//getter 函数在尝试读取 age 属性的值时会被调用,而 setter 函数在尝试设置 age 属性的值时
//会被调用。 定义对象中的新属性
Object.defineProperty(person, 'age', {
// value: 18,
// enumerable: true, //控制属性是否可以枚举
// writable: true, //控制属性是否可以被修改 默认值是false
// configurable: true, //控制属性是否可以被删除 默认值是false
get () {
console.log('有人读取age属性了')
return number
},
set (value) {
console.log('有人修改了age属性,且是', value)
number = value
}
})
// console.log(Object.keys(person));
// for in遍历对象
// for (let key in person) {
// console.log('@', person[key])
// }
// console.log(person);
</script>
1.7 数据代理
// 数据代理:通过一个对象代理对另一个对象中属性的操作(读 / 写)
let obj = { x: 100 }
let obj2 = { y: 200 }
//对A对象的访问实现对B对象的访问
Object.defineProperty(obj2, 'x', {
get () {
return obj.x
}
, set (value) {
obj.x = value
}
})
页面的数据发生改变:
一旦data中的数据发生改变,那么页面中用到该数据的地方也会自动更新
数据代理总结:
1.Vue中的数据代理:
通过vm对象来代理data对象中属性的操作(读/写)
2.Vue中数据代理的好处:
更加方便的操作data中的数据
3.基本原理:
通过Object.defineProperty()把data对象中所有属性添加到vm上。
为每一个添加到vm上的属性,都指定一个getter/setter。
在getter/setter内部去操作(读/写)data中对应的属性。
1.8 事件处理
(1)事件的基本使用
- 回调函数中传参 要加上(),并且不要忘记event,便于获取event.target.innerHTML,$event
- vm中showinfo方法和data数据的区别是showinfo不进行数据代理,为什么?——(1)顾名思义,showinfo是函数,是方法,通过别人调用他来工作的,写完是不改变的,只需要等着被调用;但是data中的数据name是数据,如果从hi1,改变为hi2,需要数据代理更好地改变数据。(2)其实showinfo放在data中也可以被调用(以下将showinfo2放在data中),但是用到了getter和setter,因为方法只会被调用,不会被更改,这样就错误滴使用了数据代理。
const vm = new Vue({
el: '#root',
data: {
name: '北京',
showInfo2 (number, event) { //所有被vue管理的函数最好写成普通函数
alert('同学你好2')
console.log(number, event)
// console.log(event.target.innerText)
// console.log(this) //此处的this是vm
}
},
//配置项
methods: {
showInfo1 (event) { //所有被vue管理的函数最好写成普通函数
alert('同学你好1')
// console.log(event.target.innerText)
// console.log(this) //此处的this是vm
}
}
});
- 总结:事件的基本使用:
1.使用v-on:xxx 或 @xxx 绑定事件,其中xxx是事件名;
2.事件的回调需要配置在methods对象中,最终会在vm上;
3.methods中配置的函数,不要用箭头函数!否则this就不是vm了;
4.methods中配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例对象;
5.@click=“demo” 和 @click=“demo($event)” 效果一致,但后者可以传参;
(2)事件修饰符
注意:stopPropagation() 方法的作用就是阻止事件在捕获和冒泡阶段的进一步传播。也就是说,当你调用一个事件对象(通常通过事件处理函数中的 event 参数获取)的 stopPropagation() 方法时,该事件将不会继续传播到 DOM 树中的其他节点。
preventDefault 阻止默认行为
capture js事件流 捕获阶段+冒泡阶段 触发box2传2 先捕获在冒泡 捕获阶段 外=》内 冒泡阶段由内岛外 开始执行 捕获阶段就开始处理
self 不加上 则可以有两次冒泡并且是一样的 加了self相当于组织了冒泡阶段
passive 事件的默认行为立即执行 无需等待事件回调执行完毕 感觉用的地方太少 不了解了!!!最后还是写了解决哈哈哈哈
这里使用到了@scroll 滚动条滚动
@wheel 滚轮滚动 对这个有用 不加passive 需要限制性回调函数 那么回调函数执行for循环之后才能完成滚动条的滚动 但我自己实际上实践的时候 其实并没有完全执行完or循环才滚动条滚动 而是一会儿就滚懂了
并且在使用@scroll 无论for循环多大 滚动条都会直接滚动 这也说明passive并不是什么事件都用得上的 在某些特定事件
<!-- passive 事件的默认行为立即执行 无需等待事件回调执行完毕 -->
<!-- 滚动条的滚动 鼠标轮的滚动 -->
<ul @wheel.passive="demo" class="list">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
demo () {
// console.log('@')
for (let i = 0; i < 100000; i++) {
console.log('#')
}
console.log('累坏了')
}
键盘事件
1.Vue中常用的按键别名:
回车 => enter
删除 => delete (捕获“删除”和“退格”键)
退出 => esc
空格 => space
换行 => tab (特殊,必须配合keydown去使用)
上 => up
下 => down
左 => left
右 => right
2.Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名)
3.系统修饰键(用法特殊):ctrl、alt、shift、meta
(1).配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
(2).配合keydown使用:正常触发事件。
4.也可以使用keyCode去指定具体的按键(不推荐)
5.Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名
1.8 计算属性
姓名案例——插值语法实现
缺点:实现都需要在vue模板中实现,vue官网说的是Vue 的模板是声明式的,意味着你告诉 Vue 你想要什么,而不是如何得到它。要求vue模版中尽量语法简洁,所以不适合在这里面那实现很多方法
<div id="root">
姓:<input type="text" v-model="firstName"></br></br>
名:<input type="text" v-model="lastName"></br></br>
<!-- vue模版中的语句应该尽量简化 此时如果还要让填进去的字母有小写变成大写,还需要继续添加代码,太复杂了-->
姓名:<span>{{firstName.slice(0,3)}}-{{lastName}}</span>
</div>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三'
}
})
姓名案例——methods实现
先说一下:插值语法与指令语法加()与不加()的区别
重点理解:数据一变化vue模板就会重新解析,自然里面的方法也会重新调用一下fullName,所以全名也会随着变化
<div id="root">
姓:<input type="text" v-model="firstName"></br></br>
名:<input type="text" v-model="lastName"></br></br>
全名: <span>{{fullName()}}</span>
<!-- <button @click="fullName()">点我</button> -->
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三'
},
methods: {
fullName () {
console.log('数据一变化vue模板就会重新解析,自然里面的方法也会重新调用一下fullName')
return this.firstName + '-' + this.lastName
}
}
});
</script>
计算属性
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="../../js/vue.js"></script>
</head>
<body>
<div id="root">
姓:<input type="text" v-model="firstName"><br><br>
名:<input type="text" v-model="lastName"><br><br>
全名:<span>{{fullName}}</span><br><br>
全名:<span>{{fullName}}</span><br><br>
全名:<span>{{fullName}}</span><br><br>
全名:<span>{{fullName}}</span><br><br>
全名:<span>{{fullName}}</span>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三'
},
computed: {
// fullName: {
// // get的作用:当有人读取fullName时,get就会被调用且返回值就作为fullName的值
// //get什么时候被调用?1.初次读取fullName时 2.所依赖的数据发生变化时
// // 这里提到一个缓存,也就是所赐调用 只有在初次调用的时候才起效果 vu'e会将返回值保存起来 并且起名fullName 与methods方法不同
// get () {
// console.log('get被调用了')
// // console.log(this)
// return this.firstName + '-' + this.lastName
// },//set什么时候被调用?当fullName被修改时
// set (value) {
// console.log('set', value)
// // 在每个分隔符处将字符串分割为子串 并返回为数组
// const arr = value.split('-')
// this.firstName = arr[0]
// this.lastName = arr[1]
// }
// }
// 只考虑读取 不考虑修改 简写
// fullName: function () {
// console.log('get被调用了')
// return this.firstName + '-' + this.lastName
// }
fullName () {
return this.firstName + '-' + this.lastName
}
}
});
</script>
</body>
</html>