Glittering's blog Glittering's blog
Home
  • 学习手册

    • 《JavaScript教程》
    • 《ES6 教程》
    • 《TypeScript 从零实现 axios》
    • 《Git》
    • TypeScript
  • 技术文档
  • 算法
  • 工作总结
  • 实用技巧
  • collect
About
  • Classification
  • Label
GitHub (opens new window)

Glitz Ma

前端开发工程师
Home
  • 学习手册

    • 《JavaScript教程》
    • 《ES6 教程》
    • 《TypeScript 从零实现 axios》
    • 《Git》
    • TypeScript
  • 技术文档
  • 算法
  • 工作总结
  • 实用技巧
  • collect
About
  • Classification
  • Label
GitHub (opens new window)
  • 技术文档

  • 算法

  • 工作总结

    • 时区校正
    • 上传下载文件方式总结
    • webpack优化实践
    • webpack基础应用知识总结
    • vue常见原理总结
    • vue基础知识总结
      • 基础知识
        • 模板
        • 指令
        • 常用指令缩写
        • 自定义指令
        • computed 和watch
        • v-for循环
        • 生命周期
        • 组件通信
        • 自定义组件v-model
        • nextTick
        • 动态组件&异步组件
        • keep-alive
        • mixin
        • vue-router
        • router实例方法
        • 完整的导航解析流程
        • vue3的差异
        • 父子组件传参
        • 定义props
        • 其它
    • react高级特性
    • react基础知识总结
    • 微前端总结
    • 今天总结一下用到的css吧
    • 地图标绘--射线法来计算点在多边形内
    • web异常监控和分析
    • 工作中遇到的css问题记录
    • axios 与 promise
    • 前端优化指南
    • 小程序笔记
    • JS随机打乱数组
    • 非常实用的JavaScript一行代码
  • 实用技巧

  • 收藏夹

  • 技术
  • 工作总结
mamingjuan
2021-03-02
目录

vue基础知识总结

# 基础知识

# 模板

vue.js的模板使用了基于html的模板语法,在模板中数据的绑定是使用"Mustache"语法(双括号)。双括号会将数据解析成普通文本,而非html代码,如果想输出真正的html需要使用v-html,双括号语法不能作用于Html Attribute上,这种情况需要借助v-bind指令。

<div v-bind:id="dynamicId"></div>
1

如果 dynamicId 的值是 null、undefined 或 false,则 disabled attribute 甚至不会被包含在渲染出来的 <div> 元素中。

在模板提供了完全的表达式支持,但每个绑定只能包含单个表达式,所以下面不会生效

<!-- 这是语句,不是表达式 -->
{{ var a = 1 }}

<!-- 流控制也不会生效,请使用三元表达式 -->
{{ if (ok) { return message } }}
1
2
3
4
5

提示

模板表达式都放在沙盒中,只能访问全局变量的一个白名单,不要在模板中试图访问用户自定义的全局变量

# 指令

指令 (Directives) 是带有 v- 前缀的特殊 attribute。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。

常用指令

  • v-if 条件渲染指令,代表存在和销毁
  • v-bind绑定指令,用来绑定属性(简写:)
  • v-on事件绑定指令(简写@)
  • v-for 循环指令
  • v-text 内容显示为文本相当于{{}}
  • v-html内容按普通 HTML 插入
  • v-once只渲染一次

# 常用指令缩写

<!-- 完整语法 -->
<a v-bind:href="url">...</a>

<!-- 缩写 -->
<a :href="url">...</a>

<!-- 动态参数的缩写 (2.6.0+) -->
<a :[key]="url"> ... </a>
1
2
3
4
5
6
7
8
<!-- 完整语法 -->
<a v-on:click="doSomething">...</a>

<!-- 缩写 -->
<a @click="doSomething">...</a>

<!-- 动态参数的缩写 (2.6.0+) -->
<a @[event]="doSomething"> ... </a>
1
2
3
4
5
6
7
8

# 自定义指令

在 Vue 2,自定义指令是通过使用下面列出的钩子来创建的,这些钩子都是可选的

  • bind - 指令绑定到元素后发生。只发生一次。
  • inserted - 元素插入父 DOM 后发生。
  • update - 当元素更新,但子元素尚未更新时,将调用此钩子。
  • componentUpdated - 一旦组件和子级被更新,就会调用这个钩子。
  • unbind - 一旦指令被移除,就会调用这个钩子。也只调用一次。
<p v-highlight="yellow">高亮显示此文本亮黄色</p>

1
2
Vue.directive('highlight', {
  bind(el, binding, vnode) {
    el.style.background = binding.value
  }
})
1
2
3
4
5

# computed 和watch

