切图妞

vuePress-theme-reco 切图妞    2020 - 2021
切图妞 切图妞
前端知识梳理
  • Vue
  • 浏览器 & 网络
  • HTML & CSS
  • Web安全
  • 算法
文章分类
  • 前端小麻烦
  • 配置乐园
  • 实战不完全手册
  • 手撕源码
宝藏女孩
  • 模板仓
  • 项目简介
  • GitHub
  • Segmentfault
  • CSDN
时间轴
author-avatar

切图妞

19

Article

18

Tag

前端知识梳理
  • Vue
  • 浏览器 & 网络
  • HTML & CSS
  • Web安全
  • 算法
文章分类
  • 前端小麻烦
  • 配置乐园
  • 实战不完全手册
  • 手撕源码
宝藏女孩
  • 模板仓
  • 项目简介
  • GitHub
  • Segmentfault
  • CSDN
时间轴
  • 浏览器 & 网络

  • HTML & CSS

  • JS基础

  • 算法(整理中)

  • Vue基础

    • Vue基础
    • v-model
    • Vue传值
  • Web安全

Vue基础

vuePress-theme-reco 切图妞    2020 - 2021

Vue基础

切图妞 2020-02-01 VueVue基础

# 介绍

# 为什么有且只能有一个根元素

官方回应是取决于 diff算法 的编写方式,树状的数据结构需要通过根元素进行遍历对比。

<body>
    <div id="app"></div>
</body>
1
2
3
new Vue({
    el: '#app'
})
1
2
3
<template>
    <div />
</template>
1
2
3

多个根元素会导致难以指定vue实例的根节点,vue在body里定义一个元素,将其绑定处理成程序的入口。template下也一样,通过唯一的根节点,递归遍历整个vue树下的所有节点并处理为vdom,渲染成真正的HTML后直接替换原来位置。

官方解答->

# vue的优缺点

优点:

  • 渐进式框架:轻量高效,简单易用。
  • MVVM的架构:数据驱动视图,方便测试。
  • 组件化:组件可复用,易维护,便于协同开发,提高开发效率。
  • 虚拟DOM:减少浏览器资源损耗,渲染更快
  • 前后端分离:服务器相对压力小。

缺点:

  • 不支持IE9以下浏览器
  • 首次加载耗时比较多。
  • SEO问题,不利于百度,360等搜索引擎收录。

# data

# data 为什么是一个函数

export default 导出的是个类,注册组件其实并不产生新的组件类,但会产生一个可以用来实例化的新方式,使用组件才是真正创建一个组件实例。 每个组件进行实例化时执行data,当data为对象时每个组件data共享,导致数据可能被另外的组件修改,而data为方法时,data就会存在在闭包当中,每个实例的data属性是独立的,不会相互影响。这是因为js本身的特性带来的,跟vue本身设计无关。

错误: 使用原型链定义data,如果两个实例同时引用一个对象,那么当你修改其中一个属性的时候,另外一个实例也会跟着改。

var MyComponent = function() {}
MyComponent.prototype.data = {
    b: 1,
}
var component1 = new MyComponent()
var component2 = new MyComponent()
component1.data.b = 5
console.log(component2.data.b) // 5
1
2
3
4
5
6
7
8

正确: 形成闭包,数据不再互相影响

var MyComponent = function() {
    this.data = this.data()
}
MyComponent.prototype.data = function() {
    return {
        b: 1,
    }
}
var component1 = new MyComponent()
var component2 = new MyComponent()
component1.data.b = 5
console.log(component2.data.b) // 1
1
2
3
4
5
6
7
8
9
10
11
12

# 修改数据未更新

vue监听不到深层次的对象属性或者数组值的改变,除了通过数组的变异方法触发视图更新,还可以使用替换的方法去改变对象存储的地址而不是单单改变数值。

# 数组

变异方法:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

替换数组:

  • filter()
  • concat()
  • slice()
  this.arr[0] = 'a' + new Date().getTime()
  this.arr.length = 5
1
2
  1. $set 方法
    this.$set(this.arr, 0, 'a' + new Date().getTime())
1
  1. 使用变异方法 splice 等
    this.$set(this.arr, 0, 'a' + new Date().getTime())
    // 该方法改变长度只能减短不能加长
    this.arr.splice(1)
1
2
3
  1. $forceUpdate 强制更新(通用但暴力)迫使 Vue 实例重新渲染。注意它仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件。
    this.arr[0] = 'a' + new Date().getTime()
    this.arr.length = 5
    this.$forceUpdate()
1
2
3

# 对象

vue在进行初始化实例时进行数据双向绑定,使用 Object.defineProperty() 对属性遍历添加 getter / setter 方法,所以属性必须在 data 对象上存在时才能进行上述过程! vue初始化实例后,再去给实例对象添加属性时并没有添加 getter 和 setter 的方法,所以改变属性值和添加属性值时只可以看到数据改变,页面数据并没有更新! 这时用 $set 属性就可以使页面数据的更新!

  this.obj.name = 'qietuniu' + new Date().getTime()
1
  1. $set 方法
    this.$set(this.obj, name, 'qietuniu' + new Date().getTime())
1
  1. Object.assign
    this.obj = Object.assign({}, this.obj, {
        name: 'qietuniu' + new Date().getTime()
    })
1
2
3
  1. ...扩展运算符
    this.obj = {
        ...this.obj,
        name: 'qietuniu' + new Date().getTime()
    }
1
2
3
4
  1. $forceUpdate 强制更新

# name

# 命名规范

建议name首字母为大写

  • 当 name:'List' 时,组件可书写成 <List /> 或者 <list />
  • 当 name:'list' 时,组件只能书写成 <list />

组件使用时命名:

  • kebab-case:字母全小写且必须包含一个连字符

