初学VUE&组件

vue router:用来做路由
vuex:用来管理
mvvm:model-view-viewmodel
cdn:内容分发网络
vue里的方法必须写到methods里面

Object.defineProperty(对象名,‘x’,操作对象):在一个对象上定义或修改新的值
操作对象中的get方法在调用obj.x时会自动执行,set方法在obj.x = 来赋值时会自动执行

特性检测
检测是否支持圆角和placeholder

检测是否是触屏设备
在这里插入图片描述

Vue底层原理

MVVM

MVVM是Model-View-ViewModel的简写,即模型-视图-视图模型。Model指的是后端传递的数据。View指的是所看到的页面。ViewModel是mvvm模式的核心,它是连接view和model的桥梁。它有两个方向:
1. 将Model转化成View,即将后端传递的数据转化成所看到的页面。实现的方式是:数据绑定。
2. 将View转化成Model,即将所看到的页面转化成后端的数据。实现的方式是:DOM事件事件监听。
3. 这两个方向都实现的,我们称之为数据的双向绑定。

模板字符串template

不能写if语句,{{}}中只能是表达式

vue指令

只有写在Vue实例的el内的元素才会识别vue指令

  • v-text:v-text可以用于替换插值表达式,如果有v-text,那么插值不起作用
    vue的指令都是v-指令这种格式,一旦使用了指令,虽然我们写的值看起来像字符串,因为我们使用的"值"已经是JavaScript的代码了,所以会报错
    错误写法:<div v-text=“这里是v-text的内容”>
    正确写法:<div v-text=" ‘这里是v-text的内容’ ">
    v-html:正确输出带有html标签的内容
    如果是富文本编辑器的内容,直接渲染的话,是被转义过的,那么html标签就会直接被渲染到页面,要正确输出正确的不带html标签转义的内容就要使用v-html指令

  • v-cloak:直接加在标签里面,可以设置css样式[v-cloak] { display: none;},这个样式只有在vue实例化之前才有效,实例化之后会自动失效。可以用来防止页面加载时闪烁出现vue标签或指令的问题

  • v-for:循环渲染数据,v-for="(item[,index]) in list",其中list为vue中data里的对象名(数组名),index会自动渲染list里的键名,item会自动渲染键值。
    :v-for是循环自身所在的标签

  • v-if: v-if=“判断式”,判断式为true就显示此条,否则不显示。其中判断式的左值在vue里定义。
    v-show:和v-if用法相似,但v-show是通过style的display来控制显示隐藏,而v-if是直接把该节点移除或插入。对于需要频繁切换显示和隐藏的节点v-show比较实用

  • v-on:v-on:事件=“赋值表达式”,绑定事件监听器,这个事件里可以直接操作data里的数据,可以和v-show/if配合使用。事件里也可以直接调用methods的方法
    :v-on:事件可以缩写为@事件
    用在普通元素上时,只能监听原生 DOM 事件。用在自定义元素组件上时,也可以监听子组件触发的自定义事件。

  • v-bind:v-bind:属性,用于动态绑定元素的属性,绑定了才能从data中获取值。v-bind:属性可以简写为:属性。
    绑定用户输入:

<body>
  <div id="root">
    <input type="text" :value="inputValue" @input="handleInput" />
    <p>您输入的内容是:{{inputValue}}</p>
  </div>
  <script src="./vue.js"></script>
  <script>
    const app = new Vue({
      el: "#root",
      data: {
        inputValue: ''
      },
      methods: {
        handleInput (e) {
          //input事件里的e.target可以获取输入框
          this.inputValue = e.target.value
        }
      }
    })
  </script>
