Vue-基础知识

2020/7/12 Vue
// 兼容性
Vue2:IE9 及以上
Vue3:不支持 IE
----------------------------------------------------------------------------------------------
// 单页应用和多页应用
1. 多页应用实际请求html页面,seo好,首页块,切换慢
2. 单页应用实际请求js动态渲染,seo差,首页慢,切换块
3. 单页应用也叫SPA
----------------------------------------------------------------------------------------------
1. new Vue({
  el: #id, // 挂载点
  data: {}, // 变量定义
  computed: {}, // 计算属性
  methods: {}, // 事件函数
  watch: {}, // 监听
  filters: {} // 过滤器
})
2. 插件表达式: {{ `data中的变量` }}
3. v-text="data中的变量" // 不能解析html标签
4. v-html="data中的变量" // 可以解析html标签,注意 xss 攻击,这个不会自动做转换标签,插值表达式会自动转换
5. v-on的简写@,用于监听事件
6. v-bind的简写:,用于绑定变量
7. v-model,用于form双向绑定
8. v-if,只有当表达式或变量是true的时候才显示,否则会移除DOM
9. v-show,只有当表达式或变量是true的时候才显示,否则会加上display: none
10. <div v-for="(item, index) of list">
      {{ item }} // 循环的val值
      {{ index }} // 循环的下标
    </div>
11. vue的实例相当于vue的组件,两者相等,子组件也有data、methods等属性
----------------------------------------------------------------------------------------------
1. vue改变对象或数组内容的方法: vue.set/vm.$set
2. 在使用组件时当标签写在<ul><table>等的标签内时可使用<li is="组件名"></li>符合h5规范
3. 获取div上的元素时使用ref属性,在函数中使用this.$refs.属性名获取
4. 当ref属性在组件上的时候获取的是它的引用而不是DOM元素 //  ref 在 v-for 取值时是数组而不是单一对象
5. 在除了根组件中的data是个函数,而不是对象
6. 在vue实例下以$开头的都是vue的属性或方法
7. 在使用if/for指令时可在其内容中加上key值表唯一
8. 在操作数组或对象时,不可以直接改变下标修改值,应使用其自带的方法或改变其引用进行修改,也可使用1.
9. 在使用for指令时,可以使用<template></template>占位符进行包裹,它不会再DOM中显示
10. for指令也可以对对象进行循环<div v-for="(val, key, index) of obj"></div>
11. 子组件不可直接修改父组件传递过来的值,如非要修改则需要在子组件的data中创建一个副本
12. props: {
      content: {
        type: [String, Number], // 类型
        required: true, // 必传
        default() {
          return arr/obj // 如果默认值是数组或对象则要写成函数形式,其他类型直接给默认值
        },
        validator: function(val) {
          return val.length > 5 // 自定义校验
        }
      }
    }
13. props特性: 父传子接,在子组件中可以直接使用传过来的内容,传递属性的参数和内容不会显示在DOM标签上
14. 非props特性: 父传子不接,在子组件中不可以使用传的内容,属性值会显示在DOM标签上
15. 给组件绑定原生的事件,正常需要通过<组件名 @click.native="handleClick"></组件名>事件修饰符native
16. 组件插槽: 当子组件的内容需要由父组件内容决定时使用
    子组件定义: <slot name="名称">默认值</slot>
    父组件使用: <子组件><div slot="名称">内容</div></子组件>
17. 作用域插槽: 在子组件的插槽上进行循环,把值用属性的方式传递
    子组件定义: <slot :item="item"></slot>
    父组件使用: <子组件><template slot-scope="传递过来的值"><div>{{ 传递过来的值.item }}</div></template></子组件>
18. 动态组件: 通过在父组件中使用<component></component>
    <component :is="type" /> 再结合对type数据的更改进行显示不同的组件,type是组件名
19. 在子组件的模板上加上v-noce指令会被载入内存,里面的数据只绑定一次,不会刷新,只渲染一次,后面发生变化也不会改变
20. 通过总线进行非父子组件传值,在main.js中引入
    Vue.prototype.bus = new Vue()
    在函数中触发: this.bus.$emit('change', this.content)
    在生命周期中监听: this.bus.$on('change', function(val) { console.log(val) })
    要在emit的组件中的destroyed方法中去解绑事件,否则会造成内存泄露 // 不解绑事件会造成绑定事件的node节点无法回收,造成DOM泄漏
