Vue基础

125 阅读14分钟

vue基础

数据驱动(双向绑定)

数据驱动是Vue.js最大的特点。在vue中,所谓的数据驱动就是当数据发生变化时,用户界面发生相应的变化,开发者不需要手动的去修改DOM。

比如,我们点击一个button,需要元素的文本做一个 “是/否” 的切换操作,在传统的jQuery中,对于页面修改的流程通常是:对button绑定事件,然后获取文案对应元素的dom对象,最后根据切换来修改dom对象的文本值。

Vue实现数据驱动

vue实现数据双向绑定主要采用数据劫持,配合发布者-订阅者模式的方式,通过 Object.defineProperty() 来劫持各个属性的 setter 和 getter ,在数据变动时发布消息给订阅者,触发相应监听回调。

当一个普通 JavaScript 对象传给 Vue 实例来作为它的 data 选项时,vue 将遍历它的属性,用Object.defineProperty 将它们转为getter/setter 。用户看不到 getter/setter ,但是在内部它们让vue追踪依赖,在属性被访问和修改时通知变化。

vue的数据双向绑定将MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的Model的数据变化,通过Compile来解析编译模板指令(vue中用来解析{{}}模板语法),最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到 数据变化 —> 视图更新;视图交互变化(input)—> 数据model变更 双向绑定效果。

getter和setter的理解

当打印出vue实例下的data对象里的属性,它的每个属性都有两个对应的get和set方法。顾名思义,get为取值方法,set为赋值方法。正常情况下,取值和赋值是用 obj.prop 的方式,但是这样做有一个问题,我们如何知道对象的值改变了?

我们可以把get和set理解为function,当我们调用对象的属性时,会进入到 get.属性(){...} 中,先判断对象是否有这个属性,如果没有,那么就添加一个name属性,并给它赋值;如果有name属性,那么就返回name属性。可以把get看成一个取值的函数,函数的返回值就是它拿到的值。

当给实例赋值时,会进入 set.属性(val){...} 中,形参val就是赋给属性的值,在这个函数里做了很多事情,比如双向绑定等等。因为这个值每次都要经过set,其他方式无法对该值做修改。在ES5中,对象原型有两个属性,defineGetterdefineSetter ,专门用来给对象绑定get和set。

虚拟DOM(Virtual DOM)

介绍

渐进式框架: 我们可以用最简单的方式直接导入 vue.js 使用 vue ,随着项目变大,可以集成周边插件使用可以被逐步集成

vue版本:我们学习 vue 的版本是 3 版本

学习的是 选项式 API

官网地址:cn.vuejs.org/

vue.js 下载地址: unpkg.com/vue@3/dist/…

0222

01-vue接入

  1. 先下载 vue.js 下载地址: unpkg.com/vue@3/dist/…
  2. 在 html 文件中导入 vue.js
  3. 创建一个 vue 实例
  4. 将 vue 实例和页面模板进行绑定,让其产生联系

createApp : 用来创建一个 vue 实例用的

mount: 将实例挂载到 id 是 app 的元素上

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
​
  <div id="app">
   <p>{{ message }}</p> 
   <input type="text" v-model='msg'>
   <p>{{ msg }}</p>
  </div>
​
  <script>
    //引入一个名为createApp的工厂函数
    const { createApp } = Vue
    createApp({
      data() {
        return {
        message: 'Hello Vue!',
        msg:'hah',
        msghtml:''
        }
      }
    }).mount('#app')
</script><script>
    const app = Vue.createApp({
      data() {
        return {
        message: 'Hello Vue!',
        msg:'hah',
        msghtml:''
        }
      }
    })
  const vm =app.mount('#app')
</script>

02-模板语法

双大括号语法是 js 表达式,可以取到 data 中的数据

指令:

  1. v-text 渲染普通文本,不会解析标签
  2. v-html 渲染标签样式,可以解析标签
  3. v-bind 用来绑定属性用的 (简写是冒号 :)
  4. v-on 用来绑定事件 用的 (简写艾特符号 @)
  5. v-if 用来进行条件判断,条件成立就添加,否则移除
  6. v-for 用来做循环用的 :[attr]="url" 动态属性,可以将 attr 进行修改,使得 v-bind 绑定不同的属性

