第二章Vue组件化编程
文章目录
- 模块与组件、模块化与组件化
- 模块
- 组件
- 模块化
- 组件化
- 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 = falseconst 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}})//创建vmnew 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 = falseVue.prototype.x = 99const 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 = falsenew 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 = falsenew 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>
相关文章:
![](https://img-blog.csdnimg.cn/img_convert/17e03ab8a94976de71cfaa6cd09dbe28.png)
第二章Vue组件化编程
文章目录模块与组件、模块化与组件化模块组件模块化组件化Vue中的组件含义非单文件组件基本使用组件注意事项使用 kebab-case使用 PascalCase组件的嵌套模板templateVueComponent一个重要的内置功能单文件组件Vue脚手架使用Vue CLI脚手架先配置环境初始化脚手架分析脚手架结构实…...
![](https://img-blog.csdnimg.cn/519928cb6d314d1eaaeeaf85d590f244.png)
面试官:vue2和vue3的区别有哪些
目录 多根节点,fragment(碎片) Composition API reactive 函数是用来创建响应式对象 Ref toRef toRefs 去除了管道 v-model的prop 和 event 默认名称会更改 vue2写法 Vue 3写法 vue3组件需要使用v-model时的写法 其他语法 1. 创…...
![](https://www.ngui.cc/images/no-images.jpg)
【TopK问题】——用堆实现
文章目录一、TopK问题是什么二、解决方法三、时间复杂度一、TopK问题是什么 TopK问题就是从1000个数中找出前K个最大的数或者最小的数这样的类似问题。 不过并不要求这k个数字必须是有序的,如果题目有要求,则进行堆排序即可。 还有比如求出全国玩韩信…...
![](https://img-blog.csdnimg.cn/2ab0ad51581f4dd9854484b124b593bc.png#pic_center)
【Spring从成神到升仙系列 四】从源码分析 Spring 事务的来龙去脉
👏作者简介:大家好,我是爱敲代码的小黄,独角兽企业的Java开发工程师,CSDN博客专家,阿里云专家博主📕系列专栏:Java设计模式、数据结构和算法、Kafka从入门到成神、Kafka从成神到升仙…...
![](https://www.ngui.cc/images/no-images.jpg)
使用Nginx反向代理OpenAI API
由于OpenAI的API在国内无法访问,所以可以通过海外服务器利用Nginx实现反向代理。 安装Nginx 这一步就不赘述了,不同的Linux系统安装方式略有不同,根据自己的服务器的系统自行百度即可。 OpenSSL创建证书 因为OpenAI的接口是https协议的&a…...
![](https://www.ngui.cc/images/no-images.jpg)
USB键盘实现——字符串描述符(四)
字符串描述符 字符串描述符内容解析和 HID鼠标 一致。 获取字符串描述符请求 标准设备请求 typedef struct __attribute__ ((packed)){union {struct __attribute__ ((packed)) {uint8_t recipient : 5; ///< Recipient type usb_request_recipient_t.uint8_t type …...
![](https://img-blog.csdnimg.cn/c7c0b0f9a0b94e85b4d19fd9380abbaa.png)
STM32的中断
目录 一、STM32中断概述 二、外部中断控制器EXTI 三、按键中断 四、串口中断 一、STM32中断概述 处理器中的中断在处理器中,中断是一个过程,即CPU在正常执行程序的过程中,遇到外部/内部的紧急事件需要处理,暂时中止当前程序的…...
![](https://img-blog.csdnimg.cn/img_convert/9fb0ae970598948cf0118967484a7dd5.png)
Flink进阶篇-CDC 原理、实践和优化采集到Doris中
简介 基于doris官方用doris构建实时仓库的思路,从flinkcdc到doris实时数仓的实践。 原文 Apache Flink X Apache Doris 构建极速易用的实时数仓架构 (qq.com) 前提-Flink CDC 原理、实践和优化 CDC 是什么 CDC 是变更数据捕获(Change Data Captur…...
![](https://img-blog.csdnimg.cn/e44f8d0328af4774a9442018443b5b1b.png)
看完这篇 教你玩转渗透测试靶机vulnhub——My File Server: 1
Vulnhub靶机My File Server: 1渗透测试详解Vulnhub靶机介绍:Vulnhub靶机下载:Vulnhub靶机安装:Vulnhub靶机漏洞详解:①:信息收集:②:FTP匿名登入:③:SMB共享服务…...
![](https://img-blog.csdnimg.cn/c927c3b482934c78b29b98758ac37782.png)
OpenHarmony实战STM32MP157开发板 “控制” Hi3861开发板 -- 中篇
一、前言 我们在 OpenHarmony实战STM32MP157开发板 “控制” Hi3861开发板 – 上篇 中介绍到了,App面板的开发,以及JS API接口的开发和调用。 那么本篇文章,会详解:BearPi-HM Nano开发板,如何实现数据上报和指令接收响应的。 看到这里,可能有同学可能已经知道思路了,因…...
![](https://img-blog.csdnimg.cn/c06ad346c63d4a97b5d0a048ef90a579.png)
【数据结构初阶】单链表
目录一、思路>>>>>>>>>>>>过程<<<<<<<<<<<<<<<1.打印2.尾插3.尾删4.头插5.头删6.查找7.指定位置后插入8.指定位置后删除9.链表的销毁二、整个程序1.SLTlist.c2.SLTlist.c一、思路 #define …...
![](https://img-blog.csdnimg.cn/7317382bf6874f668db994c998cfbd3e.png)
多线程代码案例-阻塞队列
hi,大家好,今天为大家带来多线程案例--阻塞队列 这块知识点也很重要,要好好掌握呀~~~ 🌸🌸🌸🌸🌸🌸🌸🌸🌸🌸🌸🌸🌸&#x…...
![](https://img-blog.csdnimg.cn/be50904ead4643fb9dc7ea227f8661af.png)
mysql的limit查询竟然有坑?
背景 最近项目联调的时候发现了分页查询的一个bug,分页查询总有数据查不出来或者重复查出。 数据库一共14条记录。 如果按照一页10条。那么第一页和第二页的查询SQL和和结果如下。 .png) 那么问题来了,查询第一页和第二页的时候都出现了11,12,13的记录…...
![](https://img-blog.csdnimg.cn/470cfa1f81d74f9891c5f5b9cbd1637d.png)
【Docker】MAC电脑下的Docker操作
文章目录安装Docker部署mysql 一主一从登录ChatGPT搞方案本地创建一个文件夹编辑docker-compose.yml文件启动检查并编排容器验证基于command的my.cnf配置的加载主数据库建一个用户给子数据库用于主从复制启动主从同步安装Docker 官网地址 https://www.docker.com/ 下载安装 验…...
![](https://img-blog.csdnimg.cn/51b40c4de18d4b7894f10ecb3fe715aa.png)
【Python3】matplotlib,模块,进/线程,文件/xml,百度人脸api,hal/aiohttp/curl
文章目录1.matplotlib/时间复杂度/线性表:顺序表要求存储空间必须连续2.python模块导入:python3 -c ‘import sys;print(sys.path)’ 显示导入模块时会去哪些路径下查找3.进/线程:进/线程是不能随便创建,就像每招一个员工是有代价…...
![](https://img-blog.csdnimg.cn/2010662931c44862966652ac3077cf50.png)
异或相关算法
文章目录1. 异或的性质2. 题目一3. 题目二4. 题目三5. 题目四1. 异或的性质 我们知道,异或的定义是:相同为0,相异为1。所以也被称为无进位相加,根据这定义,我们可以得出三个性质: 1. N ^ N0。2. N ^ 0N。3…...
![](https://www.ngui.cc/images/no-images.jpg)
python 使用pyshp读写shp文件
安装 pip install pyshp 引入 import shapefile读取 sfshapefile.Reader("{路径名}",encodingutf-8) # 仅仅读取 shapes与shape shapessf.shapes() 返回值是一个列表,包含该文件中所有的”几何数据”对象shapesf.shape(0) Shape是第1个”几何数据”…...
![](https://img-blog.csdnimg.cn/img_convert/fa1e64e1415b3d6c63c531376e1f4366.png)
eNSP FTP基础配置实验
关于本实验在本实验中,我们通过两台路由器来展示通过FTP在两台路由器之间传输文件。其中一台路由器AR2作为FTP服务器,另一台路由器AR1以FTP的方式登录AR2,并对AR2的文件系统进行一些更改。实验目的熟悉华为网络设备文件系统的管理。掌握华为网…...
![](https://img-blog.csdnimg.cn/865ac09e3efe4410b562094c2745c25a.png)
堆及其多种接口与堆排序的实现
我们本期来讲解堆结构 目录 堆的结构 堆的初始化 堆的销毁 堆的插入 向上调整算法 堆的删除 向下调整算法 取堆顶元素 判断堆是否为空 堆中元素个数 堆排序 向下调整与向上调整效率计算 Top-K问题 全部代码 堆的结构 堆是一种用数组模拟二叉树的结构 逻辑结构是…...
![](https://img-blog.csdnimg.cn/img_convert/fb726af0e444019feb1268123d3d68c9.webp?x-oss-process=image/format,png)
JNI原理及常用方法概述
1.1 JNI(Java Native Interface) 提供一种Java字节码调用C/C的解决方案,JNI描述的是一种技术。 1.2 NDK(Native Development Kit) Android NDK 是一组允许您将 C 或 C(“原生代码”)嵌入到 Android 应用中的工具,NDK描述的是工具集…...
![](https://img-blog.csdnimg.cn/05a6b930e9b74990a89e1f3936064a8c.png)
【Docker】之docker-compose的介绍与命令的使用
🍁博主简介 🏅云计算领域优质创作者 🏅华为云开发者社区专家博主 🏅阿里云开发者社区专家博主 💊交流社区:运维交流社区 欢迎大家的加入! 文章目录docker-compose简介docker-compose基础…...
![](https://img-blog.csdnimg.cn/9431052ba024472f9901b1e774aac902.png#pic_center)
水果新鲜程度检测系统(UI界面+YOLOv5+训练数据集)
摘要:水果新鲜程度检测软件用于检测水果新鲜程度,利用深度学习技术识别腐败或损坏的水果,以辅助挑拣出新鲜水果,支持实时在线检测。本文详细介绍水果新鲜程度检测系统,在介绍算法原理的同时,给出Python的实…...
![](https://www.ngui.cc/images/no-images.jpg)
flask多并发
多线程 flask默认使用多进程处理请求,因此,是支持并发的。比如两个调用a.html和b.html, 请求a.html未运行完成,在浏览访问b.html不会阻塞。开两个不同浏览器,分别请求请求运行时间较长的a.html也不阻塞。只要不用一个…...
![](https://img-blog.csdnimg.cn/img_convert/fa4cd871faaa4b938b3dac5bf7973a12.jpeg)
我用Python django开发了一个商城系统,已开源,求关注!
起始 2022年我用django开发了一个商城的第三方包,起名为:django-happy-shop。当时纯粹是利用业余时间来开发和维护这个包,想法也比较简单,Python语言做web可能用的人比较少,不一定有多少人去关注,就当是一个…...
![](https://img-blog.csdnimg.cn/58a03d4d7be14fbf99e7c26938fc700f.png)
大数据项目之数仓相关知识
第1章 数据仓库概念 数据仓库(DW): 为企业指定决策,提供数据支持的,帮助企业,改进业务流程,提高产品质量等。 DW的输入数据通常包括:业务数据,用户行为数据和爬虫数据等 ODS: 数据…...
![](https://img-blog.csdnimg.cn/f5d9ea1eaf0b46b99818aa9471b2ee10.jpeg)
RK3588平台开发系列讲解(视频篇)RTP H264 码流打包详解
平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、单 NALU 封包方式二、组合封包方式三、分片封包方式沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 H264 码流是放在 RTP 的有效载荷部分的。因此有效载荷前面的 RTP 头部跟码流本身是没有关系的,所以我…...
![](https://www.ngui.cc/images/no-images.jpg)
realloc的补充 柔性数组
🐶博主主页:ᰔᩚ. 一怀明月ꦿ ❤️🔥专栏系列:线性代数,C初学者入门训练,题解C,C的使用文章,「初学」C 🔥座右铭:“不要等到什么都没有了,才下…...
![](https://img-blog.csdnimg.cn/4632463a72104169838246cf21712270.png)
【C语言】柔性数组
柔性数组1. 柔性数组介绍2. 柔性数组特点3. 用例3.1 代码一:3.2 代码二:4. 柔性数组优势:1. 柔性数组介绍 也许你从来没有听说过柔性数组(flexible array)这个概念,但是它确实是存在的。 C99 中,…...
![](https://img-blog.csdnimg.cn/img_convert/c6588e81487da374f3ce69ddffacbe4c.png)
【Linux】权限详解
前言首先我们先来看一下权限的概念:在多用户计算机系统的管理中,权限(privilege)是指某个特定的用户具有特定的系统资源使用权力,像是文件夹,特定系统指令的使用或存储量的限制。通常,系统管理员…...
![](https://img-blog.csdnimg.cn/img_convert/048293c67da93a9dafa3596fd8060e28.png)
Android 之 打开相机 打开相册
Android 之 打开系统摄像头拍照 打开系统相册,并展示1,清单文件 AndroidManifest.xml<uses-permission android:name"android.permission.INTERNET" /><!--文件读取权限--><uses-permission android:name"android.permiss…...
![](/images/no-images.jpg)
wordpress 主题结构/seo优化网络
package mainimport "fmt"/** 切片的操作*/ func main() {func4() }/** 数组切片可以基于一个已存在的数组创建,数组切片可以值使用数组的一部分元素或者整个数组来创建,甚至可以创建一个比所基于的数组还要打的数组切片*/ func func1() {arra…...
![](http://www.jdzj.com/edit/UploadFile/200942151513271.jpg)
做首饰网站/移动慧生活app下载
关键词:电网络;传递函数;频域模型系统的数学模型是该系统在信号传递过程中的动态特性的数学描述。它是舍弃了各种系统具体特点而抽象出来的共同性质,从而成为研究系统的工具。建立描述控制系统运动特性的数学模型,…...
![](https://img-blog.csdnimg.cn/img_convert/af8f0abae21ad36ff5db051ce35082cd.gif)
网站建设预算计算方法/百度seo关键词优化市场
👇👇关注后回复 “进群” ,拉你进程序员交流群👇👇一年一度高考季和毕业季,填报志愿和大学生就业成为焦点问题。曾经面对清华70%~80%的高考状元都选择了金融行业,施一公呐喊:中国大学…...
![](/images/no-images.jpg)
网站租用价格/外包公司排名
寻找根节点很容易让人联想到DisjointSet,但是DisjointSet只有合并操作, 所以询问离线倒着考虑,标记会一个一个消除,这时候就变成合并了。 因为询问和查询的时间以及标记生效的时间有关,记录下查询时间,在树…...
![](https://img-blog.csdnimg.cn/img_convert/df6050bb48d3846d52e10e5e7617f1ff.png)
桂林北站疫情防控最新消息/seo排名优化表格工具
题库来源:安全生产模拟考试一点通公众号小程序 2022高压电工复审题库为高压电工模拟考试题库考前必练习题目!2022高压电工理论题库及在线模拟考试根据高压电工新版教材大纲编写。高压电工模拟考试试题随时根据安全生产模拟考试一点通上查找答案。 1、【…...
![](/images/no-images.jpg)
奥门网站建设/最新新闻摘抄
Stepper常常用于表单的提交中,比如内容较多,一页显示不了,要分几页提交时使用 https://api.flutter.dev/flutter/material/Stepper-class.html...