学习Vue的历程

86 篇文章 21 订阅

目录

Vue模板

生命周期图示

使用 JavaScript 表达式

参数

缩写

v-bind 缩写

计算属性

计算属性 vs 侦听属性

侦听器

渲染

​编辑

计算属性监听

数据监听 

Class 与 Style 绑定

 class绑定

​编辑

style绑定

 数据更改(v-on: 绑定)

 条件渲染

v-if

 v-else

 v-else-if

v-show

v-if vs v-show

v-if 与 v-for 一起使用

列表渲染

用 v-for 把一个数组对应为一组元素

在 v-for 里使用对象

 v-for 与 v-if 一同使用

 在组件上使用 v-for

 列表渲染

事件处理

监听事件

事件处理方法

 事件修饰符

按键修饰符

 鼠标按钮修饰符

表单输入绑定

 深度监听

 生命周期测试

属性绑定

组件基础 

data 必须是一个函数

组件基础

表单属性绑定

 通过 Prop 向子组件传递数据

指定插槽

​编辑

组件注册(局部注册、全局注册)

​编辑

组件注册

组件名

组件名大小写

 全局注册

 局部注册

 props组件通信

传递静态或动态 Prop

​编辑

单向数据流

Prop 验证

prop组件通信

prop父子组件的通信

 具名插槽

动态组件

 异步组件

混入

 自定义指令

 插槽的使用综合案例

插件

 插件

 过滤器

 渲染函数

 vueRouter路由重定向

 动态路由

 路由守卫(全局守卫、独享守卫、组件内守卫)

路由跳转


Vue模板

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <!-- 容器 -->
    <div id="app">{{msg}}</div>

    <script>
      // 整个页面只能有一个Vue实例对象
      let vm = new Vue({
        el: "#app",
        data: {
          msg: "hello",
        },
        methods: {},
      });
    </script>
  </body>
</html>

生命周期图示

下图展示了实例的生命周期。你不需要立马弄明白所有的东西,不过随着你的不断学习和使用,它的参考价值会越来越高。

 在底层的实现上,Vue 将模板编译成虚拟 DOM 渲染函数。结合响应系统,Vue 能够智能地计算出最少需要重新渲染多少组件,并把 DOM 操作次数减到最少。

使用 JavaScript 表达式

迄今为止,在我们的模板中,我们一直都只绑定简单的 property 键值。但实际上,对于所有的数据绑定,Vue.js 都提供了完全的 JavaScript 表达式支持。

{{ number + 1 }}

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

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

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

这些表达式会在所属 Vue 实例的数据作用域下作为 JavaScript 被解析。有个限制就是,每个绑定都只能包含单个表达式,所以下面的例子都不会生效。

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

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

指令 (Directives) 是带有 v- 前缀的特殊 attribute。指令 attribute 的值预期是单个 JavaScript 表达式 (v-for 是例外情况,稍后我们再讨论)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。回顾我们在介绍中看到的例子:

<p v-if="seen">现在你看到我了</p>

这里,v-if 指令将根据表达式 seen 的值的真假来插入/移除 <p> 元素。

参数

一些指令能够接收一个“参数”,在指令名称之后以冒号表示。例如,v-bind 指令可以用于响应式地更新 HTML attribute:

<a v-bind:href="url">...</a>

在这里 href 是参数,告知 v-bind 指令将该元素的 href attribute 与表达式 url 的值绑定。

另一个例子是 v-on 指令,它用于监听 DOM 事件:

<a v-on:click="doSomething">...</a>

在这里参数是监听的事件名。我们也会更详细地讨论事件处理。

缩写

v- 前缀作为一种视觉提示,用来识别模板中 Vue 特定的 attribute。当你在使用 Vue.js 为现有标签添加动态行为 (dynamic behavior) 时,v- 前缀很有帮助,然而,对于一些频繁用到的指令来说,就会感到使用繁琐。同时,在构建由 Vue 管理所有模板的单页面应用程序 (SPA - single page application) 时,v- 前缀也变得没那么重要了。因此,Vue 为 v-bindv-on 这两个最常用的指令,提供了特定简写:

v-bind 缩写

<!-- 完整语法 -->
<a v-bind:href="url">...</a>

<!-- 缩写 -->
<a :href="url">...</a>

<!-- 动态参数的缩写 (2.6.0+) -->
<a :[key]="url"> ... </a>

v-on 缩写

<!-- 完整语法 -->
<a v-on:click="doSomething">...</a>

<!-- 缩写 -->
<a @click="doSomething">...</a>

<!-- 动态参数的缩写 (2.6.0+) -->
<a @[event]="doSomething"> ... </a>

它们看起来可能与普通的 HTML 略有不同,但 :@ 对于 attribute 名来说都是合法字符,在所有支持 Vue 的浏览器都能被正确地解析。而且,它们不会出现在最终渲染的标记中。缩写语法是完全可选的,但随着你更深入地了解它们的作用,你会庆幸拥有它们。

计算属性

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

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

在这个地方,模板不再是简单的声明式逻辑。你必须看一段时间才能意识到,这里是想要显示变量 message 的翻转字符串。当你想要在模板中的多处包含此翻转字符串时,就会更加难以处理。

所以,对于任何复杂逻辑,你都应当使用计算属性

基础例子

<div id="example">
  <p>Original message: "{{ message }}"</p>
  <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('')
    }
  }
})

结果:

Original message: "Hello"

Computed reversed message: "olleH"

计算属性 vs 侦听属性

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

<div id="demo">{{ fullName }}</div>

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

上面代码是命令式且重复的。将它与计算属性的版本进行比较:

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

好得多了,不是吗?

 现在再运行 vm.fullName = 'John Doe' 时,setter 会被调用,vm.firstNamevm.lastName 也会相应地被更新。

侦听器

虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。

渲染

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
    <style>
      span {
        color: red;
      }
    </style>
  </head>
  <body>
    <!-- 容器 -->
    <div id="app">
      {{msg}}
      <hr />
      <div><span>123456</span></div>
      {{String}}
    </div>

    <script>
      // 整个页面只能有一个Vue实例对象
      let vm = new Vue({
        el: "#app",
        data: {
          msg: "hello",
          String: "<span>123456</span>",
        },
        methods: {},
      });
    </script>
  </body>
</html>

计算属性监听

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <!-- 容器 -->
    <div id="app">
        {{msg}}
        <hr>
        a: <input type="number" v-model.number="a"></input>
    <br>
    +
    <br>
        b:<input type="number" v-model.number="b"></input>
    <br>
    =
    <br>
        <div style="border: 1px solid red; width: 50px;height: 30px;">{{total}}</div>

    </div>

    <script>
    
      // 整个页面只能有一个Vue实例对象
      let vm = new Vue({
        el: "#app",
        data: {
          msg: "hello",
          a:'',
          b:'',
        },
        methods: {},
        //计算属性,有及时跟新的效果,随时监听效果
        computed:{
            total(){
                return this.a+this.b;
            }
        }
      });
    </script>
  </body>
