Vue.js 基础指南

引用

vue.js脚本地址如下:

<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="https://unpkg.com/vue"></script>

基础

  • 数据与方法
    当一个 Vue 实例被创建时,它向 Vue 的响应式系统中加入了其 data 对象中能找到的所有的属性。当这些属性的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。
    当这些数据改变时,视图会进行重渲染。值得注意的是只有当实例被创建时 data 中存在的属性才是响应式的。也就是说如果你后添加一个新的属性,那么对这个属性的改动将不会触发任何视图的更新。

  • 申明式渲染

    {{ ... }}

    <div id="app">{{ message }}</div>
                    ↓ ↓ ↓
    <div id="app" v-text="message"></div>
    
    var app = new Vue({
      el: '#app',
      data: {
        message: 'Hello Vue!'    //在data内定义变量
      }     ↓ ↓ ↓
    });     return { 
                message: 'Hello Vue!'
            }
    

    不使用return包裹的数据会在项目的全局可见,会造成变量污染;使用==return包裹后数据中变量只在当前组件中生效==,不会影响其他组件。

  • 指令

    v-bind: [--缩写--]--> :

    v-bind:title="message"
    
  • 条件(用于控制切换一个元素是否显示)

    v-if="..."
    v-else-if
    v-else
    /* 必须紧跟在带 v-if 元素之后 */

    切换多个元素: 将<template> 当做不可见的包裹元素,并在上面使用 v-if。最终的渲染结果将不包含 <template> 元素。

    <template v-if="ok">
        <p>Paragraph 1</p>
        <p>Paragraph 2</p>
    </template>
    

    v-show=""

    v-if 是“真正”的条件渲染,它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。v-if也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
    相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。-->只是简单地切换元素的 CSS 属性 display。

  • 循环

    v-for = "(item, index) in items"
    /* 不支持 <template> 元素,也不支持 v-else
    当 v-if 与 v-for 一起使用时,==v-for 具有比 v-if 更高的优先级==。
    支持一个可选的第二个参数为当前项的索引。*/

    1. 数组循环:
    <ul id="example-1">
      <li v-for="item in items">
        {{ item.message }}
      </li>
    </ul>
    
    var example1 = new Vue({
      el: '#example-1',
      data: {
        items: [
          { message: 'Foo' },
          { message: 'Bar' }
        ]
      }
    })
    
    1. 对象循环:
    <div v-for="(value, key, index) in object">
      {{ key }}: {{ value }}
    </div>
    
  • 双向绑定 (实现表单输入和应用状态之间的双向绑定)

    v-model

    <input v-model="message">
    data: {
        message: 'Hello Vue!'
    }
    
    • 修饰符
      • .lazy 在“change”时而非“input”时更新

        <input v-model.lazy="msg" >

      • .number 将用户的输入值转为数值类型

      • .trim 自动过滤用户输入的首尾空白字符

    (!)提示

    • v-model 会忽略所有表单元素的 value、checked、selected 特性的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data 选项中声明初始值。

    • 如果 v-model 表达式的初始值未能匹配任何选项,<select> 元素将被渲染为“未选中”状态。在 iOS 中,这会使用户无法选择第一个选项。因为这样的情况下,iOS 不会触发 change 事件。因此,推荐提供一个值为空的禁用选项。

  • 数组更新检测

    ++变异方法++
    push()
    pop()
    shift()
    unshift()
    splice()
    sort()
    reverse()
    会改变被这些方法调用的原始数组,也会触发视图更新。

    ++非变异方法++
    filter(), concat(), slice()
    不会改变原始数组,但总是返回一个新数组。当使用非变异方法时,可以用新数组替换旧数组。

    • 由于 JavaScript 的限制,Vue 不能检测以下变动的数组:

      1. 用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue
      2. 修改数组的长度时,例如:vm.items.length = newLength
    • Vue 不能检测对象属性的添加或删除:

      var vm = new Vue({
        data: {
          a: 1, 
          userProfile: {
            name: 'Anika'
          }
        }
      })
      // `vm.a` 现在是响应式的
      
      vm.b = 2
      // `vm.b`不是响应式的
      Vue.set(vm.userProfile, 'age', 27) 
       ↓ ↓ ↓
      vm.$set(this.userProfile, 'age', 27)
      

      Vue 不能动态添加根级别的响应式属性。但是可以使用 Vue.set(object, key, value) 方法向嵌套对象添加响应式属性

  • 计算属性

    模板内的表达式非常便利,但是设计它们的初衷是用于++简单运算++的。在模板中放入太多的逻辑会让模板过重且难以维护。例如:

    <div id="example">
      {{ message.split('').reverse().join('') }}
    </div>
    

    如果要重复多次使用这个运算,应适当适应计算属性:

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

    我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖发生改变时才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。

    • setter

      计算属性默认只有 getter ,不过在需要时也可以提供一个 setter :
      (这样可以省去watch侦听)

      computed: {
        reversedMessage: {
          // getter
          get: function () {
            return this.message.split('').reverse().join('')
          },
          // setter
          set: function (newValue) {
            this.message = newValue.split('').reverse().join('');
            //调用vm.reversedMessage("haha"),message也会改变成"ahah"
          }
        }
      }
      
  • 侦听属性 watch

    当需要在数据变化时执行异步或开销较大的操作时,用watch侦听变化是最有用的。

    watch: {
        // 如果 `message` 发生改变,这个函数就会运行
        message: function () { ... }
    },
    
  • 显示过滤/排序结果

    .filter()

    <li v-for="n in evenNumbers">{{ n }}</li>
    
    data: {
      numbers: [ 1, 2, 3, 4, 5 ]
    },
    computed: {
      evenNumbers: function () {
        return this.numbers.filter(function (number) {
          return number % 2 === 0
        })
      }
    }
    
  • Class & Style 绑定

    • 模板语法

      <div class="static"
          v-bind:class="{ active: isActive, 'text-danger': hasError }">  
          //当classname有其他符号时需要使用引号包裹
      </div>
      

      or 使用计算属性:

      <div v-bind:class="classObject"></div>
      
      data: {
        isActive: true,
        error: null
      },
      computed: {
        classObject: function () {
          return {
            active: this.isActive && !this.error,
            'text-danger': this.error && this.error.type === 'fatal'
          }
        }
      }
      

      or 使用数组:

      <div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
                              ↓ ↓ ↓  //三元 转 对象形式
      <div v-bind:class="[{ active: isActive }, errorClass]"></div>
      
      data: {
        activeClass: 'active',
        errorClass: 'text-danger'
      }
      