</body>
  • v-model:绑定用户输入(<input type=“text” v-model="inputValue />,data:{inputValue: ‘’})。可以实现双向绑定,内容会根据改动而更新
<body>
  <div id="root">
    <input type="text" v-model="inputValue" />
    <p>您输入的内容是:{{inputValue}}</p>

    <input type="checkbox" v-model="hobi" value="打球">打球
    <input type="checkbox" v-model="hobi" value="被球打">被球打
    <input type="checkbox" v-model="hobi" value="被球员打">被球员打

    <p>你的爱好是:{{hobi}}</p>
  </div>
  <script src="./vue.js"></script>
  <script>
    const app = new Vue({
      el: "#root",
      data: {
        inputValue: '',
        hobi: ['打球']
      }
    })
  </script>
</body>
  • vue的事件:不加括号,默认事件处理函数接收event对象,加括号就可以传递想要的值

  • 按键修饰符:@keyup.enter:敲击回车触发。可以定义组合键,如:@keyup.ctrl.enter(同时敲击ctrl和回车);.prevent阻止默认事件

@click.stop:阻止事件冒泡
@click.prevent:阻止事件的默认行为

  • 样式绑定:class的样式可以通过true和false来控制(<div :class="{a: true,b: false}">,其中a和b都是定义的style)

  • map方法:创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。
    filter方法:创建一个新数组,其结果是该数组中通过筛选条件的元素

计算属性&监视&过滤器

computed:vue里的计算属性,和methods类似,有依赖缓存,只有它依赖的数据发生了改变才会重新计算,必须要有返回值。在插值表达式中调用computed的方法会直接在页面中打印方法的返回值。

可以这样理解,computed并不是一个方法,而是依赖于属性的,就是一个属性的封装,属性的值不变化,那么不会多次调用computed,所以性能更好

计算属性默认只有getter,需要时也可以提供setter。当调用这个计算属性就会自动调用get,赋值的时候就会自动调用set(value),其参数为赋的值

methods: 这是方法,每次有数据更改,只要在模板里有使用这个方法,这个方法就会执行。它是没有缓存的。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>simple todolist</title>
  <link href="https://cdn.bootcss.com/bulma/0.7.4/css/bulma.min.css" rel="stylesheet">
</head>

<body>
  <div id="root">
    <section class="hero is-primary">
      <div class="hero-body">
        <div class="container">
          <h1 class="title">
            待办事项列表
          </h1>
          <h2 class="subtitle">
            完成今天的事情,明天就可以做更多事情。老板的任务是做不完的。提高效率没有用
          </h2>
        </div>
      </div>
    </section>
    <section class="hero">
      <div class="container" style="margin-top: 15px">

        <div class="field has-addons">
          <div class="control">
            <input
              @keyup.enter="addTodo"
              v-model="inputValue"
              class="input"
              type="text"
              placeholder="请输入待办事项"
            >
          </div>
          <div class="control">
            <a @click="addTodo" class="button is-info">
              添加
            </a>
          </div>
          <div class="control">
            总共有{{totalTodosCount}}个任务,其中未完成{{unCompletedTodosCount}}个,已完成{{completedTodosCount}}</div>
        </div>
        <h4>未完成</h4>
        <div class="columns is-multiline is-mobile">

          <div
            class="column is-one-quarter"
            v-for="todo in unCompletedTodos"
            :key="todo.id"
          >
            <div class="card">
              <div class="card-content">
                <div class="content">
                  {{todo.title}}
                </div>
              </div>
              <footer class="card-footer">
                <span
                  :class="{
                    'card-footer-item': true,
                    'has-text-primary': todo.hasCompleted,
                    'has-text-danger': !todo.hasCompleted
                  }"
                >
                  {{ todo.hasCompleted ? '已完成' : '未完成'}}
                </span>
                <span
                  class="card-footer-item"
                  @click="toggleCompleted(todo.id)"
                >
                  标记为{{ todo.hasCompleted ? '未' : '已'}}完成
                </span>
              </footer>
            </div>
          </div>
        </div>
        <h4>已完成</h4>
        <div class="columns is-multiline is-mobile">

            <div
              class="column is-one-quarter"
              v-for="todo in completedTodos"
              :key="todo.id"
            >
              <div class="card">
                <div class="card-content">
                  <div class="content">
                    {{todo.title}}
                  </div>
                </div>
                <footer class="card-footer">
                  <span
                    :class="{
                      'card-footer-item': true,
                      'has-text-primary': todo.hasCompleted,
                      'has-text-danger': !todo.hasCompleted
                    }"
                  >
                    {{ todo.hasCompleted ? '已完成' : '未完成'}}
                  </span>
                  <span
                    class="card-footer-item"
                    @click="toggleCompleted(todo.id)"
                  >
                    标记为{{ todo.hasCompleted ? '未' : '已'}}完成
                  </span>
                </footer>
              </div>
            </div>
          </div>

      </div>
    </section>
  </div>
  <script src="./vue.js"></script>
  <script>
    new Vue({
      el: '#root',
      data: {
        inputValue: '',
        todos: [{
          id: 1,
          title: '吃饭',
          hasCompleted: true
        }, {
          id: 2,
          title: '再吃一次饭',
          hasCompleted: false
        }]
      },
      
      computed: {
        totalTodosCount () {
          console.log('total')
          return this.todos.length
        },
        completedTodosCount () {
          return this.todos.filter(todo => todo.hasCompleted === true).length
        },
        completedTodos () {
          return this.todos.filter(todo => todo.hasCompleted === true)
        },
        unCompletedTodosCount () {
          return this.totalTodosCount - this.completedTodosCount
        },
        unCompletedTodos () {
          return this.todos.filter(todo => todo.hasCompleted !== true)
        }
      },
      
      methods: {
        addTodo () {
          this.todos.push({
            id: Math.random(),
            title: this.inputValue,
            hasCompleted: false
          })
          this.inputValue = ''
        },
        toggleCompleted (id) {
          this.todos = this.todos.map(todo => {
            if (todo.id === id) {
              todo.hasCompleted = !todo.hasCompleted
            }
            return todo
          })
        }
      }
    })
  </script>