computed:🎉:

  1. 计算属性的结果会被缓存,除非依赖的响应式 property 变化才会重新计算。
  2. 不支持异步,当computed内有异步操作时无效,无法监听数据的变化
  3. 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用computed

模板内的表达式逻辑复杂时应当使用computed计算属性。
它为什么需要缓存:因为一个开销比较大的计算属性A,它需要遍历一个巨大的数组做大量计算,然后我们有其它属性依赖于A。如果它没有做缓存,我们将不可避免的多次执行A的getter。

watch:🎉:

  1. 不支持缓存,数据变,直接会触发相应的操作
  2. watch支持异步
  3. 当一个属性发生变化时,需要执行对应的操作;一对多
  4. 监听数据必须是data中声明过或者父组件传递过来的props中的数据,当数据变化时,触发其他操作,函数有两个参数:
  5. watch加immediate才能触发立即执行 immediate:组件加载立即触发回调函数执行,
    deep: 深度监听,为了发现对象内部值的变化,复杂类型的数据时使用,例如数组中的对象内容的改变,注意监听数组的变动不需要这么做。注意:deep无法监听到数组的变动和对象的新增,参考vue数组变异,只有以响应式的方式触发才会被监听到。

# v-for循环

v-for指令循环要携带key,因为dom做diff对比时,会把开始和结束做一系列对比,命中则拿新节点的key对比旧节点的key,未对应上会new element,如果key对应上再用旧节点的tag和新节点的tag是同样的,会认为这是一个旧节点。减少对比次数。

# 生命周期

beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、activated、deactivated、beforeDestroy、destroyed、errorCaptured

一般将ajax请求放到mounted中,放到create中会被重复执行

生命周期函数

在父子组件中,在某个阶段,会先执行父组件生命周期,再执行子组件生命周期,子组件先完成生命周期然后父组件完成生命周期如:

父beforeCreate 父created

子beforeCreate 子created

beforeCreate 数据检测事件配置之前调用

create 数据监测、事件(watch、event)生效后调用

beforeMount 相关的render首次被调用后执行

mounted 实例挂载后调用

beforeUpdate 数据更新时调用,在虚拟dom打补丁之前,适合更新访问现有dom,比如移除已添加的事件监听

update 数据已经更新,避免此期间更改状态。update不保证所有子组件的更新,如果想在整个视图绘制完毕可以在updated中用vm.$nextTick中执行

activated 被keep-alive缓存的组件激活时调用

deactivate 被keep-alive缓存的组件停用时调用

beforeDestroy 实例销毁之前调用,在这一步实例仍然完全可用。解绑自定义事件event.$off、清除定时器、解绑自定义的dom事件,如window.scroll等

destroyed 实例销毁后调用对应的vue指令解绑、事件监听移除、子实例也被销毁。

errorCaptured 捕获一个来自子孙组件的错误时调用。此钩子会收到三个参数:错误对象、发生错误的组件实例、错误来源信息字符串。此钩子可以返回false以阻止该错误继续向上传播。

# 组件通信

  1. 父组件A向子组件B传递数据可以通过props向下传递给子组件。不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。

提示

注意在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变变更这个对象或数组本身将会影响到父组件的状态。

  1. 子组件向父组件传递值通过事件的形式。子组件通过$emit向父组件调用时携带的事件发送数据
  2. 通过sync修饰符对一个prop进行双向数据绑定 相当于第1,2的简写方式
// 子组件
this.$emit('update:title', newTitle)
// 父组件
<text-document
  v-bind:title="doc.title"
  v-on:update:title="doc.title = $event"
></text-document>
// 简写
<text-document v-bind:title.sync="doc.title"></text-document>
4. 自定义事件
5. slot 作用域插槽
6. vuex
    //修改方式: 1)可以直接使用 this.$store.state.变量 = xxx;
                2)this.$store.dispatch(actionType, payload) 
                   或者:this.$store.commit(commitType, payload)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 自定义组件v-model

一个组件v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像单选框、复选框等类型的输入控件可能会将 value attribute 用于不同的目的。model 选项可以用来避免这样的冲突:

// 子组件
Vue.component('base-checkbox', {
  model: {
    prop: 'checked',
    event: 'change'
  },
  props: {
    checked: Boolean
  },
  template: `
    <input
      type="checkbox"
      v-bind:checked="checked"
      v-on:change="$emit('change', $event.target.checked)"
    >
  `
})

// 父组件

<base-checkbox v-model="lovingVue"></base-checkbox>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# nextTick

vue是异步渲染的,data修改后dom不会李可渲染。$nextTick会在dom渲染之后被触发。以获取最新的dom节点。
Vue 会根据当前浏览器环境优先使用原生的 Promise.then 和 MutationObserver,如果都不支持,就会采用 setTimeout 代替,目的是 延迟函数到 DOM 更新后再使用。

