vue实践推荐 vue实践推荐

vue实践推荐

 

1.vue

(1)组件

每个组件单独分成文件,如A.vue文件实现A组件;
除index.vue之外文件名推荐大写开头如BaseHeader.vue或横线连接base-header.vue;
基础组件名可以共用相同的前缀,如BaseButton;
组件名应该倾向于完整单词而不是缩写;
组件名推荐多个单词,如TodoItem代替Todo;html模板中引用推荐todo-item
prop定义尽量详细,如指定其类型,必填与否;
复制代码
props: {
  status: {
    type: String,
    required: true,
    validator: function (value) {
    // 校验方法
      return ***
    }
  }
}
复制代码
 
编辑器通常按照字母顺序组织文件,可以考虑将具有相关关系的组件的命名相关,如都是搜索相关的组件可带公共前缀Search,具有父子关系的组件,可以考虑将子组件命名带上父组件命名参数SearchSidebar.vue NavigationForSearchS idebar.vue;
自闭合组件引用;
<!-- 在单文件组件、字符串模板和 JSX 中 -->
<MyComponent />
<!-- 在 DOM 模板中 -->
<my-component></my-component>
<!-- 在所有地方 -->
<my-component></my-component>
 
在声明 prop 的时候,其命名应该始终使用camelCase,而在模板和 JSX 中应该始终使用 kebab-case;
props: {
  greetingText: String
}
<WelcomeMessage greeting-text="hi"/>
多个特性的元素应该分多行撰写,每个特性一行,易读;
<WelcomeMessage
  greeting-text="hi"
  name="hh" />

(2)js文件-scss文件

文件名推荐 '-' 连接
 

(3)watch

watch 一个变量的时候,初始化并不会立即执行,需要在created手动触发一次;推荐添加immediate
复制代码
watch:{
  searchText:{
    handler:'getList',
    immediate:true,
    <!-- 是否深度监听,如对象 -->
    deep:true
  }
}
复制代码

监听属性主要是监听某个值发生变化后,对新值去进行逻辑处理。

Watcher 初始化watch vm.$watch

Wacher充当一个中介的角色,数据发生变化的时候通知它,它再通知其他地方;

vm.$watch( 'a.b.c', function(new,old){
  // 当data.a.b.c发生变化时,触发该函数执行
})
 
// 只要把watcher实例添加到data.a.b.c属性的Dep中即可;当data.a.b.c的值发生变化时会通知watcher;接着watcher再执行参数中的回调函数;
 

(4)data 响应式数据

组件的  data 必须是一个函数,则每个组件实例都管理其自己的数据,数据间不会相混淆。当 data 的值是一个对象时,它会在这个组件的所有实例之间共享,当修改其中一个组件的数据,其他共享数据组件都会受影响。
复制代码
data () {
  return {
    foo: 'bar'
  }
}
 
vm.$set( target, key, value )
Vue.set(vm.obj,'k1','v1')
this.$set(this.obj,'k1','v1')
this.obj = Object.assign({}, this.obj)
this.obj = Object.assign({}, this.obj,{'k1','v1'})
复制代码
 
对象添加可以使用:
this.$set(对象名,属性,值)
 
若所需数据是响应式的,则需要一开始在 data中进行定义,否则后期追加的属性是不具备响应式特征的,vue是数据劫持的方式,后期只通过赋值的方式(this.foo.a='a')没办法进行劫持(Object.defineProperty()来实现对属性的劫持);需要额外将其处理成响应式的,通过this.$set或Vue.set实现响应式;
this.$set(this.foo,'aProperty','aavalue')
Vue.set(obj, 'newProp', 123)
或
state.obj = {...state.obj, newProp:123}

(5)computed计算属性

需要对数据进行处理之后再显示 格式化输出结果 反转数组
复制代码
computed:{
  newPrice:function(){
    return this.price='¥' + this.price + '元';
  }
}
 
 
computed:{
   reverseNews:function(){
    return this.newsList.reverse();
  }
}
 
把复杂计算属性分割为尽可能多的更简单的属性;
computed: {
  basePrice: function () {
    return this.manufactureCost / (1 - this.profitMargin)
  },
  discount: function () {
    return this.basePrice * (this.discountPercent || 0)
  },
  finalPrice: function () {
    return this.basePrice - this.discount
  }
}
复制代码

