Vue学习小结

122 阅读3分钟

vue新建项目

1.全局安装vue-cli:npm install --global vue-cli

2.新建vue项目:vue init webpack 项目名称

###命名 1.组件名,官方推荐的组件名是 每个单词首字母大写(PascalCase) 或者 全小写用 - 连接(kebab-case)。 在DOM中使用的时候, 改为全小写, 单词之间用 - 连接。

Vue.component('MyComponent', {});    
或者      
Vue.component('my-component', {});

import MyComponent from 'MyComponent.vue';
export default {
    name: 'MyComponent'
}

在DOM(即非字符串的模板) 中使用的时候,

<my-component></my-component>

2.props 声明 prop的时候, 使用驼峰命名(myProps), 模板中使用的时候, 用 - 连接(my-props)

props: {
    myProps: {}
}
<my-component :my-props="abc"></my-component>

3.自定义事件名 因为html对大小写不敏感,所以大写的都会被转为小写的。 所以推荐 都用 -连接来命名。

this.$emit('my-event');

<my-component @my-event="abc"></my-component>

数据与方法

当一个 Vue 实例被创建时,它将data对象中的所有的 property 加入到 Vue 的响应式系统中。当这些 property 的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。 值得注意的是只有当实例被创建时就已经存在于data中的 property 才是响应式的

// 我们的数据对象
var data = { a: 1 }

// 该对象被加入到一个 Vue 实例中
var vm = new Vue({
  data: data
})

// 获得这个实例上的 property
// 返回源数据中对应的字段
vm.a == data.a // => true

// 设置 property 也会影响到原始数据
vm.a = 2
data.a // => 2

// ……反之亦然
data.a = 3
vm.a // => 3

html:

<body>
    <div id="app">{{a}} {{b}}</div>
 </body>

js:

    import Vue from 'vue'
    let data = {a:'ee'}
    let vm = new Vue({
      el: '#app',
      data,
    })
    console.log(vm.a) // ee
    console.log(vm.b) // undefined
    vm.a = 'changed'
    vm.b = 'bchanged' 
    // 页面显示:achanged bchanged

改为异步:(Promise或者setTimeout)

    import Vue from 'vue'
    let data = {a:'ee'}
    // 顶层元素,其他页面页面通过路由+组件的形式引入
    let vm = new Vue({
      el: '#app',
      data,
    })
    console.log(vm.a) // ee
    console.log(vm.b) // undefined
    vm.a = 'changed'
    setTimeout(() => {
        vm.b = 'bchanged' 
    }, 1)
    // 页面显示:achanged 

那么对 b 的改动将不会触发任何视图的更新。如果你知道你会在晚些时候需要一个 property,但是一开始它为空或不存在,那么你仅需要设置一些初始值。比如:

    data: {
      b: '',
      visitCount: 0,
      hideCompletedTodos: false,
      todos: [],
      error: null
    }

修改代码

    import Vue from 'vue'
    let data = {a:'ee', b:''}
    let vm = new Vue({
      el: '#app',
      data,
    })
    console.log(vm.a) // ee
    console.log(vm.b) // undefined
    vm.a = 'changed'
    setTimeout(() => {
        vm.b = 'bchanged' 
    }, 1)
    // 页面显示:achanged bchanged

这里唯一的例外是使用 Object.freeze(),这会阻止修改现有的 property,也意味着响应系统无法再追踪变化。

生命周期

生命周期