v-bind:

绑定属性的时候 可以决定属性是否渲染

<button v-bind:disabled="!isAgree">注册

数据属性值 如果是true false 直接决定 标签属性 是否渲染

数据属性值 如果是null undefined 直接决定标签属性不渲染 等价于 false

数据属性值 如果是0 NAN ' ' 直接决定标签属性渲染 但是 标签属性无值

数据属性值 如果是 非布尔值 非 null undefined 非0 NaN ' '的正常值 都会作为 标签属性值渲染上

03-vue响应式

数据发生改变后不会立即改变页面,先修改虚拟 DOM 中的值,然后有虚拟 DOM 来进行修改真实 DOM

  1. 虚拟 DOM 其实是一个 js 对象

  2. vue 数据发生改变后不会立即更新到真实 DOM 中,而是先更新虚拟 DOM

    1. 当虚拟 DOM 改变后会通过 DIFF 算法将改变后的值更新到真实 DOM 中

      1. DIFF:会比较新老 DOM 找到有改变的地方,只更新有变化的地方,提高性能
      2. nextTick 会在真实 DOM 更新之后调用(如故使用普通获取 无法获取真实dom的值)

04-data要是一个函数要返回一个对象

因为要保证每个组件都有自己独立的数据,具备独立的作用域

写在 data 中的数据是具备响应式的,通过 Proxy 实现

05-计算属性

计算属性的特点:

  1. 计算属性有缓存

  2. 计算属性一上来就会立即执行一次

  3. 如果计算属性中有使用到多个属性,那么这些属性全部都会被监听到只要有一个发生改变那么该函数就会被执行

  4. 计算属性中一定要有一个 return,因为要有一个计算的结果

 5. 计算属性是多个值影响一个值

06-可写的计算属性

可以将计算属性定义为一个对象

该对象中可以设置一个 set 和 一个 get 方法

get 是用户计算结果用的

set 是用来修改计算属性的

07-侦听器

侦听器:

  1. 一上来不会立即执行一次(具有惰性)

  2. 侦听器无需返回值

  3. 支持异步的

  4. 没有缓存

5. 侦听器一次只能监听一个值,一个值影响多个值

计算属性:

  1. 运行就会立即执行一次 (非 惰性)

  2. 计算属性中需要一个返回值

  3. 计算属性不支持异步

  4. 计算属性是有缓存

5. 计算属性能监听多个值,多个值影响一个值

侦听器和计算属性

1.如果一个数据依赖于其他数据,那么把这个数据设计为computed的

2.如果你需要在某个数据变化时做一些事情,使用watch来观察这个数据变化

计算属性

模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。对于任何复杂逻辑,你都应当使用计算属性。computed

当其依赖的属性的值发生变化时,计算属性会重新计算,反之,则使用缓存中的属性值。 例:

    <div id="example">
      {{ message.split('').reverse().join('') }}
      <p>{{reversedMessage}}</p>
    </div>
    <script>
      Vue.createApp({
        data() {
          return {
            message: 'Hello'
          }
        },
        computed: {
          // 计算属性的 getter
          reversedMessage() {
            // `this` 指向 vm 实例
            return this.message.split('').reverse().join('')
          }
        }
      }).mount('#example')
    </script>

computed的getter函数

在vue中,computed的属性可以被视为是data一样,可以读取和设值。因此,在computed中可以分为getter(读取)和setter(设值),一般情况下,是没有setter的,computed只是预设了getter,也就是只能读取,不可以改变设值。所以,computed默认格式(是不表明getter函数的):

html:

<div>
  message: {{message}}
</div>
<!-- 计算属性 -->
<div>
  计算属性: {{updateMessage}}
</div>
computed: {
  updateMessage(): {
    console.log('计算属性', this.message)
    return this.message
 }
}

等价于

