MyselfNotes_vue_by_sir.w

Vue页面显示语法

在 Vue 语法中基础入门部分存在两个页面显示的语法

1:插值语法(大胡子表达式) 格式如下

{{ 初始化的数据 }}{{ 初始化的数据 }},其中null和underfined不显示

只能使用在标签内或者标签外,不能使用在标签上,天生支持单向绑定,大胡子表达式只能进行简单的业务逻辑,没有迭代 流程控制,基本上只能用来进行数据的显示工作。修改data中初始化的数据,则页面模板中的数据随之发生更改,这称之为单向绑定,又叫这个数据实现了可响应式,大胡子表达式和指令元素都支持单向绑定

{{ content }}

2:指令语法:指令语法存在多种书写方式格式如下

<tagName v-指令名=“绑定的数据” />

注意各种指令只能书写在标签上,不能书写在标签外,绑定的数据不是必须的

几种常见的指令语法

v-html:向元素中插入超文本,其实就是 js 中的innerHTML

v-text:向元素中插入文本,注意不支持标签,就是 js 中的innerText

ex:

<p style="background-color: aqua;" v-html="value"></p>
<p style="background-color: aqua;" v-text="value"></p>

输出:

在这里插入图片描述

v-once:一次性插值绑定,仅仅存在一次单向绑定之后失效

v-model:只能使用在表单项,天生自带单双向绑定功能

ex:

<input type="text" v-model="content">

输出:

在这里插入图片描述

在这里插入图片描述

对于复选框 单向:v-model 后面绑定的值如果为真,则复选框默认勾选为假默认不勾选

​ 双向:主动勾选复选框,则v-model 后面绑定的值为 true不勾选则为 false

 <input type="checkbox" v-model="flag">

v-bind:绑定元素的属性,值为初始化的值

​ <tagName v-bind:属性名=“初始化的值” />

语法糖:

​ <tagName :属性名=“初始化的值” />

   ~~~html
    <img :src="mySrc" :title="myTitle" :style="myCss">
   ~~~

v-on:绑定元素的动作(事件)

​ <tagName v-on:事件=“函数” />

语法糖:

​ <tagName @事件=“函数” />

​ 事件:与 js 类似,但是没有 on 前缀

​ 函数:如果没有实参,则不书写括号

<button @click="touch">{{ count }}</button>
<script>
	const app = new Vue({
        el: '#app',
        data: {
                content: 'hello',
                count: 100,
            },
        methods: {
/* 设置函数:注意如果一个函数放置在对象中,则也可以称之为方法 */
                touch() {
/* 如果 this 书写在 Vue 实例中,则表示本实例,这里就表示 app,通过 this可以获取本实例中所有的 data 调用其他函数 */
                    this.count++
                    /* 生成 0-255 随机整数 */
                    let r = Math.floor(Math.random() * 256)
                    let g = Math.floor(Math.random() * 256)
                    let b = Math.floor(Math.random() * 256)
                    this.myStyle = `background-color:rgb(${r},${g},${b})`
                },
            },
    })
</script>

Vue是如何工作的(生命周期简化版)

​ 浏览器从上往下解析页面,当解析完全文之后,如果没有任何错误,浏览器中由于无法解析 插值语法和指令语法,则这些语法就直接显示在页面中,这种状态称之为 虚拟 DOM之后 Vue 开始工作,根据 el 设置的模板,Vue 实例开始对这个模板进行编译,将初始化的数据挂载到插值语法中,解析指令语法,绑定数据,最终生成真实 DOM ,这个真实 DOM 会覆盖之前的虚拟 DOM,我们最终看到的就是真实 DOM

  • 虚拟DOM

在这里插入图片描述

  • 真实DOM

在这里插入图片描述

  • 控制台打印app.$el

在这里插入图片描述

Vue前端八股文

什么是单向绑定,什么是双向绑定

  • 单向绑定
    • 修改data中初始化的数据,则页面模板中的数据随之发生更改,这称之为单向绑定,又叫这个数据实现了可响应式,大胡子表达式和指令元素都支持单向绑定
  • 双向绑定
    • 修改页面模板中的数据,则管理模板的 Vue 实例的中 data 随之发生更改,则称之为双向绑定,v-model 自带双向绑定功能,其它数据如果想实现双向绑定功能,则必须使用计算属性

Vue2 模板如何防止网络 XSS 攻击

  • 禁止在使用 v-html 时插入动作

Vue2 如何进行样式渲染

思路1:绑定class

  • 绑定 class,此处省略v-blid,用v-blid的语法糖,即 :
    • ,这个是最常用的
    • ,注意此处数组中的类名为string类型,需要加 ‘ ’ 或者 “ ” ,建议加 ’ ’
<!--,其中box为对象名也就是class的名字(类名),flag为初始化的值,如果为真值类名存在,如果为假值类名不存在,这样可以设置不同css样式,实现不同的效果 -->
<p :class="{box2:flag2,box3:flag3}">1.2:绑定 class,后面是初始化的值</p>
<style>
    .box2 {
        background-color: red;
    }
   
    .box3 {
        background-color: green;
    }
</style>

思路2:绑定style

  • 绑定 style

    • 这个初始化的值就是行内式的样式
    • ,注意此处的样式名使用小驼峰格式
    • 以下为渲染的demo
    <style>
    .box1 {
        background-color: yellow;
    }
    
    .box2 {
        background-color: red;
    }
    
    .box3 {
        background-color: green;
    }
    
    .box4 {
        color: yellow;
        font-size: xx-large;
    }
    
    .box5 {
        background-color: orchid;
    }
    </style>
    
    <body>
    <div id="app">
        <!-- v-blid渲染样式,两种思路,思路1用:class,思路2用:style -->
        <!-- 思路1用:class -->
        <p :class="test1">1.1:绑定 class,后面是初始化的值</p>
        <!-- !!!!!!!!!!!!!!!!!!!!!!!!最常用!!!!!!!!!!!!!!!!!!!!!! -->
        <p :class="{box2:flag2,box3:flag3}">1.2:绑定 class,后面是初始化的值,其中box为对象名也就是class的名字,flag为初始化的值,真值类名存在,假值类名不存在</p>
        <p :class="['box4','box5']">1.3:绑定class,后面是一个数组</p>
        <!-- 思路2:style -->
        <p :style="val1">2.1:绑定行内式</p>
        <p :style="{backgroundColor:val2,color:val3}">2.2绑定对象</p>
    </div>
    <script src="../node_modules/vue/dist/vue.js"></script>
    <script>
        new Vue({
            el: '#app',
            data: {
                test1: 'box1',
                flag2: NaN,
                flag3: 99,
                val1: 'background-color:blue',
                val2: 'crimson',
                val3: 'white',
            },
        })
    </script>
    </body>
    