21. 过渡: 显示——隐藏,隐藏——显示,在标签上用<transition>内容</transition>包住
    .v-leave-active,
    .v-enter-active{
      transition: opacity 1s // 也可用animate动画
    }
    .v-leave-to,
    .v-enter{
      opacity: 0
    }
22. 使用animate库动画:<transition>标签上自定义class<transition enter-active-class="animated 动画名 也可用继续写transition过渡动画" />
    <transition leave-active-class="animated 动画名 也可用继续写transition过渡动画" />
    可以嵌套
    在<transition>标签内可写自定义属性和两种动画一起使用时type="transition",表示持续时间由transition决定
23. js动画:<transition>中定义事件,el、done为函数接收的参数
    @before-enter="函数名(el)"、@enter="函数名(el, done)"、@after-enter="函数名(el)"
    @before-leave="函数名(el)"、@leave="函数名(el, done)"、@after-leave="函数名(el)"
24. velocity.js: 动画库,写在@enter/@levave中
    velocity(el, { opacity: 1 }, { duration: 1000, complate: done })
    如果自己写要手动执行done()
25. 多个元素或组件的过渡,多个元素时需要添加属性,key值表示唯一值
    在<transition>加mode="in-out/out-in" // 先进入再隐藏/先隐藏再进入
    动态组件也是在外面加上<transition>
26. 列表过渡: 在列表外包一层<transition-group>循环列表,一样在样式中定义过渡效果,会多一个 v-move 类名
27. 使用@keyframes 动画名{ 0% { 样式 } 50% { 样式 } 100% { 样式 }}在样式中正常使用
28. 通过在<transition>标签内增加appear和appear-active-class="动画名"实现页面刚加载第一次就有动画
29. 动态路由: 点哪个请求哪个,接口参数不一样,在url上设置:to="'/' + id"
30. 按需加载: () => import('路径') // 路由或组件注册
31. key值也指唯一,vue会尽量复用DOM
32. 标签跳转: <router-link to="/" tag="li"></router> // tag="li" 可以变成li标签
33. 可以使用<keep-alive exckude="不被缓存的组件名"></keep-alive>实现页面ajax缓存,会一个activated()生命周期函数
34. 组件的name常用于递归组件,自己调用自己
----------------------------------------------------------------------------------------------
1. this.$on('事件名称', 处理函数) // 定义事件,可以定义多个事件名称为数组,也可以为同一个事件定义多个处理函数
2. this.$emit('事件名称', '传递参数') // 触发事件,有try catch,有异常不会中断执行
3. Vue.directive // 自定义指令(bind、inserted、update、componentUpdated、unbind)
4. Vue.component // 定义组件
5. Vue.extend // 生成组件的构造方法
6. Vue.use // 使用插件
7. provide和inject // 组件通信
8. Vue.$mount // 挂载
9. filters // 过滤器
10. watch // 监听器
11. vue.observable // 相当于vuex
12. v-slot
  具名插槽:
    组件定义:
              <div>
                <slot name="header">默认内容</slot>
                <slot>默认内容</slot>
                <slot name="footer">默认内容</slot>
              </div>
    使用:
              <组件名>
                <template v-slot:header>
                  <div>将插入header的slot中</div>
                </template>
                <div>将插入没命名的slot中</div>
                <template v-slot:footer>
                  <div>将插入footer的slot中</div>
                </template>
              </组件名>
              在使用时'v-slot:'也可用#代替,: #header、#footer
  作用域插槽:
    组件定义:
              <div>
                <slot :list="data">
                  {{ data.id }} // 默认内容
                </slot>
              </div>
    使用:
              <组件名>
                <template v-slot="prop">
                  {{ prop.list.id }} // prop名字随意
                </template>
              </组件名>
----------------------------------------------------------------------------------------------
// 插槽(已废弃,建议使用上面)
1. 组件定义: <div><slot name="body"></slot></div> // 具名插槽
   使用: <组件名><div slot="body">内容</div></组件名>
2. 组件定义: <div><slot a="1" b="2"></slot></div> // 组件向父组件传值
   使用: <组件名><div slot-scope="obj">{{ obj.a/obj.b }}</div></组件名>