组件

在 Vue 里,一个组件本质上是一个拥有预定义选项的一个 Vue 实例。

Vue.component(tagName, options)

注意确保在初始化根实例之前注册组件:

// 定义名为 myitem 的新组件
Vue.component('myitem', {
    // myitem 组件现在接受一个
    // "prop",类似于一个自定义特性。
    props: ['attributeA','attributeB'], // 在 JavaScript 中使用 camelCase
    template: '<li>{{ attributeA.text }} _ {{ attributeB }}</li>'
});
        //Prop 验证
        props:{
            propA: Number,
            propB: [String, Number],
            propC: {
              type: Number,
              required: true,
              default: 100 //默认值
            },
            // 自定义验证函数
            propD: {
              validator: function (value) {
                return value > 10
              }
            }
        }

var app = new Vue({
    el: '#app',
    data: {
        groceryList: [
            { id: 0, text: '蔬菜' },
            { id: 1, text: '奶酪' },
            { id: 2, text: '随便其他什么人吃的东西' }
        ]
    }
});

模板使用:

<myitem v-for="item in groceryList" :attribute-a="item" :attribute-b="item.id" :key="item.id"></myitem>
     ↓ ↓ ↓                                 ↓在HTML中使用 kebab-case↓
<li is="myitem" v-for="item in groceryList" :attribute-a="item" :attribute-b="item.id" :key="item.id"></myitem>

    //html上可以直接添加非 Prop 属性 
                ↓ ↓ ↓ 
    <myitem some-attr="xxx"></myitem>

注意这里的 is="myitem" 属性。
这种做法在使用 DOM 模板时是十分必要的,因为在 <ul> 元素内只有 <li> 元素会被看作有效内容。
这样做实现的效果与 <myitem> 相同,但是可以避开一些潜在的浏览器解析错误。

