Vue组件化编程
组件:实现应用中局部功能代码和资源的集合
模块:对外提供特定功能的js程序,一般是一个js文件
模块化:应用中JS都是以模块来编写,这就一个模块化应用
组件化:应用中的功能都是多组件的方式编写,这就是一个组件化应用
组件命名规范
-
单个单词的名字
(1). 纯小写,注册和使用时都用小写
(2). 注册和使用时首字母大写,vue渲染时本身就是大写开头,可以与之呼应。 -
多个单词组成的名字
(1). 全部小写,单词之间短横线连接
(2). 脚手架环境下,注册和使用时每个单词首字母大写 -
创建组件时配置的组件名字
name
仅仅在开发工具中呈现。
非单文件组件
- 创建组件
Vue.extend({})
// 创建组件
const student = Vue.extend({
name: 'student'
template:` <div> <h1> {{ name }} </h1> </div>`,
data(){
return {
name:'',
}
},
methods: {
copyName(){},
},
});
# 简写形式 不需要写Vue.extend 内部源码进行了判断
const student = {
name: 'student'
template:` <div> <h1> {{ name }} </h1> </div>`,
data(){
return {
name:'',
}
},
methods: {
copyName(){},
},
};
- 注册组件
全局注册于局部注册
// 全局注册
Vue.component('student',student); # 第一个参数是组件名,第二个参数是组件
// 局部注册
new Vue({
data() {
return {
time: 1
};
},
// 局部注册组件
components:{ student }
});
- 使用组件
使用组件时,采用自闭合写法需要再脚手架环境下使用。<student/>
<div id="root">
<student> </student>
</div>
// cli环境下
<template>
<div>
<student />
</div>
</template>
单文件组件
VueComponent
VueComponent是一个构造函数
-
组件的本质是一个名为VueComponent的构造函数,是
Vue.extend()
生成的。 -
在使用组件时,Vue解析时会去创建组件的
实例对象
,Vue执行了new VueComponent(option)
-
每次执行
Vue.extend
时都会返回一个全新的
VueComponent -
在注册组件并使用组件标签后,
才会创建
VueComponent实例对象
// 模拟 Vue.extend 生成VueComponent
Vue.extend = function(extendOptions) {
/*..........*/
var sub = function VueComponent(options){
this._init(options);
}
return sub;
/*..........*/
}
-
this
指向问题。
(1)在组件配置中
,data函数,methods中的函数、watch中的函数、computed中的函数,他们的this指向均是【VueComponent实例对象】
(2)在new Vue(options)配置中
,data函数,methods中的函数、watch中的函数、computed中的函数,他们的this指向均是【Vue实例对象】
- VueComponent实例对象称之为
vc
或组件实例对象
;Vue实例对象称之为vm
。vm和vc都有数据代理和数据监视。vc配置项不可传el
,data
必须是一个函数
;vm配置项可以传el,data可以是函数,也可以是对象。 -
VueComponent.prototype.__proto__
与Vue.prototype
指向相同。存在VueComponent.prototype.__proto__ === Vue.prototype
的关系。组件实例对象vc可以访问到Vue原型上的属性和方法。
image.png
单文件配置
对外暴露的是Vue.extend
的配置对象,这里没有写Vue.extend
,Vue会帮助我们使用Vue.extend
来创建VueComponenet实例对象
export default {
data(){
return {};
},
methods:{
}
}
Vue CLI
CLI: command line interface
全局安装@vue/cli:npm install -g @vue/cli
创建项目:vue create xxx
启动项目:vue run serve
// index.html
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<!-- 让IE浏览器以最高的渲染 级别渲染页面 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- 开启移动端的理想视口 -->
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<!-- 配置页签图标 BASE_URL代表public目录 -->
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<!-- 配置网页标题 值是获取package.json中的name -->
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<!-- 当浏览器不支持js时,该标签的内容就会渲染 -->
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly...</strong>
</noscript>
<!-- 容器 -->
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
render函数
完整版vue是由vue核心
和模板解析器
组成。模板解析器所占体积占用vue三分之一。如果使用完整版vue,webpack在打包时也会将模板解析器打包,由于webpack打包时会做翻译.vue
解析模板的工作,所以模板解析器就没有存在的必要,于是就产生了没有模板解析器的精简版Vue。
new Vue({
router,
store,
render: (h) => h(App),
}).$mount('#app');
-
render的本质是一个函数,render函数的参数是createElement,createElement也是一个函数。
import Vue from 'vue'
引入的是ES6模块化版本dist/vue.runtime.esm.js
,这个版本的没有模板解析器
,带有runtime的版本都没有模板解析器。在new Vue(options)
时,配置对象template选项,就无法解析。 -
render的作用是为了弥补不完整版本vue.js的功能:模板解析。
引入了运行时版Vue,在new Vue(options)
时,需要使用render函数
。 -
vue-template-compiler
插件 用于解析组件中的template
。 -
vue.js与vue.runtime.xx.js的区别:
(1). vue.js是完整版的Vue,包含Vue核心和模板解析器
(2). vue.runtime.xx.js是运行版的Vue,只包含Vue核心,不包含模板解析器 -
为什么要使用render函数?
因为vue.runtime.esm.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容。
配置文件
vue.config.js是可选配置文件,使用了commonJS暴露。Vue会将该文件输送给webpack,与webpack中的配置进行合并。webpack是基于node的,nodeJS采用的模块化是commonJS。
ref属性
- 作用:用于给DOM元素或者子组件注册引用信息(id选择器的替代者)
- 使用
- 用在HTML元素上,获取到的是
真实的DOM元素
<h1 ref="title">{{ msg }}</h1>
获取真实DOM元素
const dom = this.$refs.title
- 用在组件标签上,获取到的是组件实例对象
<School ref="sch"></School>
获取组件实例对象
const dom = this.$refs.sch
- 用在HTML元素上,获取到的是
props配置项
- 作用:让组件介绍外部传来的参数
- 三种配置方式
// 1. 简单接收 数组形式,无法指定数据类型
props: ['name','age'],
// 2. 仅限制数据类型
props: {
name:String,
age:Number,
},
// 3. 指定多种类型
props: {
type: [String, Number],
}
// 4. 完整配置 require与default不必同时配置
props: {
name: {
type:String,
require:true,
},
age: {
type:Number,
default:60,
}
},
props可以接收函数参数
- 传参
<Student name="zs" :age="18"/>
- props中配置的参数是只读的,
不能修改
。
data中声明的数据名不可与props中的参数名相同。
要修改props传过来的值,需要借助data。
data(){
return {
myName:this.name, // props的数据优先于data,所以可以这样使用
}
},
props:[ 'name' ]
修改myName即可,但是不会影响name的值。
mixins配置项
- 作用:可多个组件共用的配置提取成一个混入对象
- 使用方式:
- 定义混合
// mixin.js export const mixin = { data(){...}, methods:{...}, mounted(){}, }
- 使用混合
全局混合:Vue.mixin(mixin);
局部混合:mixins:[mixin]
插件
插件是一个必须含义install函数的对象
- 功能:用于增强Vue
- 本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后得参数就是插件使用者传递的数据。
- 定义插件
// plugins.js
export default {
install(Vue){
Vue.filter();// 添加过滤器
Vue.directive();// 添加全局指令
Vue.mixin();// 配置全局混入
Vue.prototype.hello = ()=>{} // 添加实例方法
}
}
- 使用插件
import plugins from './plugins'
Vue.use(plugins);
组件的自定义事件
-
子给父传递数据
通过父给子传递函数类型的props实现
通过父给子绑定自定义事件实现 -
绑定自定义事件
- v-bind或@ 方式
<Student @name="getName"/>
<Student @name.once="getName"/>
触发一次事件 - ref 方式
<Student ref="student"/>
this.$refs.student.$on('name',getName)
this.$refs.student.$once('name',getName)
触发一次事件
- v-bind或@ 方式
-
触发事件
this.$emit('name',this.name)
可以传递多个参数 -
解绑事件
this.$off('name')
解绑一个事件
this.$off(['name','test'])
解绑多个事件 参数是数组类型
this.$off()
解绑所有的自定义事件 -
总结
- 组件的自定义事件是用于子组件给父组件传递数据
- 给谁绑定事件,就找谁触发事件
- 组件上也可以绑定DOM原生事件,需要使用
native
修饰符 -
this.$refs.student.$on('name',callback)
,这种方式绑定事件时,callback要么配置在methods中,要么使用箭头函数,否则callback中使用了this,指向会出问题。
全局事件总线
- 作用:实现任意组件之间通信
- 原理:给Vue的原型对象增加一个对象,一个具有
$on、$off、$emit
方法的对象,VueComponent和Vue的实例对象都可以访问到Vue的原型对象,这样在组件就可以使用自定义事件进行跨组件通信。 - 实现:
Vue.prototype.$bus = this;
new Vue({
render: h => h(App),
beforeCreate() {
Vue.prototype.$bus = this;
},
}).$mount('#app')
- 使用:
this.$bus.$on('xxx')
、this.$bus.$off('xxx')
、this.$bus.$emit('xxx')
在组件的beforeDestory
钩子函数中解绑当前组件注册的总线事件
消息订阅与发布
消息订阅与发布库:pubsub-js
- 作用:实现任意组件之间通信
- 订阅消息:
pubsub.subscribe('xxx',(msgName,params)=>{})
- 发布消息:
pubsub.publish('xxx',params)
- 取消订阅:
pubsub.unsubscribe(pubId)
this.$nextTick()
this.$nextTick()
中的回调在下一次DOM更新完毕之后再执行
Vue控制动画和过渡
<translation name="hello" :appear="true"></translation>
<translation/>
标签不是真实的DOM元素,只能控制单个DOM元素
<translation-group/>
可以控制多个DOM元素
,但是必须给元素设置key属性
-
动画
定义css动画,vue通过v-enter-active
等和translate标签
来实现
<template>
<div>
<button @click="show = !show">显示/隐藏</button>
<!-- name 决定class的名称前缀; appear 决定第一次显示是否执行动画 -->
<transition name="move" :appear="true">
<h1 v-show="show">渚清与沙白</h1>
</transition>
</div>
</template>
<script>
export default {
name: "Test",
data() {
return {
show: true,
};
},
};
</script>
<style scoped>
h1 {
background-color: coral;
}
/** 进入时激活的动画 */
/** 不设置name="move",这里使用 v-enter-active */
.move-enter-active {
animation: move 1s linear;
}
/** 离开时激活的动画 */
.move-leave-active {
animation: move 1s linear reverse;
}
@keyframes move {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0);
}
}
</style>
-
过渡
不需要配置动画(@keyframes
)
v-enter
v-enter-to
v-leave
v-leave-to
<template>
<div>
<button @click="show = !show">显示/隐藏</button>
<!-- name 决定class的名称前缀 appear 决定第一次显示是否执行动画 -->
<transition name="move" :appear="true">
<h1 v-show="show">渚清与沙白</h1>
</transition>
</div>
</template>
<script>
export default {
name: "Test",
data() {
return {
show: true,
};
},
};
</script>
<style scoped>
h1 {
background-color: cornflowerblue;
}
/** 进入的起点、离开的终点 */
.move-enter, .move-leave-to {
opacity: 0;
transform: translateX(-100%);
}
.move-enter-active, .move-leave-active {
transition: all 0.5s linear;
}
/** 进入的终点、离开的起点 */
.move-enter-to, .move-leave{
opacity: 1;
transform: translateX(0);
}
</style>
- animation.css动画库
- 引入
import 'animation.css'
- 配置库
<transition
name="animate__animated animate_bounce"
:appear="true"
enter-active-class="animate__fadeInUp"
leave-active-class="animate__fadeOutDown"
>
<h1 v-show="show">渚清与沙白</h1>
</transition>
配置代理
发送Ajax请求有哪些方式?
- XMLHttpRequest
- jQuery
- axios
- fetch
jQuery和axios是对XMLHttpRequest的封装。XMLHttpRequest是js内置的对象。fetch和XMLHttpRequest平级,浏览器window
对象上就有XMLHttpRequest
和fetch
。
fetch:双层Promise、不支持IE
**** CORS **** Access-Control-Allow-Origin ****
跨域,违背同源策略。同源策略规定协议名
、主机名
、端口号
必须一致。
跨域是客户端请求发送成功,服务器也收到数据,并且还返回了数据,浏览器发现跨域,将数据留下来了。
- 解决跨域
- 后端解决。服务器响应头配置特定参数,但是不安全。
- jsonp 只能解决get请求的跨域问题,其他请求解决不了
- 前端配置代理服务器
代理服务器端口跟前端服务器所处位置一样,端口一样。在发起请求时,先找代理服务器,代理服务器转发给服务器,服务器将响应数据发送给代理服务器,代理服务器再转发给前端服务器。当请求资源前端服务器本身就有,不会走代理服务器。
服务器与服务器之间通信不用Ajax,ajax是前端与服务器的通信工具。
开启代理服务器
- nginx
- 借助Vue CLI 脚手架
- 脚手架配置代理
- 方式一
在vue.config.js
脚本中配置
devServer: {
proxy: 'http://localhost:5000',// 接口服务器的地址
},
优点:配置简单,请求资源时直接发送给前端(8080)即可
缺点:不能配置多个代理,不能灵活控制是否走代理
工作方式:当请求了前端不存在的资源时,该请求会转发给服务器。(优先匹配前端资源)
- 方式二
在vue.config.js脚本中配置
// /api: 请求前缀 方式二
// 收到请求时发现有前缀才会转发到接口服务器 否则不做转发
devServer:{
proxy: {
https: false,
'/api': {//控制是否走代理
target: 'http://localhost:5000',
changeOrigin: true,// 用于控制请求头的host值
ws: true,// 用于支持websocket
pathRewrite: {// 重写地址
'^/api': ''// 正则匹配,匹配所有以/api开头的请求路径
}
},
'/demo':{// 配置多个代理
target: 'http://localhost:5001',
changeOrigin: true,// 用于控制请求头的host值
pathRewrite: {// 重写地址
'^/demo': ''// 正则匹配,匹配所有以/demo开头的请求路径
}
}
}
}
// 请求 http://localhost:5000/api/user/login 无需配置pathRewrite
// 请求 http://localhost:5000/user/login 需配置pathRewrite
changeOrigin: true
:服务器收到的请求中请求头的host是http://localhost:5000/
,与服务器端口一致
changeOrigin: false
:服务器收到的请求中请求头的host是http://localhost:8080/
,与前端端口一致
优点:可以配置多个代理,可以灵活控制请求是否走代理
缺点:配置繁琐,请求接口时必须加前缀
-
vue-resource
vue1.0阶段使用的请求库
vue中的插件库,vue-resource也是对XmlHttpRequest的封装,与axios的用法一致。
this.$http.get()
插槽
- 默认插槽
<slot></slot>
<slot> 这是一个默认插槽 </slot>
- 具名插槽
<slot name="top"></slot>
设置名字
<ul slot="top"></li>
使用,将ul放入top插槽
<template #footer> </template>
// 定义插槽 插槽名是title,默认值是title变量值
<slot name="title">{{title}}</slot>
// 使用组件 传递title
<el-collapse-item title="一致性 Consistency" name="1">
<div>与现实生活一致:与现实生活的流程、逻辑保持一致,遵循用户习惯的语言和概念;</div>
</el-collapse-item>
// 使用插槽 替换title
<template slot="title">
<div>与现实生活一致:与现实生活的流程、逻辑保持一致,遵循用户习惯的语言和概念;</div>
</template>
2.6.0 新增
跟 v-on 和 v-bind 一样,v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符#
。例如 v-slot:header 可以被重写为 #header
:
定义具名插槽
<template>
<div>
<p> 我是子组件 </p>
<solt></solt>
<solt name='top'></solt>
</div>
</template>
引用插槽v-solt:
// v-slot:top写法只能在template标签中使用
<template v-slot:top>
<h1>1</h1>
<h1>2</h1>
<h1>3</h1>
</template>
引用插槽v-solt:header
的简写形式#header
// 简写
<template>
<h1>1</h1>
<template #top>
<h1>2</h1>
</template>
</template>
- 作用域插槽
数据在定义插槽的组件(子组件)中,根据数据生成不同的DOM结果需要组件的使用者来决定(父组件),
// 父组件
<category>
<template slot-scope="scopeData">// slot-scope=""和scope=“”均可,目前已过时!
<h1>{{scopeData}}</h1>
</template>
</category>
// v-slot
<category>
<template v-slot="scopeData">
<h1>{{scopeData}}</h1>
</template>
</category>
// 子组件
<template>
<div>
<slot :datas="list"></slot>
</div>
</template>
在 2.6.0 中,我们为具名插槽和作用域插槽引入了一个新的统一的语法 (即v-slot
指令)。它取代了 slot 和 slot-scope。
- 动态插槽名
<base-layout>
<template v-slot:[dynamicSlotName]>
...
</template>
</base-layout>
组件间通信
- props
- 自定义事件
- 全局事件总线
- 消息订阅与发布
- 插槽
- vuex