Vue $nextTick 原理 (opens new window)

# 动态组件&异步组件

  • 动态组件
<!-- 失活的组件将会被缓存!-->
<keep-alive>
  <!-- 动态组件 -->
  <component v-bind:is="currentTabComponent"></component>
</keep-alive>
1
2
3
4
5
  • 异步组件 在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块。为了简化,Vue 允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。
Vue.component('async-example', function (resolve, reject) {
  setTimeout(function () {
    // 向 `resolve` 回调传递组件定义
    resolve({
      template: '<div>I am async!</div>'
    })
  }, 1000)
})

Vue.component('async-webpack-example', function (resolve) {
  // 这个特殊的 `require` 语法将会告诉 webpack
  // 自动将你的构建代码切割成多个包,这些包
  // 会通过 Ajax 请求加载
  require(['./my-async-component'], resolve)
})

Vue.component(
  'async-webpack-example',
  // 这个动态导入会返回一个 `Promise` 对象。
  () => import('./my-async-component')
)

new Vue({
  // ...
  components: {
    'my-component': () => import('./my-async-component')
  }
})

const AsyncComponent = () => ({
  // 需要加载的组件 (应该是一个 `Promise` 对象)
  component: import('./MyComponent.vue'),
  // 异步组件加载时使用的组件
  loading: LoadingComponent,
  // 加载失败时使用的组件
  error: ErrorComponent,
  // 展示加载时组件的延时时间。默认值是 200 (毫秒)
  delay: 200,
  // 如果提供了超时时间且组件加载也超时了,
  // 则使用加载失败时使用的组件。默认值是:`Infinity`
  timeout: 3000
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

提示

注意如果你希望在 Vue Router 的路由组件中使用上述语法的话,你必须使用 Vue Router 2.4.0+ 版本。

# keep-alive

  • 缓存组件
  • 频繁切换不需要重复渲染
  • 与v-show的区别是,v-show是css样式来实现,keep-alive是vue层的控制,简单的可用v-show,像tab切换这种复杂的可以keep-alive

Props:

  • include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
  • exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
  • max - 数字。最多可以缓存多少组件实例。

include 和 exclude prop 允许组件有条件地缓存。二者都可以用逗号分隔字符串、正则表达式或一个数组来表示:

<!-- 逗号分隔字符串 -->
<keep-alive include="a,b"> // 这里的a,b是注册的component名
  <component :is="view"></component>
</keep-alive>

<!-- 正则表达式 (使用 `v-bind`) -->
<keep-alive :include="/a|b/">
  <component :is="view"></component>
</keep-alive>

<!-- 数组 (使用 `v-bind`) -->
<keep-alive :include="['a', 'b']">
  <component :is="view"></component>
</keep-alive>
1
2
3
4
5
6
7
8
9
10
11
12
13
14

有keep-alive时生命周期:
加载时:parent-beforeCreate ==> parent-created ==> parent-beforeMount ==> child-beforeCreate ==> child-created ==> child-beforeMount ==> child-Mounted ==> child-actived ==> parent-mounted

销毁时:parent-beforeDestroy==> child-deactived ==> child-beforeDestroy ==> child-Destroyed ==> parent-Destroyed

# mixin

将多个组件相同的逻辑抽离出来放到一起,引用的钩子,按照传入顺序依次调用,并在调用组件钩子之前会被调用。

问题:

  • 变量来源不明确
  • 多个可能会有变量冲突
  • 和组件可能出现多对多关系,复杂度较高

# vue-router

vue-router的模式分三种:"hash" | "history" | "abstract"
默认值是: "hash" (浏览器环境) | "abstract" (Node.js 环境)

  • hash: 使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器。
  • history: 依赖 HTML5 History API 和服务器配置。查看 HTML5 History 模式 (opens new window)。
  • abstract: 支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式。

创建路由常用参数

  • path 路径
  • name 命名路由
  • components 命名视图组件
  • meta
  • alias
  • children 嵌套路由
  • redirect 重定向
  • props // 2.6.0+
  • caseSensitive?: boolean, // 匹配规则是否大小写敏感?(默认值:false)
  • pathToRegexpOptions?: Object // 编译正则的选项

# router实例方法

全局钩子函数

  • router.beforeEach 全局前置守卫,在路由切换开始时调用,必须调用 next。当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中。

next方法

next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。

next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。

next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: 'home' 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。

next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。

  • router.beforeResolve 全局解析守卫,必须调用 next

在 2.5.0+ 你可以用 router.beforeResolve 注册一个全局守卫。这和 router.beforeEach 类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。

  • router.afterEach 路由切换离开时调用,钩子不会接受 next 函数也不会改变导航本身
  • router.beforeEnter 路由独享的守卫。这些守卫与全局前置守卫的方法参数是一样的。
  • 组件内的守卫
    • beforeRouteEnter
    • beforeRouteUpdate (2.2 新增)
    • beforeRouteLeave
      const Foo = {
      template: `...`,
      beforeRouteEnter(to, from, next) {
        // 在渲染该组件的对应路由被 confirm 前调用
        // 不!能!获取组件实例 `this`
        // 因为当守卫执行前,组件实例还没被创建
        // 你可以通过传一个回调给 next来访问组件实例。
      },
      beforeRouteUpdate(to, from, next) {
        // 在当前路由改变,但是该组件被复用时调用
        // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
        // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
        // 可以访问组件实例 `this`
      },
      beforeRouteLeave(to, from, next) {
        // 导航离开该组件的对应路由时调用
        // 可以访问组件实例 `this`
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
  • 编程式导航
    • router.push
    • router.replace
    • router.go
    • router.back

局部到单个路由

  • beforeEnter

组件的钩子路由

  • beforeRouterEnter
  • beforeRouterUpdate
  • beforeRouterLeave

🎉 全局路由的使用

router.beforeEach((to, from, next) =>{
  // to 表示即将进入的目标对象
  // from 当前要离开的路由对象
  // next 是一个函数 调用resolve 执行下一步
})
1
2
3
4
5

# 完整的导航解析流程

钩子被触发顺序: beforeEach全局路由守卫 => beforeEnter 路由独享守卫 => beforeRouteEnter 组件内的守卫 => beforeResolve全局解析守卫 => afterEach全局后置钩子

提示

如果是根路径的话beforeEnter路由独享守卫不会执行。
如果是一个组件路由切换到另外一个组个,会先触发组件内beforeRouteLeave再触发beforeEach全局路由守卫

  1. 导航被触发。
  2. 在失活的组件里调用 beforeRouteLeave 守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
  5. 在路由配置里调用 beforeEnter。
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter。
  8. 调用全局的 beforeResolve 守卫 (2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

# vue3的差异

  1. 去掉了filters
  2. 以前用prototype,现在不建议用
  3. ref不能直接使用
  4. 添加了setup
  5. 添加组合式api
  6. 生命周期
  7. 定义props的方式不同

# 父子组件传参

Vue 3中使用 emit 和 update: 来实现父子组件之间的数据传递。

update: 用于在父组件中通过 v-model 绑定子组件的数据,并在子组件中触发一个 update:modelValue 事件来更新数据。例如,在子组件中:

<!-- 子组件 -->
<template>
  <input :value="modelValue" @input="updateValue">
</template>

<script>
export default {
  props: ['modelValue'],
  methods: {
    updateValue(event) {
      this.$emit('update:modelValue', event.target.value);
    }
  }
}
</script>

<!-- 父组件 -->
<template>
  <child-component v-model="data"></child-component>
</template>

<script>
export default {
  data() {
    return {
      data: 'initial data'
    }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 定义props

<!--  子组件声明了的 props ,若父组件未传,则该值为 undefined -->
<script lang='ts' setup>
const props = defineProps({
  child: {
    type:String, // 参数类型
    default: 1, //默认值
    required: true, //是否必传
    validator: value => {
      return value >= 0 // 除了验证是否符合type的类型,此处再判断该值结果是否符合验证
    }
  },
  sda: String, //undefined
  strData: String,
  arrFor: Array
})
</script>

<!-- 类型声明方式 -->
<script lang='ts' setup>
const props = defineProps<{
  either: '必传且限定'|'其中一个'|'值', // 利用TS:限定父组件传 either 的值
  child?: string|number,
  strData?: string,
  arrFor: any[]
}>();
console.log(props);
</script>

<!-- 类型声明添加默认值 :默认值为引用类型的,需要包装一个函数 return 出去。-->
<script lang='ts' setup>
interface Props {
  either: '必传且限定'|'其中一个'|'值', // 利用TS:限定父组件传 either 的值
  child: string|number,
  sda?: string, // 未设置默认值,为 undefined
  strData: string,
  msg?: string
  labels?: string[],
  obj?:{a:number}
}
const props = withDefaults(defineProps<Props>(), {
  msg: 'hello',
  labels: () => ['one', 'two'],
  obj: () => { return {a:2} }
})
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

# 其它

  1. 从elementUi中el-dialog转到elementPlus后,visible.sync修改为v-model="visible"
上次更新: 2025/04/07, 01:42:58
vue常见原理总结
react高级特性

← vue常见原理总结 react高级特性→

Copyright © 2015-2025 Glitz Ma
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式