</body>

</html>

watch:监视已有的元素发生改变时会自动执行方法
:computed是新生成一个数据,wacth是观测已有的数据,当被观测的数据发生改变的时候,后面的方法会自动执行

Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch——特别是如果你之前使用过 AngularJS。然而,通常更好的做法是使用计算属性而不是命令式的 watch

watch与computed的区别

1.watch监控现有的属性,computed通过现有的属性计算出一个新的属性
2.watch不会缓存数据,每次打开页面都会重新加载一次,
但是computed如果之前进行过计算他会将计算的结果缓存,如果再次请求会从缓存中
得到数据(所以computed的性能比watch更好一些)

下面这段代码摘自 https://blog.csdn.net/soullines/article/details/80449321

### watch

var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar',
  },
  watch: {
    firstName: function (val) {
      this.fullName = val + ' ' + this.lastName
    },
    lastName: function (val) {
      this.fullName = this.firstName + ' ' + val
    }
  }
})

### computed
var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }
})

filters:过滤器可以在插值表达式或v-bind里加上 | 来实现过滤,过滤方法传入的参数为当前需要过滤的数据,返回过滤结果
例:

<body>
  <div id="app">
    {{name | firstToUpperCase}}
	<!-- 这里的sum调用了计算属性 -->
    {{sum | tofix}}

    {{orderStatus | formatOrderStatus}}
  </div>
  <script src="./vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        name: 'leo',
        x: 0.1,
        y: 0.2,
        orderStatus: '001'
      },
      filters: {
        firstToUpperCase(v) {
          console.log(v);
          return v.slice(0, 1).toUpperCase() + v.slice(1)
        },
        tofix(v) {
          console.log(v);
          return v.toFixed(2)
        },
        formatOrderStatus(v) {
          console.log(v);
          const orderStatusMap = {
            '001': '未付款',
            '002': '已付款',
            '003': '已发货',
            '004': '已签收',
            '005': '已评价'
          }
          return orderStatusMap[v]
        }
      },
      computed: {
        sum() {
          return this.x + this.y
        }
      }
    })
  </script>
</body>

computed、watch、filter总结

watch:监控已有属性,一旦属性发生了改变就去自动调用对应的方法
computed:监控已有的属性,一旦属性的依赖发生了改变,就去自动调用对应的方法
filter:js中为我们提供的一个方法,用来帮助我们对数据进行筛选

ajax:在created()里面用fetch方法做

axios:需要重新引入,也是用来发送ajax请求。在Vue这个对象的原型上扩展一个$http 让它指向于axios, 那么在实例上就可以直接使用this.$http来使用axios了

<div id="app">
    <ul>
      <li v-for="todo in todos" :key="todo.id">{{todo.title}}</li>
    </ul>
  </div>
  <script src="./vue.js"></script>
  <script src="./axios.min.js"></script>
  <script>
    // 在Vue这个对象的原型上扩展一个$http 让它指向于axios, 那么在实例上就可以直接使用this.$http来使用axios了
    Vue.prototype.$http = axios
    const app = new Vue({
      el: '#app',
      data: {
        todos: []
      },
      created() {
        this.$http.get('https://jsonplaceholder.typicode.com/todos')
          .then(resp => {
            // 这里是和后端约定好的一个状态,可以是任何的key, 只要约定好就行。
            if (resp.status === 200) {
              // 这里才是成功的逻辑
              this.todos = resp.data
            } else {
              // 处理错误的逻辑
            }
          })
          .catch(err => {
            // 处理错误的逻辑
          })
      }
    })
  </script>