(6)指令

v- for v-if:
v- for循环须绑定key,在对比新旧虚拟节点时辨识虚拟节点,更新dom时效率更高;
<ul>
  <li v-for="todo in todos" :key="todo.id">
    {{ todo.text }}
  </li>
</ul>
避免v- for v-if同时作用在同一个元素上 v-for优先级高于v-if; 可以选择多加一层div;
若为了避免渲染本应该隐藏的列表,可以将v- if用于上一层容器上;
若过滤列表中数据,可以使用计算属性代替v- if;
 
指令缩写 (用 : 表示 v-bind: 、用 @ 表示 v- on: 和用 # 表示 v-slot:) 应该要么都用要么都不用;
 
如果一组 v- if + v-else 的元素类型相同,最好使用 key (比如两个 <div> 元素),防止本不相同的元素被识别为相同的元素;
<div v-if="error" key="search-status">
   {{ error }}
</div>
<div v-else key="search-results">
  {{ results }}
</div> 

(7)样式

为组件样式设置作用域,可以选择scoped或类名包裹,防止影响全局样式;
为了给样式设置作用域, Vue 会为元素添加一个独一无二的特性,例如 data-v-f3f3eg9。然后修改选择器,使得在匹配选择器的元素中,只有带这个特性才会真正生效;
在 scoped 样式中,类选择器比元素选择器更好,因为大量使用元素选择器是很慢的;
 

(8)渲染

组件模板{{}}内推荐简单表达式,复杂的表达式可选择计算属性或方法或过滤器;如果防止出现短暂{{}}的情况,可以选择使用v-text替换{{}} ;{{}}和v-text方式会将元素当成纯文本输出;v-html会将元素当成HTML标签解析后输出; 
 

(9)组件通信

应该优先通过 prop 和事件进行父子组件之间的通信,而不是 this. $parent 或改变 prop;
一个理想的 Vue 应用是 prop 向下传递,事件向上传递的;
子组件利用 v-bind="$attrs"得到父组件的所有属性的绑定,无需在每个子组件上都绑定父组件的属性如placeholder;
<input
  :value="value"
  v-bind="$attrs"
  @input="$emit('input', $event.target.value)"
>

(10)路由切换组件不变的问题

当页面切换同一个路由但不同参数的地址时,组件的生命周期钩子并不会重新触发:vue-router会识别出两个路由使用同一个组件从而进行复用,而不会重新构建组件,因此组件生命周期钩子也不会被触发。想要重新触发可以选择:

a.路由导航守卫beforeRouteUpdate,可以在当前路由改变且组件被复用时调用,只需要将每次切换路由时需要处理的逻辑放在该守卫里即可如获取新数据更新状态并渲染视图。

b.观察$route对象变化,,可能会导致依赖追踪的内存消耗

watch:{
  '$route'(to,from){
  // 对路由变化作出响应
  }
}

ps:如果共用组件页面有共用信息,可以只观察变化的那部分,而不用整个页面信息都重刷

复制代码
watch:{
  '$route.query.id'(){
    // 请求个人信息
   },
  '$route.query.page'(){
    // 请求不同页面列表数据
  }
}
复制代码

c.为router-view组件添加标识属性key,利用虚拟dom在渲染时通过key来对比两个节点是否相同的原理;可以使得每次切换路由时key都不一样,让虚拟dom认为router-view组件是一个新节点,从而销毁组件再重建组件。但浪费性能。

(11) v-model 双向绑定

使用v-model来进行双向数据绑定的时:
<input  v-model="something">
仅仅是一个语法糖:
<input v-bind: value="something" v-on:input="something = $event.target.value">

https://juejin.im/post/5d70aed76fb9a06b04721ec3

(12)keep-alive

Vue.js内部将DOM节点抽象成了一个个的VNode节点,keep-alive组件的缓存也是基于VNode节点的而不是直接存储DOM结构;它将满足条件的组件在cache对象中缓存起来,在需要重新渲染的时候再将vnode节点从cache对象中取出并渲染,还未缓存过则进行缓存

生命周期函数

1. activated

在 keep-alive 组件激活时调用

该钩子函数在服务器端渲染期间不被调用

2. deactivated

在 keep-alive 组件停用时调用

该钩子在服务器端渲染期间不被调用

被包含在 keep-alive 中创建的组件,会多出两个生命周期的钩子: activated 与 deactivated

使用 keep-alive 会将数据保留在内存中,如果要在每次进入页面的时候获取最新的数据,需要在 activated 阶段获取数据,承担原来 created 钩子函数中获取数据的任务。

https://blog.csdn.net/fu983531588/article/details/90321827

<!-- 我们更希望那些标签的组件实例能够被在它们第一次被创建的时候缓存下来。为了解决这个问题,我们可以用一个 <keep-alive> 元素将其动态组件包裹起来。 -->
<keep-alive>
  <component v-bind:is="currentTabComponent" class="tab"></component>
</keep-alive>
<!-- keep-alive保持这个组件在内存中是常驻的,由于动态组件可能需要动态切换,可以减少组件变化时候的内存消耗,影响性能. -->
<!--<keep-alive>是Vue的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM。--> 

https://cn.vuejs.org/v2/api/#keep-alive

<!--使用 router.meta 属性,预先定义需要缓存的组件--> 
复制代码
<keep-alive>
  <router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
--------------------------------------------------------
export default {
   data() {
     return {};
 },
 mounted() {},
 methods: {},
 beforeRouteLeave(to, from, next) {
   if (to.path == "/index") {
     to.meta.keepAlive = true;
   } else {
     to.meta.keepAlive = false;
   }
   next();
 }
};
 

// keepalive组件选项
var KeepAlive = {
  name: 'keep-alive',
  // 抽象组件的标志
  abstract: true,
  // keep-alive允许使用的props
  props: {
    include: patternTypes,
    exclude: patternTypes,
    max: [String, Number]
  },
 
created: function created () {
  // 缓存组件vnode
  this.cache = Object.create(null);
  // 缓存组件名
  this.keys = [];
},
 
destroyed: function destroyed () {
  // destroyed钩子中销毁所有cache中的组件实例
  for (var key in this.cache) {
    pruneCacheEntry(this.cache, key, this.keys);
  }
},
 
mounted: function mounted () {
  var this$1 = this;
  // 动态include和exclude
  // 对include exclue的监听
  this.$watch('include', function (val) {
    pruneCache(this$1, function (name) { return matches(val, name); });
  });
  this.$watch('exclude', function (val) {
    pruneCache(this$1, function (name) { return !matches(val, name); });
  });
},
// keep-alive的渲染函数
render: function render () {
  // 拿到keep-alive下插槽的值
  var slot = this.$slots.default;
  // 第一个vnode节点
  var vnode = getFirstComponentChild(slot);
  // 拿到第一个组件实例
  var componentOptions = vnode && vnode.componentOptions;
  // keep-alive的第一个子组件实例存在
  if (componentOptions) {
  // check pattern
  //拿到第一个vnode节点的name
    var name = getComponentName(componentOptions);
    var ref = this;
    var include = ref.include;
    var exclude = ref.exclude;
    // 通过判断子组件是否满足缓存匹配
    if ((include && (!name || !matches(include, name))) ||(exclude && name && matches(exclude, name))) {
      return vnode
    }
 
    var ref$1 = this;
    var cache = ref$1.cache;
    var keys = ref$1.keys;
    var key = vnode.key == null ? componentOptions.Ctor.cid + (componentOptions.tag ? ("::" + (componentOptions.tag)) : '') : vnode.key;
    // 再次命中缓存
    if (cache[key]) {
      vnode.componentInstance = cache[key].componentInstance;
      // make current key freshest
      remove(keys, key);
      keys.push(key);
    } else {
      // 初次渲染时,将vnode缓存
      cache[key] = vnode;
      keys.push(key);
      // prune oldest entry
      if (this.max && keys.length > parseInt(this.max)) {
        pruneCacheEntry(cache, keys[0], keys, this._vnode);
      }
    }
    // 为缓存组件打上标志
    vnode.data.keepAlive = true;
  }
  // 将渲染的vnode返回
    return vnode || (slot && slot[0])
  }
};
复制代码
<!-- keep-ailve组件没有用template而是使用render函数。keep-alive本质上只是存缓存和拿缓存的过程,并没有实际的节点渲染,所以使用render处理是最优的选择。 -->
<!-- render函数执行的关键一步是缓存vnode,由于是第一次执行render函数,选项中的cache和keys数据都没有值,其中cache是一个空对象,我们将用它来缓存{ name: vnode }枚举,而keys我们用来缓存组件名。 因此我们在第一次渲染keep-alive时,会将需要渲染的子组件vnode进行缓存。-->

https://juejin.im/post/5d8871c851882509630338c4

https://juejin.im/post/5da42574f265da5b991d6173

https://segmentfault.com/q/1010000011537852

https://segmentfault.com/a/1190000011978825

https://github.com/answershuto/learnVue/blob/master/vue-src/core/components/keep-alive.js

(13) vue初始化

https://cn.vuejs.org/v2/guide/instance.html#%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E5%9B%BE%E7%A4%BA

vue整体生命周期分为4阶段:初始化(new Vue() 到created之前) / 模板编译(created到beforeMount之前) / 挂载(beforeMount到mounted) / 卸载(beforeDestroy到destroyed)

初始化目的:在vue.js实例上初始化一些属性-事件-响应式数据如props-methods-data-computed-watch-provide-inject等

https://blog.csdn.net/a419419/article/details/90764860

https://blog.csdn.net/qq_20143169/article/details/83745727

(14)vue 同步更新 异步更新

异步更新队列指的是当状态发生变化时,Vue异步执行DOM更新。

Vue的dom更新是异步的,当数据发生变化,vue并不是里面去更新dom,而是开启一个队列。

Vue.js实现了一个queue队列,在下一个tick的时候会统一执行queue中Watcher的run;数据变化时,根据响应式触发setter->Dep->Watcher->update->patch;

比如我们调用一个方法,同时涉及多个数据的操作改变,vue会把这一些列操作推入到一个队列中,相当于JavaScript的同步任务,在执行过程中可能会出现一些产生任务队列的异步任务,比如定时器、回调等。

在vue里面任务队列也叫事件循环队列。我们都知道JavaScript是循环往复的执行任务队列。Vue也一样,在一个同步任务过程中是不会去更新渲染视图,而是在同步任务(事件循环队列)执行完毕之后,在主线程的同步执行完毕,读取任务队列时更新视图。

需要先渲染数据然后操作dom,这时候就要使用vue提供的nextTick函数(当你需要数据先渲染,然后去操作渲染完成之后的dom,要把操作dom的逻辑写在这个函数里面):Vue.$nextTick(callback);

将状态改变之后想获取更新后的DOM,往往我们获取到的DOM是更新前的旧DOM,我们需要使用vm.$nextTick方法异步获取DOM;

复制代码
data() {
  return {
    message: '数据更新前no'
  }
},
edit(){
  this.message = '数据更新后have';
  console.log(document.getElementById('message').innerHTML);
  //数据更新前no
  this.$nextTick(() => {
    // 这个函数就是有了数据之后,渲染完成之后会执行,也就是说当你需要数据先渲染,
    // 然后去操作渲染完成之后的dom,要把操作dom的逻辑写在这个函数里面
    console.log(document.getElementById('message').innerHTML);
    // 数据更新后have1
  });
  this.message = '数据更新后have1';
}
// 多次修改了状态,但其实Vue只会渲染一次
复制代码
 

Vue优先将渲染操作推迟到本轮事件循环的最后,如果执行环境不支持会降级到下一轮;Vue的变化侦测机制决定了它必然会在每次状态发生变化时都会发出渲染的信号,但Vue会在收到信号之后检查队列中是否已经存在这个任务,保证队列中不会有重复。如果队列中不存在则将渲染操作添加到队列中;之后通过异步的方式延迟执行队列中的所有渲染的操作并清空队列,当同一轮事件循环中反复修改状态时,并不会反复向队列中添加相同的渲染操作;在使用Vue时,修改状态后更新DOM都是异步的。

当某个响应式数据发生变化的时候,它的setter函数就会通知闭包中的Dep,Dep则会触发对应的Watcher对象的update方法

1.vue

(1)组件

每个组件单独分成文件,如A.vue文件实现A组件;
除index.vue之外文件名推荐大写开头如BaseHeader.vue或横线连接base-header.vue;
基础组件名可以共用相同的前缀,如BaseButton;
组件名应该倾向于完整单词而不是缩写;
组件名推荐多个单词,如TodoItem代替Todo;html模板中引用推荐todo-item
prop定义尽量详细,如指定其类型,必填与否;
复制代码
props: {
  status: {
    type: String,
    required: true,
    validator: function (value) {
    // 校验方法
      return ***
    }
  }
}
复制代码
 
编辑器通常按照字母顺序组织文件,可以考虑将具有相关关系的组件的命名相关,如都是搜索相关的组件可带公共前缀Search,具有父子关系的组件,可以考虑将子组件命名带上父组件命名参数SearchSidebar.vue NavigationForSearchS idebar.vue;
自闭合组件引用;
<!-- 在单文件组件、字符串模板和 JSX 中 -->
<MyComponent />
<!-- 在 DOM 模板中 -->
<my-component></my-component>
<!-- 在所有地方 -->
<my-component></my-component>
 
在声明 prop 的时候,其命名应该始终使用camelCase,而在模板和 JSX 中应该始终使用 kebab-case;
props: {
  greetingText: String
}
<WelcomeMessage greeting-text="hi"/>
多个特性的元素应该分多行撰写,每个特性一行,易读;
<WelcomeMessage
  greeting-text="hi"
  name="hh" />

(2)js文件-scss文件

文件名推荐 '-' 连接
 

(3)watch

watch 一个变量的时候,初始化并不会立即执行,需要在created手动触发一次;推荐添加immediate
复制代码
watch:{
  searchText:{
    handler:'getList',
    immediate:true,
    <!-- 是否深度监听,如对象 -->
    deep:true
  }
}
复制代码

监听属性主要是监听某个值发生变化后,对新值去进行逻辑处理。

Watcher 初始化watch vm.$watch

Wacher充当一个中介的角色,数据发生变化的时候通知它,它再通知其他地方;

vm.$watch( 'a.b.c', function(new,old){
  // 当data.a.b.c发生变化时,触发该函数执行
})
 
// 只要把watcher实例添加到data.a.b.c属性的Dep中即可;当data.a.b.c的值发生变化时会通知watcher;接着watcher再执行参数中的回调函数;
 

(4)data 响应式数据

组件的  data 必须是一个函数,则每个组件实例都管理其自己的数据,数据间不会相混淆。当 data 的值是一个对象时,它会在这个组件的所有实例之间共享,当修改其中一个组件的数据,其他共享数据组件都会受影响。
复制代码
data () {
  return {
    foo: 'bar'
  }
}
 
vm.$set( target, key, value )
Vue.set(vm.obj,'k1','v1')
this.$set(this.obj,'k1','v1')
this.obj = Object.assign({}, this.obj)
this.obj = Object.assign({}, this.obj,{'k1','v1'})
复制代码
 
对象添加可以使用:
this.$set(对象名,属性,值)
 
若所需数据是响应式的,则需要一开始在 data中进行定义,否则后期追加的属性是不具备响应式特征的,vue是数据劫持的方式,后期只通过赋值的方式(this.foo.a='a')没办法进行劫持(Object.defineProperty()来实现对属性的劫持);需要额外将其处理成响应式的,通过this.$set或Vue.set实现响应式;
this.$set(this.foo,'aProperty','aavalue')
Vue.set(obj, 'newProp', 123)
或
state.obj = {...state.obj, newProp:123}

(5)computed计算属性

需要对数据进行处理之后再显示 格式化输出结果 反转数组
复制代码
computed:{
  newPrice:function(){
    return this.price='¥' + this.price + '元';
  }
}
 
 
computed:{
   reverseNews:function(){
    return this.newsList.reverse();
  }
}
 
把复杂计算属性分割为尽可能多的更简单的属性;
computed: {
  basePrice: function () {
    return this.manufactureCost / (1 - this.profitMargin)
  },
  discount: function () {
    return this.basePrice * (this.discountPercent || 0)
  },
  finalPrice: function () {
    return this.basePrice - this.discount
  }
}
复制代码

(6)指令

v- for v-if:
v- for循环须绑定key,在对比新旧虚拟节点时辨识虚拟节点,更新dom时效率更高;
<ul>
  <li v-for="todo in todos" :key="todo.id">
    {{ todo.text }}
  </li>
</ul>
避免v- for v-if同时作用在同一个元素上 v-for优先级高于v-if; 可以选择多加一层div;
若为了避免渲染本应该隐藏的列表,可以将v- if用于上一层容器上;
若过滤列表中数据,可以使用计算属性代替v- if;
 
指令缩写 (用 : 表示 v-bind: 、用 @ 表示 v- on: 和用 # 表示 v-slot:) 应该要么都用要么都不用;
 
如果一组 v- if + v-else 的元素类型相同,最好使用 key (比如两个 <div> 元素),防止本不相同的元素被识别为相同的元素;
<div v-if="error" key="search-status">
   {{ error }}
</div>
<div v-else key="search-results">
  {{ results }}
</div> 

(7)样式

为组件样式设置作用域,可以选择scoped或类名包裹,防止影响全局样式;
为了给样式设置作用域, Vue 会为元素添加一个独一无二的特性,例如 data-v-f3f3eg9。然后修改选择器,使得在匹配选择器的元素中,只有带这个特性才会真正生效;
在 scoped 样式中,类选择器比元素选择器更好,因为大量使用元素选择器是很慢的;
 

(8)渲染

组件模板{{}}内推荐简单表达式,复杂的表达式可选择计算属性或方法或过滤器;如果防止出现短暂{{}}的情况,可以选择使用v-text替换{{}} ;{{}}和v-text方式会将元素当成纯文本输出;v-html会将元素当成HTML标签解析后输出; 
 

(9)组件通信

应该优先通过 prop 和事件进行父子组件之间的通信,而不是 this. $parent 或改变 prop;
一个理想的 Vue 应用是 prop 向下传递,事件向上传递的;
子组件利用 v-bind="$attrs"得到父组件的所有属性的绑定,无需在每个子组件上都绑定父组件的属性如placeholder;
<input
  :value="value"
  v-bind="$attrs"
  @input="$emit('input', $event.target.value)"
>

(10)路由切换组件不变的问题

当页面切换同一个路由但不同参数的地址时,组件的生命周期钩子并不会重新触发:vue-router会识别出两个路由使用同一个组件从而进行复用,而不会重新构建组件,因此组件生命周期钩子也不会被触发。想要重新触发可以选择:

a.路由导航守卫beforeRouteUpdate,可以在当前路由改变且组件被复用时调用,只需要将每次切换路由时需要处理的逻辑放在该守卫里即可如获取新数据更新状态并渲染视图。

b.观察$route对象变化,,可能会导致依赖追踪的内存消耗

watch:{
  '$route'(to,from){
  // 对路由变化作出响应
  }
}

ps:如果共用组件页面有共用信息,可以只观察变化的那部分,而不用整个页面信息都重刷

复制代码
watch:{
  '$route.query.id'(){
    // 请求个人信息
   },
  '$route.query.page'(){
    // 请求不同页面列表数据
  }
}
复制代码

c.为router-view组件添加标识属性key,利用虚拟dom在渲染时通过key来对比两个节点是否相同的原理;可以使得每次切换路由时key都不一样,让虚拟dom认为router-view组件是一个新节点,从而销毁组件再重建组件。但浪费性能。

(11) v-model 双向绑定

使用v-model来进行双向数据绑定的时:
<input  v-model="something">
仅仅是一个语法糖:
<input v-bind: value="something" v-on:input="something = $event.target.value">

https://juejin.im/post/5d70aed76fb9a06b04721ec3

(12)keep-alive

Vue.js内部将DOM节点抽象成了一个个的VNode节点,keep-alive组件的缓存也是基于VNode节点的而不是直接存储DOM结构;它将满足条件的组件在cache对象中缓存起来,在需要重新渲染的时候再将vnode节点从cache对象中取出并渲染,还未缓存过则进行缓存

生命周期函数

1. activated

在 keep-alive 组件激活时调用

该钩子函数在服务器端渲染期间不被调用

2. deactivated

在 keep-alive 组件停用时调用

该钩子在服务器端渲染期间不被调用

被包含在 keep-alive 中创建的组件,会多出两个生命周期的钩子: activated 与 deactivated

使用 keep-alive 会将数据保留在内存中,如果要在每次进入页面的时候获取最新的数据,需要在 activated 阶段获取数据,承担原来 created 钩子函数中获取数据的任务。

https://blog.csdn.net/fu983531588/article/details/90321827

<!-- 我们更希望那些标签的组件实例能够被在它们第一次被创建的时候缓存下来。为了解决这个问题,我们可以用一个 <keep-alive> 元素将其动态组件包裹起来。 -->
<keep-alive>
  <component v-bind:is="currentTabComponent" class="tab"></component>
</keep-alive>
<!-- keep-alive保持这个组件在内存中是常驻的,由于动态组件可能需要动态切换,可以减少组件变化时候的内存消耗,影响性能. -->
<!--<keep-alive>是Vue的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM。--> 

https://cn.vuejs.org/v2/api/#keep-alive

<!--使用 router.meta 属性,预先定义需要缓存的组件--> 
复制代码
<keep-alive>
  <router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
--------------------------------------------------------
export default {
   data() {
     return {};
 },
 mounted() {},
 methods: {},
 beforeRouteLeave(to, from, next) {
   if (to.path == "/index") {
     to.meta.keepAlive = true;
   } else {
     to.meta.keepAlive = false;
   }
   next();
 }
};
 

// keepalive组件选项
var KeepAlive = {
  name: 'keep-alive',
  // 抽象组件的标志
  abstract: true,
  // keep-alive允许使用的props
  props: {
    include: patternTypes,
    exclude: patternTypes,
    max: [String, Number]
  },
 
created: function created () {
  // 缓存组件vnode
  this.cache = Object.create(null);
  // 缓存组件名
  this.keys = [];
},
 
destroyed: function destroyed () {
  // destroyed钩子中销毁所有cache中的组件实例
  for (var key in this.cache) {
    pruneCacheEntry(this.cache, key, this.keys);
  }
},
 
mounted: function mounted () {
  var this$1 = this;
  // 动态include和exclude
  // 对include exclue的监听
  this.$watch('include', function (val) {
    pruneCache(this$1, function (name) { return matches(val, name); });
  });
  this.$watch('exclude', function (val) {
    pruneCache(this$1, function (name) { return !matches(val, name); });
  });
},
// keep-alive的渲染函数
render: function render () {
  // 拿到keep-alive下插槽的值
  var slot = this.$slots.default;
  // 第一个vnode节点
  var vnode = getFirstComponentChild(slot);
  // 拿到第一个组件实例
  var componentOptions = vnode && vnode.componentOptions;
  // keep-alive的第一个子组件实例存在
  if (componentOptions) {
  // check pattern
  //拿到第一个vnode节点的name
    var name = getComponentName(componentOptions);
    var ref = this;
    var include = ref.include;
    var exclude = ref.exclude;
    // 通过判断子组件是否满足缓存匹配
    if ((include && (!name || !matches(include, name))) ||(exclude && name && matches(exclude, name))) {
      return vnode
    }
 
    var ref$1 = this;
    var cache = ref$1.cache;
    var keys = ref$1.keys;
    var key = vnode.key == null ? componentOptions.Ctor.cid + (componentOptions.tag ? ("::" + (componentOptions.tag)) : '') : vnode.key;
    // 再次命中缓存
    if (cache[key]) {
      vnode.componentInstance = cache[key].componentInstance;
      // make current key freshest
      remove(keys, key);
      keys.push(key);
    } else {
      // 初次渲染时,将vnode缓存
      cache[key] = vnode;
      keys.push(key);
      // prune oldest entry
      if (this.max && keys.length > parseInt(this.max)) {
        pruneCacheEntry(cache, keys[0], keys, this._vnode);
      }
    }
    // 为缓存组件打上标志
    vnode.data.keepAlive = true;
  }
  // 将渲染的vnode返回
    return vnode || (slot && slot[0])
  }
};
复制代码
<!-- keep-ailve组件没有用template而是使用render函数。keep-alive本质上只是存缓存和拿缓存的过程,并没有实际的节点渲染,所以使用render处理是最优的选择。 -->
<!-- render函数执行的关键一步是缓存vnode,由于是第一次执行render函数,选项中的cache和keys数据都没有值,其中cache是一个空对象,我们将用它来缓存{ name: vnode }枚举,而keys我们用来缓存组件名。 因此我们在第一次渲染keep-alive时,会将需要渲染的子组件vnode进行缓存。-->

https://juejin.im/post/5d8871c851882509630338c4

https://juejin.im/post/5da42574f265da5b991d6173

https://segmentfault.com/q/1010000011537852

https://segmentfault.com/a/1190000011978825

https://github.com/answershuto/learnVue/blob/master/vue-src/core/components/keep-alive.js

(13) vue初始化

https://cn.vuejs.org/v2/guide/instance.html#%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E5%9B%BE%E7%A4%BA

vue整体生命周期分为4阶段:初始化(new Vue() 到created之前) / 模板编译(created到beforeMount之前) / 挂载(beforeMount到mounted) / 卸载(beforeDestroy到destroyed)

初始化目的:在vue.js实例上初始化一些属性-事件-响应式数据如props-methods-data-computed-watch-provide-inject等

https://blog.csdn.net/a419419/article/details/90764860

https://blog.csdn.net/qq_20143169/article/details/83745727

(14)vue 同步更新 异步更新

异步更新队列指的是当状态发生变化时,Vue异步执行DOM更新。

Vue的dom更新是异步的,当数据发生变化,vue并不是里面去更新dom,而是开启一个队列。

Vue.js实现了一个queue队列,在下一个tick的时候会统一执行queue中Watcher的run;数据变化时,根据响应式触发setter->Dep->Watcher->update->patch;

比如我们调用一个方法,同时涉及多个数据的操作改变,vue会把这一些列操作推入到一个队列中,相当于JavaScript的同步任务,在执行过程中可能会出现一些产生任务队列的异步任务,比如定时器、回调等。

在vue里面任务队列也叫事件循环队列。我们都知道JavaScript是循环往复的执行任务队列。Vue也一样,在一个同步任务过程中是不会去更新渲染视图,而是在同步任务(事件循环队列)执行完毕之后,在主线程的同步执行完毕,读取任务队列时更新视图。

需要先渲染数据然后操作dom,这时候就要使用vue提供的nextTick函数(当你需要数据先渲染,然后去操作渲染完成之后的dom,要把操作dom的逻辑写在这个函数里面):Vue.$nextTick(callback);

将状态改变之后想获取更新后的DOM,往往我们获取到的DOM是更新前的旧DOM,我们需要使用vm.$nextTick方法异步获取DOM;

复制代码
data() {
  return {
    message: '数据更新前no'
  }
},
edit(){
  this.message = '数据更新后have';
  console.log(document.getElementById('message').innerHTML);
  //数据更新前no
  this.$nextTick(() => {
    // 这个函数就是有了数据之后,渲染完成之后会执行,也就是说当你需要数据先渲染,
    // 然后去操作渲染完成之后的dom,要把操作dom的逻辑写在这个函数里面
    console.log(document.getElementById('message').innerHTML);
    // 数据更新后have1
  });
  this.message = '数据更新后have1';
}
// 多次修改了状态,但其实Vue只会渲染一次
复制代码
 

Vue优先将渲染操作推迟到本轮事件循环的最后,如果执行环境不支持会降级到下一轮;Vue的变化侦测机制决定了它必然会在每次状态发生变化时都会发出渲染的信号,但Vue会在收到信号之后检查队列中是否已经存在这个任务,保证队列中不会有重复。如果队列中不存在则将渲染操作添加到队列中;之后通过异步的方式延迟执行队列中的所有渲染的操作并清空队列,当同一轮事件循环中反复修改状态时,并不会反复向队列中添加相同的渲染操作;在使用Vue时,修改状态后更新DOM都是异步的。

当某个响应式数据发生变化的时候,它的setter函数就会通知闭包中的Dep,Dep则会触发对应的Watcher对象的update方法

猜你喜欢

转载自www.cnblogs.com/mmnxfgdfgx/p/12078506.html