</html>

数据监听 

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <!-- 容器 -->
    <div id="app">
        {{msg}}
        <hr>
        a: <input type="number" v-model.number="a"></input>
    <br>
    +
    <br>
        b:<input type="number" v-model.number="b"></input>
    <br>
    =
    <br>
        <div style="border: 1px solid red; width: 50px;height: 30px;">{{total}}</div>

    </div>

    <script>
    
      // 整个页面只能有一个Vue实例对象
      let vm = new Vue({
        el: "#app",
        data: {
          msg: "hello",
          a:'',
          b:'',
          total:''
        },
        methods: {},
        watch:{
            a(newValue,oldValue){
                console.log(newValue,oldValue);
                this.total=newValue+this.b;
            },
            b(newValue,oldValue){
                console.log(newValue,oldValue);
                this.total=newValue+this.a;
            }
        }
      });
    </script>
  </body>
</html>

Class 与 Style 绑定

操作元素的 class 列表和内联样式是数据绑定的一个常见需求。因为它们都是 attribute,所以我们可以用 v-bind 处理它们:只需要通过表达式计算出字符串结果即可。不过,字符串拼接麻烦且易错。因此,在将 v-bind 用于 classstyle 时,Vue.js 做了专门的增强。表达式结果的类型除了字符串之外,还可以是对象或数组。

 class绑定

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
    <style>
      .red {
        color: red;
      }
      .font {
        font-size: 30px;
      }
      .green {
        background-color: green;
      }
    </style>
  </head>
  <body>
    <!-- 容器 -->
    <div id="app">
      {{msg}}
      <div class="red" :class="{font:isFontSize}">hello</div>
      <!-- //可以写布尔值来判断是否要使用该样式 -->
      <div :class="{red:true,font:true}">world</div>
      <div :class="[{red:false},{font:false}]">world</div>
      <hr />
      <div :class="{red:isRed,green:isGreen}">hello</div>
    </div>

    <script>
      // 整个页面只能有一个Vue实例对象
      let vm = new Vue({
        el: "#app",
        data: {
          msg: "hello",
          isFontSize: true,
          isRed: false,
          isGreen: true,
        },
        methods: {},
      });
    </script>
  </body>
</html>

style绑定

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <!-- 容器 -->
    <div id="app">
      {{msg}}
      <hr />
      <div style="color: red; font-size: 30px">hello</div>
      <div :style="styleObj">hello</div>
      <!-- 可以自己内置写一个对象 -->
      <!-- 写死 ,且属性名需要用{}括起来-->
      <div :style="{color:'blue'}">hello</div>
      <!-- 不写死 -->
      <div :style="{color:currentCol}">hello</div>
      <div :style="[styleObj,styleObj2]">hello</div>
    </div>
    <script>
      // 整个页面只能有一个Vue实例对象
      let vm = new Vue({
        el: "#app",
        data: {
          msg: "hello",
          styleObj: {
            color: "blue",
            //短斜线是需要加引号
            "font-size": "40px",
            //驼峰命名法
            fontWeight: 700,
          },
          styleObj2: {
            color: "blue",
            //短斜线是需要加引号
            "font-size": "50px",
            //驼峰命名法
            fontWeight: 700,
          },
          currentCol: "yellow",
        },
        methods: {},
      });
    </script>
  </body>
</html>

 数据更改(v-on: 绑定)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="../js/vue.js"></script>
    <!-- <script>
      window.onload = function () {
        let vm = new Vue({
          el: "#app",
          data: {
            //写变量和声明,直接绑定到了实例上
            msg: "hello world",
          },
          methods: {
            //点击事件
            changeMsg() {
              console.log(this.msg);
              this.msg = 123;
            },
          },
        });

        //还可以这样改变值,因为上面说了变量直接绑定到了实例上
        //vm.msg = 123; //而不是vm.data.msg
      };
    </script> -->
  </head>
  <body>
    <!-- 通过vue实例对象中el数据绑定模板 -->
    <div id="app">
      {{msg}}
      <button @click="changeMsg">点击改变msg的值</button>
    </div>
    <script>
      //创建vue实例
      let vm = new Vue({
        // el:与dom进行绑定  根实例绑定方式
        el: "#app",
        //数据模型
        data: {
          //写变量和声明,直接绑定到了实例上
          msg: "hello world",
        },
        //写方法,函数
        methods: {
          //点击事件
          changeMsg() {
            console.log(this.msg);
            this.msg = 123;
          },
        },
      });

      //还可以这样改变值,因为上面说了变量直接绑定到了实例上
      //vm.msg = 123; //而不是vm.data.msg
      //vue实例中数据模型上绑定的变量和函数中声明的方法,都是直接绑定到vm实例上的,可以通过vm.msg直接调用

      //vue实例中访问绑定的属性和方法
    </script>
  </body>
</html>

点击即可改变

 条件渲染

v-if

v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 truthy 值的时候被渲染。

<h1 v-if="awesome">Vue is awesome!</h1>

也可以用 v-else 添加一个“else 块”:

<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>

 v-else

 v-else-if

v-show

另一个用于根据条件展示元素的选项是 v-show 指令。用法大致一样:

<h1 v-show="ok">Hello!</h1>

不同的是带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS property display

v-if vs v-show

v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。

v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。

相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。

一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。

v-if 与 v-for 一起使用

不推荐同时使用 v-ifv-for。请查阅风格指南以获取更多信息。

v-ifv-for 一起使用时,v-for 具有比 v-if 更高的优先级。请查阅列表渲染指南以获取详细信息。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      {{msg}}
      <hr />
      <div v-if="isLogin">欢迎您</div>
      <!-- <div v-if="!isLogin">请登录</div> -->
      <!-- 和if--else差不多 -->
      <div v-else>请登录</div>
      <div v-show="isLogin">show测试</div>
      <div>111</div>
    </div>

    <script>
      let vm = new Vue({
        el: "#app",
        data: {
          msg: "hello",
          isLogin: true,
        },
        methods: {},
      });
    </script>
  </body>
</html>

列表渲染

用 v-for 把一个数组对应为一组元素

我们可以用 v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名

 在 v-for 块中,我们可以访问所有父作用域的 property。v-for 还支持一个可选的第二个参数,即当前项的索引。

 你也可以用 of 替代 in 作为分隔符,因为它更接近 JavaScript 迭代器的语法:

<div v-for="item of items"></div>