组件

可以自定义html标签,用来展示更多的内容

组件配置项的模板只能有一个根元素,template可以写到script标签外面<template>渲染内容</template>

一般用局部组件

:当定义组件的名字为 kebab-case (短横线分隔命名)时,html中引用这个组件只能用kebab-case;当定义组件的名字为PascalCase (首字母大写命名)时,html中引用这个组件可以用kebab-case也可以用PascalCase

组件配置项:定义组件的执行内容,一般都用template渲染。可以定义组件的data方法(只能是方法),return一个对象,这样才能保证每个组件的数据是独立的而不是共享的。

component

全局组件
**Vue.component(组件名,组件配置项)**全局注册组件,定义的每个vue实例都可以引用这些组件。组件配置项可以放在外面定义,其内容会渲染组件。

components

局部组件

在vue里面定义一个components: {组件名:组件配置项},只有当前的Vue示例中才能使用这些组件

组件嵌套

一个组件的配置项里面可以嵌套另一个组件
:内层组件只能在外层组件的渲染模板里面使用,而且外层组件的template中必须要有一个唯一的根元素

props组件通信

传递非字符串类型的数据,需要使用动态绑定属性 v-bind:

以下描述的自定义属性并非真正的自定义属性,只是定义方式和定义位置和自定义属性类似

写法一:组件配置项里面加上props:[‘属性名’],其中的属性名是接收的组件里的自定义属性,将这个属性名作为插值表达式写入template里。
:组件的自定义属性名为props定义的需要在组件配置项中用到的,属性值为vue实例里的内容(data或computed等等),这也是父组件向子组件传值的方式

<body>
<div id="app">
  <!-- 在调用这个组件的时候通过text属性绑定一个值,在组件内部就可以通过props来接收这个值 -->
  <my-text :text="text1"></my-text>
  <my-text text="world"></my-text>
  <my-text text="!"></my-text>
</div>
<script src="./vue.js"></script>
<script>
const MyText = {
  template: '<span>{{text}}</span>',
  // 通过props来接收调用的时候传过来的值。一旦接收了,就可以直接把props当data用,但是不能直接在内部修改props的值。这是基于单向数据流的原则的。
  props: ['text']
}
const app = new Vue({
  el: '#app',
  data: {
    text1: 'hello',
    text2: 'world',
    text3: '!'
  },
  components: {
    MyText
  }
})
</script>
</body>

写法二:有时希望每个 prop 都有指定的值类型,可以以对象形式列出 props,这些属性的名称和值分别是 prop 各自的名称和类型

template: '<div>{{x}} + {{y}} = {{x + y}}</div>',
  // 如果要对传入的props进行类型检查,就需要使用对象的方式来写props
  props: {
    // 如果要对x进行更多约束,就可以再把这个x写成一个对象,里面可以有默认值,必须这些选项
    x: {
      //类型为number,如果传入的类型为string会报错
      type: Number,
      //必传就required: true
      required: true
    },
    y: {
      type: Number,
      //不需要传就设置默认值
      default: 0
    }
  }

:组件的自定义属性中,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名

Vue.component('blog-post', {
  // 在 JavaScript 中是 camelCase 的
  props: ['postTitle'],
  template: '<h3>{{ postTitle }}</h3>'
})
<!--HTML 中是 kebab-case-->
<blog-post post-title="hello!"></blog-post>

自定义事件

点击的时候先执行父组件下面的某一个方法,通过$emit和on做了中转

$emit(事件名,传递参数):事件名即为组件中的自定义事件名,这个事件调用的方法在父组件中执行,方法需要的参数即为子组件的emit中传递的参数

流程:子组件上触发事件的方法在子组件的methods中执行,子组件的方法通过$emit引用子组件的自定义事件而触发父组件对应的方法,所以实际上是调用的父组件的方法,因为数据在哪里方法就在哪里

父子组件通信:父组件通过props传递到子组件,子组件通过自定义事件传递到父组件,父组件调用子组件的同时注册一个监听(v-on),最终的数据修改在父组件里面

1.Vue创建的实例可以看做一个组件
2.父组件向子组件用子组件的自定义属性来传值

