一、Vue简介
vue是什么
- vue.js是一套构建用户界面的框架
vue的两个特性
- 数据驱动视图:数据的变化会驱动视图的自动更新
- 双向数据绑定:js数据的变化,会被自动渲染到页面上;页面上表单采集的数据发生变化时,会被vue自动获取到,并更新到js数据中
MVVM
Model数据源,View视图,ViewModel是vue的实例
vue的基本使用
- 导入vue.js的script脚本文件
- 在页面中声明一个将要被vue控制的DOM区域
- 创建vue实例对象
<div id="app">{{ username }}</div>
<script src="./lib/vue-2.6.12.js"></script>
<script>
const vm = new Vue({
el: '#app', // el属性,接收的值是一个选择器
data: {
username: 'zs'
} // data对象是要渲染到页面上的数据
methods: {
// ...
} // methods用于定义事件的处理函数
})
</script>
二、Vue指令
内容渲染指令
v-text
,会覆盖元素内部原有的内容{{ }}
,插值表达式,不能用在属性节点v-html
,可以把带标签的字符串渲染成html
属性绑定指令
v-bind
,为元素的属性动态绑定值,简写为:
<img :src="photo" alt="">
注意:插值表达式和属性绑定指令中可以写js表达式
事件绑定指令
v-on
,简写为@
,用于给元素绑定事件- 如果在方法中要修改
data
中的数据,可以通过this
访问到 $event
事件对象,防止事件传参覆盖事件对象e
<div id="app">
<button @click="add(2,$event)"></buttton>
</div>
<script>
const vm = new Vue({
el: '#app',
methods:{
add(step,e){ // e:事件对象
const bgc = e.target.style.backgroundColor
this.count += step
}
}
})
</script>
- 事件修饰符
.prevent
阻止默认行为.stop
阻止事件冒泡
<button @click.stop = "add(2,$event)"></button>
- 按键修饰符
.esc
.enter
<input @keyup.esc = "clearInput">
双向数据绑定指令
v-model
,只能用在表单元素上,页面上的改动同步到数据源- 修饰符
.number
自动将用户输入值转换为数字型.trim
自动过滤用户输入的首尾空格.lazy
在change
时而非input
时更新
条件渲染指令
- 将根据表达式值(
true
或false
)决定是否显示元素 v-if
,动态创建/移除元素v-show
,添加/移除display:none
v-else-if
v-else
<div v-if="type === 'A'">优秀</div>
<div v-else-if="type === 'B'">良好</div>
<div v-else-if="type === 'C'">一般</div>
<div v-else>差</div>
循环渲染指令
v-for
<li v-for="(item, index) in list" :key="item.id">姓名是:{{ item.name }}</li>
...
data: {
list[
{ id:1, name: 'zs'},
{ id:2, name: 'ls'},
{ id:3, name: 'ww'}
]
}
注意:用索引当key没有意义
三、Vue过滤器,侦听器,计算属性
过滤器
- 要定义到
filters
节点下,本质是一个函数 - 在过滤器函数中,一定要有
return
值 - 在过滤器的形参中,可以获取到管道符
|
前面待处理的那个值
<p>message的值是:{{ message | capi }}</p>
...
filters: {
capi(val){
...
}
}
- 全局过滤器写法:
Vue.filter('函数名',funciton(过滤器前面的值){})
如果全局过滤器和私有过滤器名字一致,此时按照就近原则,调用的是私有过滤器
侦听器
- 方法格式的侦听器
watch: { // username:要监听的data节点中的数据名
username(newVal, oldVal){
...
}
}
- 对象格式的侦听器
watch: {
username: {
handler(newVal,oldVal) {
...
},
immediate: true, // 加载页面自动触发
deep: true // 深度监听对象中每个属性的变化
}
}
- 监听对象单个属性的变化
data: {
info: {
username: 'admin'
}
},
watch:{
'info.username': {
handler(newVal,oldVal){
...
}
}
}
计算属性
- 要定义到
computed
节点下,要被定义为方法 - 在使用计算属性的时候,当普通的属性使用即可
- 要
return
一个计算结果 - 优点:实现了代码复用,只要计算属性中依赖的数据源变化了,则计算属性会自动重新求值
四、vue组件
组件的三个组成部分
<template>
// 不能出现两个根元素
...
</template>
<style lang="less"> //启用less语法
...
<style>
<script>
export default {
data() { //组件中的data必须是函数
return {
username: 'admin'
}
},
methods: {
changeName() {
this.username = '哇哈哈'
}
},
watch: {},
computed: {},
filters: {},
components: {}
}
</script>
使用组件的三个步骤
- 使用
import
语法导入需要的组件 - 使用
components
节点注册节点 - 以标签形式使用组件
<template>
<div class="box">
<Left></Left>
</div>
</template>
<script>
import Left from '@/components/Left.vue'
export default {
components: {
'Left': Left
}
}
</script>
...
注册全局组件
在main.js
中通过import
导入组件
import Count from '@/components/Count.vue'
Vue.component('MyCount', Count)
组件间样式冲突
- 默认情况下写在
.vue
中的样式全局生效,易造成样式冲突 - 解决方法:给
<style>
标签添加scoped
属性 /deep/
样式穿透- 在父组件中改子组件样式,在子组件中加
/deep/
- 修改第三方组件默认样式
- 在父组件中改子组件样式,在子组件中加
组件的生命周期
- 生命周期:每个组件从创建->运行->销毁的一个过程
- 生命周期函数:在生命周期的不同阶段,会按次序,自动执行的函数
beforeCreate
created
- 可以调用
methods
中的方法,请求服务器的数据 - 可以把请求到的数据,转存到
data
中,供template
模板渲染的时候使用
- 可以调用
beforeMount
mounted
- 如果要操作当前组件的
DOM
,最早,只能在mounted
阶段执行
- 如果要操作当前组件的
beforeUpdate
updated
- 当数据变化之后,操作最新的
DOM
结构
- 当数据变化之后,操作最新的
beforeDestroy
destroyed
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rXONjjSS-1661245247524)(9886AC03753F4F70A231881FF3B8FD5C)]
五、组件间的数据共享
父向子传值:自定义属性
- 子组件中,通过
props
来自定义属性 - 父组件中,负责把数据,通过
v-bind:
绑定给子组件 props
自定义属性:- 在封装通用组件时,可以利用
props
提高组件的复用性 props
是只读的,若想修改可以把props
的值转存到data
中
- 在封装通用组件时,可以利用
<MyCount :init="9"></MyCount>
...
props: {
init: {
default: 0, // 默认值
type: Number, // 值类型
required: true // 必填项
}
}
子向父传值:自定义事件
- 在子组件中调用
this.$emit()
来触发自定义事件- 参数1:字符串,表示自定义事件的名称
- 参数2:值,要发送给父组件的数据
- 在父组件中,通过
v-on:
来绑定自定义事件,并提供一个事件处理函数。通过事件处理函数的形参,接收到子组件传递过来的数据
// 子组件:
<button @click="onBtnClick"></button>
<scripts>
export default {
// 触发自定义事件
methods:{
onBtnClick(){
this.$emit('change',this.count)// 第二个参数用来传参
}
}
}
</scripts>
// 父组件
// 监听自定义事件
<mycounter @change="getCount"></mycounter>
<scripts>
export default {
methods:{
getCount(val){ // 接收this.count
// ...
}
}
}
兄弟组件共享数据:EventBus
- 数据发送方:导入
eventBus.js
,调用bus.$emit
触发自定义事件
import bus from './eventBus.js'
export default {
data() {
return {
msg: 'hello vue.js'
}
},
methods: {
sendMsg() {
bus.$emit('share', this.msg)
}
}
}
EventBus
模块:创建并向外导出Vue
的实例对象
import Vue from 'vue'
export default new Vue()
- 数据接收方:
导入eventBus.js
,调用bus.$on
注册一个自定义事件
import bus from './eventBus.js'
export default {
data() {
return {
msgFromLeft: ''
}
},
created() {
bus.$on('share', val=>{
this.msgFromLeft = val
})
}
}
六、路由
路由简介
- 概念:Hash地址path与组件component的对应关系
- 工作方式
- 用户点击了页面上的路由链接
- 导致了URL地址栏中Hash值发生了变化
- 前端路由监听到了Hash地址的变化
- 前端路由把当前Hash地址对应的组件渲染到浏览器中
vue-router的安装配置
- 安装
vue-router
包 - 创建路由模块:在
src
源码目录下,新建router/index.js
路由模块
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const router = new VueRouter()
export default router
- 导入并挂载路由模块,在
main.js
中
import router from '@/router/index.js'
...
new Vue({
router: router
})
- 声明路由链接和占位符
- 路由链接
<router-link>
可代替<a>
- 占位符
<router-view></router-view>
- 路由链接
<a href="#/home">首页</a>
<router-link to="/home">首页</router-link>
- 声明路由规则,在路由模块
index.js
中
const router = new VueRouter({
routes: [
{ path: '/', redirect: '/home' }, //重定向
{ path: '/home', component: Home },
{ path: '/movie', component: Movie }
]
})
嵌套路由
const router = new VueRouter({
routes: [
{ path: '/about', component: About, children:[ // 子路由规则
// 默认子路由:在children数组在,path值为空,展示父组件时默认展示
{ path: '', component: Tab1 },
{ path: 'tab2', component: Tab2 }
] },
]
动态路由
- 概念:把Hash地址中可变的部分定义为参数项,从而提高路由规则的复用性
$route
- 用冒号声明,
:参数名
- 路径参数,
this.$route.params.参数名
- 查询参数,
this.$route.query
- 完整路径,
this.$route.fullPath
- 用冒号声明,
// 对应vue组件
<h3>{{ this.$route.params.mid }}</h3>
// index.js
{ path: '/movie/:mid', component: Movie }
- 为路由规则开启props传参
// 对应vue组件
props:['mid']
// index.js
{ path: '/movie/:mid', component: Movie, props: true }
编程式导航API
- 概念:
- 声明式导航:点击链接实现导航
- 编程式导航:调用API方法实现导航
this.$router.push('hash地址')
跳转到指定的hash地址,并增加一条历史记录this.$router.replace('hash地址')
跳转到指定的hash地址,并替换掉当前的历史记录this.$router.go(数字n)
可以在浏览历史中前进或后退this.$router.forward()
this.$router.back()
导航守卫
- 功能:控制路由的访问权限
- 全局前置守卫
const router = new VueRouter({ ... })
// 只要发生了路由的跳转,必然触发beforeEach的回调函数
router.beforeEach((to, from, next) => {
//to表示将要访问的路由对象
//from表示将要离开的路由对象
//next()函数表示放行
//next函数的三种调用方式:
//next(),next('/login'),next(false)
if(to.path === '/main'){
const token = localStroage.getItem('token')
if(token){
next()
}else{
next('/login')
}else{
next()
}
}
})
七、ref引用、动态组件、插槽、自定义指令
ref引用
ref
不仅可以用来引用DOM元素,也可以用来引用组件实例- 在标签内或者组件内添加一个
ref
属性即可
<h1 ref="helloRef">hello</h1>
- 使用:
this.$refs.helloRef.
//+组件内的数据或者是方法
this.nextTick(cb)
把cb回调函数推迟到下一个更新DOM周期之后执行
动态组件
- 通过
:is
改变实现动态切换组件
<component :is='动态组件的名称'></component>
<keep-alive>
可以把内部组件进行缓存,而不是销毁组件
<keep-alive>
<component :is='动态组件的名称'></component>
</keep-alive>
<keep-alive>
的生命周期函数:activated
,组件被激活;deactivated
,组件被缓存- 可通过
include/exclude
之一指定哪些组件需要/不需要被缓存
插槽
- 作用:插槽就是在自定义组件中预留一个位置,这个位置的内容可以由使用组件的人来定义,具有高度的灵活性
- 默认插槽
//在my-com组件中
<slot>后备内容</slot>
//使用组件
<my-com>插槽内容</my-com>
- 具名插槽
<slot name="left"></slot> //默认name为default
<template v-slot:left></template> //v-slot只能用在template标签上
<template #left></template>
- 作用域插槽
<slot name="left" msg="hello"></slot>
<template #left="obj">{{ obj.msg }}</template>
<template #left="{ msg }">{{ msg }}</template>
自定义指令
- 私有自定义指令
directives:{
color:{
//binding:用户通过=传的值
bind(el,binding) {
el.style.color = binding.value
}
//在DOM更新的时候,会触发update函数
update(el,binding) {
el.style.color = binding.value
}
}
}
directives:{
//简写
color(el,binding) {
el.style.color = binding.value
}
}
}
- 全局自定义指令
Vue.directive('color',function(el,binding){
el.style.color = binding.value
})
八、Vuex
简介
Vuex
是实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间数据的共享- 核心概念
State
Mutation
Action
Getter
Vuex的使用
- 安装
vuex
依赖包 - 导入
vuex
包
import Vuex from 'vuex'
Vue.use(Vuex)
- 创建
store
对象
const store = new Vuex.Store({
// state 中存放的是全局共享的数据
state: {
count: 0
},
mutations: {
},
actions: {
}
})
- 把
store
对象挂载到vue
实例中
new Vue({
el: '#app',
render: h => h(app),
router,
// 将创建的共享数据对象,挂载到 Vue 实例中
// 所有的组件,就可以直接从 store 中获取全局的数据了
store
})
State
State
提供唯一的公共数据源,所有共享的数据要统一放到Store
的State
中进行存储
// 创建 store 数据源,提供唯一的公共数据
const store = new Vuex.Store({
state: { count: 0 }
})
- 组件访问
State
中数据的第一种方式:
this.$store.state.全局数据名称
- 组件访问
State
中数据的第二种方式:
// 1. 从 vuex 中按需导入 mapState 函数
import { mapState } from 'vuex'
// 2. 将全局数据,映射为当前组件的 computed 计算属性
computed: {
... mapState(['count'])
}
Mutation
Mutation
用于变更Store
中的数据- 只能通过
mutation
变更Store
数据,不可以直接操作Store
中的数据,这种方式可以集中监控所有数据的变化 - 可以在触发
mutations
时传递参数 - 不要再
mutations
函数中,执行异步操作
// 定义 Mutation
const store = new Vuex.Store({
state: {
count: 0
},
mutation: {
addN(state, step) {
// 变更状态
state.count += step
}
}
})
// 触发 mutation
methods: {
handle(){
// 调用 commit 函数
// 触发 mutations 时可以携带参数
this.$store.commit('addN', 3)
}
}
- 触发
mutations
的第一种方式
this.$store.commit()
- 触发
mutations
的第二种方式:
// 1. 从 vuex 中按需导入 mapMutations 函数
import { mapMutations } from 'vuex'
// 2. 将需要的 mutations 函数,映射为当前组件的 methods 方法
methods: {
... mapMutations(['add', 'addN'])
}
Action
- Action 用于处理异步任务
- 如果通过异步操作变更数据,必须通过
Action
,而不能用Mutation
,但是在Action
中还是要通过触发Mutation
的方式间接变更数据 - 触发
actions
异步任务时可以携带参数
// 定义 Action
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
addN(state, step) {
state.count += step
}
},
actions: {
addNAsync(context, step) {
setTimeout(() => {
context.commit('addN', step)
}, 1000)
}
}
})
// 触发 Action
methods: {
handle() {
// 触发 actions 的第一种方法
this.$store.dispatch('addAsync', 5)
}
}
- 触发
actions
的第一种方式
this.$store.dispatch()
- 触发
actions
的第二种方式:
// 1. 从 vuex 中按需导入 mapActions 函数
import { mapActions } from 'vuex'
// 2. 将需要的 actions 函数,映射为当前组件的 methods 函数
methods: {
... mapActions(['addASync', 'addNASync'])
}
Getter
Getter
用于对Store
中的数据进行加工处理形成新的数据,类似Vue
中的计算属性。Store
中数据发生变化,Getter
的数据也会跟着变化
// 定义 Getter
const store = new Vuex.Store({
state: {
count: 0
},
getters: {
showNum: state => {
return '当前最新的数量为[' + state.count + ']'
}
}
})
- 使用
getters
的第一种方式:
this.$store.getters.名称
- 使用
getters
的第二种方式:
import { mapGetters } from 'vuex'
computed: {
...mapGetters(['showNum'])
}