computed: {
  updateMessage: {
     get: function() {
       console.log('计算属性', this.message)
       return this.message
    }
  }
},

在这里,就需要我们注意一下,不是说我们更改了getter中使用的变量(即依赖的属性),就会触发computed的更新,他有一个前提是computed里的值必须要在模板中使用才可以。但是会触发生命周期的updated()

computed中的setter函数

当赋值给计算属性的时候,将调用setter函数。多用于在模板组件中需要修改计算属性自身的值的时候

 <div id ='app'>
    {{ msg1 }}----{{ msg2 }} 
    <button @click="msgf1" >修改出生年份</button> 
    <button @click="msgf2">修改年龄</button>
  </div>
</body>
<script>
  const app = Vue.createApp({
    data() {
      return {
        msg1:'2000',
      }
    },
    computed:{
      msg2:{
        get(){
          return new Date().getFullYear() - this.msg1
        },
        set(value){
          //根据年龄计算出 出生年份 年费改变 会调用get 年龄也会改变
          this.msg1 =  new Date().getFullYear() - value
        }
      }
    },
    methods: {
      msgf1(){
        this.msg1 = 1998
      },
      msgf2(){
        // console.log(2)
        this.msg2 = 30
      }
    },
  })
​
  //挂载
  const vm = app.mount('#app') 

0223

01-class和style

v-bind 该指令是用来绑定属性用的,后面引号中的内容为 js 表达式

绑定 class 可以通过 字符串直接绑定,也可以通过对象和数组进行绑定

如果是通过对象进行绑定的,对象的属性就是 class ,属性对应的值是一个布尔值

布尔值为真就添加该 class 否则不添加

如果绑定 class 之前已经设置了 class 那么 vue 会将这些 class 合并起来

style 绑定可以通过 字符串 对象和数组

class绑定

02-条件渲染

v-if v-else-if v-else

是用来判断是否需要渲染元素

具有惰性,默认条件不成立的时候直接会不显示

在切换显示不频繁的时候可以使用 v-if

v-show

是用来判断是否显示元素

非惰性的,默认条件不管是否成立,标签都会被渲染,只是通过 css 让其不显示

在频繁的切换显示隐藏的时候推荐使用 v-show

03-列表渲染 for问题

v-for

可以遍历 数字 一段字符串 一个数组 一个对象

遍历数字 v-for='num in 10' 此时就会执行 10 次 第一次的值是 1

遍历字符串 v-for='str in '你好世界'' 你 好 世 界

遍历一个数组无需注意其他

遍历对象 v-for='(值,属性,索引) in {name: "aaa", age: 18}'

v-for 使用的时候一定要加一个 key 属性,而且该 key 属性还要是唯一的

因为 vue 使用的是虚拟 DOM ,当我们再前面添加一个内容的时候,会在虚拟 DOM

的前面添加该内容,但是渲染成真是 DOM 后,原来的标签位置不会改变,只会在

最后位置新增一个新的标签,如果要解决该问题,可以在使用 v-for 的时候添加一个 key 属性

v-if 和 v-for 一起使用

不推荐 一起使用,因为优先级不同

在 vue2 中 for 优先级高于 if(不管遍历多少次,我就需要判断多少次)

在 vue3 中 if 优先级高于 for (只要条件不成立,压根就不会执行循环)

04-事件处理

内联事件处理器:事件被触发时执行的内联 JavaScript 语句 (与 onclick 类似)。

就是 @click='count++'

方法事件处理器:一个指向组件上定义的方法的属性名或是路径。

就是 @click='add'

修饰符:

.self 是将事件指明只有点击自身的时候才会被触发 .capture触发内部事件时,先执行外部事件 .stop 是修饰符,该修饰符是用来阻止冒泡用的 .prevent 是用来阻止默认事件用的 .once 设置该事件只被触发一次

.passive @scroll.passive="myScroll" 用来提升移动端性能用的

键盘事件,修饰符

@keyup.enter

  • .enter
  • .tab
  • .delete (捕获“Delete”和“Backspace”两个按键)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right

05-表单处理

