Vue基础知识汇总(含webpack打包工具的使用)

1,810 阅读22分钟

基础知识一

框架和库的区别

  • 概念:小而巧的是库,大而全的是框架
  • 框架:是一套完整的解决方案,对项目的侵入性很大,项目如果需要更换框架,则需要重新架构整个项目
  • 库(插件):提供某一个小功能,对项目的侵入性较小,如果某个库无法完成某些需求,可以很容易的切换到其他库实现需求

MVC和MVVM思想

  • MVC 主要是后端的分层开发思想;把 一个完整的后端项目,分成了三个部分:

    • Model:(数据层)主要负责 数据库的操作;
    • View:(视图层)所有前端页面,统称为 View 层
    • Controller:(业务逻辑层)主要处理对应的业务逻辑;(对于后台来说,这是开发的重点)

  • MVVM是前端页面的分层开发思想,主要关注于 视图层 分离,也就是说:MVVM把前端的视图层,分为了 三部分 Model, View, ViewModel
    • Model 是 页面中,需要用到的数据
    • View 是页面中的HTML结构;
    • ViewModel 是 一个 中间的调度者,提供了双向数据绑定的概念;
  • 为什么有了MVC还要有MVVM
    • 因为 MVC是后端的开发思想,并没有明确定义前端的页面该如何开发;
    • MVVM 是前端的页面的开发思想,把每个页面,分成了三个部分,同时 VM 作为 MVVM 的核心,提供了双向数据绑定的概念,前端程序员,不需要手动渲染页面了,而且,页面数据发送变化,也不需要程序员手动把 数据的变化同步到Model中;这所有的操作,都是 VM 自动完成的!
    • 有了 MVVM 的思想以后,前端只关心 页面交互逻辑,不关心页面如何渲染;

Vue.js 基本代码 和 MVVM 之间的对应关系

1. 注意:Vue中,不推荐程序员手动操作DOM元素;所以,在Vue项目中,没有极其变态的需求,一般不要引入 Jquery;
2. Vue代码解析执行的步骤:
   1. 当 VM 实例对象,被 创建完成之后,会立即解析 el 指定区域中的所有代码;
   2. 当 VM 在解析 el 区域中所有代码的时候,会把 data 中的数据,按需,填充到 页面指定的区域;
3. 注意:每当 vm 实例对象,监听到 data 中数据发生了变化,就会立即 重新解析 执行 el 区域内,所有的代码;
<!-- 1.导入vue的包 -->
    <!-- 只要导入了vue的包,在window全局会挂载Vue成员(构造函数) -->
    <script src="./lib/vue-2.5.16.js"></script>
</head>
<body>
    <!--2. 放一个id为app的div,将来new 出来的vue实例会控制这个div内部的代码 -->
    <!-- 注意:不能vm实例对象直接控制body或者是html-->
    <div id="app">
        <h3>{{msg}}</h3>
    </div>
    <script>
        // 3new 一个Vue,创建Vue的实例对象
        // new出来的vm实例对象就是mvvm中的 vm
        const vm=new Vue({
            // 指定new 出来的vm实例要控制页面上的哪个区域
            el:'#app',//这个vm中指定的区域就是mvvm中view
            data:{//指定被控制的区域,要用到的数据
                msg:'Hello Vue.js'
            }//这个data指向对象就是mvvm 中的model
        });

        // function Person(obj){
        //     this.name=obj.name;
        //     this.age=obj.age;
        // }
        // const p1=new Person({name:'zs',age:20});
    </script>
</body>

Vue指令及插值表达式

定义:Vue中,通过一些特殊的用法,扩展了HTML的能力
  • 将来 创建 Vue 实例的时候,Vue 会把 这些指令 都进行解析,从而,根据不同的指令,执行不同的操作、渲染不同的结果;
Vue指令之 插值表达式 {{ }}
  1. 基本使用演示 在指定的位置动态插入内容,例如:
     <p>{{msg}}</p>
注意:指令是框架中提出的概念,扩展了html的能力,指令如果想要生效就必须被vm实例对象所解析
  1. 在插值表达式中 使用简单的表达式,不能写语句
  <div id="app">
        <h3>{{msg}}</h3>
        <h3>{{1+1}}</h3>
        <h3>{{boo ? '条件为真':'条件为假'}}</h3>
        <h3>{{msg.length}}</h3>
        <!--注意插值表达式只能在内容区域,不能在属性节点里面-->
        <h3>{{arr}}</h3>
       <!--不能写循环等语句-->
    </div>
    <script>
        const vm=new Vue({
            el:'#app',
            data: {
               msg:'Hello Vue.js',
               boo:false,
               arr:[1,2,3]
            },
        });
    </script>
  1. 注意:插值表达式只能用在元素的内容区域;不能用在元素的属性节点中; 4.如果对于{{}}的这种形式不满意,可以通过下面的形式修改自定义的形式:
  var vm = new Vue({
            delimiters:['$','#'],//把插值表达中的双花括号换成是指定的,但是一般不推荐
            el: '#app',
            data: {
                city: '北京',
                people: 2000
            }
        })

5.对于自增和自减运算会出现异常,所以这里暂时不推荐在插值表达中使用自增自减元素。

Vue指令之v-cloak
  1. 解决的问题
    • 插值表达式有闪烁的问题(v-cloak 指令来解决闪烁问题)
  [v-cloak]{
            display: none;
        }
  1. 应用场景
    • 当 网络比较卡的时候,我们可以为 最外层的 元素,添加 v-cloak ,防止用户看到 插值表达式
  2. 原理
    • 通过vm创建完成之后,动态的移除v-cloak的属性,从而显示插值表达式的节点
Vue指令之v-text

1.基本使用 在元素的属性节点上,添加v-text 命令,例如:

<p v-text="msg"></p>

2.v-text中也可以使用简单的语句 3.v-text与{{}}的区别

  • v-text会覆盖所有内容 {{}}不会覆盖
  • v-text不会出现闪烁问题,但是{{}}会出现闪烁问题

4.应用场景(v-text)

  • 向指定元素的内容区域中,渲染指定的文本
Vue指令之v-html

1.基本使用 在元素的属性节点上,添加v-text 命令,例如:

    <!-- 总结:
            vue 中的指令只有插值表达式是用在内容节点上的,
            其他的所有指令都是用在属性节点的
        -->
<p v-text="msg"></p>

2.应用场景 当服务器返回的数据中,包含的html的标签,此时,这些标签只能在v-html来渲染

Vue指令之 v-bind: 属性绑定(自定义属性和固有属性)
添加在元素节点身上的属性都可以通过v-bind进行绑定(id width src)等

1.基本使用

  • v-bind:是为html属性节点动态绑定数据的,例如:
<div id="app">
        <!-- v-bind:指令表示属性绑定,
            可以在v-bind中写一些简单的表达式
            今后在开发中非常常用
            v-bind:指令可以简写为英文的:代表的是属性绑定
        -->
        <button :title="titleStr">按钮</button>
        <img :src="boo ? img:img1" alt="">
        <!-- <img v-bind:src="img1" alt=""> -->
    </div>
    <script>
        // 当vm实例被创建完毕后会立即解析执行el区域内所有vue的指令
        // 而且只要data数据中发生了变化,就会立即重新解析数据
        const vm=new Vue({
            el:'#app',
            data:{
                titleStr:'这是title属性值',
                boo:false,
                img:'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=2391791031,3623366227&fm=26&gp=0.jpg'
                ,img1:'https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=486958716,243120904&fm=26&gp=0.jpg'
            }
        })
    </script>

2.应用场景

  • 如果元素的属性值,需要动态地进行绑定,则需要使用v-bind: 指令
Vue指令之v-on:事件绑定
  1. 基本使用: v-on: 的作用,是为 HTML 元素,绑定 事件处理函数,例如:
 <input type="button" value="按钮" v-on:click="事件处理函数名" />
  1. 绑定事件处理函数并传参:
  <input type="button" value="按钮" v-on:click="show(123)" />
关于参数问题:
1. 有传递使用传递的参数
2. 没有声明(),第一个形参就是事件对象object MouseEvent
3. 有声明(),还没有传递实参,形参就是undefined
   methods: {
            // 成员方法
                exp(id){
                    console.log('商品被删除了'+id);
                }
                // 不传递实参的情况下是undefined
                // 不加括号[object MouseEvent]
            },

  1. 简写形式: v-on: 指令可以简写成 @,例如,可以简写成如下格式:
 <input type="button" value="按钮" @click="事件处理函数名" />
Vue指令之v-modle实现双向绑定

1.基本使用:

  • 可以把页面上数据的变化,自动同步更新到 VM 实例的 data 中。例如:
<input type="text" v-model="msg"/>

2.和v-bind的区别

  • v-bind: 只能实现单向的数据同步 data ---> 页面;
  • v-model 可以实现双向的数据同步 data <--> 页面;
  • 在写法上v-bind可以简写为:的形式.v-model没有简写方式
  • v-model ="msg",:v-bind:src="mySrc",他们里面的属性值都需要在data中设置,哪怕没有值也需要设置为空字符串。 3.注意:
  • v-model只能和表单元素配合使用,例如input select textarea等
  • v-model是Vue中唯一支持双向数据绑定的指令.

4.v-model简易版原理整理 v-model的原理就给input输入框中定义oninput事件,在该事件中把用户输入的信息都给随时获得到,并对data成员进行赋值

