vue 组件
组件的出现,就是为了拆分Vue实例的代码量的,能够让我们以不同的组件,来划分不能的功能模块,将来需要什么样的功能,就可以去调用。
组件和模块化的不同:
模块化:是从代码逻辑的角度进行划分的,方便代码分层开发,保证每个功能模块职能单一
组件化:是从UI界面的角度进行划分的,前端的组件化,方便UI组件的重用
一、全局组件定义的方式
使用Vue.extend
Vue.extend
属于 Vue 的全局 API,在实际业务开发中我们很少使用,因为相比常用的 Vue.component
写法使用 extend
步骤要更加繁琐一些.
<div id="root">
<!--组件的使用:以html标签的形式,组件名称作为html的标签名称-->
<login></login>
</div>
<script src="./js/vue.js" type="text/javascript"></script>
<script type="text/javascript">
// 方法1:使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象。
// 创建构造器
var login = Vue.extend({
//定义组件的模版
template: "<h1>欢迎使用登录模块</h1>"
})
// 需要通过 new login().$mount('#mount-point') 来挂载到指定的元素上。
// new login().$mount('#root')
// 或者结合 Vue.component('login', login) 来挂载到指定的元素上。
Vue.component('login', login)
var vm = new Vue({
el: "#root"
})
</script>
使用Vue.component
<div id="root">
<!--组件的使用:以html标签的形式,组件名称作为html的标签名称-->
<register></register>
</div>
<script src="./js/vue.js" type="text/javascript"></script>
<script type="text/javascript">
// 方法2:直接使用Vue.component方法
/*
注意,使用Vue.component 定义全局组件的时候,组件名称使用了驼峰命名,则在引用
组件的时候,需要把大写的驼峰改为小写字母,同时两个单词之间使用“-”进行连接,
如果没有小驼峰命名,直接使用即可
*/
Vue.component("register", {
// 注意组件里只能有一个根元素,下面的方式是错误的
// template:"<h1>欢迎新用户注册</h1><h3>注册第一步</h3>"
// 可以使用一个div将所有元素包括住
template: "<div><h1>欢迎新用户注册</h1><h3>注册第一步</h3></div>"
})
var vm = new Vue({
el: "#root"
})
</script>
使用template标签
<div id="root">
<main-nav></main-nav>
</div>
<!-- 在被控制的app标签外面,使用tempalte元素,定义组件的HTML模板结构 -->
<template id="nav_tpl">
<!--
组件模板的html内容仅能有一个根标签,因为组件只会渲染第一个标签
推荐:template内部定义一个div,然后所有的内容都定义在div里面
-->
<div>
<ul>
<li><a href="#">首页</a></li>
<li><a href="#">新闻</a></li>
<li><a href="#">产品</a></li>
</ul>
</div>
</template>
<script type="text/javascript">
// 方法3,在被控制的#root外部定义组件结构,这种方式有代码高亮
// 组件命名同样需要注意小驼峰命名的问题
Vue.component("main-nav", {
template: "#nav_tpl"
})
var vm = new Vue({
el: "#root"
})
</script>
二、定义局部组件
<div id="root">
<page-header></page-header>
<page-footer></page-footer>
</div>
<script src="./js/vue.js" type="text/javascript"></script>
<script type="text/javascript">
// 方法3,在被控制的#root外部定义组件结构,这种方式有代码高亮
// 组件命名同样需要注意小驼峰命名的问题
var vm = new Vue({
el: "#root",
components: {
pageHeader: {
template: '<h2>我是网页头部内容</h2>'
},
pageFooter: {
template: '<h2>©版权所有</h2>'
}
}
})
</script>
组件命名规则:
-
不能和html标签名冲突;
-
名称多单词;
- 中划线间隔:my-main,list-nav等
- 驼峰:myMain,listNav等,定义组件时使用驼峰,使用组件的时候需要把大写的驼峰改为小写字母,同时两个单词之间使用“-”进行连接,因为html标签不区分大小写。
三、全局组件和局部组件使用区别
全局组件注册以后,在不同的Vue实例挂载点内都可以使用,而局部的只能在注册的Vue实例对应的挂载点里使用
四、组件中的data
- 组件可以有自己的data数据
- 组件的data和实例的data有点不一样,实例中的data可以为一个对象,也可以是一个方法,但是组件的data必须是一个方法
- 组件中的data除了必须为一个方法以外,这个方法内部,还必须返回一个对象才行
- 组件中的data数据使用方式和实例中的data使用方式完全一样
<div id="root">
<main-nav></main-nav>
</div>
<template id="nav_tpl">
<div>
<nav>{{user}}的个人主页</nav>
<ul>
<li>姓名:{{user}}</li>
<li>年龄:{{age}}</li>
<li>地址:{{address}}</li>
</ul>
</div>
</template>
<script src="./js/vue.js" type="text/javascript"></script>
<script type="text/javascript">
/*
组件可以有自己的data数据
组件的data和实例的data有点不一样,实例中的data可以为一个对象,但是组件的data必须是一个方法
组件中的data除了必须为一个方法以外,这个方法内部,还必须返回一个对象才行
组件中的data数据使用方式和实例中的data使用方式完全一样
*/
Vue.component("main-nav", {
template: "#nav_tpl",
// data:{
// user:"张三"
// }
/*
*好处:
* 1、数据可拥有自己的过程代码(对组件的data数据进行初步处理)
* 2、组件的数据是在每个组件实例中独有的(隔离的)
* 3、Vue对象也是一个组件,把它称为父组件,自己定义的组件成为子组件
* 父组件与子组件之间数据也是隔离的
*/
data() {
return {
user: "张三",
age: 12,
address: "河南省郑州市"
}
}
})
var vm = new Vue({
el: "#root",
data: {
message: 'hello vuejs'
}
})
</script>
【练习】创建一个可以点击的按钮,每次点击按钮,按钮后边的 数字都会加1,使用组件实现:
<div id="root">
<!--组件实例1-->
<data-test></data-test>
<!--组件实例2-->
<data-test></data-test>
</div>
<template id="test">
<div>
<button @click="add()">递增按钮</button><span>{{count}}</span>
</div>
</template>
<script src="./js/vue.js" type="text/javascript"></script>
<script type="text/javascript">
Vue.component("data-test", {
template: "#test",
data: function () {
return {
count: 1
}
},
methods: {
add() {
this.count++;
}
}
})
var vm = new Vue({
el: "#root"
})
</script>
五、切换组件
有如下登录和注册的模板,我们来学习使用3种方式进行切换:
<div id="root">
<a href="">登录</a>
<a href="">注册</a>
<login></login>
<register></register>
</div>
<template id="login">
<div>
<h2>用户登录</h2>
<p>用户名:<input type="text"></p>
<p>密码:<input type="text"></p>
</div>
</template>
<template id="register">
<div>
<h2>用户注册</h2>
<p>手机号:<input type="text"></p>
<p>身份证号:<input type="text"></p>
</div>
</template>
<script src="./js/vue.js" type="text/javascript"></script>
<script type="text/javascript">
Vue.component("login", {
template: "#login"
})
Vue.component("register", {
template: "#register"
})
var vm = new Vue({
el: "#root"
})
</script>
默认效果如下:
实现切换后的效果如下:
使用v-if和v-else切换
<div id="root">
<a href="javascript:void(0)" @click="flag = true">登录</a>
<a href="javascript:void(0)" @click="flag = false">注册</a>
<login v-if="flag"></login>
<register v-else></register>
</div>
<template id="login">
<div>
<h2>用户登录</h2>
<p>用户名:<input type="text"></p>
<p>密码:<input type="text"></p>
</div>
</template>
<template id="register">
<div>
<h2>用户注册</h2>
<p>手机号:<input type="text"></p>
<p>身份证号:<input type="text"></p>
</div>
</template>
<script src="./js/vue.js" type="text/javascript"></script>
<script type="text/javascript">
Vue.component("login", {
template: "#login"
})
Vue.component("register", {
template: "#register"
})
var vm = new Vue({
el: "#root",
data: {
flag: true
}
})
</script>
component元素实现组件切换
component元素是一个占位符, :is 属性用来指定要展示的组件名称
<component :is="componentId"></component>
需要注意的是,:is属性的值是组件名称字符串,需要按照如下的代码,才能将组件正常展示
<div id="root">
<a href="javascript:void(0)" @click="flag=true">登录</a>
<a href="" @click.prevent="flag=false">注册</a>
<!-- component元素是一个占位符, :is 属性用来指定要展示的组件名称 -->
<component :is="'login'"></component>
<component :is="'register'"></component>
</div>
所以,如果使用<component>
元素就要定一个变量
<div id="root">
<a href="javascript:void(0)" @click="componentName='login'">登录</a>
<a href="" @click.prevent="componentName='register'">注册</a>
<!-- component元素是一个占位符, :is 属性用来指定要展示的组件名称 -->
<component :is="componentName"></component>
</div>
<template id="login">
<div>
<h2>用户登录</h2>
<p>用户名:<input type="text"></p>
<p>密码:<input type="text"></p>
</div>
</template>
<template id="register">
<div>
<h2>用户注册</h2>
<p>手机号:<input type="text"></p>
<p>身份证号:<input type="text"></p>
</div>
</template>
<script type="text/javascript">
Vue.component("login", {
template: "#login"
})
Vue.component("register", {
template: "#register"
})
var vm = new Vue({
el: "#root",
data: {
componentName: "login"
}
})
</script>
组件过渡切换
官网地址:https://cn.vuejs.org/v2/guide/transitions.html#多个组件的过渡
根据官网代码,改造案例:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<title></title>
<!-- 引入vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style>
.component-fade-enter-active,
.component-fade-leave-active {
transition: all .3s ease;
}
.component-fade-enter,
.component-fade-leave-to {
opacity: 0;
transform: translateX(150px)
}
</style>
</head>
<body>
<div id="root">
<a href="javascript:void(0)" @click="componentName='login'">登录</a>
<a href="" @click.prevent="componentName='register'">注册</a>
<!-- 添加 mode="out-in",意思是让过渡效果显示为一个消失了,另一个再进来 -->
<!-- 添加name属性,是为了设置过渡样式 -->
<transition name="component-fade" mode="out-in">
<component :is="componentName"></component>
</transition>
</div>
<template id="login">
<div>
<h2>用户登录</h2>
<p>用户名:<input type="text"></p>
<p>密码:<input type="text"></p>
</div>
</template>
<template id="register">
<div>
<h2>用户注册</h2>
<p>手机号:<input type="text"></p>
<p>身份证号:<input type="text"></p>
</div>
</template>
<script type="text/javascript">
Vue.component("login", {
template: "#login"
})
Vue.component("register", {
template: "#register"
})
var vm = new Vue({
el: "#root",
data: {
componentName: "login"
}
})
</script>
</body>
</html>
六、组件传值
组件传值的遇到的问题
局部定义组件的方式,需要考虑Vue实例和组件之间的传值的问题,而子组件默认无法访问到父组件的数据和methods里方法。
<div id="root">
<product-list></product-list>
</div>
<script src="./js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: "#root",
data: {
msg: "Vue实例相当于内部组件的父组件"
},
methods: {},
components: {
productList: {
template: '<h2>{{msg}}</h2>'
}
}
})
</script>
浏览器里的显示效果如下:
父组件向子组件传值
通过属性绑定的形式,把需要传递给子组件的数据,以属性绑定的形式,传递到子组件内部,供子组件使用。
官网摘抄:
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
<div id="app">
<h2>父组件内部的数据: {{message}}</h2>
<p>
<button type="button" @click="message += '@123'">修改父组件中的数据</button>
</p>
<!-- 传:在父组件中使用子组件时,通过子组件的属性进行传递 -->
<sub-component v-bind:msg="message"></sub-component>
</div>
<template id="sub">
<div>
<h2>子组件内部的数据: {{username}}</h2>
<h2>子组件接收父组件传递的数据: {{msg}}</h2>
<p>
<button type="button" @click="msg += '_321'">修改子组件中的数据</button>
</p>
</div>
</template>
<script src="./js/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
var subComponent = {
template: "#sub",
data() {
return {
username: "Tom"
};
},
props: ["msg"]
};
let vm = new Vue({
el: "#app",
data: {
message: "hello vuejs",
},
components: {
subComponent
}
});
</script>
子组件向父组件传值
方式一
1、使用v-on指令,将父组件中的函数与子组件绑定。
v-on:子组件中调用函数的名称="父组件中的函数名称"
2、子调用父函数。
通过Vue对象中提供一个“触发函数”来实现在子组件内调用父组件的函数
触发函数: $emit(父组件映射的函数名称, 调用父组件函数传递的参数)
<div id="app">
<h2>父组件内部的数据: {{message}}</h2>
<p>
<button type="button" @click="message += '@123'">修改父组件中的数据</button>
</p>
<!--
1.使用v-on指令,将父组件中的函数与子组件绑定,
v-on:子组件中调用函数的名称="父组件中的函数名称"
-->
<sub-component v-bind:msg="message" v-on:test="changeVal"></sub-component>
</div>
<template id="sub">
<div>
<h2>子组件内部的数据: {{username}}</h2>
<h2>子组件接收父组件传递的数据: {{msg}}</h2>
<p><button type="button" @click="doChange">调用父组件中的函数</button></p>
</div>
</template>
<script src="./js/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
var subComponent = {
template: "#sub",
data() {
return {
username: "Tom",
};
},
props: ["msg"],
methods: {
doChange() {
/**
* 2.子调用父函数
* 通过Vue对象中提供一个“触发函数”来实现在子组件内调用父组件的函数
* 触发函数: $emit(父组件映射的函数名称, 调用父组件函数传递的参数)
*
* 注意:在子向父传值时,无需修改子组件中的props数据
* 在子中调用$emit()修改父中data,父中的data一旦发生又立刻将新数据更新
到子
*/
// this.msg = '你好,组件'
this.$emit("test", "你好,组件");
},
},
};
let vm = new Vue({
el: "#app",
data: {
message: "hello vuejs",
},
components: {
subComponent,
},
methods: {
changeVal(p) {
console.log("changeVal函数执行了...");
this.message = p;
},
},
});
</script>
方式二
1、v-bind指令+sync修饰符(同步修饰符):可以实现数据的“双向绑定”
2、通过Vue对象中提供一个“触发函数”来实现
$emit('update:Props中数据的名称', 传递的数据)
<div id="app">
<h2>父组件内部的数据: {{message}}</h2>
<p>
<button type="button" @click="message += '@123'">修改父组件中的数据</button>
</p>
<!--
1. v-bind指令+sync修饰符(同步修饰符):可以实现数据的“双向绑定”
-->
<sub-component v-bind:msg.sync="message"></sub-component>
</div>
<template id="sub">
<div>
<h2>子组件内部的数据: {{username}}</h2>
<h2>子组件接收父组件传递的数据: {{msg}}</h2>
<p><button type="button" @click="dataBack">回传数据</button></p>
</div>
</template>
<script src="./js/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
var subComponent = {
template: "#sub",
data() {
return {
username: "Tom",
};
},
props: ["msg"],
methods: {
dataBack() {
/**
* 2.通过Vue对象中提供一个“触发函数”来实现
*
* $emit('update:Props中数据的名称', 传递的数据)
*/
this.$emit("update:msg", "你好,前端");
},
},
};
let vm = new Vue({
el: "#app",
data: {
message: "hello vuejs",
},
components: {
subComponent,
},
});
// 组件传值
// 1.父子传值:父data --> 子props
// 父向子:通过v-bind传递数据,通过v-on绑定函数
// 子向父:v-bind指令需要有.sync修饰符,通过this.$emit('update:更新props')回传数据
// 2.路由传值
// 3.vuex状态管理
</script>
七、Props
Props 是你可以在组件上注册的一些自定义特性。当一个值传递给一个 props 特性的时候,它就变成了那个组件实例的一个属性。
Props的大小写
HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:
// 组件
Vue.component('my-component', {
props: ['addTitle'],
template: '<h3>{{ addTitle }}</h3>'
})
在HTML中是中划线的:
<my-component :add-title="'hello'"></my-component>
Props类型
到这里,我们只看到了以字符串数组形式列出的 props:
props: ['name', 'age', 'isLogin', 'hobby', 'address']
但是,通常你希望每个 props 都有指定的值类型。这时,你可以以对象形式列出 props,这些属性的名称和值分别是 props 各自的名称和类型:
props: {
name: String,
age: Number,
isLogin: Boolean,
hobby: Array,
address: Object
}
示例代码:
<div id="app">
<sub-component
v-bind:msg="message"
:username="username"
:age="age"
:gender="gender"
:hobby="hobby"
:address="address"
:add-user="addUser"
title="标题"
></sub-component>
</div>
<template id="sub">
<div>
<h2>子组件内部的数据: {{name}}</h2>
<h2>子组件接收父组件的数据msg: {{msg}}</h2>
<h2>子组件接收父组件的数据addUser: {{addUser}}</h2>
<h2>子组件接收父组件的数据username: {{username}}</h2>
<h2>子组件接收父组件的数据age: {{age}}</h2>
<h2>子组件接收父组件的数据gender: {{gender}}</h2>
<h2>子组件接收父组件的数据hobby: {{hobby}}</h2>
<h2>子组件接收父组件的数据address: {{address}}</h2>
<h2>子组件接收父组件的数据title: {{title}}</h2>
<h2>子组件接收父组件的数据str: {{str}}</h2>
</div>
</template>
<script src="./js/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
var subComponent = {
template: "#sub",
data() {
return {
name: "Tom",
};
},
//props的值可以为数据,也可以是一个字面量对象
//字面量对象的属性名为接收父向子传数据的变量名称
//字面量对象的属性值为变量类型
props: {
msg: String,
addUser: String,
username: String,
age: Number,
gender: Boolean,
hobby: Array,
address: Object,
title: String,
str: {
//数据类型,数据默认,数据校验...
type: String,
default: "你好",
required: true,
},
},
};
let vm = new Vue({
el: "#app",
data: {
message: "hello vuejs",
addUser: "新增用户",
username: "Jerry",
age: 26,
gender: true,
hobby: ["读书", "运动", "游戏"],
address: {
city: "郑州市",
street: "迎春街",
},
},
components: {
subComponent,
},
});
</script>
传递静态或动态Props
像这样,你已经知道了可以像这样给 props 传入一个静态的值:
<my-component title="数据列表"></my-component>
也可以通过 v-bind 动态赋值,例如:
<!-- 动态赋予一个变量的值 -->
<my-component v-bind:title="addTitle"></my-component>
<!-- 动态赋予一个复杂表达式的值 -->
<my-component v-bind:title="name + '的' + addTitle"></my-component>
注意:静态传值的类型只能为字符串
Props验证
可以为组件的 props 指定验证要求,例如你知道的这些类型。如果有一个需求没有被满足,则 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
}
}
}
})
八、插槽
插槽内容和编译作用域
Vue 实现了一套内容分发的 API,这套 API 的设计灵感源自 Web Components 规范草案,将 <slot>
元素作为承载分发内容的出口。
如果 组件模板中没有包含一个 <slot>
元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃。
<div id="app">
<!--
在使用组件时,组件标签内所有的内容都会被忽略
props: 接收父组件传递的数据
插槽: 父向子传递的html文本
-->
<sub-component :msg="message">hello</sub-component>
<sub-component :msg="message">
<h3>html元素</h3>
<!--
编译作用域:
在父组件使用子组件时,在其标签内使用子组件的数据,这种情况是不允许的
该插槽模板使用的数据与其他非插槽使用数据应在同一个作用域内
-->
<!-- <h3>@{{name}}@</h3> -->
</sub-component>
</div>
<template id="sub">
<div>
<h2>组件数据</h2>
<p>{{name}}</p>
<p>{{msg}}</p>
<!--
通过<slot>标签来接收父组件向子传递的html文本
-->
<slot></slot>
</div>
</template>
<script src="./js/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
var subComponent = {
template: "#sub",
data() {
return {
name: "Tom",
};
},
props: ["msg"],
};
let vm = new Vue({
el: "#app",
data: {
message: "hello vuejs",
},
components: {
subComponent,
},
});
</script>
作为一条规则,请记住:
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
后备内容
有时为一个插槽设置具体的后备 (也就是默认的) 内容是很有用的,它只会在没有提供内容的时候被渲染。
<div id="app">
<my-button>提交</my-button>
<br />
<my-button>登录</my-button>
<br />
<my-button>取消</my-button>
<br />
<my-button></my-button>
</div>
<template id="btn">
<div>
<button>
<!--后备:插槽的默认文本-->
<slot>后备内容</slot>
</button>
</div>
</template>
<script src="./js/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
var myButton = {
template: "#btn",
};
let vm = new Vue({
el: "#app",
data: {
message: "hello vuejs",
},
components: {
myButton,
},
});
</script>
具名插槽
有时我们需要多个插槽。例如对于一个带有如下模板的 <base-layout>
组件:
<div class="container">
<header>
<!-- 我们希望把页头放这里 -->
</header>
<main>
<!-- 我们希望把主要内容放这里 -->
</main>
<footer>
<!-- 我们希望把页脚放这里 -->
</footer>
</div>
对于这样的情况,
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
一个不带 name 的
在向具名插槽提供内容的时候,我们可以在一个 元素上使用 v-slot 指令,并以 v-slot的参数的形式提供其名称:
现在 然而,如果你希望更明确一些,仍然可以在一个 中包裹默认插槽的内容:
完整示例代码: 废弃语法: https://cn.vuejs.org/v2/guide/components-slots.html#带有-slot-attribute-的具名插槽 废弃语法: https://cn.vuejs.org/v2/guide/components-slots.html#带有-slot-scope-attribute-的作用域插槽 混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。 当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”。 比如,数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先。 同名钩子函数将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用。 值为对象的选项,例如 methods 、 components 和 directives ,将被合并为同一个对象。两个对象键名冲突时,取组件对象的键值对。 在很多 Vue 项目中,我们使用 这种方式在很多中小规模的项目中运作的很好,在这些项目里 JavaScript 只被用来加强特定的视图。但当在更复杂的项目中,或者你的前端完全由 JavaScript 驱动的时候,下面这些缺点将变得非常明显: 全局定义 (Global definitions) 强制要求每个 component 中的命名不得重复 字符串模板 (String templates) 缺乏语法高亮,在 HTML 有多行的时候,需要用到丑陋的 \ 不支持 CSS (No CSS support) 意味着当 HTML 和 JavaScript 组件化时,CSS 明显被遗漏 没有构建步骤 (No build step) 限制只能使用 HTML 和 ES5 JavaScript, 而不能使用预处理器,如Pug (formerly Jade) 和 Babel 文件扩展名为 .vue 的 single-file components(单文件组件) 为以上所有问题提供了解决方法,并且还可 以使用 webpack 或 Browserify 等构建工具。 这是一个文件名为 Hello.vue 的简单实例: 现在我们获得: 完整语法高亮 CommonJS 模块 组件作用域的 CSS 在父组件中引入即可使用:<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
<template>
元素中的所有内容都将会被传入相应的插槽。任何没有被包裹在带有 v-slot
的<template>
中的内容都会被视为默认插槽的内容。<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<template v-slot:default>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</template>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
<div id="app">
<base-layout>
<template v-slot:footer>
<h1>这是页面底部信息</h1>
</template>
<!--
<p>这是页面的主要信息</p>
<p>页面的其他内容</p>
-->
<template v-slot:default>
<p>这是页面的主要信息</p>
<p>页面的其他内容</p>
</template>
<template v-slot:header>
<h1>这是页面头部信息</h1>
</template>
</base-layout>
</div>
<template id="layout">
<div>
<header>
<p>头部:</p>
<slot name="header"></slot>
</header>
<main>
<p>主区域</p>
<!--
<slot>标签的name属性默认值为default,表示当前插槽的为默认插槽
-->
<slot></slot>
</main>
<footer>
<p>底部:</p>
<slot name="footer"></slot>
</footer>
</div>
</template>
<script src="./js/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
var baseLayout = {
template: "#layout",
};
let vm = new Vue({
el: "#app",
data: {
message: "hello vuejs",
},
components: {
baseLayout,
},
});
</script>
作用域插槽
<div id="app">
<current-user v-slot:default="slotProps">{{slotProps}}</current-user>
<current-user v-slot="slotProps">{{slotProps.user.username}}</current-user>
<current-user v-slot="slotProps">{{slotProps.user.realname}}</current-user>
<current-user v-slot="{user}">{{user.email}}</current-user>
<current-user v-slot="{user}">{{user.mobile}}</current-user>
</div>
<template id="cuser">
<div>
<h2>当前用户组件</h2>
<!--
作用域插槽:为了让子组件的数据能够在父组件的编译作用域中使用
-->
<slot :user="user">无数据</slot>
</div>
</template>
<script src="./js/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
var currentUser = {
template: "#cuser",
data() {
return {
user: {
username: "Steven",
realname: "史蒂芬",
email: "[email protected]",
mobile: "13012345678",
},
};
},
};
let vm = new Vue({
el: "#app",
data: {
message: "hello vuejs",
},
components: {
currentUser,
},
});
</script>
九、混入
基础
<div id="app">
<sub-component></sub-component>
</div>
<template id="sub">
<div>
<h2>组件</h2>
<p>{{name}}</p>
<p><button type="button" @click="fn">按钮</button></p>
</div>
</template>
<script src="./js/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
/**
* 混入: mixin
* 将每个组件中公共内容提取出来,形成一个独立js文件,此js文件可以在模块中通过混入的模式引入
* 混入的封装: 数据,函数,生命周期钩子...一系列组件配置项
*/
//定义一个混入对象
var myMixin = {
methods: {
fn() {
console.log("hello from mixin");
},
},
created() {
this.fn();
},
};
//定义组件
var subComponent = {
template: "#sub",
data() {
return {
name: "Tom",
};
},
//混入配置
mixins: [myMixin],
};
let vm = new Vue({
el: "#app",
data: {
message: "hello vuejs",
},
components: {
subComponent,
},
});
</script>
选项合并
<div id="app">
<sub-component></sub-component>
</div>
<template id="sub">
<div>
<h2>组件</h2>
<p>{{name}}</p>
<p>{{age}}</p>
<p>{{gender}}</p>
<p>{{address}}</p>
<p><button type="button" @click="add">新增</button></p>
<p><button type="button" @click="edit">编辑</button></p>
<p><button type="button" @click="remove">删除</button></p>
</div>
</template>
<script src="./js/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
//定义一个混入对象
var myMixin = {
data() {
return {
name: "Jerry",
age: 23,
gender: "female",
address: "shanghai",
};
},
methods: {
add() {
console.log("mixin add...");
},
edit() {
console.log("mixin edit...");
},
remove() {
console.log("mixin remove...");
},
},
created() {
console.log("mixin created...");
},
};
var subComponent = {
template: "#sub",
//当组件中的data与混入对象的data冲突时,组件覆盖混入
data() {
return {
name: "Tom",
gender: "male",
};
},
//当组件中生命周期函数与混入对象冲突时,先执行混入再执行组件,不会出现覆盖的情况
created() {
console.log("subComponent created...");
},
//当组件中函数与混入对象冲突时,组件覆盖混入
methods: {
add() {
console.log("subComponent add...");
},
},
mixins: [myMixin],
};
let vm = new Vue({
el: "#app",
data: {
message: "hello vuejs",
},
components: {
subComponent,
},
});
</script>
十、vue单文件组件
Vue.component
来定义全局组件,紧接着用 new Vue({ el:'#container '})
在每个页面内指定一个容器元素。
<template>
<p>{{msg}}</p>
</template>
<script>
export default {
data() {
return {
msg: "hello vuejs",
};
},
};
</script>
<style scoped>
p {
font-size: 35px;
text-align: center;
color: red;
}
</style>
<template>
<p>{{msg}}</p>
</template>
<script>
import otherComponent from "./other.vue";
export default {
data() {
return {
msg: "hello vuejs",
};
},
components: {
otherComponent,
},
};
</script>
<style scoped>
p {
font-size: 35px;
text-align: center;
color: red;
}
</style>