v-model 是双向数据绑定,可以将输入框的内容和 data 中定义状态进行关联

修饰符:

.lazy 在 "change" 事件后同步更新而不是 "input" .number 将输入内容转换为数字 (一般是需要正则判断数字 如手机号 金额等东西) .trim 用来去除首位空格,不能去除中间空格

v-model 在内部为不同的输入元素使用不同的 property 并抛出不同的事件: input['text'] 和 textarea 元素使用 value property 和 input 事件; checkbox 和 radio 使用 checked property 和 change 事件; select 字段将 value 作为 prop 并将 change 作为事件

<label for="">性别</label>
      男<input type="radio" name="sex" value="0" v-model="sex" /><input type="radio" name="sex" value="1" v-model="sex" />
      <p>{{sex}}</p>
​
      <input type="text" v-model="username" />
      <p>{{username}}</p>
​
      <textarea v-model="comment"></textarea>
      <p>{{ comment }}</p>
​
      <!-- 复选框 复选框多选框 -->
      <label for="">兴趣</label>
      <input type="checkbox" name="hoo" value="篮球" v-model="hoos" />
      <input type="checkbox" name="hoo" value="足球" v-model="hoos" />
      <input type="checkbox" name="hoo" value="台球" v-model="hoos" />
      <input type="checkbox" name="hoo" value="排球" v-model="hoos" />
      <p>{{hoos}}</p>
      <!-- 复选框 -->
      <input type="checkbox" id="checkbox" name="hooaa" v-model="toggle" true-value="同意" false-value="不同意" />
      <label for="checkbox">{{ toggle }}</label>
      <p>{{hooa}}</p><!-- 下拉 -->
      <!-- 多选 multiple-->
      <select name="books" multiple v-model="books" id="">
        <option value="122">2222</option>
        <option value="233">333</option>
        <option value="113">444</option>
        <option value="133">555</option>
        <option value="145">666</option>
      </select>
      <p>{{books}}</p>
//小案例。。。。。。。。。。。。。。。。。。。。。。。。小案例。。。。。。。。。。。。。。。。。。。。。。。。。。。
      <input type="checkbox" name="agree" v-model="isAgree" />我已经自习阅读并同意条款内容
      <br />
      <button :disabled="!isAgree">注册</button>
    </div>
  </body>
  <script>
    const app = Vue.createApp({
      data() {
        return {
          username: '张三',
          sex: '',
          comment: '',
          hoos: [],
          toggle: '不同意',
          books: [],
          isAgree: ''
        }
      }
    })
    app.mount('#app')

06-生命周期

选项式 API 中有 8 个生命周期

// 在组合式 API 中,setup 会替代掉 beforeCreate,created 两个生命周期
        beforeCreate () {
            console.log('创建之前');
        },
        created () {
            console.log('创建完成');
            // 老程序员,可能会在这里进行数据请求
        },
        beforeMount () {
            console.log('挂载之前');
        },
        mounted () {
            console.log('挂载完成');
            // 数据请求写在这里
        },
        beforeUpdate () {
            console.log('更新之前');
        },
        updated () {
            console.log('更新完成');
        },
        beforeUnmount () {
            console.log('卸载之前');
        },
        unmounted () {
            console.log('卸载完成');
            // 清除一些影响性能的函数或者说监听的事件
        },

0224

01-ref

ref:

ref 可以用来标记一个或者多个元素,目的是方便我们操作原生 DOM 元素

ref 也可以添加在组件上,可以获取到对应组件实例

02-注册局部组件

  1. 声明一个组件
  2. 通过 components 注册一个组件
  3. 使用 该组件
// 1. 声明一个组件
    // 组件中的选项,和我们之前 app 中的选项是一模一样的
    const myCom = {
        // 组件模板
        template: `<h1>{{msg}}</h1>`,
        // 组件的数据
        data () {
            return {
                msg: '有些闲言碎语 ,之所以听了格外难受,是因为他们说的是对的。'
            }
        },
        // 组件中的函数
        methods: {
​
        },
    }
