Vue优点
数据驱动和组件化
- 数据驱动:自动计算属性和追踪依赖的模板表达
- 组件化:可复用,解耦的组件来构造页面
Vue生命周期
- beforeCreated:创建前。数据观测和初始化事件未开始。
- created:创建后。完成数据观测、属性和方法的处理。以及初始化事件。
- beforeMounted:挂载前。挂载开始前被调用,render函数被首次调用。完成编译模板,data里的数据和模板生成HTML
- mounted:挂在后。el属性被新创建出来的vm.$el代替,并挂载到实例上之后被调用。
- beforeUpdate:虚拟DOM重新渲染之前
- update:虚拟DOM重新渲染之后
- beforeDestory:实例被摧毁之前调用,实例仍然可用。
- destoryed:实例被摧毁之后调用;调用时,所有的监听器都会被移除。
Vue的指令有哪些
v-if/v-else/v-else-if/v-bind(:)/v-on(@)/v-show/v-html/v-text/v-model/v-for/v-once;
Vue常用修饰符
.prevent: 提交事件不再重载页面
.stop: 阻止单击事件冒泡
.self: 当事件发生在该元素本身而不是子元素的时候会触发
.capture: 事件侦听,事件发生的时候会调用
.once: 跟v-once作用类似,只渲染一次,第二次不会执行
watch和computed的区别
- computed:计算属性。通过其他变量来获取另一个属性,具有缓存。计算属性只有在他们依赖的属性改变的时候才会重新计算求值
- watch:监听某一属性。回调里面可以传入新旧值。
watch详解
watch:可以用来监测VUE实例上数据的变动;
- 简单运用:
watch: function(newVal,OldVal) {}; - 复杂运用:
1.immediate: 由于watch有一个特点,就是最初绑定的时候 不会去执行,只有当值发生改变的时候 才会去执行监听计算。如果我们想要在最初绑定的时候就去执行监听计算的话,就需要在watch中设置immediate属性值为true;
2.deep: 比如说我们data里面监听的是一个对象,但是我们想要监听的是object.a的值的变化,就监听不到的。由于handler只监听data里的obj的变化。这时候我们就需要这是deep属性为true。deep就是指深度观察,监听器会逐一的遍历下去,给obj的每一属性都加上监听器,但是这样是非常耗性能的。
watch: {
handler:functio(newVal,OldVal) {},
immediate:true,
}
Vue中如何获取DOM
只有在mounted阶段之后才能获取dom
- ref:ref被用来给元素或者是子组件注册引用信息,引用信息将会注册在父组件的$refs对象上。如果是在普通的DOM上用,那么指向的就是普通的元素,如果是在子组件上使用,指向的就是子组件实例;
- 选择器获取:比如docment.querySelector("#id");
组件之间的传值
- 父组件传给子组件:子组件通过props接收传递的数据
- 子组件传递给父组件:通过$emit传递参数
- 同级之间传递:eventBus,创建一个事件中心,相当于一个中转站,可以用它来传递数据和接收数据;
- vuex
- 祖传孙:通过provide
//祖
provide: {
foo: {
a: 1,
b: 2,
c: 3
}
}
//在孙组件中
inject: ["foo"],
data() {
return {
bar: this.foo.a
};
},
created() {
console.log(1);
console.log(this.bar); // => "bar"
}
Vue-router的钩子函数
- 全局导航钩子函数
- router.beforeEach(to, from, next),
- router.beforeResolve(to, from, next),
- router.afterEach(to, from ,next)
- 组件内钩子:
- beforeRouterEnter
- beforeRouterUpdate
- beforeRouterLeave
- 单独路由独享组件:
beforeEnter
完整的导航解析流程
- 导航被触发
- 在失活的组件里调用离开守卫
- 调用全局的 beforeEach 守卫
- 在重用的组件里调用 beforeRouteUpdate 守卫
- 在路由配置里调用 beforEnter
- 解析异步路由组件
- 在被激活的组件里调用 beforeRouteEnter
- 调用全局的 beforeResolve 守卫
- 导航被确认
- 调用全局的 afterEach 钩子
- 触发 DOM 更新
- 在创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数
vue组件中data为什么必须是函数
因为一个组件是可以共享的,但他们的data是私有的,所以每个组件都要return一个新的data对象,返回一个唯一的对象,不要和其他组件共用一个对象
从源码看Vue中数组的问题
由于JavaScript的限制,Vue不能做以下操作
- 利用索引直接设置一个数组项。vm.items[indexofitem] = newValue
- 修改数组的长度,vm.items.length = newLength
解决方法
在源代码中,采用数组变异思路,首先对功能进行扩展,之后进行数组劫持。
功能扩展思路:
- 创建一个继承原Array的新函数对象。
- 重新定义函数对象
- 在新定义的函数中调用原函数
// 变异方法名称
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
const arrayProto = Array.prototype
// 继承原有数组的方法
const arrayMethods = Object.create(arrayProto)
mutationMethods.forEach(method => {
// 缓存原生数组方法
const original = arrayProto[method]
arrayMethods[method] = function (...args) {
const result = original.apply(this, args)
console.log('执行响应式功能')
return result
}
共有7种变异函数。
数据劫持思路
使用原型链,将普通函数指向我们所扩展的新组对象
let arr = []
// 通过隐式原型继承arrayMethods
arr.__proto__ = arrayMethods
// 执行变异后方法
arr.push(1)
Vue 的变异数组从本质上是来说是一种装饰器模式,通过学习它的原理,我们在实际工作中可以轻松处理这类保持原有功能不变的前提下对其进行功能拓展的需求。
// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// vm.$set,Vue.set的一个别名
vm.$set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)
vm.items.splice(newLength)
在vue中有的时候是不能获取到数组的,那么vue源码是怎样去处理这些数组,并获取到的呢?
答:组件处理 vue做了拦截,重写了数组的方法,最终还是通过数据劫持获取到的。
Vue 怎么用 vm.$set() 解决对象新增属性不能响应的问题 ?
vue 无法检测到对象属性的添加或者删除。所以Vue提供了Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value) 来实现为对象添加响应式属性。
源码实现思想
如果目标是数组,直接使用扩展数组的splice方法触发响应式。
如果目标是对象,会先判读属性是否存在、对象是否是响应式,最终如果要对属性进行响应式处理,则是通过调用 defineReactive 方法进行响应式处理( defineReactive 方法就是 Vue 在初始化对象时,给对象属性采用 Object.defineProperty 动态添加 getter 和 setter 的功能所调用的方法)
父子组件生命周期调用顺序
父组件beforeMounted阶段之后进入子组件,子组件完成mounted之后继续父组件的周期。当子组件触发数据更新,先触发父组件beforeUpdate,之后触发子组件beforeUpdate,然后触发子组件updated,最后触发父组件updated。
Vuex
- state: vuex store实例的根状态对象,用于定位共享的状态
- action: 动作,执行本地操作或者异步操作(相当于state的methods)。action可以进行异步操作。store.dispatch()来执行action
- Mutations: 修改器。唯一只用于修改state。store.commit()来执行。
- getter:读取器,外部程序通过他获取变量的具体值,或者是在取值前做一些计算,可以认为是store的计算属性。
vuex提供了三种辅助函数用于获取、修改vuex:
mapState、mapGetters、mapActions
即将vuex的变量或者方法映射到vue组件this指针上。
methods: {
//下述中的 ... 是拓展运算符
// 使用 [] 是解构赋值
...mapActions([
'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`
// `mapActions` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
]),
...mapActions({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
})
}
Vue-router原理
spa实现方式分为三种
hash模式
地址栏中#符号。特点是hash虽然出现在浏览器url中,但是不会包含在HTTP请求中,对后端没有影响,不会重新加载页面
history模式
利用HTML5新增的pushState()和replaceState()实现。这两个方法应用于浏览器的历史记录栈。但是需要后端对路由进行配置,重定向到Vue打包生成的index.html的页面上寻找相应的代码,否则会报错。
window.history.pushState(state, title, url)
- state: 一个与指定网址相关的状态对象,popstate事件触发时,该对象会传入回调函数
- title:新页面的标题,大部分浏览器不支持这个
- url: 新的网址,必须是与当前页面同一个域名。浏览器显示的地址
重点是 pushstate方法不会触发页面刷新,只是会导致history对象发生变化,地址栏会有反应
window.onpopstate = function (event) {}
popstate事件会在点击后退、前进按钮(或调用history.back()、history.forward()、history.go()方法)时触发。前提是不能真的发生了页面跳转,而是在由history.pushState()或者history.replaceState()形成的历史节点中前进后退
注意:用history.pushState()或者history.replaceState()不会触发popstate事件。
在Vue中需要这样配置node:'history'
abstract模式
abstract模式是使用一个不依赖于浏览器的浏览历史虚拟管理后端。
根据平台差异可以看出,在 Weex 环境中只支持使用 abstract 模式。 不过,vue-router 自身会对环境做校验,如果发现没有浏览器的 API,vue-router 会自动强制进入 abstract 模式,所以 在使用 vue-router 时只要不写 mode 配置即可,默认会在浏览器环境中使用 hash 模式,在移动端原生环境中使用 abstract 模式。 (当然,你也可以明确指定在所有情况下都使用 abstract 模式)
注意
- hash模式不会生成404的错误
- history模式需要后端支持。指定的路径需要返回对应的HTML页面。