data成员变化了,页面上用到的地方就重新渲染,达成简易双向绑定的效果 代码如下:

 <div id="app">
        <p>{{city}}</p>
        <!-- 这里的$event就代表的是event -->
        <input type="text" @input="city=$event.target.value" :value="city">
        <input type="text" @input="feel" :value="city">
    </div>
    <script src="./vue.js"></script>
    <script>
    var vm=new Vue({
        el:'#app',
        data:{
            city:'北京'
        },
        methods: {
            feel(evt){
            //    console.log(evt);
            // console.log(evt)  // InputEvent对象
        // evt.target:代表触发当前事件的html元素dom对象,具体是input框对象
        // evt.target.value: 随时感知输入框输入的信息

        // 把随时输入的信息赋予给city,这样city变化,由于“重新渲染”,页面上用到的地方就更新了
        // 就达成v-model双向绑定的效果了  
            this.city=evt.target.value;
            }
        },
    })
    </script>

5.应用场景: 简易计算器的实现:

<body>
    <div id="app">
        <!-- 第一个运算的数值 -->
        <input type="text" v-model="n1">
        <select v-model="opt">
            <option value="+">+</option><br>
            <option value="-">-</option><br>
            <option value="*">*</option><br>
            <option value="/">/</option><br>
        </select>
        <!-- 第二个元素的数值 -->
        <input type="text" v-model="n2">
        <!-- 注意这里不要写错 -->
        <button @click="calc">=</button>
        <!-- 运算的结果 -->
        <input type="text" readonly :value="result">
    </div>
    <script>
        const vm=new Vue({
            el:'#app',
            data:{
                n1:0,
                n2:0,
                opt:'+',
                result:0
            },
            methods: {
                
                calc(){
                    switch(this.opt){
                        case '+':
                       this.result= parseFloat(this.n1)+parseFloat(this.n2);
                        break;
                        case '-':
                       this.result= parseFloat(this.n1)-parseFloat(this.n2);
                        break;
                        case '*':
                       this.result= parseFloat(this.n1)*parseFloat(this.n2);
                        break;
                        case '/':
                       this.result= parseFloat(this.n1)/parseFloat(this.n2);
                        break;
                    }
                }
            },
        })
    </script>
</body>
this 操控data

根据业务需要,事件在执行过程中需要对Vue实例的data数据进行操作,通过this关键字实现,

this代表Vue实例对象,并且针对data或methods成员都可以直接进行调用

在Vue中使用class样式

1.类名数组

  • 通过 v-bind: 为元素的 class 绑定具体的类名:
  <p :class="['thin', 'red', 'big']">哈哈哈</p>

2.类名数组中使用三元表达式,按需为元素添加某些类名

<button @click="getInfo()" >获取数据</button>

<script>
  var vm = new Vue({
    el:'#app',
    data:{
      address:'铁岭'
    },
    methods:{
      getInfo:function(){
        // 通过 this关键字 获得 data区域的数据信息
        console.log(this.address+'是一个大城市');
      }
    }
  })
</script>
<p :class="['thin', flag ? 'red' : '']">哈哈哈</p>

3.应用场景

  • 网页开关灯
 <div id="app" :class="[flag ? 'light':'dark']">
        <button @click="flag=!flag">切换</button>
        <h1>{{msg}}大渣好,我系咕天乐,我系渣渣辉,贪挽难约,介系一个你没有挽过的船新版本,挤需体验3番钟,里就会干我一样,挨上节款游戏</h1>
        <img :src="flag ? img1:img2" alt="">
    </div>
    <script>
        const vm=new Vue({
            el:'#app',
            data:{
                //false是黑夜,true是白天
                flag:false,
                img1:'./images/6.jpg',
                img2:'./images/段段.jpg'
            }
        })
    </script>
在Vue中使用style样式

style属性也比较特殊,其可以给标签设置许多css样式,在vue中可以做如下应用

- 对象语法
      <div :style="{color: 'red', 'font-size': '20px', fontWeight:'bold' }"></div>
- 数组语法
      <div :style="[{color: 'red'}, {'font-size': '20px', fontWeight:'bold' }]"></div>

在一个数组元素中可以绑定多个或一个样式成员 有的样式属性名称是通过"-"中横线连接的,这在javascript的命名规则中不允许的,例如font-size、font-weight,在处理时有两种方式

  1. 引号限定 如 'font-size'
  2. 中横线去除,后边首字母大写 如 fontWeight

以上对象或数组绑定class语法均渲染为:

 <div style="color:red; font-size:20px; font-weight:bold"></div>

通过 数组 或 对象 对 class/style 进行绑定的好处是,属性值可以嵌入编程内容,实现精细化控制

Vue指令之v-for和:key属性

1.基本用法:

  • 普通数组 (一般般)
<li v-for="(item,i) in list1">{{item}}---索引{{i}}</li>
  • 对象数组(用的最多)
 <li v-for="(item,i) in list2">{{item.id}}---{{item.name}}---索引值{{i}}</li>
  • 迭代对象中的属性
  • 迭代数字 (这两种方法平时不怎么用,所以这里不详细介绍了)

2.:key的用法

<li v-for="item in list2" :key="item.id">{{item.id}}---{{item.name}}---索引值{{i}}</li>
注意:今后只要用到了id值,就一定要为循环的每一项,添加:key属性绑定,而且key的值最好绑定到id值上,key的值一定要唯一,单独的使用索引值也是不可以的,因为这个key值是用来标识数据唯一性的,通过key绑定的数据项和数据状态(比如复选框的选中伪选中关系)之间的关系
Vue中v-if和v-show指令
  • v-if 和 v-show 的作用,都是切换界面上元素的显示或隐藏的;
 <div id="app">
        <button @click="flag=!flag">Toggle</button>{{flag}}
        <!-- v-if是通过动态创建或者移除元素实现动态切换 -->
        <h3 v-if="flag">奔跑的五花肉</h3>
        <hr>
        <!-- v-show是通过控制元素的display:none样式实现切换 -->
        <h3 v-show="flag">2432432</h3>
    </div>
一般来说,v-if 有更高的切换消耗 而  v-show 有更高的初始渲染消耗。

因此,如果需要频繁切换 v-show 较好,如果在运行时条件不大可能改变 v-if 较好。

修饰符

事件修饰符(用来修饰事件的,如果点击事件)

.prevent 阻止默认行为(使用最频繁)

 <a href="http://www.baidu.com" @click.prevent="show">百度</a>

.once 只触发1次(几乎不常用)

 <button @click.once="btnHandler">按钮</button>

.stop阻止冒泡

 <button @click.stop="btnHandler">按钮</button>

.self只有在当前元素上触发事件的时候,才会调用处理函数

 <div class="inner" @click.self="innerHandler"></div>
按键修饰符

按键修饰符是配合文本框的使用的 .enter

        <input type="text" v-model="password" @keyup.enter="login"><br>

.tab

        <input type="text" v-model="password" @keyup.tab="login"><br>

.esc

        <input type="text" v-model="password" @keyup.esc="login"><br>

基础知识二

Vue过滤器

 "2018-01-25T02:10:02.945Z"    =>   2018-01-25

概念:过滤器的本质就是一些函数,可被用作一些常见的文本格式化

  • 过滤器只能用在两个地方,插值表达式和v-bind表达式中
  <td :title="item.ctime |dateFormate">{{item.ctime | dateFormate}}</td>
  • 过滤器应该被添加在javascript表达式的尾部,由 管道 符指示

全局过滤器

1.使用全局过滤器的语法

<span>{{ dt | 过滤器的名称 }}</span>
|的作用就是调用一个过滤器

2.定义全局过滤器的语法

  • Vue.filter('过滤器的名称', function(originVal){ /* 对数据进行处理的过程,在这个 function 中,最后必须 return 一个处理的结果 */ })
注意:全局过滤器必须定义在new Vue()之前

3.使用过滤器的注意事项

  • 如果想拿管道符前面的值,通过 function 的第一个形参来拿
  • 过滤器中,一定要返回一个处理的结果,否则就是一个无效的过滤器
  • 在调用过滤器的时候,直接通过 () 调用就能传参; 从过滤器处理函数的第二个形参开始接收传递过来的参数
  • 可以 多次 使用 | 管道符 一次调用多个过滤器
  <div id="app">
        <!-- 在插值表达式中可以使用|管道符来调用指定的过滤器 -->
        <!-- 如果调用过滤器了,则在这个内容区域显示的内容是过滤器方法最终返回的处理结果 -->
        <!-- 过滤器只是对原有的数据做了一层包装,并没有修改原来的值 -->
        <h3>{{dt | dateFormate}}</h3>
        <p>{{dt}}</p>
    </div>
    <script>
        // 通过Vue.filter定义一个全局过滤器
        // 注意,调用过滤器的时候,管道符前面的值,必须通过function的第一个参数接收
        // Vue.filter('过滤器的名称',function(originVal){//过滤器的处理函数

        // })
        Vue.filter('dateFormate',function(originVal){
            //最终一定要返回一个处理结果
            // return originVal+'-------'
            const dt=new Date(originVal);
            const y=dt.getFullYear();
            const m=(dt.getMonth()+1+'').padStart(2,'0');
            const d= (dt.getDate()+'').padStart(2,'0');
            const hh= (dt.getHours()+'').padStart(2,'0');
            const mm=(dt.getMinutes()+'').padStart(2,'0');
            const ss= (dt.getSeconds()+'').padStart(2,'0');

            const dtstr=`${y}-${m}-${d} ${hh}:${mm}:${ss}`;

            return dtstr;
        });
        const vm=new Vue({
            el:'#app',
            data:{
                dt:'2018-01-25T02:10:02.945Z'
            },
            methods: {
                
            },
        })
    </script>