函数 计算属性 侦听器有什么不同?使用场合是?

  • 函数
    • 一般绑定事件或者被直接调用,函数没有缓存机制,只要被调用就会执行, 函数仅仅支持单向绑定功能,没有双向绑定功能,函数体内部可以书写异步代码
  • 计算属性
    • 就是一个属性,不会传递值,也没有括号,是由它依赖的属性计算而来,只要依赖的属性发生变动,则计算属性,重新执行,重新计算新的数据,只要依赖的属性没有变化,则计算属性根本不会再次执行,因为计算属性存在缓存机制,直接调用缓存中的数据
    • 计算属性在书写 set 之后则可以实现双向绑定功能
    • 计算属性在书写 get 之后则可以实现单向绑定功能
    • 由于计算属性中的单向绑定必须书写 return 语句,因此,不能在计算属性中书写异步代码(return相当于同步,异步和同步出现在同一场合异步根本不会执行)
  • 侦听器
    • 一般不考虑单双向绑定,侦听器侦听哪个值,只要这个值发生变化,则侦听器执行,如果书写 immediate,则侦听器立即执行一次
    • 侦听器默认只能侦听基本数据类型,如果要侦听复杂类型,则必须开启深度侦听,deep:true
    • 侦听器内部不需要强制书写 return,因此可以发送异步

条件渲染 v-if 和 v-show 的区别

  • v-if

    • 后面如果是真值,则元素显示,假值元素隐藏,底层不渲染,可以取反,切换消耗较大,适用于不频繁切换的场合

      <div :class="{box:val1}" v-if="!flag1"></div>
      
  • v-show

    • 后面如果是真值,则元素显示,假值元素隐藏,底层依然渲染只不过添加了一个 display:none的行内式 可以取反,初始载入消耗较大,之后切换消耗较小,适合频繁切换的场合

      <div :class="{box:val1}" v-show="!flag2"></div>
      
  • v-else-if 和 v-else

    • 以上两个指令是 Vue2.4 添加 专门用来搭配 v-if组成流程控制,不能与 v-show 连用,与v-if连用必须紧邻

    • 流程控制 v-else-if v-else 与 v-if 搭配使用 ,放在标签上 ,标签并不会像其他HTML代码一样被浏览器渲染出来,它仅仅包含了HTML标签、属性和内容的结构,而没有实际的样式或交互操作。

      <template v-if="count===1">
          React世界排名第一
      </template>
      <template v-else-if="count===2">
          Vue世界排名第二
      </template>
      <template v-else>
          什么?你居然还在用 jQuery?!!
      </template>
      

如何使用事件原型获取元素节点

  • event.target

  • 事件原型的作用在于,它可以让你在事件处理器中访问事件对象并在Vue实例中使用。其实就是获取元素节点

  • 注意如果我们在函数中不书写任何实参,则系统自动传递一个事件原型

    <!--如果 @事件 = 函数 的函数中只有一个参数,括号可以不写,如果存在两个及其以上参数,需要写括号-->
    <button @click="touch1">事件原型测试1</button>
    methods: {
            <!--这里 event 就表示事件原型,如果不使用则可以不写形参 随意命名 -->
            touch1(event) {
                <!--event.target:获取的就是这个事件激发的目标也就是 button 元素节点 -->
                event.target.innerHTML = this.content
            },
    }
    
  • 如果我们在函数中传递了参数,则底层不再自动传递事件原型,如果还需要事件原型,则必须手动传递实参 $event

    <button @click="touch2('hello-world',$event)">事件原型测试2</button>
    methods: {
            <!--这里 event 就表示事件原型,如果不使用则可以不写形参 随意命名 -->
            touch1(str,event) {
                <!--event.target:获取的就是这个事件激发的目标也就是 button 元素节点 -->
                event.target.innerText = str
            },
    }
    

简述你使用过的事件修饰符

写法:@事件.事件修饰符

  • .stop:解决冒泡问题

  • .once:激活一次性事件

  • .prevent:屏蔽元素固有的动作,例如表单 连接提交

    <!-- 表单和链接提交默认同步,可以用prevent终止同步,以后让异步执行,相当于$.ajax中的return false终止同步 -->
    <form action="./1-初试vue.html" @submit.prevent="go">
        <input type="submit" value="提交表单">
    </form>
    <a href="./1-初试vue.html" @click.prevent="go">提交链接</a>
    
  • .native:给组件添加原生事件

  • @keyup.键位名:监听键位的激发,当某个键位激发后抬起执行

  • @keydown.tab:监听 tab 键激发

