1 Vue是什么及优势
- 前端框架,可管理数据,根据数据渲染页面;也可根据页面交互更新数据,重新渲染页面。
- 优势:渐进式(可局部使用vue),模块化(组件)
2 文件结构
xxx.vue 后缀
- template中使用vue语法绑定属性及方法或控制展示:{{}}、v-bind、v-model、v-on、v-if/v-show、v-for
- js部分:设置展示使用的data、components、watch、methods、以及vue生命周期函数
- style 控制样式: scoped 局部样式
代码示例:
<template>
<div id="app">
<div class="todo-container">
<div class="todo-wrap">
<Header></Header>
<List :todos="todos"></List>
<Footer :todos="todos"></Footer>
<button @click="death">App自我毁灭</button>
</div>
</div>
</div>
</template>
<script>
import PubSub from 'pubsub-js'
import Footer from './components/Footer.vue'
import List from './components/List.vue'
import Header from './components/Header.vue'
export default {
name: 'App',
components: {
Header, List, Footer
},
data() {
return {
todos: JSON.parse(localStorage.getItem('todos')) || []
}
},
methods: {
//新增todo
//发布订阅,回调函数接收的第一个参数是消息名,实际用不到,可用_占位符接收
addTodo(_, todo) {
this.todos.unshift(todo);
},
//勾选/取消勾选操作
checkTodo(_, id) {
console.log('checkTodo被调用了')
this.todos.forEach(todo => {
todo.done = todo.id === id ? !todo.done : todo.done;
});
},
death() {
this.$destroy();
}
},
watch: {
// todos有变化时更新localStorage浏览器的本地存储
todos: {
deep: true,
handler(value) {
localStorage.setItem('todos', JSON.stringify(value))
}
}
},
mounted() {
//挂载事件(类似监听) 时间名+对应回调方法
this.$bus.$on('deleteTodo', this.deleteTodo);
this.PubSubAddTodoId = PubSub.subscribe('addTodo', this.addTodo);
this.PubSubCheckTodoId = PubSub.subscribe('checkTodo', this.checkTodo)
},
beforeDestroy() {
//销毁前,解绑自定义事件
this.$bus.$off('deleteTodo');
PubSub.unsubscribe(this.PubSubAddTodoId);
PubSub.unsubscribe(this.PubSubCheckTodoId);
}
}
</script>
<style>
body {
background: #fff;
}
.btn-danger {
color: #fff;
background-color: #da4f49;
border: 1px solid #bd362f;
}
</style>
3 Vue 语法
template中使用,定义如何渲染页面,可以直接使用vue实例上的属性,不用加this
3.1 插值语法{{js表达式}}
{{}}直接可以放data的属性,computed计算属性,常量以及由三者组成的js表达式,也可调用一些js自带的函数(可返回确定数值)例如toString(),trim()....
注意:if() for() 这类js语句不是表达式
3.2 指令语法
v-bind:为标签绑定属性,单向绑定;
注意:
:href绑定的是动态值,data中school.url的值
x="hello" 则是写死的
<a v-bind:href="school.url" x="hello">点我去{{school.name}}学习1</a>
//简写形式
<a :href="school.url" x="hello">点我去{{school.name}}学习2</a>
v-model: 绑定元素的value, 双向绑定;
双向绑定:用户交互时,元素状态改变时,对应data中的值也变化。
用于可交互的标签(input(text/checkbox)、select 下拉框)
<input type="text" v-model="name"><br/>
<input type="checkbox" v-model="todo.done"/>
//可以限制类型
<select v-model.number="n">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
v-on:为元素事件绑定函数
元素事件:click keyup keydown
注意:
- 未传参数时,默认传递$event
- 显示传递参数时,直接正常传参
- 如果需要获取$event及参数,形参要显示声明
<button @click="showInfo1">点我提示信息1(默认传递$event)</button>
<button @click="showInfo2(6)">点我提示信息1(传6)</button>
<button @click="showInfo3($event,66)">点我提示信息2(传$event,6)</button>
事件对应的函数:
- 简单逻辑可以直接写类似 @click="n++"
- 复杂逻辑,需在methods中定义方法,注意使用普通函数,保证this指代vm,因为大概率要操作 this.data
- event.target 对应触发事件的DOM元素
//01 简单逻辑直接写方法体
<button @click="n++">点击事件</button>
//02 复杂些的逻辑,直接调用methods对应的函数
<button @click="showInfo2($event,66)">点我提示信息2(传参)</button>
methods:{
showInfo1(event){
alert('同学你好!')
},
//接收$event及参数
showInfo2(event,number){
console.log(event,number)
// console.log(event.target.innerText)
// console.log(this) //此处的this是vm
alert('同学你好!!')
}
}
v-for 循环渲染
注意:
- 哪部分循环,v-for写在哪:li循环生成多份,写li上,不确定的话可以检查生成页面的dom元素确认。
- 设置唯一的:key,尽量避免使用数组index,因为在unshfit slice 等改变数组顺序的操作时,新旧DOM比对会错位,不能最大程度的diff。
<ul>
<li v-for="p in persons" :key="p.id"></li>
</ul>
v-if/v-show
v-show: 直接不加载元素,改动不频繁
v-if: 变化频繁,只是不可见,元素还在
<span v-show="!todo.isEdit">{{ todo.title }}</span>
3.3 事件类型及修饰符
3.3.1 鼠标事件 click
- prevent:阻止默认事件(常用)
- stop:阻止事件冒泡(常用)
- once:事件只触发一次(常用)
- capture:使用事件的捕获模式
- self:只有event.target是当前操作的元素时才触发事件
- passive:事件的默认行为立即执行,无需等待事件回调执行完毕
<!--阻止默认的跳转操作-->
<a href="http://www.atguigu.com" @click.prevent="showInfo">点我提示信息</a>
<!-- 阻止事件冒泡(常用) -->
<button @click.stop="showInfo">点我提示信息</button>
<button @click.once="showInfo">点我提示信息</button>
3.3.2 盘事件 keydown&keyup
按键别名
- 回车 => enter
- 删除 => delete (捕获“删除”和“退格”键)
- 退出 => esc
- 空格 => space
- 换行 =>tab (特殊,必须配合keydown去使用)
- 上下左右 => up down left right
<!-- input输入框中按下回车 -->
<input type="text" placeholder="按下回车提示输入" @keydown.enter="showInfo">
3.3.3 系统按键修饰符:ctrl、alt、shift、meta
<!-- Alt + Enter -->
<input @keyup.alt.enter="clear" />
<!-- Ctrl + 点击 -->
<div @click.ctrl="doSomething">Do something</div>
4 vue实例化options配置
4.1 el 挂载的目标元素
注意:
- 只有Root 元素可以这么写,其他组件template使用位置直接加标签或用路由控制
- 效果等同于.mount('#app')
new Vue({
el:'#root', //第一种写法
data:{
name:'尚硅谷'
}
})
new Vue({
data:{
name:'尚硅谷'
}
}).$mount('#root') //第二种写法
4.2 name
组件名称,对应Vue调试工具中的Vue结构的组件名。
export default {
name:'Hello',
methods:{
forward(){
this.$router.forward();
}
}
}
Vue DevTool中的组件树
4.3 data
组件中使用到的数据。
注意:只有Root中可以用对象形式,其他组件中需使用函数形式。
new Vue({
el:'#root',
//data的第一种写法:对象式
data:{
name:'尚硅谷'
},
})
new Vue({
el:'#root',
//data的第二种写法:函数式
data(){
return{
name:'尚硅谷'
}
}
})
4.4 computed
计算属性,是对象,对象的属性为函数,函数对data数据进行加工,返回计算后的值,template中可直接使用。
优势:有缓存机制,只有当依赖的值变化时,才重新计算。
注意:当涉及到修改计算属性值时,需要采取完整写法,配置get(),set()方法
<div id="root">
姓:<input type="text" v-model="firstName"> <br/><br/>
名:<input type="text" v-model="lastName"> <br/><br/>
全名:<span>{{fullName}}</span> <br/><br/>
</div>
computed:{
//完整写法
/* fullName:{
get(){
console.log('get被调用了')
return this.firstName + '-' + this.lastName
},
set(value){
console.log('set',value)
const arr = value.split('-')
this.firstName = arr[0]
this.lastName = arr[1]
}
} */
//简写
fullName(){
console.log('get被调用了')
return this.firstName + '-' + this.lastName
}
}
4.5 watch
作用:监视data中某个值的变化。
注意:
- Vue watch默认只检测一层,若要检测对象内部属性的变化,需开启deep:true。
- vue对修改数组元素的方法 push,pop,shift,unshift,sort,splice 做了封装,通过方法修改,会更新DOM,直接复制 person[0]={xxx},data有改变,但因为未做数据代理,不会更新DOM元素
- vue对数组对象元素中的每个属性做了数据代理,改变属性this.personArr[0].name = '李四';会更新DOM
data() {
return {
isHot:true,
personArr: [
{ id: 1, name: '张三', age: 18 }
]
}
},
methods: {
changePreson() {
//直接改变数组元素的值,Vue监测不到,页面数据不会变
this.personArr[0] = { id: 2, name: '李四', age: 100 };
//改变元素对应属性值,vue可以检测到
this.personArr[0].name = '李四';
this.personArr[0].age = 100;
}
},
watch:{
//正常写法
personArr: {
immediate:true, //初始化时让handler调用一下
deep: true,
handler(newValue, oldValue) {
console.log('person有变化')
},
}
//简写
isHot(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue,this)
}
}
4.6 methods
对象,属性是具体的方法。
methods: {
//新增todo
addTodo(todo) {
this.todos.unshift(todo);
},
//删除todo
deleteTodo(id) {
this.todos = this.todos.filter(todo => todo.id != id)
},
//删除所有已完成
clearAllDone() {
this.todos = this.todos.filter(todo => !todo.done)
}
},
4.7 props
接受父组件传递的属性或者路由配置中props。
//简单声明接收
props:['name','age','sex']
//待限制条件的声明接收
props:{
name:String,
age:Number,
sex:Boolean
}
4.8 生命周期函数
- beforeCreate():数据代理未开始,不能通过vm访问data、methods; 绑定事件总线Vue.prototype.$bus = this; 一切都为开始,来得及。
- created():数据代理完成,
- beforeMount():页面层显的是未经vue编译的DOM结构
- mounted():常用,页面已经经Vue编译,开启定时器、发送网络请求、订阅消息、绑定自定义事件、事件总线绑定事件。
- beforeUpdate():数据是新的,但页面是旧的,尚未对比新旧DOM,未重新渲染页面。
- updated():页面重新渲染完成
- beforeDestroy():常用,关闭定时器,取消订阅。
- destroyed():组件被销毁
mounted() {
//挂载事件(类似监听) 订阅消息
this.$bus.$on('deleteTodo', this.deleteTodo);
this.PubSubCheckTodoId = PubSub.subscribe('checkTodo', this.checkTodo)
//配置定时器
this.timer = setInterval(() => {
console.log('1');
this.opacity -= 0.01;
if (this.opacity <= 0) {
this.opacity = 1;
}
}, 16);
},
beforeDestroy() {
//销毁前,解绑自定义事件
this.$bus.$off('deleteTodo');
//取消订阅
PubSub.unsubscribe(this.PubSubAddTodoId);
PubSub.unsubscribe(this.PubSubCheckTodoId);
//删除定时器
clearInterval(this.timer)
}