文章目录
- 模块与组件、模块化与组件化
- 模块
- 组件
- 模块化
- 组件化
- Vue中的组件含义
- 非单文件组件
- 基本使用
- 组件注意事项
- 使用 kebab-case
- 使用 PascalCase
- 组件的嵌套
- 模板template
- VueComponent
- 一个重要的内置功能
- 单文件组件
- Vue脚手架
- 使用Vue CLI脚手架
- 先配置环境
- 初始化脚手架
- 分析脚手架结构
- 实例
- render函数——解决无模板解析
- 修改默认配置
- ref属性——定位元素
- props配置项——传递参数
- :age="20"和 age="20的区别"
- mixin混入
- plugin插件
- scoped样式
- 插槽Slot
- 实例
- 自定义事件
模块与组件、模块化与组件化
模块
- 理解:向外提供特定功能的 js 程序,一般就是一个 js 文件
- 为什么:js 文件很多很复杂
- 作用:复用 js,简化 js 的编写,提高 js 运行效率
组件
- 定义:用来实现局部功能的代码和资源的集合(html/css/js/image…)
- 为什么:一个界面的功能很复杂
- 作用:复用编码,简化项目编码,提高运行效率
模块化
当应用中的 js 都以模块来编写的,那这个应用就是一个模块化的应用
组件化
当应用中的功能都是多组件的方式来编写的,那这个应用就是一个组件化的应用
Vue中的组件含义
组件是可复用的Vue实例
, 说白了就是一组可以重复使用的模板
, 跟JSTL的自定义标签、Thymeleal的th:fragment
等框架有着异曲同工之妙,通常一个应用会以一棵嵌套的组件树的形式来组织:
- 组件系统是 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">
<script type="text/javascript" src="../js/vue.js"></script>
<title>Document</title>
</head>
<body>
<div id="root">
<!-- 3使用组件 -->
<school></school>
<student></student>
</div>
<script type="text/javascript">
//1创建组件
const school=Vue.extend({
template: `
<div>
<h1>{{schoolName}}</h1>
<h2>{{address}}</h2>
</div>
`,
data() {
return {
schoolName: '一中',
address: '柳岸'
}
},
})
//1创建组件的快捷方式
const student={
template: `
<div>
<h1>{{studentName}}</h1>
<h2>{{age}}</h2>
</div>
`,
data() {
return {
studentName: 'lsc',
age: 22
}
},
}
//2全局注册组件
Vue.component('student',student)
new Vue({
el: '#root',
data: {
msg: 'hello compentments'
},
//2局部注册组件
components: {
school:school //这种可以简写为 school
}
})
</script>
</body>
</html>
总结:
Vue中使用组件的三大步骤:
-
定义组件(创建组件)——Vue.extend
-
注册组件——局部和全局
-
使用组件(写组件标签)
如何定义一个组件?
-
使用
Vue.extend(options)创建
,其中options和new Vue(options)时传入的options几乎一样,但也有点区别: -
el不要写,为什么?
- 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器
-
data必须写成函数,为什么?
- 避免组件被复用时,数据存在引用关系,防止同一个组件的实例对象之间数据相互影响
如何注册组件?
- 局部注册:new Vue的时候传入components选项
- 全局注册:Vue.component(‘组件名’,组件)
全局注册
Vue.component('my-component-name', { // ... 选项 ... })
这些组件是全局注册的。也就是说它们在注册之后可以用在任何新创建的 Vue 根实例 (
new Vue
) 的模板中Vue.component('component-a', { /* ... */ }) Vue.component('component-b', { /* ... */ }) Vue.component('component-c', { /* ... */ }) new Vue({ el: '#app' }) <div id="app"> <component-a></component-a> <component-b></component-b> <component-c></component-c> </div>
在所有子组件中也是如此,也就是说这三个组件
在各自内部也都可以相互使用。
局部注册
- 全局注册往往是不够理想的。比如,如果你使用一个像 webpack 这样的构建系统,全局注册所有的组件意味着即便你已经不再使用一个组件了,它仍然会被包含在你最终的构建结果中。这造成了用户下载的 JavaScript 的无谓的增加。
在这些情况下,你可以通过一个普通的 JavaScript 对象来定义组件:
var ComponentA = { /* ... */ } var ComponentB = { /* ... */ } var ComponentC = { /* ... */ }
然后在
components
选项中定义你想要使用的组件:new Vue({ el: '#app', components: { 'component-a': ComponentA, 'component-b': ComponentB } })
对于
components
对象中的每个 property 来说,其 property 名就是自定义元素的名字,其 property 值就是这个组件的选项对象。注意局部注册的组件在其子组件中不可用。例如,如果你希望
ComponentA
在ComponentB
中可用,则你需要这样写:var ComponentA = { /* ... */ } var ComponentB = { components: { 'component-a': ComponentA }, // ... }
如果你通过 Babel 和 webpack 使用 ES2015 模块
import ComponentA from './ComponentA.vue' export default { components: { ComponentA }, // ... }
注意在 ES2015+ 中,在对象中放一个类似
ComponentA
的变量名其实是ComponentA: ComponentA
的缩写,即这个变量名同时是用在模板中的自定义元素的名称
- 包含了这个组件选项的变量名
如何使用
编写组件标签:<school></school>
组件注意事项
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>组件注意事项</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>{{msg}}</h1>
<school></school>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const school = Vue.extend({
name:'atguigu',
template:`
<div>
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
</div>
`,
data(){
return {
name:'尚硅谷',
address:'北京'
}
}
})
new Vue({
el:'#root',
data:{
msg:'欢迎学习Vue!'
},
components:{
school
}
})
</script>
</html>
总结:
关于组件名:
一个单词组成:
-
第一种写法(首字母小写):school
-
第二种写法(首字母大写):School
多个单词组成:
-
第一种写法(kebab-case命名):my-school
-
第二种写法(CamelCase命名):MySchool (需要Vue脚手架支持)
组件名大小写
定义组件名的方式有两种:
使用 kebab-case
Vue.component('my-component-name', { /* ... */ })
当使用 kebab-case (短横线分隔命名) 定义一个组件时,你也必须在引用这个自定义元素时使用 kebab-case,例如
<my-component-name>
。使用 PascalCase
Vue.component('MyComponentName', { /* ... */ })
当使用 PascalCase (首字母大写命名) 定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用。也就是说
<my-component-name>
和<MyComponentName>
都是可接受的。注意,尽管如此,直接在 DOM (即非字符串的模板) 中使用时只有 kebab-case 是有效的。
备注:
-
组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行
-
可以使用name配置项指定组件在开发者工具中呈现的名字
关于组件标签:
-
第一种写法:
<school></school>
-
第二种写法:
<school/>
-
备注:不使用脚手架时,
<school/>
会导致后续组件不能渲染
一个简写方式:const school = Vue.extend({options})可简写为:const school = {options}
组件的嵌套
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>组件的嵌套</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
//定义student组件
const student = Vue.extend({
template:`
<div>
<h2>学生名称:{{name}}</h2>
<h2>学生年龄:{{age}}</h2>
</div>
`,
data(){
return {
name:'JOJO',
age:20
}
}
})
//定义school组件
const school = Vue.extend({
template:`
<div>
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<student></student>
</div>
`,
components:{
student
},
data(){
return {
name:'尚硅谷',
address:'北京'
}
}
})
//定义hello组件
const hello = Vue.extend({
template:`
<h1>{{msg}}</h1>
`,
data(){
return {
msg:"欢迎学习尚硅谷Vue教程!"
}
}
})
//定义app组件
const app = Vue.extend({
template:`
<div>
<hello></hello>
<school></school>
</div>
`,
components:{
school,
hello
}
})
//创建vm
new Vue({
template:`
<app></app>
`,
el:'#root',
components:{
app
}
})
</script>
</html>
效果:
- 对于一个组件想嵌套在哪个组件中,就要将对于的字组件嵌套在对应的父组件中
模板template
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script language="JavaScript" src="static/script/vue.js"></script>
</head>
<body>
<!--view层 模板-->
<div id="app">
<myfirstcomponent></myfirstcomponent>
</div>
<script language="JavaScript">
// 定义一个Vue组件
Vue.component("myfirstcomponent", {
template: '<div> hello,我是一个组件 </div>'
});
var vm=new Vue({
el: "#app",
data: {
}
});
</script>
</body>
</html>
- 组件的第一个参数就是组件名
- 当直接在 DOM 中使用一个组件 (而不是在字符串模板或单文件组件) 的时候,我们强烈推荐遵循 W3C 规范中的自定义组件名 (字母全小写且必须包含一个连字符)。这会帮助你避免和当前以及未来的 HTML 元素相冲突。
- 组件失效的原因
- 没有实例化某个Vue对象
- 组件必须挂载在某个Vue实例之下,否则不会生效
- 标签名称不能有大写字母
- 创建组件构造器和注册组件的代码必须在Vue实例之前
VueComponent
const school = Vue.extend({
name:'atguigu',
template:`
<div>
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
</div>
`,
data(){
return {
name:'尚硅谷',
address:'北京'
}
}
})
关于VueComponent:
-
school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的
-
我们只需要写
<school/>或<school></school>
,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:new VueComponent(options) -
特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent!
-
关于this指向:
-
组件配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是VueComponent实例对象
-
new Vue(options)配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是Vue实例对象
-
VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)
-
Vue的实例对象,以后简称vm
-
只有在本笔记中VueComponent的实例对象才简称为vc
- vc在创建的时候不能写el,只能跟着对应的vm的时候,来决定
一个重要的内置功能
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>一个重要的内置关系</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<school></school>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
Vue.prototype.x = 99
const school = Vue.extend({
name:'school',
template:`
<div>
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<button @click="showX">点我输出x</button>
</div>
`,
data(){
return {
name:'尚硅谷',
address:'北京'
}
},
methods: {
showX(){
console.log(this.x)
}
},
})
const vm = new Vue({
el:'#root',
data:{
msg:'你好'
},
components:{school}
})
</script>
</html>
-
一个重要的内置关系:
VueComponent.prototype.__proto__ === Vue.prototype
- VueComponent.prototype指的是我们的VueComponent的原型对象 Vue.prototype指的是Vue的原型对象
-
为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue 原型上的属性、方法
- 通过显示原型链可以给原型对象添加属性,实例对象通过隐式原型链获取原型对象的属性,从自身沿着原型链一直找到window的原型对象为空
单文件组件
School.vue
<template>
<div id='Demo'>
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<button @click="showName">点我提示学校名</button>
</div>
</template>
<script>
export default {
name:'School',
data() {
return {
name:'尚硅谷',
address:'北京'
}
},
methods: {
showName(){
alert(this.name)
}
},
}
</script>
<style>
#Demo{
background: orange;
}
</style>
- export default 默认暴露,将我们的组件School暴露出去
- 可以使用name配置项指定组件在开发者工具中呈现的名字
- 其实export default {}是 简写 全称是 export default Vue.expend({optitons})
Student.vue
<template>
<div>
<h2>学生姓名:{{name}}</h2>
<h2>学生年龄:{{age}}</h2>
</div>
</template>
<script>
export default {
name:'Student',
data() {
return {
name:'JOJO',
age:20
}
},
}
</script>
App.vue
<template>
<div>
<School></School>
<Student></Student>
</div>
</template>
<script>
import School from './School.vue'
import Student from './Student.vue'
export default {
name:'App',
components:{
School,
Student
}
}
</script>
main.js
import App from './App.vue'
new Vue({
template:`<App></App>`,
el:'#root',
components:{App}
})
index.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>单文件组件练习</title>
</head>
<body>
<div id="root"></div>
<script src="../../js/vue.js"></script>
<script src="./main.js"></script>
</body>
</html>
- 直接这样写,并不能成功。需要我们的Vue 脚手架的帮助,才能实现
Vue脚手架
使用Vue CLI脚手架
先配置环境
Node.js
:http://nodejs.cn/download/安装就是无脑的下一步就好,安装在自己的环境目录下
- 确认nodejs安装成功
- cmd下输入node -v,查看是否能够正确打印出版本号即可!
- cmd下输入npm -v,查看是否能够正确打印出版本号即可!
- 这个npm,就是一个软件包管理工具,就和linux下的apt软件安装差不多!
Git
:https://git-scm.com/doenloads
初始化脚手架
- Vue 脚手架是 Vue 官方提供的标准化开发工具(开发平台)
- 最新的版本是 4.x
- 文档:Vue CLI
具体步骤
- 如果下载缓慢请配置 npm 淘宝镜像:npm config set registry http://registry.npm.taobao.org
-g 就是全局安装
npm install cnpm -g
或使用如下语句解决npm速度慢的问题
npm install --registry=https://registry.npm.taobao.org
- 但是能不用cnpm就不用,npm比较好,因为cnpm可能打包的时候会出现问题
- 全局安装@vue/cli——npm install -g @vue/cli
- 切换到你要创建项目的目录,然后使用命令创建项目:vue create xxx(项目名)
- 选择使用vue的版本,我们学习的是Vue2,所以选择2
- 启动项目:npm run serve
- 暂停项目:Ctrl+C
- Vue 脚手架隐藏了所有 webpack 相关的配置,若想查看具体的 webpakc 配置,请执行:vue inspect > output.js
呈现效果——访问http://localhost:8080/
- 这就是 Node.js的服务,跟tomcat 差不多。
- Node.js它是一个服务器,它可以运行一些东西。
分析脚手架结构
初始脚手架文件结构:
.文件目录
├── node_modules
├── public
│ ├── favicon.ico: 页签图标
│ └── index.html: 主页面
├── src
│ ├── assets: 存放静态资源
│ │ └── logo.png
│ │── component: 存放组件
│ │ └── HelloWorld.vue
│ │── App.vue: 汇总所有组件
│ └── main.js: 入口文件
├── .gitignore: git版本管制忽略的配置
├── babel.config.js: babel的配置文件
├── package.json: 应用包配置文件
├── README.md: 应用描述文件
└── package-lock.json: 包版本控制文件
- main.js是一切的开端
实例
src/components/School.vue
<template>
<div id='Demo'>
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<button @click="showName">点我提示学校名</button>
</div>
</template>
<script>
export default {
name:'School',
data() {
return {
name:'尚硅谷',
address:'北京'
}
},
methods: {
showName() {
alert(this.name)
}
},
}
</script>
<style>
#Demo{
background: orange;
}
</style>
src/components/Student.vue
<template>
<div>
<h2>学生姓名:{{name}}</h2>
<h2>学生年龄:{{age}}</h2>
</div>
</template>
<script>
export default {
name:'Student',
data() {
return {
name:'JOJO',
age:20
}
},
}
</script>
src/App.vue
<template>
<div>
<School></School>
<Student></Student>
</div>
</template>
<script>
import School from './components/School.vue'
import Student from './components/Student.vue'
export default {
name:'App',
components:{
School,
Student
}
}
</script>
src/main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
//将App组件放入容器中
render: h => h(App),
}).$mount('#app')
- 我们发现在main.js中,我们没有注册App组件,这些其实都是在render函数中,等会解释
public/index.html
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="UTF-8">
<!-- 针对IE浏览器的特殊配置,含义是让IE浏览器以最高渲染级别渲染页面 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- 开启移动端的理想端口 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 配置页签图标 -->
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<!-- 配置网页标题 -->
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<!-- 容器 -->
<div id="app"></div>
</body>
</html>
- 虽然在表面上我们没有使用
<script src="./main.js"></script>
来引入,但是我们可以进行使用<div id="app"></div>
,是因为脚手架帮我们做好了
render函数——解决无模板解析
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
el:'#app',
// 简写形式
render: h => h(App),
// 完整形式
// render(createElement){
// return createElement(App)
// }
// 因为只有一个参数所以可以把括号去掉 和一条语句只是return,写成render createElement =>return createElement(App)
})
总结:
关于不同版本的函数:
-
vue.js 与 vue.runtime.xxx.js的区别:
- vue.js 是完整版的 Vue,包含:核心功能+模板解析器
- vue.runtime.xxx.js 是运行版的 Vue,只包含核心功能,没有模板解析器,我们的import Vue from 'vue’引入的就是import Vue from ‘vue’
-
因为 vue.runtime.xxx.js 没有模板解析器,所以不能使用 template 配置项,需要使用 render函数接收到的createElement 函数去指定具体内容
修改默认配置
- vue.config.js 是一个可选的配置文件,如果项目的(和 package.json 同级的)根目录中存在这个文件,那么它会被 @vue/cli-service 自动加载
- 使用 vue.config.js 可以对脚手架进行个性化定制,详见配置参考 | Vue CLI
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
lintOnSave:false /*关闭语法检查*/
})
webpack将配置文件放在了webpack.config.js中,这个文件被隐藏,就是防止我们乱改,对于我们想改Vue的配置,我们可以通过新建一个vue.config.js 文件,将这个文件和我们webpack.config.js进行合并,达到我们修改配置的功能
ref属性——定位元素
<template>
<div>
<h1 ref="title">{{msg}}</h1>
<Student ref="sch"/>
<button @click="show" ref="btn">点我输出ref</button>
</div>
</template>
<script>
import Student from '../components/Student.vue'
export default {
name:'App',
components: { Student },
data() {
return {
msg:'欢迎学习Vue!'
}
},
methods:{
show(){
console.log(this.$refs.title)
console.log(this.$refs.sch)
console.log(this.$refs.btn)
}
}
}
</script>
总结:
ref属性:
-
被用来给元素或子组件注册引用信息(id的替代者)
- 应用在html标签上获取的是真实DOM元素,应用在组件标签上获取的是组件实例对象(vc)
-
使用方式:
- 打标识:
<h1 ref="xxx"></h1> 或 <School ref="xxx"></School>
- 打标识:
-
获取:this.$refs.xxx
props配置项——传递参数
像上面那样用组件没有任何意义,所以我们是需要传递参数到组件
的,此时就需要使用props
属性了!注意:默认规则下props属性里的值不能为大写
;
HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:
src/App.vue
<template>
<div>
<h1>{{msg}}</h1>
<Student name="JOJO" sex="男" :age="20" />
<Student name="POKO" sex="男" :age="30" />
<Student name="SJO" sex="女" :age="25" />
</div>
</template>
<script>
import Student from './components/Student'
export default {
name:'App',
components:{
Student
},
data() {
return {
msg: '我是一个正常人'
}
},
}
</script>
src/components/Student.vue
<template>
<div>
<h2>学生姓名:{{name}}</h2>
<h2>学生性别:{{sex}}</h2>
<h2>学生年龄:{{myage}}</h2>
<hr>
</div>
</template>
<script>
export default {
name:'Student',
//简单接收
// props:['name','age','sex'],
// 接收的同时对数据进行类型限制
// props:{
// name:String,
// age:Number,
// sex:String
// },
// 接收的同时对数据进行类型限制 + 指定默认值 + 限制必要性
props:{
name:{
type:String,
required:true,
},
age:{
type:Number,
default:99
},
sex:{
type:String,
required:true
}
},
data() {
return {
myage: this.age
}
},
}
</script>
总结:
props配置项:
-
功能:让组件接收外部传过来的数据
-
传递数据:
<Demo name="xxx"/>
,在使用组件标签的时候,进行传递数据 -
接收数据:
- 第一种方式(只接收):props:[‘name’]
- 第二种方式(限制数据类型):props:{name:String}
- 第三种方式(限制类型、限制必要性、指定默认值):
props:{
name:{
type:String, //类型
required:true, //必要性
default:'JOJO' //默认值
}
}
props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据
- 经过测试,我们发现对于组件来说,prop的属性的优先级是大于我们的data里面的属性,所以我们复制可以data() return{ mydata: this.prop}
:age="20"和 age=“20的区别”
- :age=“20” 的全称是v-bind:age=“20” ,也就是数据单向绑定了,而且我们知道这样绑定,""中的内容是表示js运算结果,所以传给age的值是数字20
- age=“20”,就表示传给age的值是一个字符串
mixin混入
src/mixin.js
//注册组件并暴露出去
export const mixin = {
methods: {
showName() {
alert(this.name)
}
},
mounted() {
console.log("你好呀~")
}
}
- 独立写一个混入的js文件
src/components/School.vue
<template>
<div>
<h2 @click="showName">学校姓名:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
</div>
</template>
<script>
//局部引入混入
import {mixin} from '../mixin'
export default {
name:'School',
data() {
return {
name:'尚硅谷',
address:'北京'
}
},
//局部注册
mixins:[mixin]
}
</script>
src/components/Student.vue
<template>
<div>
<h2 @click="showName">学生姓名:{{name}}</h2>
<h2>学生性别:{{sex}}</h2>
</div>
</template>
<script>
// 局部引入混入
import {mixin} from '../mixin'
export default {
name:'Student',
data() {
return {
name:'JOJO',
sex:'男'
}
},
//局部注册
mixins:[mixin]
}
</script>
src/App.vue
<template>
<div>
<School/>
<hr/>
<Student/>
</div>
</template>
<script>
import Student from './components/Student.vue'
import School from './components/School.vue'
export default {
name:'App',
components: { Student,School },
}
</script>
全局混入:
src/main.js:
import Vue from 'vue'
import App from './App.vue'
import {mixin} from './mixin'
Vue.config.productionTip = false
Vue.mixin(mixin)
new Vue({
el:"#app",
render: h => h(App)
})
总结:
mixin(混入):
- 功能:可以把多个组件共用的配置提取成一个混入对象
使用方式:
第一步定义混入:
const mixin = {
data(){....},
methods:{....}
....
}
第二步使用混入:
- 全局混入:Vue.mixin(xxx)
- 局部混入:mixins:[‘xxx’]
备注:
组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”,在发生冲突时以组件优先。
var mixin = {
data: function () {
return {
message: 'hello',
foo: 'abc'
}
}
}
new Vue({
mixins: [mixin],
data () {
return {
message: 'goodbye',
bar: 'def'
}
},
created () {
console.log(this.$data)
// => { message: "goodbye", foo: "abc", bar: "def" }
}
})
同名生命周期钩子将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用。
var mixin = {
created () {
console.log('混入对象的钩子被调用')
}
}
new Vue({
mixins: [mixin],
created () {
console.log('组件钩子被调用')
}
})
// => "混入对象的钩子被调用"
// => "组件钩子被调用"
plugin插件
src/plugin.js:
export default {
install(Vue,x,y,z){
console.log(x,y,z)
//全局过滤器
Vue.filter('mySlice',function(value){
return value.slice(0,4)
})
//定义混入
Vue.mixin({
data() {
return {
x:100,
y:200
}
},
})
//给Vue原型上添加一个方法(vm和vc就都能用了)
Vue.prototype.hello = ()=>{alert('你好啊')}
}
}
- 独立写一个插件的js文件
src/main.js:
import Vue from 'vue'
import App from './App.vue'
import plugin from './plugin'
Vue.config.productionTip = false
Vue.use(plugin,1,2,3)
new Vue({
el:"#app",
render: h => h(App)
})
src/components/School.vue:
<template>
<div>
<h2>学校姓名:{{name | mySlice}}</h2>
<h2>学校地址:{{address}}</h2>
</div>
</template>
<script>
export default {
name:'School',
data() {
return {
name:'尚硅谷atguigu',
address:'北京'
}
}
}
</script>
src/components/Student.vue:
<template>
<div>
<h2>学生姓名:{{name}}</h2>
<h2>学生性别:{{sex}}</h2>
<button @click="test">点我测试hello方法</button>
</div>
</template>
<script>
export default {
name:'Student',
data() {
return {
name:'JOJO',
sex:'男'
}
},
methods:{
test() {
this.hello()
}
}
}
</script>
总结:
插件:
- 功能:用于增强Vue
- 本质:
包含install方法的一个对象
,install的第一个参数是Vue,也就vm实例对象的构造函数,第二个以后的参数是插件使用者传递的数据
定义插件:
plugin.install = function (Vue, options) {
// 1.添加全局过滤器
Vue.filter(....)
// 2.添加全局指令
Vue.directive(....)
// 3. 配置全局混入
Vue.mixin(....)
// 4. 添加实例方法
Vue.prototype.$myMethod = function () {...}
Vue.prototype.$myProperty = xxxx
}
使用插件:Vue.use(plugin)
scoped样式
src/components/School.vue
<template>
<div class="demo">
<h2>学校姓名:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
</div>
</template>
<script>
export default {
name:'School',
data() {
return {
name:'尚硅谷',
address:'北京'
}
}
}
</script>
<style scoped>
.demo{
background-color: blueviolet;
}
</style>
src/components/Student.vue
<template>
<div class="demo">
<h2>学生姓名:{{name}}</h2>
<h2>学生性别:{{sex}}</h2>
</div>
</template>
<script>
export default {
name:'Student',
data() {
return {
name:'JOJO',
sex:'男'
}
}
}
</script>
<style scoped>
.demo{
background-color: chartreuse;
}
</style>
src/App.vue
<template>
<div>
<School/>
<Student/>
</div>
</template>
<script>
import Student from './components/Student.vue'
import School from './components/School.vue'
export default {
name:'App',
components: { Student,School },
}
</script>
总结:
scoped
样式:
- 作用:让样式在局部生效,防止冲突
- 我们在给组件写对应的样式时,可能会有类名相同而导致样式冲突,所以对于组件的样式,我们最好能让其只在组件内局部生效,而不影响其他组件的样式
- 写法:
<style scoped>
scoped
样式一般不会在App.vue
中使用
插槽Slot
实例
定义插槽模板
Vue.component('todo',{
template:'<div>\
<slot name="todo-title"></slot>\
<ul>\
<slot name="todo-items"></slot>\
</ul>\
</div>'
});
创建对应的组件
Vue.component('todo-title',{
props:['title'],
template:'<div>{{title}}</div>'
});
Vue.component("todo-items",{
props:["item"],
template:"<li>{{item}}</li>"
});
实例Vue并初始化数据
var vm = new Vue({
el:"#app",
data:{
title:"博客",
todoItems:['Java','Php','C#']
}
});
将这些值,通过插槽插入
<div id="app">
<todo>
<todo-title slot="todo-title" :title="title"></todo-title>
<todo-items slot="todo-items" v-for="item in todoItems" :item="item"></todo-items>
</todo>
</div>
完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层,模板-->
<div id="app">
<todo>
<todo-title slot="todo-title" :title="title"></todo-title>
<todo-items slot="todo-items" v-for="item in todoItems" :item="item"></todo-items>
</todo>
</div>
<script language="JavaScript" src="static/script/vue.js"></script>
<script type="text/javascript">
Vue.component('todo-title',{
props:['title'],
template:'<div>{{title}}</div>'
});
Vue.component("todo-items",{
props:["item"],
template:"<li>{{item}}</li>"
});
Vue.component('todo',{
template:'<div>\
<slot name="todo-title"></slot>\
<ul>\
<slot name="todo-items"></slot>\
</ul>\
</div>'
});
var vm = new Vue({
el:"#app",
data:{
title:"博客",
todoItems:['Java','Php','C#']
}
});
</script>
</body>
</html>
自定义事件
如果想实现,按一个删除键就能删除todo-items对应的一条数据,该如何实现
- 通以上代码不难发现,
数据项在Vue的实例中
,但删除操作要在组件中完成
, 那么组件如何才能删除Vue实例中的数据呢?此时就涉及到参数传递与事件分发了, Vue为我们提供了自定义事件的功能很好的帮助我们解决了这个问题; 使用this.$emit(‘自定义事件名’, 参数) , 操作过程如下:
在vue的实例中增加了methods对象并定义了一个名为removeItems的方法
var vm = new Vue({
el:"#app",
data:{
title:"标题",
todoItems:['Java','Php','C#']
},
methods:{
removeItems: function(index){
this.todoItems.splice(index,1);
}
}
});
- 因为数据是在vue实例中,所以删除的方法也应该在vue实例中
修改todo-items待办内容组件的代码,增加一个删除按钮,并且绑定事件!
Vue.component("todo-items",{
props:['item','index'],
//只能绑定绑定当前组件的方法
template:'<li>{{index}}--{{item}}<button @click="remove">删除</button></li>',
methods:{
remove: function(index){
//this.$emit()自定义事件分发
this.$emit('delete',index);
}
}
});
- this.$emit(‘’,‘’ …)第一个参数是我们的自定义事件名,第二个是相关的参数…
修改todo-items待办内容组件的HTML代码,增加一个自定义事件,比如叫remove,可以和组件的方法绑定,然后绑定到vue的方法!
<div id="app">
<todo>
<todo-title slot="todo-title" :title="title"></todo-title>
<todo-items slot="todo-items" v-for="(item,index) in todoItems"
:item="item" v-bind:index="index" v-on:delete="removeItems"></todo-items>
</todo>
</div>
完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--view层,模板-->
<div id="app">
<todo>
<todo-title slot="todo-title" :title="title"></todo-title>
<todo-items slot="todo-items" v-for="(item,index) in todoItems"
:item="item" v-bind:index="index"
v-on:delete="removeItems">
</todo-items>
</todo>
</div>
<script language="JavaScript" src="static/script/vue.js"></script>
<script type="text/javascript">
Vue.component('todo-title',{
props:['title'],
template:'<div>{{title}}</div>'
});
Vue.component("todo-items",{
props:['item','index'],
//只能绑定绑定当前组件的方法
template:'<li>{{index}}--{{item}}<button @click="remove(index)">删除</button></li>',
methods:{
remove: function(index){
//this.$emit()自定义事件分发
this.$emit('delete',index);
}
}
});
Vue.component('todo',{
template:'<div>\
<slot name="todo-title"></slot>\
<ul>\
<slot name="todo-items"></slot>\
</ul>\
</div>'
});
var vm = new Vue({
el:"#app",
data:{
title:"标题",
todoItems:['Java','Php','C#']
},
methods:{
removeItems: function(index){
this.todoItems.splice(index,1);
}
}
});
</script>
</body>
</html>