第二章Vue组件化编程

文章目录

  • 模块与组件、模块化与组件化
      • 模块
      • 组件
      • 模块化
      • 组件化
    • Vue中的组件含义
  • 非单文件组件
    • 基本使用
    • 组件注意事项
        • 使用 kebab-case
        • 使用 PascalCase
    • 组件的嵌套
    • 模板template
    • VueComponent
    • 一个重要的内置功能
  • 单文件组件
  • Vue脚手架
    • 使用Vue CLI脚手架
      • 先配置环境
      • 初始化脚手架
    • 分析脚手架结构
      • 实例
      • render函数——解决无模板解析
      • 修改默认配置
    • ref属性——定位元素
    • props配置项——传递参数
      • :age="20"和 age="20的区别"
    • mixin混入
    • plugin插件
    • scoped样式
    • 插槽Slot
      • 实例
    • 自定义事件

模块与组件、模块化与组件化

image-20210723115936262

image-20210723120028543

模块

  • 理解:向外提供特定功能的 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>

image-20230324235329231

总结:

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 值就是这个组件的选项对象。

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

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>

效果:

img

  • 对于一个组件想嵌套在哪个组件中,就要将对于的字组件嵌套在对应的父组件中

模板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>

img

image-20210730113353890

  1. 一个重要的内置关系:VueComponent.prototype.__proto__ === Vue.prototype

    • VueComponent.prototype指的是我们的VueComponent的原型对象 Vue.prototype指的是Vue的原型对象
  2. 为什么要有这个关系:让组件实例对象(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/

image-20230325113825842

  • 这就是 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>


img

总结:

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>

image-20230326102808424

总结:

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>

img

全局混入:

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>

img

总结:

插件:

  • 功能:用于增强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>

img

总结:

scoped样式:

  1. 作用:让样式在局部生效,防止冲突
    • 我们在给组件写对应的样式时,可能会有类名相同而导致样式冲突,所以对于组件的样式,我们最好能让其只在组件内局部生效,而不影响其他组件的样式
  2. 写法:<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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/3589.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vue路由的使用

地址&#xff1a; 入门 | Vue Router 一、导入vuerouter依赖包 注意&#xff0c;一定要先引入vue&#xff0c;再引入vue-router&#xff08;引入vue在引入vue-router的上面&#xff09;。不然会报错 <head><meta charset"utf-8"><title></ti…

算法:贪婪算法、分而治之

算法&#xff1a;贪婪算法、分而治之 文章目录1.贪婪算法计数硬币实例12.分而治之分割/歇征服/解决合并/合并实例23.动态规划对照实例34.基本概念算法数据定义数据对象内置数据类型派生数据类型基本操作1.贪婪算法 设计算法以实现给定问题的最佳解决方案。在贪婪算法方法中&am…

中国蚁剑AntSword实战

中国蚁剑AntSword实战1.基本使用方法2.绕过安全狗连接3.请求包修改UA特征伪造RSA流量加密4.插件使用1.基本使用方法 打开蚂蚁宝剑&#xff0c;右键添加数据&#xff1a; 输入已经上传马的路径和连接密码&#xff1a; 测试连接&#xff0c;连接成功&#xff01; GetShell了&…

【Linux】权限详解

前言首先我们先来看一下权限的概念&#xff1a;在多用户计算机系统的管理中&#xff0c;权限&#xff08;privilege&#xff09;是指某个特定的用户具有特定的系统资源使用权力&#xff0c;像是文件夹&#xff0c;特定系统指令的使用或存储量的限制。通常&#xff0c;系统管理员…

动态内存管理详细讲解

目录 1.为什么存在动态内存分配 2. 动态内存函数的介绍 2.1 malloc和free 2.2 calloc 2.3 realloc 今天要和大家分享的内容是的动态内存管理&#xff0c;我们先从他的定义入手学习。 1.为什么存在动态内存分配 我们到现在已经掌握了内存开辟的方式就是要么创建一个变量…

【差分数组】

差分数组一维差分差分数组的作用差分矩阵结语一维差分 输入一个长度为 n 的整数序列。接下来输入 m个操作&#xff0c;每个操作包含三个整数 l,r,c&#xff0c;表示将序列中 [l,r] 之间的每个数加上 c &#xff0c;请你输出进行完所有操作后的序列。 输入格式 第一行包含两个…

ArduPilot飞控之ubuntu22.04-Gazebo模拟

ArduPilot飞控之ubuntu22.04-Gazebo模拟1. 源由2. Gazebo安装2.1 ubuntu22.04系统更新2.2 安装Gazebo Garden2.3 安装ArduPilot Gazebo插件2.3.1 基础库安装2.3.2 源代码编译2.3.3 配置插件2.4 测试Gazebo Garden3. ArduPilot SITL Gazebo模拟3.1 Gazebo Garden模拟环境3.2 Ar…

大数据周会-本周学习内容总结07

目录 01【hadoop】 1.1【编写集群分发脚本xsync】 1.2【集群部署规划】 1.3【Hadoop集群启停脚本】 02【HDFS】 2.1【HDFS的API操作】 03【MapReduce】 3.1【P077- WordCount案例】 3.2【P097-自定义分区案例】 历史总结 01【hadoop】 1.1【编写集群分发脚本xsync】…

【Spring从成神到升仙系列 四】从源码分析 Spring 事务的来龙去脉

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱敲代码的小黄&#xff0c;独角兽企业的Java开发工程师&#xff0c;CSDN博客专家&#xff0c;阿里云专家博主&#x1f4d5;系列专栏&#xff1a;Java设计模式、数据结构和算法、Kafka从入门到成神、Kafka从成神到升仙…

YOLOv7训练自己的数据集(手把手教你)

YOLOv7训练自己的数据集整个过程主要包括&#xff1a;环境安装----制作数据集----模型训练----模型测试----模型推理 一&#xff1a;环境安装 conda create -n yolov7 python3.8 conda activate yolov7 #cuda cudnn torch等版本就不细说了&#xff0c;根据自己的显卡配置自行…

水果新鲜程度检测系统(UI界面+YOLOv5+训练数据集)

摘要&#xff1a;水果新鲜程度检测软件用于检测水果新鲜程度&#xff0c;利用深度学习技术识别腐败或损坏的水果&#xff0c;以辅助挑拣出新鲜水果&#xff0c;支持实时在线检测。本文详细介绍水果新鲜程度检测系统&#xff0c;在介绍算法原理的同时&#xff0c;给出Python的实…

给准备面试网络工程师岗位的应届生一些建议

你听完这个故事&#xff0c;应该会有所收获。最近有一个23届毕业的大学生和我聊天&#xff0c;他现在网络工程专业大四&#xff0c;因为今年6、7月份的时候毕业&#xff0c;所以现在面临找工作的问题。不管是现在找一份实习工作&#xff0c;还是毕业后找一份正式工作&#xff0…

100天精通Python丨基础知识篇 —— 03、Python基础知识扫盲(第一个Python程序,13个小知识点)

文章目录&#x1f41c; 1、Python 初体验Pycharm 第一个程序交互式编程第一个程序&#x1f41e; 2、Python 引号&#x1f414; 3、Python 注释&#x1f985; 4、Python 保留字符&#x1f42f; 5、Python 行和缩进&#x1f428; 6、Python 空行&#x1f439; 7、Python 输出&…

Linux基础知识点总结

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a;小刘主页 ♥️每天分享云计算网络运维课堂笔记&#xff0c;努力不一定有收获&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️夕阳下&#xff0c;是最美的绽放&#xff0…

史上最详细的改良顺序表讲解,看完不会你打我

目录 0.什么是顺序表 1.顺序表里结构体的定义 2.顺序表的初始化 3.顺序表的输入 4.增加顺序表的长度 5.1顺序表的元素查找&#xff08;按位查找&#xff09; 5.2顺序表的元素查找&#xff08;按值查找&#xff09;在顺序表进行按值查找&#xff0c;大概只能通过遍历的方…

HFish蜜罐的介绍和简单测试(三)

在学习蜜罐时&#xff0c;HFish是个不错的选择。首先是免费使用&#xff0c;其次易于安装管理&#xff0c;然后文档支持比较丰富&#xff0c;最后还有更多扩展功能。第三篇的话作为本系列的最终篇章进行总结&#xff0c;具体是看到哪里写到哪里。 0、HFish平台管理 0.1、报告…

基于SpringBoot实现冬奥会运动会科普平台【源码+论文】

基于SpringBoot实现冬奥会科普平台演示开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#…

一文吃透SpringBoot整合mybatis-plus(保姆式教程)

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

23.3.26总结

康托展开 是一个全排列与自然数的映射关系&#xff0c;康托展开的实质是计算当前序列在所有从小到大的全排列中的顺序&#xff0c;跟其逆序数有关。 例如&#xff1a;对于 1,2,3,4,5 来说&#xff0c;它的康托展开值为 0*4&#xff01;0*3&#xff01;0*2&#xff01;0*1&…

Android 之 打开相机 打开相册

Android 之 打开系统摄像头拍照 打开系统相册&#xff0c;并展示1&#xff0c;清单文件 AndroidManifest.xml<uses-permission android:name"android.permission.INTERNET" /><!--文件读取权限--><uses-permission android:name"android.permiss…