import Vue from 'vue'
let data = {a:'aVal', b: 'bVal'}
let vm = new Vue({
  el: '#app',
  data,
  beforeCreate: function () {
    // `this` 指向 vm 实例
    console.log('beforeCreate-----a is: ' + this.a + ', b is:' + this.b, document.getElementById('app').innerText);
  },
  created: function () {
    console.log('created-----a is: ' + this.a + ', b is:' + this.b, document.getElementById('app').innerText);
  },
  beforeMount: function () {
    console.log('beforeMount-----a is: ' + this.a + ', b is:' + this.b, document.getElementById('app').innerText);
  },
  mounted: function () {
    console.log('mounted-----a is: ' + this.a + ', b is:' + this.b, document.getElementById('app').innerText);
  },
  beforeUpdate: function () {
    console.log('beforeUpdate-----a is: ' + this.a + ', b is:' + this.b, document.getElementById('app').innerText);
  },
  updated: function () {
    console.log('updated-----a is: ' + this.a + ', b is:' + this.b, document.getElementById('app').innerText);
  }
})
vm.a = 'changed'
new Promise((resolve) => {
    resolve();
}).then(() => {
    console.log('set b in promise')
    vm.b = 'bchanged in promise' 
})
console.log('mounted');

执行结果:

// beforeCreate-----a is: undefined, b is:undefined {{a}}--{{b}}
// created-----a is: aVal, b is:bVal {{a}}--{{b}}
// beforeMount-----a is: aVal, b is:bVal {{a}}--{{b}}
// mounted-----a is: aVal, b is:bVal aVal--bVal
// mounted
// beforeUpdate-----a is: changed, b is:bVal aVal--bVal
// updated-----a is: changed, b is:bVal changed--bVal
// set b in promise
// beforeUpdate-----a is: changed, b is:bchanged in promise changed--bVal
// updated--a is: changed, b is:bchanged in promise changed--bchanged in promise

[自我总结]:

created: 针对设置data数据

mounted: 针对替换el节点

updated: 针对data数据变化

单向数据流

所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。 额外的,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。 这里有两种常见的试图变更一个 prop 的情形: 这个 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个计算属性

<body>
    <div id="app">
        <!-- a、父组件通过属性绑定的方式传值 -->
        <counter :count="0"></counter>
        <counter :count="1"></counter>
    </div>
    <script>    
        var counter={
            //b、子组件接收父组件的传值
            props:['count'],  
            //c、把父组件传来的值放入模板中渲染
            template:"<div @click='handleClick'>{{number}}</div>",
            data:function(){
                return{
                    //复制一份,子组件就可以修改了
                    number:this.count
                }
            },
            methods:{
                handleClick:function(){
                    //不能直接这么写,因为子组件不能修改父组件的传值
                    // this.count++
                    this.number++
                }
            }
        }
         var app = new Vue({
            el:'#app',
            //注册局部组件
            components:{
                counter:counter
            }       
        })
    </script>
</body>

除了数据 property(data上的属性),Vue 实例还暴露了一些有用的实例 property 与方法。它们都有前缀 vm.(vm.data-实例属性、vm.$watch-实例方案), 以便与用户定义的 property 区分开来,参考:API 参考

模板语法

文本:

{{msg}}

原始HTML(XSS攻击)

<div id="app">
    <p>Using mustaches: {{ rawHtml }}</p>
    <p>Using v-html directive: <span v-html="rawHtml"></span></p>
</div>

属性:html节点的有效属性

v-bind:属性

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

方法:html节点的有效方法,在method中定义此方法

v-on:方法

<div v-on:click="doSomething"></div>
// 缩写
<div @click="doSomething"></div>

动态参数:[],如空格和引号无效

<a v-on:[eventName]="doSomething"> ... </a>

使用 JavaScript 表达式

{{ number + 1 }}

{{ ok ? 'YES' : 'NO' }}

{{ message.split('').reverse().join('') }}

<div v-bind:id="'list-' + id"></div>

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

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

修饰符(补充,缩小范围)

修饰符 (modifier) 是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。例如,.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault():

修饰符包括,事件修饰符、按键修饰符、系统键盘修饰符、系统键组合修饰符、鼠标操作键修饰符等

<form v-on:submit.prevent="onSubmit">...</form>

计算属性(computed) VS 方法(method) VS 侦听属性(watch)

计算属性:基于它们的响应式依赖进行缓存的,响应式依赖的数据变动而触发, 依赖属性(可多个)