<body>
<div id="app">
  <!-- 在调用这个组件的时候,咱们就通过 @事件名 这种方式去监听组件内部触发的自定义事件, 当内部有这个自定义事件触发的时候,就会执行app里的onChangeText方法 -->
  <hello :hello-text="text" @change-text="onChangeText"></hello>
</div>
<script src="./vue.js"></script>
<script>
const Hello = {
  template: '<div>{{helloText}} <button @click="changeHelloText">改文字</button></div>',
  props: ['helloText'],
  methods: {
    changeHelloText () {
      // 如果我在这里直接写this.helloText = 'xxxx'就会报错,因为单向数据流的原因。
      // 如果要修改的话,就得找到data定义的地方,在这个例子里面是父级。
      // 我们就需要在这个组件的按钮点击事件上,通过this.$emite触发一个自定义事件
      this.$emit('change-text', Math.random().toString())
    }
  }
}
const app = new Vue({
  el: '#app',
  methods: {
    onChangeText (text) {
      // 最终的数据修改在父组件里面
      this.text = text
    }
  },
  data: {
    text: 'hello world!!!'
  },
  components: {
    Hello
  }
})
</script>
</body>

单向数据流
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
即子组件的方法不能修改父组件里的数据

插槽

作用:可以在自定义组件里面插入其它标签

slot 组件配置项里的slot标签需要有name属性,这个name的值对应组件里带有slot属性的标签且其值相等,当组件渲染的时候slot标签就会替换成相应的标签

const TodoHeader = {
	template: '<slot name="before"></slot>'
}

<todo-header>
	<h1 slot="before" class="title">待办事项列表</h1>
</todo-header>

组件化todolist