Vue如何进行列表渲染(迭代数组和迭代对象)

  • v-for=“(alias,index) in 数组”

  • v-for=“(value,name,index) in 对象”

  • 注意存在主键则 :key=“主键” 没有主键则 :key=“index”

  • v-for:凡是书写此指令的元素都会进行迭代

    • 初始化数组

    ​ v-for=“(alias,index) in 初始化的数组”

    ​ alias:别名

    ​ index:索引,从 0 开始 不是必须,如果不写索引,则小括号可省略

    ​ in:可以替换为 of

    ​ 初始化的数据 可以是数组或者对象,必须初始化进 Vue 实例

    ​ :key=“主键”

    ​ 注意,如果存在主键,则必须在 :key中绑定主键,否则可能在

    ​ 迭代时由于底层是使用 索引值 进行排序,当使用 unshift

    ​ 从头部添加数据时,可能会造成错误,因此这里必须设置为

    ​ 按照主键排序

    <!-- 初始化数组 -->
    <table border="1px">
        <tr>
            <th>序号</th>
            <th>姓名</th>
            <th>性别</th>
            <th>归属地</th>
            <th>操作</th>
        </tr>
    
        <tr v-for="(person,index) in persons" :key="person.id">
            <td>{{ index+1 }}</td>
            <td>{{ person.name }}</td>
            <td>{{ person.gender?'男':'女' }}</td>
            <td>{{ person['location'] }}</td>
            <td><span style="cursor: pointer;" @click="remove(index)">删除</span></td>
        </tr>
    </table>
    <script>
        const persons = [
            /* id:模拟主键 gender: 0表示女 1表示男 */
            { id: 1, name: 'elena', gender: 0, location: '济南', },
            { id: 2, name: 'matt', gender: 1, location: '青岛', },
            { id: 3, name: 'penny', gender: 0, location: '济南', },
            { id: 4, name: 'aleric', gender: 1, location: '淄博', },
            { id: 5, name: 'tommy', gender: 1, location: '济宁', },
            { id: 6, name: 'damon', gender: 1, location: '济宁', },
            { id: 7, name: 'stefan', gender: 1, location: '烟台', },
            { id: 8, name: 'nancy', gender: 0, location: '青岛', },
            { id: 9, name: 'jerry', gender: 1, location: '德州', },
            { id: 10, name: 'sheldon', gender: 1, location: '济南', }
        ]
    
        const player = {
            name: '梅西',
            age: 35,
            nation: '阿根廷',
        }
    
    
        new Vue({
            el: '#app',
            data: {
                /* 初始化进数据源 persons:persons, player:player*/
                persons,
                player,
            },
            methods: {
                remove(index) {
                    if (confirm('您确定删除本条记录吗?')) {
                        this.persons.splice(index, 1)
                    }
                }
            },
        })
    </script>
    
    • 初始化对象

      v-for:如果迭代对象

      v-for=“(value,name,index) in 对象”

      value:属性值

      name:属性名

      index:索引 不是必须

      <!--初始化对象-->
      <ul>
          <li v-for="(value,name,index) in player">
              属性名:{{ name }},属性值:{{ value }}
          </li>
      </ul>
      <script>
          const persons = [
              /* id:模拟主键 gender: 0表示女 1表示男 */
              { id: 1, name: 'elena', gender: 0, location: '济南', },
              { id: 2, name: 'matt', gender: 1, location: '青岛', },
              { id: 3, name: 'penny', gender: 0, location: '济南', },
              { id: 4, name: 'aleric', gender: 1, location: '淄博', },
              { id: 5, name: 'tommy', gender: 1, location: '济宁', },
              { id: 6, name: 'damon', gender: 1, location: '济宁', },
              { id: 7, name: 'stefan', gender: 1, location: '烟台', },
              { id: 8, name: 'nancy', gender: 0, location: '青岛', },
              { id: 9, name: 'jerry', gender: 1, location: '德州', },
              { id: 10, name: 'sheldon', gender: 1, location: '济南', }
          ]
      
          const player = {
              name: '梅西',
              age: 35,
              nation: '阿根廷',
          }
      
      
          new Vue({
              el: '#app',
              data: {
                  /* 初始化进数据源 persons:persons, player:player*/
                  persons,
                  player,
              },
              methods: {
                  remove(index) {
                      if (confirm('您确定删除本条记录吗?')) {
                          this.persons.splice(index, 1)
                      }
                  }
              },
          })
      </script>
      

使用过滤器应该注意什么

  • 不能与 v-model 连用,过滤器为单向绑定,过滤器不能使用在表单项中时与 v-model 连用,因为过滤器只是单向,以下报错

    ​ (X)

    以下为正确写法

    ​ ( √ )

  • 不能使用 this,其中this代表window

  • 格式如下

    <!-- {{ 被过滤的值 | 过滤器名 | 过滤器名 }} -->
    <td>{{ person.payType | payFilter }}</td>
    

自定义指令

自定义指令,Vue 语法中提供的 v-xxx 指令元素如果无法满足我们的要求,则我们可以自己创建自定义指令,自定义指令指令名可以随意书写,但是注意 不能存在 v- 前缀,因为不管我们如何命名,都会自动添加 v- 前缀,自定义指令分为全局自定义和局部自定义

  • 全局自定义:可以使用在任意一个模板中

    • 其中bind(el)渲染样式
    • 其中inserted(el,binding)渲染动作
    /*模板
    Vue.directive('指令名',{
            //渲染样式
            bind(el){
                el:表示书写这个自定义指令的元素
            },
            //渲染动作
            inserted(el,binding){
                el:表示书写这个自定义指令的元素
                binding:就表示这个自定义指令
    },	
    })*/
    <div id="app1">
            <p v-etoak="content"></p>
    </div>
    ---------------------------------------------------------------------------------------------------------------
    Vue.directive('etoak',{
        /* 渲染样式 */
        bind(el){
            /* 书写 v-etoak 的元素节点(也就是 p 元素)的背景色
            为珊瑚橘 */
            el.style.backgroundColor = 'coral'
        },
        /* 渲染动作 */
        inserted(el,binding){
            /* 
                binding:表示指令元素 也就是 v-etoak
                binding.value:就表示指令元素后面绑定的值 这里就是
                content
            */
            el.innerText = binding.value.toUpperCase()
        },
    })
    ---------------------------------------------------------------------------------------------------------------
    new Vue({
        el:'#app1',
        data:{
            content:'thisisetoak',
        },
    })
    
  • 局部自定义:只能使用在某一个 Vue 实例管理的模板中

    <div id="app2">
        <p v-etoak="content"></p>
        <input type="text" placeholder="替换自动获取焦点" v-focus>
    </div>
    ---------------------------------------------------------------------------------------------------------------
    const app = new Vue({
        data:{
            content:'loveu3000',
        },
        /* 
            2:局部自定义指令 
            directives:{
                指令名:{
                    //渲染样式
                    bind(el){
                        el:表示书写这个自定义指令的元素
                    },
                    //渲染动作
                    inserted(el,binding){
                        el:表示书写这个自定义指令的元素
                        binding:就表示这个自定义指令
                    },
                },
            },
        */
        directives:{
            focus:{
                /* 渲染动作 */
                inserted(el,binding){
                    /* 
                        所有的事件其实都是函数,我们可以直接调用,强制激发事件 click() blur() keyup()
                        以下为强制激发获取焦点
                    */
                    /* focus为vue中的函数,在js中为onfocus,onfocus 事件在对象获得焦点时发生。
                    Onfocus 通常用于 <input>, <select>, 和 <a>.
                    提示: onfocus 事件的相反事件为 onblur 事件。 */
                    el.focus()
                },
            },
        },
    })
    app.$mount('#app2')
    