私有过滤器

 const vm=new Vue({
            el:'#app',
            data:{
                dt:'2018-01-25T02:10:02.945Z'
            },
            methods: {
               
               
            },
            // 注意是跟methods同级
             // 注意:私有的过滤器带s,全局过滤器不带过滤器
             // 私有过滤器的名称:function (){//私有过滤器的处理函数}
            //  过滤器是按照就近原则进行调用的,先调用私有的,如果私有过滤器先看私有过滤器,没有再看全局过滤器
             filters:{
                //  es3写法
                    // dateFormate:function(originVal){
                    //     return originVal+'-----'
                    // }
                // es6写法
                    dateFormate(originVal){
                        return originVal+'-----'
                    }
                }
        })
过滤器是按照就近原则进行调用的,先调用私有的,如果私有过滤器先看私有过滤器,没有再看全局过滤器

带参数的过滤器

有的时候,过滤器主体业务逻辑不变化的情况下,可能结果的形式根据业务要求有所调整,为了增强灵活度,可以通过传递参数实现。

Vue实例.$mount()动态挂载实例

语法:Vue.$mount("选择器 - 指定要控制的区域")

 const vm=new Vue({
            data:{
                msg:'Hello'
            }
        });

        //mount是挂载的意思,表示 手动指定当前的vm实例要控制的区域
        vm.$mount('#app');

template属性指定模块

语法:template:'<h6>{{msg}}</h6>'

 const vm=new Vue({
            el:'#app',
            data:{
                msg:'Hello'
            },
            methods: {},
            filters:{},
            // 指定当前vm要渲染的模版
            // 结论:如果同时指定了el和template,那么template会把el区域替换掉
            template:'<h6>{{msg}}</h6>'
        })

Vue的生命周期

生命周期

概念 : 实例的生命周期,就是一个阶段,从创建到运行,再到销毁的阶段;

生命周期函数

在实例的生命周期中,在特定阶段执行的一些特定的事件,这些特定的事件叫做生命周期函数

  • 生命周期函数=生命周期钩子=声明周期事件
主要的生命周期函数分类
  • 创建期间的生命周期函数(特点:每个实例一辈子只执行一次)
    • beforeCreate:创建之前,此时data和methods方法尚未初始化,还不能访问里面的数据
    • created:(第一个重要的函数,此时data和methods已经创建好了,里面的数据还有方法都可以被访问了)
    • beforeMounted:挂载模版之前,此时页面还没有被渲染出来
    • mounted:(第二个重要的函数,此时,页面刚被渲染出来,,如果要操作DOM元素,最好在这个阶段)
  • 运行期间的生命周期函数(特点,按需被调用,至少0次,最多N次)
    • beforeUpdate :数据是最新的,但是页面还是旧的页面
    • updated 页面和数据都是最新的
  • 销毁期间的声明周期函数(特点:每个实例一辈子只执行一次)
  • beforeDestroy:销毁之前,实例还可以正常使用
  • destroyed :销毁之后,实例已经不存在,无法工作了

axios的使用

<script>
    // 通过这个属性,全局设置 请求的 根路径
    axios.defaults.baseURL = 'http://www.liulongbin.top:3005'
    // 将来项目中都这么搞
    Vue.prototype.$http = axios

    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
      el: '#app',
      data: {},
      methods: {
        async getInfo() {
          const { data: res } = await this.$http.get('/api/get', { params: { name: 'zs', age: 22 } })
          console.log(res)
        },
        async postInfo() {
          const { data: res } = await this.$http.post('/api/post', { name: 'zs', age: 22 })
          console.log(res)
        }
      }
    });
  </script>

注意:axios这个库可以在任意的请求中使用,他们发送get和post请求时的参数是不相同的,get请求参数是params而那个post请求,请求参数是data,但是这里太过麻烦,就用对象结构赋值的方式将代码进行解构
axios的缺点
  • 只支持 get 和 post 请求,无法发起 JSONP 请求;
  • 如果涉及到 JSONP 请求,可以让后端启用 cors 跨域资源共享即可;

在Vue中,还可以使用 vue-resource 发起数据请求

  • vue-resource 支持 get, post, jsonp请求【但是,Vue官方不推荐使用这个包了!】

基础知识三

Vue中的动画

Vue中动画的基本介绍

使用动画的原因
  • 动画能够增加页面的趣味性,目的是为了让用户更好的理解页面的功能
注意:Vue中的动画,都是简单的动画过渡,并不会有CSS3那么炫酷
动画简单介绍

1.每个动画都是由两部分组成的:

- 入场动画:从不可见(flag = false) -> 可见(flag = true)
- 出场动画:可见(flag = true) -> 不可见(flag = false)
  1. 入场时候,Vue把这个动画,分成了两个时间点和一个时间段:
    • v-enter:入场前的样式
    • v-enter-to:入场完成以后的样式
    • v-enter-active:入场的时间段
  2. 离场时候,Vue把动画,分成了两个时间点和一个时间段:
    • v-leave:离场之前的样式
    • v-leave-to:离场完成以后的样式
    • v-leave-active:离场的时间段

动画图示:

总结:带active的都是时间段的效果,
     带to的都是完成之后的状态,
     什么也不带的都是开始时的状态
    
也就是说动画都是从哪来,从哪去

使用过渡类名
  1. 把需要添加动画的元素,使用v-if或v-show进行控制
  2. 把需要添加动画的元素,使用Vue提供的元素 包裹起来
  3. 添加两组类:
<style>
        /* 定义元素入场之间和离场之后的位置 */
        /* 注意:他在入场之前并不是标准里的位置 */
        .v-enter,
        .v-leave-to {
            transform: translateX(150px);
            opacity: 0;
        }

        /* 定义元素,入场阶段,和离场阶段的过渡效果 */
        .v-enter-active,
        .v-leave-active {
            transition: all 0.8s;
        }

        /* 动画完成之后,默认的位置就是标准流中的效果 */

        /* 定义元素在标准流中的效果, 这个标准流的效果就是入场完成之后,移入离场开始之前,元素的效果*/
        h3 {
            transform: translateX(50px);
            opacity: 0.5;
        }
        /* 通过类名,可以设置页面上的多组动画效果 */
        .test-enter,.test-leave-to{
            opacity: 0;
            transform: translateY(200px);
        }
        .test-enter-active,
        .test-leave-active{
            transition: all 0.8s;
        }
    </style>

此时transition身上的name属性也要改成对应的属性值

<div id="app">
        <button @click="flag=!flag">Toggle</button>
        <!-- 1.使用vue框架提供的transition标签,把需要添加过渡的效果包裹起来 -->
        <transition name="">
            <h3 v-if="flag">函数哈哈胡莎莎</h3>
        </transition>
        <hr>
        <button @click="flag2=!flag2">Toggle</button>
      <transition name="test">
            <h6 v-if="flag2">这是第二个元素</h6>
      </transition>
    </div>
注意:v-if和v-show在前面讲过是控制元素的显示和隐藏,具体的操作可以再回顾一下上面的笔记.
使用第三方的css动画库
  1. 把需要添加动画的元素,使用v-if或v-show进行控制
  2. 把需要添加动画的元素,使用Vue提供的元素 包裹起来
  3. 为 添加两个属性类enter-active-class, leave-active-class
  4. 把需要添加动画的元素,添加一个 class="animated"
 <div id="app">
        <button @click="flag=!flag">Toggle</button>
        <!-- 指定入场的类名 -->
        <transition enter-active-class="bounceInDown" leave-active-class="bounceOutDown">
            <h3 v-if="flag" class="animated"> 哇哈哈哈哈哈哈</h3>
        </transition>
    </div>
v-for的列表过渡
  1. 把v-for循环渲染的元素,添加 :key 属性[注意:如果要为列表项添加动画效果,一定的指定key,并且key的值不能是索引]
  2. 在 v-for循环渲染的元素外层,包裹 标签
  3. 添加两组类即可:
 <!-- transition-group这个标签要放在ul里面,并且这个标签会自动的渲染为span -->
            <!-- 但是这里也可以强制的将这个标签渲染为ul,这时,外面的ul属性也不需要设置了 -->
            <transition-group tag="ul">
                <li v-for="item in list" :key="item.id">
                    {{item.id}}---{{item.name}}
                </li>
            </transition-group>
列表的排序过渡

组件还有一个特殊之处。不仅可以进入和离开动画,还可以改变定位。要使用这个新功能只需了解新增的 v-move 特性,它会在元素的改变定位的过程中应用。

  • v-move 和 v-leave-active 结合使用,能够让列表的过渡更加平缓柔和:
    <style>
   /* 为即将被删除的元素,添加样式,让他脱离标准流 */
        .v-leave-active{
            position: absolute;
            width: 100%;
        }
        /* 通过.v-move这个类,可以让后续的元素,通过过渡,渐渐的顶上去 */
        /* 如果不加这个类的话,下面的元素会一下就顶上去,也就没有了过渡的效果 */
        .v-move{
            transition: all 0.8s;
        }
    </style>