----------------------------------------------------------------------------------------------
1. const vm = new Vue({}) // vm === this
2. vm.$data // 返回一个对象,data里面的数据
3. vm.$props // 传递的值组件
4. vm.$el // 挂载到哪个的节点
5. vm.$options // 在new时传进去的整个对象,包括全部属性(不能通过$options.data修改)
6. vm.$root // 根节点的vue对象, vm.$root === vm
7. vm.$children // 组件里面的值或传递的值
8. vm.$slots // 插槽
9. vm.$scopedslots // 插槽
10. vm.$refs // 获得模板中的节点或组件中的实例引用
11. vm.isServer // 服务端渲染
12. vm.$watch('监听的名字', (newVal, oldVal) => {})
13. const unwatch = vm.$watch() // 执行unwatch取消监听,在options中写的watch会自动取消监听
14. vm.$on('事件名称', () => {}) // 定义事件,相当于@
15. vm.$emit('事件名称', '传递参数') // 触发事件,有try catch,有异常不会中断执行
16. vm.$once('事件名称', () => {}) // 定义只触发一次的事件
17. vm.forceUpdate() // 重新渲染页面
18. vm.$set(vm.obj, 'name', 'cj') // 修改vm.obj.name = cj
19. vm.$delete(vm.obj, 'name') // 删除vm.obj.name
20. vm.$nextTick() // vue进行DOM更新时执行
21. vm.$destroy() // 销毁实例
22. vue的事件绑定@click="handle(agruments, $event)"
    如果不加括号在methods方法中能拿到e事件参数,如果加了括号则需要把$event传进去,下面才能拿到e参数
    在第一个参数前加上agruments可拿到所有传递的参数
    event是原生的 // mouseEvent
    事件被挂载到当前元素
    e.target是事件触发的元素
    e.currentTarget是事件绑定的元素
    自定义事件可以通过 vue 示例的 $on 和 $emit 实现,在实战中没必要手动实现 bus 来监听触发,vue 本身已经支持
23. vm.$attrs和vm.$listeners
    所谓的$arrts其实就是多级组件中的props,它就像一个中间件,用来传递爷组件给孙组件的数据,使用的时候只需给父组件中的孙组件配置v-bind="$attrs",然后再爷组件中传入孙组件所需要的数据,孙组件正常接收即可,v-on="$listeners"也是类似
    1. 在 Vue3 中,已经把 $listeners 移除,把事件和属性全部挂载到 $attrs 里面了
       所以使用 v-bind="$attrs" 就相当于 Vue2 中使用了 v-bind 和 v-on 的集合
    2. 组件如果没有在 props 中声明对应的属性,父组件传递的属性就会挂载到 $attrs 里面
    3. 组件如果没有在 emits 中声明对应的方法,父组件监听的方法就会挂载到 $attrs 里面
    4. 如果 $attrs 有值,并且组件只有一个根节点,那么会把 $attrs 里面的属性挂载到 DOM 树上
       可以通过配置 inheritAttrs: false,属性就不会挂载到 DOM 树上
----------------------------------------------------------------------------------------------
// 生命周期
1. beforeCreate() // 初始化事件和生命周期后触发
  1. 创建一个空白的 Vue 示例
  2. data、method 尚未被初始化,不可使用
  3. 在 Vue3 的 Composition API  中使用 setup
2. create() // 初始化双向绑定和数据注入后触发
  1. Vue 实例初始化完成,完成响应式绑定
  2. data、method 都已经初始化完成,可调用
  3. 尚未开始渲染模板
  4. 在 Vue3 的 Composition API  中使用 setup
3. beforeMount() // 页面挂载之前
  1. 编译模板,调用 render 生成 vdom
  2. 还没有开始渲染 DOM
4. mounted() // 页面挂载之后
  1. 完成 DOM 渲染
  2. 组件创建完成
  3. 开始由创建阶段进入运行阶段