如何解决闪现问题

​ v-cloak:用来解决闪现问题,配合 css 使用,当浏览器解析完之后,Vue 还未进行解析之前,元素中的指令元素,直接显示的页面中,那么此时 css 生效,元素被隐藏,之后 Vue 实例开始解析,元素中的指令元素消失,css 失效,元素显示,这样在页面呈现虚拟 DOM 的时间点元素是隐藏的,因此根本看不到闪现的虚拟 DOM

<style>
    /* 设置样式配合解决闪现问题
    这里表示获取一个元素,属性名为 v-cloak */
    [v-cloak]{
        display: none;
    }
</style>
<h1 v-cloak>{{ title }}</h1>
<script>
    new Vue({
        data:{
            title:'我是标题!',
        },
    }).$mount('#app')
</script>

简述你所使用过的指令元素

  • v-once:一次性插值绑定,之后失去绑定功能

  • v-html:向元素中插入超文本,注意!为了防止网络 XSS 攻击,禁止插入脚本

  • v-text:向元素中插入文本

  • v-model:使用在表单项中,支持双向绑定

    • v-model.trim:双向绑定数据,去掉字符串两侧空格
    • v-model.number:双向绑定数据,同时转换为 number,注意如果无法转换,则不转换
    • v-model.lazy:点击回车才会激活双向绑定功能
  • v-bind: 语法糖 : ,绑定元素的属性,值为初始化的值

  • v-on:语法糖@,绑定事件

    • @事件.once 事件仅仅激发一次
    • @事件.stop 屏蔽事件冒泡问题
    • @事件.prevent 屏蔽元素固有的行为动作 例如 连接和表单的同步提交
    • @事件.native
    • @keyup.键位名
    • @keydown.tab
  • v-for:迭代数据,可以迭代数组或者对象

  • v-if:后面如果为真,则元素显示,为假,元素不显示,底层不渲染,可以取反,切换消耗较大,适用于不频繁切换的场合

  • v-else-if:搭配 v-if 使用必须紧邻,使用在上

  • v-else:搭配 v-if 使用必须紧邻

  • v-show:后面如果为真,则元素显示,为假,元素不显示,底层依然渲染,只不过添加了display:none;css行内式,初始载入消耗较大,之后切换消耗较小,适合频繁切换的场合

  • v-pre:提示 Vue 实例不解析

  • v-cloak:解决闪现问题

  • v-slot:父子组件传值时传递模板使用,是插槽的另外一种书写方式

Vue2实例对用户书写的 data 对象做了哪些处理?

  • 数据劫持(数据观测)
    • Vue 实例可以拦截到 data 中的所有数据,通过数据劫持,不管数据封装多少层,都会给数据添加一对可响应式函数 reactiveGetter 和 reactiveSetter,只要读取这个数据,则执行 reactiveGetter,如果要修改这个数据则执行 reactiveSetter,执行 reactiveSetter 时的同时这个函数开始操作 DOM,找到页面模板中的数据,进行修改,最终这些实现了可响应式的数据都被封装到了 _data 对象中
  • 数据代理
    • 一个对象可以对另外一个对象中的数据进行读写,则称之为数据代理,经过数据劫持之后,所有被施加了可响应式的数据都被封装到了_data 中,缺点是我们每次从模板使用都要添加 _data 前缀,因此有通过数据代理将 _data 中的数据代理到 Vue 实例的表层,使用的同样是Object.defineProperty() 读写操作其实都是对 _data 中的数据进行读写,在模板中不再需要添加 _data 前缀了

什么是数据劫持?

  • 在Vue中,数据劫持是通过使用ES5的Object.defineProperty方法来跟踪data内的所有属性变化的过程,以便Vue可以在数据变化时执行响应式的操作。当Vue实例化时,Vue将遍历每个对象的每个属性,并将其转换为getter/setter。这些getter/setter允许Vue追踪每个属性的变化并执行响应式更新。每当数据被修改时,Vue会被通知并执行响应式更新,以确保DOM和data保持同步。这个过程也被称为数据绑定。这是Vue实现其响应式系统的核心原理,它使得Vue非常适合构建大型和复杂的应用程序。

什么是数据代理?

  • 一个对象可以对另外一个对象中的数据进行读写,则称之为数据代理,经过数据劫持之后,所有被施加了可响应式的数据都被封装到了_data 中,缺点是我们每次从模板使用都要添加 _data 前缀,因此有通过数据代理将 _data 中的数据代理到 Vue 实例的表层,使用的同样是Object.defineProperty() 读写操作其实都是对 _data 中的数据进行读写,在模板中不再需要添加 _data 前缀了

  • 模拟数据代理

    <script>
        /* 数据代理:一个对象可以对另外一个对象的属性进行读写操作,则称之为数据代理 */
        
        
        /* 此处的_data模拟Vue中的_data数组 */
        const _data = {
            name: '胡桃',
            age: 18,
        }
    	/* 此处的app模拟Vue中的实例 */
        const app = {
            $demo1: 'demo1',
            $demo2: 'demo2',
            $demo3: 'demo3',
        }
    	/* 给app实例添加name属性 */
        Object.defineProperty(app,'name',{
            /* 给app实例添加name属性,当读取 name 属性执行此函数 */
            get(){
                console.log('app 的 name 属性被读取啦------')
                return _data.name
            },
            /* 当修改 name 属性执行此函数 */
            set(newVal){
                console.log('app 的 name 属性被修改啦------')
                _data.name = newVal
            },
        })
    
    </script>
    

实现对象可响应式

