vue分析

vue总结

vuex

源码实现
是vue的一个中全局状态管理的工具,是一个插件,只能在vue中使用,因为依赖于vue的数据绑定。

  • 数据持久化
    利用插件 vuex-persist

    import VuexPersistedstate from 'vuex-persistedstate'
    
    const vuexLocal = new VuexPersistedstate({
        // storage: window.localStorage,//默认
        storage: window.sessionStorage  
    })
    
    const store = new Vuex.Store({
      state: { ... },
      mutations: { ... },
      actions: { ... },
      plugins: [vuexLocal.plugin]
    }) 
    
    
  • 严格模式

    不使用 mutation 改变状态,会抛出异常。

    const store = new Vuex.Store({
        strict:true,
    })
    

vue-router

官方Vue Router

  • hash模式
    hash表示的是地址栏URL中#符号(也称作为锚点)后的部分, hash虽然会出现在URL中, 但是不会被包含在Http请求中, 因此hash值改变不会重新加载页面,但是会触发 hashchange事件, 浏览器的前进后退也能被触发, 所以在HTML5之前, 基本都是使用hash来实现前端路由。

  • history模式
    利用了HTML5新增的**pushState()和replaceState()**两个api, 通过这两个api完成URL跳转不会重新加载页面。

  • hash和history 实现vue-router 区别:

    api hash history
    push window.location.assign window.history.pushState
    replace window.location.replace window.history.replaceState
    go window.history.go window.history.go
    back window.history.go(-1) window.history.go(-1)
    forward window.history.go(1) window.history.go(1)
  • router-link、router-view 这两个组件哪来的?

    <router-link to="/home">Home</router-link>
    <router-link to="/login">Login</router-link>
    <router-view></router-view>
    

    是在 Vue.use(VueRouter)注册路由 的时候创建的。

    Vue.use(VueRouter)
    

    源码做了什么呢?
    执行 install 方法,在全局注册了组件。
    vue分析_第1张图片
    注意:
    在注册两个组件之前,还做了其他事情:
    就是定义两个属性 $router、 $route

  • $router、 $route

    源码中可以看出,定义这两个属性是用mixin方法混入某个组件,执行钩子 beforeCreate 中处理的。
    $router的处理逻辑简单, $route 就相对复杂些,在路由切换时响应式的变化。

  • 路由懒加载
    减少首次加载资源,节约时间,提高效率。
    使用 import 导入组件,可定义chunkName,webpack会单独打包为一个js,在进入当前这个路由时才会加载。

动态组件

核心是使用 component 标签和 is 属性。
1、AST解析
标签上 is 属性的存在,会在 抽象语法树上打上 component属性标记,值为 is 属性上绑定的变量。
2、render
根据 ast 树生成render函数,有component属性则会执行动态组件分支。
和普通组件区别是 _c 函数第一参数是一个变量。

style scoped

  • 作用
    style标签内的样式只在当前模板输出的HTML标签上生效

  • 原理
    1、每个Vue文件都将对应一个唯一的id,该id可以根据文件路径名和内容hash生成
    2、编译template标签时为每个标签添加了当前组件的id
    3、编译style标签时,会根据当前组件的id通过属性选择器和组合选择器输出样式

实现vue中双向绑定

  • Object.defineproperty ** ,进行数据劫持,有个缺点:**
    1、不能对数组进行劫持,vue源码只对 Array的几个属性进行了处理。(push、pop、unshift、shift、splice、sort、reverse)。
    2、不能对一个完整的对象进行劫持,只能劫持对象的属性。
  • Proxy
    可以弥补 Object.defineproperty 的缺点。
  • 参考文章
    Object.defineproperty、Proxy优劣

实现vue中dom异步更新

computed

参考文章

