MVVM模型
1.M: 模型(Model) : data中的数据
2.V: 视图(view) : 模板代码
3.VM: 视图模型(ViewModel): Vue实例
数据代理:
1.Vue中的数据代理:
通过vm对象来代理data对象中属性的操作(读/写)
2.Vue中数据代理的好处:
更加方便的操作data中的数据
3.基本原理:
通过object.defineProperty()把data对象中所有属性添加到vm上为每一个添加到vm上的属性,都指定一个getter/setter。
在getter/setter内部去操作 (读/写) data中对应的属性。
Vue中的事件修饰符:
1.prevent: 阻止默认事件(常用) :
2.stop: 阻止事件冒泡(常用) ;
3.once: 事件只触发一次(常用)
4.capture:使用事件的捕获模式:
5.self: 只有event.target是当前操作的元素时才触发事件;6.passive:.事件的默认行为立即执行,无需等待事件回调执行完毕;
6.v-mode1的三个修饰符
(1)lazy:失去焦点再收集数据
(2)number:输入字符串转为有效的数字
(3)trim:输入首尾空格过滤
计算属性:
1.定义:要用的属性不存在,要通过已有属性计算得来。
2.原理: 底层借助了objcet.defineproperty方法提供的getter和setter。
3.get函数什么时候执行?
(1).初次读取时会执行一次。(2).当依赖的数据发生改变时会被再次调用。
4.优势: 与methods实现相比,内部有缓存机制(复用) ,效率更高,调试方便。
5.备注:
1.计算届性最终会出现在vm上,直接读取使用即可。2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生
监视属性watch:
1.当被监视的属性变化时,回调函数自动调用,进行相关操作
2.监视的属性必须存在,才能进行监视
3.监视的两种写法:
(1).new Vue时传入watch配置
(2).通过vm.$watch监视
4.深度监视:
(1).Vue中的watch默认不监测对象内部值的改变(一层)
(2).配置deep:true可以监测对象内部值改变(多层)。
备注:
(1).Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以
(2).使用watch时根据数据的具体结构,决定是否采用深度监视。
Vue监视数据的原理:
1。vue会监视data中所有层次的数据。
2。如何监测对象中的数据?
通过setter实现监视,且要在new Vue时就传入要监测的数据。
(1).对象中后追加的属性,Vue默认不做响应式处理
(2).如需给后添加的属性做响应式,请使用如下API:
Vue.set(target,propertyName/index,value) 或
vm.$set(target, propertyName/index,value )
3。如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
(1).调用原生对应的方法对数组进行更新。
(2).重新解析模板,进而更新页面。
4.在Vue修改数组中的某个元素一定要用如下方法:
1.使用这些API:push()、pop()、shift()、unshift()、splice()、sort()reverse()
2.Vue.set() 或 vm.$set()
特别注意: Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性
computed和watch之间的区别:
1.computed能完成的功能,watch都可以完成。
2.watch能完成的功能,computed不一定能完成,例如: watch可以进行异步操作。两个重要的小原则:
1.所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。
2.所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等》,最好写成箭头函数.
这样this的指向才是vm 或 组件实例对象。
条件渲染:
1.v-if
适用于:切换频率较低的场景。特点:不展示的DOM元素直接被移除。注意: v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”
2.v-show
写法: v-show="表达式"适用于:切换频率较高的场景。特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉
3.备注: 使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到。
Vue中key的作用
1。虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据[新数据]生成[新的虚拟DOM)随后vue进行[新虚拟DOM]与[旧虚拟DOM] 的差异比较,比较规则如下:
2.对比规则:
(1).旧虚拟DOM中找到了与新虚拟DOM相同的key:@.若虚拟DOM中内容没变,直接使用之前的真实DOM!@.若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
(2).旧虚拟DOM中未找到与新虚拟DOM相同的key创建新的真实DOM,随后渲染到到页面。
3。用index作为key可能会引发的问题:1。若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==>界面效果没问题,但效率低。
2。如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题
4。开发中如何选择key?:
1.最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值。
2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。
自定义指令
1.定义语法:
(1).局部指令:new Vue(directives:{指令名:配置对象 或 回调函数})
(2).全局指令:Vue.directive( 指令名 , 配置对象 或 **回调函数 **)
2.配置对象中常用的3个回调:
(1).bind: 指令与元素成功绑定时调用。
(2).inserted: 指令所在元素被插入页面时调用。
(3).update:指令所在模板结构被重新解析时调用
备注:
1.配置对象写法如下
Vue.directive( 'my-directive' ,{
// element为DOM元素,binding为元素的属性
bind:(element,binding)=>{
element.innerText = binding.value
...
//一些逻辑
}
// element为DOM元素,binding为元素的属性
inserted:(element,binding)=>{
element.innerText = binding.value
...
//一些逻辑
}
// element为DOM元素,binding为元素的属性
update:(element,binding)=>{
element.innerText = binding.value
...
//一些逻辑
}
} )
2.指令定义时不加v-,但使用时要加v-;
3.指令名如果是多个单词,要使用kebab-case命名方式,不要用camelcase命名。
生命周期
常用的生命周期钩子:
1.mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等[初始化操作]2.beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等[收尾工作]。
关于销毁Vue实例
1.销毁后借助Vue开发者工具看不到任何信息。
2.销毁后自定义事件会失效,但原生DOM事件依然有效。
3.一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了.
Vue的组件
1.定义组件
使用Vue,extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别:
区别如下:
1.不写el, 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器。
2.data必须写成函数,避免组件被复用时,数据存在引用关系。
2.注册组件:
1.局部注册:new Vue的时候传入components选项
2.全局注册: Vue.component("组件名,组件)、编写组件标签:
3.一个重要的关系:VueComponent .prototype._proto === Vue .prototype
Vue的过滤器
对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)
语法:
1.注册过滤器: Vue.filter(name,callback) 或 new Vue{filters: {}}
2.使用过滤器: {[ xxx过滤器名]] 或 v-bind:属性 =“xxx过滤器名"
备注:
1.过滤器也可以接收额外参数、多个过滤器也可以串联
2.并没有改变原本的数据,是产生新的对应的数据
Vue的webpack相关配置
vue.config.js配置文件
使用vue inspect > output.js可以查看到Vue脚手架的默认配置。
使用vue.config.js可以对脚手架进行个性化定制,具体配置看vue-cli的api。
属性
1.ref属性
1.被用来给元素或子组件注册引用信息 (id的替代者)
2.应用在htm1标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象 (vc)
3.使用方式:
获取: this.$refs.xxx
2.props属性
(1).传递数据:
<Demo name="xxx"/>
(2).接收数据:
第一种方式(只接收) :
props:[ ' name' ]
第二种方式(限制类型) :
props:{
name:String
}
第三种方式(限制类型、限制必要性、指定默认值) :
props:(
name:{
type:String,//类型
required;true,//必要性
default;'老王’//默认值
}
}
备注: props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,
若业务需求确实需要修改,复制props的内容到data中一份,然后去修改data中
的数据。
例如
// vc中部分代码
{
data(){
return{
myData: xxProps
}
},
props: ['xxProps']
}
mixin(混入)
多个组件共用的配置提取成一个混入对象
使用方式:
定义混合,例如:
{
data()[....}
methods:[....]
}
使用混入,例如:
(1).全局混入: Vue.mixin(xxx)
(2).局部混入: mixins:[‘xxx’]
插件
功能:用于增强Vue
本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数
据。
一般写在plugins.js中,与main.js同级
// plugins中
export defalut {
install(Vue,options){
// 1。添加全局过滤器
Vue.filter(....)
// 2。添加全局指令
Vue.directive(....)
// 3.全局混入
Vue.mixin(....)
// 4.添加实例方法
Vue.prototype.$myMethod = function () [...]
}
}
// main.js中
import plugin from './plugin';
Vue.use(plugin)
不同版本的Vue的区别
1.vue.js与vue.runtime.xxx.js的区别:
(1).vue.js是完整版的Vue,包含: 核心功能+模板解析器,模板解析器占到三分之一左右的资源。
(2).vue.runtime.xxx.is是运行版的Vue,只包含: 核心功能:没有模板解析器。
2.因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用
render函数接收到的createElement函数去指定具体内容。
render函数:
render: h => h(App) 的意思为将App组件放入容器中
scoped样式
作用:让样式在局部生效,防止冲突。
用法:
<style scoped>
.xxx{
}
</style>
组件的自定义事件
1.一种组件间通信的方式,适用于子组件给父组件传值
2.使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。
3.绑定自定义事件:
1.第一种方式,在父组件中:
<Demo @sendMsg="sendMsg"/>
2.第二种方式,在父组件中:
<Demo ref="demo"/>......
mounted(){
this.$refs.demo.$on('sendMsg',this.sendMsg)
}
3.若想让自定义事件只能触发一次,可以使用once 修饰符,或$once 万法
4.触发自定义事件:
this.$emit('sendMsg’,数据)
5.解绑自定义事件
this.$off('sendMsg')
6.组件上也可以绑定原生DOM事件,需要使用 native 修饰符
7.直接写回调函数
this.$refsxxx.on('sendMsg',callback)
// 直接回调的话,callback需要为箭头函数,不然因为是在子组件中调用的,所以this为子组件,只有写成箭头函数才能让this指向父组件
全局事件总线 (GlobalEventBus)
1.一种组件间通信的方式,适用于任意组件间通信,类似于订阅消息。
2.注册
// vm中
new Vue({
beforeCreate(){
Vue.prototype.$bus = this
}
})
3.使用
(1).接收数据
// vc中
mounted(){
this.$bus.$on('xxxEvent', ()=>{
// ...一些逻辑
})
}
(2).提供数据
// vc中
this.$bus.$emit('xxxEvent', 参数)
4.使用完之后解绑
// vc中
beforeDestroy(){
this.$bus.$off(['xxxEvent'])
}
nextTick
1.用法
this.$nextTick(()=>{
// ...
})
2.作用: 在下一次 DOM 更新结束后执行其指定的回调
3.什么时候用: 当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行.
插槽
1.作用:当有一个公共组件需要根据传来的html结构来进行不同的展示时,让父组件可以向子组件指定位置插入html结构,适用于 父组件 ===>子组件
2.分类: 默认插槽、具名插槽、作用域插槽
2.1作用域插槽
2.1.1功能:父需要根据子的数据来确定展示内容,就用到这种类型的插槽,将子组件自身的数据传递给父组件后由父组件来决定展示的内容。
3使用方式
// Father中
<template>
<div>
<!-- 默认插槽 -->
<slot>默认内容</slot>
<!-- 具名插槽 -->
<slot name="slotA">默认内容</slot>
<!-- 具名插槽 + 作用域插槽 -->
<slot name="slotB" :childrenAttribute="childrenAttribute" >默认内容</slot>
</div>
</template>
<script>
export default {
data(){
return {
childrenAttribute: {
showButton: true,
showTitle: true,
},
}
}
};
</script>
// Children中
<template>
<Children>
<!-- 默认插槽使用者 -->
<div>Father传来的默认插槽的内容</div>
<!-- 具名插槽 -->
<template slot="slotA">slotA</template>
<!-- 具名插槽 + 作用域插槽使用者 -->
<template slot="slotB" scope="scopeData">
<button v-if="scopeData.childrenAttribute.showButton">插槽中的button</button>
<h2 v-if="scopeData.childrenAttribute.showTitle">插槽中的title</h2>
</template>
</Children>
</template>
<script>
import Children from "./Children";
export default {
components: { Children },
};
</script>
VueX
1.概念
在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理,也是一种组件
间通信的方式,且适用于任意组件间通信。
2.使用
1.创建文件:src/store/index.js
import Vuex from "vuex";
import Vue from "vue";
import module from "./module";
Vue.use(Vuex);
const state = {
count: 0,
};
const actions = {
countAdd: (context, value) => {
context.commit("COUNT_ADD", value);
},
};
const mutations = {
COUNT_ADD: (state, value) => {
state.count += value;
},
};
const getters = {
countGetter: (state) => {
return `${state.count}Getter`;
},
};
const store = new Vuex.Store({
state,
actions,
mutations,
getters,
// 模块化
modules: {
...module,
},
});
export default store;
2.在main.js中创建vm时传入store配置项
import Vue from 'vue'
import App from './App.vue'
import store from './store'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
store,
}).$mount('#app')
3.在组件中使用
<template>
<div>
state:
{{ count }}
getters:
{{ countGetter }}
<button @click="countAdd(1)">actions改变count</button>
<button @click="COUNT_ADD(1)">mutations改变count</button>
<div>模块化:</div>
moduleA中: state:
{{ moduleACount }}
getters:
{{ moduleACountGetter }}
<button @click="moduleACountAdd(1)">actions改变count</button>
<button @click="MODULEA_COUNT_ADD(1)">mutations改变count</button>
</div>
</template>
<script>
import { mapState, mapActions, mapMutations, mapGetters } from "vuex";
export default {
computed: {
// 数组或对象
...mapState(["count"]),
...mapGetters(["countGetter"]),
...mapState("moduleA", ["moduleACount"]),
...mapGetters("moduleA", { moduleACountGetter: "moduleACountGetter" }),
},
methods: {
...mapActions(["countAdd"]),
...mapActions("moduleA", { moduleACountAdd: "moduleACountAdd" }),
...mapMutations(["COUNT_ADD"]),
...mapMutations("moduleA", { MODULEA_COUNT_ADD: "MODULEA_COUNT_ADD" }),
},
};
</script>
<style lang="less" scoped>
</style>
组件中读取vuex中的数据:
$store.state.sum
组件中修改vuex中的数据: store dispatch('action中的方法名数据)或 storecommit('mutations中的方法名数据),若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写dispatch,直接编写commit
4.getters的使用
1.概念:当state中的数据需要经过加工后再使用时,可以使用getters加工
2.在store.js中追加getters配置
3组件中读取数据
$store.getters.bigSum
const getters = {
countGetter: (state) => {
return `${state.count}Getter`;
},
};
5.四个map方法的使用
1mapstate方法:用于帮助我们映射state中的数据为计算属性
2.mapGetters方法:用于帮助我们映射getters 中的数据为计算属性
3.mapActions方法:用于帮助我们生成与actions 对话的方法即: 包含store.dispatch(xxx)的函数
4.mapMutations方法: 用于帮助我们生成与mutations对话的方法,即: 包含$store.commit(xxx)的函数
备注: mapActions与mapMutations使用时,若需要传递参数则需要在模板中绑定事件时传递好参数,否则参数是事件对象。
6.模块化+命名空间
1.目的:让代码更好维护,让多种数据分类更加明确。
// module.js中 //模块化
const nameSpace = 'MODULEA'
const COUNT = [`${nameSpace}Count`]
const _COUNT_ = [`${nameSpace}_COUNT_`]
const state = {
[COUNT]: 0,
};
const actions = {
[`${COUNT}Add`]: (context, value) => {
context.commit(`${_COUNT_}ADD`, value);
},
};
const mutations = {
[`${_COUNT_}ADD`]: (state, value) => {
state[COUNT] += value;
},
};
const getters = {
[`${nameSpace}Getter`]: (state) => {
return state[COUNT];
},
};
const moduleA = {
namespaced: true, //开启命名空间
state,
actions,
mutations,
getters,
};
const store = {
moduleA,
};
export default store;
export { nameSpace }
// index.js中
import Vuex from "vuex";
import Vue from "vue";
import module from "./module";
Vue.use(Vuex);
const store = new Vuex.Store({
// 模块化
modules: {
...module,
},
});
export default store;
3.开启命名空间后,组件中读取state数据
方式一:自己直接读取
this.$store.state.moduleA.moduleACount
方式二:借助mapstate读取:
…mapState(‘moduleA’, [‘moduleACount’]) 或
…mapState(‘moduleA’, { moduleACount (随意命名) : ‘moduleACount’})
4开启命名空间后,组件中读取getters数据
方式一:自己直接读取
this.$store.getters[‘moduleA/moduleACountGetter’]
方式二:借助mapGetters读取:
…mapGetters(‘moduleA’,[‘moduleACountGetter’])
5.开启命名空间后,组件中调用dispatch
方式一: 自己直接dispatch
this.$store.dispatch(‘moduleA/moduleACountAdd’, newCount)
方式二:借助mapActions:
…mapActions(‘moduleA’, { incrementOdd(随意命名) : ‘moduleACountAdd’ })
6.开启命名空间后,组件中调用commit与上方相同
路由
1.理解: 一个路由(route)就是一组映射关系(key - value),多个路由需要路由器 (router) 进行管理
2.前端路由: key是路径,value是组件。
1.作用
1,安装vue-router,命令: npm i vue-router
2.应用插件: Vue.use(VueRouter)
3.编写router配置项
注意点:
1.通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
2.每个组件都有自己的$route 属性,里面存储着自己的路由信息
3.整个应用只有一个router,可以通过组件的 $router 属性获取到
4.路由携带params参数时,必须使用name的方式进行跳转
1.缓存路由组件
1.作用: 在组件切换过程中将状态保留在内存中,防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验性。
Props:
- include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
- exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
- max - 数字。最多可以缓存多少组件实例。
2.具体编码:
<keep-alive>
<router-view></router-view>
</keep-alive>
被包含在 keep-alive 中创建的组件,会多出两个生命周期的钩子: activated 与 deactivated
1.作用: 路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
1.activated 路由组件被激活时触发
2.deactivated 路由组件失活时触发
路由守卫
1.作用:对路由进行权限控制
2.分类:全局守卫、独享守卫、组件内守卫
3.全局守卫
// router代码
import VueRouter from 'vue-router';
import Vue from 'vue';
import pageA from '../pages/pageA'
import pageB from '../pages/pageB'
Vue.use(VueRouter)
const router = new VueRouter({
routes: [
{
path: '/pageA',
component: pageA,
name: 'pageA',
// 独享守卫
beforeEnter(to,from,next){
console.log('我是独享守卫');
next()
}
},
{
path: '/pageB',
component: pageB,
name: 'pageB',
}
]
})
router.beforeEach((to, from ,next)=>{
console.log('to, from ', to, from );
if(to.path === '/pageA'){
next();
}else{
alert('前置路由拦截!')
}
})
router.afterEach((to, from)=>{
console.log('to, from ', to, from );
if(to.path === '/pageA'){
document.title = '后置路由启动!'
}
})
export default router;
路由器的两种工作模式
1.对于一个url来说,什么是hash值? -#及其后面的内容就是hash值
2.hash值不会包含在 HTTP 请求中,即: hash值不会带给服务器
3.hash模式:
1.地址中永远带着#号,不美观
2.若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法
3.兼容性较好
4.history模式:
1.地址干净,美观。
2.兼容性和hash模式相比略差
3.应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题
Vue3
setup
1理解:Vue3.0中一个新的配置项,值为一个函数。
2.setup是所有Composition API (组合API)的基石。
3.组件中所用到的:数据、方法等等,均要配置在setup中
4.setup函数的两种返回值:
1.若返回一个对象,则对象中的属性、方法在模板中均可以直接使用。
2.若返回一个渲染函数: 则可以自定义渲染内容。 (了解)
5.注意点:
1.尽量不要与Vue2.x配置混用。
1.1.Vue2.x配置(datamethos、computed…) 中可以访问到setup中的属性、方法。
1.2.但在setup中不能访问到Vue2.x配置 (data、methos、computed…)。
1.3如果有重名,setup优先
2.setup不能是一个async函数,因为返回值不再是return的对象而是promise,模板看不到retum对象中的属性(除用异步组件的情况)
ref函数
作用: 定义一个响应式的数据
语法: const xxx = ref(initValue)
。创建一个包含响应式数据的引用对象 (reference对象,简称ref对象)
。JS中操作数据:
xxx .value
。模板中读取数据: 不需要.value,直接:
备注:
。接收的数据可以是: 基本类型、也可以是对象类型
。基本类型的数据:响应式依然是靠object.defineProperty()的get 与set 完成的
。对象类型的数据:内部借助了Vue3.0中的一个新函数 reactive 函数
reactive函数
。作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用 ref 函数)
·语法: const 代理对象 reactive(源对象)接一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象)
reactive定义的响应式数据是“深层次的”
内部基于ES6的Proxy 实现,通过代理对象操作源对象内部数据进行操作。
vue2.x的响应式
实现原理:
对象类型: 通过 object.defineProperty() 对属性的读取、修改进行拦截(数据劫持)
数组类型:通过重写更新数组的一系列方法来实现拦截。
(对数组的变更方法进行了包裹)
Object.defineProperty(data, 'count',(get () {},set () {}))
存在问题:
。新增属性、删除属性,界面不会更新
直接通过下标修改数组,界面不会自动更新
Vue3.0的响应式
实现原理:
。通过Proxy(代理): 拦截对象中任意属性的变化,包括: 属性值的读写、属性的添加、性的删除等.
。通过Reflect (反射): 对源对象的属性进行操作。
reactive对比ref
。从定义数据角度对比:
。ref用来定义:基本类型数据
。reactive用来定义: 对象 (或数组)类型数据
。备注: ref也可以用来定义对象 (或数组)类型数据,它内部会自动通过 reactive 转为代理对象。
。从原理角度对比:
。ref通过object.defineProperty()的get 与set 来实现响应式(数据劫持)
。reactive通过使用Proxy来实现响应式 (数据劫持),并通过Reflect操作源对象内部的数据
。从使用角度对比:
。ref定义的数据:操作数据需要 value,读取数据时模板中直接谈取不需要 value.
。reactive定义的数据: 操作数据与读取数据:均不需要 value
setup的两个注意点
setup执行的时机
。在beforeCreate之前执行一次,this是undefined。
setup的参数
1.props:值为对象,包含: 组件外部传递过来,且组件内部声明接收了的属性。
2.context:上下文对象
2.1attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性,相当于 this.$attrs
2.2slots: 收到的插槽内容,相当于 this.$slots。
2.3emit: 分发自定义事件的函数,相当于 this.$emit
计算属性与监视
1.computed函数
与Vue2.x中computed配置功能一致
import { computed } from "vue"
setup(){
const person = {
firstName: 'j',
lastName: 'gs',
}
let data = {
userName: "KUN",
realName: "CX",
count: 0,
};
//计算属性一简写
let fullName = computed(()=>{
return person.firstName + '-' + person.lastName
})
// 计算属性
const computedAttr = computed({
get() {
console.log("get");
return `${data.userName}-${data.realName}`;
},
set(value) {
console.log("set");
const arr = value.split("-");
data.userName = arr[0];
data.realName = arr[1];
},
});
}
watch函数
。与Vue2x中watch配置功能一致
。两个小“坑”:
。监视reactive定义的响应式数据时: oldValue无法正确获取、强制开启了深度监视 (deep配置失效)
。监视reactive定义的响应式数据中某个属性时: deep配置有效
//情况一:监视ref定义的响应式数据
watch(sum,(newValue ,oldValue)=>(
console.log("sum变化了 ,newValue,oldValue)
),{immediate:true})
//情况二,监视多个ref定义的响应式数据
watch([sum,msg],(newValue,oldValue)=>{
console.log("sum或msg变化了,newValue,oldValue)
})
/* 情况三,监视reactive定义的响应式数据
若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue! !
若watch监视的是reactive定义的响应式数据,则强制开启了深度监视
*/
watch(person,(newValue,oldValue)=>(
console.log("person变化了,newValue,oldValue)
},{immediate:true,deep:false}) //此处的deep配置不再奏效
//情况四,监视reactive定义的响应式故据中的某个属性
watch(()=>person.job,(newValue oldValue)=>{
console.log("person的job变化了 newValue,oldValue)
},{immediate:true,deep:true})
3.watchEffect函数
。watch的套路是: 既要指明监视的属性,也要指明监视的回调。
。watchEfect的套路是: 不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性.
。watchEffect有点像computed:
。但computed注重的计算出来的值 (回调函数的返回值),所以必须要写返回值
。而watchEffect更注重的是过程 (回调函数的函数体),所以不用写返回值
//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调
watchEffect(()=>{
const x1 = sum.value
const x2 = person.age
console.log('watchEffect配置的回调执行了)
})
Vue3生命周期
Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名
beforeDestroy 改名为 beforeUnmount
destroyed 改名为unmounted
Vue3.0也提供了 Composition API形式的生命周期钩子,与Vue2.x中钩子对应关系如下
- beforeCreate ===> setup()
- created =======> setup()
- beforeMount ===> onBeforeMount
- mounted =======>onMounted
- beforeUpdate ===> onBeforeUpdate
- updated =======> onUpdated
- beforeUnmount ==>onBeforeUnmount
- unmounted =====> onUnmounted
自定义hook函数
什么是hook? --本质是一个函数,把setup函数中使用的Composition API进行了封装.
类似于vue2.x中的mixin。
自定义hook的优势: 复用代码,让setup中的逻辑更清楚易懂
toRef
作用: 创建一个 ref 对象,其value值指向另一个对象中的某个属性.
语法:
const name = toRef(person ,‘name’)
应用:
要将响应式对象中的某个属性单独提供给外部使用时
扩展:
toRefs 与toRef 功能一致,但可以批量创建多个 ref对象,语法: toRefs(person)
其它 Composition API
1.shallowReactive 与 shallowRef
。shallowReactive : 只处理对象最外层属性的响应式 (浅响应式)
。shallowRef: 只处理基本数据类型的响应式,不进行对象的响应式处理
。什么时候使用?
。如果有一个对象数据,结构比较深,但变化时只是外层属性变化 ===> shallowReactive。
。如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 ===> shallowRef.
2.readonly 与 shallowReadonly
。readonly: 让一个响应式数据变为只读的 (深只读)
shallowReadonly: 让一个响应式数据变为只读的 (浅只读)
应用场景:不希望数据被修改时
3.toRaw 与 markRaw
toRaw:
。作用:将一个由 reactive 生成的响应式对象转为普通对象
。使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新
markRaw:
作用: 标记一个对象,使其永远不会再成为响应式对象
应用场景:
1.有些值不应被设置为响应式的,例如复杂的第三方类库等
2.当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能
4.customRef
作用:创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制
实现防抖效果:
<template>
<input type="text" v-model="keyword">
<h3>{{keyword}}</h3>
</template>
<script>
import { ref, customRef }from 'vue'
export default {
name: 'Demo',
setup(){
let keyword = ref('hello') //使用Vue准备好的内置ref
//自定义一个myRef
function myRef(value,delay){
let timer
//通过customRef去实现自定义
return customRef((track,trigger)=>{
return{
get(){
track() //告诉Vue这个value值是需要被”追踪”的
return value
}
},
set(newValue){
clearTimeout(timer)
timer = setTimeout(()=>{
value = newValue
trigger(); // 更新界面
})
}
}
let keyword = myRef('myKeyword', 500) // 防抖
},
provide 与 inject
作用:实现祖孙组件间通信
适用:父组件有一个 provide 选项来提供数据,子组件有一个 nject 选项来开始使用这些数据
具体写法:
1.祖组件中:
setup(){
let car = reactive({name:'奔驰', price:'40万')
provide('car', car)
}
2.孙组件中:
setup(props,context){
const car = inject('car')
return { car }
}
响应式数据的判断
isRef: 检查一个值是否为一个 ref 对象
isReactive:检查一个对象是否是由 reactive 创建的响应式代理
isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理
新的组件
1.Fragment
在Vue2中: 组件必须有一个根标签
在Vue3中: 组件可以没有根标签,内部会将多个标签包含在一个Fragment虚拟元素中
好处: 减少标签层级,减小内存占用
Teleport
什么是Teleport? Teleport 是一种能够将我们的组件html结构移动到指定位置的技术
<teleport to="移动位置">
<div v-if="isShow" class="mask">
<div class="dialog">
<h3>我是一个弹窗</h3>
<button @click="isShow = false">关闭弹窗</button>
</div>
</div>
</teleport>
Suspense
等待异步组件时渲染一些额外内容,让应用有更好的用户体验
使用:
import {defineAsyncComponent] from 'vue!
const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
<template>
<div class="app">
<h3>我是App组件</h3>
<Suspense>
<template v-slot:default>
<Child/>
</template>
<template v-slot:fallback>
<h3>加载中.....</h3>
</template>
</Suspense>
</div>
</template>
全局API的转移
Vue 2x有许多全局API和配置
例如:注册全局组件、注册全局指令等
其他改变
data选项应始终被声明为一个函数,过度类名的更改,移除keyCode作为 v-on 的修饰符,同时也不再支持config.keyCodes,移除 v-on.native 修饰符,移除过滤器 (filter)