Vue2知识点全集

244 阅读7分钟

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可以定制按键别名
<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不推荐使用

image.png

计算属性

  • 不能和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,随后渲染到到页面
    • 使用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,永不要用在用户提交的内容上使用
  • 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的初始化流程全部结束
  • 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在解析组件标签的时候会创建组件实例对象

image.png

不同版本的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的具体来源和版本号

image.png

组件中的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"

image.png

混入mixin

  • 用来分发 Vue 组件中的可复用功能
  • vue提供mixins配置项,是个数组,可以接收多个混入文件
  • 每个混入文件本质是一个对象,里面的配置项和vue实例中的配置项一致,所以混入文件中也可以嵌套mixins配置项
  • 混入规则
    • 对vue钩子函数(created、mounted)的合并
      • 将函数合并成数组,mixins的钩子函数先被调用,再调用组件中的钩子函数
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.refs.xxx.refs.xxx.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后自创的
  • 用于所有组件间的通讯
  • 思想就是让所有的组件都能使用,并且其需要有onon、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中的数据只是不响应

码字不易,请务随便转载~~

最好祝读到此文的每一位朋友事事顺心~