vue基础

作者: 肥羊猪 | 来源:发表于2021-02-20 18:14 被阅读0次

vue的data是函数的原因:
1.Object是引用数据类型,指向地址为同一个,造成多个实例的data混乱
2.函数作用域独立每个实例的data数据。
3.根本在于js特性导致的
Object.defineProperty()的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性

Object.defineProperty(obj, prop, desc)

let Person = {}
Object.defineProperty(Person, 'name', {
   value: 'jack',
   writable: true // 是否可以改变
})

let Person = {}
let temp = null
Object.defineProperty(Person, 'name', {
  get: function () {
    return temp
  },
  set: function (val) {
    temp = val
  }
})
属性定义,通过Object.defineProperty()形式
如果Obj没有名为Prop的自身属性的话:如果Obj是可扩展的话,则创建Prop这个自身属性,否则拒绝
如果Obj已经有了名为Prop的自身属性:则按照下面的步骤重新配置这个属性
如果这个已有的属性是不可配置的,则进行下面的操作会被拒绝

defineProperty不能检测到数组长度的变化,监听数组所有索引的的代价也比较高,可通过修改数组的一些方法(push、pop、shift、unshift、splice、sort、reverse)去监听,但避免直接修改原型:

第一步:先获取原生 Array 的原型方法,因为拦截后还是需要原生的方法帮我们实现数组的变化。
第二步:对 Array 的原型方法使用 Object.defineProperty 做一些拦截操作。
第三步:把需要被拦截的 Array 类型的数据原型指向改造后原型。
// 将data中我们定义的每个属性进行响应式绑定
export function observe (data) {
  const keys = Object.keys(data);
  for (let i = 0; i < keys.length; i++) {
    // 如果是数组
    if (Array.isArray(keys[i])) {
      observeArray(keys[i]);
    } else {
      // 如果是对象
      defineReactive(obj, keys[i]);
    }
  }
}
 
// 数组的处理
export function observeArray () {
  // ...省略
}
function def (obj, key) {
  Object.defineProperty(obj, key, {
    writable: true,
    enumerable: true,
    configurable: true,
    value: function(...args) {
      console.log('key', key);
      console.log('args', args); 
    }
  });
}
 
// 重写的数组方法
let obj = {
  push() {}
}
 
// 数组方法的绑定
def(obj, 'push');

vue响应式原理: https://cn.vuejs.org/v2/guide/reactivity.html

众所周知,vue2.0 的核心原理就是利用Object.defineProperty(); 可以重新定义数据的属性,给属性增加 gettersetter


// 首先先定义一个 对象数据

let data = {name :'yolin'};

// 那么我们需要对这个数据进行观察;

// 定义一个 observer() 函数

observer(data);



  • observer函数

function observer(target) {

// 首先我们要判断下传递进来的参数是否是对象,所以就有了下面的if判断

if(typeof target !== 'object' && target === null){

// 如果传递进来的参数不是对象或者是null,则直接滚粗,return出去

return target;

}

// 如果传递进来的参数是对象数据结构, 那就循环,抡它!

for (let key in target) {

// 在调用一个 下面说到的一个 核心函数了

// 三个参数 键值对, 属性, 值

defineReactive(target,key,target[key]);

}

}

  • defineReactive函数

