Vue.js学习笔记

Vue.js学习笔记

  • 安装与部署
  • 创建第一个Vue应用
  • 数据
  • Vue提供的方法
  • 生命周期
  • 模板语法
        • 文本方式
        • 使用 JavaScript 表达式
  • 指令
        • 指令参数
        • 动态参数(2.6.0新增)
        • 对动态参数的值的约束
        • 指令缩写
  • 计算属性
  • class和style绑定
        • 对象方式绑定方式
        • 数组方式绑定
  • 条件渲染
        • v-if
        • v-show
        • v-if VS v-show
        • v-if 与 v-for 一起使用
  • 列表渲染
        • 在v-for中使用列表和对象
        • 维护状态
        • 数组更新检测
        • 对象更新检测
        • 在 v-for 里使用值范围
        • 在 template上使用 v-for
        • 在组件中使用v-for
  • 事件监听
        • v-on指令
        • 事件修饰符
        • 按键修饰符
        • 按键码
        • 鼠标按钮修饰符(2.2.0 新增)
        • 系统修饰键(2.1.0 新增)
        • .exact 修饰符(2.5.0 新增)
        • 小案例
  • 表单输入绑定
        • 基础用法
        • 手动实现v-model双向绑定
        • 值绑定
        • 修饰符
        • 在组件上使用 v-model
  • 组件基础
        • 创建组件
        • 组件的注册
        • 单个根元素
        • js分离template
        • 解析 DOM 模板时的注意事项
        • 父子组件
        • 父子组件通信
        • 父子组件通信--props的驼峰标识
        • 父访问子
        • 子访问父
        • 插槽slot
        • 动态组件
        • 监听子组件事件
        • 组件上使用 v-model
  • 组件注册
        • 组件名
        • 全局注册
        • 局部注册
        • 模块系统
  • 单文件组件
        • 创建组件

安装与部署

  登录官网 https://cn.vuejs.org/v2/guide/installation.html,下载开发版本或生产版本vue.js。然后将vue.js放在代码目录中,通过scrirt标签引入。
       
  或者使用cdn方式:
       
  生产版本,最好指明版本号:
       

创建第一个Vue应用

在引入vue.js后,在script标签代码块中可以通过new Vue()的方式创建一个新的应用:

  1. el属性 :即element,用于定位对应html视图中的所使用的id=“app”, 这里使用的是id选择器;
  2. data属性: 即app中要双向绑定的一些数据,这里绑定了message属性,在html中可以直接使用{{message}}来展示数据;
<body>
    <div id="app">
        {{message}}
    </div>
    {{message}}
    <script type="text/javascript">
        var data = {message: "Hello vue"};
        # 创建新的视图应用(viewModel)
        var vm = new Vue({
            el: "#app",
            data: data
        });
    </script>
</body>

在浏览器中查看效果,可以看到div标签内的message正常显示了数据,div之外的只显示了文本,即div的id=“app”定义了视图对应哪个应用,同时所在的div标签的范围即应用的作用域。
在这里插入图片描述

数据

增加两行代码:

<body>
    <div id="app1">
        {{message}}
        {{test}}  // 展示 test
    </div>
    {{message}}
    <script type="text/javascript">
        var data = {message: "Hello vue"};
        var app = new Vue({
            el: "#app1",
            data: data
        });
        // 修改message属性的值
        data.message = "Modify message value!";
        // 修改test属性的值
        data.test = "test";
    </script>
</body>

查看效果,发现data.message属性修改成功,但是data.test属性由于没有在初始化app的时候定义,修改失败。
即当一个 Vue 实例被创建时,它将 data 对象中的所有的属性加入到 Vue 的响应式系统中。当这些属性的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。
只有当实例被创建时就已经存在于 data 中的属性才是响应式的。
Vue.js学习笔记_第1张图片

 <body>
     <div id="app1">
         {{message}}
     </div>
     <script type="text/javascript">
         var data = {message: "Hello vue"};
         var vm = new Vue({
             el: "#app1",
             data: data
         });
         data.message = "abc";
         // 使用vm.message修改属性和修改原始数据具有同等效果
         vm.message = "bcd";
         console.log(vm.message === data.message);
     </script>
 </body>

Vue.js学习笔记_第2张图片

Vue提供的方法

<script type="text/javascript">
       var data = {message: "Hello vue"};
       var app = new Vue({
           el: "#app1",
           data: data
       });
       // 使用$watch监听message属性,当值发生变化时触发钩子函数
       app.$watch("message", function (newVle, oldVal) {
           console.log(newVle, oldVal)
       });
       data.message = "abc";
</script>

查看效果,当message被修改为“”abc“时出发了该方法,能获取new和old值。
Vue.js学习笔记_第3张图片
其他方法参考API文档:https://cn.vuejs.org/v2/api/#%E5%AE%9E%E4%BE%8B%E5%B1%9E%E6%80%A7

生命周期

每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。

官方文档:https://cn.vuejs.org/v2/api/#%E9%80%89%E9%A1%B9-%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E9%92%A9%E5%AD%90

<body>
    <div id="app1">
        {{message}}
    </div>
    <script type="text/javascript">
        var data = {message: "Hello vue"};
        var vm = new Vue({
            el: "#app1",
            data: data,
            // 在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用
            beforeCreate: function () {
                console.log("beforeCreate");
            },
            // 在实例创建完成后被立即调用。
            // 在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。
            // 然而,挂载阶段还没开始,$el 属性目前尚不可用。
            created: function () {
                console.log("created");
            },
            // 在挂载开始之前被调用:相关的 render 函数首次被调用。该钩子在服务器端渲染期间不被调用。
            beforeMount: function () {
                console.log("beforeMount");
            },
            // 实例被挂载后调用,这时el被新创建的vm.$el替换了。
            // 如果根实例挂载到了一个文档内的元素上,当mounted被调用时vm.$el也在文档内。
            // 注意: mounted 不会保证所有的子组件也都一起被挂载。
            // 如果你希望等到整个视图都渲染完毕,可以在 mounted 内部使用 vm.$nextTick:
            // 该钩子在服务器端渲染期间不被调用。
            mounted: function () {
                this.$nextTick(function () {
                    // Code that will run only after the
                    // entire view has been rendered
                });
                console.log("mounted");
            },
            // 数据更新时调用,发生在虚拟 DOM 打补丁之前。
            // 这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。
            // 该钩子在服务器端渲染期间不被调用,因为只有初次渲染会在服务端进行。
            beforeUpdate: function () {
                console.log("beforeUpdate");
            },
            // 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
            // 当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。
            // 然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或 watcher 取而代之。
            // 注意 updated 不会保证所有的子组件也都一起被重绘。如果你希望等到整个视图都重绘完毕,可以在 updated 里使用 vm.$nextTick:
            // 该钩子在服务器端渲染期间不被调用。
            updated : function () {
                this.$nextTick(function () {
                    // Code that will run only after the
                    // entire view has been re-rendered
                });
                console.log("updated");
            },
            // 被 keep-alive 缓存的组件激活时调用。
            // 该钩子在服务器端渲染期间不被调用。
            activated: function () {
                console.log("activated");
            },
            // 被 keep-alive 缓存的组件停用时调用。
            // 该钩子在服务器端渲染期间不被调用。
            deactivated: function () {
                console.log("deactivated");
            },
            // 实例销毁之前调用。在这一步,实例仍然完全可用。
            // 该钩子在服务器端渲染期间不被调用。
            beforeDestroy: function () {
                console.log("beforeDestroy");
            },
            // 实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。
            // 该钩子在服务器端渲染期间不被调用。
            destroyed: function () {
                console.log("destroyed");
            }
        });
        vm.message = "test111";
    </script>
</body>

最后一行修改vm.message属性时出发了beforeUpdate和updated钩子。
Vue.js学习笔记_第4张图片

模板语法

文本方式

数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值:
msg需要在vm的data中定义,当vm中的msg值发生变化时,视图中也会刷新:

	<span>Message: {{ msg }}</span>

使用 JavaScript 表达式

对于所有的数据绑定,Vue.js 都提供了完全的 JavaScript 表达式支持。
如下:计算、三目表达式、js的方法

	<body>
	    <div id="app1">
	        <p>计算: {{num + 10}}</p>
	        <p>三目表达式: {{ok ? "YES": "NO"}}</p>
	        <p>js方法: {{string.split("").reverse().join("")}}</p>
	    </div>
	    <script type="text/javascript">
	        var data = {num: 1, string: "abc", ok: true};
	        var vm = new Vue({
	            el: "#app1",
	            data: data
	        });
	    </script>
	</body>

Vue.js学习笔记_第5张图片

指令

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

   // 当ok属性为true时,p标签内容会展示出来。反之,会隐藏。
   <p v-if="ok">现在你看到我了</p>