在 v-for 里使用对象

 

 

 v-for 与 v-if 一同使用

 在组件上使用 v-for

 列表渲染

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Vue的数据模型</title>
        <script src="../js/vue.js"></script>
      </head>
      <body>
        <div id="app">
          {{msg}}
          <hr />
          <!-- 列表渲染 -->
          <ul>
            <li>苹果</li>
            <li>香蕉</li>
            <li>西瓜</li>
          </ul>
          <ul>
            <!-- key的作用 -->
            <li v-for="{item,index}in arr" :key="index">{{item}}</li>
          </ul>
          <hr />
          <!-- 表格渲染 -->
          <table>
            <thead>
              <tr>
                <th>编号</th>
                <th>姓名</th>
                <th>年龄</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="item in stu" :key="item.id">
                <td>{{item.id}}</td>
                <td>{{item.name}}</td>
                <td>{{item.age}}</td>
              </tr>
            </tbody>
          </table>
        </div>

        <script>
          let vm = new Vue({
            el: "#app",
            data: {
              msg: "hello",
              arr: ["苹果", "苹果", "香蕉", "西瓜"],
              stu: [
                {
                  id: 1,
                  name: "张三",
                  age: 20,
                },
                {
                  id: 1,
                  name: "李四",
                  age: 20,
                },
              ],
            },
            methods: {},
          });
        </script>
      </body>
    </html>
  </body>
</html>

事件处理

监听事件

可以用 v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。

示例:

事件处理方法

然而许多事件处理逻辑会更为复杂,所以直接把 JavaScript 代码写在 v-on 指令中是不可行的。因此 v-on 还可以接收一个需要调用的方法名称。

 

 

 事件修饰符

在事件处理程序中调用 event.preventDefault()event.stopPropagation() 是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。

为了解决这个问题,Vue.js 为 v-on 提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的。

.stop

.prevent

.capture

.self

.once

.passive

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
    <style>
      .outer {
        width: 200px;
        height: 200px;
        background-color: pink;
      }
      .inner {
        width: 100px;
        height: 100px;
        background-color: yellow;
        margin: 50px;
      }
    </style>
  </head>
  <body>
    <!-- 容器 -->
    <div id="app">
      {{msg}}
      <hr />
      <!--阻止事件冒泡 @click.stop=""、@click.self="" -->

      <!-- 在vue里面如何阻止事件冒泡  -->
      <!-- 第二种方法 使用:@click.self-->
      <div class="outer" @click.self="outer">
        out
        <!-- 第一种方法 使用:@click.stop -->
        <!-- <div class="inner" @click.stop="inner">inner</div> -->
        <div class="inner" @click="inner">inner</div>
      </div>

      <!-- prevent 阻止事件默认行为-->
      <!-- @click.prevent.once只阻止一次,第二次会解绑直接跳转 -->
      <a href="http://www.baidu.com" @click.prevent.once="toJump">百度一下</a>
      <!-- @click.once.prevent这里的顺序翻转没问题,但是其他并不一定可以 -->
      <a href="http://www.baidu.com" @click.once.prevent="toJump">百度一下</a>

      <hr />
      <!-- @keyup.enter="" 按下回车弹起触发  键盘事件-->
      <input type="text" @keyup.enter="keyUpHandle" />

      <hr />
      <!-- 鼠标事件 -->
      <input type="text" @mouseup.left="mouseUpHandle" />
    </div>

    <script>
      // 整个页面只能有一个Vue实例对象
      let vm = new Vue({
        el: "#app",
        data: {
          msg: "hello",
        },
        methods: {
          outer(e) {
            //e.target 触发事件的源头元素  e.currentTarget 执行事件处理程序的元素,当前目标元素
            console.log("outer", e.target, e.currentTarget);
          },
          inner(e) {
            console.log("inner", e.target, e.currentTarget);
            // e.stopPropagation  阻止事件冒泡,操作了dom
          },
          toJump() {
            alert("请确认");
          },
          keyUpHandle() {
            console.log("回车");
          },
          mouseUpHandle() {
            console.log("鼠标");
          },
        },
      });
    </script>
  </body>
</html>

 

 使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。

按键修饰符

 鼠标按钮修饰符

  • left
  • .right
  • .middle

为什么在 HTML 中监听事件?

你可能注意到这种事件监听的方式违背了关注点分离 (separation of concern) 这个长期以来的优良传统。但不必担心,因为所有的 Vue.js 事件处理方法和表达式都严格绑定在当前视图的 ViewModel 上,它不会导致任何维护上的困难。实际上,使用 v-on 有几个好处:

1.扫一眼 HTML 模板便能轻松定位在 JavaScript 代码里对应的方法。

2.因为你无须在 JavaScript 里手动绑定事件,你的 ViewModel 代码可以是非常纯粹的逻辑,和 DOM 完全解耦,更易于测试。

3.当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除。你无须担心如何清理它们。

表单输入绑定

 

 

 深度监听

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <!-- 容器 -->
    <div id="app">
      {{msg}} {{obj}}
      <br />
      <button @click="changeObj">更改obj数据</button>
    </div>

    <script>
      // 整个页面只能有一个Vue实例对象
      let vm = new Vue({
        el: "#app",
        data: {
          msg: "hello",
          obj: {
            name: "张三",
            age: "24",
          },
        },
        methods: {
          changeObj() {
            this.msg = "123";
            this.obj.name = "lisi";

            //因为obj对象是复杂数据类型,只能是浅监听监听不到
            //需要进行解构才能监听到
            // this.obj = {
            //   ...this.obj,
            //   name: "lisi",
            // };
          },
        },
        watch: {
          msg(newValue, oldValue) {
            console.log("msg数据发生变化");
          },
          //浅监听
          //   obj(newValue, oldValue) {
          //     console.log("obj数值发生变化");
          //   },

          //浅监听另一种写法
          msg: {
            handler(newValue, oldValue) {
              console.log("msg数值发生变化");
            },
          },

          //对于复杂数据类型,内部属性的改变,普通监听监听不到,如果要监听,使用深度监听
          //应用数据类型的监听(浅度监听),这是监听引用地址有没有变化
          //引用数据类型深度监听,内部属性发生变化时,才能监听到
          //深度监听
          obj: {
            handler(newValue, oldValue) {
              console.log("深度监听,obj数据发生改变");
            },
            deep: true,
          },
        },
      });
    </script>
  </body>