name:'ListTree' => <list-tree />

  • PascalCase:每一个单字的首字母都采用大写字母

name:'ListTree' => <ListTree />

# 作用

  1. 组件模板递归调用自身
      <ul>
          <li v-for="(item, index) of list" :key="index">
              {{item.title}}
              <div v-if="item.children">
                  <List :list="item.children" />
              </div>
          </li>
      </ul>
1
2
3
4
5
6
7
8
      name: 'List',
          props: {
              list: {
                  type: Array,
                  default: function() {
                      return [{
                              title: '1',
                              children: [{
                                      title: '1-1'
                                  },
                                  {
                                      title: '1-2'
                                  },
                                  {
                                      title: '1-3'
                                  }
                              ]
                          },
                          {
                              title: '2',
                              children: [{
                                      title: '2-1',
                                      children: [{
                                              title: '2-1-1'
                                          },
                                          {
                                              title: '2-1-2'
                                          },
                                          {
                                              title: '2-1-3'
                                          }
                                      ]
                                  },
                                  {
                                      title: '2-2'
                                  },
                                  {
                                      title: '2-3'
                                  }
                              ]
                          }
                      ]
                  }
              }
          }
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. 便于调试

vue-devtools中有名字的组件有更友好的警告信息,未命名组件将显示成 <AnonymousComponent>

  1. 作为keep-alive 的 include 和 exclude 的参数

详情请见keep-alive

# props

# props的写法与属性

HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名。

  • type

    可以是下列原生构造函数中的一种: String 、 Number 、 Boolean 、 Array 、 Object 、 Date 、 Function 、 Symbol 、任何自定义构造函数、或上述内容组成的数组。会检查一个 prop 是否是给定的类型,否则抛出警告。

  • default : any

    为该 prop 指定一个默认值。如果该 prop 没有被传入,则换做用这个值。对象或数组的默认值必须从一个工厂函数返回。

  • required : Boolean

    定义该 prop 是否是必填项。在非生产环境中,如果这个值为 truthy 且该 prop 没有被传入的,则一个控制台警告将会被抛出。

truthy(真值)指的是在布尔值上下文中,转换后的值为真的值

  • validator : Function

    自定义验证函数会将该 prop 的值作为唯一的参数代入。在非生产环境下,如果该函数返回一个 falsy 的值 (也就是验证失败),一个控制台警告将会被抛出。你可以在这里查阅更多 prop 验证的相关信息。

  Vue.component('my-component', {
      props: {
          // 基础的类型检查 ( `null` 和 `undefined` 会通过任何类型验证)
          propA: Number,
          // 多个可能的类型
          propB: [String, Number],
          // 必填的字符串
          propC: {
              type: String,
              required: true
          },
          // 带有默认值的数字
          propD: {
              type: Number,
              default: 100
          },
          // 带有默认值的对象
          propE: {
              type: Object,
              // 对象或数组默认值必须从一个工厂函数获取
              default: function() {
                  return {
                      message: 'hello'
                  }
              }
          },
          // 自定义验证函数
          propF: {
              validator: function(value) {
                  // 这个值必须匹配下列字符串中的一个
                  return ['success', 'warning', 'danger'].indexOf(value) !== -1
              }
          }
      }
  })
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

# props验证

基础的类型检查 ( null 和 undefined 会通过任何类型验证), type 可以是下列原生构造函数中的一个:

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol

额外的,type 还可以是一个自定义的构造函数,并且通过 instanceof 来进行检查确认。例如,给定下列现成的构造函数来验证 author prop 的值是否是通过 new Person 创建的。:

props: {
    title: String,
    likes: Number,
    isPublished: Boolean,
    commentIds: Array,
    list: Object,
    callback: Function,
    contactsPromise: Promise, // or any other constructor
    age: {
        type: Number,
        default: 0,
        required: true,
        validator: function(value) {
            return value >= 0
        }
    },
    author: Person
}

function Person(firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 单向数据流

所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。

额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。

这里有两种常见的试图改变一个 prop 的情形:

# prop 用来传递一个初始值

子组件希望将prop作为本地数据来使用。在这种情况下,最好定义一个本地的 data 属性并将这个 prop 用作其初始值:

  props: ['initialCounter'],
      data: function() {
          return {
              counter: this.initialCounter
          }
      }
1
2
3
4
5
6

# prop 以一种原始的值传入且需转换

在这种情况下,最好使用这个 prop 的值来定义一个计算属性:

  props: ['txt'],
      computed: {
          normalizedTxt: function() {
              return this.txt.trim().toLowerCase()
          }
      }
1
2
3
4
5
6

# class与style绑定

# class

在一个自定义组件上用到 class 属性的时候,这些类将被添加到根元素上面,这个元素上已经存在的类不会被覆盖。

  1. 对象语法

:class 指令也可以与普通的 class 属性共存。

  <li class="bold" :class="{ blue: true }">class对象语法</li>
1
  1. 数组语法
    <li :class="['bold', {'blue': isActive}]">class数组语法</li>
1
  1. 三元运算语法
    <li :class="[true?'blue':'bold']">class三元运算语法</li>
1

# style

Vue会自动侦测需要添加浏览器引擎前缀的 CSS 属性,并添加相应的前缀,如 transform、flex等

  1. 对象语法
    data() {
        return {
            isActive: true,
            activeColor: {
                color: '#08d'
            },
            activeFont: {
                fontSize: '18px',
                fontWeight: 'bold'
            }
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
  <li :style="activeColor">style数组语法</li>
1
  1. 数组语法

可以使用data属性也可以使用计算属性

    <li :style="[activeColor, activeFont]">style数组语法</li>
1
  1. 三元运算语法
    <li :style="{color: isActive?'#08d':''}">style三元运算语法</li>
1