// 开始重写数据的属性,比如get 获取属性值,以及set 设置属性值

  function defineReactive(target, key, value) {

    // 如果是多层数据,比如 let person = {name: 'bryant',age:{n:24}};

    //这种数据类型 在data中的age属性还是一个对象,即为多层嵌套,所以我们要对这样的value 再次进行观察

    if (typeof value === 'object' && value !== null) {

        observer(value);

    }

    // 核心来了

    Object.defineProperty(target, key, {

        get() {

            return value

        },

        set(newValue) {

            if (newValue !== value) {

                if (typeof value === 'object' && value !== null) {

                    observer(value);

                }

                updateView();

                value = newValue();

            }

        },

    })

  • updateView 函数(视图更新触发的函数)

function updateView () {

console.log("更新视图");

}

  • vue2.0缺陷,如果数据层级过多,则会出现大量的递归,造成内存性能损耗

vue-router有router-link 链接、router-view试图 组件

$route为当前router跳转对象里面可以获取name、path、query、params
$routerVueRouter实例,想要导航到不同URL,则使用$router.push方法

keep-alive用于缓存组件 <transition name="bounce"> 过渡

路由懒加载

const Foo = () => import('./Foo.vue')
const router = new VueRouter({
  routes: [
    { path: '/foo', component: Foo }
  ]
})

vue-router传参:
传一个值时,用params

配置路由格式:/router/:id
// $routes耦合 
const User = {
  template: '<div>User {{ $route.params.id }}</div>'
}
const router = new VueRouter({
  routes: [
    { path: '/user/:id', component: User }
  ]
})
// props 解耦
const User = {
  props: ['id'],
  template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({
  routes: [
    { path: '/user/:id', component: User, props: true },

    // 对于包含命名视图的路由,你必须分别为每个命名视图添加 `props` 选项:
    {
      path: '/user/:id',
      components: { default: User, sidebar: Sidebar },
      props: { default: true, sidebar: false }
    }
  ]
})

//函数模式
const router = new VueRouter({
  routes: [
    { path: '/search', component: SearchUser, props: (route) => ({ query: route.query.q }) }
  ]
})

如果传入数据较多时,用query,因为query传入的是一个对象

配置路由格式:/router,也就是普通配置
<router-link :to="{path:'/profile',query={name:'why',age=18,height=1.88}}">档案</router-link>
获取参数:this.$route.query.name
const router = new VueRouter({
  mode: 'history',// 防止404
  routes: [
    { path: '*', component: NotFoundComponent }
  ]
})

组件之间传参:

八种通信方式
1. props 、$emit 子组件中书写
 props: ['message']
 <child :message="msg"></child>
2. children 、children、parent
3. ref
 <child ref="msg"></child>
this.$refs.msg
4. provide 、reject
5. Vuex
state:页面状态管理容器对象
commit:状态改变提交操作方法。对mutation进行提交,是唯一能执行mutation的方法。
mutations:存改变状态的操作方法
getters:读取state中的数据

6. $attrs 与listenters
7. eventBus
8. localStorage 、sessionStorage

父子组件通信: props; $parent / $children; provide / inject ; ref ; $attrs / $listeners
兄弟组件通信: eventBus ; vuex
跨级通信: eventBus;Vuex;provide / inject 、$attrs / $listeners

组件复用时,路由传参a的变化可以通过beforeRouteUpdate或者watch (监测变化) $route 对象

const router = new VueRouter({ ... })
vue-router有哪几种导航钩子( 导航守卫 )?
1、全局守卫: router.beforeEach next() 必须
2、全局解析守卫: router.beforeResolve
3、全局后置钩子: router.afterEach
4、路由独享的守卫: beforeEnter
5、组件内的守卫: beforeRouteEnter next()、beforeRouteUpdate (2.2 新增)、beforeRouteLeave

vue-router响应路由参数的变化:
一是用watch监听
在一个就是在父组件的router-view上加个key   <router-view :key="$route.fullPath"></router-view>
vue生命周期.png
beforeCreate:function(){}
//组件实例化之前执行的函数
 
created:function(){}
//组件实例化完毕,但是页面没有显示
 
beforeMount:function(){}
//组件挂载前,页面还没有展示,但是虚拟的DOM已经配置
 
mounted:function(){}
//组件挂载后,这个方法执行后,页面显示
 
beforeUpdate:function(){}
//当页面操作后,组件更新前,页面没有显示,此时虚拟DOM已经挂载
 
updated:function(){}
//组件更新完毕,页面已经显示
 
beforeDestroy:function(){}
//组件销毁之前
 
destroyed:function(){}
//组件销毁之后

vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的。
1.使用Object.defineProperty进行数据劫持,把data对象computed等里的所有属性进行数据劫持。
2.使用观察者模式,完成发布订阅
模板里使用data对象属性dom对象订阅
data对象里的属性的发生变化时,就会发布,发布时,就改变了dom里的内容。

原理对data的getter/setter方法进行拦截(Object.defineProperty或者Proxy),利用发布订阅的设计模式,在getter方法中进行订阅,在setter方法中发布通知,让所有订阅者完成响应
监听器watch、计算属性computed、视图渲染template/render三个角色同时作为订阅者.前者直接操作属性,后两者是操作属性getter

双向绑定.png
vue怎么操作dom:(在mounted钩子进行挂载后)

1.ref="idRef" =>this.$refs.idRef.style.color='red';(子组件ref报错)

[2.id=](http://2.id)"id". => document.querySelector("#id").innerHTML="xxxx"

如果一个数据需要经过复杂计算就用 computed

如果一个数据需要被监听并且对数据做一些操作就用 watch

methods: 存放的方法是一些内部方法、事件的回调、命令是调用的方法。
watch: 用于监听数据的实时的变化。在数据变化的回调中执行异步操作或者开销很大的时候使用。
computed: 也是实时监听数据变化,做出相应的变化,跟watch不同的是他可以被看成一个data里面的属性值来使用。所以当我们需要监听一个值并且需要生成一个新的属性时就可以使用computed。

计算属性computed和监听器watch都可以观察属性的变化从而做出响应,不同的是:
计算属性computed更多是作为缓存功能的观察者,它可以将一个或者多个data的属性进行复杂的计算生成一个新的值,提供给渲染函数使用,当依赖的属性变化时,computed不会立即重新计算生成新的值,而是先标记为脏数据,当下次computed被获取时候,才会进行重新计算并返回。
而监听器watch并不具备缓存性,监听器watch提供一个监听函数,当监听的属性发生变化时,会立即执行该函数。

computed:适用单一值依赖多值进行变化的场景 具有缓存性 必须用return返回 为了进行不必要的资源消耗选择用计算属性 变量在computed中定义

watch:自动执行 newVal和oldVal参数且并不一定需要return 适用多值依赖单一值进行变化的场景 属性监听在data中定义

vif与vshow的区别: if操作dom是否渲染 show修改css display

登陆前验证计算 beforeRouteUpdate

路由守卫~监听路由 全局的beforeEach(next) afterEach, 单个路由独享的beforeEnter , 或者组件级的beforeRouteEnter(next) beforeRouteUpdate beforeRouteLeave

数据初始化通常在mounted 因为create无法不能操作dom

组件通信:props、$emit/$on、vuex、$parent / $children、$attrs/$listeners和provide/inject
<meta charset="utf-8">

父组件 -> 子组件:prop
子组件 -> 父组件:on/emit
兄弟组件通信:
Event Bus:每一个Vue实例都是一个Event Bus,都支持
emit,可以为兄弟组件的实例之间new一个Vue实例,作为Event Bus进行通信。
跨级组件通信:使用provide/inject



1.父组件A通过props的方式向子组件B传递,B to A 通过在 B 组件中 $emit, A 组件中 v-on 的方式实现。

vuex:

Vuex实现了一个单向数据流,在全局拥有一个State存放数据,当组件要更改State中的数据时,必须通过Mutation进行,Mutation同时提供了订阅者模式供外部插件调用获取State数据的更新。而当所有异步操作(常见于调用后端接口异步获取更新数据)或批量的同步操作需要走Action,但Action也是无法直接修改State的,还是需要通过Mutation来修改State的数据。最后,根据State的变化,渲染到视图上。

$attrs , $listeners 来传递数据与事件,跨级组件之间的通讯

$attrs与$listeners 是两个对象,$attrs 里存放的是父组件中绑定的非 Props 属性,$listeners里存放的是父组件中绑定的非原生事件。

父子通信:

父向子传递数据是通过 props,子向父是通过 events($emit);通过父链 / 子链也可以通信($parent / $children);ref 也可以访问组件实例;provide / inject API;$attrs/$listeners

兄弟通信:Bus;Vuex

跨级通信:

Bus;Vuex;provide / inject API、$attrs/$listeners

Vues.png

vue 渲染原理:
template无法被浏览器解析并渲染,所以需要先编译成js函数render
解析parse,优化optimize,生成generate,最终生成可执行函数render
templatejsx的都是render的一种表现形式

Virtual DOM 是 DOM 节点在JavaScript中的一种抽象数据结构

虚拟DOM的作用是在每一次响应式数据发生变化引起页面重渲染时,Vue对比更新前后虚拟DOM,匹配找出尽可能少的需要更新的真实DOM,从而达到提升性能的目的。

在对节点进行diff的过程中,判断是否为相同节点的一个很重要的条件是key是否相等,如果是相同节点,则会尽可能的复用原有的DOM节点。所以key属性是提供给框架在diff的时候使用的

相关文章