初始化所有状态时,也初始化了计算属性,将每个计算属性作为Watcher函数参数实例化,把实例对象保存在一个computedWatchers中,挂在vue实例上。这个watcher构造函数内部定义了获取计算属性的方法和计算结果,并提供一个标识表示是否计算过了。
初始化计算属性时对每个计算属性做了响应式化,定义了一个高阶函数作为get方法,内部就是获取vue实例上的computedWatchers中的每个watcher实例,通过标识判断取值。

keep-alive

本身是一个抽象组件。不会渲染一个dom,也不会出现在父组件链中。
使用keep-alive包裹动态组件时,会缓存不活动的组件实例,而不销毁它们。
1、在动态组件中的应用
2、在vue-router中的应用

  • 缓存原理
    在 created 函数调用时将需要缓存的 VNode 节点保存在 cache 中,在 render(页面渲染) 时,如果 VNode 的 name 符合缓存条件(可以用 include 以及 exclude 控制),则会从 cache 中取出之前缓存的 VNode 实例进行渲染

    源码分析

$set

  • 解决什么问题?
    vue创建实例完成,新添加的属性不能响应式化。也就是修改数据视图不会更新。

  • 使用和原理

$set方法是在vue实例化时挂在vue原型上的(stateMixin)。

this.$set(target, key, val)

1、target 是 null 或 undefined 抛错

if (process.env.NODE_ENV !== 'production' &&(isUndef(target) || isPrimitive(target))) {
    warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
}

2、target 是数组,利用 splice方法

if (Array.isArray(target) && isValidArrayIndex(key)) {
	//首先需要处理数组length,保证索引>length,避免splice报错
    target.length = Math.max(target.length, key)
    target.splice(key, 1, val)
    return val
}

3、target 是对象

if (key in target && !(key in Object.prototype)) {
    target[key] = val
    return val
}

4、给Vue 实例对象添加属性 或 为根数据对象(vm.$data)添加属性

const ob = (target: any).__ob__
if (target._isVue || (ob && ob.vmCount)) {
process.env.NODE_ENV !== 'production' && warn(
  'Avoid adding reactive properties to a Vue instance or its root $data ' +
  'at runtime - declare it upfront in the data option.'
)
	return val
}  

5、target 创建全新属性,target本身不是响应式数据, 直接赋值,否则 调用 内部响应式方法

const ob = (target: any).__ob__
 if (!ob) {
    target[key] = val
    return val
  }
  // 进行响应式处理
  defineReactive(ob.value, key, val)
  ob.dep.notify()
  return val

修饰符

  • native
    让自定义组件可以响应到自身绑定的事件

  • sync
    允许props数据的双向数据绑定。

    // 子组件
    this.$emit('update:val', newVal)
    //父组件
    <v-child :val.sync="val"></v-child>
    

    Vue其实底层是继承了eventBus ,它提供了两个api

    this.$emit可以触发事件
    $event可以获取$emit的参数
    
  • stop
    阻止事件冒泡,相当于 e.stopPropagation()

  • present
    阻止事件默认行为,相当于 e.preventDefault()

  • self
    只有点击元素本身才会触发事,相当于变相的阻止事件冒泡

  • trim
    输入框过滤首尾的空格

  • lazy
    input框光标离开才更新数据

  • once
    只能用一次,无论点击几次,执行一次之后就不会再执行

  • capture
    冒泡反转

  • passive
    提升移动端的性能, 在每次滚动中都会有一个默认事件会触发,加上这个,则是告诉浏览器, 不需要查询, 不需要触发这个默认事件

$nextick、事件循环在dom更新中的使用

  • 为什么需要 $nextick?

    因为vue的 dom更新是异步的 ,dom更新完成会触发$nextTick回调函数,通知dom更新完成。
    vue的异步dom更新借助事件循环,根据执行环境优先选择Promise.then, 不支持则最终会调用setTimeout(fn, 0)

    参考