Webpack

什么是webpack?

  • 什么是webpack:webpack 是前端项目的构建工具;前端的项目,都是基于 webpack 进行 构建和运行的;
  • 为什么要使用webpack:
    1. 如果项目使用 webpack 进行构建,我们可以书写高级的ES代码,且不用考虑兼容性;
    2. webpack 能够优化项目的性能,比如合并、压缩文件等;
    3. 基于webpack,程序员可以把 自己的开发重心,放到功能上;
  • 什么项目适合使用webpack:
    • webpack 非常适合与 单页面应用程序(SinglePageApplication) 结合使用;
      • vue, react, angular 只要用前端三大框架开发项目,必然会使用webpack工具;
    • 不太适合与多页面的普通网站结合使用;
  • 根据webpack官网的图片介绍webpack打包的过程
  • webpack分很多版本 1.x 2.x 3.x 4.x

ES6导入导出语法

由于下面的笔记,在配置的时候可能会遇到ES6中导入导出的语法,所以这里提前在笔记中记录一下。

在webpack中一切皆模块,这里主要是ES6中模块化的导入和导出。

在webpack中,每个js文件都需要独立的模块,每个模块都有独立的作用域,其他模块默认无法直接访问当前模块中定义的成员。

默认导入导出方式

1.默认导入

默认导入的语法可以使用任何合法的名称来进行接收
import 接收名称 from '模块名称'

2.默认导出语法:

export default {

    a:a
}
按需导入和导出
  1. 按需导入语法: import { 成员名称 } from '模块名称'
  2. 按需导出语法: export var a = 10

在项目中安装和配置

webpack 是前端的一个工具,这个工具,可以从NPM官网上下载到本地使用;

  1. 新建一个项目的空白目录,并在在终端中,cd到项目的根目录,执行npm init -y 初始化项目
  2. 装包:运行 npm i webpack webpack-cli -D 安装项目构建所需要的 webpack
  3. 打开 package.json文件,在 scripts 节点中,新增一个 dev 的节点:
  4. 在项目根目录中,新建一个 webpack.config.js 配置文件,内容如下
{
  "name": "code2",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev":"webpack" 
  },

  1. 在项目根目录中,新增一个 src 目录,并且,在 src 目录中,新建一个 index.js 文件,作为 webpack 构建的入口;会把打包好的文件输出到 dist -> main.js
  2. 在终端中,直接运行 npm run dev 启动webpack进行项目构建;

实现webpack的实时打包构建

  1. 借助于 webpack-dev-sever 这个工具,能够实现 webpack 的实时打包构建;
  2. 运行npm i webpack-dev-server -D 安装包
  3. 打开package.json文件,把 scripts 节点下的 dev 脚本,修改为如下配置:
     "scripts": {
           "test": "echo \"Error: no test specified\" && exit 1",
           "dev": "webpack-dev-server"
         },
  1. 修改 index.html 文件中的 script 的 src, 让 src 指向 内存中根目录下的 /main.js
     <script src="/main.js"></script>

使用html-webpack-plugin插件配置启动页面

  1. 装包npm i html-webpack-plugin -D
  2. 在 webpack.config.js中,导入 插件:
   // 导入 html-webpack-plugin,从而帮我们自动把 打包好的 main.js 注入到 index.html 页面中
       // 同时,html-webpack-plugin 可以把 磁盘上的 index.html 页面,复制一份并托管到 内存中;
       const HtmlPlugin = require('html-webpack-plugin')
       const htmlPlugin = new HtmlPlugin({
         // 传递一个配置对象
         template: './src/index.html', // 指定路径,表示 要根据哪个物理磁盘上的页面,生成内存中的页面
         filename: 'index.html' // 指定,内存中生成的页面的名称
       })
  1. 把 创建好的 htmlPlugin 对象,挂载到 plugins数组中:
  // webpack 这个构建工具,是基于 Node.js 开发出来的一个前端工具
       module.exports = {
         mode: 'development', // 当前处于开发模式
         plugins: [htmlPlugin] // 插件数组
       }

实现自动打开浏览器、热更新和配置浏览器的默认端口号

  • --open 自动打开浏览器
  • --host 配置IP地址
  • --port 配置 端口号
  • --hot 热更新;最新的代码,以打补丁的形式,替换到页面上,加快编译的速度;

webpack打包非js文件

webpack打包css文件

由于webpack只能打包js文件,所以对于非js文件就需要单独进行处理

使用webpack打包css文件

  1. 运行 npm i style-loader css-loader -D
  2. 打开 webpack.config.js 配置文件,在 module -> rules 数组中,新增处理 css 样式表的loader规则:
     module: { // 所有 非.js 结尾的第三方文件类型,都可以在 module 节点中进行配置
           rules: [ // rules 是匹配规则,如果 webpack 在打包项目的时候,发现,某些 文件的后缀名是 非 .js 结尾的
             //  webpack 默认处理不了,此时,webpack 查找 配置文件中的 module -> rules 规则数组;
             { test: /\.css$/, use: ['style-loader', 'css-loader'] }
           ]
         }
注意:由于使用非js文件打包,都需要对他进行loader的出来,但是这里css-loader也有他自己的调用顺序,这里这个loader的调用时逆向的,先调用css-loader,然后再调用style-loader

使用webpack打包less文件

  1. 运行 npm i less-loader less -D
  2. 在 webpack 的配置文件中,新增一个 rules 规则来 处理 less 文件:
     { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] }
注意:只要是样式表,在他的loader配置中都需要加上style-loader和css-loader这两个选项。

使用webpack处理css中的路径

  1. 运行 npm i url-loader file-loader -D
  2. 在 webpack 的配置文件中,新增一个 rules 规则来 处理 图片 文件:
    { test: /\.jpg|png|gif|bmp$/, use: 'url-loader' }

webpack中使用bootstrap

  1. 运行 npm i bootstrap 对bootstrap装包
  2. 在webpack中使用bootstrap本身没有问题,但是由于boostrap本身引入了其他的字体的样式表,这里又只能打包的是js文件,所以这里还需要字体样式表的loader
{ test: /\.eot|woff|woff2|ttf|svg$/, use: 'url-loader' }

默认情况下,如果导入的模块是路径,webpack会优先去node_modules目录下,查找指定的路径是否存在

注意:这里打包字体文件和loader和打包处理图片的loader都是url-loader

使用babel处理高级JS语法

1.由于webpack默认只能打包处理一部分高级的js的语法,如果某些js语法,过于高级,则webpack也是处理不了的,此时只能借助于babel这个插件,来打包处理高级的js语法 2.运行两套命令,去安装相关的 loader:

- 运行 npm i babel-core babel-loader babel-plugin-transform-runtime -D
- 运行 npm i babel-preset-env babel-preset-stage-0 -D
  1. 添加 babel-loader 配置项:
       // 注意:在配置 babel-loader 的时候,一定要添加 exclude 排除项,把 node_modules 目录排除
       // 这样,只让 babel-loader 转换 程序员 自己手写的 JS 代码;
       // 好处:1. 能够提高编译的转换效率; 2. 能够防止不必要的报错!
       { test: /\.js$/, use: 'babel-loader', exclude: /node_modules/ }
  1. 在项目根目录中,添加 .babelrc 配置文件:
       {
         "presets": ["env", "stage-0"],
         "plugins": ["transform-runtime"]
       }

基础知识四

在 webpack 中安装和配置 vue

  1. 运行 npm i vue -S 把 vue 安装到项目依赖
  2. 在 index.js 中使用 import 导入 vue 模块:
    import Vue from 'vue'
  1. 在 index.html 中创建将来要被 vm 实例控制的 div:
     <!-- 将来,这个 div 就是 Vue实例 要控制的区域 -->
       <div id="app"></div>
  1. 在 index.js 中创建 vm 实例,并使用 el 指定要控制的区域,使用 data 指定要渲染的数据:
     const vm = new Vue({
         el: '#app', // 要控制的区域
         data: {
           msg: 'ok' // 要渲染的数据
         }
       })

为什么在基于 webpack 的 vue项目中, 按照如上操作会报错呢

  1. 因为使用 import 导入的 vue 模块,导入的并不是功能最全的 vue 包;而是删减版的;
  2. 删减版的 vue 包中功能很少,目的是为了减小vue 包的体积,因为文件越小,网络请求越快!
  3. 如何让 import 导入最全的 vue 包呢?
  - 把 import Vue from 'vue' 改写为 import Vue from 'vue/dist/vue.js'
  1. 注意:在学习阶段,可以暂时 import 最全的 vue 包;等开发项目的时候,一定要使用 删减版的 包;

定义Vue组件

模块化和组件化的概念解读

  1. 什么是模块化:是从代码的角度分析问题;把可复用的代码,抽离为单独的模块;
    • 模块化的好处:
      • 提供模块作用域的概念,防止全局变量污染;
      • 提高了代码的复用率,方便了程序员之间 共享代码;
  2. 什么是组件化:组件化是从页面UI的角度进行分析问题的;把页面中可复用的UI结构,抽离为单独的组件;
    • 组件化的好处:
      • 方便了UI结构的重用;
      • 随着项目开发的深入,手中可用的组件会越来越多;
      • 可以直接使用第三方封装好的组件库;
      • 组件化能够让程序员更专注于自己的业务逻辑;

定义全局组件