** v-cloak 指令**: 这个指令保持在元素上直到关联实例结束编译。和 CSS 规则如 [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕。

	<body>
	    <div id="app">
	        <p>{{message}}</p>
	    </div>
	<script type="text/javascript">
		// 构造异常场景,1秒后再实例化vue;这会导致用户进入页面时会看到{{message}},等到vue实例化后才会去渲染message。
	    setTimeout(function () {
	        var vm = new Vue({
	            el: "#app",
	            data: {
	                message: "abc"
	            }
	        })
	    }, 1000)
	</script>

解决办法,html中添加v-cloak属性,然后样式中添加[v-cloak] { display: none } ,初始时会隐藏掉div标签。等vue实例化后,会去删除v-cloak属性,这样div就可以显示出来,数据正常渲染。

    <div id="app" v-cloak>
        <p>{{message}}</p>
    </div>
	<style>
	    [v-cloak] {
	        display: none;
	    }
	</style>

** v-text 指令** : v-text指令与mustache语法作用类似,但是v-text不实用,不能在前后拼接一些文本。

    <span>Message: {{ msg }}</span>
	<span v-text="msg"></span>

** v-pre 指令**: v-pre指令用于显示原始文本,即不经过vue编译转换。

	<p>{{message}}</p>
    <p v-pre>{{message}}</p>

在这里插入图片描述
v-once 指令:通过使用 v-once 指令,可以执行一次性地插值,即元素或组件只会渲染一次,当数据改变时,插值处的内容不会更新。但请留心这会影响到该节点上的其它数据绑定:

	    <body>
        <div id="app1">
            <span v-once>这个将不会改变: {{message}}</span>
            <p></p>
            {{message}}
        </div>
        <script type="text/javascript">
            var data = {message: "Hello vue"};
            var vm = new Vue({
                el: "#app1",
                data: data
            });
            vm.message = "test111";
        </script>
    </body>

Vue.js学习笔记_第6张图片
** v-html 指令**:输出原始HTML。
如果后台返回的数据是一个html标签,前台为了输出真正的 HTML,需要使用 v-html 指令:

<body>
    <div id="app1">
        // 通过v-html输出原始html
        <p v-html="message"></p>
        {{message}}
    </div>
    <script type="text/javascript">
        var data = {message: "Hello vue"};
        var vm = new Vue({
            el: "#app1",
            data: data
        });
        vm.message = "Original html";
    </script>
</body>

Vue.js学习笔记_第7张图片
** v-bind 指令**: 动态绑定属性Attribute。
使用 v-bind 指令修改 HTML attribute:
例如有一种场景,color属性会变化,需要页面对应的字体随之变化。

<body>
    <div id="app1">
        // 通过v-bind 绑定class属性
        <p v-bind:class="color">use v-bind.</p>
        {{message}}
    </div>
    <script type="text/javascript">
        var data = {message: "Hello vue",
                    color: "red"};
        var vm = new Vue({
            el: "#app1",
            data: data
        });
        vm.message = "";
    </script>
</body>
<style type="text/css">
    .red {color: red;}
</style>

指令参数

一些指令能够接收一个“参数”,在指令名称之后以冒号表示。例如,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>

在这里参数是监听的事件名,需要在vm的methods中定义doSomething。

<body>
    <div id="app1">
        <a v-bind:href="href">链接</a>
        <p v-on:click="doSomething">click me</p>
    </div>
    <script type="text/javascript">
        var data = {href: "https://cn.vuejs.org/v2/guide/syntax.html"};
        var vm = new Vue({
            el: "#app1",
            data: data,
            methods: {
                doSomething: function () {
                    console.log("v-on click method.")
                }
            }
        });
    </script>
</body>

Vue.js学习笔记_第8张图片

动态参数(2.6.0新增)

从 2.6.0 开始,可以用方括号括起来的 JavaScript 表达式作为一个指令的参数:

	<a v-bind:[attributeName]="url"> ... </a>

这里的 attributeName 会被作为一个 JavaScript 表达式进行动态求值,求得的值将会作为最终的参数来使用。
例如,如果 Vue 实例有一个 data 属性 attributeName,其值为 “href”,那么这个绑定将等价于 v-bind:href。
同样地,可以使用动态参数为一个动态的事件名绑定处理函数:

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

对动态参数的值的约束

  1. 特殊字符:某些字符,如空格引号,放在 HTML attribute 名里是无效的。例如:
	<!-- 这会触发一个编译警告 -->
	<a v-bind:['h' + bar]="value"> ... </a>

Vue.js学习笔记_第9张图片
因此,要使用没有空格引号的表达式,或用计算属性替代这种复杂表达式。

  1. 避免使用大写字符来命名键名,因为浏览器会把 attribute 名全部强制转为小写:
    如下: 视图中v-bind绑定的键名为someAttr,浏览器会自动转换为someattr,但是vue中只定义了大写的someAttr,就会导致找不到someattr而报错。
<body>
    <div id="app1">
        <a v-bind:[someAttr]="value">链接</a>
    </div>
    <script type="text/javascript">
        var data = {value: "https://cn.vuejs.org/v2/guide/syntax.html", someAttr: "href"};
        var vm = new Vue({
            el: "#app1",
            data: data
        });
    </script>
</body

Vue.js学习笔记_第10张图片
因此,需要在vue中定义小写的someattr,或视图中也使用小写的字符:

var data = {value: "https://cn.vuejs.org/v2/guide/syntax.html", someattr: "href"};

指令缩写

Vue 为 v-bind 和 v-on 这两个最常用的指令,提供了特定简写:

	<!-- 完整语法 -->
	<a v-bind:href="url">...</a>
	
	<!-- 缩写 -->
	<a :href="url">...</a>
	
	<!-- 动态参数的缩写 (2.6.0+) -->
	<a :[key]="url"> ... </a>
	<!-- 完整语法 -->
	<a v-on:click="doSomething">...</a>
	
	<!-- 缩写 -->
	<a @click="doSomething">...</a>
	
	<!-- 动态参数的缩写 (2.6.0+) -->
	<a @[event]="doSomething"> ... </a>

计算属性

计算属性: 如果要定义一个属性,但是需要通过计算才能确定值,比如拼接、计算,那么可以通过computed定义计算属性。

  1. 计算属性的完整形式
    计算属性定义实际包含get和set方法,但是大多数情况下set方法都不自定义。
    // 使用
	<p>{{fullName}}</p>

	
    var vm = new Vue({
        el: "#app",
        data: {
            firstName: "Kobe",
            lastName: "Byrant",
        },
        computed: {
        	// 定义计算属性,在html中调用时,实际上是调用了get方法。由于get方法返回值不是一个方法,所以在html中使用时不需要加()。
            fullName: {
                get: function () {
                    return this.firstName + " " + this.lastName
                },
                set: function (newName) {
                    console.log("newName: " + newName);
                    const name = newName.split(" ");
                    this.firstName = name[0];
                    this.lastName = name[1];
                }
            }
        }
    })
  1. 计算属性的简写方式
    由于的set方法很少使用,所以可以采用简写方式:如下:省去set方法和get方法的key。
	<div id="app">
	    <p>总价格:{{totalPrice}}</p>
	</div>
	<script type="text/javascript">
	    var vm = new Vue({
	        el: "#app",
	        data: {
	            books: [
	                {id: 1, name: "a", price: 20},
	                {id: 2, name: "b", price: 30},
	                {id: 3, name: "c", price: 40},
	                {id: 4, name: "d", price: 50},
	                {id: 5, name: "e", price: 60}
	            ]
	        },
	        computed: {
	            // 定义计算属性
	            totalPrice: function () {
	                let price = 0;
	                for (let i=0; i<this.books.length; i++) {
	                    price += this.books[i].price;
	                }
	                return price
	            }
	        }
	    })
	</script>
  1. 计算属性的缓存
    计算属性内部有做缓存,如果计算相关的变量没有变化,则不会再重复去做计算操作。
    如下,分别使用method和computed两种方式展示fullName,getFullName方法是会多次调用的,fullName只调用了一次:
	<body>
	    <div id="app" v-cloak>
	        <p>{{getFullName()}}</p>
	        <p>{{getFullName()}}</p>
	        <p>{{getFullName()}}</p>
	        <p>{{fullName}}</p>
	        <p>{{fullName}}</p>
	        <p>{{fullName}}</p>
	    </div>
	<script type="text/javascript">
	    var vm = new Vue({
	        el: "#app",
	        data: {
	            firstName: "Kobe",
	            lastName: "Byrant",
	        },
	        methods: {
	            getFullName: function () {
	                console.log("method---getFullName");
	                return this.firstName + " " + this.lastName
	            }
	        },
	        computed: {
	            fullName: function () {
	                console.log("computed---fullName");
	                return this.firstName + " " + this.lastName
	            }
	        }
	    })
	</script>

Vue.js学习笔记_第11张图片
修改firstName值,计算属性fullName会重新计算full:
Vue.js学习笔记_第12张图片

class和style绑定

操作元素的 class 列表和内联样式style是数据绑定的一个常见需求。因为它们都是属性,所以我们可以用 v-bind 处理它们:只需要通过表达式计算出字符串结果即可。vue还提供对象或数组的方式。

对象方式绑定方式

  1. 如下,赋值给v-bind:class一个对象,其中active、bg样式属性为true时渲染该样式。同时,v-bind:class和普通的class可以同时使用。
    但是当两者冲突时,由于v-bind:class会覆盖普通class。
    // class 对象形式绑定
	<p v-bind:class="{类1: boolean, 类2: boolean....}"> 链接 </p>
    // style 对象形式绑定
	<p class="borderClass" v-bind:style="{fontSize: '50px', color: 'red'}">{{message}}</p>
<body>
    <div id="app1">
        <p class="size" v-bind:class="{active: isActive, bg: showBg}"> 链接 </p>
    </div>
    <script type="text/javascript">
        var data = {isActive: true, showBg: true};
        var vm = new Vue({
            el: "#app1",
            data: data
        });
    </script>
</body>
<style type="text/css">
	.size {font-size: 20px;}
    .active {color: red;}
    .bg {background-color: blue}
</style>

渲染结果就是class=“size active bg”:
Vue.js学习笔记_第13张图片

  1. 绑定的数据对象不必内联定义在模板里,通过在data中定义classObject的方式,在v-bind:class中绑定classObject也可以达到同样的效果:
<body>
    <div id="app1">
        <p class="size" v-bind:class="classObject"> 链接 </p>
    </div>
    <script type="text/javascript">
        var vm = new Vue({
            el: "#app1",
            data: {
                classObject: {
                    active: true,
                    bg: true
                }
            }
        });
    </script>
</body>
  1. 通过方法也可以实现同样效果:
  <p class="borderClass" v-bind:class="classed()">{{message}}</p>

  // 定义方法
  methods: {
    classed: function () {
        return {active: this.isActive, bg: this.showBg}
    }
}
  1. 绑定一个返回对象的计算属性,是一个常用且强大的模式:
<body>
    <div id="app1">
        <p class="size" v-bind:class="classObject"> 链接 </p>
    </div>
    <script type="text/javascript">
        var vm = new Vue({
            el: "#app1",
            data: {
                isActive: true,
                bgShow: false
            },
            // 计算
            computed: {
                classObject: function () {
                    return {
                        active: this.isActive && this.bgShow,
                        bg: this.bgShow
                    }
                }
            }
        });
    </script>
</body>

数组方式绑定

可以把一个数组传给 v-bind:class,以应用一个 class 列表:
列表中还可以使用对象或者三目表达式的方式。

<body>
    <div id="app1">
        <p class="size" v-bind:class="[activeClass, borderShow ? 'borderClass' : '', {bg: bgShow}]"> 链接 </p>
    </div>
    <script type="text/javascript">
        var vm = new Vue({
            el: "#app1",
            data: {
                activeClass: "active",
                borderShow: true,
                bgShow: true
            },
        });
    </script>
</body>
<style type="text/css">
    .size {font-size: 20px;}
    .active {color: red;}
    .borderClass {border: 3px solid black}
    .bg {background-color: blue;}
</style>

Vue.js学习笔记_第14张图片
v-bind:style 数组形式:

   <div id="app" v-cloak>
        <p class="borderClass" v-bind:style="[style1, style2]">{{message}}</p>
    </div>
	<script type="text/javascript">
	    var vm = new Vue({
	        el: "#app",
	        data: {
	            message: "abc",
	            style1: {fontSize: '50px'},
	            style2: {color: 'red'}
	        }
	    })
	</script>

条件渲染

v-if

  1. v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 truthy 值的时候被渲染。其中v-else-if为2.1.0 新增语法。
    如下,type属性为"",if和else if条件都不满足,所有
<body>
    <div id="app1">
        <span v-if="type==='A'">A</span>
        <span v-else-if="type==='B'">B</span>
        <span v-else-if="type==='C'">C</span>
        <span v-else>Not A/B/C</span>
    </div>
    <script type="text/javascript">
        var vm = new Vue({
            el: "#app1",
            data: {
                type: "B"
            },
        });
    </script>
</body>

Vue.js学习笔记_第15张图片

  1. 在 元素上使用 v-if 条件渲染分组:
    因为 v-if 是一个指令,所以必须将它添加到一个元素上。但是如果想切换多个元素呢?此时可以把一个 元素当做不可见的包裹元素,并在上面使用 v-if。最终的渲染结果将不包含 元素。
<div id="app1">
    <template v-if="type==='A'">
        <h1>Title</h1>
        <p>Paragraph 1</p>
        <p>Paragraph 2</p></p>
    </template>
    <template v-else-if="type==='B'">
        <h1>Title</h1>
        <p>Paragraph 1</p>
        <p>Paragraph 2</p>
    </template>
    <template v-else>Not A/B/C</template>
</div>

Vue.js学习笔记_第16张图片

  1. 用 key 管理可复用的元素
    Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这么做除了使 Vue 变得非常快之外,还有其它一些好处。
    例如,如果允许用户在不同的登录方式之间切换:
<div id="app1">
    <template v-if="loginType === 'username'">
        <label>Username</label>
        <input placeholder="Enter your username">
    </template>
    <template v-else>
        <label>Email</label>
        <input placeholder="Enter your email address">
    </template>
    <p></p>
    <button v-on:click="modifyLogin()">modifyLogin</button>
</div>
<script type="text/javascript">
    var vm = new Vue({
        el: "#app1",
        data: {
            loginType: "username"
        },
        methods: {
            modifyLogin: function () {
            	// 切换loginType值
                this.loginType = this.loginType === "username" ? "" : "username";
            }
        }
    });
</script>

切换 loginType 将不会清除用户已经输入的内容。因为两个模板使用了相同的元素, 不会被替换掉——仅仅是替换了它的 placeholder。
Vue.js学习笔记_第17张图片
另外一种场景,如果是“这两个元素是完全独立的,不要复用它们”。只需添加一个具有唯一值的 key 属性即可:

<div id="app1">
    <template v-if="loginType === 'username'">
        <label>Username</label>
        <!-- input标签给定唯一的key属性,每次切换时vue都会重新刷新dom -->
        <input placeholder="Enter your username" key="username">
    </template>
    <template v-else>
        <label>Email</label>
        <input placeholder="Enter your email address" key="Email">
    </template>
    <p></p>
    <button v-on:click="modifyLogin()">modifyLogin</button>
</div>

如下图,input标签也被刷新了。
Vue.js学习笔记_第18张图片

v-show

v-show 指令用法大致一样,不同的是带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS 属性 display。
注: v-show 不支持 元素,也不支持 v-else。

<div id="app1">
    <h1 v-show="ok">Hello!</h1>
</div>
<script type="text/javascript">
    var vm = new Vue({
        el: "#app1",
        data: {
            ok: false
        }
    });
</script>

Vue.js学习笔记_第19张图片

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-if 和 v-for 同时用在同一个元素上。
当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级
例如:

	<ul>
	  <li
	    v-for="user in users"
	    v-if="user.isActive"
	    :key="user.id"
	  >
	    {{ user.name }}
	  </li>
	</ul>

将会经过如下运算过滤出要显示的users:

this.users.map(function (user) {
  if (user.isActive) {
    return user.name
  }
})

因此,哪怕只渲染出一小部分用户的元素,也得在每次重渲染的时候遍历整个列表,不论活跃用户是否发生了变化。

修改为:

computed: {
  activeUsers: function () {
    return this.users.filter(function (user) {
      return user.isActive
    })
  }
}

<ul>
  <li
    v-for="user in activeUsers"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

我们将会获得如下好处:

(1)过滤后的列表只会在 users 数组发生相关变化时才被重新运算,过滤更高效。
(2)使用 v-for=“user in activeUsers” 之后,我们在渲染的时候只遍历活跃用户,渲染更高效。
(3)解耦渲染层的逻辑,可维护性 (对逻辑的更改和扩展) 更强。

列表渲染

在v-for中使用列表和对象

用 v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名。
v-for 还支持一个可选的第二个参数,即当前项的索引。

<div id="app1">
    <!--遍历数组---第二个参数index可以不要 -->
    <ul>
        <li v-for="value, index in listStr">
            {{ index }}---{{ value }}
        </li>
    </ul>
    <ul>
        <li v-for="value in listObj">
            {{ value.a }}
        </li>
    </ul>
    <!--遍历对象---第二个参数key可以不要 ,如果需要下标还可以写第三个参数-->
    <ul>
        <li v-for="value, key in Obj">
            {{ key }} : {{ value }}
        </li>
    </ul>
</div>
<script type="text/javascript">
    var vm = new Vue({
        el: "#app1",
        data: {
            listStr: ["a", "b", "c"],
            listObj: [{a: 1}, {a: 2}, {a: 3}],
            Obj: {
                title: 'How to do lists in Vue',
                author: 'Jane Doe',
                publishedAt: '2020-04-05'
            }
        }
    });
</script>

Vue.js学习笔记_第20张图片

维护状态

当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。这个类似 Vue 1.x 的 track-by="$index"。

这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。
参考顺序表插入元素的操作,复杂度为O(n)。如果给定唯一的key索引,就可以想链表一样插入操作变为O(1);
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,需要为每项提供一个唯一 key 属性

最好不要用下标作为key,比如: [“a”, “b”, “c”],以下标作为key,则对应关系就是0-a、1-b、2-c,如果在下标1插入d,,则原来下标对应的元素就会发生变化,vue就没办法跟踪到原有的节点(参考:https://segmentfault.com/q/1010000021434057/)。可以采用index+value的方式,提供唯一值(单纯用value可能会冲突)。

    <ul>
        <li v-for="value, index in Obj" v-bind:key="index+value">
            {{ index }} : {{ value }}
        </li>
    </ul>

数组更新检测

  1. 变异方法 (mutation method):
    vue 侦听的数组的变异方法,它们触发列表数据更新时也将会触发视图更新。这些被包裹过的方法包括:

     push()
     pop()
     shift()
     unshift()
     splice()
     sort()
     reverse()
    
<body>
<div id="app1">
    <ul>
        <li v-for="value, index in listStr" v-bind:key="index">
            {{ index }}---{{ value }}
        </li>
    </ul>
    <button @click="pushList">pushList</button>
    <button @click="popList">popList</button>
    <button @click="shiftList">shiftList</button>
    <button @click="unshiftList">unshiftList</button>
    <button @click="spliceList">spliceList</button>
    <button @click="sortList">sortList</button>
    <button @click="reverseList">reverseList</button>
</div>
<script type="text/javascript">
    var vm = new Vue({
        el: "#app1",
        data: {
            listStr: ["a", "b", "c"]
        },
        methods: {
            // 列表尾部添加元素
            pushList: function () {
                this.listStr.push("d");
            },
            // 列表尾部删除元素
            popList: function () {
                this.listStr.pop();
            },
            // 列表头部添加元素
            unshiftList: function () {
                this.listStr.unshift("e");
            },
            // 列表头部删除元素
            shiftList: function () {
                this.listStr.shift();
            },
            // 指定下标删除n个/添加元素(index, n, item1, item2...)
            // splice(start, num, x, xx, xxx...)  在下标start删除num个元素,添加x,xx,xxx...
            spliceList: function () {
                this.listStr.splice(0, 1);
            },
            // 列表排序
            sortList: function () {
                this.listStr.sort();
            },
            // 列表反转
            reverseList: function () {
                this.listStr.reverse();
            }
        }
    });
</script>
</body>

Vue.js学习笔记_第21张图片

  1. 替换数组(变异方法):
    相对于变异方法,也有非变异方法,例如:filter()、concat() 和 slice()。它们不会改变原始数组,而总是返回一个新数组。当使用非变异方法时,可以用新数组替换旧数组:
	app1.items = app1.items.filter(function (item) {
	  return item.message.match(/Foo/)
	})
  1. 不会被vue 检测到数组变动的情况
    由于 JavaScript 的限制,Vue 不能检测以下数组的变动:

    (1) 通过索引直接设置一个数组项时— items[1] = ‘x’。
    (2)修改数组的长度时— items.length = 2。

       var vm = new Vue({
       data: {
           items: ['a', 'b', 'c']
       }
       })
       vm.items[1] = 'x'  // 不是响应性的
       vm.items.length = 2  // 不是响应性的
    
       以下两种方式可以解决列表添加元素的问题,Vue.set等同于vm.$set,也可以使用 vm.$set 方法是全局方法 Vue.set 的一个别名::
```javascript
	// Vue.set添加元素
	Vue.set(vm.items, indexOfItem, newValue)
	vm.$set(vm.items, indexOfItem, newValue)
	// splice方法添加元素
	vm.items.splice(indexOfItem, 1, newValue)
 为了解决第二类问题,可以使用 splice:
    vm.items.splice(newLength)

对象更新检测

由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除:

	var vm = new Vue({
	  data: {
	    a: 1
	  }
	})
	vm.a = 3  // `vm.a` 是响应式的
	vm.b = 2  // `vm.b` 不是响应式的

对于已经创建的实例,Vue 不允许动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, propertyName, value) 方法向嵌套对象添加响应式属性。例如,对于:

	var vm = new Vue({
	  data: {
	    userProfile: {
	      name: 'Anika'
	    }
	  }
	})

可以添加一个新的 age 属性到嵌套的 userProfile 对象:

	Vue.set(vm.userProfile, 'age', 27)
	vm.$set(vm.userProfile, 'age', 27)

当需要添加多个属性时:

	vm.userProfile = Object.assign({}, vm.userProfile, {
	  age: 27,
	  favoriteColor: 'Vue Green'
	})

在 v-for 里使用值范围

	<div>
	  <span v-for="n in 10">{{ n }} </span>
	</div>

在这里插入图片描述

在 template上使用 v-for

类似于 v-if,你也可以利用带有 v-for 的 来循环渲染一段包含多个元素的内容。比如:

	<ul>
	  <template v-for="item in items">
	    <li>{{ item.msg }}</li>
	    <li class="divider" role="presentation"></li>
	  </template>
	</ul>

如果要动态更新标签的属性,需要用v-bind:属性或者缩写形式**:属性**来动态更新。比如:

    <template  v-for="data in checkboxData">
        <input type="checkbox" :id="data" :value="data" v-model="checkedNames" v-on:change="checkboxEvent">
        <label :for="data">{{data}}</label>
    </template>

在组件中使用v-for

这部分内容需要先学习组件相关知识。

在自定义组件上,可以像在任何普通元素上一样使用 v-for。在2.2.0+ 版本里,当在组件上使用 v-for 时,key是必须的。
通过prop把迭代数据传递到组件里:

    <div id="app">
        <blog-post v-for="item in titles" :key="item.id" :title="item.name"></blog-post>
    </div>


    Vue.component('blog-post', {
        props: ['title'],
        template: '

Blog: {{ title }}

'
}); var vm = new Vue({ el: "#app", data: { temp: "abc", titles: [ {id: 1, name: "ZZZZ"}, {id: 2, name: "XXXX"}, {id: 3, name: "CCCC"} ] } })

Vue.js学习笔记_第22张图片

事件监听

v-on指令

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

	<body>
	    <div id="app">
	        <p>{{num}}</p>
	        <!-- 1. 简单的事件可以直接在html中添加 -->
	        <button v-on:click="num++">+</button>
	        <button v-on:click="num--">-</button>
	        <!-- 2. 复杂的事件可以通过添加方法 -->
	        <button v-on:click="increment">+</button>
	        <button v-on:click="decrement">-</button>
	        <!-- 3. v-on的语法糖:缩写形式 -->
	        <button @click="increment">+</button>
	        <button @click="decrement">-</button>
	    </div>
	<script type="text/javascript">
	    var vm = new Vue({
	        el: "#app",
	        data: {
	            num: 0
	        },
	        methods: {
	            // ES6中对象方法的新写法
	            increment () {
	                this.num ++;
	            },
	            decrement() {
	                this.num --;
	            }
	        }
	    })
	</script>

v-on绑定方法的调用方式:

    <div id="app">
        <p>{{num}}</p>
        <ul>
            <li v-for="value, index in listStr" v-bind:key="index">
                {{ index }}---{{ value }}
            </li>
        </ul>
        <!-- 1. 方法不需要传入任何参数时可以省略括号 -->
        <button v-on:click="increment">+</button>
        <!-- 2. 方法需要传入参数时需要在括号中传入参数,否则vue会自动把事件event传入 -->
        <button v-on:click="decrement('abc')">-</button>
        <!-- 3. 方法需要获取普通参数和事件event时, 需要写括号并且事件通过$event传入 -->
        <button v-on:click="pushList('test', $event)">pushList</button>
    </div>
	<script type="text/javascript">
	    var vm = new Vue({
	        el: "#app",
	        data: {
	            num: 0,
	            listStr: ["a", "b", "c"]
	        },
	        methods: {
	            pushList: function (str, e) {
	                console.log("------pushList", str, e);
	                this.listStr.push(str);
	            },
	            increment () {
	                console.log("------increment");
	                this.num ++;
	            },
	            decrement(str) {
	                console.log("------decrement", str);
	                this.num --;
	            }
	        }
	    })
	</script>

Vue.js学习笔记_第23张图片

事件修饰符

Vue.js 为 v-on 提供了事件修饰符。修饰符是由点开头的指令后缀来表示的。

  1. .stop修饰符:阻止事件冒泡:

    如下, 子级的点击事件会继续传播到父级:

    <div id="app1">
        <div v-on:click="parent">aaaaaaa
            <button v-on:click="child">child</button>
        </div>
    </div>
    <script type="text/javascript">
        var vm = new Vue({
            el: "#app1",
            methods: {
                parent: function () {
                    console.log("parent");
                },
                child: function () {
                    console.log("child");
                }
            }
        });
    </script>
    

Vue.js学习笔记_第24张图片
添加.stop修饰符,阻止事件继续向上传播:

    <div v-on:click="parent">aaaaaaa
        <button v-on:click.stop="child">child</button>
    </div>

其他修饰符:
.prevent
.capture
.self
.once
.passive

	<!-- 阻止单击事件继续传播 -->
	<a v-on:click.stop="doThis"></a>
	
	<!-- 提交事件不再重载页面 -->
	<form v-on:submit.prevent="onSubmit"></form>
	
	<!-- 修饰符可以串联 -->
	<a v-on:click.stop.prevent="doThat"></a>
	
	<!-- 只有修饰符 -->
	<form v-on:submit.prevent></form>
	
	<!-- 添加事件监听器时使用事件捕获模式 -->
	<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
	<div v-on:click.capture="doThis">...</div>
	
	<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
	<!-- 即事件不是从内部元素触发的 -->
	<div v-on:click.self="doThat">...</div>
	
	<!-- 点击事件将只会触发一次 2.1.4新增 -->
	<a v-on:click.once="doThis"></a>

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

按键修饰符

Vue 允许为 v-on 在监听键盘事件时添加按键修饰符:

	<!-- 只有在 `key``Enter` 时调用 `vm.submit()` -->
	<input v-on:keyup.enter="submit">
	<input v-on:keyup.page-down="onPageDown">

你可以直接将 KeyboardEvent.key 暴露的任意有效按键名转换为 kebab-case 来作为修饰符。
在上述示例中,处理函数只会在 $event.key 等于 PageDown 时被调用。

按键码

注:keyCode 的事件用法已经被废弃了并可能不会被最新的浏览器支持。

	<input v-on:keyup.13="submit">

为了在必要的情况下支持旧浏览器,Vue 提供了绝大多数常用的按键码的别名:

	.enter
	.tab
	.delete (捕获“删除”和“退格”键)
	.esc
	.space
	.up
	.down
	.left
	.right

有一些按键 (.esc 以及所有的方向键) 在 IE9 中有不同的 key 值, 如果你想支持 IE9,这些内置的别名应该是首选。

你可以通过全局 config.keyCodes 对象自定义按键修饰符别名:

	// 可以使用 `v-on:keyup.f1`
	Vue.config.keyCodes.f1 = 112

鼠标按钮修饰符(2.2.0 新增)

.left
.right
.middle

这些修饰符会限制处理函数仅响应特定的鼠标按钮。

系统修饰键(2.1.0 新增)

可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器。
.ctrl
.alt
.shift
.meta

<!-- Alt + C -->
<input @keyup.alt.67="clear">

<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>

请注意修饰键与常规按键不同,在和 keyup 事件一起用时,事件触发时修饰键必须处于按下状态。换句话说,只有在按住 ctrl 的情况下释放其它按键,才能触发 keyup.ctrl。而单单释放 ctrl 也不会触发事件。如果你想要这样的行为,请为 ctrl 换用 keyCode:keyup.17。

.exact 修饰符(2.5.0 新增)

.exact 修饰符允许你控制由精确的系统修饰符组合触发的事件。

	<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
	<button @click.ctrl="onClick">A</button>
	
	<!-- 有且只有 Ctrl 被按下的时候才触发 -->
	<button @click.ctrl.exact="onCtrlClick">A</button>
	
	<!-- 没有任何系统修饰符被按下的时候才触发 -->
	<button @click.exact="onClick">A</button>

小案例

展示购物车书籍信息,涉及知识点: v-if、v-for、v-on、v-bind、计算属性、过滤器
Vue.js学习笔记_第25张图片

<div id="app1">
    <div v-if="books.length !== 0">
        <table border="1">
            <thead>
            <tr>
                <th></th>
                <th>书籍名称</th>
                <th>出生日期</th>
                <th>价格</th>
                <th>购买数量</th>
                <th>操作</th>
            </tr>
            </thead>
            <tbody>
            <!-- 使用index+book.name作为key,保证唯一 -->
            <tr v-for="book, index in books" :key="index+book.name">
                <td>{{index + 1}}</td>
                <td>{{book.name}}</td>
                <td>{{book.date}}</td>
                <td>{{book.price | showPrice}}</td>
                <td>
                    <button @click="calculatePrice(index, -1)" :disabled="book.nums===1">-</button>
                    {{book.nums}}
                    <button @click="calculatePrice(index, 1)">+</button>
                </td>
                <td><button @click="removeBook(index)">移除</button></td>
            </tr>
            </tbody>
        </table>
        <!-- 过滤器 -->
        总价: {{totalPrice | showPrice}}
    </div>
    <div v-else>当前购物车中没有书籍</div>
</div>
<script type="text/javascript">
    var vm = new Vue({
        el: "#app1",
        data: {
            books: [
                {id: 1, name: "《算法导论》", date: 2006-9, price: 85.00, nums: 1},
                {id: 2, name: "《UNIX编程艺术》", date: 2006-2, price: 59.00, nums: 1},
                {id: 3, name: "《编程珠玑》", date: 2008-10, price: 39.00, nums: 1},
                {id: 4, name: "《代码大全》", date: 2006-3, price: 128.00, nums: 1},
            ]
        },
        methods: {
            calculatePrice(index, num) {
                num = parseFloat(num);
                this.books[index].nums += num;
            },
            removeBook(index) {
                // 删除列表元素用splice
                this.books.splice(index, 1);
            }
        },
        computed: {
            // 总价格通过计算属性来展示,只有书籍信息发生变化时再重新计算
            totalPrice() {
                // 高阶函数reduce(func(前一次的值, 循环得到每一项){具体操作}, 初始值)
                return this.books.reduce(function (preValue, book) {
                    return preValue + book.price * book.nums
                }, 0)
            }
        },
        // vue的过滤器
        filters: {
            // 过滤器一般都是输入值,通过对输入值做其他操作再返回
            showPrice(price) {
                return "¥" + price.toFixed(2)
            }
        }
    });
</script>

表单输入绑定

基础用法

v-model 指令在表单 、 及 元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。用户需要通过 JavaScript 在组件的 data 选项中声明初始值。

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

  2. 对于需要使用输入法 (如中文、日文、韩文等) 的语言, v-model 不会在输入法组合文字过程中得到更新。如果需要处理这个过程,使用 input 事件。例如:

    <input v-model="message" placeholder="edit me" v-on:input="inputEvent()">
  1. 在文本区域插值 ({{text}}) 并不会生效,应用 v-model 来代替。例如:
    <textarea placeholder="add multiple lines" v-on:input="textareaEvent()">{{message1}</textarea>
    修改为:
	<textarea v-model="message1" placeholder="add multiple lines" v-on:input="textareaEvent()"></textarea>
  1. 对于select下拉列表,如果 v-model 表达式的初始值未能匹配任何选项, 元素将被渲染为“未选中”状态。在 iOS 中,这会使用户无法选择第一个选项。因为这样的情况下,iOS 不会触发 change 事件。因此,更推荐提供一个值为空的禁用选项。例如:
    <select v-model="selected" v-on:change="selectEvent">
        <option disabled value="">请选择</option>
        <option v-for="item in selectItems" :value="item">{{item}}</option>
    </select>

Vue.js学习笔记_第26张图片

<body>
<div id="app1">
    <p>文本input</p>
    <!-- input事件可以监听到在输入法中的变动,v-model不会处理输出法中的变动-->
    <input v-model="message" placeholder="edit me" v-on:input="inputEvent()">
    <label>Message is: {{ message }}</label>
    <p>多行文本textarea</p>
    <textarea v-model="message1" placeholder="add multiple lines" v-on:input="textareaEvent()"></textarea>
    <label>Multiline message is: {{ message1 }}</label>
    <br>
    <!-- checkbox单选框,绑定到布尔值 -->
    <p>单选框</p>
    <input type="checkbox" id="data" v-model="checked">
    <label for="data">Checked: {{checked}}</label>
    <br>
    <p>复选框</p>
   	<!-- checkbox复选框,绑定到同一个数组 -->
    <template  v-for="data in checkboxData">
        <input type="checkbox" :id="data" :value="data" v-model="checkedNames" v-on:change="checkboxEvent">
        <label :for="data">{{data}}</label>
    </template>
    <br>
    <span>Checked names: {{ checkedNames }}</span>
    <br>
    <p>单选按钮</p>
    <input type="radio" id="one" value="One" v-model="picked">
    <label for="one">One</label>
    <input type="radio" id="two" value="Two" v-model="picked">
    <label for="two">Two</label>
    <span>Picked: {{ picked }}</span>
    <br>
    <p>select 下拉列表:单选</p>
    <select v-model="selected" v-on:change="selectEvent">
        <option disabled value="">请选择</option>
        <option v-for="item in selectItems" :value="item">{{item}}</option>
    </select>
    <label>Selected: {{selected}}</label>
    <p>select 下拉列表:多选</p>
    <select v-model="selectedMultiple" multiple>
        <option v-for="item in selectItems" :value="item">{{item}}</option>
    </select>
    <label>selectedMultiple: {{selectedMultiple}}</label>
</div>
<script type="text/javascript">
    var vm = new Vue({
        el: "#app1",
        data: {
            message: "",
            message1: "",
            checked: false,
            checkedNames: [],
            checkboxData: ["Jack", "John", "Mike"],
            picked: "",
            selectItems: ["A", "B", "C"],
            selected: "",
            selectedMultiple: ["A"]
        },
        methods: {
            inputEvent: function () {
                console.log("inputEvent");
            },
            textareaEvent: function () {
                console.log("textareaEvent");
            },
            checkboxEvent: function () {
                console.log(this.checkedNames);
            },
            selectEvent: function (item) {
                console.log("selectEvent");
            }
        }
    });
</script>

Vue.js学习笔记_第27张图片

手动实现v-model双向绑定

v-model实际也是一个语法糖,用v-bind和v-on也可以实现:

	<div id="app1">
		<!-- <input type="text" v-model="message"> -->
	
	    <!--  调用方法时不传参数,默认会把$event传给方法 -->
		<!--  <input type="text" v-bind:value="message" v-on:input="inputValue"> -->
	
	    <input type="text" v-bind:value="message" v-on:input="message = $event.target.value">
	    {{message}}
	</div>
	<script type="text/javascript">
	    var vm = new Vue({
	        el: "#app1",
	        data: {
	            message: "aaaaaaaaa"
	        },
	        methods: {
	            inputValue(event) {
	                // 通过event事件获取input框的实际输入值,刷新message属性
	                this.message = event.target.value;
	            }
	        }
	    });
	</script>

值绑定

对于单选按钮,复选框及选择框的选项,v-model 绑定的值通常是静态字符串 (对于复选框也可以是布尔值):

	<!-- 当选中时,`picked` 为字符串 "a" -->
	<input type="radio" v-model="picked" value="a">
	
	<!-- `toggle`truefalse -->
	<input type="checkbox" v-model="toggle">
	
	<!-- 当选中第一个选项时,`selected` 为字符串 "abc" -->
	<select v-model="selected">
	  <option value="abc">ABC</option>
	</select>
  1. 复选框,修改选中和未选中时的值:
<input type="checkbox" id="data" value="data" v-model="checked" true-value="yes" false-value="no">

// 当选中时
vm.checked=== ‘yes’
// 当没有选中时
vm.checked=== ‘no’
在这里插入图片描述

修饰符

  1. lazy延迟数据同步------失去焦点或敲回车时刷新值:
    默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步 (除了上述输入法组合文字时)。可以添加 lazy 修饰符,从而转变为使用 change 事件进行同步:
	<!-- 在“change”时而非“input”时更新 -->
	<input v-model.lazy="message" placeholder="edit me" v-on:input="inputEvent()">

oninput事件和onchange事件区别:oninput事件是当input的value值发生变化时就会触发,而onchang是当input失去焦点或敲回车并且它的value值发生变化时触发。

  1. number------将用户输入的值转换为数值类型,不能转换时返回原始值:
    如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符。这通常很有用,因为即使在 type=“number” 时,v-model绑定的输入元素的值也会返回字符串。如果这个值无法被 parseFloat() 解析,则会返回原始的值
	<input v-model.number="message" placeholder="edit me" v-on:input="inputEvent()">

Vue.js学习笔记_第28张图片

  1. trim------过滤用户输入的首尾空白字符:
    如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符:
	<input v-model.trim="message" placeholder="edit me" v-on:input="inputEvent()">

Vue.js学习笔记_第29张图片

在组件上使用 v-model

待学习

组件基础

组件是可复用的 Vue 实例,且带有一个名字,可以在一个通过 new Vue 创建的 Vue 根实例中,把这个组件作为自定义元素来使用。

创建组件

因为组件是可复用的 Vue 实例,所以与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。

老版本的写法

  1. 创建组件构造器Vue.extend():
  2. 注册组件;
  3. 在视图中使用组件;

注:一个组件的 data 必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝。否则,点击一个按钮就可能会影响到其它所有实例。

如下组件名称为button-counter:

        // 1.创建组件构造器
    const btnCom = Vue.extend({
        data: function () {
            return {
                num: 0
            }
        },
        methods: {
            count: function () {
                return this.num++
            }
        },
        template: ""
    });
    // 2. 注册全局组件
    Vue.component('button-counter', btnCom);
    var vm = new Vue({
        el: "#app1",
        data: {
        }
    });
	<div id="app">
		//  3.使用组件 
        <button-counter></button-counter>
        <button-counter></button-counter>
        <button-counter></button-counter>
    </div>

在这里插入图片描述
2.X版本之后很少用Vue.extend()去创建组件,可以直接用

	// 注册组件,传入一个选项对象 (自动调用 Vue.extend)
	Vue.component('my-component', { /* ... */ })

组件的注册

组件注册有两种注册类型:全局注册局部注册

  1. 全局注册 Vue.component
    全局注册的组件可以用在其被注册之后(需要先创建组件,再去new Vue)的任何通过** new Vue **新创建的 Vue 根实例,也包括其组件树中的所有子组件的模板中。
	<script type="text/javascript">
	    // 注册全局组件
	    Vue.component('button-counter', {
	        data: function () {
	            return {
	                num: 0
	            }
	        },
	        methods: {
	            count: function () {
	                return this.num++
	            }
	        },
	        template: ""
	    });
	    var vm1 = new Vue({
	        el: "#app1",
	        data: {
	        }
	    });
	    var vm2 = new Vue({
	        el: "#app2",
	        data: {
	        }
	    });
	</script>
	<div id="app1">
	    // 在 app1中可以使用button-counter组件
    	<button-counter></button-counter>
	</div>
	<p>----------------------------</p>
	<div id="app2">
		// 在 app2中可以使用button-counter组件
	    <button-counter></button-counter>
	</div>

Vue.js学习笔记_第30张图片

  1. 局部注册:
    通过 components属性 定义局部组件,只在当前实例作用域中可以使用。
	<script type="text/javascript">
	    var vm1 = new Vue({
	        el: "#app1",
	        // 注册局部组件
	        components: {
	            buttonCounter: {
	                data: function () {
	                    return {
	                        num: 0
	                    }
	                },
	                methods: {
	                    count: function () {
	                        return this.num++
	                    }
	                },
	                template: ""
	            }
	        }
	    });
	    var vm2 = new Vue({
	        el: "#app2",
	        data: {
	        }
	    });
	</script>
	<div id="app1">
	    <button-counter></button-counter>
	</div>
	<p>----------------------------</p>
	<div id="app2">
	    <button-counter></button-counter>
	</div>

如下图:在app2中组件没有被解析
Vue.js学习笔记_第31张图片

单个根元素

template中包含多个同级别标签时,需要用一个根元素包裹
当组件模板包含多个同级别html标签时Vue 会显示一个错误,解决办法就是将模板的内容包裹在一个父元素内
Vue.js学习笔记_第32张图片
如下,使用div作为根元素包裹h3和div。

	    <div id="app">
	        <blog-post v-for="item in titles" :key="item.id" :item="item"></blog-post>
	    </div>
	    
	    Vue.component('blog-post', {
	        props: ['item'],
	        template: '

Blog: {{ item.title }}

Content: {{item.content}}
'
});

Vue.js学习笔记_第33张图片

js分离template

由于在js中写template,编辑器没有很好的支持,并且代码结构看着很乱,所以,有以下两种方式可以把template定义在html中,通过id进行关联:

  1. 通过script标签定义模板,type为text/x-template;
  2. 通过template标签定义模板。
	<div id="app1">
	    <comp></comp>
	</div>
	<!--1. 通过script标签定义模板,type为text/x-template-->
	<!--<script type="text/x-template" id="com1">-->
	<!--    <div>-->
	<!--        <h2>这是组件1</h2>-->
	<!--    </div>-->
	<!--</script>-->
	
	<!--2. 通过template标签定义模板-->
	<template id="com1">
	    <div>
	        <h2>这是组件1</h2>
	    </div>
	</template>
	<script type="text/javascript">
	    const com1 = Vue.extend({
	        template: "#com1"
	    });
	    // 注册全局组件
	    var vm = new Vue({
	        el: "#app1",
	        components: {
	            comp: com1
	        }
	    });
	</script>

解析 DOM 模板时的注意事项

下面的例子中,自定义row组件,在table中使用:

	     <table border="1">
	         <tbody>
	             <row></row>
	             <row></row>
	             <row></row>
	         </tbody>
	     </table>

		  Vue.component("row",{
		      template:'this is a row'
		  });

实际效果与预想的不一致,组件被解析在了table外面:
Vue.js学习笔记_第34张图片
产生这个问题的原因是:有些 HTML 元素,诸如 ul、ol、table 和 select,对于哪些元素可以出现在其内部是有严格限制的。而有些元素,诸如 li、tr 和 option,只能出现在其它某些特定的元素内部。
自定义组件 row 会被作为无效的内容提升到外部,并导致最终渲染结果出错。

解决办法,使用is属性:

        <table border="1">
            <tbody>
                <tr is="row"></tr>
                <tr is="row"></tr>
                <tr is="row"></tr>
            </tbody>
        </table>

Vue.js学习笔记_第35张图片

需要注意的是如果从以下来源使用模板的话,这条限制是不存在的:
1. 字符串 (例如:template: ‘…’)
2. 单文件组件 (.vue)
3. script type=“text/x-template”

父子组件

如下,

  1. 在组件2内部注册组件1,即组件1位组件2的子组件,组件2位组件1的父组件。在组件2中可以使用组件1。
  2. 在根组件vm中注册组件2。
  3. 在vm作用域内使用组件2。
  4. com1由于没有再vm中注册,所以不能解析。
<div id="app1">
    <comp></comp>
    <com1></com1>
</div>
<script type="text/javascript">
	// 构造组件1
    const com1 = Vue.extend({
        template: `

这是组件1

`
}); // 构造组件2 const com2 = Vue.extend({ template: `

这是组件2

`
, // 注册子组件 components: { com1: com1 } }); // 注册全局组件 var vm = new Vue({ el: "#app1", components: { comp: com2 } }); </script>

Vue.js学习笔记_第36张图片

父子组件通信

  1. 父组件与子组件通信
    ① 子组件通过 props定义 要接收的子属性名称;
    ② 父组件通过 v-bind:子属性名称 来绑定要传递的值;
	<div id="app1">
	    <comp :cmovies="'abc'"></comp>
	</div>
	<template id="com1">
	    <div>
	        <ul>
	            <li v-for="movie in cmovies">{{movie}}</li>
	        </ul>
	    </div>
	</template>
	<script type="text/javascript">
	    const com1 = Vue.extend({
	        template: "#com1",
	        // 1. 数组形式:定义子组件的“属性名称列表”
	        // 父组件调用子组件时,通过v-bind绑定子组件的属性,传入值
	        // props: ["cmovies"],
	        // 2. 对象形式
	        // 可以定义参数类型
	        props: {
	            cmessage: {
	                type: String,
	                default: "子组件"
	            },
	            cmovies: {
	                type: Array,
	                // 数组和对象的default值 需要通过方法定义
	                default() {
	                    return ["空"]
	                },
	                required: true
	            },
	            cprice: {
	                // 自定义校验器
	                validator(value) {
	                    return value === "success"
	                }
	            }
	        }
	    });
	    // 注册全局组件
	    var vm = new Vue({
	        el: "#app1",
	        data: {
	          movies: ["海王", "海贼王", "海绵宝宝"]
	        },
	        components: {
	            comp: com1
	        }
	    });
	</script>

Vue.js学习笔记_第37张图片

  1. **子组件与父组件通信 e m i t ( ) ∗ ∗ : ① 子 组 件 通 过 “ emit()**: ① 子组件通过 “ emit()emit(自定义事件名称, 要传递的参数) ” 发送事件给父组件;

② 父组件通过 “ v-on:子组件自定义的事件名称” 监听子组件事件,通过$event接收子组件事件传递的参数;

	<div id="app1">
	    // 2. 父组件通过v-on监听子组件的自定义事件, 
	    // 如果子组件有传递参数,则父组件通过$event接收子组件传递的参数
	    <comp @categoryclick="compClick($event)"></comp>
	</div>
	<template id="com1">
	    <div>
	        <button v-for="category in categories" @click="btnClick(category)">{{category.name}}</button>
	    </div>
	</template>
	<script type="text/javascript">
	    const com1 = Vue.extend({
	        template: "#com1",
	        data() {
	            return {
	                categories: [
	                    {id: 1, name: "家电"},
	                    {id: 2, name: "男装"},
	                    {id: 3, name: "女装"}
	                ]
	            }
	        },
	        methods: {
	            // 1. 子组件定义点击事件,通过$emit发送自定义事件(categoryclick)给父组件
	            // 第一个参数为自定义事件名称,第二个参数为需要传递的参数
	            btnClick(category) {
	                this.$emit("categoryclick", category)
	            }
	        }
	    });
	    // 注册全局组件
	    var vm = new Vue({
	        el: "#app1",
	        components: {
	            comp: com1
	        },
	        methods: {
	            // 入参为子组件传递的参数
	            compClick(category) {
	                console.log("compClick", category)
	            }
	        }
	    });
	</script>

Vue.js学习笔记_第38张图片

父子组件通信–props的驼峰标识

尽量避免使用驼峰命令定义属性名称,例如: 属性名称cMovies,在html中使用时就得写为中划线方式c-movies,否则会报错找不到cMovies。

props: {
       // 子组件定义属性名称时,采用驼峰命名
       cMovies: {
             type: Array,
             // 数组和对象的default值 需要通过方法定义
             default() {
                 return ["空"]
             },
             required: true
         }
     }
	<div id="app1">
	    // 父组件使用时,也使用驼峰命名
	    <comp :cMovies="movies"></comp>
	</div>

如下,会报错找不到cMovies属性
Vue.js学习笔记_第39张图片
在html中改为中划线方式可以正常使用:

	<div id="app1">
	    <comp :c-movies="movies"></comp>
	</div>

父访问子

有两种方式 c h i l d r e n 和 children 和 childrenrefs。
$children:是一个列表,里面包含了所有的子组件。一般只有在要获取所有子组件时使用。
r e f s : 需 要 在 组 件 定 义 r e f 属 性 , 属 性 名 称 即 最 终 会 显 示 在 refs:需要在组件定义ref属性,属性名称即最终会显示在 refsrefrefs对象中的key值。

	<div id="app1">
	    <comp></comp>
	    <!-- 定义组件2在$refs中的key值 -->
	    <comp ref="component2"></comp>
	    <button @click="btnClick">按钮</button>
	</div>
	<template id="cpn1">
	    <div>
	        <ul>
	            <li v-for="category in categories">{{category.name}}</li>
	        </ul>
	    </div>
	</template>
	<script type="text/javascript">
	    const cpn1 = Vue.extend({
	        template: "#cpn1",
	        data() {
	            return {
	                categories: [
	                    {id: 1, name: "家电"},
	                    {id: 2, name: "男装"}
	                ]
	            }
	        },
	        methods: {
	            showMessage() {
	                console.log("cpn1");
	            }
	        }
	    });
	    var vm = new Vue({
	        el: "#app1",
	        components: {
	            comp: cpn1
	        },
	        methods: {
	            btnClick() {
	                // 通过$children获取所有的子组件列表
	                console.log(this.$children);
	                this.$children[0].showMessage();
	                // 通过$refs获取子组件对象
	                console.log(this.$refs);
	                this.$refs.component2.showMessage()
	            }
	        }
	    });
	</script>

Vue.js学习笔记_第40张图片
可以拿到子组件的方法并调用:
Vue.js学习笔记_第41张图片

子访问父

实际项目开发中,不建议在子组件中访问父组件,这样会降低组件的复用性。
$root: 获取根组件。
$parent:获取父组件。

	<div id="app1">
	    <comp></comp>
	</div>
	<template id="cpn1">
	    <div>
	        <ul>
	            <li v-for="category in categories">{{category.name}}</li>
	        </ul>
	        <button @click="btn1Click">cpn1按钮</button>
	        <cpn2></cpn2>
	    </div>
	</template>
	<template id="cpn2">
	    <div>
	        <button @click="btn2Click">cpn2按钮</button>
	    </div>
	</template>
	<script type="text/javascript">
	    const cpn1 = Vue.extend({
	        template: "#cpn1",
	        data() {
	            return {
	                categories: [
	                    {id: 1, name: "家电"},
	                    {id: 2, name: "男装"}
	                ]
	            }
	        },
	        methods: {
	            btn1Click() {
	                // 获取父组件
	                console.log("cpn1 $parent", this.$parent);
	            }
	        },
	        // 子组件cpn1中定义子组件cpn2
	        components: {
	            cpn2: {
	                template: "#cpn2",
	                methods: {
	                    btn2Click() {
	                        // 获取父组件
	                        console.log("cpn2 $parent", this.$parent);
	                        // 获取根组件
	                        console.log("$root", this.$root);
	                        console.log("$root", this.$root.test);
	                    }
	                }
	            }
	        }
	    });
	    var vm = new Vue({
	        el: "#app1",
	        data: {
	            test: "123456"
	        },
	        components: {
	            comp: cpn1
	        }
	    });
	</script>

如下:
cpn2的父组件是cpn1,是VueComponent实例。
cpn1的父组件是Vue实例,即根组件。
Vue.js学习笔记_第42张图片

插槽slot

  1. 和 HTML 元素一样,如果需要向一个组件传递内容,则需要使用Vue 自定义的 slot 元素,只要在需要的地方加入插槽就行了。插槽可以定义默认值,如果使用组件时,没有插入任何内容,就会显示默认内容:
	<div id="app1">
    <cpn1>
        <button>按钮</button>
    </cpn1>
    <h2>------------------------</h2>
    <cpn1>
    </cpn1>
</div>
<template id="cpn1">
    <div>
        <h3>这是子组件</h3>
        <slot>
            <!-- 定义插槽默认内容 -->
            <h4>这是插槽中默认内容</h4>
        </slot>
    </div>
</template>
<script type="text/javascript">
    const cpn1 = Vue.extend({
        template: "#cpn1"
    });
    var vm = new Vue({
        el: "#app1",
        components: {
            cpn1: cpn1
        }
    });
</script>

Vue.js学习笔记_第43张图片

  1. 具名插槽:
    有时候一个组建中需要有多个位置插入不同的内容,这时就要定义具名插槽,来区分不同位置插入的内容。
    slot 属性的写法在vue3之后将会废弃。
	<div id="app1">
	    <cpn1>
	        <!--  1. 直接使用要插入的元素,并定义slot属性 -->
	        <button slot="left"><</button>
	        <input type="text" slot="right">
	        <!--  2. 使用template标签,并定义slot属性 -->
	        <template slot="left">
	            <button><</button>
	        </template>
	        <template slot="right">
	            <input type="text" slot="right">
	        </template>
	        <!--  3. 官方推荐新写法 v-slot指令,可简写为 “#名称” -->
	        <template v-slot:left>
	            <button><</button>
	        </template>
	        <template #right>
	            <input type="text" slot="right">
	        </template>
	    </cpn1>
	</div>
	<template id="cpn1">
	    <div>
	        <h3>这是子组件</h3>
	        <slot name="left">
	            <h4>这是左插槽中默认内容</h4>
	        </slot>
	        <slot name="right">
	            <h4>这是右插槽中默认内容</h4>
	        </slot>
	        <slot><h3>非具名插槽的默认值</h3></slot>
	    </div>
	</template>

Vue.js学习笔记_第44张图片

  1. 编译作用域:
    父组件在使用子组件时,使用了test变量;子组件定义时也使用了test变量;
    准则:父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
	<div id="app1">
	    <cpn1>
	        <h2>父组件使用的变量会去父级的data中找对应变量:{{test}}</h2>
	    </cpn1>
	</div>
	<template id="cpn1">
	    <div>
	        <h3>这是子组件</h3>
	        <h3>子组件中使用的变量会去自身的data中找对应变量:{{test}}</h3>
	        <slot></slot>
	    </div>
	</template>
	<script type="text/javascript">
	    const cpn1 = Vue.extend({
	        template: "#cpn1",
	        data() {
	            return {
	                test: "children scope data"
	            }
	        }
	    });
	    var vm = new Vue({
	        el: "#app1",
	        data: {
	            test: "parent scope data"
	        },
	        components: {
	            cpn1: cpn1
	        }
	    });
	</script>

Vue.js学习笔记_第45张图片

  1. 作用域插槽:
    如果父组件使用子组件时,不想显示默认的插槽内容:
    如下:① 子组件中定义了user变量,插槽中默认显示user.firstName;
    ② 父组件想显示user.lastName;
    ③ 子组件中通过v-bind绑定user属性(属性名称可以变化),属性值对应user变量的值。绑定在 元素上的属性被称为“插槽 prop”;
    ④ 父组件中通过**“v-slot:插槽名称”**,定义子组件提供的插槽 prop 的名字。例如:slotProps,然后从slotProps中可以获取user变量。
	<div id="app1">
	    <!-- 官方推荐用法 -->
	    <cpn1>
	    	 <!--  这个例子中没有定义具名插槽,默认的插槽名称为default,其中default也可以省略 -->
       		 <!--  但是有具名插槽时,不能省略“插槽名称” -->
	        <template v-slot:default="slotProps">
	            {{slotProps.user.lastName}}
	        </template>
	    </cpn1>
	    <!--  使用slot-scope定义子组件插槽prop的名称是已废弃的用法  -->
	    <cpn1>
	        <template slot-scope="slotProps1">
	            {{slotProps1.user.lastName}}
	        </template>
	    </cpn1>
	</div>
	<template id="cpn1">
	    <div>
	        <h3>这是子组件</h3>
	        <slot :user="user">{{user.firstName}}</slot>
	    </div>
	</template>
	<script type="text/javascript">
	    const cpn1 = Vue.extend({
	        template: "#cpn1",
	        data() {
	            return {
	                user: {
	                    lastName: "Byrant",
	                    firstName : "Kobe"
	                }
	            }
	        }
	    });
	    var vm = new Vue({
	        el: "#app1",
	        components: {
	            cpn1: cpn1
	        }
	    });
	</script>

Vue.js学习笔记_第46张图片

动态组件

在不同组件之间进行动态切换需要通过 Vue 的 component 元素加一个特殊的** is attribute ** 来实现:

    <div id="app">
        <component :is="currentView"></component>
        <button @click="changeView('A')">切换到A</button>
        <button @click="changeView('B')">切换到B</button>
        <button @click="changeView('C')">切换到C</button>
    </div>
    var vm = new Vue({
        el: "#app",
        data: {
            currentView: 'comA'
        },
        methods: {
            changeView: function (name) {
                this.currentView = "com" + name;
            }
        },
        components: {
            comA: {
                template: '
组件A
'
}, comB: { template: '
组件B
'
}, comC: { template: '
组件C
'
} }, })

Vue.js学习笔记_第47张图片

监听子组件事件

一些功能可能要求我们和父级组件进行沟通。

  1. 例如:引入一个辅助功能来放大博文的字号,同时让页面的其它部分保持默认的字号。
	// 在父组件通过 “v-on:子组件自定义组件名称” 来监听子组件的冒泡事件
	<div id="app">
        <div :style="{fontSize: postFontSize + 'px'}">
            <blog-post v-for="item in titles" :key="item.id" :item="item" v-on:enlarge-text="postFontSize += 1">
            </blog-post>
        </div>
    </div>

	// 子组件通过调用内建的 $emit 方法传入事件名称来触发一个事件
	// 如下子组件通过v-on:click绑定了点击事件,然后通过 $emit(“自定义事件名称”) 向父组件上报要触发的事件,类似于“冒泡”;
    Vue.component('blog-post', {
        props: ['item'],
        template: '

Blog: {{ item.title }}

Content: {{item.content}}
'
}); var vm = new Vue({ el: "#app", data: { temp: "abc", postFontSize: 16, titles: [ {id: 1, name: "ZZZZ", content: "11111111111111"}, {id: 2, name: "XXXX", content: "22222222222222"} ] } })

Vue.js学习笔记_第48张图片
2. 使用事件抛出一个值:
可以使用 e m i t 的 第 二 个 参 数 来 提 供 要 抛 出 的 值 , 父 组 件 使 用 emit 的第二个参数来提供要抛出的值,父组件使用 emit使event接收值:

    //子组件通过 $emit('enlarge-text', 2)抛出事件并传值
	Vue.component('blog-post', {
        props: ['item'],
        template: '

Blog: {{ item.title }}

Content: {{item.content}}
'
}); // 父组件通过$event接收值 <blog-post v-for="item in titles" :key="item.id" :item="item" v-on:enlarge-text="postFontSize += $event"> </blog-post>

或者用函数形式:

        <blog-post v-for="item in titles" :key="item.id" :item="item" v-on:enlarge-text="modifyFontSize">
        </blog-post>
		// 父组件定义modifyFontSize方法,直接通过形参size接收值
        methods: {
            modifyFontSize: function (size) {
                console.log("modifyFontSize!" + size);
                this.postFontSize += size;
            }
        }

组件上使用 v-model

待学习

组件注册

组件名

组件名强烈推荐遵循 W3C 规范中的自定义组件名 (字母全小写且必须包含一个连字符)。
使用 kebab-case (短横线分隔命名) 或使用 PascalCase (首字母大写命名) 定义一个组件都是允许的, 即 和 都是可接受的。但直接在 DOM (即非字符串的模板) 中使用时只有 kebab-case 是有效的。

全局注册

通过 Vue.component 来创建全局注册组件,它们在注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中。比如:

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

所有子组件在各自内部也都可以相互使用。

局部注册

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

可以通过在components 选项中定义想要使用的组件,来局部注册组件:

	var ComponentA = { /* ... */ };
	var ComponentB = { /* ... */ };
	
	var vm = new Vue({
	  el: '#app',
	  components: {
	  	// key就是自定义组件的名称
	    'component-a': ComponentA,
	    'component-b': ComponentB
	  }
	})

注:局部注册的组件在其子组件中不可用。如果希望 ComponentA 在 ComponentB 中可用,则需要这样写:

	var ComponentA = { /* ... */ }
	
	var ComponentB = {
	  components: {
	    'component-a': ComponentA
	  },
	  // ...
	}

模块系统

待学习。

单文件组件

使用 Vue.component 来定义全局组件,紧接着用 new Vue({ el: '#container '}) 在每个页面内指定一个容器元素,这在很多中小规模的项目中运作的很好。但是更复杂的项目中,或者你的前端完全由 JavaScript 驱动的时候,下面这些缺点将变得非常明显:

  1. 全局定义 (Global definitions) 强制要求每个 component 中的命名不得重复;
  2. 字符串模板 (String templates) 缺乏语法高亮,在 HTML 有多行的时候,需要用到丑陋的“\” ;
  3. 不支持 CSS (No CSS support) 意味着当 HTML 和 JavaScript 组件化时,CSS 明显被遗漏;
  4. 没有构建步骤 (No build step) 限制只能使用 HTML 和 ES5 JavaScript,而不能使用预处理器,如 Pug (formerly Jade) 和 Babel;

文件扩展名为 .vue 的 single-file components(单文件组件) 为以上所有问题提供了解决方法,并且还可以使用 webpack 或 Browserify 等构建工具。
Vue.js学习笔记_第49张图片

安装好环境后:

  1. 在命令行执行:vue ui
    在这里插入图片描述
    Vue.js学习笔记_第50张图片
  2. 创建新项目:
    Vue.js学习笔记_第51张图片
    Vue.js学习笔记_第52张图片
    Vue.js学习笔记_第53张图片
    Vue.js学习笔记_第54张图片

创建完成后的新项目目录结构,其中public是打包后用于部署到生成环境的目录;src是主要的开发目录,src/components目录就是存放自定义组件的目录;App.vue是项目的入口文件。
Vue.js学习笔记_第55张图片

通过npm运行HelloWorld.vue:
Vue.js学习笔记_第56张图片
Vue.js学习笔记_第57张图片

创建组件

新建组件分为三部分:template即模板, style即样式, script即js代码。
Vue.js学习笔记_第58张图片
如下创建自定义组件test:

	<template>
	    <h3 class="red">test....{{msg}}</h3>
	</template>
	
	<script>
	    export default {
	        name: "test",
	        props: {
	            msg: {
	                type: String,
	                // 设置默认值,父级组件没有传参时使用默认值展示。
	                default: "  default msg."
	            }
	        },
	        methods: {
	
	        },
	        data: function () {
	            return {}
	        }
	    }
	</script>
	
	<style scoped>
	.red { color: red}
	</style>

Vue.js学习笔记_第59张图片
在App.vue中引入test组件,template中引用test组件,并通过msg属性传参给test组件:
Vue.js学习笔记_第60张图片
Vue.js学习笔记_第61张图片

你可能感兴趣的:(前端)