router-view
router-view在哪个组件下,就是谁的children,因为App.vue处于一级
所以router/index.js的路由配置从App组件开始生效,属于同一级别
的可以通过router-view依据路由配置实现不同组件的显式,如果是
下一级需要一句路由切换组件则需要配置children子级路由
关于Vue组件通信的总结我提取到另一篇Vue组件通信 小结
M V VM
M 模型 data
V 视图 模板
VM 模型视图 Vue实例 负责 M和V之间的解析与渲染
vue可以实现
1.数据绑定,数据响应式 v-model v-text ...
2.属性动态绑定 :attribute
3.样式动态绑定 :style={} 用json对象绑定
4.基本事件的绑定 @click
5.循环遍历输出数组 item in var
vue
1.采用组件化模式,提高代码复用率,且代码易于维护
2.声明式编码,无需直接操作DOM,提高开发效率
v-model只用于输入类元素:表单类元素,v-model默认选择就是value属性
箭头函数没有this
data中的数据和_data中的数据是一个东西
vm上的东西以及原型上的所有的属性和方法都可以在view上用
数据代理:
1.Vue中的数据代理通过vm对象来代理data对象中属性的操作 读/写
2.基本原理:
通过Object.defineProperty()把data对象中所有属性添加到vm上
为每一个属性指定getter,setter在getter,setter内部去操作data
中对应的属性
data{
中的数据会做数据代理和数据劫持已完成响应式的页面响应
}
键盘事件:
tab是一个特殊的键应配合keydown使用而不是keyup
CapsLock需要写成@click.caps-lock
计算属性 computed
计算属性不存在,要通过已有的属性计算出来
底层使用Object.defineProperty方法提供的getter和setter方法
get方法什么时候执行
1.第一次读取时会执行
2.当依赖的数据改变时会被再次调用
与methods相比computed是有缓存的
侦听对象
当监视的对象改变时会调用处理逻辑handler
看起来computed和watch都可以实现firstName和lastName的相加,但是
计算属性是不能实现异步的任务的,而watch可以,因为computed是靠返回值
computed和watch对比
1.computed可以完成的watch也可以完成
2.watch可以完成的computed不一定可以完成,例如watch实现异步操作
原则:
1.所被vm管理的函数,最好写成普通函数,这样this指向的是vm或者组件实例
2.所有不被vue管理的函数(定时器的回调函数,ajax的回调函数),最好写成
箭头函数,这样this指向的才是vm 或 组件实例
目标就是让this指向vm
v-for指令
1.用于展示列表数据
2.语法 v-for="(item,index) in xxx" :key="xxx"
3.可遍历:数组,对象,(字符串,指定次数)
vue的监视原理
vue的data数据会被首先加工成_data,然后通过defineProperty实现对数据的监视
当值被改了,就会调用setter,getter,然后去解析模板,生成虚拟dom...
data
数据需要先收集到_data中,然后做了数据代理,代理到vm上
数组操作
vue对数组的元素是没有对应的getter,setter的,所以需要对数组进行监控
push
pop
shift
unshift 头部插入
splice 指定位置插入
sort
reverse
使用vue对数组添加元素时,对以上7种方法做了包装,会先调用Array原型上的方法如push,
然后去解析模板
vue监视数据的原理(实现响应式)
1.vue会监视data中所有层次的数据
2.如何监视对象中的数据
1.通过setter实现监视,且要在new Vue时传入要监视的数据
对对象追加睡醒Vue默认不做响应式处理
如需要后期添加属性做响应式,请使用
Vue.set(target,property/index,value)
this.$set(target,property/index,value)
3.如何监视数组中的数据
通过包装数组常用的更新元素的方法,本质做了两件事
1.调用原生api对数组进行更新
2.重新解析模板,进行页面更新
4.在Vue中修改数组的某个元素一定要使用如下方法
push
pop
shift
unshift 头部插入
splice 指定位置插入
sort
reverse
Vue.set() this.$set()
特别注意Vue.set() this.$set()不能给vm或者vm的根对象添加属性!!!
数据劫持:
_data是一个Observer监视器,_data中的数据是vm传入的要监控的数据
当有人要修改传入的数据时Observer的setter就会劫持数据的发改变,然后重新解析模板
重新渲染(劫持到后:重新改数据 > 解析模板 > 更新页面)
如何通过v-model去收集表单中的数据
对于表单的输入数据通过v-model收集value
<input type="number" v-model.number="userInfo.age"><br>
对于checkbox,如果没有配置value则默认收集的是checked的值true or false
v-model的初始值是非数组,收集的是checked的值,如果是数组,则收集value的值
v-model的三个修饰符
.number
.trim
.lazy
input没有配置value则收集checked的值,如果配置了value如果给的是非数组,仍然收集的是checked的值
像复选框这样的本身就不需要输入,所以需要配置value
vue的过滤器是对数据进行格式或者进一步处理
1.配置全局过滤器
2.配置局部过滤器
时间格式库
"https://cdn.bootcdn.net/ajax/libs/dayjs/1.10.7/dayjs.min.js"
学过的指令
v-bind : 单向绑定解析式
v-model : 双向数据绑定
v-for : 遍历数组/对象/字符串
v-on : 绑定事件
v-if : 条件渲染(动态控制节点是否存在)
v-else : 条件渲染(动态控制节点是否存在)
v-show : 条件渲染(动态控制节点是否存在)
v-once : 在初次动态渲染后就视为静态内容
v-pre : 跳过当前所在节点的编译过程,可利用它跳过没有插值语法的节点,加快编译
v-cloak : 本质是一个特殊属性,Vue实例创建完毕后并接管容器后,会删除该属性,使用
时与css配合可以解决网速过慢时展示未经渲染的模板,如{{xx}}
自定义指令
何为mounted(),但Vue完成模板的解析并把初始的真实Dom放入页面后(挂载完毕)调用mounted
生命周期:
在beforeUpdate这个生命周期中,页面尚未与数据保持同步
创建
挂载
更新
销毁
组件:
实现应用中局部功能和资源的集合,组件树状组合
模块:是针对js来说的
组建的使用
1.创建组件
const student = Vue.extend({
template:`<div>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
</div>`,
data(){
return{
name:'sakura',
age:20
}
}
})
2.注册组件
new Vue({
el:'#app',
components:{
/*school:school,
student:student*/
school,
student
}
})
3.使用组件(组件标签)
<!--使用组件-->
<school></school>
组件的data必须写成函数式!!! 为什么? why
为了避免组件复用时,数据存在引用关系
const sc = Vue.extend({}) 可简写为 const sc = {}
其中管理了所有组件的子组件将其定义为app,root直接跟app打交道即可
Root
|
App
| |
Foo Brother
|
Son
所谓组件的本质就是 构造函数
f VueComponent(options){
this._init(options)
}
vm或者vc中出现的属性都会通过数据代理呈现出来
vm和vc重要的内置关系
VueComponent.prototype.__proto__ === Vue.prototype
可以让组件实例对象可以访问Vue原型上的属性,方法
Vue.extend返回的就是VueComponent实例
在调用Vue.extend方法后会返回VueComponent对象,当写了如 <school></school>
后会new VueComponent()实例
vc:组件实例对象
组件暴露:
1.分别暴露 export ...
2.统一暴露 export {school}
3.默认暴露 export default school 或 export default Vue.extend({})
脚手架创建项目:
项目分析:
vue inspect > output.js输入隐藏的配置文件
ref属性:
1.被用来给元素或子组件注册引用信息(id替代者)
2.在html标签上获取的是真实的DOM,在vc上获取的时组件实例
props: 让组件接收外部传进来的数据,如何接收
//props:['name','age','sex']
/*接收时对数据进行限制*/
/*props:{
name:String,
age:Number,
sex:String
}*/
//限制必要性及默认值
props:{
name:{
type:String,
required: true
},
age:{
type:Number,
default: 32
},
sex:{
type:String,
required: true
}
}
props是只读的,Vue底层会监测对props的修改,如果进行修改,就会触发警告
解决:复制一份内容到data中,然后去修改该其中的数据.
解析时会先一步将数据传入props中
mixin:混入
目的:便于复用代码
plugins:
功能是用于增强Vue,install的第一个参数是Vue,第二个插件使用者传递的参数
插件 Vue.use(plugins)
style
App的样式不要加scoped,这里写是全局可以使用的
scoped是让样式局部生效,防止冲突
npm view less-loader versions npm view webpack versions npm install less-loader@7会安装less-loader7的最新版
小结:
computed计算属性,将一些属性计算后的结果利用起来get,set
watch侦听器,computed可以做的watch都可以做,但watch可以实现异步处理deep,immediate,handler
filters:对数据进行格式化处理
directives:自定义指令
数据代理:Object.defineProperty
:key属性
表单数据收集
实时为对象添加属性Vue.set(this.student,'gender','男'),this.$set(this.student,'gender','男')
数据监听:对象的监听,数据的监听(内置的一些包装方法)
ref对节点的选择 $refs
props:方便组件接收外部的数据,以便复用
mixin:组件之间的公共部分提取
plugins:插件 Vue.use(plugins)
组件化的编码流程:
1.实现静态组件:抽取组件,使用组件实现静态页面效果
2.展示动态数据
1.数据类型,名称是什么
2.数据保存在哪个组件
3.交互--从绑定事件监听开始
兄弟组件之间如何互相传递数据
1.如果组件的模板中有用到此组件可通过"自定义标签属性"传递数据
2.App可以给任何子组件传递数据
全局事件总线
消息订阅与发布
数据在哪里操作数据的行为就在哪里
不要随意的修改props,把它当作只读的!藏在对象中的数据改变vue是监视不到的,
如果不是对象就会报错
delete是js中删除属性的操作
todoList小结
总结TodoList案例
1. 组件化编码流程:
1)拆分静态组件:组件要按照功能点拆分,命名不要与htm/元素冲突。
2)实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是-
些组件在用:
1一个组件在用:放在组件自身即可。
2)一些组件在用:放在他们共同的父组件上(状态提升)。
3)实现交互:从绑定事件开始。
2.props 适用于:
1)父组件==>子组件 通信
2)子组件 二=>父组件 通信(要求父先给子一个函数)
3. 使用v-model时要切记:v-model鄉定的值不能是props传过来的值,因为props
是不可以修改的!
4.props传过来的若是对象类型的值,修改对象中的属性时vue不会报错,但不推
荐这样做
浏览器本地存储
浏览器总local storage最大的是特点是关闭浏览器数据依然在
1. local storage手动清除才会消失
2. session storage中的数据浏览器关闭就会丢失
1和2统称为webStorage,存储内容一般为5M
Teardown 拆卸,摧毁
$destroy()
当组件调用了this.$destroy()方法,组件上的 [侦听器] [自定义事件] 都会销毁
原生的事件调用仍然有效,但是响应式丢了.因为此时组件实例已经销毁了
组件的通信(组件自定义事件)
js中的事件只能是html自带标签可以使用,而自定义组件则需要自定义事件来处理
1.方法一:<Student v-on:sakura="getStudentName"/>
2.方法二:在Student的vc实例对象绑定sakura事件
mounted() {
this.$refs.student.$on('sakura',this.getStudentName)
}
mounted()方式更加灵活
绑定的事件需要触发
this.$emit('sakura',this.name,this.sex,this.age)
this.$emit('demo')
如何解绑事件
1.this.$off(['sakura','demo'])
this.$off()
2.this.$destroy() //自杀
这两种方式都可解绑事件,组件在父容器定义,在父容器中绑定事件,事件的触发则需要子
组件去做
小结:组件之间传递数据目前有两种:
1.首先父组件需要准备一个回调函数,传递给子组件使用,可以深度传递
2.使用组件自定义事件实现"父子"组件的数据传递,由于父组件中包含子组件
在父组件里的子组件上添加自定义事件,然后由子组件去触发该事件.
有点类似 "推球" 或者说 "深度为2的二叉树"(无法完成兄弟组件数据的传输)
原则:给谁绑定事件就去找谁触发
数据来源
data
props
computed
组件是否可使用原生事件
可以
@click.native=""
全局事件总线$bus(任意组件间的通信)
const Demo = Vue.extend({}) //返回VueComponent
const d = new Demo() //创建VC实例对象
Vue.prototype.x = d //目的是让绑定在Vue上的属性具备 $on $off $emit
改进:
//在vm上创建事件总线
beforeCreate() {
Vue.prototype.$bus = this
}
//给总线上绑定事件
mounted() {
this.$bus.$on('send',(data)=>{
console.log('student接收数据',data)
})
},
//组件销毁前解除事件,这样才是比较完整事件总线编写方式
beforeDestroy() {
this.$bus.off('send')
}
//普通方法去触发事件
methods:{
sendSchName(){
this.$bus.$emit('send',this.name)
}
}
组件间传递数据的方式
1.父----子 props
2.子----父 props 需要父组件创建一个回调
3.自定义事件:定义在 上 触发在 下
4.全局事件总线:适应于 "爷孙之间"
没有大的隔代或者亲缘关系的组件间传递数据或者方法时前三种如果可以解决就不要麻烦事件总线!
另一种组件间通信(发布与订阅)
谁需要消息谁就去订阅:
订阅时机-->mouted
1.解除订阅时机-->beforeDestroy()与事件总线解除事件一样
(为什么解除:如果组件过多,$bus或pubsub上的只能过多,在组件解除时就应解除订阅)
mounted() {
//this.$bus.$on('delTodo',this.delTodo)
this.pubId = pubsub.subscribe('delId',this.delTodo)
},
//组件销毁解绑事件
beforeDestroy() {
//this.$bus.$off('delTodo')
pubsub.unsubscribe(this.pubId)
}
2.发布订阅:消息的发布者(消息来源)
handleDeleteTodo(id){
if(confirm('confirm to delete this todo')){
//this.$bus.$emit('delTodo',id) //触发事件
pubsub.publish('delId',id) //发布订阅
}
}
[注:基于事件的组件通信机制谁发送数据谁就去触发事件]
事件总线 vs 订阅/发布
两者都可实现任意组件间数据的传输,而且 订阅/发布利用了vue的库
pubsub-js充当了$bus的角色
组件通信小结
组件通信的方式:
1.最基本的组件交互/组件通信方式
props:
props可以实现组件之间的数据(属性or方法)传输,但是对于跨级的
数据传输依然需要逐层传递与接收,所以缺点就是不需要数据的组件
依然需要承担数据传输的责任,导致组件标签上绑定了太多的传值属
性这种方式也就是需要"自定义标签属性"来实现传递数据
props应该注意的地方:
a.不要随意的修改props,把它当作只读的!藏在对象中的数据改变vue
是监视不到的,如果不是对象就会报错,所以在需要修改props传递的
数据时应该借助中间值修改:如 a --> b -->修改
b.props只适合 父-->子 的数据传递方向
2.基于回调函数的组件交互
这种方式依旧需要借助props来传递数据,不过此时传递的数据是一个函数,当
这个函数传递到发送数据的组建后,发数据的组件就可以通过诸如click类的
事件使用传递过来的函数,达到传递数据的目的
3.基于事件的组件通信
这种通信依赖于"自定义组件事件",为什么要去自定义事件呢? js有内置的事件
可以使用,而组件没有,就需要去自定义(这么说也不对,其实也可以用...具体原因
我还没有去想,总归可以达到数据的交互)
实现方式:
组件上需要绑定自定义事件,然后由发送数据的组件去触发该事件
例如给Student组件绑定事件,有两种方式:
a. <Student @sakura="getStudentName"/>
b. <Student ref="student"/>
mounted() {
/*在Student的vc实例对象绑定sakura事件*/
this.$refs.student.$on('sakura',this.getStudentName)
}
这两种方式都需要去解除事件
a. this.$off(['sakura','demo'])
this.$off()
b. this.$destroy() //自杀
这两种方式都可解绑事件,组件在父容器定义,在父容器中绑定事件,
事件的触发则需要子组件去做
弊端:只能实现父子,爷孙 ... 之间的数据交互,不能解决兄弟间的交互
4.全局事件总线$bus
$bus只是起了方便的名字而已,以做区别.
它解决了兄弟组件间的数据交互,全局事件总线是解决第三方的组件来实现交互的,好像
两个关在房间里的人,他们想说话,但是对方都听不见,所以就借助一个具有一定作用的第
三者来实现彼此间的通信.
那么这种通信基于: VueComponent.prototype.__proto__ === Vue.prototype
这一条内置关系来实现的,这条关系让所有vm的子组件,子孙组件都可以看到vm里的东西
所以把vm作为第三者来传递数据再好不过了,因为子孙组件都可以看见(使用)
现在给Vue.prototype上绑定一个属性,让所有组件都可以看见
实现有两种:
1. 如:绑定一个 x
Vue.prototype.x = {a:'1',c:'hello'}
这样子组件可以看到,但是如何在一个属性 x 上去绑定事件并且,其它组件可以
触发呢? 显然是办不到的
那么这个 x应该具备什么样的功能才能承担起,这个第三个?
1.可见性 可用性
2.具备 $on 用于事件绑定
3.具备 $off 用于解绑事件
4.具备 $emit 用于事件触发
基于以上几点,目标就是VueComponent,Vue者两个大哥了
VueComponent要通过Vue.extend({})去创建,然后new出来才是一个
VueComponent实例对象(等同去写一个<Student/>组件时才会去new出
VueComponent实例),然后绑定到Vue.prototype上就可以实现那个第三
者,但是这样写不够优雅
2.在Vue的原型上绑定vm
new Vue({
/*将App挂载到容器上*/
render:h=>h(App),
beforeCreate() {
Vue.prototype.$bus = this //绑定事件总线$bus
}
}).$mount('#app')
5.消息订阅与发布
上面有提到过
注:基于事件的组件交互,谁要发数据谁就去触发事件并携带数据(需要的话)
6.slot prop(插槽prop)
在slot标签上绑定属性传递数据给数据的使用者
数据的使用者可使用scope=""或slot-scope=""或v-slot=""接收数据使用
接受的数据可以起别名,然后 . 去使用,也可以通过解构赋值接收
$nextTick 下一次DOM更新执行其回调
$nextTick指定的回调会在所有dom更新之后再执行
this.$nextTick(()=>this.$refs.inputFocus.focus())
如果使用this.$refs.inputFocus.focus()代码执行过后DOM还没有
出现在页面上,那么就不会有获取焦点的效果了
什么时候使用:当改变数据后,要基于更新后的DOM进行某些操作时,要在nextTick所指定
的回调中执行
跨域
跨域问题
服务器之间打交道不用ajax,ajax是前端技术,你有浏览器才有window,xhr,fetch...
服务器之间使用http请求
实现:使用Nginx,也可使用vue/cli来配置
devServer: {
proxy: 'http://localhost:5000'
}
缺点:这种方式不够灵活,只能配置一个要访问的服务器,而且优先匹配前端资源
vue.config.js的代理配置解析
module.exports = {
pages: {
index: {
// page 的入口
entry: 'src/main.js'
}
},
lintOnSave: false,
devServer: {
proxy: {
'/api': { // '/api'为请求前缀,在端口后写
target: '<url>', //把请求前缀为 /api的转发到target服务器
pathRewrite: {'^/atguigu':''} //正则匹配atguigu开头的替换空
ws: true, // 用于支持 web socket
changeOrigin: true //用于控制请求来源的host的值
},
'/foo': {
target: '<other_url>'
}
}
}
}
其中:这两个配置不写时默认为true
ws: true, // 用于支持 web socket
changeOrigin: true //用于控制请求来源的host的值
优点:可配置多个要访问的服务器,可以灵活的控制请求是否走代理
缺点:请求资源时必须加前缀
github案例使用的服务端接口
服务器地址:https://api.github.com/search/users?q=xxx
vue-resource
这是vue的一个插件库
import vueResource from 'vue-resource' //使用fetch
Vue.use(vueResource)
发送异步请求的方式
1.xhr: new XMLHttpRequest() xhr.open() xhr.send()
2.jQuery: $.get() $.post()
3.axios: 基于Promise
4.fetch: fetch是与xhr平级的,fetch是js内置的,兼容性不太好
5.vue-resource Vue初期使用的库
2,3都是对xhr的封装,即使没有xhr也可以发送请求
跨域: 协议:主机:端口任何不同都是跨域
slot插槽
slot作用:让父组件可以向子组件指定位置插入html结构,也是一种组件的通信方式
<Category title="games">
<template scope="prs">
<ul>
<li v-for="(g,index) in prs.games" :key="index">{{g}}</li>
</ul>
<h3>{{prs.msg}}</h3>
</template>
</Category>
<Category title="games">
<template scope="{games,msg}">
<ul>
<li v-for="(g,index) in games" :key="index">{{g}}</li>
</ul>
<h3>{{msg}}</h3>
</template>
</Category>
案例视频资源: http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4
slot分为三种:
1.默认插槽: <slot></slot>
2.具名插槽: 具有name属性的插槽,v-slot:footer v-slot只可使用到template标签上
3.作用域插槽: 作用域插槽能够解决数据不在自身的情况,需要使用"插槽prop"传递数据到
数据的使用者,数据的使用者必须使用template标签包裹
可定义scope="data-name"接收
支持结构赋值,scope="{games}",同名的会接收
slot-scope=""与scope和v-slot一样
但是v-slot可以指定插槽
如v-slot:header="接收的数据"
如v-slot:footer="{结构赋值}"
Promise
Promise,简单的说它就是一个容器,里面保存了某个未来要执行的操作,通常是一个
异步的操作,如Ajax,SetTimeout ...
ES6规定,Promise对象是一个构造函数,用于生成Promise实例
const p = Promise((resolve, reject) => {
if(/*异步请求成功*/){
resolve(value)
}else{
reject(error)
}
})
Promise的then方法可以接受两个回调函数,第一个参数是Promise对象正确调用
resolve函数时调用(res=>{}),另一个是发生错误时执行的reject函数调用时调用(error=>{})
Promise的基本方法
all() Promise.all([one,two,three]).then(()=>{})
race() Promise.race([one,two,three]).then(()=>{})
catch()
asyn/await