Vue模板
- vue管理的容器中的内容,里面的代码符合html规范,混入了特殊的Vue语法
- 内容可以是data中的属性、computed属性或者js表达式
js表达式 和 js代码(语句)
1.表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方:
(1). a
(2). a+b
(3). demo(1)
(4). x === y ? 'a' : 'b'
2.js代码(语句)
(1). if(){}
(2). for(){}
数据绑定
- v-bind:单向绑定,数据从vue实例中流向页面
- v-modle:双向绑定,页面和实例的数据相互影响
-
- 双向绑定可以应用在表单元素和组件上
- v-modle默认获取的是value的值
- v-modle修饰符
- .lazy:失去焦点再收集数据
- .number:输入字符串转为有效的数字
- .trim:输入首尾空格过滤
<!-- v-model收集的是value值,用户输入的就是value值 -->
<input type="text"/>
<!-- v-model收集的是value值,且要给标签配置value值 -->
<input type="radio"/>
<!-- 没有配置input的value属性,收集的是checked值 是布尔值
配置input的value属性:
v-model的初始值是非数组,收集的还是checked
v-model的初始值是数组,收集的的就是value组成的数组
-->
<input type="checkbox"/>
事件绑定
- methods中配置的函数,如果使用箭头函数里面的this将不会指向vue实例或者组件实例
-
事件修饰符
- .prevent:阻止默认事件
- .stop:阻止事件冒泡
- .once:事件只触发一次
- .capture:使用事件的捕获模式
-
- 事件是先捕获再冒泡,使用该修饰符则先触发事件
- self:只有event.target是当前操作的元素时才触发
- passive:事件的默认行为立即执行,无需等待事件回调执行完毕
- 有些事件是先执行回调函数里面的方法再执行行为
- 如@wheel(滚轮事件);表现为滚动滚轮,滚动条位置先不变化,等待方法执行完毕之后滚动条再动
- 加上passive滚动条会立即滚动条,事件慢慢执行不影响
<style>
.list{
width: 200px;
height: 200px;
overflow: auto;
}
</style>
<!-- 使用事件的捕获模式 点击box2的时候先执行box1上的事件-->
<div class="box1" @click.capture="showMsg(1)">
div1
<div class="box2" @click="showMsg(2)">
div2
</div>
</div>
<!-- 事件的默认行为立即执行,无需等待事件回调执行完毕 -->
<!-- 使用@scroll就不会有这个问题 -->
<ul @wheel.passive="demo" class="list">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
Vue.config.keyCodes.huiche = 13 //定义了一个别名按键
new Vue({
el:'#root',
methods:{
showMsg(msg){
console.log(msg)
},
demo(){
for (let i = 0; i < 100000; i++) {
console.log('#')
}
console.log('这个方法执行了好久好久')
}
}
})
</script>
-
键盘事件
- 可以使用@keydown和@keyup绑定
- 键盘按键都有一个名字和一个编码
- 获取键名:event.key
- 获取键码:event.keyCode
- Vue中常用的按键别名:
- 回车 => enter
- 删除 => delete (捕获'Delete'和'Backspace'键)
- 退出 => esc
- 空格 => space
- 换行 => tab
-
- 必须使用@keydown.tab不能使用keyup
- 上 => up
- 下 => down
- 左 => left
- 右 => right
- Vue未提供别名的按键,可以使用按键的key值或者keyCode去绑定
- 特殊的按键修饰符:ctrl、alt、shift、meta
- 配合keyup使用时:
- 按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发
- 配合keydown使用时:正常触发事件
- Vue.config.keyCodes可以定制按键别名
- 配合keyup使用时:
<input type="text"
placeholder="按切换大小写提示"
@keydown.20="showInfo(20)">
<!-- 等同于下面的写法 注意两个单词的名字需要使用短横线命名 -->
<input type="text"
placeholder="按切换大小写提示"
@keydown.caps-lock="showInfo('caps-lock')">
<input type="text"
placeholder="按切换大小写提示"
@keydown.huiche="showInfo('huiche')">
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
Vue.config.keyCodes.huiche = 13 //定义了一个别名按键
new Vue({
el:'#root',
methods:{
showInfo(msg){
console.log(msg)
}
}
})
</script>
event.keyCode不推荐使用
计算属性
- 不能和data中的属性重复
- 属性本身不存在,要通过已有属性计算得来
- 有缓存机制,效率更高
- 计算属性要被修改,必须写set函数去响应修改
<div id="root">
姓:<input type="text" v-model="firstName"> <br/><br/>
名:<input type="text" v-model="lastName"> <br/><br/>
全名:<span>{{fullName}}</span> <br/><br/>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三'
},
computed:{
fullName:{
// 当有人读取fullName时,get就会被调用,且返回值就作为fullName的值
//初次读取fullName时和fullName所依赖的数据发生变化时会被调用
get(){
console.log('get被调用了')
// console.log(this) //此处的this是vm
return this.firstName + '-' + this.lastName
},
//set什么时候调用? 当fullName被修改时。
set(value){
console.log('set',value)
}
}
//简写,简写相当于执行的是get方法
fullName(){
console.log('get被调用了')
return this.firstName + '-' + this.lastName
}
}
})
</script>
侦听属性
- 被watch的属性变化时,回调函数自动执行
- Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以
<div id="root">
<h2>{{obj}}</h2>
<button @click="changeObj">切换</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
obj:{
a:1,
b:1,
c:{
d:{
e:100
}
}
},
number:10
},
watch:{
//监视多级结构中某个属性的变化
'obj.a':{
handler(){
console.log('a被改变了')
}
},
obj:{
immediate:true, //初始化时让handler调用一下
deep:true, //监视多级结构中所有属性的变化
handler(){
console.log('numbers改变了')
}
},
// 简写
number(newValue,oldValue){
console.log('number被修改了',newValue,oldValue,this)
}
},
methods:{
changeObj(){
this.number = 20;
this.obj.d = 50;
}
}
})
</script>
computed和watch之间的区别
- computed能完成的功能,watch都可以完成
- watch能完成的功能,computed不一定能完成
- watch可以进行异步操作,computed不可以
绑定class
- :class="xxx" xxx可以是字符串、对象、数组
<!-- 动态获取一个字符串作为类名 -->
<div class="basic" :class="mood">{{name}}</div>
<!-- 动态获取多个字符串作为类名 -->
<div :class="[mood,'fd-basic',mood1]">{{name}}</div>
<!-- 动态获取多个字符串作为类名 -->
<div :class="[mood,'fd-basic',{mood1:active}]">{{name}}</div>
<!-- 三元表达式 -->
<div :class="[active ? 'normal' : 'normal1']">{{name}}</div>
<script type="text/javascript">
const vm = new Vue({
el:'#root',
data:{
name:'这样绑定class',
mood:'normal',
mood1:'normal1',
active:false
}
})
v-if和v-else
- v-if
- 切换频率较低的场景 - 不展示的DOM元素直接被移除 - v-if可以和:v-else-if、v-else一起使用,但中间结构不能被“打断”
- v-show
- 切换频率较高的场景
- 不展示的DOM元素未被移除,仅仅是使用样式隐藏掉(display:none)
v-for
- v-for="(item,index) in xxx"
遍历数组
- item为数组的每一项,index为数组的下标
遍历对象
- item为对象的value值,index为对象的key值
v-for为什么要使用key
- key是虚拟DOM对象的标识,数据变化时,Vue会新生成虚拟DOM和旧的虚拟DOM进行比较
- 旧的虚拟DOM找到了与新的虚拟DOM相同的key
- 若虚拟DOM中内容没变, 直接使用之前的真实DOM
- 若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM
- 旧虚拟DOM中未找到与新虚拟DOM相同的key
- 创建新的真实DOM,随后渲染到到页面
- 旧的虚拟DOM找到了与新的虚拟DOM相同的key
- 使用index作为key会引发的问题
- 若对数据进行:逆序添加、逆序删除等破坏顺序操作
- 会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低
- 如果结构中还包含输入类的DOM
- DOM会更新错误
- 如果不存在对数据的逆序添加、逆序删除等破坏数据顺序的操作,可以使用index作为key
- 若对数据进行:逆序添加、逆序删除等破坏顺序操作
<div id="root">
<!-- 遍历数组 -->
<button @click.once="add">添加一个老刘</button>
<ul>
<li v-for="(p,index) of persons" :key="index">
{{p.name}}-{{p.age}}
<input type="text">
</li>
</ul>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
persons:[
{id:'001',name:'张三',age:18},
{id:'002',name:'李四',age:19},
{id:'003',name:'王五',age:20}
]
},
methods: {
add(){
const p = {id:'004',name:'老刘',age:40}
this.persons.unshift(p)
}
},
})
</script>
数据双向绑定
- vue会对data中所有层次的数据做响应式处理
- 响应式处理对象中的数据
- 对象中后面追加的属性,vue默认不做响应式处理
- 需要使用$set给后面添加的属性做响应式
- 响应式处理数组中的数据
- 调用数组原生对应的方法对数组进行更新,重新解析模板,进而更新页面
- 使用push()、pop()、shift()、unshift()、splice()、sort()、reverse()可以实现数组的响应式处理
- $set不可以给vue实例或者vue实例的根对象添加属性
Object.defineProperty
- 该方法会在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象
- 数据代理:通过一个对象代理对另一个对象中属性的读写
- Vue中的数据代理:
- 通过vue实例对象来代理data对象中属性的操作(读/写)
- 通过Object.defineProperty()把data对象中所有属性添加到vm上,为每一个添加到vm上的属性,都指定一个getter/setter;在getter/setter内部去操作(读/写)data中对应的属性
let person = {
name:'张三',
sex:'男',
}
Object.defineProperty(person,'age',{
// value:18,
// enumerable:true, //控制属性是否可以枚举,默认值是false
// writable:true, //控制属性是否可以被修改,默认值是false
// configurable:true //控制属性是否可以被删除,默认值是false
//当有人读取person的age属性时,get函数(getter)就会被调用
//返回值就是age的值
get(){
console.log('有人读取age属性了')
return 18
},
//当有人修改person的age属性时,set函数(setter)就会被调用
//收到修改的具体值
set(value){
console.log('有人修改了age属性,且值是',value)
}
})
<!-- 模拟数据的双向绑定 -->
<script type="text/javascript" >
let data = {
name:'yangf',
sex:'女',
}
//创建一个监视的实例对象,用于监视data中属性的变化
const obs = new Observer(data)
console.log(obs)
//准备一个vm实例对象
let vm = {}
vm._data = data = obs
// 观察者模式
function Observer(obj){
//汇总对象中所有的属性形成一个数组
const keys = Object.keys(obj)
//遍历
keys.forEach((k)=>{
Object.defineProperty(this,k,{
get(){
return obj[k]
},
set(val){
console.log(`${k}被改了,我要去解析模板,生成虚拟DOM.....`)
obj[k] = val
}
})
})
}
</script>
过滤器
- 对要显示的数据进行特定格式化后再显示
- {{ xxx | 过滤器名}} 或 v-bind:属性 = "xxx | 过滤器名"
- 过滤器可以接收额外参数、多个过滤器也可以串联
- 并没有改变原来的数据,是产生新的对应数据
<div id="root">
<!-- 过滤器实现(传参) -->
<h3>现在是:{{time | timeFormater('YYYY_MM_DD') | mySlice}}</h3>
<h3 :x="msg | mySlice">name</h3>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
time:1621561377603, //时间戳
msg:'hello world'
},
//局部过滤器
filters:{
// 为str添加一个默认值
// dayjs是一个外部插件
timeFormater(value,str='YYYY年MM月DD日 HH:mm:ss'){
return dayjs(value).format(str)
},
mySlice(value){
return value.slice(0,4)
}
}
})
</script>
指令
- v-text
- 向其所在的节点渲染文本内容
- 会替换掉节点中的所有内容
- v-html
- 向指定节点中渲染包含html结构的内容
- 会替换掉节点中所有的内容
- 在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击
- 一定要在可信的内容上使用v-html,永不要用在用户提交的内容上使用
- 向指定节点中渲染包含html结构的内容
- v-clock
- 一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性
- 使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题
[v-cloak]{
display:none;
}
- v-once
- 所在节点在初次动态渲染后,就视为静态内容了
- 以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能
- v-pre
- 跳过其所在节点的编译过程
- 可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译
自定义指令
- 配置对象中的回调
- .bind:指令与元素成功绑定时调用
- .inserted:指令所在元素被插入页面时调用
- .update:指令所在模板结构被重新解析时调用
- 回调中的参数
- el:指令所绑定的元素,可以用来直接操作 DOM
- binding:一个对象
- name:指令名,不包括 v- 前缀
- value:指令的绑定值
- oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用
- expression:字符串形式的指令表达式
- arg:传给指令的参数
- modifiers:一个包含修饰符的对象
- vnode:Vue 编译生成的虚拟节点
- oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用
- 定义时不加v-,使用时才加
- 指令名如果时多个单词,使用kebab-case命名
<div id="root">
<h2>当前的n值是:<span v-text="n"></span> </h2>
<h2>放大10倍后的n值是:<span v-big-number:[direction].a.b="n"></span> </h2>
<button @click="n++">点我n+1</button>
<hr/>
<input type="text" v-fbind:value="n">
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
n:1,
direction:'left'
},
directives:{
//big-number函数何时会被调用?
// 指令与元素成功绑定时(一上来)
// 指令所在的模板被重新解析时
// 简写的话没有inserted回调,默认执行bind和update
// binding.value为n
// binding.expression为n
// binding.arg为left
// binding.modifiers为{a:true,b:ture}
'big-number'(element,binding){
element.innerText = binding.value * 10
},
fbind:{
//指令与元素成功绑定时(一上来)
bind(element,binding){
element.value = binding.value
},
//指令所在元素被插入页面时
inserted(element,binding){
element.focus()
},
//指令所在的模板被重新解析时
update(element,binding){
element.value = binding.value
}
}
}
})
//全局自定义指令
Vue.directive('fbind',{
//指令与元素成功绑定时(一上来)
bind(element,binding){
element.value = binding.value
},
//指令所在元素被插入页面时
inserted(element,binding){
element.focus()
},
//指令所在的模板被重新解析时
update(element,binding){
element.value = binding.value
}
})
</script>
生命周期
new Vue():初始化vue实例的生命周期和事件
- beforeCreate
- 在这个阶段无法访问vue实例中的data数据和methods方法
初始化数据监测和数据代理
- created
- 在这个阶段可以访问vue实例中的data数据和methods方法
开始解析模板,生成虚拟DOM,页面还不能显示解析好的内容
- beforeMount
- 页面呈现未经Vue编译的DOM结构
- 对DOM的所有操作最终都不奏效(后面会被解析好的DOM所替换)
将解析好的虚拟DOM转为真实DOM插入页面
- mounted
- 页面中呈现经过Vue编译的DOM
- 至此vue的初始化流程全部结束
- 页面中呈现经过Vue编译的DOM
- beforeDestory
- vue实例中的data、methods等都是可用的
- 等待vue实例被销毁
- destoryed
- beforeUpdate
- 数据已经被更新但是页面还未被更新
- 页面和数据尚未保持同步
生成新的虚拟DOM,随后与旧的虚拟DOM进行比较
- updated
- 数据被更新
- 页面被更新
- 注意的点
- 一般数据请求等可以提前处理的数据或者事件可以在created中执行
- 需要操作DOM的只能写到mounted中(这个时候真实DOM才被渲染)
- 一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了
- vue实例销毁后组件自定义事件会失效,但是原生DOM事件不会失效,页面将不再更新
- 关闭定时器、取消订阅消息、解绑自定义事件一般都在beforeDestroy中处理
组件
- 没有el,因为el决定当前的vue实例服务于哪个容器
- 最终所有的组件都要经过一个vm的管理
- data必须写成一个函数
- 避免组件被复用时,数据存在引用关系会相互影响
- 组件名(使用时)
- kebab-case命名:my-school
- CamelCase命名:MySchool (需要Vue脚手架支持)
- 组件标签
- ,不使用脚手架时,该方法会导致后续组件不能渲染
- 定义组件
- const school = Vue.extend(options)或者const school = options
- VueComponent
-
组件本质是一个名为VueComponent的构造函数,定义组件时自动生成
-
Vue在解析组件标签的时候会创建组件实例对象
-
不同版本的vue
- vue.js是完整版的Vue,包含:核心功能 + 模板解析器
- vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器
- 所以不能使用template这个配置项
- 需要使用render函数接收到的createElement函数去指定具体内容
ref属性
- 给元素或者组件注册引用信息(类似id)
- 应用在html标签上获取的是真实DOM元素
- 应用在组件标签上是组件实例对象
<!-- 使用ref打标识 -->
<h1 ref="dom1">.....</h1>
<!-- js中获取 -->
this.$refs.dom1
package.json和package-lock.json
- package.json
- 在使用npm init创建包的时候会自动生成该文件(JSON格式)
- 该文件用于描述当前包的相关信息
- 包名(name)、版本(version)、描述(description)、作者(author)等
- private:true;表示当前包将不会发布到npm库
- scripts设置之后可以用简写形式调用相关的npm命令
- engines指定包运行的环境
- browserslist指定目标浏览器
- dependencies该模块中所列举的插件属于生产环境必须的依赖
- 在项目中如果我们没有import进去也会将插件打包
- devDependencies这些依赖只有在开发时候才需要(如eslint)
- 若文件中import 引入 devDependencies 中插件 依然会把当前引入的插件打包到文件中, 不引入 ,则不打包
npm install echarts --save-dev // 将会把echarts安装到devDependencies
npm install echarts --save // 将会把echarts安装到dependencies
在安装别人的项目时
使用npm install会安装package.json中devdependencies 和 dependencies两个模块下所列举的依赖
在项目中我们直接使用npm install echarts可以直接安装依赖,但是不会生成到package.json中,别人拿我们项目的时候npm install是不会直接下载echarts的
- package-lock.json
- 锁定安装时的包的版本号,并且需要上传到git,以保证其他人在npm install时大家的依赖能保证一致(cnpm install的没有package.json文件)
- 该文件详细记录了当前状态下实际安装的各个npm package的具体来源和版本号
组件中的props配置项
- props是只读的
- 如果修改了props,vue会警告
- 如果必须修改props,需要在data中定义数据,值为props接收的值,再修改data中的数据
- 注意props接收参数的名和data中数据的名字不能相同
- v-model绑定的值不应该是props中接收的参数
- props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错
- props可以接收方法,在子组件中调用来传参
<template>
// 使用组件
<MyHeader :addPerson="addPerson"/>
</template>
<script>
import MyHeader from './components/MyHeader'
export default {
data() {
return {
person:[
{id:'001',name:'yfeng'}
]
}
},
methods:{
addPerson(personObj){
this.person.unshift(personObj)
}
}
}
</script>
// 组件
<template>
<input type="text" v-model="name" @keyup.enter="add"/>
</template>
import {nanoid} from 'nanoid'
<script>
export default {
props:['addPerson'],
data(){
return {
name:''
}
},
methods: {
add(){
//校验数据
if(!this.name.trim()) return alert('输入不能为空')
//将用户的输入包装成一个todo对象
const personObj = {id:nanoid(),name:this.name}
//通知App组件去添加一个todo对象
this.addPerson(personObj)
//清空输入
this.title = ''
}
}
}
</script>
- props接收方式(三种)
new Vue({
el:'#root',
// 只接收
// props:['name'],
//接收并限制类型
// props:{name:String},
// 接收并限制类型、是否必传、指定默认值
props:{
name:{
type:String, //类型
required:true, //是否必传
default:'老王' //默认值
}
},
data:{
n:1
}
})
- 多级组件传值
- v-bind="$attrs"
混入mixin
- 用来分发 Vue 组件中的可复用功能
- vue提供mixins配置项,是个数组,可以接收多个混入文件
- 每个混入文件本质是一个对象,里面的配置项和vue实例中的配置项一致,所以混入文件中也可以嵌套mixins配置项
- 混入规则
- 对vue钩子函数(created、mounted)的合并
- 将函数合并成数组,mixins的钩子函数先被调用,再调用组件中的钩子函数
- 对vue钩子函数(created、mounted)的合并
new Vue({
beforeCreate:[],
created: [
function() { console.log(111)},
function() { console.log(222)}
],
...
})
- 对name、data、methods等配置项,以组件优先
插件
- 常用来为 Vue 添加全局功能
- 通过全局方法 Vue.use() 使用插件,需要在调用 new Vue() 启动应用之前使用
- vue本身提供很多插件,比如vue-router,vuex,在main中都需要调用Vue.use() 来使用插件
- 自己开发插件
- 插件本质是一个对象,对外暴露一个install方法,方法的第一个参数是Vue构造器,其他参数为可选
// 定义插件
export default {
install(Vue,x,y,z){
console.log(x,y,z)
//全局过滤器
Vue.filter('mySlice',function(value){
return value.slice(0,4)
})
//定义全局指令
Vue.directive('fbind',{
//指令与元素成功绑定时(一上来)
bind(element,binding){
element.value = binding.value
},
//指令所在元素被插入页面时
inserted(element,binding){
element.focus()
},
//指令所在的模板被重新解析时
update(element,binding){
element.value = binding.value
}
})
//定义混入,所有的vue实例和组件实例都有x和y
Vue.mixin({
data() {
return {
x:100,
y:200
}
},
})
//给Vue原型上添加一个方法(组件实例和vue实例都可以使用)
Vue.prototype.hello = ()=>{alert('你好啊')}
}
}
//引入插件
import plugins from './plugins'
//使用插件
Vue.use(plugins,1,2,3)
组件的自定义事件($emit)
- 子组件中使用this.$emit('clickBtn',params)向父组件传递自定义事件
- 使用组件时绑定自定义事件
- 直接绑定在使用的组件上@click="clickBtn"
- 或者在mounted里面使用this.on绑定
- 不支持v-on="$listeners"形式的多级自定义方法绑定
- 事件解绑
- this.$off('clickBtn')解绑一个自定义事件
- this.$off(['clickBtn','demo'])解绑多个自定义事件
- this.$off()解绑所有的自定义事件
- 组件销毁自定义事件会自动解绑
- 事件解绑或者组件销毁并不会影响原生事件的调用,只是页面不响应了,但是自定义事件不会被调用了
// 组件中
<template>
<div>
<p>{{name}}</p>
<button @click="clickBtn">点击</button>
<button @click="death">销毁当前组件的实例</button>
<button @click="unbind">解绑clickBtn事件</button>
</div>
</template>
<script>
export default {
name:'test',
props: ['name'],
data() {
return {age:18};
},
methods: {
clickBtn() {
// 组件被销毁或者解绑,下面的console语句还是会被执行
console.log('被调用了');
this.$emit('clickBtn', this.age);
},
death() {
this.$destroy();
},
unbind() {
this.$off();
}
}
};
</script>
// 使用组件
<template>
<test ref="test" :name="name" @clickBtn="clickBtn"></test>
</template>
<script>
import test from '@/components/app/test1/index.vue';
export default {
name: 'demo',
components: {test},
data() {
return {name:'yfeng'};
},
mounted() {
// 或者使用这种方式绑定
// this.$refs.test.$on('clickBtn', this.clickBtn);
// 使用下面这种方式绑定一次性事件
// this.$refs.test.$once('clickBtn', this.clickBtn);
},
methods: {
// 组件被销毁或者解绑,下面方法将不被执行
clickBtn(age) {
console.log('我也被调用了');
alert(age)
}
}
};
</script>
事件全局总线($bus)
- Vue官方文档的API中是没有这个方法的,是网友使用Vue后自创的
- 用于所有组件间的通讯
- 思想就是让所有的组件都能使用,并且其需要有off、$emit方法
- 可以将$bus放到组件的原型VueComponent
- 注意在接收数据的组件中的beforeDestroy钩子中解绑$bus上的事件
new Vue({
......
// 在beforeCreate里面还没有开始解析模板
// 所以在解析组件的时候Vue的原型上已经有了$bus
beforeCreate() {
//安装全局事件总线,$bus就是当前的vue实例
Vue.prototype.$bus = this;
},
......
})
// 有了全局事件总线想提供数据就触发$emit
clickBtn(){
this.$bus.$emit('clickBtn', this.age)
}
// 接收数据使用$on
mounted() {
this.$bus.$on('clickBtn',this.clickBtn)
}
this.$nextTick
- 在下一次DOM更新结束后执行制定的回调
- 数据改变后,不会马上更新真实DOM,所以如果要基于更新后的新DOM进行某些操作时,需要在nextTick所指定的回调函数中执行
// 修改数据
vm.msg = 'Hello'
// DOM 还没有更新
Vue.nextTick(function () {
// DOM 更新了
})
插槽
- 让父组件可以向子组件指定位置插入html结构
- 默认插槽
<!-- 父组件中使用组件 -->
<test>
<div>html结构1</div>
</test>
<!-- 子组件中定义插槽 -->
<template>
<div>
<!-- 定义插槽 -->
<slot>插槽默认内容...</slot>
</div>
</template>
- 具名插槽
- 使用v-slot:xxx的时候必须使用template
<!-- 父组件中使用组件 -->
<test>
<div slot="center">html结构1</div>
<!-- 或者使用这种方式 -->
<template v-slot:center>
<div>html结构2</div>
</template>
</test>
<!-- 子组件中定义插槽 -->
<template>
<div>
<!-- 定义插槽 -->
<slot name="center">插槽默认内容...</slot>
</div>
</template>
- 作用域插槽
- 传递组件的数据供父组件生成结构使用
- 使用scope="xxxx"必须使用template
<!-- 父组件中使用组件 -->
<test>
<template scope="age" slot="center">
<div>{{age.age}}</div>
</template>
<!-- 或者使用这种方式 -->
<div slot-scope="age" slot="center">{{age.age}}</div>
</test>
<!-- 子组件中定义插槽 -->
<template>
<div>
<!-- 定义插槽 -->
<slot name="center" :age="age">插槽默认内容...</slot>
</div>
</template>
<script>
export default {
name:'test',
data() {
return {age:18};
}
};
</script>
代理(devServer)
- 解决跨域问题
- 代理服务器相当于中间件(也是一个服务器),和发出请求的浏览器同源
- 代理服务器和后台服务器是服务器之间通讯,不涉及同源策略,用http请求
- 在vue.config.js中添加配置
// 方式一
/**
*不能配置多个代理,不能灵活的控制请求是否走代理
*请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)
**/
devServer:{
// 要请求的服务器
proxy:"http://localhost:5000"
}
// 方式二
/**
*可以配置多个代理,且可以灵活的控制请求是否走代理
**/
devServer: {
proxy: {
'/api': { // 匹配所有以 '/api'开头的请求路径
target: 'http://localhost:5000', // 代理目标的基础路径
//用于控制请求头中的host值,默认为true
// 如果为true,不告诉服务器真实的请求头的host值,为false则相反
changeOrigin: true,
pathRewrite: {'^/api1': ''} //重定向请求路径
}
}
render函数
- render是vue实例或者组件实例的一个配置项
- 函数中返回的内容将被vue解析后渲染
- 函数接收一个参数,官网叫createElement,我们一般使用h代替
render(createElement) {
// 渲染成<h1>欢迎你</h1>
return createElement('h1', this.title)
},
data(){
return {title:'欢迎你'}
}
// 渲染多层内容
render(createElement){
return createElement('h1',[
createElement('div','我还是喜欢码代码呀')
])
}
// 添加属性等其他标识
render(createElement){
return createElement('h1',[
createElement('div',{
// 普通的 HTML attribute
attrs:{
name:'div'
},
// 接受一个字符串、对象或字符串和对象组成的数组
class: {
foo: true,
bar: false
},
// 接受一个字符串、对象,或对象组成的数组
style: {
color: 'red',
fontSize: '14px'
},
// 组件 prop
props: {
myProp: 'bar'
},
on: {
click: this.clickHandler
},
domProps: {
innerHTML: '我还是喜欢码代码呀'
},
...//等等可以看vue官网
})
])
}
// 返回的节点必须是唯一的
render(createElement) {
var myParagraphVNode = createElement('p', 'hi')
return createElement('div', [
// 如果需要多个这里不可以定义直接使用
// myParagraphVNode, myParagraphVNode
// 需要这样
Array.apply(null, { length: 20 }).map(function () {
return createElement('p', 'hi')
})
])
}
// v-if和v-for的使用
props: ['items'],
render(createElement) {
if (this.items.length) {
return createElement('ul', this.items.map(function (item) {
return createElement('li', item.name)
}))
} else {
return createElement('p', 'No items found.')
}
}
被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。
所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,这样this的指向才是vm 或 组件实例对象
如果不需要双向绑定的数据不必定义到data,可以在created里面初始化,和Object.freeze()相比不同的是,冻结之后的数据不可更改,但是不放到data中的数据只是不响应
码字不易,请务随便转载~~
最好祝读到此文的每一位朋友事事顺心~