</html>

 生命周期测试

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <div id="app">
      {{msg}}
      <button @click="test">按钮</button>
      <ul id="My">
        <li>111</li>
      </ul> 
    </div>

    <script>
      let vm = new Vue({
        el: "#app",
        data: {
          msg: "hello",
          //   time: new Date(),
          msg: true,
        },
        methods: {
          test() {
            this.msg = "hello vue";
            console.log("点击按钮-----");
          },
        },
        //钩子函数,生命周期,在beforeCreate这个阶段,
        //现在属性msg数据还没有绑定到实例对象vm上,所以数据不能输出来
        //钩子函数在new Vue()之后执行 Vue内部给实例添加了一些属性(data)和方法(methods)
        //钩子函数在data和methods之前执行

        beforeCreate() {
          //属性或方法声明了,但没有和实例绑定
          console.log(
            "beforeCreated函数执行了,---但此时获取不到实例内的属性和方法",
            this.msg
          ); //输出:undefined
        },
        //生命周期,在created这个阶段属性msg数据绑定到vm实例对象上
        //data和methods之后执行
        created() {
          //属性和方法绑定到实例上,可以通过实例进行调用
          console.log(
            "created执行了,---此时可以获取data和methods中的变量值",
            this.msg
          ); //输出:'hello
        },

        /* 2.挂载 */
        //虚拟dom挂载成真实dom之前,不可以操作dom
        //使用场景,预处理data,不会触发update钩子函数
        beforeMount() {
          console.log(
            "beforeMount执行了,---但此时获取不到真实的DOM节点",
            "挂载之前"
          );
          console.log(document.getElementById("app"));
        },
        //虚拟dom挂载完毕变成真实dom,此处可以可以操作dom节点
        mounted() {
          console.log("mounted执行了,---此时可以获取到真实的DOM", "挂载后");
          console.log(document.getElementById("app"));
        },

        /*2.更新*/
        //更新前
        beforeUpdate() {
          console.log(document.querySelectorAll("#My>li"));
          console.log(
            "beforeUpdate函数执行了,---此时获取不到更新的真实DOM",
            this.msg
          );
        },
        //更新后
        //场景:获取到更新的真实DOM节点
        updated() {
          console.log(document.querySelectorAll("#My>div"));
          console.log(
            "update函数执行了,---此时可以获取到更新的真实DOM",
            this.msg
          );
        },

        /*销毁*/
        //销毁监听
        beforeDestroy() {
          console.log("beforeDestroy函数执行了", this.msg);
        },
        destroyed() {
          console.log("destroy函数执行了", this.msg);
        },
      });
      setInterval(() => {
        vm.$destroy();
      }, 5000);
    </script>
  </body>
</html>

属性绑定

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <!-- 容器 -->
    <div id="app">
      {{msg}}
      <hr />
      <div title="你好">你好我是vue</div>
      <div v-bind:title="msg">你好我是vue</div>
      <div :title="msg">你好我是vue</div>
      <!-- 这个直接显示msg,并不是变量 -->
      <div title="msg">你好我是vue</div>

      <!-- @点击事件绑定 -->
      <button v-on:click="btnHandler">按钮1</button>
      <button @click="btnHandler2">按钮2</button>
      <button @click="coust++">按钮2---{{coust}}</button>
    </div>

    <script>
      // 整个页面只能有一个Vue实例对象
      let vm = new Vue({
        el: "#app",
        data: {
          msg: "hello",
          coust: 1,
        },
        methods: {
          btnHandler() {
            console.log("按钮1111");
          },
          btnHandler2() {
            console.log("按钮2222");
          },
        },
      });
    </script>
  </body>
</html>

组件基础 

 

组件的复用

你可以将组件进行任意次数的复用:

<div id="components-demo">
  <button-counter></button-counter>
  <button-counter></button-counter>
  <button-counter></button-counter>
</div>

data 必须是一个函数

组件基础

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <!-- 容器 -->
    <div id="app">
      {{msg}}
      <!-- 重复代码片段的复用:组件的封装:就是把这片重复的代码片段封装起来 -->
      <!-- <div>
        <span></span>
        <div></div>
      </div> -->
      <my-com></my-com>
      <hr />
      <my-com></my-com>

      <!-- <div @click="const++">按钮{{comMsg}}</div> -->
      <!-- <div>按钮{{comMsg}}</div> -->
    </div>

    <script>
      //1.创建组件  组件声明  对象
      let myCom = {
        //组件的配置信息
        data() {
          return {
            comMsg: "组件内的msg",
            const: 1,
          };
        },
        methods: {},
        template: `
        <div>
            <span>hello Vue</span>
            <div>Hello Vue</div>
            <div>按钮{{comMsg}}</div>
            <button @click="comMsg=123">按钮</button>
        </div>
        `,
      };

      //2.组件注册
      //全局注册
      Vue.component("my-com", myCom);

      // 整个页面只能有一个Vue实例对象
      let vm = new Vue({
        el: "#app",
        //局部注册组件
        /* components: {
          "my-com": myCom,
        }, */
        data: {
          msg: "hello",
        },
        methods: {},
      });
    </script>
  </body>
</html>

表单属性绑定

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <!-- 容器 -->
    <div id="app">
      {{msg}} {{form}}
      <hr />

      <!--vue使用v-model进行数据绑定  如果有这些,则忽略value、checked、selected等等默认的值 ,则就是下面的value="你好"-->
      <!-- 即时跟新 -->
      <!-- 用户名:<input type="text" v-model="form.username" value="你好" /> -->
      <!-- lazy修饰符作用,失去焦点才跟新 -->
      用户名:<input type="text" v-model.lazy="form.username" value="你好" />

      <br />
      <!-- 即时更新 -->
      <!-- 年龄:<input type="text" v-model="form.age" /> -->
      <!-- 不完整规则校验number -->
      <!-- 年龄:<input type="number" v-model.number="form.age" /> -->城市: 上海
      <!-- 去掉两端空格trim ,无论两端多长空格后有数据,都只显示一个空格 -->
      年龄:<input type="text" v-model.trim="form.age" />

      <br />
      <!-- 选什么传递什么,互斥事件 -->
      性别:<input type="radio" value="male" v-model="form.gender" />男
      <input type="radio" value="female" v-model="form.gender" />女
      <br />
      爱好:<input type="checkbox" value="篮球" v-model="form.hobby" />篮球
      <input type="checkbox" value="足球" v-model="form.hobby" />足球
      <input type="checkbox" value="游泳" v-model="form.hobby" />游泳
      <input type="checkbox" value="跳舞" v-model="form.hobby" />跳舞
      <br />
      城市:
      <select v-model="form.city">
        <!-- //value是传递给后台的参数,这里和其他的value不一样 -->
        <option value="南京">南京</option>
        <option value="西安">西安</option>
        <option value="北京">北京</option>
        <option value="上海">上海</option>
      </select>
    </div>

    <script>
      // 整个页面只能有一个Vue实例对象
      let vm = new Vue({
        el: "#app",
        data: {
          msg: "hello",
          form: {
            username: "不好",
            gender: "male",
            //多选框必须加一个数组框 []:即声明为空数组
            hobby: [],
            //hobby: ["篮球"], //默认选中
            city: "上海", //默认选中
          },
        },
        methods: {},
      });
    </script>
  </body>
</html>

 通过 Prop 向子组件传递数据

早些时候,我们提到了创建一个博文组件的事情。问题是如果你不能向这个组件传递某一篇博文的标题或内容之类的我们想展示的数据的话,它是没有办法使用的。这也正是 prop 的由来。

Prop 是你可以在组件上注册的一些自定义 attribute。当一个值传递给一个 prop attribute 的时候,它就变成了那个组件实例的一个 property。为了给博文组件传递一个标题,我们可以用一个 props 选项将其包含在该组件可接受的 prop 列表中:

如上所示,你会发现我们可以使用 v-bind 来动态传递 prop。这在你一开始不清楚要渲染的具体内容,比如从一个 API 获取博文列表的时候,是非常有用的。

到目前为止,关于 prop 你需要了解的大概就这些了,如果你阅读完本页内容并掌握了它的内容,我们会推荐你再回来把 prop 读完。

 

 

指定插槽

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <!-- 容器 -->
    <div id="app">
      {{msg}}
      <hr />
      <my-com></my-com>
      <hr />
      <my-com>
        <!-- //添加给哪个插槽 -->
        <template v-slot:default>aaaa</template>
        <!-- //缩写 #slot2 -->
        <template #slot2>bbbb</template>
      </my-com>
    </div>

    <script>
      let myCom = {
        //如果设置为默认插槽,则都会被  我不好  这个值覆盖
        //如果不想被覆盖,那就要  具名化 进行区分
        template: `
      <div>
        <slot name="default">默认内容1</slot>
        <div>111111111</div>
        <div>222222222</div>
        <slot name="slot2">默认内容2</slot>
      </div>`,
      };
      Vue.component("my-com", myCom);
      // 整个页面只能有一个Vue实例对象
      let vm = new Vue({
        el: "#app",
        data: {
          msg: "hello",
        },
        methods: {},
      });
    </script>
  </body>
</html>

组件注册(局部注册、全局注册)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <!-- 容器 -->
    <div id="app">
      {{msg}}
      <br />
      <div>A</div>
      <!-- <my-com-a></my-com-a> -->
      <br />
      <div>B</div>
      <my-com-b></my-com-b>
    </div>

    <script>
      //第一步:声明组件
      let myComA = {
        template: `
              <div>A组件</div>
          `,
      };

      //什么是父组件 子组件  问题:组件通信 父子组件之间通信
      let myComB = {
        //局部注册,在哪里用写到哪里
        components: {
          "my-com-a": myComA,
        },
        //可以在myComB组件里使用myComA里面的组件
        //组件里面定义模板的时候只能有一个根标签,写在外面会报错或者没有渲染效果显示出来
        template: `
            <div>B组件
                <my-com-a></my-com-a>
            </div>
             
          `,
      };

      //第二步:全局注册组件
      //   Vue.component("my-com-a", myComA);
      Vue.component("my-com-b", myComB);

      // 整个页面只能有一个Vue实例对象
      let vm = new Vue({
        el: "#app",
        data: {
          msg: "hello",
        },
        methods: {},
      });
    </script>
  </body>
</html>

组件注册

组件名

在注册一个组件的时候,我们始终需要给它一个名字。比如在全局注册的时候我们已经看到了:

Vue.component('my-component-name', { /* ... */ })

该组件名就是 Vue.component 的第一个参数。

你给予组件的名字可能依赖于你打算拿它来做什么。当直接在 DOM 中使用一个组件 (而不是在字符串模板或单文件组件) 的时候,我们强烈推荐遵循 W3C 规范中的自定义组件名 (字母全小写且必须包含一个连字符)。这会帮助你避免和当前以及未来的 HTML 元素相冲突。

你可以在风格指南中查阅到关于组件名的其它建议。

组件名大小写

 全局注册

 局部注册

全局注册往往是不够理想的。比如,如果你使用一个像 webpack 这样的构建系统,全局注册所有的组件意味着即便你已经不再使用一个组件了,它仍然会被包含在你最终的构建结果中。这造成了用户下载的 JavaScript 的无谓的增加。

在这些情况下,你可以通过一个普通的 JavaScript 对象来定义组件:

 

 props组件通信

 

传递静态或动态 Prop

 

单向数据流

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

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

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

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

Prop 验证

我们可以为组件的 prop 指定验证要求,例如你知道的这些类型。如果有一个需求没有被满足,则 Vue 会在浏览器控制台中警告你。这在开发一个会被别人用到的组件时尤其有帮助。

为了定制 prop 的验证方式,你可以为 props 中的值提供一个带有验证需求的对象,而不是一个字符串数组。例如:

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
      }
    }
  }
})

prop组件通信

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <!-- 容器 -->
    <div id="app">
      {{msg}}
      <hr />
      <my-a title="hello"></my-a>
    </div>

    <script>
      let myA = {
        // props: ["title"],

        props: {
          msg: String,
          age: String,
          name: {
            type: String, //指定数据类型
            required: true, //指定是否必填
          },

          age: {
            type: Number,
            default: 100, //默认值,针对的是基本数据类型
          },

          obj: {
            type: Object,
            default: function () {
              //默认值,针对引用数据类型,Object/Array 都要一个工厂函数的形式返回
              return [1, 2, 3];
            },
          },
        },

        template: `<div>
            A组件---{{hello}}
            </div>`,
        data() {
          return {
            comMsg: "A组件得数据msg",
          };
        },
      };
      // 整个页面只能有一个Vue实例对象
      let vm = new Vue({
        el: "#app",
        data: {
          msg: "hello",
        },
        methods: {
          msg: hello,
        },
      });
    </script>
  </body>
</html>

prop父子组件的通信

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <!-- 容器 -->
    <div id="app">
      {{msg}}
      <hr />
      <div title="hello">你好</div> 
      <!-- //age="38"  age是字符串类型  :age="38" 绑定的age则是数字Number类型 -->
      <my-a title="hello" :msg="msg" :age="38" name="张三"></my-a>
    </div>

    <script>
      let myA = {
        /* props: ["title", "msg"],
              //父组件给子组件传值props接收就可以在子组件中使用 */

        //限制传值的类型,比如只能传递成字符串类型的,而上面的数组就是传什么就直接接收什么
        //转换成对象的写法,可以限制传入接收的数据类型,如果数据类型不对则接收失败
        props: {
          msg: String,
          //age:String,  设置为这个会报警告类型问题
          //age: Number,
          name: {
            //指定数据类型
            type: String,
            //代表子组件希望父组件传值的时候有一个name属性,并且这个name属性是必须有的
            required: true, //表示name属性必须有
          },

          /*  //基本数据类型
          //如果上面没有写 age: Number,那么默认值为100
          age: {
            type: Number,
            default: 100, //设置默认值为100
          }, */

          //复杂数据类型,例如 对象
          obj: {
            type: Object,
            //默认值,针对引用数据类型,Object/Array 都要一个工厂函数的形式返回
            default: function () {},
          },

          //复杂数据类型,例如 数组
          arr: {
            type: Array,
            //默认值,针对引用数据类型,Object/Array 都要一个工厂函数的形式返回
            default: function () {
              return [1, 2, 3];
            },
          },

          //自定义规则
          age: {
            type: Number,
            // validator: function () {},  完整写法
            validator(value) {
              //   if (value >= 50) {
              //     return true;
              //   } else {
              //     return false;
              //   }

              return value >= 50;
            },
          },
        },

        template: `
                  <div>A组件--{{msg}}--{{age}}</div>
                  `,
        data() {
          return {
            comMsg: "A组件的数据msg",
          };
        },
      };

      // 整个页面只能有一个Vue实例对象
      let vm = new Vue({
        el: "#app",
        //局部注册
        components: {
          "my-a": myA,
        },
        data: {
          msg: "hello world",
        },
        methods: {},
      });
    </script>
  </body>