构造 Vue 实例时传入的各种选项大多数都可以在组件里使用。只有一个例外:++data 必须是函数++。

  • 组件组合

    父子组件的关系:++prop 向下传递,事件向上传递++。父组件通过 prop 给子组件下发数据,子组件通过事件给父组件发送消息。

    • 父组件控制子组件

      ++Prop 单向绑定++:当父组件的属性变化时,将传导给子组件,但是反过来不会。这是为了防止子组件无意间修改了父组件的状态。

      在两种情况下,我们很容易忍不住想去修改 prop 中数据:

      • Prop 作为初始值传入后,子组件想把它当作局部数据来用;

        props: ['initialCounter'],
        data: function () {
          return { counter: this.initialCounter }
        }
        //定义一个局部变量,并用 prop 的值初始化它
        
      • Prop 作为原始数据传入,由子组件处理成其它数据输出。

        props: ['size'],
        computed: {
          normalizedSize: function () {
            return this.size.trim().toLowerCase()
          }
        }
        //定义一个计算属性,处理 prop 的值并返回
        

      ** 注意在 JavaScript 中对象和数组是引用类型,指向同一个内存空间,如果 prop 是一个对象或数组,在子组件内部改变它会影响父组件的状态。

    • 子组件向父组件通信

      • 使用 $on(eventName) 监听事件
      • 使用 $emit(eventName) 触发事件

      父组件可以在使用子组件的地方直接用 v-on 来监听子组件触发的事件,但不能用 $on 监听子组件释放的事件。

      如:

      <div id="counter-event-example">
        <p>{{ total }}</p>
                      <!-- increment事件触发incrementTotal方法 (3) -->
                                   ↓ ↓ ↓ 
        <button-counter v-on:increment="incrementTotal"></button-counter>
        <button-counter v-on:increment="incrementTotal"></button-counter>
      </div>
      
      Vue.component('button-counter', {
                      //click事件触发incrementCounter方法 (1)
                              ↓ ↓ ↓ 
        template: '<button v-on:click="incrementCounter">{{ counter }}</button>',
        data: function () {
          return {
            counter: 0
          }
        },
        methods: {
          incrementCounter: function () {
            this.counter += 1
            this.$emit('increment')  //触发increment事件 (2)
          }
        },
      })
      
      new Vue({
        el: '#counter-event-example',
        data: {
          total: 0
        },
        methods: {
          incrementTotal: function () {
            this.total += 1  //total+1 (4)
          }
        }
      })
      

事件处理

  • 监听事件

    v-on: [--缩写--]--> @

    <div id="example-3">
      <button v-on:click="say('hi', $event)">Say hi</button>
      //内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event 把它传入方法
    </div>
    
    new Vue({
      el: '#example-3',
      methods: {
        say: function (message) {
          if (event) event.preventDefault()
          alert(message)
        }
      }
    })
    
  • 事件修饰符

    修饰符 对应功能 demo -
    .stop event.stopPropagation() v-on:click.stop="doThis" 阻止单击事件继续传播
    .prevent event.preventDefault() v-on: submit.prevent="onSubmit" 提交事件不再重载页面
    .capture 事件捕获模式 v-on:click.capture="doThis" 元素自身触发的事件先在此处处理,然后才交由内部元素进行处理
    .self 当前元素自身触发函数 v-on:click.self="doThat" 只当在 event.target 是当前元素自身时触发处理函数,不是从内部元素触发的
    .once 事件只触发一次 - -
    .passive 事件的默认行为立即触发 v-on: scroll.passive="onScroll" 滚动事件的默认行为 (即滚动行为) 将会立即触发
    .keyCode 监听键盘事件 v-on:keyup.enter="submit" 回车时调用 vm.submit()

    使用修饰符时,顺序很重要,如:
    @click.prevent.self 会阻止所有的点击,而 @click.self.prevent 只会阻止对元素自身的点击。

    不要把 .passive 和 .prevent 一起使用,因为 .prevent 将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive 会告诉浏览器你不想阻止事件的默认行为。

    <!-- 修饰符可以串联 -->
    <a v-on:click.stop.prevent="doThat"></a>
    
    <!-- 只有修饰符 -->
    <form v-on:submit.prevent></form>
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343