<script>
const app = new Vue({
    data:{
        person:{
            name:'宫本武藏',
            age:33,
            type:'战士',
        },
    },
    methods: {
        add(){
           	/* 给 person 添加属性 skill 以下写法可以添加成功,但是没有实现可响应式,页面模板不会发生任何改变*/
            //this.person.skill = '二天一流'
            /* 以下两种书写方式都可以实现可响应式的对象属性添加 */
            this.$set(this.person,'skill','二天一流')
            Vue.set(this.person,'skill','二天一流')
        },
        remove(){
            /* 删除属性 */
            /* 以下写法无法实现可响应式 */
            // delete person.skill
            /* 以下两种写法都可以实现可响应式 */
            this.$delete(this.person,'skill')
            Vue.delete(this.person,'skill')
        },
    },
}).$mount('#app')

实现数组的可响应式

  • Vue2 中对于数组的可响应式封装与对象不同,不是使用的 reactiveGetter 和 reactiveSetter,而是尤雨溪封装了七个函数,数组只能使用这七个函数来实现可响应式功能

​ push()

​ unshift()

​ shift()

​ pop()

​ reverse()

​ sort()

​ splice()

注意这七个不是 js 中的那七个函数,是重新使用可响应式封装的 Vue 版本,只不过功能一致 命名一致,Vue3 对于对象和数组没有任何限制,如何操作都是实现可响应式的

<script>
const coffees = ['美式','拿铁','摩卡','意式浓缩','卡布奇诺']

const app = new Vue({
    data:{
        coffees,
    },
    methods: {
        add(){
            /* 以下写法可以添加数据,但是没有实现可响应式 */
            //this.coffees[5] = '澳瑞白'
            this.coffees.push('澳瑞白')
        }
    },
}).$mount('#app')

使用Vue实现ToDoMvc

Vue中的组件

Vue实现Bootstrap模板

Vue组件通讯规则

  • 不要在子组件中修改父组件传递的数据
  • 数据初始化,要根据初始化的数据是否用于多个组件中,如果需要被应用在多个组件中,则初始化在父组件中,如果只在一个组件中使用,那就初始化在这个要使用的组件中
  • 数据初始化在哪个组件,更新数据的函数就应该定义在哪个组件

props父组件向子组件传递数据(父->子数据)

  • props只用于父组件向子组件传递数据

  • 所有的标签属性多会成为组件对象的属性,模板页面可以直接引用

  • 如果需要向非子后代传递数据,必须逐层传递

  • 兄弟组件不能直接使用props通讯,必须借助父组件

    父组件:

        <子组件 :绑定的属性(自己起名字)="初始化的值"></子组件>  
    

    格式1:
    子组件
    props:[‘绑定的属性’]

    格式2:
    子组件
    props:{
    绑定的属性1:数据类型,
    绑定的属性2:数据类型
    }
    数据类型: String Number Array Object Function Boolean
    注意不能传递标签

    功能最强大⬇️

    格式3:
    props:{
    绑定的属性:{
    type:数据类型,
    required:是否需要设置数据类型,
    default:type如果为基本数据类型,显示错误的提示
    default:()=>type如果为复杂数据类型,显示错误的提示

    ​ }
    ​ }

$emit自定义事件(子->父数据)

  • 自定义事件只用于子组件向父组件发送数据

  • 不能在隔代组件通讯时使用

    子组件:
    this.$emit(‘父组件中的自定义事件’,要传递的值)

    父组件:
    <给父组件传值的子组件 :hobbies=“mydatas” @自定义事件=“要调用的函数”></ 给父组件传值的子组件>

slot插槽分发内容(父->子标签+数据)

  • 用于父组件向子组件传递 标签和数据 (上面那二位只能传递数据)一般用于某个位置需要经常动态切换显示效果

  • 数据必须初始化在父组件中

    父组件

          <子组件>
          <tagName slot="插槽名"></tagName>
          </子组件>
    

    子组件

          <slot name="插槽名"></slot>
    
  • v-slot:只能写在template或者自定义标签(引用的组件)中 ,不能使用在元神标签中,优点是内部可以嵌套大量slot插槽分发内容

(已被删除)PubSubJs(任意组件数据)

  • 用来实现非父子组件之间的通信,使用PubSubJs进行消息发布与订阅模式,来进行数据传递

  • 必须安装
    npm install pubsub-js

  • 由于是第三方插件,必须使用箭头函数

    订阅:

      PubSub.subscribe('订阅的事件',(event,传递的数据)=>{
           
      })
    

    发布:

    PubSub.publish('订阅的事件',传递的数据)
    

普通组件与路由组件

注意官方从未将组件分为普通组件和路由组件,如有雷同纯属巧合
本文仅为便于记忆,路由组件和普通组件本质上都是组件,书写没有任何差异

  • 普通组件

    • 永远展示在某一个位置,不会随着哈希的更改而变化
    • 被放置 components 包中
    • 被注册在根组件或者父组件中
  • 路由组件

    • 会随着哈希的更改(哈希模式)而决定是否显示,显示在哪里
    • 被放置在 views 包中
    • 被注册在路由表中,而路由表被注册在 Vue 实例中

在Vue中,普通组件与路由组件主要通过它们在使用中的位置区分。普通组件是在组件中通过引入和注册的方式来使用的,而路由组件则是作为路由配置中的component选项来使用的。具体区别如下:

  • 普通组件:

    普通组件是在单文件组件(.vue文件)或全局注册的组件中定义的。在组件模板中可以直接使用它们的标签名来使用它们。例如:

    <template>
      <div>
        <hello-world></hello-world>
      </div>
    </template>
    
    <script>
    import HelloWorld from './components/HelloWorld.vue';
    export default {
      name: 'App',
      components: {
        HelloWorld
      }
    };
    </script>
    
  • 路由组件:

    路由组件是在路由配置文件中通过component选项来定义的。对于路由组件,Vue Router在路由改变时会将其渲染到router-view组件中,而不是使用组件标签名的方式。

    import Home from '@/components/Home.vue'
    import About from '@/components/About.vue'
    import VueRouter from 'vue-router'
    
    const router = new VueRouter({
      routes: [
        {
          path: '/',
          component: Home
        },
        {
          path: '/about',
          component: About
        }
      ]
    })
    

总的来说,在Vue中我们可以将某个组件定义为路由组件,这样Vue Router 就会自动完成组件的加载和渲染,并将该组件嵌套进路由视图中。而普通组件可以被任何代码调用并使用,所以普通组件与路由组件还是有些区别的。