<body>
  <!-- todo-header组件的模板 -->
  <template id="todoHeader">
    <section class="hero is-primary">
      <div class="hero-body">
        <div class="container">
          <slot name="before"></slot>
          <h2 class="subtitle">
            完成今天的事情,明天就可以做更多事情。老板的任务是做不完的。提高效率没有用
          </h2>
          <slot name="after"></slot>
        </div>
      </div>
    </section>
  </template>

  <!-- todo-input组件的模板 -->
  <template id="todoInput">
    <div class="field has-addons">
      <div class="control">
        <input
          ref="myInput"
          @keyup.enter="addTodo"
          v-model="inputValue"
          class="input"
          type="text"
          placeholder="请输入待办事项"
        >
      </div>
      <div class="control">
        <a @click="addTodo" class="button is-info">
          添加
        </a>
      </div>
    </div>
  </template>

  <!-- card组件的模板 -->
  <template id="card">
    <div class="column is-one-quarter">
      <div class="card">
        <div class="card-content">
          <div class="content">
            {{title}}
          </div>
        </div>
        <footer class="card-footer">
          <span :class="{
                  'card-footer-item': true,
                  'has-text-primary': hasCompleted,
                  'has-text-danger': !hasCompleted
                }">
            {{ hasCompleted ? '已完成' : '未完成'}}
          </span>
          <span class="card-footer-item" @click="toggleCompleted">
            标记为{{ hasCompleted ? '未' : '已'}}完成
          </span>
        </footer>
      </div>
    </div>
  </template>

  <!-- 以下html可以理解为最外层的组件的模板 -->
  <div id="root"> <!-- 实例的挂载点 -->

    <!-- 使用todo-header, 它是没有props的,所以就直接用 -->
    <todo-header>
      <h1 slot="before" class="title">待办事项列表</h1>
      <h3 slot="after">Lorem ipsum dolor sit amet consectetur adipisicing elit. Sit non omnis, officia ratione perspiciatis ipsa minus dignissimos neque aliquam nobis ipsam consequatur, facere sunt quasi corrupti architecto quis eum sequi.</h3>
    </todo-header>
    <!-- 这些正常的html标签,不用管,你怎么写都行 -->
    <section class="hero">
      <div class="container" style="margin-top: 15px">
        <!-- 这是使用todo-input这个组件,由于这个组件内部会emit一个事件叫做add-todo, 所以我们要在这里创建监听, 当内部$emit('add-todo', 参数)的时候,就会执行这个模板所对应的组件(Vue实例)里的方法叫addTodoToData-->
        <todo-input @add-todo="addTodoToData"></todo-input>

        <h4>未完成</h4>
        <div class="columns is-multiline is-mobile">
          <!-- 这里使用card组件,card组件需要接收多个props,id, title, has-completed(html写法)在内部用hasCompleted来接收, 注意,这里是循环,需要有一个key, 还有就是,动态绑定 -->
          <!-- 内部还会$emit一个toggle-completed-by-id的自定义事件 -->
          <card
            v-for="todo in unCompletedTodos"
            :key="todo.id"
            :id="todo.id"
            :title="todo.title"
            :has-completed="todo.hasCompleted"
            @toggle-completed-by-id="toggleCompletedById"
          ></card>
        </div>

        <h4>已完成</h4>
        <div class="columns is-multiline is-mobile">
            <card
              v-for="todo in completedTodos"
              :key="todo.id"
              :id="todo.id"
              :title="todo.title"
              :has-completed="todo.hasCompleted"
              @toggle-completed-by-id="toggleCompletedById"
            ></card>
        </div>
        <div class="control">
          总共有{{totalTodosCount}}个任务,其中未完成{{unCompletedTodosCount}}个,已完成{{completedTodosCount}}</div>
      </div>
    </section>
  </div>
  
  <script src="./vue.js"></script>
  <script>
    // 这个组件最简单
    const TodoHeader = {
      template: '#todoHeader'
    }

    const TodoInput = {
      template: '#todoInput',
      data() {
        return {
          // 由于咱们不需要每次输入的时候都向外发送数据,所以这个inputValue就放在组件内部
          inputValue: ''
        }
      },
      methods: {
        // 当在组件内部点击添加的时候,会执行这个方法,当这个方法执行的时候,会向外发送一个add-todo的自定义事件,并且把this.inputValue传递出去
        addTodo() {
          this.$emit('add-todo', this.inputValue)
          this.inputValue = ''
          this.$refs.myInput.focus()
        }
      }
    }

    const Card = {
      template: '#card',
      props: {
        title: String,
        id: Number,
        hasCompleted: Boolean
      },
      methods: {
        toggleCompleted () {
          this.$emit('toggle-completed-by-id', this.id)
        }
      }
    }

    const app = new Vue({
      el: '#root',
      components: {
        TodoHeader,
        TodoInput,
        Card
      },
      data: {
        todos: [{
          id: 1,
          title: '吃饭',
          hasCompleted: true
        }, {
          id: 2,
          title: '再吃一次饭',
          hasCompleted: false
        }]
      },
      computed: {
        totalTodosCount() {
          console.log('total')
          return this.todos.length
        },
        completedTodosCount() {
          return this.todos.filter(todo => todo.hasCompleted === true).length
        },
        completedTodos() {
          return this.todos.filter(todo => todo.hasCompleted === true)
        },
        unCompletedTodosCount() {
          return this.totalTodosCount - this.completedTodosCount
        },
        unCompletedTodos() {
          return this.todos.filter(todo => todo.hasCompleted !== true)
        }
      },
      methods: {
        addTodoToData(todoTitle) {
          this.todos.push({
            id: Math.random(),
            title: todoTitle,
            hasCompleted: false
          })
        },
        toggleCompletedById(id) {
          this.todos = this.todos.map(todo => {
            if (todo.id === id) {
              todo.hasCompleted = !todo.hasCompleted
            }
            return todo
          })
        }
      }
    })
  </script>
</body>

兄弟组件通过中间的来通信

在vue里推荐使用ref的方式来获取dom或者组件,这样就可以使用this.$refs.name取到那个tag,<tag ref=“name”></tag>

ref 被用来给DOM元素或子组件注册引用信息。引用信息会根据父组件的 $refs 对象进行注册。如果在普通的DOM元素上使用,引用信息就是元素; 如果用在子组件上,引用信息就是组件实例
在这里插入图片描述

dynamic-component(动态组件)
component组件:<conponent is=“组件名”></component>,component组件就会替换成相应的组件内容

<body>
<div id="app">
  <ul>
    <li @click="currentComponent = 'guo-nei'">国内新闻</li>
    <li @click="currentComponent = 'guo-ji'">国际新闻</li>
  </ul>
  <component :is="currentComponent"></component>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
  el: '#app',
  data: {
    currentComponent: 'guo-nei'
  },
  components: {
    'guo-nei': {
      template: '<div>国内新闻的页面</div>'
    },
    'guo-ji': {
      template: '<div>international news</div>'
    }
  }
})
</script>
</body>

is可以实现语义化标签和解决标签不合法的情况

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值