千锋逆战班,vue的基础,或许有一些些小错误或者语病,例子估计不怎么精炼,但还可以直接复制粘贴执行代码查看效果。
Vue知识点总结
一、起步
1、 首先在HTML文件中引入vue.js。
2、新建一个div
标签,并且为其设一个id
值。<div id="app"> {{msg}}</div>
3、js代码里面创建实例。
// vue实例就是ViewModel:负责联系view和model之间,实现响应式的双向数据绑定
const app = new Vue({
el: '#app',
// data就是model:定义数据的
data: {
msg: 'hello 1912'
}
})
下面是完整的引用代码。
<!DOCTYPE html>
<html lang="en">
<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="./vue.js"></script>
</head>
<body>
<!-- view: 视图,页面显示,当model层的数据发生修改,ViewModel层会响应式的更新DOM -->
<div id="app">
{{msg}}
</div>
<script>
console.dir(Vue)
// vue实例就是ViewModel:负责联系view和model之间,实现响应式的双向数据绑定
const app = new Vue({
el: '#app',
// data就是model:定义数据的
data: {
msg: 'hello 1912'
}
})
</script>
</body>
</html>
二、原理(实现方法)
1、Object.defineProperty
- 静态方法
Object.defineProperty()
直接在对象上定义新属性,或修改对象上的现有属性,然后返回对象。
var obj ={}
Object.defineProperty(obj, "key", {
enumerable: false, // 不可枚举:通过for-in不能遍历到
configurable: false, // 不可配置:不能通过delete obj.key 进行删除
writable: false, // 不可写:值不能被修改
value: "static"
})
-
用法
-
在页面HTML文件中写入
<div id="app"></div>
-
申明一个新的对象obj
const obj = {}
-
设置Object.defineProperty。
let v=10;//定义初始值 Object.defineProperty(obj, 'x', { // get是再每次访问obj的x属性的时候就会执行,并且这个属性得到的值来自于get的返回值 get() { return v }, // 每次要修改obj的x属性,就会调用set set(newValue) { console.log('newValue:' + newValue) v = newValue // 每次set的时候都调用渲染DOM的方法,这样就实现了响应式 setAppContent() } }) // 这个方法专门负责渲染DOM const setAppContent = () => { document.querySelector('#app').innerHTML = obj.x } setAppContent();//首先div#app的内容渲染成obj.x的值,但是只要调用了obj.x=""这个语句,obj.x的值就会被改变,然后再次调用渲染页面的方法,这样就可以实现页面有数据渲染。
-
2、Proxy代理实现
- 用法
const obj=new Proxy({},{ get(obj,key){ retuen obj[key] }, set(obj,key,value){ obj[key]=value setAppContent();//重新渲染页面 } }) const setAppContent = () => { document.querySelector('#app').innerHTML = obj.x }
3、v-model绑定input原理
- 注意:原生js的input元素也有input事件的,相当于onkeydown
<div id="app">
<input type="text" :value="username" @input="handleUsernameInput">
{{username}}
</div>
<script>
const app = new Vue({
el: '#app',
data: {
username: ''
},
methods: {
handleUsernameInput (e) {
this.username = e.target.value
}
}
})
</script>
4、v-model绑定checkbox原理
- (checked属性不显示在浏览器标签内)
<div id="app">
<p>
<label><input @change="handleLikesChange" :checked="likes.includes('唱')" type="checkbox" value="唱">唱</label>
<label><input @change="handleLikesChange" :checked="likes.includes('跳')" type="checkbox" value="跳">跳</label>
<label><input @change="handleLikesChange" :checked="likes.includes('rap')" type="checkbox" value="rap">rap</label>
<label><input @change="handleLikesChange" :checked="likes.includes('篮球')" type="checkbox" value="篮球">篮球</label>
</p>
{{likes}}
</div>
<script>
const app = new Vue({
el: '#app',
data: {
likes: ['唱']
},
methods: {
handleLikesChange (e) {
const value = e.target.value
console.log(value)
// 判断likes里是否包含value,如果包含,从likes里把这条数据删除
if (this.likes.includes(value)) {
this.likes = this.likes.filter(like => like !== value)
} else {
// likes不包含当前value,说明目前没有选中,把value push到likes里
this.likes.push(value)
}
}
}
})
</script>
5、v-model绑定radio原理
- (checked属性不显示在浏览器标签内)
<div id="app">
<p>
你要找
<input type="radio" :checked="friend.includes('男朋友')" @change="radioChange" value="男朋友">男朋友
<input type="radio" :checked="friend.includes('女朋友')" @change="radioChange" value="女朋友">女朋友
<div style="color: red;">
{{friend}}
</div>
</p>
</div>
<script>
const app = new Vue({
el: '#app',
data: {
friend: '女朋友'
},
methods: {
radioChange(e) {
this.friend = e.target.value
}
}
})
</script>
6、v-model绑定select下拉框原理
- (seleceted属性不显示在浏览器标签内)
<div id="app">
<select @change="selectedOne">
<option :selected="like.includes('唱')" value="唱">唱</option>
<option :selected="like.includes('跳')" value="跳">跳</option>
<option :selected="like.includes('rap')" value="rap">rap</option>
<option :selected="like.includes('篮球')" value="篮球">篮球</option>
</select>
<p>{{like}}</p>
</div>
<script>
const app = new Vue({
el: '#app',
data: {
like: '篮球'
},
methods: {
selectedOne(e) {
this.like = e.target.value
}
}
})
</script>
三、语法
- 注意:Vue里面的数值基本都做了处理,可以通过
this.msg
获取data里面的msg,也可以通过this.$data.msg
获取,但是this.data.msg
是错误的用法。 - 双大括号的文本插值,只是加载整个文本,不会渲染成标签 :
<span>Message: {{ msg }}</span>
- v-html 指令才能渲染成标签,v-text渲染文本,和{{}}差不多
- 当指令和插值表达式发生冲突时,插值表达式生效
四、指令
1、v-html和v-text
- v-html 指令渲染成标签,v-text渲染成文本
<div id="app">
<div v-html="str">{{num}}</div>
<!--title而不是1-->
<div v-text="str">{{num}}</div>
<!--<h1>title</h1>而不是1-->
</div>
<script>
const app = new Vue({
el: '#app',
data: {
num: 1,
str: '<h1>title</h1>'
}
})
</script>
2、v-if和v-show
- v-if是不渲染这个标签,v-show通过设置display:none来控制元素的显示与否
- 注意:v-if和v-else必须用在相邻的兄弟元素,否则会报错
<div id="app">
<p v-if="isShow > 0">这是一段文字</p>
<p v-else-if="isShow < 0">这是第二段文字</p>
<p v-else>这是第三段文字</p>
<p v-show="isShow > 0">v-show案例</p>
</div>
<script>
const app = new Vue({
el: '#app',
data: {
isShow: 0
}
})
</script>
3、v-for
- 用法:
<div v-for="val in $"></div>
- 或者
<div v-for="val of $ "></div>
- 或者
<div v-for="(val,key) in obj"></div>
- 或者
<div v-for="(val,index) in arr"></div>
- 或者
<div v-for="(val,index) in str"></div>
- $可以是数组,对象,字符串和数字(Number)
4、v-on:event绑定事件(@event)
v-on绑定事件
- 用法
v-on:click="handleBtnClick"
可以简写成@click="handleBtnClick"
。v-on:click="isShow=!isShow"
可以直接写js代码。@click = " fun() "
和@click = " fun "
效果一样,在methods里面的第一个参数就是事件源/事件对象。@click = " fun( $event, n1, n2... )"
如果需要传其他参数并且获取事件源应当这样写。
<div id="app">
<button v-on:click="isShow = !isShow">显示/隐藏弹框</button>
<button v-on:click="handleBtnClick">显示/隐藏弹框</button>
<button v-on:click="handleBtnClick2($event, 123)">显示/隐藏弹框</button>
<button @click="handleBtnClick2($event, 123)">显示/隐藏弹框</button>
<p v-show="isShow">这是一个弹窗</p>
</div>
<script>
const app = new Vue({
el: '#app',
data: {
isShow: false,
data: 'sddsds'
},
methods: {
handleBtnClick (e) {
console.log(e)
this.isShow = !this.isShow
},
handleBtnClick2 (e, num) {
console.log(e)
console.log(num)
}
}
})
console.log(app)
</script>
5、v-cloak
-
v-cloak属性是vue实例化之前存在,之后就自动消失 ,一般用来处理页面的加载问题。
-
可以利用它让元素一开始的时候隐藏,有vue实例之后再显示 ,隐藏未编译的{{msg}}标签直到实例准备完毕。
-
使用属性选择器设置css样式:
[v-cloak]
属性选择器。[v-cloak] { display: none; } <div id="app" v-cloak> {{str}} </div> <script> setTimeout(() => { const app = new Vue({ el: '#app', data: { str: 'hello' } }) }, 1000) </script>
6、v-bind:attribute绑定属性(:attribute)
-
v-bind可以用来绑定任意属性,包括自定义属性
<div id="app"> <img v-bind:src="user.avatar" v-bind:alt="user.name" :title="user.name"> </div> <script> const app = new Vue({ el: '#app', data: { user: { avatar: 'https://cn.vuejs.org/images/dcloud.gif', name: '张三' } } }) </script>
7、class和style绑定
1)、对象语法:
-
class名可叠加
-
给v-bind:class传一个对象,表示这个div的class值取决于isActive的值是否为true;
<div class="class1" v-bind:class="{active:isActive ,'class2':isActive}"></div>
若isActive=true,则结果为
<div class="class1 active class2"></div>
-
绑定的数据对象不必内联定义在模板里:
<div :class="classObject"></div>
data:{ classObject:{ 'active':true, class2:false } }
2)、三目运算符
<div class="class1" v-bind:class="isActive ? 'active' : ''"></div>
3)、数组语法
<div v-bind:class="[activeClass, errorClass]"></div>
data: {
activeClass: 'active',
errorClass: 'text-danger'
}
<div id="app">
<div :style="{ width: divWidth }">box1</div>
<!-- 样式绑定一个对象,也可以和原生样式一起使用,最终渲染结果会合并 -->
<div :style="styleObj" style="color: #fff">box2</div>
<!-- 可以直接把data作为class使用 -->
<div :class="className">box</div>
<!-- 三目运算表达式绑定class -->
<div :class="active ? 'ac' : '' ">box3</div>
<!-- 绑定对象 -->
<div :class="{ ac: active }">box4</div>
<!-- 绑定数组 -->
<div :class="[className, className2, 'ac']">box5</div>
<!-- 绑定数组里再嵌套对象 -->
<div :class="[className, className2, { ac: active }]">box6</div>
<!-- 绑定的class和原生class可以共存,会合并到一起 -->
<div :class="[className, className2]" class='ac'>box7</div>
</div>
<script>
const app = new Vue({
el: '#app',
data: {
divWidth: '200px',
styleObj: {
height: '300px',
width: '300px',
backgroundColor: 'red'
},
active: false,
className: 'current',
className2: 'current2'
}
})
</script>
8、v-model双向绑定数据
<div id="app">
<!-- input的v-model的原理 -->
<input type="text" v-mode="username">
{{username}}
</div>
<script>
const app = new Vue({
el: '#app',
data: {
username: '555'
}
})
</script>
9、修饰符(modifier)
1)、表单输入修饰符
-
.lazy :在input的值发生改变时更新data,而非input或keydown时更新
<!-- 在“change”时而非“input”时更新 --> <input v-model.lazy="msg" >
-
number : 自动将用户的输入值转为数值类型 。因为即使在
type="number"
时,HTML 输入元素的值也总会返回字符串。<input v-model.number="age" type="number">
-
trim : 自动过滤用户输入的首尾空白字符
<input v-model.trim="msg">
2)、按键修饰符
-
自定义按键修饰符别名:
// 可以使用 `v-on:keyup.f1` Vue.config.keyCodes.f1 = 112
-
按键码
.13
<input v-on:keyup.13="submit"> <!--和按下enter键一样--> <input v-on:keyup.enter="submit">
-
.enter
-
.tab
-
.delete
(捕获“删除”和“退格”键) -
.esc
-
.space
-
.up
-
.down
-
.left
-
.right
3)、事件修饰符(event-modifier)
[例] event.preventDefault()
阻止默认事件(.prevent> ),或 event.stopPropagation()
阻止冒泡(.stop)是非常常见的需求,vue提供了相应的修饰符
.stop
.prevent
.capture
.self
.once
.passive
- 2.1.4新增
.once
- 2.3.0新增
.passive
,不要把.passive
和.prevent
一起使用,因为.prevent
将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive
会告诉浏览器你不想阻止事件的默认行为。
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成 -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>
五、computed计算属性
- computed是一个对象,里面写方法,这个方法名可以作为data去使用,值就是这个方法的返回值 ,只有当值改变的时候才调用,如果将逻辑写在页面上,则每一次都需要重新渲染。
- 计算属性是有依赖缓存的
<div id="app">
{{str}} <br>
{{ str.split('').reverse().join('') }}<br>
{{ str1 }}
</div>
<script>
const app = new Vue({
el: '#app',
data: {
str: 'abc'
},
computed: {
str1 () {
return this.str.split('').reverse().join('')
}
}
})
</script>
computed原理(set,get)
- 如果写成普通函数的方式,就相当于get(){}方法,不能设置了,也就不能改动。
<div id="app">
姓:<input type="text" v-model="xing">
名:<input type="text" v-model="ming">
姓名:<input type="text" v-model="username">
</div>
<script>
const app = new Vue({
el: '#app',
data: {
xing: '',
ming: ''
},
computed: {
username: {
set (value) {
// 修改username的时候就会走set,参数就是修改之后的值
console.log(value)
this.xing = value.slice(0, 1)
this.ming = value.slice(1)
},
get () {
return this.xing + this.ming
}
}
}
})
</script>
六、methods
-
用法
var vm = new Vue({ data: { a: 1 }, methods: { plus: function () { this.a++ } } }) vm.plus()
也可以在标签内调用
<div @click="plus"></div>
具体用法及注意事项->4、v-on:event绑定事件(@event):v-on绑定事件
七、watch侦听器
- watch是监听器,只要数据被修改了就会重新渲染。computed有依赖缓存,computed一般是依赖某个属性得到一个新的值。watch一般用来监听某个值的变化然后进行一些操作,可能并不需要得到新的值
<div id="app">
姓:<input type="text" v-model="xing">
名:<input type="text" v-model="ming">
姓名:<input type="text" v-model="username">
</div>
<script>
const app = new Vue({
el: '#app',
data: {
xing: '',
ming: '',
username: ''
},
watch: {
xing (newV, oldV) {
// 当data里的xing字段发生变化的时候就会走这个方法
// newV是修改之后的最新的值, oldV是修改之前的旧值
this.username = newV + this.ming
},
ming (newV, oldV) {
this.username = this.xing + newV
},
username (newV, oldV) {
this.xing = newV.slice(0, 1)
this.ming = newV.slice(1)
}
}
})
</script>
八、filters 过滤器
1、全局过滤器:Vue.filter( ‘’ ,()=>{})
-
全局过滤器,每个vue实例里面都能用
Vue.filter('addDanwei', (value) => { return '¥' + value })
2、局部过滤器:filters: {fn(){}}
- 在其他Vue实例里面就不能用。
filters: {
toFixed2 (num) {
return num.toFixed(2)
}
}
3、用法:在{{}}加中线,可叠加使用
<p>{{shop.price | toFixed2 | addDanwei}}</p>
<div id="app">
<ul>
<li v-for="shop in list">
<span>{{shop.title}}</span>
<b>{{shop.status | formatStatus}}</b>
<em>{{shop.price | toFixed2 | addDanwei}}</em>
</li>
</ul>
</div>
<div id="app1">
{{ num | addDanwei }}
<!-- {{ num | toFixed2 | addDanwei }} -->
</div>
<script>
// 全局过滤器,在下面这两个vue实例里都能使用
Vue.filter('addDanwei', (value) => {
return '¥' + value
})
// app1里就用不了app里定义的局部过滤器
const app1 = new Vue({
el: '#app1',
data: {
num: 12
}
})
const app = new Vue({
el: '#app',
data: {
list: [
{ id: 1, status: '001', title: '拖把', price: 19.99999 },
{ id: 2, status: '004', title: '扫把', price: 9 },
{ id: 3, status: '003', title: '簸箕', price: 29.1 },
{ id: 4, status: '002', title: '吸尘器', price: 2999.159839 },
{ id: 5, status: '005', title: '鸡毛掸子', price: 23.19 }
]
},
// 局部过滤器,只有当前vue实例里能使用
filters: {
formatStatus (status) {
// status就是使用过滤器位置的值
const statusMap = {
'001': '未付款',
'002': '待发货',
'003': '已发货',
'004': '已签收',
'005': '已评价'
}
return statusMap[status]
},
toFixed2 (num) {
return num.toFixed(2)
}
}
})
</script>
九、AJAX请求
1、 fetch
-
原生的fetch,返回的是一个promise,返回的结果做了二次封装,所以第一个then里先解析
<div id="app"> <ol> <li v-for="todo in todos" :key="todo.id"> {{todo.title}} </li> </ol> </div> <script> const app = new Vue({ el: '#app', data: { todos: [] }, created () { // 一开始就会执行的代码 // 原生的fetch,返回的是一个promise,返回的结果做了二次封装,所以第一个then里先解析 fetch('https://jsonplaceholder.typicode.com/todos') .then(resp => resp.json()) .then(todos => { this.todos = todos }) } }) </script>
2、 axios
- 使用前必须先引入axios.min.js或axios包,可以配置许多参数(如baseURL和headers)。还可以进行请求拦截和响应拦截,还可以把request挂在Vue的原型上,这样的话用的时候可以直接this.$request而不用每次引入。
<div id="app">
<ol>
<li v-for="todo in todos" :key="todo.id">
{{todo.title}}
</li>
</ol>
</div>
<script>
const request = axios.create({
baseURL: 'https://jsonplaceholder.typicode.com',
})
// 请求拦截器:每次请求发出去之前都会进入这里
request.interceptors.request.use((config) => {
// 在这里可以对请求的一些参数做全局配置,比如token,每隔请求接口都要携带的数据在这里配置
// config.url += '?token=dhfgkalk'
// 在这里还可以给用户转个圈
console.log('request starting')
// 这里必须要return加工之后的config,请求才能发的出去
return config
})
// 响应拦截器:每次数据返回的时候进入这里
request.interceptors.response.use(response => {
// 停止转圈
console.log('recieved response')
// 在这里我们还可以做一些全局错误处理
if (response.status === 200) {
return response.data
}
return response
})
// 可以把request挂在Vue的原型上,这样的话用的时候可以直接this.$request而不用每次引入
Vue.prototype.$request = request
const app = new Vue({
el: '#app',
data: {
todos: []
},
created () {
this.$request.get('/todos').then(todos => {
console.log(todos)
this.todos = todos
})
}
})
</script>
九、component组件
- *注意:*在template里只能由一个子元素
1、 全局注册
const hello = {
template: '<p>hello components</p>'
}
// 把这个hello对象注册成为一个helloWorld全局组件
Vue.component('helloWorld', hello)
2、 局部注册
const hello2 = {
template: '<p>hello components2222</p>'
}
const app = new Vue({
el: '#app',
data: {
title: 'hello 1912'
},
components: {
hello2
}
})
3、 普通写法
-
将组建的内容写在对象里面,相当于字符串做值
<div id="app"> <hello-world></hello-world> <hello2></hello2> </div> <div id="app1"> <hello-world></hello-world> <!-- hello2是app实例里局部注册的组件,所以这里用不了 --> <!-- <hello2></hello2> --> </div> <script> // 定义了对象 const hello = { template: '<p>hello components</p>' } // 把这个hello对象注册成为一个helloWorld全局组件 Vue.component('helloWorld', hello) const hello2 = { template: '<p>hello components2222</p>' } const app = new Vue({ el: '#app', data: { title: 'hello 1912' }, components: { hello2 } }) const app1 = new Vue({ el: '#app1', data: { } }) </script>
4、 高级写法
- 可以在DOM中定义template,template选项指向这个template ,将组建的内容写在html里面,在js里面进行操作,方便观察组件的结构以及组件的嵌套。
<template id="hello">
<div>
<p>hellovvvvvvvvvvvv</p>
<son></son>
</div>
</template>
<template id="son">
<div>
son
</div>
</template>
<div id="app">
{{title}}
<hello-world></hello-world>
</div>
<script>
// 嵌套组件
const son = {
template: '#son'
}
const helloWorld = {
template: '#hello'
components: {
son
},
}
const app = new Vue({
el: '#app',
data: {
title: 'hello 1912'
},
components: {
helloWorld
}
})
</script>
5、 组件内部属性
- 组件都是对象,这个对象里有template选项,还可以定义data,methods,computed这些Vue实例里有的选项, 组件里的data是一个方法,返回一个对象,data属于当前组件本身,只有这个组件可以使用,子父组件里的data默认情况下不会互相影响的。其他和vue的入口#app差不多。
<div id="app">
{{title}}
<hello-world></hello-world>
</div>
<script>
// 子组件都是对象,这个对象里有template选项,还可以定义data,methods,computed这些Vue实例里有的选项
const helloWorld = {
template: '<p>hello {{title | a}}</p>',
// 组件里的data是一个方法,返回一个对象
// 这里定义的data属于当前组件本身,只有这个组件可以使用
// 子父组件里的data默认情况下不会互相影响的
data () {
return {
title: 'happy new year'
}
},
filters: {
a (value) {
return value + ' 123'
}
}
}
const app = new Vue({
el: '#app',
data: {
title: 'hello 1912'
},
components: {
helloWorld
}
})
</script>
6、 组件props(父->子)通信
-
父组件通过属性给子组件传值,子组件通过props接收属性值,可以直接当做data属性用。
<shop-item v-for="shopItem in list" :key="shopItem.id" :title="shopItem.title" :status="shopItem.status" :price="shopItem.price" :id="shopItem.id" ><shop-item> props: [ 'title', 'status', 'id', 'price' ],或者 props: { title: String, status: { type: String, required: false, default: '001' }, id: Number, price: Number },
<template id="shop-item">
<div>
<!-- 3. 子组件接收到的props就可以直接作为data去使用 -->
<p>
<span>{{ id }}</span>
<b>{{ title }}</b>
<span>{{ status | formatStatus }}</span>
<em>{{ price | toFixed2 }}</em>
</p>
</div>
</template>
<div id="app">
<div class="container">
<!-- 子组件也可以写成单标签 -->
<!-- 1. 父组件里使用子组件的时候可以通过绑定属性来传参 -->
<shop-item
v-for="shopItem in list"
:key="shopItem.id"
:title="shopItem.title"
:status="shopItem.status"
:price="shopItem.price"
:id="shopItem.id"
/>
</div>
</div>
<script>
const shopItem = {
template: '#shop-item',
// 2. 在子组件里就可以通过props来接收参数
// props: [ 'title', 'status', 'id', 'price' ],
// props还可以用一个对象的方式来接收
props: {
title: String,
status: {
type: String,
required: false,
default: '001'
},
id: Number,
price: Number
},
filters: {
formatStatus (status) {
// status就是使用过滤器位置的值
const statusMap = {
'001': '未付款',
'002': '待发货',
'003': '已发货',
'004': '已签收',
'005': '已评价'
}
return statusMap[status]
},
toFixed2 (num) {
return num.toFixed(2)
}
}
}
const app = new Vue({
el: '#app',
data: {
list: [
{ id: 1, status: '001', title: '拖把', price: 19.99999 },
{ id: 2, status: '004', title: '扫把', price: 9 },
{ id: 3, status: '003', title: '簸箕', price: 29.1 },
{ id: 4, status: '002', title: '吸尘器', price: 2999.159839 },
{ id: 5, status: '005', title: '鸡毛掸子', price: 23.19 }
]
},
components: {
shopItem
}
})
</script>
7、组件emit(子->父)通信
-
父组件通过触发emit时间操作子组件的值。
<div id="box"> 父组件--{{myname}} <child @myevent="handleEvent"></child> </div> <script type="text/javascript"> //子组件 Vue.component("child",{ template:`<div> 我是child <button @click="handleClick()">click</button> </div>`, data(){ return { childname:"我式child状态" } }, methods:{ handleClick(){ //通知父组件 this.$emit("myevent","来自子组件问候",this.childname) } } }) new Vue({ el:"#box", data:{ myname:"" }, methods: { handleEvent(data1,data2){ console.log("父组件调用",data1,data2) // data2 this.myname = data2 } } }) </script>
8、 中央时间总线event bus
- bus其实就是一个空的Vue实例,作用帮助两个非父子关系组件之间通信
<div id="app">
<dage></dage>
<erdi></erdi>
</div>
<script>
// 事件总线:event bus
// bus其实就是一个空的Vue实例,作用帮助两个非父子关系组件之间通信
const bus = new Vue()
const dage = {
template: '<p @click="playErdi">打我弟</p>',
methods: {
playErdi () {
// 先把这个行为通知bus,bus再去通知其他关联的组件
// 向bus触发一个自定义事件
bus.$emit('play', { ku: true })
}
}
}
const erdi = {
template: '<div><p v-if="ku">哭 wuwuwu</p></div>',
data () {
return {
ku: false
}
},
created () {
// 通过bus去监听play事件
bus.$on('play', ({ ku }) => {
this.ku = ku
})
}
}
const app = new Vue({
el: '#app',
data: {
},
components: {
dage,
erdi
}
})
</script>
9、refs通信
- ref放在标签上, 拿到的是原生节点, ref放在组件上, 拿到的是组件对象,比较粗暴的直接获取原生对象节点。
<div id="box">
<input type="text" ref="myinput"/>
<input type="password" ref="mypassword"/>
<button @click="handleClick()">click</button>
<child ref="mychild"></child>
</div>
<script type="text/javascript">
//子组件
Vue.component("child",{
template:`<div>
child
</div>`,
data(){
return {
childname:"11111111111"
}
},
methods:{
getState(data){
console.log(data)
}
}
})
new Vue({
el:"#box",
data:{
parentname:"2222222222"
},
methods: {
handleClick(){
// console.log(this.$refs.myinput.value,this.$refs.mypassword.value)
console.log(this.$refs.mychild.childname)
// this.$refs.mychild.childname="22222"
this.$refs.mychild.getState(this.parentname)
}
},
})
</script>
10、 v-model通信
<div id="box">
<input type="text" v-model="mytext"/>
<child :mytext="mytext" @event="handleEvent"></child>
</div>
<script>
Vue.component("child",{
template:`
<div>
child-{{mytext}}
<button @click="handleClick">click</button>
</div>
`,
props:["mytext"],
methods: {
handleClick(){
this.$emit("event","222222222222222222222")
}
},
})
new Vue({
el:"#box",
data:{
mytext:""
},
methods: {
handleEvent(data){
this.mytext = data
}
},
})
</script>
11、props修改
- this.$emit(“update:title”,“2222222222”),加update关键字
<div id="box">
{{mytitle}}
<child :title.sync="mytitle"></child>
</div>
<script>
Vue.component("child",{
template:`
<div v-once>
child--{{title}}
<button @click="handleClick()">胆大的按钮</button>
</div>
`,
methods: {
handleClick(){
// this.title="kerwin-"+this.title
this.$emit("update:title","2222222222")
}
},
props:["title"]
})
new Vue({
el:"#box",
data:{
mytitle:"11111111111111"
}
})
</script>
12、动态组件dynamic
-
comoponent标签的is属性来决定渲染哪一个子组件
-
<keep-alive><keep-alive>
缓存当前页面的数据,如正在输入时跳转到其他页面再回到此页面时输入的数据还在。
<div id="box">
<!-- <shopcar></shopcar> -->
<keep-alive>
<component :is="which"></component>
</keep-alive>
<footer>
<ul>
<li><a @click="which='home'" >首页</a></li>
<li><a @click="which='list'">列表页</a></li>
<li><a @click="which='shopcar'">购物车页面</a></li>
</ul>
</footer>
</div>
<script type="text/javascript">
var vm = new Vue({
el:"#box",
data:{
which:"home"
},
components:{
"home":{template:`<div>home组件<input type="text"/></div>`},
"list":{template:`<div>list组件</div>`},
"shopcar":{template:`<div>shopcar组件</div>`}
}
})
</script>
13、插槽slot
- 在定义子组件的时候预留一个插槽,使用子组件的时候可以传递html结构进来。单插槽不需要写name,直接插入即可,但是多插槽的话需要给每个插槽写上name属性,插入标签的时候写上slot属性,跟插槽的name对应
<template id="hello">
<div>
<slot name="s1"></slot>
<h3>{{title}}</h3>
<slot name="s2"></slot>
</div>
</template>
<div id="app">
<hello-world :title="title">
<p slot="s1">content</p>
<p slot="s2">content2</p>
</hello-world>
<hello-world :title="title">
<son slot="s1"></son>
</hello-world>
</div>
<script>
const helloWorld = {
template: '#hello',
props: ['title', 'content']
}
const son = {
template: '<b>son</b>'
}
const app = new Vue({
el: '#app',
data: {
title: 'hello 1912'
},
components: {
helloWorld,
son
}
})
</script>
-
新写法 ,template不会渲染成任何标签。
<div id="box"> <child> <template v-slot:a> <div>联通卡</div> </template> <template v-slot:b> <div>移动卡</div> </template> <template v-slot:c> 电信卡 </template> </child> </div> <script type="text/javascript"> Vue.component("child",{ template:`<div> 111111 <slot name="a"></slot> 2222222 <slot name="b"></slot> 3333333 <slot name="c"></slot> </div> ` }) new Vue({ el:"#box" }) </script>
十、transition过渡动画
<transition>
把过渡效果应用到其包裹的内容上 ,name和mode,mode属性可以指定过度模式: out-in指的是先消失,消失之后新的组件在进来
<style>
.v-enter,
.v-leave-to {
opacity: 0;
}
.v-enter-active,
.v-leave-active {
transition: opacity 1s;
}
.v-enter-to,
.v-leave {
opacity: 1;
}
.guoji-enter,
.guoji-leave-to {
position: absolute;
left: -100%;
}
.guoji-enter-active,
.guoji-leave-active {
position: absolute;
transition: left 1s;
}
.guoji-enter-to,
.guoji-leave {
position: absolute;
left: 0;
}
</style>
</head>
<body>
<div id="app">
<button @click="news = 'guonei'">国内新闻</button>
<button @click="news = 'guoji'">国际新闻</button>
<div>
<!-- 没有name属性的过度应用的是全局的v-开头的class -->
<!-- mode属性可以指定过度模式: out-in指的是先消失,消失之后新的组件在进来 -->
<transition mode="out-in">
<component :is="news"></component>
</transition>
<!-- 加了name属性过度应用的就是guoji-开头的class -->
<transition name="guoji">
<component :is="news"></component>
</transition>
</div>
</div>
<script>
const guonei = {
template: '<h3>cctv国内新闻</h3>'
}
const guoji = {
template: '<h3>international news</h3>'
}
const app = new Vue({
el: '#app',
data: {
news: 'guoji'
},
components: {
guonei,
guoji
}
})
</script>
- 写法2:@keyframes,reverse
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Examples</title>
<meta name="description" content="">
<meta name="keywords" content="">
<link href="" rel="stylesheet">
<style>
.a-enter-active, .a-leave-active {
transition: all 1.5s;
}
.a-enter, .a-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
transform: translateX(100px);
}
.b-enter-active {
animation: aaa 1.5s;
}
.b-leave-active {
animation: aaa 1.5s reverse;
}
@keyframes aaa {
0% {
opacity: 0;
transform: translateX(100px);
}
100% {
opacity: 1;
transform: translateX(0px);
}
}
</style>
<link rel="stylesheet" href="lib/animate.css">
<script type="text/javascript" src="lib/vue.js"></script>
</head>
<body>
<!-- <h1 class="animated hinge">动画</h1> -->
<div id="box">
<button @click="isShow=!isShow">click</button>
<transition name="a">
<div v-if="isShow">
111111111111111111111
</div>
</transition>
<transition name="b">
<div v-if="isShow">
222222222222222222
</div>
</transition>
<transition enter-active-class="animated bounceInRight"
leave-active-class="animated bounceOutRight">
<div v-if="isShow">
3333333333333333333333
</div>
</transition>
</div>
<script>
var vm = new Vue({
el:"#box",
data:{
isShow:true
}
})
</script>
</body>
</html>
-
写法三
<transition enter-active-class="animated bounceInRight" leave-active-class="animated bounceOutRight"> <div v-if="isShow"> 3333333333333333333333 </div> </transition>
-
transition-group
<style> .b-enter-active { animation: aaa 1.5s; } .b-leave-active { animation: aaa 1.5s reverse; } @keyframes aaa { 0% { opacity: 0; transform: translateX(100px); } 100% { opacity: 1; transform: translateX(0px); } } </style> </head> <body> <div id="box"> <input type="text" v-model="mytext" /> <button @click="handleClick()">add</button> <transition-group tag="ul" name="b"> <li v-for="(item,index) in items" :key="item"> {{item}} -{{index}} <button @click="handleDelClick(index)">del</button> </li> </transition-group> </div> <script> var vm = new Vue({ el: "#box", data: { mytext: "", //状态 items: ["aaa", "bbbb", "ccc"] }, methods: { handleClick() { console.log("输入框value", this.mytext) this.items.push(this.mytext) }, handleDelClick(index) { console.log("del", index) this.items.splice(index, 1); } }, }) </script>
十一、生命周期
<!DOCTYPE html>
<html lang="en">
<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="./vue.js"></script>
</head>
<body>
<div id="app">
<input type="text" ref="myinput">
{{msg}}
</div>
<script>
const app = new Vue({
el: '#app',
data: {
msg: 'hello 1912'
},
beforeCreate () {
// vue实例创建之前,这里什么都获取不到
console.log('--------beforeCreate----------')
console.log(this.$el, this.$refs.myinput, this.$data)
},
created () {
// vue实例创建完成,在这里可以做一些初始化的操作,一般初始的ajax请求很多时候可以在这里做
console.log('--------created----------')
console.log(this.$el, this.$refs.myinput, this.$data)
},
beforeMount () {
// 挂载之前,这里虚拟DOM已经创建好了,但是还没有真实DOM
console.log('--------beforeMount----------')
console.log(this.$el, this.$refs.myinput, this.$data)
},
mounted () {
// 完成挂载,这里就能获取到真实DOM了
console.log('--------mounted----------')
console.log(this.$el, this.$refs.myinput, this.$data)
},
beforeUpdate () {
// 如果修改了数据,这是数据更新以后,DOM刷新之前,这货没啥用
console.log('--------beforeUpdate----------')
console.log(this.$el, this.$refs.myinput, this.$data.msg)
},
updated () {
// 如果修改了数据,这是数据更新以后,DOM也完成刷,这货也没啥用
console.log('--------updated----------')
console.log(this.$el, this.$refs.myinput, this.$data.msg)
},
beforeDestroy () {
// 销毁之前,一般在这里做一些善后工作,比如:清除定时器,或者在销毁之前给后端发送一些数据
},
destroyed () {
// 销毁完成,这里也没啥好做的
}
})
</script>
</body>
</html>
十二、Vue.nextTick
-
修改相应到真实DOM之后才能调用swiper
<!DOCTYPE html> <html lang="en"> <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="./vue.js"></script> <script src="https://cdn.bootcss.com/Swiper/4.5.1/js/swiper.min.js"></script> <link href="https://cdn.bootcss.com/Swiper/4.5.1/css/swiper.min.css" rel="stylesheet"> <style> .swiper-container { width: 800px; height: 500px; } </style> </head> <body> <div id="app"> <div class="swiper-container"> <div class="swiper-wrapper"> <div clas hs="swiper-slide" v-for="item in banner" :key="item.id">{{ item.title }}</div> </div> <!-- 如果需要分页器 --> <div class="swiper-pagination"></div> </div> </div> <script> const app = new Vue({ el: '#app', data: { banner: [] }, created () { // 在这里发送ajax请求,拿到swiper的数据渲染轮播图 // 用定时器模拟一下 // 有可能在拿到数据以后DOM还没有更新 setTimeout(() => { this.banner = [ { id: 1, title: 'slider1' }, { id: 2, title: 'slider2' }, { id: 3, title: 'slider3' } ] // 应该banner的修改相应到真实DOM之后才能调用swiper Vue.nextTick().then(() => { this.initSwiper() }) }, 100) }, methods: { initSwiper () { var mySwiper = new Swiper ('.swiper-container', { loop: true, // 循环模式选项 // 如果需要分页器 pagination: { el: '.swiper-pagination', }, // 如果需要前进后退按钮 navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev', }, // 如果需要滚动条 scrollbar: { el: '.swiper-scrollbar', }, }) } } }) </script> </body> </html>
<swiper :key="datalist.length" :perview="3"> <div v-for="data in datalist" :key="data.filmId" class="swiper-slide"> <img :src="data.poster"/> </div> </swiper>
十三、自定义指令
-
directives - 对普通 DOM 元素进行底层操作
(2)钩子函数
- 参数 el,binding,vnode,oldvnode
- bind,inserted,update,componentUpdated,unbind
-
bind
:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。 -
inserted
:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。 -
update
:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 -
componentUpdated
:指令所在组件的 VNode 及其子 VNode 全部更新后调用。 -
unbind
:只调用一次,指令与元素解绑时调用。 -
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 }。
-
vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。
-
oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。
<div id="box"> <!-- <div v-show="isShow"></div> --> <div v-hello="'red'">{{myname}}</div> <div v-hello="'yellow'">{{myname}}</div> <div v-hello="'blue'">{{myname}}</div> {{mycolor}} <div v-hello="mycolor">{{myname}}</div> <input type="text" v-focus/> </div> <script type="text/javascript"> Vue.directive("hello",{ inserted(el,binding){ //指令的生命周期-1 ,这个节点插入dom,会被调用 console.log(binding.value) el.style.background=binding.value }, update(el,binding){ // update 不是updated el.style.background=binding.value } }) Vue.directive("focus",{ inserted(el){ el.focus() } }) var vm = new Vue({ el:"#box", data:{ mycolor:"pink", myname:"kerwin" } }) </script> </body> </html>