</html>

 具名插槽

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <!-- 容器 -->
    <div id="app">
      {{msg}}
      <hr />
      <my-com></my-com>
      <hr />
      <my-com>
        <!-- //添加给哪个插槽 -->
        <!-- 访问不到组件内的数据comMsg-->
        <template v-slot:default>aaaa--{{msg}}</template>
        <!-- //v-slot:slot2 缩写 #slot2  只有这样写才能传参 -->
        <template #slot2="scope">
          bbbb--{{scope.comMsg}}--{{scope.title}}--{{scope.age}}
        </template>

        <!-- #slot2="{comMsg}"  解构得到comMsg -->
        <!-- <template #slot2="{comMsg}"> BBB--{{comMsg}} </template> -->
      </my-com>
    </div>

    <script>
      let myCom = {
        //注册的组件内一定是函数
        data() {
          return {
            comMsg: "组件的msg",
          };
        },
        //如果设置为默认插槽,则都会被  我不好  这个值覆盖
        //如果不想被覆盖,那就要  具名化 进行区分
        template: `
      <div>
        <slot name="default" >默认内容1</slot>
        <div>111111111</div>
        <div>222222222</div>
        <slot name="slot2" :comMsg='comMsg' title='这是title' :age="20">默认内容2</slot>
      </div>`,
      };
      Vue.component("my-com", myCom);
      // 整个页面只能有一个Vue实例对象
      let vm = new Vue({
        el: "#app",
        data: {
          msg: "hello",
        },
        methods: {},
      });
    </script>
  </body>
</html>

动态组件

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <!-- 容器 -->
    <div id="app">
      {{msg}}
      <hr />
      <button @click="currentCom='com-a'">A组件</button>
      <button @click="currentCom='com-b'">B组件</button>
      <button @click="currentCom='com-c'">C组件</button>
      <div style="border: 2px solid red; width: 200px; height: 200px">
        <!-- 静态is值  值为注册的组件的名称-->
        <!-- <component is="com-a"></component> -->
        <!-- 动态is值  值为绑定的一个变量名称-->
        <!-- //频繁组件切换,没有数据更新,可以用keep-alive标签组件缓存起来 -->
        <keep-alive>
          <component :is="currentCom"></component>
        </keep-alive>
        <!-- 不可行 -->
        <!-- <component :is="comC"></component>
        <component is="comC"></component> -->
      </div>
    </div>

    <script>
      let comA = {
        template: `
            <div>A组件的内容</div>
            `,
        created() {
          console.log("A组件的created");
        },
      };
      let comB = {
        template: `
            <div>B组件的内容</div>
            `,
        created() {
          console.log("B组件的created");
        },
      };
      let comC = {
        template: `
            <div>C组件的内容</div>
            `,
        created() {
          console.log("C组件的created");
        },
      };

      Vue.component("com-a", comA);
      Vue.component("com-b", comB);
      Vue.component("com-c", comC);

      // 整个页面只能有一个Vue实例对象
      let vm = new Vue({
        el: "#app",
        data: {
          msg: "hello",
          currentCom: comC,
        },
        methods: {
          clickBtn() {},
        },
      });
    </script>
  </body>
</html>

 异步组件

混入

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <!-- 容器 -->
    <div id="app">
      {{msg}}--{{mixMsg}}--{{obj}}
      <br />
      <button @click="test">按钮</button>
    </div>

    <script>
      // 声明一个混入对象
      let mixin = {
        data() {
          return {
            mixMsg: "混入的msg",
            //优先使用组件中的方法
            msg: "混入msg",
            obj: {
              name: "张三",
              age: 20,
            },
          };
        },
        created() {
          console.log("混入的created");
        },
        methods: {
          test() {
            console.log("这是混入test方法");
          },
        },
      };

      /*  //全局混入
      Vue.mixin(mixin); */

      //混入之后
      //数据模型data当中的属性会递归合并,有属性重名时,组件数据优先
      //声明周期合并,两个都会合并,两个都会执行,混入的钩子在组件自生钩子之前调用
      //值为对象的选项,methods、components、directives合并成一个对象,如果两个键名重名,以组件对象的值为主

      // 整个页面只能有一个Vue实例对象
      let vm = new Vue({
        el: "#app",
        // 局部混入
        mixins: [mixin],
        data: {
          msg: "hello",
        },
        created() {
          console.log("组件的created");
        },
        methods: {
          test() {
            console.log("组件test方法");
          },
        },
      });
    </script>
  </body>
</html>

 自定义指令

除了核心功能默认内置的指令 (v-modelv-show),Vue 也允许注册自定义指令。注意,在 Vue2.0 中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。举个聚焦输入框的例子,如下:

 

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <!-- 容器 -->
    <div id="app">
      {{msg}}
      <input type="text" />
      <br />
      <input type="text" v-focus />
    </div>

    <script>
      //声明指令
      let myFocus = {
        bind(el, binding, vNode, oldNode) {},
        inserted(el, binding, vNode, oldNode) {
          console.log(el);
          el.focus(); //聚焦元素
        },
        update(el, binding, vnNde, oldNode) {},
        componentUpdated(el, binding, vNode, oldNode) {},
        unbind(el, binding, vNode, oldNode) {},
      };

      //指令全局注册
      Vue.directive("focus", myFocus);
      // 整个页面只能有一个Vue实例对象
      let vm = new Vue({
        el: "#app",
        data: {
          msg: "hello",
        },
        methods: {},
      });
    </script>
  </body>