Vue中不能使用箭头函数的情况

  • 不应该使用箭头函数来定义一个生命周期方法
  • 不应该使用箭头函数来定义 method 函数
  • 不应该使用箭头函数来定义计算属性函数
  • 不应该对 data 属性使用箭头函数
  • 不应该使用箭头函数来定义 watcher 函数

原因:

箭头函数绑定了父级作用域的上下文,this 将不会按照期望指向 Vue 实例。也就是说,你不能使用this来访问你组件中的data数据以及method方法了。this将会指向undefined。

Vue2 生命周期

  • beforeCreate()

    • 执行此钩子之前 Vue实例还未挂载任何模板,也未初始化任何数据,数据劫持和数据代理都没有发生,函数也没有准备好

       beforeCreate() {
                      console.log('beforeCreate------------------')
                      /* 
                          this._data:获取目前已经被添加了可响应式的初始化的数据
                          this.$el:获取 Vue 实例管理的模板                   
                      */
                      console.log(this._data,this.$el,this.touch())
           			//输出Error in beforeCreate hook: "TypeError: this.touch is not a function"
           			//其中this._data,this.$el输出undefined undefined
      
                  },
      
  • created()

    • 在此步之前,数据代理 和 数据劫持已经完成用户的数据已经被施加可响应式之后代理到Vue实例表层,函数也已经全部准备好,激发事件或者直接可以调用,此时created中是最早获取用户数据的时机,因此此处可以发送异步

      created() {
                      console.log('created------------------')
                      console.log(this._data, this.$el,this.touch())
          			//输出{__ob__: Observer} undefined undefined(函数的undefined是因为没有返回值)
                  },
      
  • beforeMount()

    • 在此钩子执行之前,首先查看是否存在el配置项如果没有则调用mount加载模板,之后再查看是否存在template配置项如果存在template则使用render函数进行渲染,模板就是template配置的的模板,如果没有template则使用el或者mount作为模板,此时Vue管理的模板已经准备好,但是Vue实例还未将数据挂载到模板中,页面直接显示插值语法和指令语法,所有的DOM操作都不奏效,此时呈现的是虚拟DOM

      beforeMount() {
                      console.log('beforeMount------------------')
                      console.log(this._data, this.$el)
                      //debugger
          		   //输出{__ob__: Observer}  div#app
                  },
      
  • mounted()

    • 此时Vue实例已经将数据挂载到模板中,真实DOM将虚拟DOM覆盖,页面就是最终的状态,此时可以发送异步,操作DOM等

      mounted() {
                      console.log('mounted------------------')
                      console.log(this._data, this.$el)
          		   //输出{__ob__: Observer}  div#app
                  },
      
  • beforeUpdate()

    • 只要data更改,则此生命周期执行,此时初始化的数据已经更新为新的数据,新的虚拟DOM已经生成但是还未转换为新的真实DOM,页面模板中显示的还是老的数据这也是唯一一个 数据与模板不统一的时机

       beforeUpdate() {
                      console.log('beforeUpdate----------')
                      console.log(this._data,this.$el)
                      debugger
                  },
      

在这里插入图片描述

  • updated()

    • 此时新的真实DOM已经覆盖新的虚拟DOM,初始化的数据与模板保持一致

      updated() {
                      console.log('updated----------')
                      console.log(this._data,this.$el)
                  },
      

在这里插入图片描述

  • beforeDestroy()

    • 在销毁之前执行的最后的生命周期钩子,此时 data methods等都可以正常工作,绑定依然有效一般在此进行收尾工作,例如清除缓存,关闭定时器取消订阅等

      beforeDestroy() {
                      console.log('beforeDestroy----------')
                      console.log(this._data,this.$el)
                  },
      

在这里插入图片描述

  • destroyed()

    • 实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。注意这里的事件是指自定义事件,而原生事件(click,blur等等)会一直有效,函数依然可以执行,但是没有绑定功能

      //函数依然会执行,但是count并没有++
      

在这里插入图片描述

在这里插入图片描述

以下几个生命周期在 日常开发 和 面试中经常被使用
1:created() 最早获取数据,发送异步
2:mounted() 页面已经被真实 DOM 覆盖完毕,处于稳定状态 此时可以操作 DOM,也可以发送异步
3:beforeUpdate() 唯一模板与数据不统一的时机
4:beforeDestroy() 即将要销毁,一般用来清除缓存删除已经占用的资源等善后工作

axios异步技术

由于 Vue 是纯前端的框架,与后端没有任何耦合,因此Vue 没有设置异步模块,如果想在 Vue 中使用异步技术,则必须借助其它技术目前较为成熟几乎没有任何兼容性问题的技术就是Axios,使用 Axios 需要注意以下两个问题

​ 1:下载依赖 npm i axios -S

​ 2:在 Vue 看来这是属于第三方技术,因此在使用时,必须使用箭头函数

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>axios异步技术</title>
    <style>
        [v-cloak]{
            display: none;
        }
    </style>
</head>
<body>
    <div id="app" v-cloak>
        <table border="1px" v-if="empList">
            <tr>
                <th>序号</th>
                <th>姓名</th>
                <th>性别</th>
                <th>薪资</th>
            </tr>
            <tr v-for="emp in empList" :key="emp.id">
                <td>{{ emp.id }}</td>
                <td>{{ emp['name'] }}</td>
                <td>{{ emp.gender?'男':'女' }}</td>
                <td>{{ emp['salary'] }}</td>
            </tr>
        </table>
        <template v-else>无数据</template>
    </div>
    <script src="../node_modules/vue/dist/vue.js"></script>
    <!-- 引入 axios 依赖 -->
    <script src="../node_modules/axios/dist/axios.js"></script>
    <script>
        new Vue({
            data:{
                empList:null,
            },
            methods: {
                /* 将发送异步的代码书写在函数中,在生命周期中
                进行调用,这种整合方式非常易于复用 */
                fetchData(){
                    /* 使用 axios 异步技术发送异步 */
                    axios.get('http://127.0.0.1:5500/src/db/empList.json')
                    .then(response=>{//发送成功
                        console.log('我是 then------')
                        console.log(response)
                        /* 给初始化的数据赋值 */
                        this.empList = response.data
                    })
                    .catch(err=>{
						//发送失败
                    })
                    .finally(()=>{
                        //不论成功还是失败,都执行finally函数
                        console.log('我是 finally-----')
                    })
                },
            },
            /* 最早的时间段,也就是最早获取 data 初始化数据的时间 */
            created() {
                /* 调用函数 发送异步 */
                this.fetchData()
            },
        }).$mount('#app')
    </script>
