- 单向绑定
v-bind:
:
- 双向绑定
v-model
v-model.number //收集到的为num格式
v-model.lazy //失焦的时候获取数据
v-model.trim //去空格
- 事件处理
v-on
@
- vue实例中el,data两种写法
new Vue({
el:'#root',
data:{
name:'123'
},
或
data(){ //只要是Vue管理的函数,都不要写箭头函数,不然this指向不对
return{
name:'123'
}
},
methods:{},
computed:{
fullName:{
get(){
return this.name+'123'
}
}
//简写:当确定只读不改的时候
fullName(){
return this.name+'123'
}
}
})
const vm = new Vue({
data:{}
})
v.$mount('#root') //不写el,通过挂载
- Object.defineProperty()
let number= 18
let person = {
name:'genji'
}
Object.defineProperty(person,'age',{
//value:18,
//enumerable:true, //控制属性是否可以枚举,默认false
//writable:true, //控制属性是否可以被修改,默认值是false
//configurable:true //控制属性是否可以被删除,默认是false
//当有人读取person的age属性时,get函数(getter)会被调用,返回值是age的值
get(){
console.log('读取age属性了')
return number //这样把age和number联动起来,当number变化,age也会变化
},
//当有人修改person的age属性时,set函数(setter)会被调用,且收到修改的具体值
set(value){
console.log(`有人修改了age属性,且值是${value}`)
number = value //修改了number
}
})
- 数据代理
let obj1 = {x:100}
let obj2 = {y:200}
Object.defineProperty(obj2,'x',{
get(){
return obj1.x
},
set(value){
obj1.x = value
}
})
- 事件中用$event来占位
@click='clickBtn($event,123)'
- 事件修饰符
@click.prevent='clickBtn' //阻止默认行为
@click.stop='clickBtn' //阻止冒泡
@click.once //事件只触发一次
@click.capture //使用时间的捕获阶段
@click.self //只有event.target是当前操作的元素时才会触发事件
@click.passive //事件的默认行为立即执行,无需等待事件回调执行完毕
- 键盘事件
@keyup.enter //按回车触发
@keyup.delete //删除触发
@keyup.esc
@keyup.space
@keyup.tab
@keyup.up
@keyup.down
@keyup.left
@keyup.right
- 监听
new Vue({
el:'#root',
data:{},
watch:{
a:{ //需要被监听的属性
immediate:true, //初始化时让handler调用一下
deep:true //深度监听
handler(newVal,oldVal){
...
}
}
}
//简写,当不需要立刻监听和深度监听时
watch:{
a(newVal,oldVal){
...
}
}
})
在外部调用监听
const vm = new Vue({
el:'#root',
data:{},
})
vm.$watch('a',{
...
}
- computed和watch的区别
- computed能完成的,watch都可以完成
- watch能完成的,computed不一定能完成,例如异步操作
- 绑定class样式
//字符串写法,适用于:样式的类名不确定,需要动态指定
<div :class='mood' @click='changeMood' ></div>
new Vue({
...
data:{
mood:'a'
},
methods:{
changeMood(){
this.mood = 'b'
}
}
})
//数组写法,适用于:要绑定的样式个数不确定,名字也不确定
<div :class='classArr' ></div>
new Vue({
...
data:{
classArr:['a','b','c']
},
})
//对象写法,适用于:要绑定的样式个数确定,名字确定,但要动态决定用不用
<div :class='classObj' ></div>
new Vue({
...
data:{
classObj:{
a:false, //false时不生效
b:true //true时该class生效
}
},
})
- 绑定style样式
<div :style='{fontSize: fsize+'px'}' ></div>
<div :style='styleObj' ></div>
new Vue({
...
data:{
fsize:40,
styleObj:{
fontSize:'40px'
}
}
},
})
- 条件渲染
v-if //组件会销毁
v-else
v-else-if //上面三个中间不允许被打断
v-show //组件不会销毁
<template v-if='n===1'> //template不会影响结构,但是只能和v-if使用,不能和v-show使用
<h2>1</h2>
<h2>2</h2>
<h2>3</h2>
</template>
- 遍历
//遍历数组
<li v-for='(p,index) in presons' :key='p.id' >
{{p.name}}
</li>
//遍历对象
<li v-for='(value,key) in car' :key='key' > //参数为键值对
{{key}}:{{value}}
</li>
//遍历字符串
<li v-for='(char,index) in str' :key='index' >
{{char}}-{{index}}
</li>
new Vue({
el:'#root',
data:{
persons:[{
name:'genji'
},
{
name:'Mcree'
}
],
car:{
name'audi',
price: 70
},
str:'12423432'
},
})
- Vue监视数据原理
Vue会监视data中所有层次的数据
如何监测对象中的数据?
通过setter实现监视,且要在new Vue时就传入要监测的数据
- 对象中后追加的属性,Vue默认不做响应式处理
- 如需给后添加的属性做响应式,要用如下API
Vue.set(target,propertyName/index,value)或者
vm.$set(target,propertyName/index,value)
如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
- 调用原生对应的方法对数组进行更新
- 重新解析模板,进而更新页面
在Vue修改数组中的某个元素一定要用如下方法:
使用这些API:
1、push(), pop(),shift(),unshift(),splice(),sort(),reverse()
2、Vue.set() 或 vm.$set()
注意:Vue.set()和vm.$set()不能给vm或vm的根数据对象(_data)添加属性!!!
- 数据劫持
其实就是
- 改变数据
- 重新解析模板,进而更新页面
- 过滤器
注册过滤器
Vue.filter(name,callback) 或者
new Vue({
filter:{}
})
使用过滤器 {{xxx | 过滤器名}} 或 v-bind:属性 = 'xxx | 过滤器名'
过滤器可以接收额外参数,多个过滤器也可以串联
v-cloak指令(没有值)
本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性 使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题
<style>
[v-cloak]{
display:none
}
</style>
<div v-cloak >{{name}}</div>
v-once指令(没有值)
- v-once所在节点在初次动态渲染后,就视为静态内容了
- 以后的数据改变,不会引起v-once所在节点的数据更新
v-pre指令(没有值)
- 跳过其所在节点的编译过程
- 可利用它跳过:没有使用指令语法,没有使用插值语法的节点,会加快编译
自定义指令
- 局部指令
//配置对象的属性和回调函数中的参数为 element,binding
new Vue({
directives:{指令名,配置对象}
})
new Vue({
directives{指令名:回调函数)
})
- 全局指令
Vue.directive(指令名,配置对象)
Vue.directive(指令名,回调函数)
- 配置对象中常用的3个回调
- bind 指令与元素成功绑定调用
- inserted 指令所在元素被插入页面时调用
- updated 指令所在模板结构被重新解析时调用
指令定义时不加v-,使用时加v- 指令名称使用kebab-case命名方式,不要用小驼峰
生命周期
- beforeCreate
- created
- beforeMount
- mounted
- beforeUpdate
- updated
- beforeDestroy
- destroyed
组件
//全局注册组件:
Vue.component('header',header)
//局部组件:
options不写el, 并且data得为函数
Vue.extend(options)
Vue文件
<template>
...
</template>
<script>
//简略写法
export default{
...
}
//详细写法
const name = Vue.extend({
....
})
export default name
</script>
<style>
...
</style>
Ref属性
<template>
<div>
<h1 ref='title'></h1>
<button @click='showDom'></button>
</div>
</template>
<script>
export default{
name:'',
methods:{
showDom(){
console.log(this.$refs.title)
}
}
}
</script>
props配置
- props只读,不接受修改 子组件:
<template>
<div>
名字:{{name}} 年龄:{{age+1}}
</div>
</template>
<script>
export defualt{
props:['name','age'] //简单声明接收
props:{ //接受的同时对数据进行类型限制
name:String,
age:Number
}
props:{ //最完整,包括必须和默认值
name:{
type:String,
required:true
},
age:{
type:Number,
default:18
}
}
}
</script>
父组件
<template>
<div>
<child name='genji' :age='18' /> //:age动态绑定,子组件接收时18不为字符串,可以用作{{age+1}}
</div>
</template>
mixin
在mixin.js文件中暴露方法,然后在组件中引用
- 局部混合
<script>
import {mixin} from '..'
export default{
mixins:[mixin]
}
</script>
- 全局混合 在main.js中引入,混合,之后在vm和每个vuecomponent中都有该混合中的数据,方法等
//main.js
import {mixin} form '...'
Vue.mixin(mixin)
插件
- 用于增强Vue,包含install方法的一个对象,install的第一个参数是Vue构造函数,第二个以后的参数是插件使用者传递的数据
//plugins.js
export default{
intall(Vue){
//全局过滤器
Vue.filter(){}
//全局指令
Vue.directive()
//全局混入
Vue.mixin()
//添加实例方法
Vue.prototype.$myMethod = function(){}
}
}
//使用插件:在main.js中导入,
Vue.use(...)
scoped
在样式style中加scoped
<style scoped lang='less'> // 需要安装less-loader
...
</style>
- 如果webpack版本是4,less-loader需要安装7版本
npm i less0loader@7
父子组件事件
- 父组件
<template>
<div>
//利用props传递
<Child :customeEvent='customeEvent' />
//利用绑定自定义事件
<Child @customeEvent='customeEvent' />
//利用ref定位元素,$on绑定自定义事件
<Child ref='child' />
</div>
</template>
<script>
export default{
methods:{
customeEvent(){...}
},
mounted:{
this.$refs.customeEvent.$on('自定义事件名称',this.cusromeEvent)
}
}
</script>
- 子组件
<template>
<div>
<button @click='clickbtn'></button>
</div>
</template>
<script>
export default{
//利用props传递
props:['customeEvent']
//利用绑定自定义事件
methods:{
clickbtn(){
this.$emit('customeEvent',参数...)
}
//解绑自定义事件
unbind(){
this.$off('customeEvent') //解绑单个
this.$off([...]) //解绑多个
this.$off() //解绑全部
}
}
}
</script>
- 组件添加原生DOM事件
//父组件
<Child @click.native='clickbtn' />
- 自定义事件只触发一次 使用once修饰符或者$once方法
全局事件总线
- 安装全局事件总线
//main.js
nwe Vue({
...
beforeCreate(){
Vue.prototype.$bus = this
}
})
- 使用事件总线
//1、A组件 接收数据
...
methods(){
demo(){...}
},
...
mounted(){
this.$bus.$on('xxx',this.demo)
},
beforeDestroy(){ //事件总线最好在组件销毁时解绑
this.$bus.$off('xxx')
}
//2、B组件 提供数据
...
methods(){
...(){
this.$bus.$emit('xxx',参数)
}
}
...
消息订阅与发布
- 可利用pubsub-js这个库,这个库适用于各个框架,例如React
npm install pubsub-js --save
import pubsub from 'pubsub-js'
//A组件发布订阅,接收数据
<script>
export default{
...
mounted(){
//subscribe函数第一个参数是订阅的消息名,第二个是回调函数(第一个参数是消息名,即hello,第二个是接收的数据)
this.pubId = pubsub.subscribe('hello',(msgName,data)=>{
console.log('接收到了消息,消息是',data)
})
},
beforeDestroy(){
pubsub.unsubscribe(this.pubId)
}
}
</script>
//B组件传递数据
...
methods:{
sendData(){
pubsub.publish('hello',12345)
}
}
...
nextTick
- 在下一次模板更新后,执行nextTick中的回调函数
...
this.nextTick(function(){
this...
})
过渡和动画
<template>
<div>
//Vue中,把需要动画的标签用transition包起来,如果没有name,会自动追加v-enter/leave-active,
<transition name='hello' appear> //有name,会追加name-enter/leave-active, appear为真时,初次渲染也会有动画
<h1 v-show='...' >hello world</h1>
</transition>
</div>
</template>
//动画实现
<style>
.hello-enter-active{
animation:donghua 1s linear;
}
.hello-leave-active{
animation:donghua 1s linear reverse;
}
@keyframes donghua {
from{
transform:translateX(-100%)
}
to{
transform:translateX(0px)
}
}
</style>
//过渡实现
<style>
//进入的起点,离开的终点
.hello-enter,.hello-leave-to{
transform:translateX(-100%)
}
.hello-enter-active,.hello-leave-active{
transition:0.5s linear //也可写在h1,即需要添加动画的元素style上
}
//进入的终点,离开的起点
.hello-enter-to,.hello-leave{
transform:translateX(0)
}
</style>
- 如果是多个组件使用共同的动画,用transtion-group包裹,并给每个标签唯一的key值
- 推荐使用第三方库,举例:animate.css,不需要写style样式
<transition
appear
name='animate_animated animate_bounce'
enter-active-class='...' //官方文档直接复制
leave-active-class='...'
>
</transtion>
...
import 'animate.css'
代理
- 在vue.config.js中添加代理服务器
module.exports={
...
//方式一
devServer:{
proxy:'后端服务器地址'
}
//方式二:
devServer:{
proxy:{
'/api':{
target:'后端地址',
changeOrigin:true, //用于控制请求头中的host值
ws:true, //用于支持websocket
pathRewrite:{
'^/api':'' //改变请求的地址,去掉/api
}
}
}
}
}
插槽
- 默认插槽
//子组件
...
<div>
<slot>默认内容,当没有传递结构时展示</slot>
</div>
//父组件
...
<Child> 传入插槽的结构 </Child>
- 具名插槽
//子组件
...
<div>
<slot name='content'>默认内容,当没有传递结构时展示</slot>
</div>
//父组件
...
<Child>
<div slot='content' >...</div> 或者
<template v-slot:footer></template> v-slot仅在template上可用,后面用:且不加''
</Child>
- 作用域插槽 数据在子组件中,父组件要使用子组件中数据。作用域插槽也可使用name
//子组件
...
<div>
<slot x:'hello'>默认内容,当没有传递结构时展示</slot>
</div>
//父组件
...
<Child>
<template scope='data'或者scope='{x}'或者slot-scope='{x}'> //必须用template标签
<h1>{{data.x}}或者{{x}}</h1>
</template>
</Child>
VueX
Vue2中,要用Vuex的3版本;Vue3中,要用Vuex的4版本
- 下载
npm install vuex@3 --save
- 引入
//main.js
import store from './store'
new Vue({
...
store
...
})
- 创建Vuex,创建store文件夹,创建index.js,使用Vuex
// /src/store/index.js
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
//用于响应组件中的动作
const actions = {}
//用于操作数据(state)
const mutations = {}
//用于存储数据
const state = {}
//用于将state中的数据进行加工
const getters = {
bigSum(state){
return state.xxx*10
}
}
export default new Vuex.Store({
actions,
mutations,
state,
getters
})
Vuex使用
//xxx.vue
...
<div>总数:{{$store.state.sum}}</div>
<button @click='increase'>加一</button>
...
increase(){
this.$store.dispatch('increase',1)
}
...
// store/index.js
const actions = {
increase(context,value){
context.commit('INCREASE',value)
//可以继续dispatch别的actions,
//context.dispatch('..',value)
}
}
const mutations = {
INCREASE(state,value){
state.sum += value
}
}
const state = {
sum:0
}
export default new Vuex.Store({
actions,
mutations,
state
})
mapState,mapGetters使用
- 从state或getters中读取数据,不必一个个写 this.$store.state.xx
// xxx.vue
...
import {mapState,mapGetters} from 'vuex'
...
computed:{
//对象写法,
...mapState({sum:'sum',school:'school'})
//数组写法
...mapState(['sum','school'])
}
mapMutations,mapActions
// xxx.vue
<button @click='increment'></button>
//使用mapMutations时,需要传参
<button @click'increment(n)'></button>
...
import {mapState,mapGetters} from 'vuex'
...
methods:{
//increment(){
// this.$store.commit('JIA',this.n)
//}
//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations,需要在函数事件中进行传参
...mapMutations({increment:'JIA'})
//数组方法
...mapMutations(['JIA'])
//decrement(){
// this.$store.dispatch('jian',this.n)
//}
...mapActions({decrement:'jian'})
...mapActions(['jian'])
}
vuex模块化
// store/index.js
...
const a = {
namespaced:true, //命名空间,为true后分类名才能被mapState等识别
actions:{...},
mutations:{...},
state:{...},
getters:{...},
}
const b = {
namespaced:true,
actions:{...},
mutations:{...},
state:{...},
getters:{...},
}
export default new Vuex.Store({
modules:{
a,
b
}
})
// xxx.vue
...
import {mapState,mapGetters} from 'vuex'
...
computed:{
//获取b命名空间中的state中的sum和school
...mapState('b',['sum','school'])
},
methods:{
add(){
//a命名空间下的mutations中的ADD
this.$store.commit('a/ADD',values)
}
}
...
- 如果用mapState等写法,都是第一个参数为命名空间,第二个参数为数组或对象
- 如果用$store的方法读取或者调用
state:$store.state.命名空间.xxx
commit,dispatch,getters:
$store.getters['a/...']
$store.commit('a/...',values)
vue-router
3版本只能在Vue2中使用,4版本只能在Vue3中使用
npm i vue-router@3 --save
// main.js
import VueRouter from 'vue-router'
import router from './router'
Vue.use(VueRouter)
new Vue({
...
router
})
// router/index.js
import VueRouter from 'vue-router'
export default new VueRouter({
routes:[
{
path:'/a',
component:'../.....'
}
]
})
// xxx.vue
...
//默认push模式,添加 :replace='true'或replace,切换为replace模式,替换当前目录
<router-link active-class='active' to='/a' >a</router-link>
//需要展示路由页面的地方
<router-view></router-view>
- 整个应用只有一个router,可以通过$router获取到
嵌套路由
routes:[
{
path:'/a',
component:'',
children:[
{
path:'b', //不要加 /
component:''
}
]
}
]
路由传参
- query传参
//query传参,字符串写法
:to='`/.../..?key=${value}`'
//获取query传参
this.$route.query
// query传参,对象写法
:to='{
path:'/../..',
query:{
id:..,
name:..
}
}'
- 命名路由,可以简化路由的跳转,在对象写法中使用
{
name:'',
path:'/a',
component:'../.....'
}
:to='{
name:'...',
query:{
id:..,
name:..
}
}'
- param传参
// router/index.js
...
{
name:'',
path:'/.../../:id/:name', //占位
component:''
}
// xxx.vue
:to='/../../6/genji'
:to='{
name:'', //params传参,用对象写法,此处不能用path,只能用name
params:{
...
}
}'
路由的props配置
// router/index.js
...
{
name:'',
path:'../..',
component:'',
//第一种写法,值为对象,该对象中的所有k-v都会以props的形式传给该组件
//props:{a:1,b:2}
//第二种写法,值为布尔值,为真,就会吧该路由组件收到的所有params参数,以props的形式传给该组件
//props:true
//第三种写法,值为函数,以props的形式传给该组件
props($route){
return {a:$route.query.a,b:$route.query.b}
}
//解构,简化
props({query}){
return {a:query.a,b:query.b}
}
}
// xx.vue
...
props:['a','b']
编程式导航
不借助router-link进行路由跳转
this.$router.push(...)
缓存路由组件
让不展示的路由组件保持挂载,不被销毁
<keep-alive include='组件名'>
//需要缓存的路由组件
<router-view></router-view>
</keep-alive>
如果缓存多个组件,写作 :include='['组件名','组件名']'
新的生命周期钩子 activated激活,deactivated失活
路由组件所独有的钩子,用于捕获路由组件的激活状态
- activated 路由组件被激活时触发(被展示的时候,不是挂载的时候,所以缓存的路由组件特殊需求用这个)
- deactivated路由组件失活时触发(不展示的时候,不是销毁的时候)
路由守卫
- 全局前置路由守卫
// router/index.js
...
const router = new VueRouter({
...
})
//全局前置路由守卫:初始化的时候被调用,每次路由切换之前被调用
router.beforeEach((to,from,next)=>{
if(...){
next()
}
})
export default router
- 可以在meta(路由元信息)中配置属性,来统一进行鉴权,不需要一个个判断
const router = new VueRouter({
routes:[{
name:'..',
path:'..',
component:'..',
meta:{isAuth:true}
}]
})
router.beforeEach((to,from,next)=>{
if(to.meta.isAuth){
next()
}
})
- 全局后置路由守卫
router.afterEach((to,from)=>{})
- 独享路由守卫
const router = new VueRouter({
routes:[{
name:'..',
path:'..',
component:'..',
meta:{isAuth:true},
beforeEnter:(to,from,next)=>{
...
}
}]
})
- 组件内路由组件
// xxx.vue
...
export default{
...
//通过路由规则,进入该组件时被调用
beforeRouteEnter(to,from,next){
...
},
//通过路由规则,离开该组件时被调用
beforeRouteLeave(to,from,next){
...
}
}