总结Vue需要重点掌握的知识
Vue响应式原理
当页面数据发生变化时,会对页面进行重新渲染,这就是Vue响应式。
这个过程中 Vue做了这些步骤:
- 数据劫持 / 数据代理 (侦测数据变化)
- 依赖收集(收集视图依赖的数据)
- 发布订阅模式(数据发生变化时,自动通知需要更新的视图部分,进行更新)
那么如何侦测数据变化呢?
两种方法:
- 使用Object.defineProperty
- 使用proxy
Object.defineProperty
当一个普通的Js对象传入Vue实例中作为data的选项时,Vue会先遍历此对象的所有属性(property),并使用Object.defineProperty 把这些属性全部转为 getter/setter。
这些getter/setter 对于用户是不可见的,但是在内部可以让Vue追踪一来,在属性被访问和改变时进行通知变更。getter时收集依赖,setter时将收集的依赖项触发,从而更新数据。不支持数组的监听
Proxy
Proxy 的代理是针对整个对象的,而不是对象的某个属性,因此不同于 Object.defineProperty 的必须遍历对象每个属性,Proxy 只需要做一层代理就可以监听同级结构下的所有属性变化,当然对于深层结构,递归还是需要进行的。此外Proxy支持代理数组的变化。
Vue组件通信
以下摘自vue组件通信全面总结
及
vue中组件通信的八种方式
场景:
- 父->子组件间的数据传递
- 子->父组件间的数据传递
- 兄弟组件间的数据传递
- 组件深层嵌套,祖先组件与子组件间的数据传递
父->子组件间的数据传递
使用props传递数据
//父组件,传递数据
<editor :inputIndex="data" :inputName="王文健"></editor>
//子组件,接受数据,定义传递数据的类型type与默认值default
props: {
inputIndex: {
type: Object,
default: function(){
return {}
}
},
inputName: {
type: String,
default: ''
},
}
子->父组件间的数据传递
- 使用$emit传递数据, $on接收数据
// 父组件监听子组件定义的事件
<editor :inputIndex="index" @editorEmit='editorEmit'></editor>
// 子组件需要返回数据时执行,并可以传递数据
this.$emit('editorEmit', data)
- 使用$ children/$ parent
//父组件
export default {
name: 'HelloWorld',
components: { ComA },
data() {
return {
msg: 'Welcome'
}
},
methods: {
changeA() {
// 获取到子组件A
this.$children[0].messageA = 'this is new value'
}
}
//子组件
export default {
data() {
return {
messageA: 'this is old'
}
},
computed:{
parentVal(){
return this.$parent.msg;
}
}
}
$ children 的值是数组,而$ parent是个对象
- provide和inject
父组件中通过provide来提供变量, 然后再子组件中通过inject来注入变量。
// 父组件
import comB from '../components/test/comB.vue'
export default {
name: "A",
provide: {
for: "demo"
},
components:{
comB
}
}
// 子组件
import comC from '../components/test/comC.vue'
export default {
name: "B",
inject: ['for'],
data() {
return {
demo: this.for
}
},
components: {
comC
}
}
孙组件也可以获取父组件的for值
- ref/refs
// 父组件
<template>
<component-a ref="comA"></component-a>
</template>
<script>
export default {
mounted () {
const comA = this.$refs.comA;
console.log(comA.name); // Vue.js
comA.sayHello(); // hello
}
}
</script>
// 子组件
export default {
data () {
return {
name: 'Vue.js'
}
},
methods: {
sayHello () {
console.log('hello')
}
}
}
兄弟组件间的数据传递
1.路由传参
// router index.js 动态路由
{
path:'/params/:Id',
component:Params,
name:Params
}
// 跳转路由
<router-link :to="/params/12">跳转路由</router-link>
// 在跳转后的组件中用$route.params.id去获取到这个id参数为12,但只适合传递较小的数据。
- BUS通信
- 新建bus.js文件
import Vue from 'vue';
export default new Vue();
- 在需要使用的两个组件内部引入 bus.js
import Bus from '../utils/bus'
// 兄组件
methods:{
// 点击按钮发送Bus消息
add(){
// 发送 myMsg 主题 的Bus消息
Bus.$emit('myMsg', "你好啊");
}
}
// 弟组件
mounted() {
Bus.$on('myMsg', myMsg => {
console.log("dome02组件收到bus消息:",myMsg);
this.msg = myMsg
});
}
组件深层嵌套,祖先组件与子组件间的数据传递
- provide和inject (已说明)
- $attrs 和 $listeners
在子组件中绑定 $attrs 和 $listeners 可以将父组件传递给子组件的值全部传递给子组件的子组件(孙组件)
//father
<template>
<div id="father">
<child :temp="tempdata" @tempFn="fatherFn" prop='$attrs不会传递child组件中定义的props
值'>
</child>
</div>
</template>
<script>
import Child from './child'
export default {
component: { Child },
data() {
tempdata: 'i am father'
},
methods: {
fatherFn: function() {
console.log('father function!');
}
}
}
</script>
// son
<template>
<div id="child">
<son v-bind="$attrs" v-on="$listener"></son>
</div>
</template>
<script>
import Son from './son'
export default {
component: {Son},
props: { 'prop' },
data() {
return {}
},
mounted() {
// 结果显示为$attrs.temp,不包含prop
console.log(this.$attrs)
this.$emit('tempFn')
},
methods: {}
}
</script>
//grandchild
<template>
<div id="son">
{{ $attrs.temp }}
</div>
</template>
<script>
export default {
prop: {},
data() {
return {}
},
mounted() {
this.$emit('tempFn')
},
methods: {}
}
</script>
虚拟dom
虚拟dom是相对于真实dom的概念,操作dom树是一件非常麻烦的是,往往是dom标签和js逻辑同事写在js文件中,容易造成代码耦合性高,难以维护。同时浏览器渲染dom非常消耗性能,尽量减少dom操作是前端性能优化的一种方式。 虚拟dom将dom的对比层放在了js层,通过比对不同来选择渲染新的dom节点,从而提高渲染效率。 虚拟dom的原理流程概括为3点: 1. 用JavaScript模拟DOM树,并渲染这个DOM树 2. 比较新老DOM树,得到比较的差异对象 3. 把差异对象应用到渲染的DOM树。 真实的dom节点:
虚拟dom节点:
vue和react异同
部分摘自 [覆盖vue3.0的最全vue知识点](https://www.bilibili.com/read/cv6273712/) 同: 1. 使用 虚拟dom 2. 提供响应式和组件化的视图组件 3. 都可以使用props传递给子组件数据 4. 将注意力集中保持在核心库,而将其他功能如路由和全局状态管理交给相关的库。异:
- react使用特殊的jsx语法.
- 改变初始数据 react需要使用setData()
Vue3.0和2.0的区别
优势: 更快、更小、更易维护、更易于原生、让开发者更轻松;- virtual DOM 完全重写,mounting & patching 提速 100%;
- 更多编译时 (compile-time)提醒以减少 runtime 开销;
- 基于 Proxy 观察者机制以满足全语言覆盖以及更好的性能;
- 放弃 Object.defineProperty ,使用更快的原生 Proxy;
- 组件实例初始化速度提高 100%;
- 提速一倍/内存使用降低一半;
- 加入了 TypeScript 以及 PWA 的支持
- 默认进行懒观察 ,2.x版本中会一开始给数据创建观察者,新版本中只会对 用于渲染初始可见部分的数据 进行创建观察者
- 默认项目目录结构也发生了变化: src文件新增views文件夹,用于区分视图组件和公共组件;移除了static文件夹,新增public文件夹,index.html移动到public文件夹中。
diff算法
数据发生变化时,vue会先根据真实的dom生成一颗虚拟dom树当虚拟dom中某个节点的数据改变时,会生成一个新的vnode,将新的vnode和旧的vnode进行对比,将旧vnode中发生变化的部分更新为新的,从而实现更新节点。原理:
- 先进行同级比较,再去比较子节点
- 先去判断一方有子节点一方没有子节点的情况
- 比较都有子节点的情况
- 递归比较子节点
使用patch方法比较节点,是同一个节点时调用patchVnode方法,比较虚拟节点的vnode和旧的vnode是否指向同一个对象,是同一个对象时直接return ,复用旧的vnode,如果不是,则进行对比,使用updateChildren比较子节点进行更新。
Proxy监听数据代理对象
Vuex
Vuex是vue的状态管理模式,核心是store,相当于仓库,用来储存状态。 包括的模块:State:
定义应用状态的数据结构,可以在这里设置默认的初始状态
Getter:
允许组件从store中获取数据。
Mutation:
用于更改store状态的方法(同步)
在组件中使用$store.commit(’’,params)
Action:
用于提交mutation,可以包含异步操作
在组件中使用是$store.dispath(’’)
Modules:
store的子模块,为了开发大型项目,方便状态管理而使用的
Vue-router导航守卫
路由跳转过程中的钩子函数 全局守卫: beforeEach: 用于登录验证 router.beforeEach((to,from,next)=>{
console.log(to); //即将要进入的目标路由对象
console.log(from); //当前导航即将要离开的路由对象;
next(); //调用该方法后,才能进入下一个钩子函数(afterEach)。
//next()//直接进to 所指路由
//next(false) //中断当前路由
//next('route') //跳转指定路由
//next('error') //跳转错误路由
})
- 完整的导航解析流程导航被触发。
- 在失活的组件里调用 beforeRouteLeave 守卫。
- 调用全局的 beforeEach 守卫。
- 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
- 在路由配置里调用 beforeEnter。
- 解析异步路由组件。
- 在被激活的组件里调用 beforeRouteEnter。
- 调用全局的 beforeResolve 守卫 (2.5+)。
- 导航被确认。
- 调用全局的 afterEach 钩子。
- 触发 DOM 更新。
- 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。
Vue性能优化
编码阶段:
- 减少data中的数据
- v-if和v-for不能连用
- 使用keep-alive缓存组件
- v-if代替v-show
- key值保证唯一性
- 路由懒加载,异步组件
- 防抖和接了节流
- 第三方模块按需导入
- 图片懒加载
打包优化:
- 压缩代码
- cdn加载第三方模块
用户体验:
- 骨架屏
- 使用缓存
- seo搜索优化
SPA单页面加载
优点:
- 用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染;
- 对服务器压力小
- 前后端职责分明,架构清晰
缺点:
- 初次加载耗时多:
- 前进后退需要使用路由管理;
- seo难度大