侦听属性:基于它们的响应式依赖进行缓存的,响应式依赖的数据变动而触发,侦听具体属性(1个)

方法:事件触发时调用

<div id="app">
    firstName:<input v-model='firstName'/><br>
    lastName:<input v-model='lastName'/><br>
    fullName: {{fullName}}
</div>

watch代码:

import Vue from 'vue'
let data = {
    firstName: 'Foo',
    lastName: 'Bar'
}
new Vue({
  el: '#app',
  data,
  methods: {
    clickTry: function () {
      console.log('aaa')
    }
  },
  watch: {
    firstName: function (val) {
      this.fullName = val + ' ' + this.lastName
    },
    lastName: function (val) {
      this.fullName = this.firstName + ' ' + val
    }
  }
})

computed代码:

import Vue from 'vue'
let data = {
    firstName: 'Foo',
    lastName: 'Bar'
}
new Vue({
  el: '#app',
  data,
  methods: {
    clickTry: function () {
      console.log('aaa')
    }
  },
  computed: {
    fullName: function () {
        return this.firstName + ' ' + this.lastName
    }
  }
})

二者不同点:初始化时,watch代码fullName显示为空,computed代码正常

Class 与 Style 绑定

基本思想:Merge 1.对象语法

// data: {
//     isActive: true,
//     hasError: false
// }
<div
  class="static"
  v-bind:class="{ active: isActive, 'text-danger': hasError }"
></div>

结果:

<div class="static active"></div>

2.数组语法

// data: {
//     activeClass: 'active',
//     errorClass: 'text-danger'
// }
<div v-bind:class="[activeClass, errorClass]"></div>

结果:

<div class="active text-danger"></div>

3.用在组件上(略)

条件渲染

v-if v-else-if v-else

v-show(切换display)

v-if 用 key 管理可复用的元素. 有key, 这两个元素是完全独立的,不要复用它们”

<template v-if="loginType === 'username'">
  <label>Username</label>
  <input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
  <label>Email</label>
  <input placeholder="Enter your email address" key="email-input">
</template>

列表渲染

v-for

// data: {
//     object: {
//         title: 'How to do lists in Vue',
//         author: 'Jane Doe',
//         publishedAt: '2016-04-10'
//     }
// }
<div v-for="(value, name, index) in object">
  {{ index }}. {{ name }}: {{ value }}
</div>

组件

表单组件

v-model:它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理,包括

<input><select><<textarea>

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

v-model 在内部为不同的输入元素使用不同的 property 并抛出不同的事件:

text 和 textarea 元素使用 value(v-model) property 和 input 事件;

checkbox 和 radio 使用 checked property 和 change 事件;

select 字段将 value(v-model) 作为 prop 并将 change 作为事件。

举例:cn.vuejs.org/v2/guide/fo…

子组件

子组件通过props获取父组件传递过来的值 子组件可以通过调用内建的 $emit 方法并传入事件名称来触发一个事件

这个 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个计算属性:

    props: ['size'],
    computed: {
      normalizedSize: function () {
        return this.size.trim().toLowerCase()
      }
    }

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

动态组件

Vue 的 元素加一个特殊的 is attribute 来实现:

<!-- 组件会在 `currentTabComponent` 改变时改变 -->
<component v-bind:is="currentTabComponent"></component>

代码示例:codesandbox.io/s/github/vu…