​
​
    const app = Vue.createApp({
        data () {
            return {}
        },
        // 2. 注册局部组件
        components: {
            //  这里可以注册组件,可以注册多个组件
            // 大写字母开头是可以使用的
            Mycom: myCom
            // 全部小写是可以使用的
            // mycom: myCom
            // 连接符的方式是可以使用的
            // 'my-com': myCom
            // 大驼峰不能使用(单页面开发时可用而且是常用)
            // MyCom: myCom
            // 小驼峰不能使用
            // myCom: myCom
        }
    })
<div id='app'>
​
        <!-- 3. 使用组件 -->
        <!-- 自定义的组件,可以单标签写法也可以支持双标签写法 -->
        <!-- <Mycom></Mycom> -->
        <Mycom />
​
    </div>

03-注册全局组件

  1. 通过 app.component 函数来进行注册组件

    该 component 有两个参数,分别是 组件名称 和 组件描述

  2. 使用组件

 // 直接注册组件
    // 1. 注册一个全局组件
    // 参数1:组件的名称
    // 参数2:组件的描述
    app.component('my-button', {
        template: `<button @click='count++'>按钮 {{count}}</button>`,
        data () {
            return {
                count: 0
            }
        },
    })
<div id='app'>
        <button>军平的按钮</button>
​
        <!-- 2. 使用组件  -->
        <my-button></my-button>
        <my-button></my-button>
        <my-button></my-button>
        <my-button></my-button>
    </div>

04-组件模板解析

111

      , 和 内部元素一定要是 li tr option 否则会将内部元素提取到外部去 通过 is 属性解决该问题 row 为组件名称

      05-props 传值

      父给子组件传值

      1. 在使用组件的时候需要给组件添加属性

        <cart msg="使用的时候传递的属性" num="10" :age="18" :is="true" :tit="title" :isshow="isShow"></cart>
        
      2. 在子组件中通过 props 选项进行接收

        app.component('cart', {
        
          template: '#cart',
        
          // 2. 在组件内部添加一个 props 接收父组件中传递过来的属性
        
          props: ['msg', 'num', 'age', 'tit', 'isshow']
        
        })
        
      3. 使用属性

        <!-- 定义组件模板 -->
        
        <template id="cart">
        
        <!-- 组件中最好是只有一个根标签 -->
        
        <div v-show="isshow" class="cart">
        
          <h1>标题</h1>
        
          <p>新闻内容</p>
        
          <!-- msg 就是 通过 props 接收的数据 -->
        
          <p>{{msg}} -- {{num + 24}}</p>
        
          <p>age: {{ age + 5 }}</p>
        
          <h1>{{tit}}</h1>
        
        </div>
        
        </template>
        

      06-单向数据流

      vue 所谓的单向数据流,就是子组件只能使用父组件中的数据,但是不能修改

      如果需要修改

      > 1. 将值复制一份,然后修改自己复制的值
      > 2. 告诉父组件我要修改什么数据,由父组件来进行修改
      

      07-属性验证

      子组件可以接收父组件传递的数据,且可以设置要求传递数据的类型

      只有符合类型要求的数据才可以传递,否则会有一个警告

      msg: [Number, String], 可以设置接收数据类型为多个

      required: true 可以设置属性为必传项

      default: 10, 设置属性的默认值,如果不传就使用默认的值

      props: {
                  // 属性验证,要求属性的类型一定要和指定的类型相同,否则会有警告信息
                  count: Number,
                  isshow: Boolean,
                  // 设置可以接收数字和字符串类型,其他类型不可以接收
                  msg: [Number, String],
                  message: {
                      type: [Number, String, Object],
                      // 设置该属性为必填项
                      required: true
                  },
                  price: {
                      // 配置默认值,当父组件不给传值的时候就使用默认值,传了就用父组件传递的值
                      default: 10,
                      type: Number
                  },
                  obj: Object,
                  arr: Array,
                  fn: Function
              }
      

      注意点

      组件中的

      组件模板template 根节点只能有一个 如果出现多个 建议用一个节点包起来