</body>
</html>

导入导出规则

  • 当用export default people导出时,就用 import people 导入(不带大括号)
  • 一个文件里,有且只能有一个export default。但可以有多个export
  • 当用export name 时,就用import { name }导入(记得带上大括号)
  • 当一个文件里,既有一个export default people, 又有多个export name 或者 export age时,导入就用 import people, { name, age }
  • 当一个文件里出现n多个 export 导出很多模块,导入时除了一个一个导入,也可以用import * as alias

ES6 模块化语法

​ 传统的非模块化开发,所有的资源都被引入到页面中,存在先后的层级关系,以及大量的资源浪费,浏览器几乎解析了所有的数据,但是这些数据很有可能根本不会使用 ,在 ES6 规范中模块化终于达成共识,每个资源都被称之为一个模块,例如一个 js 一个 css 都可以称之为模块,模块存在以下两个功能:

​ 导入:一个模块可以导入另外一个模块导出的数据为己所用

​ 导出:一个模块只有导出资源,另外的模块才能导入使用

使用原生 ES6 实现模块化要注意以下两个问题

​ 1:必须在 script 标签中声明 开启模块化 type=“module”

 2:必须部署到服务器中使用 alt+b 打开本地的方式无效
/* 
    一个模块存在两种导出功能
    1:导出默认成员,一个模块只能导出一个默认成员,默认成员可以不指定成员名
    	export default 默认成员
    2:导出普通成员,一个模块可能导出任意个普通成员,必须指定成员名
    	export 成员名
*/
/* A:导出默认成员,成员是一个对象 */
/* export default {
    name:'胡桃',
    age:18,
} */

/* B:导出默认成员,成员是一个函数 */
/* export default () => console.log('我是导出的函数!!!!') */

/* C:导出默认成员,成员是基本类型 */
export default '端午节要吃粽子啦!!'

/* D:导出普通成员 */
export let count = 100
export const arr = [1,2,3,4]
export const obj = {
    oname:'甘雨',
    oage:27,
}
export function sum(a,b,c,d){
    return a+b+c+d
}
/* 
    导入默认成员
    import alias from 路径
    alias 为别名 路径必须书写 js 后缀
*/
import bar from './bar.js'

//A:导入一个对象
//console.log(bar)
//B:导入的是一个函数,直接调用函数
/* bar() */
/* C:导入的是一个基本类型 */
console.log(bar)
/* 
    D:导入普通成员 
    import {成员名,成员名,成员名} from 路径
*/
import {count,arr,obj,sum} from './bar.js'
console.log(count,arr,obj,sum(...arr))

/* 
    E:导入其它模块中所有导出的成员 
    import * as alias from 路径
*/
import * as all from './bar.js'
/* 注意这个 all 就是一个大对象内部封装了普通成员和默认成员
普通成员名一一对应,默认成员被添加了一个属性名 default */
console.log(all,all.default)

webpack

在main.js中导入 Vue 依赖引发的问题

注意这里会引发一个问题,此处默认导入的 vue 的依赖并不是我们之前使用完全版的 Vue 依赖,而是一个运行版 vue.common.js

此版本缺失以下两个功能

​ Q1:无法编译 .vue 后缀的组件文件的问题

​ Q2:无法将 根组件 App.vue 替换 div#app 渲染到页面中的问题

解决方案 1:

​ 使用完全版 import Vue from ‘…/node_modules/vue/dist/vue.js’ 强烈不推荐,打包体积是运行版的四五倍之多

解决方案 2:

​ 弥补运行版的缺失

​ Q1:无法编译 .vue 后缀的组件文件的问题

​ 使用 webpack 的插件 vue-loader vue-template-complier

​ Q2:无法将 根组件 App.vue 替换 div#app 渲染到页面中的问题

​ 运行版中无法渲染根组件,就意味着 template 属性实际上已经失效,之前我们书写 template 会自动调用底层的 render 函数,

​ 将传入的根组件渲染到页面中替换 el 或者 mount 管理的模板,但是此时 template 失效了,我们自己书写 render 函数

/* 注意 在 webpack 和 VueCli 中 .js 后缀省略 */
/*导入vue依赖,此处导入的为vue.common.js*/
import Vue from 'vue'
/* 导入根组件 */
import App from './App.vue'

new Vue({
    components:{
        App,
    }, 
    /* 手写 render 函数 */
    //render:function(h){
        /* h 本身是一个函数将传入的组件渲染到页面中替换 div#app */
        //return h(App)
    //}
    render: h => h(App)
    /* h 本身是一个函数将传入的组件渲染到页面中替换 div#app */    
}).$mount('#app')

在.vue文件中style中的scoped属性

scoped:如果不存在则此处设置的样式同样会影响子组件,如果存在,则不会影响子组件

VueCli脚手架