<!DOCTYPE html>
<html>
  <head>
    <title>Dynamic Components Example</title>
    <script src="https://unpkg.com/vue"></script>
    <style>
      .tab-button {
        padding: 6px 10px;
        border-top-left-radius: 3px;
        border-top-right-radius: 3px;
        border: 1px solid #ccc;
        cursor: pointer;
        background: #f0f0f0;
        margin-bottom: -1px;
        margin-right: -1px;
      }
      .tab-button:hover {
        background: #e0e0e0;
      }
      .tab-button.active {
        background: #e0e0e0;
      }
      .tab {
        border: 1px solid #ccc;
        padding: 10px;
      }
    </style>
  </head>
  <body>
    <div id="dynamic-component-demo" class="demo">
      <button
        v-for="tab in tabs"
        v-bind:key="tab"
        v-bind:class="['tab-button', { active: currentTab === tab }]"
        v-on:click="currentTab = tab"
      >
        {{ tab }}
      </button>

      <component v-bind:is="currentTabComponent" class="tab"></component>
    </div>

    <script>
      Vue.component("tab-home", {
        template: "<div>Home component</div>"
      });
      Vue.component("tab-posts", {
        template: "<div>Posts component</div>"
      });
      Vue.component("tab-archive", {
        template: "<div>Archive component</div>"
      });

      new Vue({
        el: "#dynamic-component-demo",
        data: {
          currentTab: "Home",
          tabs: ["Home", "Posts", "Archive"]
        },
        computed: {
          currentTabComponent: function() {
            return "tab-" + this.currentTab.toLowerCase();
          }
        }
      });
    </script>
  </body>
</html>

解析 DOM 模板时的注意事项

// 有些 HTML 元素,诸如 <ul>、<ol>、<table> 和 <select>,对于哪些元素可以出现在其内部是有严格限制的。而有些元素,诸如 <li>、<tr> 和 <option>,只能出现在其它某些特定的元素内部。
// 这会导致我们使用这些有约束条件的元素时遇到一些问题。例如:

<table>
  <blog-post-row></blog-post-row>
</table>
//这个自定义组件 <blog-post-row> 会被作为无效的内容提升到外部,并导致最终渲染结果出错。幸好这个特殊的 is attribute 给了我们一个变通的办法:
<table>
  <tr is="blog-post-row"></tr>
</table>

需要注意的是如果我们从以下来源使用模板的话,这条限制是不存在的:

1.字符串 (例如:template: '...')

2.单文件组件 (.vue)

3.如下形式:

<script type="text/x-template">

单文件组件

<!-- my-component.vue -->
<template>
  <div>This will be pre-compiled</div>
</template>
// 或如下图内联
<script src="./my-component.js"></script>
// 或如下图内联
<style src="./my-component.css"></style>

单文件组件

检测变化的注意事项

由于 JavaScript 的限制,Vue 不能检测数组和对象的变化。尽管如此我们还是有一些办法来回避这些限制并保证它们的响应性。

  1. 对于对象,Vue 无法检测property 的添加或移除。
<div id="app">
    firstName:<input v-model='name.firstName'/><br>
    lastName:<input v-model='name.lastName'/><br>
    fullName: {{fullName}}
    <div @click="clickTry">clickTry</div>
</div>
  import Vue from 'vue'
  let data = {
      name: {
          firstName: 'Foo'
      },
      canShow: true
  }
  window.vm = new Vue({
    el: '#app',
    data,
    methods: {
      clickTry: function () {
          // 此场景
          this.name = Object.assign(this.name, {lastName: 'aaa' })
      }
    },
    computed: {
      fullName: function () {
          return this.name.firstName + ' ' + this.name.lastName
      }
    }
  })

执行结果:触发clickTry,未改变fullName值。(lastName是非响应式的) 原因,在初始化时未对lastName绑定检测

分析&解决方案:绑定监测(defineProperty)的实际:

a. 初始化initInjections时循环调用defineReactive$$1

b. Vue.set(this.name, 'lastName', 'aa')

c. this.$set(this.name, 'lastName', 'aa')

d. compiler后的input(v-model)的方法会调用defineReactive$$1

e. 改变父对象,重新绑定子对象

this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })

  1. 对于数组,Vue 不能检测以下数组的变动:

a. 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue b. 当你修改数组的长度时,例如:vm.items.length = newLength

分析:Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括 push() pop() shift() unshift() splice() sort() reverse()

解决方案:

a.Vue.set(vm.items, indexOfItem, newValue) b.vm.$set(vm.items, indexOfItem, newValue) c.vm.items.splice(indexOfItem, 1, newValue) d.替换数组