组件
一个页面通常包括三个部分:HTML(骨架)、CSS(样式)、JS(交互)
传统开发方式:写某些通用的功能写在一个JS文件内,在多个也页面中进行使用,不通用的需要单独再写一个文件;弊端就是当引用页面过多,不利于维护,牵一发而动全身
组件化开发方式:页面上有很多功能,如搜索栏、轮播图、热点……,把这些功能都独立成一个个的组件,每个组件都有自己单独的JS、CSS、HTML片段……,当某个页面需要某个功能时引入组件即可
组件应用
<div id="app">
<!-- 使用组件 -->
<user-list></user-list>
</div>
<script>
// 创建一个局部组件 可以把Vue.extend()部分省略只留{...}部分
const userList = Vue.extend({
// 骨架
template: `
<ul>
<li v-for="(user, index) in users" :key="index">
{{user.name}} - {{user.age}}
</li>
</ul>
`,
// 数据
data() {
return {
users: [
{ name: "张三", age: 18 },
{ name: "李四", age: 19 },
{ name: "王五", age: 20 }
]
}
},
// 交互
methods(){
// ...
},
})
// Vue 实例
const vm = new Vue({
el: "#app",
// 注册组件(局部)
components: {
// 组件的名字: 变量名
'userList': userList
}
})
</script>
组件的创建和创建Vue实例基本相同,不同的是组件没有 el
属性,data
属性只能使用 function
形式返回数据
在注册的时候组件名和变量名一样的话只用写一个就行,如:components: { userList }
对于组件可使用的命名方式:
- 全部小写
- 首字母大写后面都小写
- 短横线分隔命名法(kebab-case)
- 驼峰命名法(CamelCase)只允许在脚手架环境下使用
- 首字母小写的驼峰方式,使用时需在单词分割处使用 - 方式访问
Vue会自动将组件名转换为 kebab-case,因此无论使用驼峰命名还是短横线分隔命名,Vue都能正确解析组件
全局组件:
// [创建|注册]一个全局组件
Vue.component('myComponent', [组件对象|对象名]);
组件嵌套
在哪里注册的组件就只能在哪里使用
// 注册组件bx
const bx = {
template: `
<div>
<h1>我是内层组件</h1>
</div>
`
// ...
}
// 注册组件tx
const tx = {
template: `
<div>
<h1>我是外层组件</h1>
<bx></bx>
</div>
`,
// 注册子组件
components: {
bx: bx
}
// ...
}
// Vue 实例
const vm = new Vue({
el: "#app",
components: {
tx: tx
}
})
实例无法使用bx的组件因为bx只在tx内注册所以只有tx可以使用
vm & vc
vm:new Vue({...});
实例:Vue
vc:Vue.extend({...});
实例:VueComponent
两个的关系如同兄弟一般,但vc有的vm一定有,vm有的vc不一定有
Vue原型对象
原型对象在JavaScript中是一个重要的概念,它是实现原型继承的基础,在Vue中,原型对象用于定义Vue实例的方法和属性
每个实例都有一个隐式的原型属性 __proto__
,指向构造函数的显式的原型属性 prototype
在JS中,每个函数(包括构造函数)都有一个
prototype
属性,而每个对象都有一个__proto__
属性
function Test() { };
const test = new Test();
// 以下数据式对等的
console.log(test.__proto__ == Test.prototype);
原型链:每个JavaScript对象都有一个原型对象,通过原型链连接起来。当尝试访问一个对象的属性或方法时,如果该对象本身没有这个属性或方法,JavaScript会沿着原型链向上查找,直到找到对应的属性或方法为止。
原型对象:原型对象是一个普通的对象,包含了一些共享的属性和方法。当创建一个新对象时,它会继承其构造函数的原型对象上的属性和方法。
原型链的最顶层如果不改动的的情况下是
Object
,Object
的prototype
是它自身__proto__
为null
单文件组件
概念:一个组件一个文件(xxx.vue),这是Vue框架规定的游览器无法直接打开识别需要Vue进行编译成正常三件套,文件命名规范和组件命名规范相同
文件内容通常包括三块:
<template>HTML骨架</template>
<script>JavaScript交互</script>
<style>CSS样式</style>
脚手架
因为游览器不支持ES6的模块化开发,所以编写的vue文件需要使用脚手架(CLI)进行编译成正常的游览器可以识别的HTML文件进行访问
安装
首先确保安装了Node.js版本建议在V10以上,其次更新以下npm的源
# 查看当前 npm 的源
npm config get registry
# 切换到淘宝镜像源
npm config set registry https://registry.npmmirror.com
# 切换到腾讯镜像源
npm config set registry http://mirrors.cloud.tencent.com/npm/
# 恢复默认的 npm 官方镜像源
npm config set registry https://registry.npmjs.org
# 全局安装命令
npm install -g @vue/cli
# 创建项目
vue create 项目名
# 编译程序并将编译内容放入内置服务器
npm run serve
项目结构