1.定义组件的语法

  • Vue.component('组件的名称', { 组件的配置对象 });

  • 在组件的配置对象中:可以使用 template 属性指定当前组件要渲染的模板结构;

Vue.component('my-test',{
    template:`<div>这是我定义的第一个Vue组件</div>`
});
注意:这里的组件名称最好都写成是小写的,并且中间最好用-连接一下
  • 在组件的配置对象中:可以使用 template 属性指定当前组件要渲染的模板结构;

2.使用组件的语法(全局组件)

  • 把 组件的名称, 以标签的形式,引入到页面上就行; 注意:

  • 从更抽象的角度来说,每个组件,就相当于是一个自定义的元素;

  • 组件中的DOM结构,有且只能有唯一的根元素(Root Element)来进行包裹!

Vue.component('my-test',{
    template:`<div>
    <div>这是我定义的第一个Vue组件</div>
    <div>这是我定义的第一个Vue组件</div>
    </div>`
});

3.使用组件的语法(私有组件)

注意:定义私有组件,只能在定义区域内使用,超出定制区域内使用无效,会报错误

const vm = new Vue({
    el: '#app',
    data: {
        msg: 'Hello World!'
    },
    components: {
        //使用compoents属性定义的组件,叫做是私有组件 
        // '组件名称':{/组件的配置对象/}
        'my-test2': {
            template: `<div>这是私有组件</div>`
        }
    }
})

组件中定义data数据、methods方法以及生命周期函数

组件中定义data数据
Vue.component('my-test', {
    template: `<div>
    <div>这是我定义的第一个Vue组件{{d1}}</div>
    <div>这是我定义的第一个Vue组件</div>
    </div>`,
    // 注意Vue规定,组件中的data必须是function函数,而且必须return一个对象
   data(){
    //    return 当前实例对象
       return {
           d1:'111'
       }
   }
});

区分vm实例中定义data数据

