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 的页面上寻找相应的代码, 否则会报错.
Windows.history.pushState(state, title, url)
- state: 一个与指定网址相关的状态对象, popstate 事件触发时, 该对象会传入回调函数
- title: 新页面的标题, 大部分浏览器不支持这个
- url: 新的网址, 必须是与当前页面同一个域名. 浏览器显示的地址
重点是 pushstate 方法不会触发页面刷新, 只是会导致 history 对象发生变化, 地址栏会有反应
Windows.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 页面.
来源: http://www.jianshu.com/p/70e321cd7494