data为什么是函数?

  1. 定义为函数,每个组件实例都会创建一个私有数据空间,各个组件维护自己的数据。如果是个对象,所有组件实例都会共用一个数据(data)。

  2. 这根vue本身的设计无关,是js特性决定的。
    我们在面向对象编程时,是基于原型链和构造函数的,原型链上添加的一般都是函数。

  3. vue中创建一个组件,相当于创建了一个组件的构造器,使用的时候才实例化。
    组件中的data是挂在原型上的,这个组件所有的实例data都指向原型中的data。
    所以,data是对象会导致组件的所有实例都共用一个data,这自然不是我们预期的样子。
    参考例子

  4. 那为什么是函数就不会共用一个data了呢?
    实例化的时候会调用这个函数,返回新的data,函数也是有作用域的概念的,会有一个私有的作用域。

v-for 中key

主要为了高效的更新虚拟dom,vue的diff算法需要用到key。
vue会基于key重新排列元素顺序,key不存在的元素会被移除

v-if、v-show

  • v-if
    控制dom元素的创建和销毁,可以结合v-else 、v-else-if
    状态不会改变建议使用,如果为false,则不用创建dom,减少消耗。
  • v-show
    控制元素的css属性display, true: display: block;false: display:none;
    状态频繁切换建议使用,减少dom创建和销毁次数,降低消耗。

v-if、v-for为什么不要同时在一个标签

v-for 会优先于 v-if 执行,这样就浪费了资源,我们可以进行嵌套

执行过程源码中都干了些什么?

  • 初始化生命周期的状态

  • 初始化事件容器

  • 初始化创建元素方法
    创建了Dep,用于watcher发布订阅模式,依赖收集

  • beforeCreate ,初始化接下来开始。。。

  • 初始化vue组件内的属性

    • props
    • methods
    • data
      数据代理,响应式化,这时候还没有依赖收集,因为vue正在初始化,模版还没渲染
    • computed
    • watch
  • created ,现在初始化已经完成,vue内属性全部可以访问

  • 判断有无 el,有,继续执行。无,执行结束,等待手动调用 $mount 方法:

    new Vue({
      ...
    }).$mount('#app') //这里的$mount 就是手动挂载
    
  • 准备 render 方法,判断有无 render 方法:

    • 有:直接进入挂载
    • 无:判断有无template,无就用el转换为template,再将其转换为render函数
  • beforeMount ,这时候内存中保存了要渲染的 render 函数

  • 将vue的 渲染方法 render 添加到 Watcher 中

  • 开始挂载,render函数内部:

    • 首先,生成一个虚拟dom,保存
    • render函数返回真实dom,保存
    • 真实dom替换vm.$el, $el append到页面
  • mounted ,挂载完成
    页面dom可见

  • 数据发生改变,触发update

  • beforeUpdate

    • 生成一个新的虚拟dom,和之前的旧虚拟dom 进行diff,得到一个最小的更新范围,更新 render函数中的数据,返回真实dom
    • 旧的虚拟dom替换成新的
  • updated

    • 页面重新渲染完成

vue分析_第2张图片

发布订阅模式

vue分析_第3张图片

el、template、render属性优先性

当Vue选项对象中有render渲染函数时,Vue构造函数将直接使用渲染函数渲染DOM树,当选项对象中没有render渲染函数时,Vue构造函数首先通过将template模板编译生成渲染函数,然后再渲染DOM树,而当Vue选项对象中既没有render渲染函数,也没有template模板时,会通过el属性获取挂载元素的outerHTML来作为模板,并编译生成渲染函数。
换言之,在进行DOM树的渲染时,render渲染函数的优先级最高,template次之且需编译成渲染函数,而挂载点el属性对应的元素若存在,则在前两者均不存在时,其outerHTML才会用于编译与渲染。

父子组件生命周期执行顺序

p beforeCreate -> p created -> p beforeMount -> 
c beforeCreate -> c created -> c beforeMount ->
c mounted -> p mounted

vue 使用步骤

  • 页面模版

    1. html标签
    2. template标签
    3. 单文件
  • vue实例

  • vue实例挂载到模版

    mount方法

你可能感兴趣的:(前端,js,vue,vue)