</html>

 插槽的使用综合案例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <!-- 容器 -->
    <!-- 用一个组件完成数据表格的渲染  数据:学生数组, 老师数组, 课程数组 -->
    <div id="app">
      {{msg}}
      <hr />
      <!-- //这样就写死了,需要动态获取多少行的数据 -->
      <!--  <thead>
          <tr>
            <th>编号</th>
            <th>姓名</th>
            <th>年龄</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>1</td>
            <td>张三</td>
            <td>23</td>
          </tr>
          <tr>
            <td>2</td>
            <td>张张</td>
            <td>22</td>
          </tr>
        </tbody> -->
      学生数组:
      <my-table :data="students">
        <template #header>
          <th prop="id">编号</th>
          <th>学号</th>
          <th>年龄</th>
        </template>
        <template #body="scope">
          <td>{{scope.row.id}}</td>
          <td>{{scope.row.name}}</td>
          <td>{{scope.row.age}}</td>
        </template>
      </my-table>

      <hr />
      老师数组:
      <my-table :data="teachers">
        <template #header>
          <th prop="id">编号</th>
          <th>学号</th>
          <th>年龄</th>
          <th>性别</th>
        </template>
        <template #body="scope">
          <td>{{scope.row.id}}</td>
          <td>{{scope.row.name}}</td>
          <td>{{scope.row.age}}</td>
          <td>{{scope.row.gender}}</td>
        </template>
      </my-table>

      <hr />
      课程数组:
      <my-table :data="courses">
        <template #header>
          <th prop="id">编号</th>
          <th>名字</th>
          <th>描述</th>
        </template>
        <template #body="scope">
          <td>{{scope.row.id}}</td>
          <td>{{scope.row.name}}</td>
          <td>{{scope.row.desc}}</td>
        </template>
      </my-table>
    </div>

    <script>
      Vue.component("my-table", {
        props: ["data"],
        data() {
          return {};
        },
        template: ` 
        <table>
            <thead>
          <tr>
            <!-- //在不能写死的部分使用插槽 -->
            <slot name="header"></slot>
          </tr>
        </thead>
        <tbody>
          <tr v-for="(item,index) in data" :key="index">
            <slot name="body" :row="item"></slot>
          </tr>
            </tbody>
        </table>
            `,
      });
      // 整个页面只能有一个Vue实例对象
      let vm = new Vue({
        el: "#app",
        data: {
          msg: "hello",
          students: [
            {
              id: 1,
              name: "张三",
              age: 23,
            },
            {
              id: 2,
              name: "小明",
              age: 20,
            },
          ],
          teachers: [
            {
              id: 1,
              name: "小李",
              age: "30",
              gender: "male",
            },
            {
              id: 2,
              name: "小红",
              age: "30",
              gender: "sex",
            },
            {
              id: 3,
              name: "小清",
              age: "30",
              gender: "sex",
            },
          ],
          courses: [
            {
              id: 1,
              name: "html",
              desc: "超文本",
            },
            {
              id: 2,
              name: "css",
              desc: "层叠样式",
            },
          ],
        },
        methods: {},
      });
    </script>
  </body>
</html>

插件

插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制——一般有下面几种:

  1. 添加全局方法或者 property。如:vue-custom-element

  2. 添加全局资源:指令/过滤器/过渡等。如 vue-touch

  3. 通过全局混入来添加一些组件选项。如 vue-router

  4. 添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。

  5. 一个库,提供自己的 API,同时提供上面提到的一个或多个功能。如 vue-router

 

 插件

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <!-- 容器 -->
    <div id="app">{{msg}}----{{msg|myFilter}}</div>

    <script>
      let myPlugin = {
        //istall方法在执行Vue.use(myPlugin) 会被调用
        install(Vue, options) {
          //提供一些静态属性或者方法
          Vue.isVue = function () {
            console.log("isVue");
          };

          //原型上绑定属性或者方法
          Vue.prototype.$sayVue = () => {
            console.log("isSayVue");
          };

          //全局注册(组件,过滤器,指令,混入)
          Vue.filter("myFilter", (data) => {
            return data.toUpperCase();
          });
        },
      };

      Vue.use(myPlugin);

      console.log(Vue.isVue());
      // 整个页面只能有一个Vue实例对象
      let vm = new Vue({
        el: "#app",
        data: {
          msg: "hello",
        },
        methods: {},
      });

      console.log(vm.$sayVue());
    </script>
  </body>
</html>

 过滤器

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <!-- 容器 -->
    <div id="app">
      {{msg}}---{{msg|parseUpper}}
      <hr />
      hhhhhjjjjjj
    </div>

    <script>
      /* //全局注册过滤器
      Vue.filter("parseUpper", (data) => {
        return data.toUpperCase();
      }); */

      // 整个页面只能有一个Vue实例对象
      let vm = new Vue({
        el: "#app",
        //局部注册过滤器  过滤器名称是函数名
        filters: {
          parseUpper(data) {
            return data.toUpperCase();
          },
        },
        data: {
          msg: "hello",
        },
        methods: {},
      });
    </script>
  </body>
</html>

 渲染函数

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
  </head>
  <body>
    <!-- 容器 -->
    <div id="app">
      {{msg}}
      <button @click="level++">按钮</button>
      <my-a :level="level">
        <template #header>hello </template>
      </my-a>
    </div>
    <script>
      let myA = {
        props: {
          level: {
            type: Number,
            required: true,
          },
        },
        /* template: `
        <div>
        <h1 v-if="level==1">
          <slot></slot>
        </h1>
        <h1 v-if="level==2">
          <slot></slot>
        </h1>
        <h1 v-if="level==3">
          <slot></slot>
        </h1>
       </div>
        `, */
        render(createElement) {
          //createdElement()创建出vNode 虚拟节点
          //   return createElement("h" + this.level, {}, "组件hello123");

          //this.slots.header   指定名字插槽
          return createElement(
            "h" + this.level,
            {
              /*  attrs: {
                id: "one",
                title: "你好",
              },
              style: {
                color: "red",
              },
              class: {
                box: true,
                box1: flase,
              }, */
            },
            this.$slots.header
          );
        },

        /*  //jsx  javascript扩展语法
        render() {
          return (
            <div>
              hello <h1>1111</h1>
            </div>
          );
        }, */
      };

      Vue.component("my-a", myA);
      // 整个页面只能有一个Vue实例对象
      let vm = new Vue({
        el: "#app",
        data: {
          msg: "hello",
          level: 1,
        },
        computed: {},
        methods: {},
      });
    </script>
  </body>
</html>

 vueRouter路由重定向

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
    <script src="../js/vue-router.js"></script>
    <style>
      .box {
        border: 1px solid red;
        width: 200px;
        height: 150px;
      }
    </style>
  </head>
  <body>
    <!-- 容器 -->
    <div id="app">
      {{msg}}---
      <a href="http://www.baidu.com">百度一下</a>
      <hr />
      <div>
        <router-link to="/a">去A组件</router-link>
        <router-link to="/b">去B组件</router-link>
      </div>
      <div class="box">
        <!-- 路由对应的组件显示的位置 -->
        <router-view></router-view>
      </div>
    </div>
    <script>
      /* 
        //1.声明路由对象
        let routes=[{

        }] */

      let comA = {
        template: `<div>A组件</div>`,
      };

      let comB = {
        template: `<div>B组件</div>`,
      };
      // 定义路由
      let router = new VueRouter({
        routes: [
          //默认根路径显示组件
          //   { path: "/", component: comA },
          //路由重定向
          { path: "/", redirect: "/a" },
          { path: "/a", component: comA },
          { path: "/b", component: comB },
        ],
      });

      // 整个页面只能有一个Vue实例对象
      let vm = new Vue({
        el: "#app",
        data: {
          msg: "hello",
        },
        methods: {},
        router: router,
      });
    </script>
  </body>