脚手架配置文件官方文档:vue.config.js配置文档
样式编写
在组件中有局部样式(在style标签中加入scoped属性)和全局样式两种,一般来说在 App.vue
中编写全局样式,在子组件中编写局部样式
如果在子组件中编写全局样式,那么如果选择器重名的话会导致一方不会按照自己的样式渲染,规则就是:组件AB都有一个 .clazz
的类名,并已写好样式,如果采用全局样式的话先引入组件A那么就会使用A组件中的样式,先引入组件B那么就会使用B组件中的样式
自定义事件
通过自定义事件来实现组件之间的通信。自定义事件是一种用于组件之间传递信息的机制。可以在一个组件中触发(emit
)一个事件,然后在另一个组件中监听(listen)这个事件,并在事件被触发时执行相应的逻辑
父组件:
<template>
<div>
<!-- 自定义事件 event -->
<User @event="contentUser"></User>
</div>
</template>
<script>
import User from "./components/User.vue";
export default {
methods: {
// 事件触发
contentUser(a,b,c) {console.log(a,b,c);}
},
components: {User}
};
</script>
子组件:
<template>
<div class="s1">
<button @click="contentuser">输出用户信息</button>
</div>
</template>
<script>
export default {
data(){
return {a: 1, b: 2, c: 3}
},
methods: {
// 事件被执行调用,并传递一些数据(多个)
contentuser() {this.$emit("event", this.a, this.b, this.c);}
}
};
</script>
父组件绑定事件 —— 子组件触发事件
使用 refs 绑定
子组件不变,父组件可以使用 this.$refs
进行绑定属性来达到相同的效果
<template>
<div>
<User ref="user"></User>
</div>
</template>
<script>
import User from "./components/User.vue";
export default {
mounted() {
// 给 ref="user" 绑定 event 事件,并给事件绑定回调函数 this.contentUser
this.$refs.user.$on("event", this.contentUser);
},
methods: {
contentUser() {console.log(666);}
},
components: {User}
};
</script>
解绑
// 解绑单个
this.$off("event");
// 解绑多个
this.$off(["event1", "event2"]);
// 全部解绑
this.$off();
在哪里子组件中进行解绑
<button @click="untying">解绑事件</button>
......
untying() {
// 解绑单个
this.$off("event");
}
全局事件总线
原理:创建一个全局的VC对象,所有组件共享这一个VC对象,本质上就是使用原型对象(prototype)
let extend = Vue.extend({});
Vue.prototype.x = new extend;
在 main.js 文件中进行为 vue 扩展一个属性 x 用来进行全局传参数
// 子组件
this.x.$emit("event", 666);
// 父组件(mounted) - 绑定一个事件
this.x.$on("event", this.contentUser);
消息的订阅与发布机制
安装:npm install pubsub-js
在 main.js 中全局导入
import pubsub from 'pubsub-js'
Vue.use(pubsub)
订阅
this.pid = pubsub.subscribe('data', (msg, data) => {
console.log(data)
});
msg:消息的名字
data:消息的具体数据
当销毁组件时需要取消订阅不然
pubsub.subscribe(this.pid)
发布
pubsub.publish('data', '333')
data:发布的名字
333:要被发送的消息
往data中发送一个消息,然后所有订阅data的都会接收到消息
组件嵌套
在一个组件标签中嵌套另一个标签或者组件可以使用 <Clock/>
来接受,如下:
<!-- Test组件 -->
<template>
<span>我是一个组件</span>
<Clock />
</template>
<!-- 父组件使用 -->
<template>
<span>我是父组件</span>
<Text>
<span>我嵌套在了Text组件中</span>
</Text>
</template>
一般 <Clock/>
只需要写一个就够用了,不管在父组件中嵌套多少个标签,一个 <Clock/>
就可以全部展示