5. beforeUpdate() // 数据更新之前(DOM 未更新)
  1. data 发生变化之后
  2. 准备更新 DOM(尚未更新 DOM6. updated() //数据更新之后(DOM 已更新)
  1. data 发生变化,且 DOM 更新完成
  2. 不要再 updated 中修改 data,可能会导致死循环
7. activeted() // keep-alive 组件激活之前
8. deactivated() // keep-alive 组件停用时
9. beforeDestroy() // 实例销毁之前(DOM 未销毁)
  1. Vue3 中改名为 beforeUnmount
  2. 组件进入销毁阶段,尚未销毁,可正常使用
  3. 可移除、解绑一些全局事件、自定义事件
10. destroyed() // 实例销毁之后 (DOM 已销毁)
  1. Vue3 中改名为 unmounted
  2. 组件被销毁了
  3. 所有子组件也都被销毁了
  4. 事件解绑这边有可能解绑不到,最好放在 beforeUnmount 中
11. renderError(h, err) // render方法错误(本组件),开发环境使用
12. errorCaptured() // 收集错误,会向上冒泡,正式环境可以使用
13. 父组件子组件生命周期执行顺序
  父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted->
  父beforeUpdate->子beforeUpdate->子updated->父updated->父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
14. 注意:mounted 和 updated 都不能保证子组件全部挂载完成
    所有如果要操作 DOM,最好放在 $nextTick 回调中进行
----------------------------------------------------------------------------------------------
// computed/watch 不要去改变值,而是去执行操作
1. computed: {
  函数名() {
    return 'cj' // 相当于getter操作,有get、set操作(要写对象),计算修改值、拼接等
  }
}
2. watch: { // 监听的如果是引用类型,则拿不到oldVal,因为指向不一样
  obj/'obj.name': {
    handler(newVal, oldVal) {
      // 操作
    },
    immediate: true, // 马上执行,不等数据变化
    deep: true // 监听到子集
  }
}
----------------------------------------------------------------------------------------------
// 事件修饰符
1. @click.prevent // 阻止默认行为
2. @click.stop // 阻止冒泡
3. @click.capture // 变成捕获
4. @click.once // 只能触发一次
5. @click.self // 当e.target = e.cuttentTarget才触发
6. @click.right // 鼠标右键点击触发,还有middle、left
6. @keydown.enter.exact // 按下回车触发,还有tab、esc等,exact表示只能通过按下enter键触发,不加exact其他键和enter一起按也可以触发
7. @keydown.ctrl // 同时按下ctrl和其他键触发,还有shift、alt、meta等
----------------------------------------------------------------------------------------------
// v-model修饰符
1. v-model.lazy = "val" // 当鼠标失焦时触发而不是同步
2. v-model.number = "val" // 把val值转成数字,默认输入框中都是String
3. v-model.trim = "val" // 把val值的首尾空格去除
----------------------------------------------------------------------------------------------
// 自定义v-model
1. 在实现的组件中:
<template>
  <input
    type="text"
    :value="value"
    @input="$emit('change', $event.taeget.value)"
  />
</template>
export default {
  model: {
    prop: 'value', // 对应props中的value
    event: 'change' // 对应$emit的change
  },
  props: {
    value: { // 对应value的属性
      type: 'String',
      default: ''
    }
  }
}
2. 在使用的组件中:
  <组件名  v-model="value" />
3. 即可实现双向绑定,要在实现的组件中配置model
----------------------------------------------------------------------------------------------
// 其他指令
1. v-pre // 不会解析标签里面的值,会原样显示
2. v-once // 里面的数据只绑定一次,不会再渲染
----------------------------------------------------------------------------------------------
// 组件继承
1. const compVue = Vue.extend(组件名)
   new compVue({ el: #id })
   数据通过propsData,而不通过props
   data会覆盖父级的data
   父级的生命周期先执行,再执行子级
2. const compVue2 = { extends: 组件名, data() {} }
   new Vue({ el: #id, components: { compVue2 }})1一致
3. 当需要扩展组件时可以用extend
4. 可以通过this.$prent.$option去查看父级的东西或修改属性
5. 只有在new Vue时才能指定new Vue({ prent: prent })
----------------------------------------------------------------------------------------------
// provide和inject(非父子组件传递方法)
1. provide() { return { '传递的名字': this }} // 和生命周期平级,父级的this
2. inject: ['传递的名字'] // 在子组件中,即可使用this.传递的名字来获取父级的组件
----------------------------------------------------------------------------------------------
// 动态组件
1. <component :is="变量名" />
2. 通过is指定要渲染哪个组件,变量名指组件名,可以进行判断渲染哪个
----------------------------------------------------------------------------------------------
// keep-alive
1. keep-alive和v-show的区别,一个是js层面,一个是css层面
2. keep-alive作用是缓存组件,不会让组件执行destroyed,被缓存的组件重新激活也不会执行mounted
----------------------------------------------------------------------------------------------
// $nextTick
1. vue是异步渲染
2. data改变之后,DOM不会立刻渲染
3. $nextTick会在DOM渲染之后被触发,以获取最新DOM节点
4. 页面渲染时会将data的修改做整合,多次data修改只会渲染一次
5. this.$nextTick(() => { '在DOM渲染完触发回调' })
6. 在操作完DOM后马上获取DOM节点是获取不到全部的,需要通过nextTick回调获取
7. 在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的DOM结构的时候,这个操作都应该放进 nextTick() 的回调函数中
8. 原理
  1. Promise.then
  2. MutationObserver
  3. setImmediate
  4. setTimeout(fn, 0)
----------------------------------------------------------------------------------------------
// 其他
1. 在vue中使用obj: { 0: [], 1: [] } = data,obj[0] = data[0]这种方式不会让DOM重新渲染,需要用this.$set()方法进行赋值
2. 可以在vue实例中加入mixins这个对象进行混入,可以把vue实例中复用的代码抽离到公用的地方,再通过mixins进行混入
    1. mixins: ['导入的对象'],混入的内容是vue的options,比如data、methods等,也有Vue.mixin()全局混入的方法
    2. mixins的缺点:
        变量来源不明确,不利于阅读
        多个mixin可能会造成命名冲突
        mixin和组件可能出现多对多的关系,复杂度较高
    3. 组件 data、methods 优先级高于 mixin data、methods 优先级,即两边都有的情况下,组件会覆盖 mixin
    4. 生命周期函数会先执行 mixin 再执行组件里面的生命周期函数
    5. 自定义属性,组件的属性优先级高于 mixin 中的优先级 // 自定义属性通过 this.$options 获取
    6. 通过 app.config.optionMergeStrategies 修改合并规则,默认是组件的优先级更高
    app.config.optionMergeStrategies.number = (mixinVal, appValue) => { // number 为自定义属性
      return mixinVal || appValue
    }
3. 在写.env环境变量时变量要以VUE_APP开头
4. template只能使用本<script>中data,methods或者其他内部初始化的变量或者函数,不可以直接使用import进来的对象
5. 有些像 Sass 之类的预处理器无法正确解析 >>>。这种情况下你可以使用 /deep/::v-deep 操作符取而代之——两者都是 >>> 的别名
----------------------------------------------------------------------------------------------
// 脚手架
1. 要使用在public下的资源文件,需要通过在index.html中的link标签引入
2. 在src/assets中的资源文件,会经过webpack打包,需要在main.js中import
3. 在vue的script标签中也可以写原生的js
4. 在main.js中import了scss文件要注意这样会把scss编译成css,如果里面都是css那就没问题
   如果里面有变量或者方法,就需要在vue文件中import(和vue文件一起编译),这样才能取到里面的变量或方法
5. 在npm run dev时如果在devServer配置了host和port是80的话,可以使用管理员权限让启动的端口是80,否则不会使用80端口
6. 在vuecli2中可以通过域名加上static目录访问静态文件
7. 在vuecli4中可以通过域名加上public目录访问静态文件且不用写public
8. vuecli3和vuecli4的区别不大
9. sockjs-node 实现全双工通信是HMR(热更新)的基础
10. Runtime Only 和 Runtime + Compiler
  1. Runtime Only
      我们在使用 Runtime Only 版本的 Vue.js 的时候,通常需要借助如 webpack 的 vue-loader 工具
      把 .vue 文件编译成 JavaScript,因为是在编译阶段做的,所以它只包含运行时的 Vue.js 代码,因此代码体积也会更轻量。
  2. Runtime + Compiler
      我们如果没有对代码做预编译,但又使用了 Vue 的 template 属性并传入一个字符串,则需要在客户端编译模板
  3. 示例
      // 需要编译器的版本,Runtime + Compiler
      new Vue({
        template: '<div>{{ hi }}</div>'
      })

      // 这种情况不需要,Runtime Only
      new Vue({
        render (h) {
          return h('div', this.hi)
        }
      })
  4. 因为在 Vue.js 2.0 中,最终渲染都是通过 render 函数,如果写 template 属性,则需要编译成 render 函数
     那么这个编译过程会发生运行时,所以需要带有编译器的版本。很显然,这个编译过程对性能会有一定损耗
     所以通常我们更推荐使用 Runtime-Only 的 Vue.js
  5. 注意:我们在使用 Runtime-Only 时不是说不可以写 template 吗?为什么还能写的原因是因为我们使用了 webpack
     的 vue-loader 把 template 转换成了 render 函数!!!
11. 构建模式、API 环境、mode 之间的关系
  1. 构建模式:即打包模式,NODE_ENV: prod/dev
  2. API 环境:alpha/rc/prod
  3. mode:会根据 mode 变量读取对应的 .env[mode] 文件,注意 mode 会改变 NODE_ENV 的值,
      如果 mode 设置的不是 prod/dev,打包构建就会出现问题
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
Last Updated: 2024/6/11 14:20:38
    飘向北方
    那吾克热-NW,尤长靖