# Part6-开发常用知识点

## 一、Devtools与Vscode开发配置（了解）

Vue-Devtools是vue在做项目开发时常用的浏览器插件。

### 1、安装方式

* 升级浏览器至最新版

[![](https://tva1.sinaimg.cn/large/008eGmZEgy1gpq8atz03bj31d10u010b.jpg)](https://tva1.sinaimg.cn/large/008eGmZEgy1gpq8atz03bj31d10u010b.jpg)

* 点击浏览器右上角的 `三个点` ，在 `更多工具` 中，选择 `扩展程序`
* 打开 `扩展程序` 的 `开发者模式`

[![image-20200718142634459](https://tva1.sinaimg.cn/large/008eGmZEgy1gpq8aw9wqnj31d30u0k8k.jpg)](https://tva1.sinaimg.cn/large/008eGmZEgy1gpq8aw9wqnj31d30u0k8k.jpg)

* 将 `Vue.js Devtools_5.3.3.crx` 这个插件直接拽入当前界面，即可完成安装

### 2、打开控制台测试devtools

随意写一个点击事件，并查看点击效果

[![https://tva1.sinaimg.cn/large/008eGmZEgy1gpq8avnnyej30go0a6glw.jpg](https://tva1.sinaimg.cn/large/008eGmZEgy1gpq8avnnyej30go0a6glw.jpg)](https://tva1.sinaimg.cn/large/008eGmZEgy1gpq8avnnyej30go0a6glw.jpg)

### 3、VsCode用户片段

```json
{
  "demo": {
    "prefix": "vue",
    "body": [
      "<template>",
      " <div class=\"$1\">",
      " ",
      " </div>",
      "</template>",
      " ",
      "<script>",
      "export default {",
      " data () {",
      " return {\n",
      " }",
      " }",
      "}",
      "</script>",
      " ",
      "<style lang = \"less\" scoped>",
      " ",
      "</style>"
    ],
    "description": "自定义的一个vue代码段"
  }
}
```

### 4、@路径指向

* 安装插件：Path Intellisense
* 配置：

```json
"path-intellisense.mappings": {
  "@": "${workspaceRoot}/src"
}
```

* 在项目package.json所在同级目录下创建文件jsconfig.json：

```json
{
    "compilerOptions": {
        "target": "ES6",
        "module": "commonjs",
        "allowSyntheticDefaultImports": true,
        "baseUrl": "./",
        "paths": {
          "@/*": ["src/*"]
        }
    },
    "exclude": [
        "node_modules"
    ]
}
```

## 二、Watch（熟悉）

watch的作用可以监控一个值的变换，并调用因为变化需要执行的方法。可以通过watch动态改变关联的状态。

简单点说，就是实时监听某个数据的变化。

### 1、普通监听

```
<template>
  <div class="home">
    <input type="text" v-model="msg">
    <h3>{{msg}}</h3>
  </div>
</template>

<script>
export default {
  name: 'Home',
  data(){
    return {
      msg: "你好，世界"
    }
  },
  watch: {
    msg(val, oldVal){
      console.log(val, oldVal)
    }
  }
}
</script>
```

### 2、立即监听

如果我们需要在最初绑定值的时候也执行函数，则就需要用到immediate属性。

```
<template>
  <div class="home">
    <input type="text" v-model="msg">
  </div>
</template>

<script>
export default {
  name: "Home",
  data() {
    return {
      msg: "你好"
    };
  },
  watch: {
    msg: {
      handler(val, oldVal) {
        console.log(123)	// 在最初绑定时直接打印123
        // console.log(val, oldVal);
      },
      immediate: true
    }
  }
};
</script>
```

> immediate需要搭配handler一起使用，其在最初绑定时，调用的函数也就是这个handler函数。

### 3、深度监听

当需要监听一个对象的改变时，普通的watch方法无法监听到对象内部属性的改变，只有data中的数据才能够监听到变化，此时就需要deep属性对对象进行深度监听。

```
<template>
  <div class="home">
    <h3>{{obj.age}}</h3>
    <button @click="btnClick">按钮</button>
  </div>
</template>

<script>
export default {
  name: 'Home',
  data(){
    return {
      obj: {
        name: "Lucy",
        age: 13
      }
    }
  },
  methods: {
    btnClick(){
      this.obj.age = 33;
    }
  },
  watch: {
    obj: {
      handler(val, oldVal){
        console.log(val.age, oldVal.age)		//  33   33
      },
      deep: true
    }
  }
}
</script>
```

> 注意：
>
> 1、如果监听的数据是一个对象，那么 `immediate: true` 失效；
>
> 2、一般使用于对引用类型的监听，深度监听，如监听一个Object，只要Object里面的任何一个字段发生变化都会被监听，但是比较消耗性能，根据需求使用，能不用则不用。

### 4、deep优化

```
<template>
  <div class="home">
    <h3>{{obj.age}}</h3>
    <button @click="btnClick">按钮</button>
  </div>
</template>

<script>
export default {
  name: "Home",
  data() {
    return {
      obj: {
        name: "Lucy",
        age: 13
      }
    };
  },
  methods: {
    btnClick() {
      this.obj.age = 33;
    }
  },
  watch: {
    // 通过点语法获取对象中的属性，然后转为字符串，即是对深度监听的优化
    "obj.age": {
      handler(val, oldVal) {
        console.log(val, oldVal);
      },
      deep: true,
      immediate: true,		// 此时监听的数据不是一个对象，可以使用immediate
    }
  }
};
</script>
```

### 5、Watch与Computed的区别

* watch中的函数是不需要调用的，computed内部的函数调用的时候不需要加()
* watch(属性监听)，监听的是属性的变化，而computed(计算属性)，是通过计算而得来的数据
* watch需要在数据变化时执行异步或开销较大的操作时使用，而对于任何复杂逻辑或一个数据属性，在它所依赖的属性发生变化时，也要发生变化，这种情况下，我们最好使用计算属性computed。
* computed 属性的结果会被缓存，且computed中的函数必须用return返回最终的结果
* watch 一个对象，键是需要观察的表达式，值是对应回调函数。主要用来监听某些特定数据的变化，从而进行某些具体的业务逻辑操作；

### 6、Watch与Computed的使用场景

* computed 　　　
  * 当一个属性受多个属性影响的时候就需要用到computed
  * 最典型的例子： 购物车商品结算的时候
* watch
  * 当一条数据影响多条数据的时候就需要用watch
  * 搜索数据
* 总结：
  * 一个值受其他值的影响，用computed
  * 一个值将时刻影响其他值，用watch

## 三、Mixins混入（熟悉）

mixins就是定义一部分公共的方法或者计算属性,然后混入到各个组件中使用,方便管理与统一修改。同一个生命周期，混入对象会比组件的先执行。

### 1、导出mixins

在src下创建 `mixins/index.js`，写入：

```js
export const MixinsFn = {
    created() { 
        console.log("这是mixins触发的created")
    }
}
```

### 2、引用mixins

```
<template>
  <div class="about">
    <h1>This is an about page</h1>
  </div>
</template>
<script>
import { MixinsFn } from '@/mixins/index.js'
export default {
  created(){
    console.log("这是about触发的created")
  },
  mixins: [MixinsFn]
}
</script>
```

我们会发现，`mixins中的created` 比 `about中的created` 优先执行。

## 四、ref与$refs（掌握）

vue中获取页面里的某个元素（标签或组件），可以给它绑定ref属性，有点类似于给它添加id名。

### 1、简单使用

```
<template>
 <div class="">
     <h3 ref="title">{{msg}}</h3>
     <button @click="btnclick">按钮</button>
 </div>
</template>
 
<script>
export default {
  data() {
    return {
      msg: "你好"
    };
  },
  methods: {
    btnclick() {
      console.log(this.$refs.title);		// 得到h3标签
      console.log(this.$refs.title.innerHTML);		// 得到h3标签的文本
    }
  }
};
</script>
 
<style lang = "less" scoped>
</style>
```

### 2、子组件中的数据获取及方法调用

子组件：

```
<template>
 <div class="">
     <h4>{{num}}</h4>
 </div>
</template>
 
<script>
export default {
  data() {
    return {
      num: 100
    };
  },
  methods: {
      subFn(){
          console.log('子组件中的方法')
      }
  }
};
</script>
 
<style lang = "less" scoped>
</style>
```

父组件：

```
<template>
 <div class="">
     <Sub ref="sub" />
     <button @click="btnclick">按钮</button>
 </div>
</template>
 
<script>
import Sub from '../components/Sub'
export default {
  methods: {
    btnclick() {
      console.log(this.$refs.sub.num);	// 100
      this.$refs.sub.subFn();						// '子组件中的方法'
    }
  },
  components: {
      Sub
  }
};
</script>
 
<style lang = "less" scoped>
</style>
```

## 五、动态组件

类似于Tab栏这种效果，有时我们往往不会去使用v-show或v-if来动态切换，而会使用动态组件搭配 `:is` 属性来完成，效果如下：

[![](https://tva1.sinaimg.cn/large/008eGmZEgy1gpq8ax278hj30ai04g74a.jpg)](https://tva1.sinaimg.cn/large/008eGmZEgy1gpq8ax278hj30ai04g74a.jpg)

在学习动态组件之前，需要记住这点：

* :is属性中的值，必须是已注册组件的名字，或[一个组件的选项对象](https://github.com/Macharf/aka-note/blob/main/note/vue/组件的选项对象.md)

具体操作：

### 1、写好三个子组件的内容

```
<template>
 <div>Home子组件</div>
</template>

<template>
 <div>List子组件</div>
</template>

<template>
 <div>User子组件</div>
</template>
```

### 2、在父组件中引入并注册

```
<template>
 <div class="">
     <button @click="btnClick(1)" :style="{background: num==1 ? 'pink' : ''}">Home</button>
     <button @click="btnClick(2)" :style="{background: num==2 ? 'pink' : ''}">List</button>
     <button @click="btnClick(3)" :style="{background: num==3 ? 'pink' : ''}">User</button>
     <component :is="componentId"></component>
 </div>
</template>
 
<script>
import Home from "@/components/Home";
import List from "@/components/List";
import User from "@/components/User";
export default {
  data() {
    return {
      componentId: "Home",
      num: 1
    };
  },
  components: {
    Home,
    List,
    User
  },
  methods: {
    btnClick(arg) {
      this.num = arg;
      switch (arg) {
        case 1:
          this.componentId = "Home";
          break;
        case 2:
          this.componentId = "List";
          break;
        default:
          this.componentId = "User";
          break;
      }
    }
  }
};
</script>
```

### 3、动态组件其他应用场景

例如table、select这种外层HTML标签，他们内部的标签（如：tr、option等），是不能被单独拿出来作为组件放在他们内部的，否则会被作为无效的内容提升到外部，并导致最终渲染结果出错。而使用动态组件的 `:is` 属性，就可以解决这个问题。

需要注意的是**如果我们从以下来源使用模板的话，这条限制是\*不存在\*的**：

* 字符串 (例如：`template: '...'`)
* 单文件组件 (.vue)
* `<script type="text/x-template">`

## 六、KeepAlive

`<keep-alive>`是Vue的内置组件，能在组件切换过程中将状态保留在内存中，防止重复渲染DOM。

KeepAlive用于处理组件缓存。有什么用呢？想象一个业务场景：

> 在Login页面填写完手机号，发现未注册，需要跳去Register页面完成注册，再返回Login页面填写密码，进行登录，此时如果在Login页面没有保存好手机号，那么跳回来时，用户又得重新输入手机号。为了解决这样的需求，我们需要借助keep-alive。

### 1、keep-alive结合component

首先，创建 `a.vue`、`b.vue` 和 `c.vue` 三个组件，内容大致如下：

```
<template>
  <li>a组件 <input type="text"></li>
</template>
```

在某个页面（如：about.vue）中引入：

```
<template>
  <div class="about">
    <button @click="mycom='acom'">a组件</button>
    <button @click="mycom='bcom'">b组件</button>
    <button @click="mycom='ccom'">c组件</button>
    <ul>
      <component :is="mycom"></component>
    </ul>
  </div>
</template>

<script>
import acom from '../components/a'
import bcom from '../components/b'
import ccom from '../components/c'

export default {
  data(){
    return {
      mycom: "acom"
    }
  },
  components: {
    acom, bcom, ccom
  }
}
</script>
```

到目前为止，我们可以切换组件，但是在切换时却无法缓存组件内容，因此我们给 component 组件套一个 `keep-alive`：

```
<keep-alive>
   <component :is="mycom"></component>
</keep-alive>
```

这样我们在三个组件的 input 中输入的东西就会被缓存。

### 2、include与exclude

如果我们想缓存指定的组件，可以使用include：

```
<keep-alive :include="['acom', 'bcom']">
   <component :is="mycom"></component>
</keep-alive>
```

如果我们想排出部分组件的缓存，可以使用exclude：

```
<keep-alive :exclude="['ccom']">
   <component :is="mycom"></component>
</keep-alive>
```

### 3、keep-alive生命周期

Keep-alive 生命周期包含：`actived` 和 `deactivated`

找到 `a.vue`，写入：

```
<script>
export default {
    created(){
        console.log('a组件的created')
    },
    mounted(){
        console.log('a组件的mounted')
    },
    activated(){
        console.log('actived: 进入当前组件时触发')
    },
    deactivated(){
        console.log('deactivated：离开当前组件时触发')
    }
}
</script>
```

我们可以观察到两个现象：

* actived在created和mounted之后调用
* deactivated在离开组件时会触发，无论第几次进入组件，actived都会触发，但created和mounted只会触发一次

## 七、NextTick

### 1、数据更新延迟的问题

我们先来思考一个数据更新问题：

当我修改一个数据时，这个数据会立刻渲染到页面中吗？

```
<template>
 <div class="">
     <h2 ref="title">{{num}}</h2>
     <button @click="btnClick">按钮</button>
 </div>
</template>
<script>
export default {
  data() {
    return {
      num: 1
    };
  },
  methods: {
    btnClick() {
      this.num++;
      console.log(this.$refs.title.innerHTML);		// 1
    }
  }
};
</script>
```

很显然，这里打印出来的是视图更新前的数据，所以我们得出结论：

> 当侦听到你的数据发生变化时， Vue将开启一个队列（该队列被Vue官方称为异步更新队列）。视图需要等队列中所有数据变化完成之后，再统一进行更新。

### 2、NextTick使用方法

那么，如果我们想提前获取更新后的数据，就需要借助NextTick。来看看官方对NextTick的定义：

> 在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法，获取更新后的 DOM。

NextTick有两种基本使用方法：

```js
// 1、CallBack形式
this.$nextTick(()=>{
  console.log(this.$refs.title.innerHTML);
})

// 2、Promise形式
this.$nextTick().then(()=>{
  console.log(this.$refs.title.innerHTML);
})
```

我们来尝试修改刚刚的案例：

```
<template>
 <div class="">
     <h2 ref="title">{{num}}</h2>
     <button @click="btnClick">按钮</button>
 </div>
</template>
 
<script>
export default {
  data() {
    return {
      num: 1
    };
  },
  methods: {
    btnClick() {
      this.num++;
      console.log(this.$refs.title.innerHTML);		// 1
      // this.$nextTick(()=>{
      //   console.log(this.$refs.title.innerHTML);	// 2
      // })
      this.$nextTick().then(()=>{
        console.log(this.$refs.title.innerHTML);	// 2
      })
    }
  }
};
</script>
```

### 3、应用场景

1、如果要在`created()`钩子函数中进行的`DOM`操作，由于`created()`钩子函数中还未对DOM进行任何渲染，所以无法直接操作dom，需要通过`$nextTick()`来完成。

```js
created () {
    this.$nextTick(()=>{
        this.$refs.ptag.innerText = "一个p标签"
    })
}
```

注：在`created()`钩子函数中进行的`DOM`操作，不使用`$nextTick()`会报错：

```js
//  Error in created hook: "TypeError: Cannot set property 'innerText' of undefined"
created(){
    this.$refs.ptag.innerText = "一个p标签"
}
```

## 八、Transition

Css中有个transition属性，我们称为过渡属性。Vue中也有个transition，我们将它做为标签。

`<transition>` 元素作为**单个**元素/组件的过渡效果。`<transition>` 只会把过渡效果应用到其包裹的内容上，而不会额外渲染 DOM 元素，也不会出现在可被检查的组件层级中。

我们来看看怎么写：

```
<button @click="flag = !flag">切换h3显示</button>
<transition name="fade">
  <h3 v-show="flag">你好，标题</h3>
</transition>

<style lang="less" scoped>
.fade-enter, .fade-leave-to{
  opacity: 0;
}
.fade-enter-active, .fade-leave-active{
  transition: opacity 1s;
}
</style>
```

我们来看看这个图：

[![](https://tva1.sinaimg.cn/large/008eGmZEgy1gpq8ausqjjj30fe07pjrp.jpg)](https://tva1.sinaimg.cn/large/008eGmZEgy1gpq8ausqjjj30fe07pjrp.jpg)

一个完整的**transiton组件的class类**，有6个，通过指定不同的class来添加样式来达到过渡的CSS效果。

1. v-enter：定义进入过渡的开始状态。
2. v-enter-active：定义进入过渡生效时的状态。这个类可以被用来定义进入过渡的过程时间，延迟和曲线函数。
3. v-enter-to: 定义进入过渡的结束状态。
4. v-leave: 定义离开过渡的开始状态。
5. v-leave-active：定义离开过渡生效时的状态。这个类可以被用来定义离开过渡的过程时间，延迟和曲线函数。
6. v-leave-to: 定义离开过渡的结束状态。

默认类名是以上6个，如果在transition标签中指定了name=’xxx’,那么6个类名将会变成xxx开头的，例如xxx-enter-active。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://gb.akanote.cn/vue/day06.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