使用 axios 创建自定义请求连接后端步骤

  1. 在根目录下创建 .env.development 配置文件,设置基本地址在 VUE_APP_BASE_API = ‘http://db.etoak.com:9527’ 中,至此通过 process.env.VUE_APP_BASE_API 可以从本工程中任意一个 js 文件中获取这个基本地址

    # 只有以 VUE_APP_ 开头的变量会被 webpack 静态嵌入到项目中进行使用 process.env.VUE_APP_xxxxxx
    # 在项目任意模块文件中,都可以使用 process.env.VUE_APP_BASE_API 获取值
    
    # 目标服务接口地址,这个地址是按照你自已环境来的, 添加 或者更改配置后,需要重启服务
    # VUE_APP_SERVICE_URL = 'https://www.easy-mock.com/mock/5f40867637dd743fd5db5cf2'
    # VUE_APP_SERVICE_URL = 'http://rap2.taobao.org:38080/app/mock/264680'
    
    # 开发环境的前缀
    # 以下为设置的基本地址
    VUE_APP_BASE_API = 'http://db.etoak.com:9527'
    
    # 本文为开发阶段使用的配置文件,所有重要的参数都放置在本文件中,注意更改此文件
    # 必须重启服务器 
    
    
    
  2. 下载 axios 依赖 npm i axios -S

  3. 在 util 包中创建 request.js,设置自定义 axios 实例,实现参数服用,在此文件中部署基本地址(从配置文件中获取)

    /* 导入 axios 依赖 */
    import axios from 'axios'
    
    /* 创建自定义 axios 实例,注意我们使用的并不是依赖提供的 axios,而是
    通过这个对象创建的自定义 axios 实例,此实例可以配置很多参数,从而实现一次配置
    一直使用的操作 ,注意这个 request 同样是一个 Promise 对象 */
    const request = axios.create({
        /* 设置基本地址 此处读取配置文件中的 'http://db.etoak.com:9527'*/
        baseURL: process.env.VUE_APP_BASE_API,
        /* 设置超时时间 单位是 ms */
        timeout: 5000,
    })
    
    /* 导出 request */
    export default request
    
    
    
  4. 在 api 包中创建 dao.js 设置每个接口的方法,使用自定义 axios 实例 request

    /* Data Access Object 简称 dao 本文仅为对应后端,命名可以随意
    命名 */
    
    /* 导入自定义 axios 实例 request */
    import request from '@/util/request'
    
    export default {
        /* 1:登录 
            username:形参 表示用户姓名
            password:形参 表示用户密码
        */
        login(username,password){
            /* return new Promise((resolve, reject) => {
                
            }) */
            return request({
                /* url:此处表示进阶地址,最终我们发送的完整地址如下
                    finalPath = baseURL + url
                    http://db.etoak.com:9527 + /testUser/login
                */
                url:`/testUser/login?username=${username}&password=${password}`,
                /* 发送异步的类型 */
                method:'get',
            })
        },
        /* 2:注册 
            pojo:对象 形参 可以随意书写,内部封装了
            注册的八个字段
        */
        reg(pojo){
            return request({
                url:'/testUser/add',
                method:'post',
                /* 由于是 post,因此可以发送 json
                这里的 data 后面是个对象 直接发送对象或者数组即可
                不需要通过 JSON.stringify 进行转换 */
                data:pojo,
            })
        }
    }
    

    $router 与 $route的不同

    $router

    $router是一个全局路由对象,是new Router的实例,$router对象正是new VueRouter所创建router对象,this.$router就等同于new Router的实例

    //常用函数
    this.$router.push('/user') //跳转路由
    this.$router.replace('/user') //跳转路由,但是不会有记录,不入栈
    

    $route

    $route是一个局部对象,表示当前正在跳转的路由对象,换句话说,当前哪个路由处于活跃状态,$route就对应那个路由。this.$route代表当前活跃的路由对象

    $route.path
    //字符串,等于当前路由对象的路径,如“/home/news”
    $route.params
    //对象,包含路由中的动态片段和全匹配片段的键值对
    $route.query
    //对象,包含路由中query参数的键值对。如“......?name=胡桃&age=16”会得到{“name”:"胡桃",“age”:16}
    $route.name
    //当前路径的名字,如果没有使用具名路径,则名字为空
    $route.router
    //路由规则所述的路由器(以及所属的组件)
    $route.matched
    //数组,包含当前匹配的路径中所包含的所有片段所对应的配置参数对象
    

    总结

    $routernew Router的实例,是全局路由对象,用于进行路由跳转等操作

    $route是路由信息对象,表示当前活跃的路由对象,用于读取路由参数;

    简单来说也就是,操作找$router,读参找$route

    备注

    vue路由报错:
    Uncaught (in promise) Error: Avoided redundant navigation to current location
    /*原因:
    多次点击跳转同一个路由是不被允许的
    解决办法:
    在引入vue-router的js文件里加上如下代码即可:*/
    //push 
    const VueRouterPush = VueRouter.prototype.push
    VueRouter.prototype.push = function push(to) {
    return VueRouterPush.call(this, to).catch(err => err)}
    //replace
    const VueRouterReplace = VueRouter.prototype.replace
    VueRouter.prototype.replace = function replace(to) {
    return VueRouterReplace.call(this, to).catch(err => err)}
    

    $router 与 $route的不同

    $router

    $router是一个全局路由对象,是new Router的实例,$router对象正是new VueRouter所创建router对象,this.$router就等同于new Router的实例

    //常用函数
    this.$router.push('/user') //跳转路由
    this.$router.replace('/user') //跳转路由,但是不会有记录,不入栈
    

    $route

    $route是一个局部对象,表示当前正在跳转的路由对象,换句话说,当前哪个路由处于活跃状态,$route就对应那个路由。this.$route代表当前活跃的路由对象

    $route.path
    //字符串,等于当前路由对象的路径,如“/home/news”
    $route.params
    //对象,包含路由中的动态片段和全匹配片段的键值对
    $route.query
    //对象,包含路由中query参数的键值对。如“......?name=胡桃&age=16”会得到{“name”:"胡桃",“age”:16}
    $route.name
    //当前路径的名字,如果没有使用具名路径,则名字为空
    $route.router
    //路由规则所述的路由器(以及所属的组件)
    $route.matched
    //数组,包含当前匹配的路径中所包含的所有片段所对应的配置参数对象
    

    总结

    $routernew Router的实例,是全局路由对象,用于进行路由跳转等操作

    $route是路由信息对象,表示当前活跃的路由对象,用于读取路由参数;

    简单来说也就是,操作找$router,读参找$route

    备注

    vue路由报错:
    Uncaught (in promise) Error: Avoided redundant navigation to current location
    /*原因:
    多次点击跳转同一个路由是不被允许的
    解决办法:
    在引入vue-router的js文件里加上如下代码即可:*/
    //push 
    const VueRouterPush = VueRouter.prototype.push
    VueRouter.prototype.push = function push(to) {
    return VueRouterPush.call(this, to).catch(err => err)}
    //replace
    const VueRouterReplace = VueRouter.prototype.replace
    VueRouter.prototype.replace = function replace(to) {
    return VueRouterReplace.call(this, to).catch(err => err)}
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值