</html>

 动态路由

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
    <script src="../js/vue-router.js"></script>
    <style>
      .box {
        border: 1px solid red;
        width: 200px;
        height: 150px;
      }
    </style>
  </head>
  <body>
    <!-- 容器 -->
    <div id="app">
      {{msg}}---
      <a href="http://www.baidu.com">百度一下</a>
      <hr />
      <div>
        <router-link to="/a/1/zhangsan">查询张三的详情</router-link>
        <router-link to="/a/2/lisi">查询李四的详情</router-link>
      </div>
      <div class="box">
        <!-- 路由对应的组件显示的位置 -->
        <router-view></router-view>
      </div>
    </div>
    <script>
      let comA = {
        template: `<div>A组件--{{stuInfo.id}}--{{stuInfo.username}}</div>`,
        data() {
          return {
            stuInfo: {},
          };
        },
        created() {
          console.log(111, this.$route.params);
          //可以发送  ajax  请求  查询内容放入stuInfo数组
          this.stuInfo = this.$route.params;
        },
        /*  
        //通过监听方式  观察路由变化 
        watch: {
          // name(newValue,oldValue){}

          //   to:去的地方  from:来的地方
          $route(to, from) {
            console.log(222, to, from);
            this.stuInfo = to.params;
          },
        }, */

        //通过  路由守卫   监听路由的变化
        beforeRouteUpdate(to, from, next) {
          console.log("from", from);
          console.log("to", to);
          this.stuInfo = to.params;
          //next();  放行   next(false);  阻断   不写next(); 默认阻断
          next();
        },
      };

      // 定义路由
      let router = new VueRouter({
        routes: [{ path: "/a/:id/:username", name: "coma", component: comA }],
      });

      // 整个页面只能有一个Vue实例对象
      let vm = new Vue({
        el: "#app",
        data: {
          msg: "hello",
        },
        methods: {},
        router: router,
      });
    </script>
  </body>
</html>

 路由守卫(全局守卫、独享守卫、组件内守卫)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
    <script src="../js/vue-router.js"></script>
    <style>
      .box {
        border: 1px solid red;
        width: 200px;
        height: 150px;
      }
    </style>
  </head>
  <body>
    <!-- 路由守卫分为三种: 1.路由器上的  全局守卫  2.路由对象上的 独享守卫 3.组件内部的  组件守卫 -->
    <!-- 容器 -->
    <div id="app">
      {{msg}}---
      <a href="http://www.baidu.com">百度一下</a>
      <hr />
      <div>
        <router-link to="/a">去A组件</router-link>
        <router-link to="/b">去B组件</router-link>
      </div>
      <div class="box">
        <!-- 路由对应的组件显示的位置 -->
        <router-view></router-view>
      </div>
    </div>
    <script>
      let comA = {
        template: `<div>A组件--{{stuInfo.id}}--{{stuInfo.username}}</div>`,
        data() {
          return {
            stuInfo: {},
          };
        },
        created() {
          console.log(111, this.$route.params);
          //可以发送  ajax  请求  查询内容放params入stuInfo数组
          this.stuInfo = this.$route.params;
        },
        /*
          //通过监听方式  观察路由变化
          watch: {
            // name(newValue,oldValue){}

            //   to:去的地方  from:来的地方
            $route(to, from) {
              console.log(222, to, from);
              this.stuInfo = to.params;
            },
          }, */

        //通过  路由守卫   监听路由的变化
        //组件内部的路由守卫
        beforeRouteEnter(to, from, next) {
          //enter 阶段不可以使用this,update和leave阶段可以使用
        },
        beforeRouteUpdate(to, from, next) {
          console.log("from", from);
          console.log("to", to);
          this.stuInfo = to.params;
          //next();  放行   next(false);  阻断   不写next(); 默认阻断
          next();
        },
        beforeRouteLeave(to, from, next) {},
      };

      let comB = {
        template: `<div>B组件</div>`,
      };
      // 定义路由
      let router = new VueRouter({
        routes: [
          { path: "/a", name: "coma", component: comA },
          {
            path: "/b",
            name: "comb",
            component: comB,
            //独享守卫
            beforeEnter: (to, from, next) => {
              console.log(222, to, from);
              next();
            },
          },
        ],
      });

      //全局守卫  前置  后置
      router.beforeEach((to, from, next) => {
        console.log(333, to, from);
        next();
      });

      router.afterEach((to, from, next) => {
        console.log(444, to, from);
        next();
      });
      // 整个页面只能有一个Vue实例对象
      let vm = new Vue({
        el: "#app",
        data: {
          msg: "hello",
        },
        methods: {},
        router: router,
      });
    </script>
  </body>
</html>

路由跳转

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue的数据模型</title>
    <script src="../js/vue.js"></script>
    <script src="../js/vue-router.js"></script>
    <style>
      .box {
        border: 1px solid red;
        width: 200px;
        height: 150px;
      }
    </style>
  </head>
  <body>
    <!-- 容器 -->
    <div id="app">
      {{msg}}---
      <a href="http://www.baidu.com">百度一下</a>
      <hr />
      <div>
        <router-link to="/a">A 路由</router-link>
        <router-link to="/b">B 路由</router-link>
        <button @click="clickBtnA">C按钮</button>
      </div>
      <div class="box">
        <!-- 路由对应的组件显示的位置 -->
        <router-view></router-view>
      </div>
    </div>
    <script>
      /* 
        //1.声明路由对象
        let routes=[{

        }] */

      let comA = {
        template: `<div>A组件</div>`,
      };

      let comB = {
        template: `<div>B组件</div>`,
      };

      let comC = {
        template: `<div>C组件</div>`,
      };

      // 定义路由
      let router = new VueRouter({
        routes: [
          { path: "/a", name: "coma", component: comA },
          { path: "/b", name: "comb", component: comB },
          { path: "/c", name: "comc", component: comC },
        ],
      });

      // 整个页面只能有一个Vue实例对象
      let vm = new Vue({
        el: "#app",
        data: {
          msg: "hello",
        },
        methods: {},
        router: router,
        methods: {
          clickBtnA() {
            //window.history.go()  以前的方法  作用:往后退一步
            /*  //历史路由操作  作用:回退一步
            this.$router.go(-1);
            //回退跳转替换到指定页面
            this.$router.replace("/list"); */

            //   this.$route 获取当前路径
            // this.$router.push("/c");   //跳转到c路径下

            this.$router.push({
              //通过path跳转  但是拿不到params的内容  params传递的参数拿不到
              //   path: "/c",

              //通过nane跳转   可以拿到params的内容  params传递的参数能拿到
              //params 参数是一次性携带,刷新会消失,query刷新不会消失
              name: "comc",
              params: {
                name: "zhangsan",
              },
              query: {
                gender: "男",
              },
            });
          },
        },
      });
    </script>
  </body>
</html>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值