//在vm实例中,data既可以是对象也可以方法,但是在组件中只能是方法
const vm = new Vue({
    el: '#app',
    // data: {
    //     msg: 'Hello World!'
    // },
    data() {
        return {
            msg: 'Hello World!'
        }
    },
    components: {
        //使用compoents属性定义的组件,叫做是私有组件 
        // '组件名称':{/组件的配置对象/}
        'my-test2': {
            template: `<div>这是私有组件</div>`
        }
    }
})
组件中定义自己的methods,fifters以及生命周期函数
Vue.component('my-test', {
    template: `<div>
    <div @click="show">这是我定义的第一个Vue组件{{d1}}</div>
    <div>这是我定义的第一个Vue组件</div>
    </div>`,
    // 注意Vue规定,组件中的data必须是function函数,而且必须return一个对象
   data(){
    //    return 当前实例对象
       return {
           d1:'111'
       }
   },
   //组件中也可以有自己的私有方法
   methods: {
       show(){
           console.log('调用了自定义组件中的show方法')
       }
   },
    //组件中也能有自己的私有过滤器
    filters:{
        testFilter(originval){
            return originval+'~~~'
        }
    } ,
    // 组件也可以有自己的声明周期函数
    created() {
        console.log('调用了created函数')
;    },
组件和实例的区别
  1. 组件中的 data 必须是一个 function 并 return 一个 字面量对象;在 Vue 实例中,实例的 data 既可以是 对象,可以是 方法;

  2. 组件中,直接通过 template 属性来指定组件的UI结构;在 Vue 实例中,通过 el 属性来指定实例控制的区域;但是实例也可以使用 template;

  3. 组件和实例,都有自己的生命周期函数,私有的过滤器,methods 处理函数;

为什么组件中的 data 属性必须定义为一个方法并返回一个对象

主要是内存中存储的地址不一样,具体操作可以看下图的指示:

.vue单文件组件

  • 为什么要把组件,单独的定义到 .vue 文件中?

    • 之前创建组件太麻烦,没有智能提示和代码高亮;
    • 之前定义组件,和其它JS代码逻辑掺杂在一块儿,代码不易维护,没有把组件化的优势发挥到极致!
  • 每个 .vue 文件,都是一个 vue 组件(叫做 单文件组件),它由三部分组成:

    1. template 结构
    2. script 行为
    3. style 样式
<template>
    <div>
        <!--注意单文件中的template节点只能有唯一的父元素进行包裹  -->
        <h3>这是使用.vue文件定义单文件组件---{{msg}}</h3>
    </div>
</template>
<script>
// 行为中固定写法
// 当前组件中私有的data数据
export default {
    data(){
        return {
            msg:'hello .vue文件'
        }
    },
    methods:{},
    filters:{},
    created(){}
}
</script>
<style >
h3{
    color: red;
}
</style>

在webpack中配置.vue组件页面的解析

// 导入单文件组件
import Home from './components/HOME.vue';
//把单文件组件,以Vue.compondent注册为全局组件
Vue.component('my-home',Home);

从上述代码中可以看出,当前导入的是.vue文件,不是以js为后缀名的文件,同时也不是我们之前处理的文件类型,所以这里在浏览器运行就会报出一个没有loader的错误.

  1. 运行npm i vue-loader vue-template-compiler -D
  2. 添加rules匹配规则:
   { test: /\.vue$/, use: 'vue-loader' }
  1. 在webpack.config.js中导入并配置插件:
     // 导入插件
       const VueLoaderPlugin = require('vue-loader/lib/plugin')
       // new 一个插件的实例对象
       const vuePlugin = new VueLoaderPlugin()
       
       // 把 new 出来的插件实例对象,挂载到 `plugins` 节点中:
       plugins: [...其它插件, vuePlugin]

导入并使用 .vue 组件的两种方式

全局注册 .vue 文件:

  • 在 index.js 中导入 .vue 组件
  • 使用 Vue.component() 把导入的 .vue 组件,注册为全局组件
// 导入单文件组件
import Home from './components/HOME.vue';
//把单文件组件,以Vue.compondent注册为全局组件
Vue.component('my-home',Home);

const vm=new Vue({
    el:'#app',
    data:{

    }
})

2.私有注册 .vue 文件:

  • 定义两个独立的组件 Home.vue 和 Son.vue
  • 在 Home.vue 组件中,导入 Son.vue 文件,并使用 Home.vue 组件的 components 属性,把 Son.vue 注册为自己的私有子组件
//导入son组件
import Son from './Son.vue';

// 行为中固定写法
// 当前组件中私有的data数据
export default {
    data(){
        return {
            msg:'hello .vue文件'
        }
    },
    // 在.vue文件中,可以通过components属性,将另外一个.vue组件,定义为自己的私有组件
    
    components:{
        'my-son':Son,
    }
}

组件中的样式问题

由于我们期望在组件中的样式只在当前组件中生效,但是我们通过运行可以发现,子组件在父组件中使用,他也被父组件的样式影响,这个是因为默认情况下,组件中定义的样式是全局组件,所以可以使用下面的这种方法:

<style  scoped>

/* 我们期望在组件中的样式只在当前组件中生效 */
/* 所以今后,我们都需要给组件的style添加scoped,防止样式冲突 */

  h3 {
    color: red;
  }

</style>>

如果我们希望使用标签嵌套的方式,也就是less的文件样式,就需要添加一个lang="less"属性,


<style lang="less" scoped>
/* 我们期望在组件中的样式只在当前组件中生效 */
/* 所以今后,我们都需要给组件的style添加scoped,防止样式冲突 */
.home-box {
    border: 1px solid #000;
  h3 {
    color: red;
  }
}
</style>>

组件之间的数据通信

父组件向子组件传递普通数据

1.在父组件中,以标签形式使用子组件的时候,可以通过属性绑定,为子组件传递数据:

<my-son :pmsg1="parentMsg" :pinfo="parentInfo"></my-son>

2.在子组件中,如果向用父组件传递过来的数据,必须先定义 props 数组来接收:

<script>
  export default {
    data(){
      return {}
    },
    methods: {},
    // property 属性
    // 注意:父组件传递到子组件中的数据,必须经过 props 的接收,才能使用;
    // 通过 props 接收的数据,直接可以在页面上使用;注意:不接受,不能使用外界传递过来的数据
    props: ['pmsg1', 'pinfo']
  }
</script>

3.接收完的props数据,可以直接在子组件的 template 区域中使用:

<template>
  <div>
    <h3>这是子组件 --- {{pmsg1}} --- {{pinfo}}</h3>
  </div>
</template>

具体操作可以看下图:

注意:父组件传递给子组件的成员数据props都是可读数据,不要为他们重新赋值,
但是data数据都是当前属性的私有数据,而且data中的数据都是可读可写的
由于props中的数据都是只读的,所以如果想为props数据重新复制,可以把数据转存到data中,从而实现重新赋值

由于上述进行值的传递和转存的时候,都是简单的数据类型的值,所以如果变成是引用数据类型的值,就会出现值会同时修改的情况,所以这里需要进行深拷贝操作,这里进行深拷贝操作的是利用一个包lodash:

import _ from 'lodash';
export default {
  data(){
    // 对于转存修改属性只是简单数据类型可以转,对于复杂数据类型还需要另外进行操作
    return {
      infoFromParent:this.pinfo,
      msgFromParent:_.cloneDeep(this.pmsg)
    }
  },
    // 子组件需要使用props按钮,接收外界传递过来的数据
  props:['pmsg','pinfo']
}

通过_cloneDeep(传递一个对象),这样就可以实现深拷贝,并且返回的是一个全新的对象,修改他的值,不会影响其他的值.

父组件向子组件传递方法(属性绑定)

  1. 如果父向子传递方法,需要使用 事件绑定机制:
  <my-son @func="show"></my-son>

其中,为 子组件传递的 方法名称为 func, 具体的方法引用,为 父组件中的 show 方法 2. 子组件中,可以直接通过 this.$emit('func') 来调用父组件传递过来的方法;

子组件向父组件传值(事件绑定)

  1. 子向父传值,要使用 事件绑定机制@;

  2. 父向子传递一个方法的引用

  3. 子组件中,可以使用 this.$emit() 来调用父组件传递过来的方法

  4. 在使用this.$emit()调用 父组件中方法的时候,可以从第二个位置开始传递参数;把子组件中的数据,通过实参,传递到父组件的方法作用域中;

 methods: {
    // 点击子组件中的按钮,触发按钮的点击事件
    btnHandle(){
      // 在子组件中通过this.$emit()方法,触发父组件,为子组件绑定func事件

      this.$emit('func' +this.msg)
    }

  },
  1. 父组件就可以通过形参,接收子组件传递过来的数据;

兄弟组件之间传值

注意:兄弟组件之间,实现传值,用到的技术,是 EventBus

  1. 定义模块 bus.js
  import Vue from 'vue'
       
   export default new Vue()
  1. 在需要接收数据的兄弟组件中,导入 bus.js 模块
   import bus from './bus.js'
  1. 在需要接收数据的兄弟组件中的 created 生命周期函数里, 使用 bus.$on('事件名称', (接收的数据) => {}) 自定义事件:
 created(){
         // 定义事件
         bus.$on('ooo', (data)=>{
           console.log(data)
         })
       }
  1. 在需要发送数据的兄弟组件中,导入 bus.js 模块
   import bus from './bus.js'
  1. 在需要发送数据的兄弟组件中,使用 bus.$emit('事件名称', 要发送的数据) 来向外发送数据:
       import bus from './bus.js'
       export default {
           data(){
             return {
               msg: 'abcd'
             }
           },
           methods: {
             sendMsg(){
               // 触发 绑定的 事件,并向外传递参数
               bus.$emit('ooo', this.msg)
             }
           }
       }

使用 this.$refs来获取元素和组件

1.把要获取的DOM元素,添加 ref 属性,创建一个DOM对象的引用,指定的值,就是引用的名称:

//通过ref获取的DOM元素的引用就是一个元素的DOM对象
<p ref="myElement11">这是父组件</p>
  1. 如果要获取 某个引用所对应的 DOM对象,则直接使用 this.$refs.引用名称
     console.log(this.$refs.myElement11)
  1. 也可以使用 ref 为组件添加引用;可以使用 this.$refs.组件应用名称,
console.log(this.$refs.myElement11)

3.也可以使用 ref 为组件添加引用;可以使用 this.$refs.组件应用名称, 拿到组件的引用,从而调用组件上的方法 和 获取组件data上的 数据;

使用 霸道的 render 函数渲染组件

  1. 如果在 vm 实例中既指定了 el 又指定了 render 函数,则会把 el 所指的的区域,替换为 render 函数中所提供的组件;

  2. 既然 render 函数会替换到 el 区域内的所有代码,也会让 template 属性失效;因此,在删减版的 vue 包中,new 出来的 Vue 实例对象,不允许 挂载 data 属性和 template 属性!

const vm = new Vue({
  el: '#app',
  // createElements 形参是一个方法,专门用于渲染一个组件,并替换掉 el 区域
  /* render: function(createElements){
    return createElements(App)
  }, */
  // 这是 render 的终极格式
  // 被render渲染的组件,叫做 根组件
  // 什么是根组件:【不论浏览器中的页面如何切换,根组件永远都在页面上显示】
  render: h => h(App)
})

// 注意:只要在 vm 实例中,指定了 render 函数来渲染组件,那么,el 区域,就会被 render 中渲染的组件替换掉;

基础知识五

使用标签实现组件切换

  1. 是Vue提供的;作用是 把 is 指定的 组件名称,渲染到 内部
  2. 身上有一个 :is属性
<template>
  <div>
    <h1>App 根组件</h1>
    <button @click="comName='my-home'">Home</button>
    <button @click="comName='my-movie'">Movie</button>
    <!-- 可以通过 component 的is属性,动态指定要渲染的组件 -->
    <component :is="comName"></component>
  </div>
</template>

<script>
import Home from './coms/Home.vue'
import Movie from './coms/Movie.vue'
export default {
  data() {
    return {
      // 默认是展示home属性的
      comName: 'my-home'
    }
  },
  components: {
    'my-home': Home,
    'my-movie': Movie
  }
}
</script>

SPA单页应用

锚链接及常规url的区别

  1. 普通的URL地址:会刷新整个页面;会追加浏览历史记录;
  2. 锚链接:不会触发页面的整体刷新;会追加浏览历史记录;(锚链接是页面内的跳转)

什么是SPA,为什么有SPA

  • 概念定义:SPA英文全称是Single Page Application, 中文翻译是 “单页面应用程序”;
  • 通俗的理解是:只有一个Web页面的网站;网站的所有功能都在这个唯一的页面上进行展示与切换;
  • 特点:
    • 只有一个页面
    • 浏览器一开始请求这个页面,必须加载对应的HTML, CSS, JavaScript
    • 用户的所有操作,都在这唯一的页面上完成
    • 页面数据都是用Ajax请求回来的
  • 好处:
    • 实现了前后端分离开发,各司其职;提高了开发效率;
    • 用户体验好、快,内容的改变不需要重新加载整个页面;
  • 缺点:
    • 对SEO不是很友好,因为页面数据是Ajax渲染出来的; (Server Side Render)服务器端渲染;
    • 刚开始的时候加载速度可能比较慢;项目开发完毕之后,可以单独对首屏页面的加载时间做优化;
    • 页面复杂度比较高,对程序员能力要求较高;

原生实现SPA

使用 component 标签的:is属性来切换组件

总结:单页面应用程序中,实现组件切换的根本技术点,就是 监听 window.onhashchange 事件;

路由

什么是路由:路由 就是 对应关系;

  1. 后端路由的定义:URL地址 到 后端 处理函数之间的关系;
  2. 前端路由的定义:hash 到 组件 之间的对应关系;
  3. 前端路由的目的:为了实现单页面应用程序的开发;
  4. 前端路由的三个组成部分:
    • 链接
    • 组件
    • 链接 和 组件之间的对应关系

在 vue 中使用 vue-router【重点】

  1. 安装导入并注册路由模块:
    • 运行 npm i vue-router -S 安装路由模块
    • 在 index.js 中导入并注册路由模块
     // 导入路由模块
         import VueRouter from 'vue-router'
         // 注册路由模块(把路由模块安装到Vue上)
         Vue.use(VueRouter)
  1. 创建路由链接:
<!-- router-link 就是 第一步,创建 路由的 hash 链接的 -->
<!-- to 属性,表示 点击此链接,要跳转到哪个 hash 地址, 注意:to 属性中,大家不需要以 # 开头 -->
<router-link to="/home">首页</router-link>
<router-link to="/movie">电影</router-link>
<router-link to="/about">关于</router-link>

3.创建并在 index.js 中导入路由相关的组件:

import Home from './components/Home.vue'
import Movie from './components/Movie.vue'
import About from './components/About.vue'

4.创建路由规则

// 创建路由规则(对应关系)
const router = new VueRouter({ // 配置对象中,要提供 hash 地址 到 组件之间的 对应关系
  routes: [ // 这个 routes 就是 路由 规则 的数组,里面要放很多的对应关系
    // { path: 'hash地址', component: 配置对象 }
    { path: '/home', component: Home },
    { path: '/movie', component: Movie },
    { path: '/about', component: About }
  ]
})
// 创建的 router 对象,千万要记得,挂载到 vm 实例上
const vm = new Vue({
  el: '#app',
  render: c => c(App),
  router // 把 创建的路由对象,一定要挂载到 VM 实例上,否则路由不会生效
})

5.在页面上放路由容器


<!-- 这是路由的容器,将来,通过路由规则,匹配到的组件,都会被展示到这个 容器中 也就是切换的内容会在下面的这个区域进行显示 -->
<router-view></router-view>
或者是直接写成是单闭合标签
<router-view />

路由规则的匹配过程

  1. 用户点击 页面的 路由链接router-link,点击的一瞬间,就会修改 浏览器 地址栏 中的 Hash 地址;
  2. 当 hash 地址被修改以后,会立即被 vue-router 监听到,然后进行 路由规则的 匹配;最终,找到 要显示的组件;
  3. 当 路由规则匹配成功以后,就找到了 要显示的 组件,然后 把 这个组件,替换到 页面 指定的 路由容器router-view 中

设置路由高亮的两种方式

  1. 通过路由默认提供的router-link-active, 为这个类添加自己的高亮样式即可;
<style lang="less" scoped>
.router-link-active {
  color: red;
  font-weight: bold;
}
</style>

  1. 通过路由构造函数,在传递路由配置对象的时候,提供 linkActiveClass 属性,来覆盖默认的高亮类样式;
// 3. 创建路由实例对象
const router = new VueRouter({
  routes: [
    // 路由?  就是对应关系
    // 前端路由? hash => 组件 之间的对应关系
    // vue 中路由的格式     { path, component }
    // path 路由hash地址中,路径必须以 / 开头,而且必须是小写,而且不能带空格
    { path: '/home', component: Home },
    { path: '/movie', component: Movie },
    { path: '/about', component: About },
    { path: '/me', component: Me }
  ],
  linkActiveClass: 'my-active' // 如果大家做项目时候, 用到的 UI 组件库中,提供了默认的高亮效果
})


//在页面上的显示类名如下:

.my-active {
  color: #007ACC;
  font-weight: 700;
}

嵌套路由(子路由)

  1. 在对应的路由组件中,新增 router-link 路由链接;
  2. 创建 router-link 对应要显示的组件;
  3. 在对应的路由规则中,通过 children 属性,定义子路由规则:
const router = new VueRouter({
  routes: [
    // 路由?  就是对应关系
    // 前端路由? hash => 组件 之间的对应关系
    // vue 中路由的格式     { path, component }
    // path 路由hash地址中,路径必须以 / 开头,而且必须是小写,而且不能带空格
    // 在路由规则中,通过 redirect 属性,指向一个新地址,就能够实现路由的重定向
    { path: '/', redirect: '/home' },
    { path: '/home', component: Home },
    { path: '/movie', component: Movie },
    // 在某个路由规则中,如何嵌套子路由规则?    path 和 component 平级,还有个 children 属性
    // children 属性 是一个数组, 作用,就是来嵌套子路由规则的
    {
      path: '/about',
      component: About,
      redirect: '/about/tab1',
      children: [
        { path: '/about/tab1', component: Tab1 },
        { path: '/about/tab2', component: Tab2 }
      ]
    },
    { path: '/me', component: Me }
  ],
  linkActiveClass: 'my-active' // 如果大家做项目时候, 用到的 UI 组件库中,提供了默认的高亮效果
})

路由传参

通过属性绑定实现路由传参,也就是在路由规则的参数项之前加冒号实现路由传参

  <!-- 当router-link的to地址,要动态进行拼接的时候,那么,一定要把 to 设置成属性绑定的形式 -->
      <router-link v-for="item in mlist" :key="item.id" :to="`/mdetail/${item.id}/${item.name}`" tag="li">{{item.name}}</router-link>

但是还是可能会出现参数不固定的情况

const router = new VueRouter({
  routes: [
    { path: '/', component: MovieList },
    // 把路由规则中, 参数项位置,前面加上 : 表示这是一个参数项
    // props: true 表示,为当前路由规则,开启 props 传参
    { path: '/mdetail/:id1/:name2', component: MovieDetail, props: true }
  ]
})

  1. 可以在组件中,直接使用this.route.params 来获取参数;【写起来太麻烦,不推荐】
this.route是一个路由的参数对象, this.$router是路由的导航对象
  2. 也可以开启路由的 props 传参,来接收路由中的参数;【推荐方式】
    1. 在需要传参的路由规则中,添加 props: true
 { path: '/movie/:type/:id', component: movie, props: true }
  1. 在 对应的组件中,定义 props 并接收路由参数
    const movie = {
                template: '<h3>电影组件 --- {{type}} --- {{id}}</h3>', // 使用参数
                props: ['type', 'id'] // 接收参数
              }

命名路由

什么是命名路由: 就是为路由规则,添加了一个 name ;

  1. 什么是命名路由
  2. router-link中通过路由名称实现跳转
  3. 命名路由使用 params属性传参
 <!-- 使用 命名路由实现跳转 -->
       <router-link v-for="item in mlist" :key="item.id" :to="{name: 'moviedetail', params: {id1: item.id, name2:item.name}}" tag="li">{{item.name}}</router-link> 

编程式(JS)导航

之前所学的router-link是标签跳转;

除了使用router-link是标签跳转之外,还可以使用Javascript来实现路由的跳转;

  1. 什么是编程式导航
    • 使用vue-router提供的JS API实现路由跳转的方式,叫做编程式导航;
  2. 编程式导航的用法

//跳转到指定的路由规则中

  • this.$router.push('路径的地址')

//可以前进和后退

  • this.$router.go(n)

//可以前进

  • this.$router.forward()

//可以后退

  • this.$router.back()

关于路由的方法的总结

  • this.$route是路由参数对象
  • this.$router是路由导航对象
  • vm实例上的router属性,是挂载路由对象的
  • 在new VueRouter(/配置对象/)的时候,配置对象中,有一个routes属性,是创建路由规则的
  • router单独的写在 vm实例对象里面,表示挂载路由

path 是要匹配的hash值, component 要展示的组件 redirect 要重定向的路由 props 开启props传参 name 命名路由 children 嵌套子路由

路由导航守卫

  • 案例需求:只允许登录的情况下访问 后台首页 ,如果不登录,默认跳转回登录页面;
  • API语法:
    // 参数1:是要去的那个页面路由相关的参数
      // 参数2:从哪个页面即将离开
      // 参数3:next 是一个函数,就相当于 Node 里面 express 中的 next 函数
      // 注意: 这里的 router 就是 new VueRouter 得到的 路由对象
      router.beforeEach((to, from, next) => { /* 导航守卫 处理逻辑 */ })

案例核心代码:

// 通过 路由导航守卫, 控制有权限页面的访问, 只有登录以后,才允许访问高级的页面
router.beforeEach((to, from, next) => {
  // to.path 表示我们下一刻要访问哪个地址
  // console.log(to)
  // from.path 表示我们上一刻,所访问的是哪个地址
  // console.log(from)

  // next() 直接调用,表示放行
  // next()

  // 如果 要访问的地址,是 /login, 证明用户要去登录, 没有必要拦截,直接放行
  if (to.path === '/login') return next()
  // 如果用户访问的不是 登录页面,则 先尝试从sessionStorage中获取 token 令牌字符串
  const tokenStr = window.sessionStorage.getItem('token')
  // 如果没有 token 令牌,则 强制用户跳转到登录页
  if (!tokenStr) return next('/login')
  // 如果有令牌,则直接放行
  next()
})

关于token验证问题

token是一个令牌,是服务器端发送过来的客户端必须通过登录,才能获取这个令牌,

watch 监听

  • watch 监听的特点:监听到某个数据的变化后,侧重于做某件事情;

    • 只要被监听的数据发生了变化,会自动触发 watch 中指定的处理函数;
  • 案例:登录 密码 的长度检测

  • 密码长度小于8位,字体为红色;大于等于8位,字体为黑色;

export default {
  data() {
    return {
      uname: '',
      upwd: ''
    }
  },
  // watch 是监听 data 中数据的变化, 侧重于做某件事件
  watch: {
    upwd(newVal, oldVal) {
      if (newVal.length < 8) {
        this.$refs.pwdDOM.style.color = 'red'
      } else {
        this.$refs.pwdDOM.style.color = ''
      }
    }
  }
}

computed 计算属性

计算属性特点:同时监听多个数据的变化后,侧重于得到一个新的值;

  • 只要依赖的任何一个数据发生了变化,都会自动触发计算属性的重新求值;
export default {
  data() {
    return {
      firstname: '',
      lastname: ''
    }
  },
  // 计算属性
  computed: {
    // 定义一个计算属性,叫做 fullName
    // 注意: 所有的计算属性,在定义的时候, 都要被定义为 function,
    // 但是,在页面上使用计算属性的时候, 是直接当作普通的 变量 来使用的,而不是当作方法去调用的!!!
    // 特点:只要计算属性的 function 中,依赖的 任何一个数据发生了变化,都会对这个计算属性,重新求值
    fullName: function() {
      return this.firstname + '-' + this.lastname
    }
  }
}

应用

如果页面需要访问一个数据,这个数据比较复杂,是需要通过其他data经过复杂步骤制作出来的,那么就可以通过“计算属性”简化获得该数据

补充:Vue本身支持模板中使用复杂表达式表现业务数据,但是这会使得模板内容过于杂乱,如果确有需求,可以通过computed计算属性实现

与methods方法的区别:

computed计算属性本身有“缓存”,在关联的data没有变化的情况下,后续会使用缓存结果,节省资源

methods方法没有缓存,每次访问 方法体 都需要加载执行,耗费资源

使用 vue-cli 快速创建 vue 项目

为什么要使用 vue-cli 创建项目:

  • 在终端运行一条简单的命令,即可创建出标准的 vue 骨架项目;
  • 不必自己手动搭建 vue 项目基本结构,省时省力;
  • 不必关心 webpack 如何配置,只关注于项目代码的开发;

webpack 中 省略文件后缀名 和配置 @ 路径标识符

省略文件扩展名:

  • 打开 webpack.config.js,在导入的配置对象中,新增 resolve 节点;
  • 在 resolve 节点中,新增 extensions 节点:
resolve: {
    // resolve 节点下的 extensions 数组中,可以配置,哪些扩展名可以被省略
    extensions: ['.js', '.vue', '.json']
}

修改完配置以后,重新运行 npm run dev 查看效果;

配置 @ 指向 src 目录:

resolve: {
    alias: {
      '@': path.join(__dirname, './src') // 让 @ 符号,指向了 项目根目录中的 src
    }
  }

基础知识六

自定义指令

全局自定义指令

  • 概念:在全局任何组件中都可以被调用的自定义指令,叫做全局自定义指令;
  • 语法:Vue.directive('全局自定义指令名称', { /* 自定义指令配置对象 */ })
// Vue.directive('全局指令名称', { /*指令的配置对象*/  })

// 注意:自定义指令名称之前,不需要手动添加 v- 前缀
Vue.directive('red', {
  // 只要指令被解析指令了,就会优先调用指令中的 bind 方法
  bind(el) {
    // 只要bind被指向了,那么el,就是它所绑定到的 UI 元素
    // el 是原生DOM对象,也正是因为他是原生的DOM对象,所以他才可以通过style.color的方式修改样式
    
    el.style.color = 'red'
  }
})

私有自定义指令

概念:只有指令所属的组件中,才可以被正常调用的指令,叫做私有自定义指令;

  // 私有自定义指令的定义节点
  directives: {
    // 指令名称: { /配置对象/ }
    blue: {
      bind(el) {
        el.style.color = 'blue'
      }
    }
  }

指令配置对象中 bind 和 inserted 的区别

  • bind 方法:
    • 绑定当前指令的元素,在内存中被创建的时候就会被立即调用;
    • 推荐把样式相关的设置,都定义在 bind 方法中;
  • inserted 方法:
    • 绑定当前指令的元素,被插入到DOM树之后才会被调用;
    • 推荐把行为相关的设置,都定义在 inserted 方法中;
  • 演示 bind 和 inserted 的区别:
    • 在终端中打印 el.parentNode 即可; 在bind中输出的el.parentNode为null, 但是在inserted中输出的是他的父节点
Vue.directive('focus', {
  // bind 表示指令第一次被解析执行时候调用,此时,这个 DOM 元素,还没有被append到父节点中;
  // 此时只是在内存中存储着,所以还你没有渲染到页面上
  bind(el) {
    // el.focus()
    // console.log(el.parentNode),null
  },
  // inserted 会在元素被插入到父节点之后,执行,此时已经渲染到页面之上了
  inserted(el) {
    // 定义 文本框获得焦点的指令,只能通过 inserted 来实现
    // 因为 bind方法 和 inserted方法 的执行时机不一样
    el.focus()
  }
})

// 总结:如果只是单纯的为元素设置样式,尽量写到 bind 中
// 如果要设置JS行为,比如文本框获取焦点,这种行为,尽量写到 inserted 中

自定义指令传参

Vue.directive('color', {
  // 通过 形参中的 binding 来接收指令传递过来的数据
  // 所有通过=传过来的值都是在binding中存储着
  // 传递过来的参数,是 binding.value 属性
  bind(el, binding) {
    // console.log(binding.value)
    el.style.color = binding.value
  }
})

插槽

定义:定义子组件的时候,在子组件内部刨了一个坑,父组件想办法往坑里填内容;

单个插槽(匿名插槽)

  1. 定义插槽:在子组件作用域中,使用 定义一个插槽;
  2. 使用插槽:在父作用域中使用带有插槽的组件时,组件内容区域中的内容,会插入到插槽中显示;
  3. 注意:在一个组件的定义中,只允许出现一次匿名插槽

Son.vue子组件代码:

<template>
    <div>
        <h4>这是子组件</h4>
        <p>哈哈哈</p>
        <!-- 没有name属性的插槽,称为是匿名插槽 -->
        <slot></slot>
        <!-- 注意:在同一个组件中,只允许定义一次插槽 -->
        <!-- <slot></slot> -->
        <p>heiehei</p>
    </div>
</template>

APP.vue主组件代码展示:

<template>
    <div>
        <h1>这是父组件</h1>
        <hr>
        <!-- 在子组件的内部放置内容 -->
        <!-- 默认情况下载组件内容中,定义的信息都会被显示到匿名插槽中 -->
        <my-son>
            <img src="./images/土拨鼠啊.gif" alt="">
            <img src="./images/老王.png" alt="">
            <h3>6666</h3>
        </my-son>
    </div>
</template>

多个插槽(具名插槽)

  1. 定义具名插槽:使用 name 属性为 slot 插槽定义具体名称;
<template>
    <div>
        <h1>这是子组件</h1>
        <p>啊,五环</p>
        <!-- 匿名插槽 -->
         <slot></slot>
        <p>你比四环多一环</p>
        <!-- 具名插槽 -->
        <slot name="s2"></slot>
        <p>啊,五环</p>
        <slot name="s3"></slot>
        <p>你比七环少两环</p> 
    </div>
</template>
  1. 使用具名插槽:在父作用域中使用带有命名插槽的组件时,需要为内容指定 slot="插槽name" 来填充到指定名称的插槽;
<template>
  <div>
    <h1>这是父组件</h1>
    <hr />
    <my-son>
      <!-- 默认情况下指定的元素会被插入到匿名插槽中 -->
      <img src="../03默认插槽/images/一脸懵逼表情包.jpg" alt />
      <img slot="s2" src="../03默认插槽/images/土拨鼠啊.gif" alt />
      <img src="../03默认插槽/images/老王.png" alt />
      <img slot="s3" src="../03默认插槽/images/擅用百度.jpg" alt="">
    </my-son>
  </div>
</template>

作用域插槽

  1. 定义作用域插槽:在子组件中,使用 slot 定义插槽的时候,可以通过 属性传值 的形式,为插槽传递数据,
<template>
    <div>
        <h4>这是子组件</h4>
        <slot smsg="hello Vue" sinfo="你好"></slot>
        <p>
            ~~~~~~~~~~~~~~
        </p>
        <slot name="s2" :umsg="m1" :uinfo="m2">

        </slot>
    </div>
</template>
<script>
export default {
    data(){
        return {
            m1:'abcd',
            m2:'123456'
        }
    }
}
</script>
  1. 使用作用域插槽:在父作用域中,通过定义 slot-scope="scope" 属性,接收并使用 插槽数据;
  2. 注意:同一组件中不同插槽的作用域,是独立的!
<template>
    <div>
        <h1>这是父组件</h1>
        <hr>
        <my-son>
            <h6 slot-scope="scope">{{scope}}</h6>
            <!-- <h3 slot="s2" slot-scope="scope">{{scope}}</h3>
            <h3 slot="s2" slot-scope="scope">{{scope}}</h3> -->
            <!--  如果要接收作用域插槽中的数据,而且渲染为多个标签,
            则必须在多个标签之外,包裹一个父元素,进行接收插槽中的数据
            -->
            <!-- 注意 template只起到包裹元素的作用,不会被渲染为任何标签-->
            <template slot="s2" slot-scope="scope">
                <h3>{{scope.uinfo}}</h3>
                <h3>{{scope.umsg}}</h3>
            </template>
        </my-son>
    </div>
</template>

element-ui

待更新....

1.element-ui 是 饿了么 前端团队,开源出来的一套 Vue 组件库;

2.完整引入 Element-UI 组件:

1. 运行 yarn add element-ui -S 安装组件库
2. 在 index.js 中,导入 element-ui 的包、配套样式表、并且安装到Vue上:
// 导入 element-ui 这个包
import ElementUI from 'element-ui'
// 导入 配套的样式表
import 'element-ui/lib/theme-chalk/index.css'

// 把 element-ui 安装到 Vue 上
Vue.use(ElementUI)

3.按需导入和配置 Element-UI :

  1. 运行 npm i babel-plugin-component -D 安装支持按需导入的模块;
  2. 打开 .babelrc 配置文件,修改如下:
{
  "presets": ["@babel/preset-env"],
  "plugins": ["@babel/plugin-transform-runtime", "@babel/plugin-proposal-class-properties", 
    + [
    + "component",
    + {
    +   "libraryName": "element-ui",
    +   "styleLibraryName": "theme-chalk"
    + }
  ]]
}

使用vue-cli快速初始化vue项目

$ npm install -g vue-cli        // 全局安装脚手架工具
$ vue init webpack my-project   // 初始化项目
$ cd my-project                 // 切换到项目根目录中
$ npm install                   // 安装依赖包
$ npm run dev                   // 一键运行项目

ESLint 语法检查规范

  1. 声明但是未使用的变量会报错
  2. 空行不能连续大于等于2
  3. 在行结尾处,多余的空格不允许
  4. 多余的分号,不允许
  5. 字符串要使用单引号,不能使用双引号
  6. 在方法名和形参列表的小括号之间,必须有一个空格
  7. 在单行注释的 // 之后,必须有一个空格
  8. 在每一个文件的结尾处,必须有一个空行
  9. import语句必须放到最顶部
  10. etc...

如何配置VSCode帮我们自动把代码格式为需要的样子

  1. 在安装 Vetur 插件
  2. 安装 Prettier - Code formatter 插件
  3. 打开 vs code 的 文件 -> 首选项 -> 设置,在用户设置最底部的合适位置,添加如下配置:
// 使用 ESLint 规则
"prettier.eslintIntegration": false,
// 每行文字个数超出此限制将会被迫换行
"prettier.printWidth": 100,
// 使用单引号替换双引号
"prettier.singleQuote": true,
// 格式化文件时候,不在每行结尾添加分号
"prettier.semi": false,
// 设置 .vue 文件中,HTML代码的格式化插件
"vetur.format.defaultFormatter.html": "prettier"
  1. 重启VS Code让插件和配置生效!
  2. 打开vue-cli生成的项目中,.eslintrc.js配置文件,找到 rules 节点, 将如下语法规则,粘贴到 rules 配置中: