Vue的基础入门及使用
第一章 Vue核心
1.1 Vue的基本认识
1.1.1 官网
- 英文官网:https://vuejs.org/
- 中文官网:https://cn.vuejs.org/
1.1.2 介绍描述
- 渐进式JavaScript框架
- 作者:尤雨溪(一位华裔前Google工程师)
- 作用:动态 构建用户界面
1.1.3 Vue特点
- 1.遵循MVVM模式
- M:Model,模型,数据对象(data)
- V:View,视图,模板页面
- VM:ViewModel,视图模型(Vue的实例)
- 2.编码简洁,体积小,运行效率高,适合移动/PC端开发
- 3.它本身只关注UI,可以轻松引入Vue插件或其他第三库开发项目
1.1.4 Vue的对象
-
el:
- 指定dom标签容器的选择器
- Vue就会管理对应的标签及其子标签
-
data:
- 对象或函数类型
- 指定初始化状态属性数据的对象
- vm也会自动拥有data中所有属性
- 页面中可以直接访问使用
- 数据代理:由vm对象来代理对data中所有属性的操作(读/写)
-
methods:
- 包含多个方法的对象
- 供页面中的时间指令来绑定回调
- 回调函数默认有event参数,但也可以指定自己的参数
- 所有的方法由vue对象来调用,访问data中的属性可以直接使用this.xxx
-
computed:
- 包含多个方法的对象
- 对状态属性进行计算返回一个新的数据,供页面获取显示
- 一般情况下是相当于是一个只读的属性
- 利用set/get方法来实现属性数据的计算读取,同时监视属性数据的变化
- 如何给对象定义get/set属性
- 在创建对象时指定:get name(){return xxx}/set name(value) {}
- 对象创建之后指定:Object.defineProperty(obj,age,{get(){},set(value){}})
-
watch
-
包含多个属性监视的对象
-
分为一般监视和深度监视
xxx:function(value){} xxx:{ deep:true, handler:fun(value) }
-
另一种添加监视方法:vm.$watch(‘xxx’,function(value){})
-
1.1.5 与其他前端JS框架的关联
- 借鉴Angular的模板和数据绑定技术
- 借鉴React的组件和虚拟DOM技术
1.1.6 Vue扩展插件
-
vue-cli:vue脚手架
-
vue-resouce(axios):ajax请求
-
vue-route:路由
-
vuex:状态管理
-
vue-lazyload:图片懒加载
-
vue-scoller:页面滑动相关
-
mint-ui:基于vue的UI组件库(移动端)
-
element-ui:基于vue的UI组件库(PC端)
1.2 Vue的基本使用
1.2.1 永远的HelloWorld
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>01_HelloWorld</title>
</head>
<body>
<!--
1. 引入Vue.js
2. 创建Vue对象
el : 指定根element(选择器)
data : 初始化数据(页面可以访问)
3. 双向数据绑定 : v-model
4. 显示数据 : {{xxx}}
5. 理解vue的mvvm实现
-->
<!--模板-->
<div id="test">
<input type="text" v-model="msg"><br><!--指令:element.value='xxx'-->
<input type="text" v-model="msg"><!--指令-->
<p>Hello {{msg}}</p><!--大括号表达式-->
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
const vm = new Vue({ // 配置对象 options
// 配置选项(option)
el: '#test', // element: 选择器,指定用vue来管理页面中的哪个标签区域
data: {
msg: 'World'
}
})
</script>
</body>
</html>
1.2.2 Vue的调试工具
打开Vue页面,F12进入开发者调试,选择Vue,即可使用该调试工具。
1.2.3 Vue的MVVM
1.3 Vue的模板语法
1.3.1 模板的理解:
- 动态的HTML页面
- 包含了一些JS语法代码
- 大括号表达式
- 指令(以v-开头的自定义标签属性)
1.3.2 代码实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>模板语法</title>
</head>
<body>
<!--
1. 模板的理解:
动态的html页面
包含了一些JS语法代码
大括号表达式
指令(以v-开头的自定义标签属性)
2. 双大括号表达式
语法: {{exp}} 或 {{{exp}}}
功能: 向页面输出数据
可以调用对象的方法
3. 指令一: 强制数据绑定
功能: 指定变化的属性值,比如将HTML语言改变为字符串
完整写法:
v-bind:xxx='yyy' //yyy会作为表达式解析执行
简洁写法:
:xxx='yyy'
4. 指令二: 绑定事件监听
功能: 绑定指定事件名的回调函数
完整写法:
v-on:click='xxx'
简洁写法:
@click='xxx'
-->
<div id="app">
<h2>1. 双大括号表达式</h2>
<p>{{content}}</p>
<p>{{content.toUpperCase()}}</p>
<h2>2. 指令一: 强制数据绑定</h2>
<a href="url">访问指定站点</a><br>
<a v-bind:href="url">访问指定站点2</a><br>
<a :href="url">访问指定站点2</a><br>
<h2>3. 指令二: 绑定事件监听</h2>
<button v-on:click="test">点我</button>
<button @click="test">点我</button>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
new Vue({
el: '#app',
data: {
content: 'NBA I Love This Game',
url: 'http://www.baidu.com'
},
methods: {
test () {
alert('好啊!!!')
}
}
})
</script>
</body>
</html>
1.4 计算属性和监视
1.4.1 计算属性
- 在computed属性对象中定义计算属性的方法
- 在页面中使用{{方法名}}来显示计算的结果
1.4.2 监视属性
- 通过VM对象的$watch()或watch配置来监视指定的属性
- 当属性变化时,回调函数自动调用,在函数内部进行计算
1.4.3 计算属性高级
- 通过getter/setter实现对属性数据的显示和监视
- 计算属性存在缓存,多次读取只执行一次getter计算
1.4.5 代码实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>计算属性和监视</title>
</head>
<body>
<!--
1. 计算属性
在computed属性对象中定义计算属性的方法
在页面中使用{{方法名}}来显示计算的结果
2. 监视属性:
通过通过vm对象的$watch()或watch配置来监视指定的属性
当属性变化时, 回调函数自动调用, 在函数内部进行计算
3. 计算属性高级:
通过getter/setter实现对属性数据的显示和监视
计算属性存在缓存, 多次读取只执行一次getter计算
-->
<div id="demo">
姓: <input type="text" placeholder="First Name" v-model="firstName"><br>
名: <input type="text" placeholder="Last Name" v-model="lastName"><br>
<!--fullName1是根据fistName和lastName计算产生-->
姓名1(单向): <input type="text" placeholder="Full Name1" v-model="fullName1"><br>
姓名2(单向): <input type="text" placeholder="Full Name2" v-model="fullName2"><br>
姓名3(双向): <input type="text" placeholder="Full Name3" v-model="fullName3"><br>
<p>{{fullName1}}</p>
<p>{{fullName1}}</p>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
const vm = new Vue({
el: '#demo',
data: {
firstName: 'A',
lastName: 'B',
fullName2: 'A-B'
},
// 计算属性配置: 值为对象
computed: {
//什么时候执行:初始化显示/相关的data属性发生变化
fullName1 () { // 属性的get()
console.log('fullName1()', this)
return this.firstName + '-' + this.lastName
},
fullName3: {
// 回调函数的三个属性:1.你定义的 2.你没有调用 3.但是它执行了
// 回调函数:什么时候调用?用来做什么
// 当获取当前属性值时自动调用, 将返回值(根据相关的其它属性数据)作为属性值
get () {
console.log('fullName3 get()')
return this.firstName + '-' + this.lastName
},
// 当属性值发生了改变时自动调用, 监视当前属性值变化, 同步更新相关的其它属性值
set (value) {// fullName3的最新value值 A-B23
console.log('fullName3 set()', value)
// 更新firstName和lastName
const names = value.split('-')
this.firstName = names[0]
this.lastName = names[1]
}
}
},
watch: {
// 配置监视firstName
firstName: function (value) { // firstName发生了变化,相当于属性的set
console.log('watch firstName', value)
// 更新fullName2
this.fullName2 = value + '-' + this.lastName
}
}
})
// 监视lastName
vm.$watch('lastName', function (value) {
console.log('$watch lastName', value)
// 更新fullName2
this.fullName2 = this.firstName + '-' + value
})
</script>
</body>
</html>
1.5 class与style绑定
1.5.1 理解
- 在应用界面上,某个(些)元素的样式是变化的
- class/style绑定就是专门用来实现动态样式效果的技术
1.5.2 class绑定
- :class=‘xxx’
- 表达式是字符串:‘classA’
- 表达式是对象:{classA:isA,classB:isB}
- 表达式是数组:[‘classA’,‘classB’]
1.5.3 style绑定
- :style="{color:activeColor,fontSize:fontSize+‘px’}"
- 必须是对象
- 其中activeColor/fontSize 是data属性
1.5.4 代码实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>class与style绑定</title>
<style>
.classA {
color: red;
}
.classB {
background: blue;
}
.classC {
font-size: 20px;
}
</style>
</head>
<body>
<!--
1. 理解
在应用界面中, 某个(些)元素的样式是变化的
class/style绑定就是专门用来实现动态样式效果的技术
2. class绑定: :class='xxx'
xxx是字符串
xxx是对象
xxx是数组
3. style绑定
:style="{ color: activeColor, fontSize: fontSize + 'px' }"
其中activeColor/fontSize是data属性
-->
<div id="demo">
<h2>1. class绑定: :class='xxx'</h2>
<p :class="myClass">xxx是字符串</p>
<p :class="{classA: hasClassA, classB: hasClassB}">xxx是对象</p>
<p :class="['classA', 'classB']">xxx是数组</p>
<h2>2. style绑定</h2>
<p :style="{color:activeColor, fontSize}">:style="{ color: activeColor, fontSize: fontSize + 'px' }"</p>
<button @click="update">更新</button>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
new Vue({
el: '#demo',
data: {
myClass: 'classA',
hasClassA: true,
hasClassB: false,
activeColor: 'red',
fontSize: '20px'
},
methods: {
update () {
this.myClass = 'classB'
this.hasClassA = !this.hasClassA
this.hasClassB = !this.hasClassB
this.activeColor = 'yellow'
this.fontSize = '30px'
}
}
})
</script>
</body>
</html>
1.6 条件渲染
1.6.1 条件渲染指令
- v-if与v-else
- v-show
1.6.2 比较v-if与v-show
- 如果需要频繁切换,v-show较好
- 当条件不成立时,v-if的所有子节点不会解析(项目中使用)
1.6.3 代码实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>条件渲染</title>
</head>
<body>
<!--
1. 条件渲染指令
v-if
v-else
v-show
2. 比较v-if与v-show
如果需要频繁切换 v-show 较好
-->
<div id="demo">
<p v-if="ok">表白成功</p>
<p v-else>表白失败</p>
<hr>
<p v-show="ok">求婚成功</p>
<p v-show="!ok">求婚失败</p>
<button @click="ok=!ok">切换</button>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
new Vue({
el: '#demo',
data: {
ok: true,
}
})
</script>
</body>
</html>
1.7 列表渲染
1.7.1 列表显示指令
- 数组:v-for/index
- 对象:v-for/key
1.7.2 列表的更新显示
- 删除item
- 替换item
1.7.3 列表的高级处理
- 列表过滤
- 列表排序
1.7.4 列表显示代码实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>列表渲染</title>
</head>
<body>
<!--
1. 列表显示
数组: v-for / index
对象: v-for / key
2. 列表的更新显示
删除item
替换item
-->
<div id="demo">
<h2>测试: v-for 遍历数组</h2>
<ul>
<li v-for="(p, index) in persons" :key="index">
{{index}}--{{p.name}}--{{p.age}}
--<button @click="deleteP(index)">删除</button>
--<button @click="updateP(index, {name:'Cat', age: 16})">更新</button>
</li>
</ul>
<button @click="addP({name: 'xfzhang', age: 18})">添加</button>
<h2>测试: v-for 遍历对象</h2>
<ul>
<li v-for="(item, key) in persons[1]" :key="key">{{key}}={{item}}</li>
</ul>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
new Vue({
el: '#demo',
data: {
persons: [
{name: 'Tom', age:18},
{name: 'Jack', age:17},
{name: 'Bob', age:19},
{name: 'Mary', age:16}
]
},
methods: {
deleteP (index) {
this.persons.splice(index, 1) // 调用了不是原生数组的splice(), 而是一个变异(重写)方法
// 1. 调用原生的数组的对应方法
// 2. 更新界面
},
updateP (index, newP) {
console.log('updateP', index, newP)
// this.persons[index] = newP // vue根本就不知道
this.persons.splice(index, 1, newP)
// this.persons = []
},
addP (newP) {
this.persons.push(newP)
}
}
})
</script>
</body>
</html>
1.7.5 列表的搜索排序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>过滤与排序</title>
</head>
<body>
<!--
1. 列表过滤
2. 列表排序
-->
<div id="demo">
<input type="text" v-model="searchName">
<ul>
<li v-for="(p, index) in filterPersons" :key="index">
{{index}}--{{p.name}}--{{p.age}}
</li>
</ul>
<div>
<button @click="setOrderType(2)">年龄升序</button>
<button @click="setOrderType(1)">年龄降序</button>
<button @click="setOrderType(0)">原本顺序</button>
</div>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
new Vue({
el: '#demo',
data: {
searchName: '',
orderType: 0, // 0代表不排序, 1代表降序, 2代表升序
persons: [
{name: 'Tom', age:18},
{name: 'Jack', age:17},
{name: 'Bob', age:19},
{name: 'Mary', age:16}
]
},
computed: {
filterPersons () {
// debugger
// 取出相关数据
const {searchName, persons, orderType} = this
let arr = [...persons]
// 过滤数组
if(searchName.trim()) {
arr = persons.filter(p => p.name.indexOf(searchName)!==-1)
}
// 排序
if(orderType) {
arr.sort(function (p1, p2) {
if(orderType===1) { // 降序
return p2.age-p1.age
} else { // 升序
return p1.age-p2.age
}
})
}
return arr
}
},
methods: {
setOrderType (orderType) {
this.orderType = orderType
}
}
})
</script>
</body>
</html>
1.8 事件处理
1.8.1 绑定监听
- v-on:xxx=“fun”
- @xxx=“fun”
- @xxx=“fun(参数)”
- 默认事件形参:event
- 隐含属性对象:$event
1.8.2 事件修饰符
- .prevent:阻止事件的默认行为event.preventDefault()
- .stop:停止事件冒泡 event.stopPropagation()
1.8.3 按键修饰符
- .keycode:操作的是某个keycode值的键
- .keyName:操作的是某个按键名的键(少部分)
1.8.4 代码实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>07_事件处理</title>
</head>
<body>
<!--
1. 绑定监听:
v-on:xxx="fun"
@xxx="fun"
@xxx="fun(参数)"
默认事件形参: event
隐含属性对象: $event
2. 事件修饰符:
.prevent : 阻止事件的默认行为 event.preventDefault()
.stop : 停止事件冒泡 event.stopPropagation()
3. 按键修饰符
.keycode : 操作的是某个keycode值的健
.enter : 操作的是enter键
-->
<div id="example">
<h2>1. 绑定监听</h2>
<button @click="test1">test1</button>
<button @click="test2('abc')">test2</button>
<button @click="test3('abcd', $event)">test3</button>
<h2>2. 事件修饰符</h2>
<a href="http://www.baidu.com" @click.prevent="test4">百度一下</a>
<div style="width: 200px;height: 200px;background: red" @click="test5">
<div style="width: 100px;height: 100px;background: blue" @click.stop="test6"></div>
</div>
<h2>3. 按键修饰符</h2>
<input type="text" @keyup.13="test7">
<input type="text" @keyup.enter="test7">
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
new Vue({
el: '#example',
data: {
},
methods: {
test1(event) {
alert(event.target.innerHTML)
},
test2 (msg) {
alert(msg)
},
test3 (msg, event) {
alert(msg+'---'+event.target.textContent)
},
test4 () {
alert('点击了链接')
},
test5 () {
alert('out')
},
test6 () {
alert('inner')
},
test7 (event) {
console.log(event.keyCode)
alert(event.target.value)
}
}
})
</script>
</body>
</html>
1.9 表单输入绑定
1.9.1 使用v-model对表单数据自动收集
- text/textarea 文本
- checkbox 多选
- radio 单选
- select 选择
1.9.2 代码实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>表单输入绑定</title>
</head>
<body>
<!--
1. 使用v-model(双向数据绑定)自动收集数据
text/textarea
checkbox
radio
select
-->
<div id="demo">
<form action="/xxx" @submit.prevent="handleSubmit">
<span>用户名: </span>
<input type="text" v-model="username"><br>
<span>密码: </span>
<input type="password" v-model="pwd"><br>
<span>性别: </span>
<input type="radio" id="female" value="女" v-model="sex">
<label for="female">女</label>
<input type="radio" id="male" value="男" v-model="sex">
<label for="male">男</label><br>
<span>爱好: </span>
<input type="checkbox" id="basket" value="basket" v-model="likes">
<label for="basket">篮球</label>
<input type="checkbox" id="foot" value="foot" v-model="likes">
<label for="foot">足球</label>
<input type="checkbox" id="pingpang" value="pingpang" v-model="likes">
<label for="pingpang">乒乓</label><br>
<span>城市: </span>
<select v-model="cityId">
<option value="">未选择</option>
<option :value="city.id" v-for="(city, index) in allCitys" :key="city.id">{{city.name}}</option>
</select><br>
<span>介绍: </span>
<textarea rows="10" v-model="info"></textarea><br><br>
<input type="submit" value="注册">
</form>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
new Vue({
el: '#demo',
data: {
username: '',
pwd: '',
sex: '男',
likes: ['foot'],
allCitys: [{id: 1, name: 'BJ'}, {id: 2, name: 'SS'}, {id: 3, name: 'SZ'}],
cityId: '2',
info: ''
},
methods: {
handleSubmit () {
console.log(this.username, this.pwd, this.sex, this.likes, this.cityId, this.info)
alert('提交注册的ajax请求')
}
}
})
</script>
</body>
</html>
1.10 Vue实例生命周期
1.10.1 生命周期流程图
1.10.2 vue生命周期分析
- 1.初始化显示
- beforeCreate()
- created()
- beforeMount()
- mounted()
- 2.更新状态:this.xxx=value
- beforeUpdate()
- updated()
- 3.销毁vue实例:vm.$destory()
- beforeDestory()
- destoryed()
1.10.3 常用的生命周期方法
- created()/mounted():发送ajax请求,启动定时器等异步任务
- beforeDestory():做收尾工作,如:清除定时器
1.10.4 代码实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>生命周期</title>
</head>
<body>
<!--
1. vue对象的生命周期
1). 初始化显示
* beforeCreate()
* created()
* beforeMount()
* mounted()
2). 更新状态
* beforeUpdate()
* updated()
3). 销毁vue实例: vm.$destory()
* beforeDestory()
* destoryed()
2. 常用的生命周期方法
created()/mounted(): 发送ajax请求, 启动定时器等异步任务
beforeDestory(): 做收尾工作, 如: 清除定时器
-->
<div id="test">
<button @click="destroyVue">destory vue</button>
<p v-if="isShow">尚硅谷IT教育</p>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
new Vue({
el: '#test',
data: {
isShow: true
},
beforeCreate() {
console.log('beforeCreate()')
},
created() {
console.log('created()')
},
beforeMount() {
console.log('beforeMount()')
},
mounted () {
console.log('mounted()')
// 执行异步任务
this.intervalId = setInterval(() => {
console.log('-----')
this.isShow = !this.isShow
}, 1000)
},
beforeUpdate() {
console.log('beforeUpdate()')
},
updated () {
console.log('updated()')
},
beforeDestroy() {
console.log('beforeDestroy()')
// 执行收尾的工作
clearInterval(this.intervalId)
},
destroyed() {
console.log('destroyed()')
},
methods: {
destroyVue () {
this.$destroy()
}
}
})
</script>
</body>
</html>
1.11 过渡&动画
1.11.1 vue动画的理解
- 操作css的trasition或animation
- vue会给目标元素添加/移除特定的class
- 过渡的相关类名
- xxx-enter-active:指定显示的transition
- xxx-leave-action:指定隐藏的transition
- xxx-enter/xxx-leave-to:指定隐藏时的样式
1.11.2 基本过渡动画的编码
- 在目标元素外包裹
- 定义calss样式
- 指定过渡样式:transition
- 指定隐藏时的样式:opacity/其他
1.11.3 代码实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>过渡&动画</title>
<style>
/*指定过渡样式*/
.xxx-enter-active, .xxx-leave-active {
transition: opacity 1s
}
/*指定隐藏时的样式*/
.xxx-enter, .xxx-leave-to {
opacity: 0;
}
.move-enter-active {
transition: all 1s
}
.move-leave-active {
transition: all 3s
}
.move-enter, .move-leave-to {
opacity: 0;
transform: translateX(20px)
}
</style>
</head>
<body>
<!--
1. vue动画的理解
操作css的trasition或animation
vue会给目标元素添加/移除特定的class
2. 基本过渡动画的编码
1). 在目标元素外包裹<transition name="xxx">
2). 定义class样式
1>. 指定过渡样式: transition
2>. 指定隐藏时的样式: opacity/其它
3. 过渡的类名
xxx-enter-active: 指定显示的transition
xxx-leave-active: 指定隐藏的transition
xxx-enter: 指定隐藏时的样式
-->
<div id="demo">
<button @click="show = !show">Toggle</button>
<transition name="xxx">
<p v-show="show">hello</p>
</transition>
</div>
<hr>
<div id="demo2">
<button @click="show = !show">Toggle2</button>
<transition name="move">
<p v-show="show">hello</p>
</transition>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
new Vue({
el: '#demo',
data: {
show: true
}
})
new Vue({
el: '#demo2',
data: {
show: true
}
})
</script>
</body>
</html>
1.12 过滤器
1.12.1 理解过滤器
- 功能:对要显示的数据进行特定格式化后在显示
- 注意:并没有改变原本的数据,可以产生新的对应的数据
1.12.2 定义和使用过滤器
- 定义过滤器
Vue.filter(filterName,function(value[,arg1,arg2,....])){
// 进行一定的数据处理
return new Value
}
- 使用过滤器
<div>{{myData|filterName}}</div>
<div>{{myData|filterName(arg)}}</div>
1.12.3 代码实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>过滤器</title>
</head>
<body>
<!--
1. 理解过滤器
功能: 对要显示的数据进行特定格式化后再显示
注意: 并没有改变原本的数据, 可是产生新的对应的数据
2. 编码
1). 定义过滤器
Vue.filter(filterName, function(value[,arg1,arg2,...]){
// 进行一定的数据处理
return newValue
})
2). 使用过滤器
<div>{{myData | filterName}}</div>
<div>{{myData | filterName(arg)}}</div>
-->
<!--需求: 对当前时间进行指定格式显示-->
<div id="test">
<h2>显示格式化的日期时间</h2>
<p>{{time}}</p>
<p>最完整的: {{time | dateString}}</p>
<p>年月日: {{time | dateString('YYYY-MM-DD')}}</p>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript" src="https://cdn.bootcss.com/moment.js/2.22.1/moment.js"></script>
<script>
// 定义过滤器
Vue.filter('dateString', function (value, format='YYYY-MM-DD HH:mm:ss') {
return moment(value).format(format);
})
new Vue({
el: '#test',
data: {
time: new Date()
},
mounted () {
setInterval(() => {
this.time = new Date()
}, 1000)
}
})
</script>
</body>
</html>
1.13 内置指令与自定义指令
1.13.1 常用内置指令
- v:text:更新元素的textContent
- v-html:更新元素的innerHTML
- v-if:如果为true,当前标签才会输出到页面
- v-else:如果为false,当前标签才会输出到页面
- v-show:通过控制display样式来控制显示/隐藏
- v-for:遍历数组/对象
- v-on:绑定事件监听,一般简写为@
- v-bind:强制绑定解析表达式,可以省略v-bind
- v-model:双向数据绑定
- ref:指定唯一标识,vue对象通过$els属性访问这个元素对象
- v-cloak:防止闪现,与css配合:[v-cloak]{display:none}
1.13.2 自定义指令
- 注册全局指令
Vue.directive('my-directive',function(el,binding){
el.innerHTML = binding.value.toupperCase()
})
- 注册局部指定
directives:{
'my-directive':{
bind(el,binding){
el.innerHTML = binding.value.toupperCase()
}
}
}
- 使用自定义指令
v-my-directive='xxx'
1.13.5 内置编码实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>内置指令</title>
<style>
[v-cloak] { display: none }
</style>
</head>
<body>
<!--
常用内置指令
v:text : 更新元素的 textContent
v-html : 更新元素的 innerHTML
v-if : 如果为true, 当前标签才会输出到页面
v-else: 如果为false, 当前标签才会输出到页面
v-show : 通过控制display样式来控制显示/隐藏
v-for : 遍历数组/对象
v-on : 绑定事件监听, 一般简写为@
v-bind : 强制绑定解析表达式, 可以省略v-bind
v-model : 双向数据绑定
ref : 为某个元素注册一个唯一标识, vue对象通过$refs属性访问这个元素对象
v-cloak : 使用它防止闪现表达式, 与css配合: [v-cloak] { display: none }
-->
<div id="example">
<p v-cloak>{{content}}</p>
<p v-text="content"></p> <!--p.textContent = content-->
<p v-html="content"></p> <!--p.innerHTML = content-->
<p ref="msg">abcd</p>
<button @click="hint">提示</button>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
new Vue({
el: '#example',
data: {
content: '<a href="http://www.baidu.com">百度一下</a>'
},
methods: {
hint () {
alert(this.$refs.msg.innerHTML)
}
}
})
</script>
</body>
</html>
1.13.6 自定义指令编码实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>自定义指令</title>
</head>
<body>
<!--
1. 注册全局指令
Vue.directive('my-directive', function(el, binding){
el.innerHTML = binding.value.toupperCase()
})
2. 注册局部指令
directives : {
'my-directive' : {
bind (el, binding) {
el.innerHTML = binding.value.toupperCase()
}
}
}
3. 使用指令
v-my-directive='xxx'
-->
<!--
需求: 自定义2个指令
1. 功能类型于v-text, 但转换为全大写
2. 功能类型于v-text, 但转换为全小写
-->
<div id="test">
<p v-upper-text="msg"></p>
<p v-lower-text="msg"></p>
</div>
<div id="test2">
<p v-upper-text="msg"></p>
<p v-lower-text="msg"></p>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
// 注册一个全局指令
// el: 指令所在的标签对象
// binding: 包含指令相关数据的容器对象
Vue.directive('upper-text', function (el, binding) {
console.log(el, binding)
el.textContent = binding.value.toUpperCase()
})
new Vue({
el: '#test',
data: {
msg: "I Like You"
},
// 注册局部指令
directives: {
'lower-text'(el, binding) {
console.log(el, binding)
el.textContent = binding.value.toLowerCase()
}
}
})
new Vue({
el: '#test2',
data: {
msg: "I Like You Too"
}
})
</script>
</body>
</html>
1.14 自定义插件
1.14.1 说明
- Vue插件是一个包含install方法的对象
- 通过install方法给Vue或Vue实例添加方法,定义全局指令等
1.14.2 代码实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>插件</title>
</head>
<body>
<div id="test">
<p v-my-directive="msg"></p>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript" src="vue-myPlugin.js"></script>
<script type="text/javascript">
// 声明使用插件(安装插件: 调用插件的install())
Vue.use(MyPlugin) // 内部会调用插件对象的install()
const vm = new Vue({
el: '#test',
data: {
msg: 'HaHa'
}
})
Vue.myGlobalMethod()
vm.$myMethod()
new Object()
</script>
</body>
</html>
第二章 Vue组件化编码
2.1 使用vue-cli创建模板项目
2.1.1 说明
- vue-cli是vue官方提供的脚手架工具
- github:https://github.com/vuejs/vue-cli
- 作用:从https://github.com/vuejs-templates下载模板项目
2.1.2 创建vue项目
- npm install -g vue-cli :全局安装vue-cli
- vue init webpack vue_demo :当前目录下创建项目名称为vue_demo的webpack模板
- cd vue_demo :进入vue_demo文件夹
- npm install :安装
- npm run serve :运行该项目
- 访问http://localhost:8080 :项目访问地址
2.1.3 项目结构
- build:webpack相关的配置文件夹(基本上不需要修改)
- dev-serve.js:通过express启动后台服务器
- config:webpack相关的配置文件夹(基本上不需要修改)
- index.js:指定的后台服务的端口号和静态资源文件夹
- node_modules:vue的依赖
- src:源码文件夹
- components:vue组件及其相关资源文件夹
- App.vue:应用根主组件
- main.js:应用入口js
- static:静态资源文件夹
- .babelrc:babel的配置文件
- .selintignore:eslint检查忽略的配置
- .eslintrc.js:eslint检查的配置
- .gitignore:git版本管制忽略的配置
- index.html:主页面的文件
- package.json:应用包配置文件
- README.md:应用描述说明的readme文件
2.2 项目的打包与发布
2.2.1 打包
- npm run build
2.2.2 发布1:使用静态服务器工具包(nginx等—)
- npm install -g serve
- serve dist
- 访问:http://localhost:5000
2.2.3 发布2:使用动态web服务器(tomcat)
- 修改配置:webpack.prod.conf.js
output:{
publicPath:'/xxx/' //打包文件夹的名称
}
- 重新打包
- npm run build
- 修改dist文件夹为项目名称:xxx
- 将xxx拷贝到运行的tomcat的webapps目录下
- 访问:http://localhost:8080/xxx
2.3 eslint
2.3.1 说明
- ESLint是一个代码规范检查工具
- 它定义了很多特定的规则,一旦你的代码违背了某一规则,eslint会作出非常有用的提示
- 官网:http://eslint.org/
- 基本已经替代以前的JSLint
2.3.2 ESLint提供以下支持
- ES
- JSX
- style检查
- 自定义错误和提示
2.3.3 ESLint提供以下几种校验
- 语法错误校验
- 不重要或丢失的标点符号,如分号
- 没法运行到的代码块
- 未被使用的参数提醒
- 确保样式的统一规则,如sass或者less
- 检查变量的命名
2.3.4 规则的错误等级有三种
- 0:关闭规则
- 1:打开规则,并且作为一个警告(信息打印黄色字体)
- 2:打开规则,并且作为一个错误(信息打印红色字体)
2.3.5 相关配置文件
- .eslintrc.js:全局规则配置文件
'rules':{
'no-new':1
}
- 在js/vue文件中修改局部规则
/* eslint-disable no-new*/
new Vue({
el:'#body',
components:{App}
})
- .eslintignore:指令检查忽略的文件
*.js
*.vue
2.4 组件定义与使用
2.4.1 vue文件的组成(3个部分)
- 模板页面
<template>
页面模板
</template>
- JS模块对象
<script>
export default{
data(){//模板里面只能写方法
return:{
}
},
methods:{},
computed:{}.
components:{}
}
</script>
- 样式
<style>
样式定义
</style>
2.4.2 基本使用
- 引入组件
- 映射成标签
- 使用组件标签
<template>
<div>
<img src="./assets/logo.png" alt="logo" />
<!-- 3.使用组件标签 -->
<HelloWorld />
</div>
</template>
<script>
//1.引入组件
import HelloWorld from './components/HelloWorld.vue'
export default {
// 2.映射组件标签
components:{
HelloWorld
}
}
</script>
<style scoped>
</style>
2.4.3 关于标签名与标签属性名书写问题
- 写法一:一模一样
- 写法二:大写变小写,并用-连接
2.5 组件间通信
2.5.1 组件间通信基本原则
- 不要在子组件中直接修改父组件的状态数据
- 数据在哪,更新数据的行为(函数)就应该定义在哪
2.5.2 vue组件间通信方式
- props
- vue的自定义事件
- 消息订阅与发布(如:pubsub库)
- slot
- vuex(后面单独)
2.6 组件间通信1:props
2.6.1 使用组件标签
<my-component name='tom' :age='3' :set-name='setName'></my-component>
2.6.2 定义MyComponent时
- 在组件内声明所有的props
- 方式一:只指定名称
props:['name','age','setName']
- 方式二:指定名称和类型
props:{
name:String,
age:Number,
setName:Function
}
- 方式三:指定名称/类型/必要性/默认值
props:{
name:{type:String,required:true,default:xxx}
}
2.6.3 注意
- 此方式用于父组件向子组件传递数据
- 所有标签属性都会成为组件对象的属性,模板页面可以直接引用
- 问题:
- 如果需要向非子后代传递数据,必须多层逐层传递
- 兄弟组件间也不能直接pops通信,必须借助父组件才可以
2.7 组件间通信2:vue自定义事件
2.7.1 绑定事件监听
//方式一:通过v-on绑定
@delete_to="deleteTodo"
//方式二:通过$on()
this.$refs.xxx.$on('delete_todo',function(todo)){
this.deleteTodo(todo)
}
2.7.2 触发事件
//触发事件
this.$emit(eventName,data)
2.7.3 注意
- 此方式只适用于子组件向父组件发送消息(数据)
- 问题:隔代组件或兄弟组件间通信此种方式不合适
2.8 组件间通信3:消息订阅与发布
2.8.1 订阅消息
PubSub.subscribt('msg',function(msg,data){})
2.8.2 发布新消息
PubSub.publish('msg',data)
2.8.3 注意
优点:此方式可实现任意关系组件间的通信(数据)
2.8.4 事件的两个重要操作
- 绑定事件监听(订阅消息)
- 目标:标签元素
- 事件名(类型):click/focus
- 回调函数:function(event){}
- 触发事件(发布消息)
- DOM事件:用户在浏览器上对应的界面上做对应的操作
- 自定义:编码手动触发
2.9 组件间通信4:slot
2.9.1 理解
此方式用于父组件向子组件传递“标签数据”
2.9.2 子组件:Child.vue
<template>
<div>
<slot name="xxx">不确定的标签结构1</slot>
<div>
组件不确定的标签结构
</div>
<slot name="yyy">不确定的标签结构2</slot>
</div>
</template>
2.9.3 父组件:Parent.vue
<child>
<div slot="xxx">
xxx对应的标签结构
</div>
<div slot="yyy">
yyy对应的标签结构
</div>
</child>
第三章 Vue-Ajax
3.1 Vue项目中常用的2个ajax库
3.1.1 Vue-Resource
vue插件,非官方库,vue1.x使用广泛
3.1.2 axios
通用的Ajax请求库,官方推荐,vue2.x使用广泛
3.2 Vue-Resource的使用
3.2.1 在线文档
https://github.com/pagekit/vue-resource/blob/develop/docs/http.md
3.2.2 下载
npm install vue-resource --save
3.2.3 代码使用
// 引入模块
import VueResource from 'vue-resource'
// 使用插件
Vue.use(VueResource)
// 通过vue组件对象发送ajax请求
this.$http.get('/someURL').then(response)=>{
//请求成功的数据
console.log(response.data)//返回结果数据
},(response)=>{
//错误信息
console.log(response.statusText)//错误信息
}
3.3 axios的使用
3.3.1 下载
npm install axios --save
3.3.2 代码实现
// 引入模块
import axios from 'axios'
//发送Ajax请求
axios.get(url).then(response=>{
console.log(response.data)//得到返回结果数据
}).catch(error=>{
console.log(error.message)//错误信息
})
第四章 Vue UI组件库
4.1 常用的UI组件库
- Mint UI:
- 饿了么开源的基于vue的移动端UI组件库
- Elment UI:
- 饿了么开源的基于vue的PC端UI组件库
4.2 使用Mint UI
4.2.1 下载
npm install --save mint-ui
4.2.2 实现按需打包
// 下载
npm install --save-dev babel-plugin-component
// 修改babel配置
"plugins":["transform-runtime",["component",{
"libraryName":"mint-ui",
"style":true
}]]
4.2.3 mint-ui组件分类
- 标签组件
- 非标签组件
4.2.4 使用mint-ui
- index.html
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimm-scale=1,user-scalable=no" />
<script src="https://as.alipayobjects.com/g/component/fastclick/1.0.6/fastclick.js"></script>
<script>
if('addEventListener' in document){
document.addEventListener('DOMContentLoaded',function(){
FastClick.attach(document.body);
},false);
}
if(!window.Promise){
document.writelv('<script src="https://as.alipayobjects.com/g/component/fastclick/1.0.6/fastclick.js"></script>')
}
</script>
- main.js
import {Button} from 'mint-ui'
Vue.component(Button.name,Button)
- App.vue
<template>
<mt-button @click="handleClick" type="primary" style="width:100%">Test</mt-button>
</template>
<script>
import {Toast} from 'mint-ui'
export default{
methods:{
handleClick(){
Toast('点击了测试');
}
}
}
</script>
第五章 Vue-router
5.1 理解
5.1.1 说明
- 官方提供的用来实现SPA(单页)的vue插件
- github:https://github.com/vuejs/vue-router
- 中文文档:http://router.vuejs.org/zh-cn/
- 下载:npm install vue-router --save
5.1.2 相关API说明
- VueRouter():用于创建路由器的构建函数
new VueRouter({
//多个配置项
})
- 路由配置
routes:[
{//一般路由
path:'/about',
component:About
},
{//自动跳转路由
path:'/',
redirect:'/about'
}
]
- 注册路由器
import router from './router'
new Vue({
router
})
- 使用路由组件标签
1.<router-link>:用来生成路由链接
<router-link to='/xxx'>Go To XXX</router-link>
2.<router-view>:用来显示当前路由组件界面
<router-view></router-view>
5.2 基本路由
5.2.1 路由组件
文件夹命名:pages/views
示例路由组件:Home.vue/About.vue
5.2.2 应用组件:App.vue
<div>
<!-- 路由链接 -->
<router-linke to="/about">About</router-linke>
</div>
5.2.3 路由器模块:src/router/index.js
export default new VueRouter({
routes:[
{
path:'/',
redirect:'/about'
},
{
path:'/about',
component:About
},
{
path:'/home',
component:Home
}
]
})
5.2.4 注册路由器:main.js
import Vue from 'vue'
import router from './router'
// 创建vue配置路由器
new Vue({
el:'#app',
router,
render:h=>h(app)
})
5.2.5 优化路由器配置
linkActiveClass:‘active’;//指定选中的路由链接的class
5.2.6 总结:编写使用路由的3步
- 定义路由组件
- 注册路由
- 使用路由
- <router-link .>
- <router-view .>
5.3.1 子路由组件
News.vue
Message.vue
5.3.2 配置嵌套路由:router.js
path:'/home',
component:hoem,
children:[
{
path:'/home/news',
component:News
},
{
path:'message',//简化写法
component:Message
}
]
5.3.4 路由链接:Home.vue
<router-link to="/home/news">News</router-link>
<router-link to="/home/message">Message</router-link>
<router-view></router-view>
5.4 缓存路由组件对象
5.4.1 理解
- 默认情况下,被切换的路由组件对象会死亡释放,再次回来时是重新创建的
- 如果可以缓存路由组件对象,可以提高用户体验
5.4.2 编码实现
<keep-alive>
<router-view></router-view>
</keep-alive>
5.5 向路由组件传递数据
5.5.1 方式1:路由路径携带参数(param/query)
- 配置路由
children:[
{
path:'mdetail/:id',
component:MessageDetail
}
]
- 路由路径
<router-link :to="'/home/message/mdetail/'+m.id">{{m.title}}</router-link>
- 路由组件中读取请求参数
this.$route.params.id
5.5.2 方式2:属性携带数据
5.6 编程式路由导航
相关API
- this.$router.push(path):相当于点击路由链接(可以返回当前路由界面)
- this.$router.replace(path):用新路由替换当前路由(不可以返回当前路由界面)
- this.$router.back():请求(返回)上一个记录路由
- this.$router.go(-1):请求(返回)上一个记录路由
- this.$router.go(1):请求下一个记录路由
5.6.2 代码实现
pushShow (id) {
this.$router.push(`/home/message/detail/${id}`)
}
6.1 说明
- 分析vue作为一个MVVM框架的基本实现原理
- 数据代理
- 模板解析
- 数据绑定
- 不直接看vue.js的源码
- 剖析github上仿vue实现的MVVM库
- 地址:https://github.com/DMQ/mvvm
6.2 准备知识
- [].slice.call(lis):将伪数组转换为真数组
- node.noteType:得到节点类型
- Object.defineProperty(obj,propName,{}):给对象添加/修改属性(指定描述符)
- configurable:true/false 是否可以重新define
- enumerable:true/false 是否可以枚举(for…in/keys())
- value:指定初始值
- writable:true/false value是否可以修改
- get:回调函数,用来得到当前属性值
- set:回调函数,用来监视当前属性值的变化
- Object.keys(obj):得到对象自身可枚举的属性名的数组
- DocumentFragment:文档碎片(高效批量更新多个节点)
- obj.hasOwnProperty(prop):判断prop是否是obj自身的属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="test">MVVMd</div>
<ul id="fragment_test">
<li>test1</li>
<li>test2</li>
<li>test3</li>
</ul>
<script type="text/javascript">
//1. [].slice.call(lis): 根据伪数组生成对应的真数组
const lis = document.getElementsByTagName('li') // lis是伪数组(是一个特别的对象, length和数值下标属性)
console.log(lis instanceof Object, lis instanceof Array)
// 数组的slice()截取数组中指定部分的元素, 生成一个新的数组 [1, 3, 5, 7, 9], slice(0, 3)
// slice2()
Array.prototype.slice2 = function (start, end) {
start = start || 0
end = start || this.length
const arr = []
for (var i = start; i < end; i++) {
arr.push(this[i])
}
return arr
}
const lis2 = Array.prototype.slice.call(lis) // lis.slice()
console.log(lis2 instanceof Object, lis2 instanceof Array)
// lis2.forEach()
//2. node.nodeType: 得到节点类型
const elementNode = document.getElementById('test')
const attrNode = elementNode.getAttributeNode('id')
const textNode = elementNode.firstChild
console.log(elementNode.nodeType, attrNode.nodeType, textNode.nodeType)
//3. Object.defineProperty(obj, propertyName, {}): 给对象添加属性(指定描述符)
const obj = {
firstName: 'A',
lastName: 'B'
}
//obj.fullName = 'A-B'
Object.defineProperty(obj, 'fullName', {
// 属性描述符:
// 数据描述符
//访问描述符
// 当读取对象此属性值时自动调用, 将函数返回的值作为属性值, this为obj
get () {
return this.firstName + "-" + this.lastName
},
// 当修改了对象的当前属性值时自动调用, 监视当前属性值的变化, 修改相关的属性, this为obj
set (value) {
const names = value.split('-')
this.firstName = names[0]
this.lastName = names[1]
}
})
console.log(obj.fullName) // A-B
obj.fullName = 'C-D'
console.log(obj.firstName, obj.lastName) // C D
Object.defineProperty(obj, 'fullName2', {
configurable: false, //是否可以重新define
enumerable: true, // 是否可以枚举(for..in / keys())
value: 'A-B', // 指定初始值
writable: false // value是否可以修改
})
console.log(obj.fullName2) // A-B
obj.fullName2 = 'E-F'
console.log(obj.fullName2) // A-B
/*Object.defineProperty(obj, 'fullName2', {
configurable: true,
enumerable: true,
value: 'G-H',
writable: true
})*/
//4. Object.keys(obj): 得到对象自身可枚举属性组成的数组
const names = Object.keys(obj)
console.log(names)
//5. obj.hasOwnProperty(prop): 判断prop是否是obj自身的属性
console.log(obj.hasOwnProperty('fullName'), obj.hasOwnProperty('toString')) // true false
//6. DocumentFragment: 文档碎片(高效批量更新多个节点)
// document: 对应显示的页面, 包含n个elment 一旦更新document内部的某个元素界面更新
// documentFragment: 内存中保存n个element的容器对象(不与界面关联), 如果更新framgnet中的某个element, 界面不变
/*
<ul id="fragment_test">
<li>test1</li>
<li>test2</li>
<li>test3</li>
</ul>
*/
const ul = document.getElementById('fragment_test')
// 1. 创建fragment
const fragment = document.createDocumentFragment()
// 2. 取出ul中所有子节点取出保存到fragment
let child
while(child=ul.firstChild) { // 一个节点只能有一个父亲
fragment.appendChild(child) // 先将child从ul中移除, 添加为fragment子节点
}
// 3. 更新fragment中所有li的文本
Array.prototype.slice.call(fragment.childNodes).forEach(node => {
if (node.nodeType===1) { // 元素节点 <li>
node.textContent = 'atguigu'
}
})
// 4. 将fragment插入ul
ul.appendChild(fragment)
</script>
</body>
</html>
6.3 数据代理
- 数据代理:通过一个对象代理对另一个对象(在前一个对象内部)中属性的操作(读/写)
- vue数据代理:通过vm对象来代理data对象中所有属性的操作
- 好处:更方便的操作data中的数据
- 基本实现流程
- 通过Object.defineProperty()给vm添加与data对象的属性对应的属性描述符
- 所有添加的属性都包含getter/setter
- getter/setter内部去操作dta中对应的属性数据
代码实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>数据代理</title>
</head>
<body>
<!--
1. vue数据代理: data对象的所有属性的操作(读/写)由vm对象来代理操作
2. 好处: 通过vm对象就可以方便的操作data中的数据
3. 实现:
1). 通过Object.defineProperty(vm, key, {})给vm添加与data对象的属性对应的属性
2). 所有添加的属性都包含get/set方法
3). 在get/set方法中去操作data中对应的属性
-->
<div id="test"></div>
<script type="text/javascript" src="js/mvvm/compile.js"></script>
<script type="text/javascript" src="js/mvvm/mvvm.js"></script>
<script type="text/javascript" src="js/mvvm/observer.js"></script>
<script type="text/javascript" src="js/mvvm/watcher.js"></script>
<script type="text/javascript">
const vm = new MVVM({
el: "#test",
data: {
name: '张三2'
}
})
console.log(vm.name) // 读取的是data中的name, vm代理对data的读操作
vm.name = '李四2' // 数据保存到data中的name上, vm代理对data的写操作
console.log(vm.name, vm._data.name)
</script>
</body>
</html>
6.4 模板解析
6.4.1 模板解析的基本流程
- 将el的所有子节点取出,添加到一个新建的文档fragment对象中
- 对fragment中的所有层次子节点递归进行编译解析处理
- 对大括号表达式文本节点进行解析
- 对元素节点的指令属性进行解析
- 事件指令解析
- 一般指令解析
- 将解析后的fragment添加到el中显示
6.4.2 模板解析1:大括号表达式解析
- 根据正则对象得到匹配出的表达式字符串:子匹配/RegExp.$1 name
- 从data中取出表达式对应的属性值
- 将属性值设置为文本节点的textContent
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>模板解析_表达式_vue</title>
</head>
<body>
<div id="test">
<p>{{name}}</p>
</div>
<script type="text/javascript" src="js/mvvm/compile.js"></script>
<script type="text/javascript" src="js/mvvm/mvvm.js"></script>
<script type="text/javascript" src="js/mvvm/observer.js"></script>
<script type="text/javascript" src="js/mvvm/watcher.js"></script>
<script type="text/javascript">
new MVVM({
el: '#test',
data: {
name: 'SADAMU'
}
})
</script>
</body>
</html>
6.4.3 模板解析2:事件指令解析
- 从指令名中取出事件名
- 根据指令的值(表达式)从methods中得到对应的事件处理函数对象
- 给当前元素节点绑定指定事件名和回调函数的dom事件监听
- 指令解析完后,移除此指令属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>模板解析_事件指令</title>
</head>
<body>
<div id="test">
<button v-on:click="test">测试</button>
</div>
<script type="text/javascript" src="js/mvvm/compile.js"></script>
<script type="text/javascript" src="js/mvvm/mvvm.js"></script>
<script type="text/javascript" src="js/mvvm/observer.js"></script>
<script type="text/javascript" src="js/mvvm/watcher.js"></script>
<script type="text/javascript">
new MVVM({
el: '#test',
data: {
msg: 'hello atguigu'
},
methods: {
test () {
alert(this.msg)
}
}
})
</script>
</body>
</html>
6.4.4 模板解析3:一般指令解析
- 得到指令名和指令值(表达式) text/html/class msg/myClass
- 从data中根据表达式得到对应的值
- 根据指令名确定需要操作元素节点的什么属性
- v-text—textContent属性
- v-html—innerHTML属性
- v-class—className属性
- 将得到的表达式的值设置到对应的属性上
- 移除元素的指令属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>模板解析_一般指令</title>
<style>
.aclass {
color: red;
}
.bClass {
font-size: 30px;
}
</style>
</head>
<body>
<div id="test">
<p v-text="msg"></p>
<p v-html="msg"></p>
<p class="bClass" v-class="myClass">xxxxxx</p>
</div>
<script type="text/javascript" src="js/mvvm/compile.js"></script>
<script type="text/javascript" src="js/mvvm/mvvm.js"></script>
<script type="text/javascript" src="js/mvvm/observer.js"></script>
<script type="text/javascript" src="js/mvvm/watcher.js"></script>
<script type="text/javascript">
new MVVM({
el: '#test',
data: {
msg: '<a href="http://www.atguigu.com">尚硅谷</a>',
myClass: 'aclass'
},
methods: {
test () {
alert(this.msg)
}
}
})
</script>
</body>
</html>
6.5 数据绑定
6.5.1 数据绑定
一旦更新了data中的某个属性数据,所有界面上直接使用或间接使用此属性的节点更新
6.5.2 数据劫持
- 数据劫持是vue中用来实现数据绑定的一种技术
- 基本思想:通过defineProperty()来监视data中所有属性(任意层次)数据的变化,有变化就去更新界面
6.5.3 四个重要对象
-
Observer
- 用来对data所有属性数据进行劫持的构造函数
- 给data中所有属性重新定义属性描述(get/set)
- 为data中的每个属性创建对应的dep对象
-
Dep(Depend)
-
data中的每个属性(所有层次)都对应一个dep对象
-
创建的时机
- 在初始化define data中各个属性时创建对应的dep对象
- 在data中的某个属性值被设置为新的对象时
-
对象的结构
{ id,//每个dep都有一个唯一的id subs//包含n个对应watcher的数组(subscribes的简写) }
-
subs属性说明
- 当watcher被创建时,内部将当前watcher对象添加到对应的dep对象的subs中
- 当此data属性的值发生改变时,subs中所有的watcher都会受到更新的通知,从而最终更新对应的界面
-
-
Compiler
- 用来解析模板页面的对象的构造函数(一个实例)
- 利用compile对象解析模板页面
- 每解析一个表达式(非事件指令)都会创建一个对应的watcher对象,并建立watcher与dep的关系
- complie与watcher的关系:一对多的关系
-
Watcher
-
模板中每个非事件指令或表达式都对应一个watcher对象
-
监视当前表达式数据的变化
-
创建的时机:在初始化编译模板时
-
对象的组成
{ vm,//vm对象 exp,//对应指令的表达式 cb,//当表达式所对应的数据发生改变的回调函数 value,//表达式当前的值 depIds//表达式中各级属性所对应的dep对象的集合对象 //属性名为dep的id,属性值为dep }
-
-
总结:dep与watcher的关系:多对多
- data中的一个属性对应一个dep,一个dep中可能包含多个watcher(模板中有几个表达式使用到了同一个属性)
- 模板中一个非事件表达式对应一个watcher,一个watcher中可能包含多个dep(表达式是多层的:a.b)
- 数据绑定使用到2个核心技术
- defineProperty()
- 消息订阅与发布
代码实现
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>数据劫持-数据绑定</title>
<!--
1. 数据绑定
* 初始化显示: 页面(表达式/指令)能从data读取数据显示 (编译/解析)
* 更新显示: 更新data中的属性数据==>页面更新
-->
</head>
<body>
<div id="test">
<p>{{name}}</p>
<p v-text="name"></p>
<p v-text="wife.name"></p>
<button v-on:click="update">更新</button>
</div>
<!--
dep
与data中的属性一一对应 (4)
watcher
与模板中一般指令/大括号表达式一一对应 (3)
1. 什么时候一个dep中关联多个watcher?
多个指令或表达式用到了当前同一个属性 {{name}} {{name}}
2. 什么时候一个watcher中关联多个dep?
多层表达式的watcher对应多个dep {{a.b.c}}
-->
<script type="text/javascript" src="js/mvvm/compile.js"></script>
<script type="text/javascript" src="js/mvvm/mvvm.js"></script>
<script type="text/javascript" src="js/mvvm/observer.js"></script>
<script type="text/javascript" src="js/mvvm/watcher.js"></script>
<script type="text/javascript">
new MVVM({
el: '#test',
data: {
name: 'sadamu', // dep0
wife: { // dep1
name: 'binbin', // dep2
age: 18 // dep3
}
},
methods: {
update () {
this.name = 'avatar'
}
}
})
</script>
</body>
</html>
6.6 MVVM原理分析
6.7 双向数据绑定
- 双向数据绑定是建立在单向数据绑定(model=>View)的基础之上的
- 双向数据绑定的实现流程
- 在解析v-model指令时,给当前元素添加input监听
- 当input的value发生改变时,将最新的值赋值给当前表达式所对应的data属性
代码实现
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>数据双向绑定</title>
</head>
<body>
<div id="test">
<input type="text" v-model="msg">
<p>{{msg}}</p>
</div>
<script type="text/javascript" src="js/mvvm/compile.js"></script>
<script type="text/javascript" src="js/mvvm/mvvm.js"></script>
<script type="text/javascript" src="js/mvvm/observer.js"></script>
<script type="text/javascript" src="js/mvvm/watcher.js"></script>
<script type="text/javascript">
new MVVM({
el: '#test',
data: {
msg: 'haha'
}
})
</script>
</body>
</html>
第七章 Vuex
7.1 Vuex理解
7.1.1 vuex是什么
- github站点:https://github.com/vuejs/vuex
- 在线文档:https://vuex.vuejs.org/zh-cn/
- 简单来说,对vue应用中多个组件的共享状态进行集中式的管理(读/写)
7.1.2 状态自管理应用
- state:驱动应用的程序源
- view:以声明方式将state映射到视图
- actions:响应在view上的用户输入导致的状态变化(包含n个更新状态的方法)
7.1.3 多组件共享状态的问题
- 多个视图依赖于同一状态
- 来自不同视图的行为需要变更同一状态
- 以前的解决办法
- 将数据以及操作数据的行为都定义在父组件
- 将数据以及操作数据的行为传递给需要的各个子组件(有可能需要多级传递)
- vuex就是用来解决这个问题的
7.2 vuex核心概念和API
7.2.1 state
-
vuex管理的状态对象
-
它应该是唯一的
const state={ xxx:initValue }
7.2.2 mutations
-
包含多个直接更新state的方法(回调函数)的对象
-
谁来触发:action中的commit(‘mutation名称’)
-
只能包含同步的代码,不能写异步代码
const mutations={ yyy(state,{data1}){ //更新state的某个属性 } }
7.2.3 actions
-
包含多个事件回调函数的对象
-
通过执行:commit()来触发mutation的调用,间接更新state
-
谁来触发:组件中:$store.dispatch(‘action名称’,data1) //‘zzz’
-
可以包含异步代码(定时器,ajax)
const actions={ zzz({commit,state},data1){ commit('yyy',{data1}) } }
7.2.4 getters
-
包含多个计算属性(get)的对象
-
谁来读取:组件中:$store.getters.xxx
const getters={ mmm(state){ return ... } }
7.2.5 modules
- 包含多个module
- 一个module是一个store的配置对象
- 与一个组件(包含有共享数据)对应
7.2.6 向外暴露store对象
export default new Vuex.Store({
state,
mutations,
actions,
getters
})
7.2.7 组件中
import {mapState,mapGetters,mapActions} from 'vuex'
export default{
computed:{
...mapState(['xxx']),
...mapGetters(['mmm']),
},
methods:mapActions(['zzz'])
}
{{xxx}}{{mmm}}@click="zzz(data)"
7.2.8 映射store
import store from './store'
new Vue({
store
})
7.2.9 store对象
- 所有用vuex管理的组件中都躲了一个属性store,它就是一个store对象
- 属性:
- state:注册的state对象
- getters:注册的getters对象
- 方法:
定是建立在单向数据绑定(model=>View)的基础之上的 - 双向数据绑定的实现流程
- 在解析v-model指令时,给当前元素添加input监听
- 当input的value发生改变时,将最新的值赋值给当前表达式所对应的data属性
代码实现
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>数据双向绑定</title>
</head>
<body>
<div id="test">
<input type="text" v-model="msg">
<p>{{msg}}</p>
</div>
<script type="text/javascript" src="js/mvvm/compile.js"></script>
<script type="text/javascript" src="js/mvvm/mvvm.js"></script>
<script type="text/javascript" src="js/mvvm/observer.js"></script>
<script type="text/javascript" src="js/mvvm/watcher.js"></script>
<script type="text/javascript">
new MVVM({
el: '#test',
data: {
msg: 'haha'
}
})
</script>
</body>
</html>
第七章 Vuex
7.1 Vuex理解
7.1.1 vuex是什么
- github站点:https://github.com/vuejs/vuex
- 在线文档:https://vuex.vuejs.org/zh-cn/
- 简单来说,对vue应用中多个组件的共享状态进行集中式的管理(读/写)
7.1.2 状态自管理应用
- state:驱动应用的程序源
- view:以声明方式将state映射到视图
- actions:响应在view上的用户输入导致的状态变化(包含n个更新状态的方法)
[外链图片转存中…(img-44Q93wcS-1590936591436)]
7.1.3 多组件共享状态的问题
- 多个视图依赖于同一状态
- 来自不同视图的行为需要变更同一状态
- 以前的解决办法
- 将数据以及操作数据的行为都定义在父组件
- 将数据以及操作数据的行为传递给需要的各个子组件(有可能需要多级传递)
- vuex就是用来解决这个问题的
7.2 vuex核心概念和API
7.2.1 state
-
vuex管理的状态对象
-
它应该是唯一的
const state={ xxx:initValue }
7.2.2 mutations
-
包含多个直接更新state的方法(回调函数)的对象
-
谁来触发:action中的commit(‘mutation名称’)
-
只能包含同步的代码,不能写异步代码
const mutations={ yyy(state,{data1}){ //更新state的某个属性 } }
7.2.3 actions
-
包含多个事件回调函数的对象
-
通过执行:commit()来触发mutation的调用,间接更新state
-
谁来触发:组件中:$store.dispatch(‘action名称’,data1) //‘zzz’
-
可以包含异步代码(定时器,ajax)
const actions={ zzz({commit,state},data1){ commit('yyy',{data1}) } }
7.2.4 getters
-
包含多个计算属性(get)的对象
-
谁来读取:组件中:$store.getters.xxx
const getters={ mmm(state){ return ... } }
7.2.5 modules
- 包含多个module
- 一个module是一个store的配置对象
- 与一个组件(包含有共享数据)对应
7.2.6 向外暴露store对象
export default new Vuex.Store({
state,
mutations,
actions,
getters
})
7.2.7 组件中
import {mapState,mapGetters,mapActions} from 'vuex'
export default{
computed:{
...mapState(['xxx']),
...mapGetters(['mmm']),
},
methods:mapActions(['zzz'])
}
{{xxx}}{{mmm}}@click="zzz(data)"
7.2.8 映射store
import store from './store'
new Vue({
store
})
7.2.9 store对象
- 所有用vuex管理的组件中都躲了一个属性store,它就是一个store对象
- 属性:
- state:注册的state对象
- getters:注册的getters对象
- 方法:
- dispatch(actionName,data):分发调用action
小结
这篇权当是学习Vue的一个基础,更多内容,我打算做一个网站,遇到了再更新吧。