Vue.js入门宝典

WHY VUE.JS?

vue.js是什么?

Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一 方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。

渐进式框架:Progressive,说明vue.js的轻量,是指一个前端项目可以使用vue.js一两个特性也可以整个项目都用 vue.js。

自底向上逐层应用:作为渐进式框架要实现的目标就是方便项目增量开发。

参考:https://cn.vuejs.org/v2/guide/

Vue.js与ECMAScript

Vue 不支持 IE8 及以下版本,因为 Vue 使用了 IE8 无法模拟的 ECMAScript 5 特性。

什么是ECMAScript?

ECMAScript(简称ES)是一种规范,我们平常所说的Js/Javascript是ECMAScript的实现,早期主要应用的ES3,当前主流浏览器都支持ES5、ES6,ES8已于2017年发布。

  • ES6:http://www.ecma-international.org/ecma-262/6.0/
  • ES7:http://www.ecma-international.org/ecma-262/7.0/

Vue.js的使用

  1. 直接CDN引入

    <!-- 开发环境版本,包含了有帮助的命令行警告 --> 
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <!-- 生产环境版本,优化了尺寸和速度 -->
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    
  2. 下载和引入

    开发环境 https://vuejs.org/js/vue.js 
    生产环境 https://vuejs.org/js/vue.min.js
    
  3. NPM安装

    • 使用Npm管理依赖,使用webpack和Vue-CLI打包工具对vue.js应用打包。**大型应用推荐此方案。 **

Vue-CLI脚手架:使用vue.js官方提供的CLI脚本架很方便去创建vue.js工程雏形

Vue.js有哪些功能?

  1. 声明式渲染
    • Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统
    • 比如:使用vue.js的插值表达式放在Dom的任意地方, 差值表达式的值将被渲染在Dom中。
  2. 条件与循环
    • dom中可以使用vue.js提供的v-if、v-for等标签,方便对数据进行判断、循环
  3. 双向数据绑定
    • Vue 提供v-model 指令,它可以轻松实现Dom元素和数据对象之间双向绑定,即修改Dom元素中的值自动修改绑定的数据对象,修改数据对象的值自动修改Dom元素中的值。
  4. 处理用户输入
    • 为了让用户和你的应用进行交互,我们可以用 v-on 指令添加一个事件监听器,通过它调用在 Vue 实例中定义的方法
  5. 组件化应用构建
    • vue.js可以定义一个一个的组件,在vue页面中引用组件,这个功能非常适合构建大型应用。

Vue.js基础

MVVM模式

vue.js是一个MVVM的框架,理解MVVM有利于学习vue.js。

  • MVVM拆分解释为:

    • Model:负责数据存储
      • 数据可能是我们固定的死数据,更多的是来自我们服务器,从网络上请求下来的数据
    • View:负责页面展示
      • 在前端开发中,通常就是DOM层。主要是给用户展示各种信息。
    • View Model:负责业务逻辑处理(比如Ajax请求等),对数据进行加工后交给视图展示
  • MVVM要解决的问题是将业务逻辑代码与视图代码进行完全分离,使各自的职责更加清晰,后期代码维护更加简单

  • 用图解的形式分析Ajax请求回来数据后直接操作Dom来达到视图的更新的缺点,以及使用MVVM模式是如何

    来解决这个缺点的

从上图看出,VM(ViewModel)可以把view视图和Model模型解耦合,VM的要做的工作就是vue.js所承担的。一方面它实现了Data Binding,也就是数据绑定,将Model的改变实时的反应到View中。另一方面它实现了DOM Listener,也就是DOM监听,当DOM发生一些事件(点击、滚动、touch等)时,可以监听到,并在需要的情况下改变对应的Data。

入门程序

环境准备

虚拟主机配置

 server {
        listen       80;
        server_name  localhost;   
        ssi on;   
       	ssi_silent_errors on;
       	location / {
       		alias   G:/dev_workspace/xc-education/xc-ui-pc-static-portal/; //静态文件的位置,绝对路劲
       		index  index.html;
       	}          
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

hello world

G:/dev_workspace/xc-education/xc-ui-pc-static-portal目录中创建vuetest目录,并且在目录下创建vue_01.html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>vue.js测试程序</title>
    <script src="vue.min.js"></script>
</head>
<body>
<!--实现在body区域显示一个hello world vue.js名称-->
<div id="app">
    {{name}}<!--相当于MVVM的view视图-->
</div>
</body>
<script>
    //编写MVVM中的model部分及VM(ViewModel)部分
    var VM = new Vue({
        el:'#app',//vm接管了app区域的管理
        data:{//model数据
            name:'hello world vue.js'
        }
    });
</script>
</html>

浏览器测试:http://localhost/vuetest/vue_01.html

代码编写步骤:

  1. 定义html,引入vue.js
  2. 定义app div,此区域作为vue的接管区域
  3. 定义vue实例,接管app区域。
  4. 定义model(数据对象)
  5. VM完成在app中展示数据

基础语法

插值操作

Mustache

将data中的文本数据,插入到HTML中,我们可以像下面这样来使用,并且数据是响应式的

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>胡子语法</title>
    <script src="vue.min.js"></script>
</head>
<body>
<div id="app"> <!--view-->
   <h2>hello {{name}}</h2> <!--插入到标签中-->
    <h3>{{num * 2}}</h3> <!--可以是一个表达式-->
</div>
</body>
<script>
   var VM = new Vue({
       el:'#app',
       data:{  //model
           name:"韩信",
           num: 12
       }
   });
</script>
</html>

v-once

在某些情况下,我们可能不希望界面随意的跟随改变,这个时候,我们就可以使用一个Vue的指令v-once

  • 该指令后面不需要跟任何表达式
  • 该指令表示元素和组件(组件后面才会学习)只渲染一次,不会随着数据的改变而改变。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>v-once</title>
    <script src="vue.min.js"></script>
</head>
<body>
<div id="app"> <!--view-->
    <h1 v-once>{{message}}</h1>  <!--F12进入浏览器控制台改变值,不会重新渲染-->

</div>
</body>
<script>
   var VM = new Vue({
       el:'#app',
       data:{  //model
           message: '李白'     
       }
   });
</script>
</html>

v-html

某些情况下,我们从服务器请求到的数据本身就是一个HTML代码,如果我们直接通过插值表达式来输出,会将HTML代码也一起输出。但是我们可能希望的是按照HTML格式进行解析,并且显示对应的内容。可以使用v-html指令,该指令后面往往会跟上一个string类型,会将string的html解析出来并且进行渲染

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>v-html</title>
    <script src="vue.min.js"></script>
</head>
<body>
<div id="app"> <!--view-->
    <span v-html="url"></span>
</div>
</body>
<script>
   var VM = new Vue({
       el:'#app',
       data:{  //model
           url: '<a href="https://www.baidu.com">百度一下</a>'
       }
   });
</script>
</html>

v-text

v-text作用和Mustache比较相似:都是用于将数据显示在界面中,v-text`通常情况下,接受一个string类型

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>v-text</title>
    <script src="vue.min.js"></script>
</head>
<body>
<div id="app"> <!--view-->
   <h2 v-text="name"></h2> 
</div>
</body>
<script>
   var VM = new Vue({
       el:'#app',
       data:{  //model
           name:"韩信"     
       }
   });
</script>
</html>

v-pre

v-pre用于跳过这个元素和它子元素的编译过程,用于显示原本的Mustache语法。

如下面的代码

  • 第一个h2元素中的内容会被编译解析出来对应的内容
  • 第二个h2元素中会直接显示{{message}}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>v-text</title>
    <script src="vue.min.js"></script>
</head>
<body>
<div id="app"> <!--view-->
   <h2>{{name}}</h2> 
   <h2 v-pre>{{name}}</h2> 
</div>
</body>
<script>
   var VM = new Vue({
       el:'#app',
       data:{  //model
           name:"韩信"     
       }
   });
</script>
</html>

v-cloak

在某些情况下,我们浏览器可能会直接显然出未编译的Mustache标签。可以使用v-cloak进行隐藏显示

绑定属性

v-bind

v-bind用于绑定一个或多个属性值,或者向另一个组件传递props值,在开发中v‐bind可以给dom对象绑定例如图片的链接src、网站的链接href、动态绑定一些类、样式等等

 <img v‐bind:src="imageSrc"> 
 <div v‐bind:style="{ fontSize: size + 'px' }"></div> 
<!--语法糖,使用更加简洁-->
 <img :src="imageSrc"> <div :style="{ fontSize: size + 'px' }"></div>

计算属性

在模板中可以直接通过插值语法显示一些data中的数据,但是在某些情况,我们可能需要对数据进行一些转化后再显示,或者需要将多个数据结合起来进行显示比如我们有firstNamelastName两个变量,我们需要显示完整的名称。但是如果多个地方都需要显示完整的名称,我们就需要写多个{{firstName}} {{lastName}}

我们可以将上面的代码换成计算属性:计算属性是写在实例的computed选项中的

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>计算属性</title>
    <script src="vue.min.js"></script>
</head>
<body>
<div id="app"> <!--view-->
    <h2>插值表达: {{firstName}}{{lastName}}</h2>
    <h2>计算属性:{{fullName}}</h2>

</div>
</body>
<script>
   var VM = new Vue({
       el:'#app',
       data: {  //model
           firstName: '李白',
           lastName: "韩信"
       },
       computed:{
           fullName(){
               return this.firstName+"--"+this.lastName
           }
       }
   });
</script>

计算属性的复杂操作

计算属性中也可以进行一些更加复杂的操作,比如下面的例子:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>计算属性</title>
    <script src="vue.min.js"></script>
</head>
<body>
<div id="app"> <!--view-->
    <h2>书籍总价:{{totalPrice}}</h2>
</div>
</body>
<script>
   var VM = new Vue({
       el:'#app',
       data: {  //model 
           books:[
               {name:'Java编程思想',price:99,count:3},
               {name:'Vue.js程序设计',price:89,count:2},
               {name:'Linux入门',price:122,count:5}
           ]
       },
       computed:{           
           totalPrice(){
              //高级函数
               return this.books.reduce((total,b)=>{
                   return total + b.price * b.count
               },0)
           }
       }
   });
</script>
</html>

计算属性的setter和getter

每个计算属性都包含一个getter和一个setter,前面默认使用getter来读取。在某些情况下,你也可以提供一个setter方法(不常用)。

<div id="app"> <!--view-->
    <h2>插值表达: {{firstName}}{{lastName}}</h2>
    <h2>计算属性:{{fullName}}</h2>
</div>

 var VM = new Vue({
       el:'#app',
       data: {  //model
           firstName: '李白',
           lastName: "韩信"       
       },
       computed:{
           fullNames:{
               get(){
                    console.log("调用了fullNames的get方法");
                   return this.firstName+"--"+this.lastName;
               },
               set(newValue){
                   console.log("调用了fullNames的set方法");
                   const names = newValue.split('');
                   this.firstName = names[0];
                   this.lastName = names[1];
               }

           }
       }
   });

计算属性的缓存

我们可能会考虑这样的一个问题:methodscomputed看起来都可以实现我们的功能,那么为什么还要多一个计算属性这个东西呢?
原因: 计算属性会进行缓存,如果多次使用时,计算属性只会调用一次。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>计算属性</title>
    <script src="vue.min.js"></script>
</head>
<body>
<div id="app"> <!--view-->
  
    <h2>methods:{{MfullName()}}</h2>
    <h2>methods:{{MfullName()}}</h2>
    <h2>methods:{{MfullName()}}</h2>
    <h2>computed:{{fullName}}</h2>
    <h2>computed:{{fullName}}</h2>
    <h2>computed:{{fullName}}</h2>

</div>
</body>
<script>
   var VM = new Vue({
       el:'#app',
       data: {  //model
           firstName: '李白',
           lastName: "韩信"       
       },
       methods:{
           MfullName(){
               console.log("执行了MfullName方法");
               return this.firstName+"--"+this.lastName
           }
       },
       computed:{
           fullName(){
               console.log("执行了fullName方法");
               return this.firstName+"--"+this.lastName
           }
       }
   });
</script>
</html>

事件监听

在前端开发中,我们需要经常和用于交互。这个时候,我们就必须监听用户发生的时间,比如点击、拖拽、键盘事件等等在Vue中使用v-on指令监听事件

v-on基础

这里,我们用一个监听按钮的点击事件,来简单看看v-on的使用,和v-on的语法糖使用,可以将事件指向一个在methods中定义的函数,写在vue的标签中的函数可以省略(),插值表达式中不能省略

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>v-on</title>
    <script src="vue.min.js"></script>
</head>
<body>

<div id="app">
    <h2>点击次数:{{counter}}</h2><!--相当于MVVM的view视图-->
    <button v-on:click="counter++">按钮点击1</button>
    <!--使用v-on的语法糖,还可以将事件指向一个在methods中定义的函数->
    <button @click="bthClick()">按钮点击2</button>
</div>
</body>
<script>
    //编写MVVM中的model部分及VM(ViewModel)部分
    var VM = new Vue({
        el:'#app',//vm接管了app区域的管理
        data:{//model数据
           counter: 0
        },
        methods:{
            bthClick(){
                console.info("调用了bthClick方法")
                this.counter++
            }
        }
    });
</script>
</html>

v-on参数

当通过methods中定义方法,以供@click调用时,需要注意参数问题:

  1. 如果该方法不需要额外参数,那么方法后的()可以不添加。但是注意:如果方法本身中有一个参数,那么会默认将原生事件event参数传递进去
  2. 如果需要同时传入某个参数,同时需要event时,可以通过$event传入事件。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>v-on</title>
    <script src="vue.min.js"></script>
</head>
<body>

<div id="app">
    <h2>点击次数:{{counter}}</h2><!--相当于MVVM的view视图-->
    <button @click="handleAdd">+1</button>
    <button @click="handleAddTen(10,$event)">+10</button>
</div>
</body>
<script>
    //编写MVVM中的model部分及VM(ViewModel)部分
    var VM = new Vue({
        el:'#app',//vm接管了app区域的管理
        data:{//model数据
           counter: 0
        },
        methods:{
            handleAdd(event){
                console.info(event)
                this.counter++
            },
            handleAddTen(count, event){
                console.info(count+"---"+event)
                this.counter+= count;

            }
        }
    });
</script>
</html>

v-on修饰符

在某些情况下,我们拿到event的目的可能是进行一些事件处理。Vue提供了修饰符来帮助我们方便的处理一些事件:

  • .stop - 调用 event.stopPropagation()。停止冒泡
  • .prevent - 调用 event.preventDefault()。阻止默认行为
  • .{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
  • .native - 监听组件根元素的原生事件
  • .once - 只触发一次回调。

条件判断

v-if、v-else-if、v-else

v-if、v-else-if、v-else,这三个指令与JavaScript的条件语句if、else、else if类似。可以根据表达式的值在DOM中渲染或销毁元素或组件,条件为false时,对应的元素以及其子元素不会渲染。

根本没有不会有对应的标签出现在DOM中。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>v-if、v-else-if、v-else</title>
    <script src="vue.min.js"></script>
</head>
<body>

<div id="app">
   <!--相当于MVVM的view视图-->
    <div v-if="score>=90">优秀</div>
    <div v-else-if="score>=80">良好</div>
    <div v-else-if="score>=60">及格</div>
    <div v-else>不及格</div>

</div>
</body>
<script>
    //编写MVVM中的model部分及VM(ViewModel)部分
    var VM = new Vue({
        el:'#app',//vm接管了app区域的管理
        data:{//model数据
            score: 98
        }
    });
</script>
</html>

v-show

v-show的用法和v-if非常相似,也用于决定一个元素是否渲染

v-if和v-show对比

  1. v-if当条件为false时,压根不会有对应的元素在DOM中。
  2. v-show当条件为false时,仅仅是将元素的display属性设置为none而已。

开发中如何选择呢?

  • 当需要在显示与隐藏之间切片很频繁时,使用v-show
  • 当只有一次切换时,通过使用v-if

循环遍历

v-for遍历数组

当我们有一组数据需要进行渲染时,我们就可以使用v-for来完成。v-for的语法类似于JavaScript中的for循环。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>v-for</title>
    <script src="vue.min.js"></script>
</head>
<body>

<div id="app">
    <!--相当于MVVM的view视图-->
    
    <li v-for="item in movies">{{item}}</li>
    ------------v-for 带下标---------------
      <!--value,index名字可以随便,和位置有关-->
    <li v-for="(item,index) in movies">{{index}}:{{item}}</li>
</div>
</body>
<script>
    //编写MVVM中的model部分及VM(ViewModel)部分
    var VM = new Vue({
        el:'#app',//vm接管了app区域的管理
        data:{//model数据
           movies:[
               '火影忍者','大话西游','盗梦空间','神雕侠侣'
           ]
        }
    });
</script>
</html>

v-for遍历对象

v-for可以用户遍历对象:比如某个对象中存储着你的个人信息,我们希望以列表的形式显示出来。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>v-for</title>
    <script src="vue.min.js"></script>
</head>
<body>

<div id="app">
    <!--相当于MVVM的view视图-->
    <ul>
    <!--value,key,index名字可以随便,和位置有关-->
       <li v-for="(value,key,index) in info">
           value:{{value}}--key:{{key}}--index:{{index}}
       </li>
    </ul>

</div>
</body>
<script>
    //编写MVVM中的model部分及VM(ViewModel)部分
    var VM = new Vue({
        el:'#app',//vm接管了app区域的管理
        data:{//model数据
            info: {
               name: '三月三',
                age: 19,
                height: 180
            }
        }
    });
</script>
</html>

组件的key属性

官方推荐我们在使用v-for时,给对应的元素或组件添加上一个:key属性。

为什么需要这个key属性呢(了解)?

  • 这个其实和Vue的虚拟DOM的Diff算法有关系。

当某一层有很多相同的节点时,也就是列表节点时,我们希望插入一个新的节点

  • 我们希望可以在B和C之间加一个F,Diff算法默认执行起来是这样的

  • 即把C更新成F,D更新成C,E更新成D,最后再插入E,是不是很没有效率?

  • 所以我们需要使用key来给每个节点做一个唯一标识

    • Diff算法就可以正确的识别此节点
    • 找到正确的位置区插入新的节点

所以一句话,key的作用主要是为了高效的更新虚拟DOM。

v-model

表单控件在实际开发中是非常常见的。特别是对于用户信息的提交,需要大量的表单。Vue中使用v-model指令来实现表单元素和数据的双向绑定。

案例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>v-model</title>
    <script src="vue.min.js"></script>
</head>
<body>

<div id="app">
    <!--相当于MVVM的view视图-->
    <input type="text" v-model="message"/>
    <h2>{{message}}</h2>
</div>
</body>
<script>
    //编写MVVM中的model部分及VM(ViewModel)部分
    var VM = new Vue({
        el:'#app',//vm接管了app区域的管理
        data:{//model数据
            message:''
        }
    });
</script>
</html>

案例的解析:

  1. 当我们在输入框输入内容时
  2. 因为input中的v-model绑定了message,所以会实时将输入的内容传递给message,message发生改变。
  3. 当message发生改变时,因为上面我们使用Mustache语法,将message的值插入到DOM中,所以DOM会发生响应的改变。
  4. 所以,通过v-model实现了双向的绑定。

v-model仅能在如下元素中使用:

  • input
  • select
  • textarea
  • components(Vue中的组件)

v-model原理

v-model其实是一个语法糖,它的背后本质上是包含两个操作:

  1. v-bind绑定一个value属性
  2. v-on指令给当前元素绑定input事件

也就是说下面的代码:等同于下面的代码:

<input type="text" v-model="message">
等同于
<input type="text" v-bind:value="message" v-on:input="message = $event.target.value">

修饰符

lazy修饰符:v-model.lazy

  • 默认情况下,v-model默认是在input事件中同步输入框的数据的也就是说,一旦有数据发生改变对应的data中的数据就会自动发生改变。
  • lazy修饰符可以让数据在失去焦点或者回车时才会更新:

number修饰符:v-model.number

  • 默认情况下,在输入框中无论我们输入的是字母还是数字,都会被当做字符串类型进行处理。但是如果我们希望处理的是数字类型,那么最好直接将内容当做数字处理。
  • number修饰符可以让在输入框中输入的内容自动转成数字类型

trim修饰符:v-model.trim

  • 如果输入的内容首尾有很多空格,通常我们希望将其去除

  • trim修饰符可以过滤内容左右两边的空格

组件化开发

认识组件化

什么是组件化?

任何一个人处理信息的逻辑能力都是有限的所以,当面对一个非常复杂的问题时,我们不太可能一次性搞定一大堆的内容。但是,我们人有一种天生的能力,就是将问题进行拆解。如果将一个复杂的问题,拆分成很多个可以处理的小问题,再将其放在整体当中,你会发现大的问题也会迎刃而解。

组件化也是类似的思想:如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展。但如果,我们讲一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了。

Vue组件化思想

组件化是Vue.js中的重要思想它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用。任何的应用都会被抽象成一颗组件树。

组件化思想的应用:

有了组件化的思想,我们在之后的开发中就要充分的利用它。尽可能的将页面拆分成一个个小的、可复用的组件。这样让我们的代码更加方便组织和管理,并且扩展性也更强。

注册组件

组件的使用分成三个步骤:

  1. Vue.extend()创建组件构造器
  2. Vue.component()注册组件并定义组件标签的名称
  3. 在Vue实例的作用范围使用组件。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vue组件化开发</title>
    <script src="vue.min.js"></script>
</head>
<body>

<div id="app">
   <!--3. 使用组件 -->
    <my-cpn></my-cpn>
</div>
</body>
<script>
    //1. Vue.extend()创建组件构造器
    const myComponent = Vue.extend({
        template: `
        <div>
            <h2>组件标题</h2>
            <p>我是组件中一个段落</p>
        </div>
        `
    });
    //2.Vue.component()注册组件并定义组件标签的名称
    Vue.component('my-cpn',myComponent);


    //编写MVVM中的model部分及VM(ViewModel)部分
    var VM = new Vue({
        el:'#app',//vm接管了app区域的管理
    });
</script>
</html>

注册组件步骤解析

Vue.extend()

调用Vue.extend()创建的是一个组件构造器。 通常在创建组件构造器时,传入template代表我们自定义组件的模板。该模板就是在使用到组件的地方,要显示的HTML代码。事实上,这种写法在Vue2.x的文档中几乎已经看不到了,它会直接使用下面我们会讲到的语法糖,但是在很多资料还是会提到这种方式,而且这种方式是学习后面方式的基础。

Vue.component()注册全局组件

调用Vue.component()是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称。所以需要传递两个参数:

  1. 注册组件的标签名
  2. 组件构造器

注意: 当我们通过调用Vue.component()注册组件时,组件的注册是全局的,这意味着该组件可以在任意Vue示例下使用。如果我们注册的组件是挂载在某个实例中, 那么就是一个局部组件

<div id="app">
   <!--相当于MVVM的view视图-->
    <my-cpn></my-cpn>
</div>
<div id="app1">
    <my-cpn></my-cpn>
</div>

<script>
    //1. Vue.extend()创建组件构造器
    const myComponent = Vue.extend({
        template: `
        <div>
            <h2>组件标题</h2>
            <p>我是组件中一个段落</p>
        </div>
        `
    });
    //2.Vue.component()注册组件并定义组件标签的名称
    //Vue.component('my-cpn',myComponent);

    //编写MVVM中的model部分及VM(ViewModel)部分
    var VM = new Vue({
        el:'#app',//vm接管了app区域的管理
        components:{
            //将组件构造器放在实例中,表明只在该实例下使用组件才生效
            'my-cpn': myComponent
        }
    });
    var app1 = new Vue({
        el:'#app1',//app1接管了app1区域的管理
    });
</script>


在Vue实例的作用范围使用组件。

我们来看下面我使用了三次而第三次其实并没有生效:

语法糖注册全局组件和局部组件

1. 全局组件:
Vue.component('my-cpn',{
    template: `
    <div>
    <h2>组件标题</h2>
    <p>我是组件中一个段落</p>
    </div>
    `
});
2. 局部组件
var app1 = new Vue({
	el:'#app1',//app1接管了app1区域的管理
	components:{
    	'my-cpn': {
   			 template: `
                <div>
                    <h2>组件标题</h2>
                    <p>我是组件中一个段落</p>
                </div>
    		 `
			}
	}
});
3. 使用模板id,创建局部组件
 <template id="child">
     <div>
       <h2>组件标题</h2>
       <p>我是组件中一个段落</p>    
     </div>
 </template>
var app1 = new Vue({
	el:'#app1',//app1接管了app1区域的管理
	components:{
    	'my-cpn': {
    	   //1. 可以直接使用ES6的字符串表达式创建
   			 template: `
                <div>
                    <h2>组件标题</h2>
                    <p>我是组件中一个段落</p>
                </div>
    		 `,
    		 //2. 也可以在外部定义一对<template id=“chid”> </template>包裹使用。并使用id选择器拿到DOM对象
    		  template: "#child"
			}
	}
});

组件数据存放

父组件和子组件

组件和组件之间存在层级关系,而其中一种非常重要的关系就是父子组件的关系

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>父组件和子组件</title>
    <script src="vue.min.js"></script>
</head>
<body>

<div id="app">
   <!--相当于MVVM的view视图-->
    <my-cpn></my-cpn>
</div>

</body>
<script>
    //1. 创建子组件构造器
    const childComponent = Vue.extend({
        template: `
        <div>
            <p>我是子组件中Child</p>
        </div>
        `
    });
    //2. 创建父组件构造器
    const parentComponent = Vue.extend({
        template: `
        <div>
           <p>我是父组件parent</p>
           <h2>我是父组件的标题</h2>
           <child-cpn></child-cpn>
        </div>
        `,
        components:{
            'child-cpn': childComponent
        }
    });
    //编写MVVM中的model部分及VM(ViewModel)部分
    var VM = new Vue({
        el:'#app',//vm接管了app区域的管理
        components:{
            //将组件构造器放在实例中,表明只在该实例下使用组件才生效
            'my-cpn': parentComponent
        }
    });
</script>
</html>

语法糖注册全局组件和局部组件:

1. 全局组件:
Vue.component('my-cpn',{
    template: `
    <div>
    <h2>组件标题</h2>
    <p>我是组件中一个段落</p>
    </div>
    `
});
2. 局部组件
var app1 = new Vue({
	el:'#app1',//app1接管了app1区域的管理
	components:{
    	'my-cpn': {
   			 template: `
                <div>
                    <h2>组件标题</h2>
                    <p>我是组件中一个段落</p>
                </div>
    		 `
			}
	}
});

组件数据存放

组件是一个单独功能模块的封装:这个模块有属于自己的HTML模板,也应该有属性自己的数据data。组件中的数据是保存在哪里呢?顶层的Vue实例中吗?我们先来测试一下,组件中能不能直接访问Vue实例中的data

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>组件数据存放</title>
    <script src="vue.min.js"></script>
</head>
<body>

<div id="app">
   <!--相当于MVVM的view视图-->
    <my-cpn></my-cpn>
</div>
</body>

<template id="myCpn">
    <div>我是{{message}}</div>
</template>
<script>

    //编写MVVM中的model部分及VM(ViewModel)部分
    var VM = new Vue({
        el:'#app',//vm接管了app区域的管理
        data: {
            message: "韩信"
        },
        components:{
            //局部组件语法糖
            'my-cpn': {
                template: '#myCpn'
            }
        }
    });

</script>
</html>

我们发现不能访问,而且即使可以访问,如果将所有的数据都放在Vue实例中,Vue实例就会变的非常臃肿。

结论:Vue组件应该有自己保存数据的地方。

**思考:**组件自己的数据存放在哪里呢?

组件对象也有一个data属性(也可以有methods等属性,下面我们有用到)只是这个data属性必须是一个函数
而且这个函数返回一个对象,对象内部保存着数据

<script>

    //编写MVVM中的model部分及VM(ViewModel)部分
    var VM = new Vue({
        el:'#app',//vm接管了app区域的管理
        data: {
            message: "韩信"
        },
        components:{
            //局部组件语法糖
            'my-cpn': {
                template: '#myCpn',
                data(){
                    return{
                        message: "李白"
                    }
                }
            }
        }
    });

</script>

**思考:**为什么data在组件中必须是一个函数呢?

首先,如果不是一个函数,Vue直接就会报错。其次,原因是在于Vue让每个组件对象都返回一个新的对象,因为如果是同一个对象的,组件在多次使用后会相互影响。

父子组件通信

子组件是不能引用父组件或者Vue实例的数据的。但是,在开发中,往往一些数据确实需要从上层传递到下层:
比如在一个页面中,我们从服务器请求到了很多的数据。其中一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示。这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件)将数据传递给小组件(子组件)。

如何进行父子组件间的通信呢?Vue官方提到通过props向子组件传递数据,通过事件向父组件发送消息

父级向子级传递

props基本用法

在组件中,使用选项props来声明需要从父级接收到的数据。

props的值有两种方式:

  1. 方式一:字符串数组,数组中的字符串就是传递时的名称。
  2. 方式二:对象,对象可以设置传递时的类型,也可以设置默认值等。

方式一:字符串数组,数组中的字符串就是传递时的名称。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>父级向子级传递</title>
    <script src="vue.min.js"></script>
</head>
<body>

<div id="app">
   <!--相当于MVVM的view视图-->
    <child-cpn :message="message"></child-cpn>
</div>
</body>


<template id="childCpn">
    <div>我是:{{message}}</div>
</template>
<script>

    //编写MVVM中的model部分及VM(ViewModel)部分
    var VM = new Vue({
        el:'#app',//vm接管了app区域的管理
        data: {
            message: "韩信"
        },
        components:{
            //局部组件语法糖
            'child-cpn': {
                template: '#childCpn',
                props:['message']
            }
        }
    });

</script>
</html>

方式二:对象,对象可以设置传递时的类型,也可以设置默认值等。当需要对props进行类型等验证时,就需要对象写法了。

验证都支持的数据类型

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol

子级向父级传递

props用于父组件向子组件传递数据,还有一种比较常见的是子组件传递数据或事件到父组件中。需要使用自定义事件来完成。

什么时候需要自定义事件呢?

当子组件需要向父组件传递数据时,就要用到自定义事件了。之前的v-on不仅仅可以用于监听DOM事件,也可以用于组件间的自定义事件。

自定义事件的流程:

  1. 在子组件中,通过$emit()来触发事件。
  2. 在父组件中,通过v-on来监听子组件事件。

看一个简单的例子:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>子级向父级传递实例</title>
    <script src="vue.min.js"></script>
</head>
<body>

<div id="app">
   <!--相当于MVVM的view视图-->
    <child-cpn @increment="changTotal" @decrement="changTotal"></child-cpn>
    {{total}}
</div>
</body>


<template id="childCpn">
    <div>
        <button @click="increment">+1</button>
        <button @click="decrement">-1</button>
    </div>
</template>
<script>

    //编写MVVM中的model部分及VM(ViewModel)部分
    var VM = new Vue({
        el:'#app',//vm接管了app区域的管理
        data: {
            total: 0
        },
        methods: {
            changTotal(counter){
                this.total = counter;
            }
        },
        components:{
            //局部组件语法糖
            'child-cpn': {
                template: '#childCpn',
                data(){
                    return {
                        counter :0
                    }
                },
                methods: {
                    //子组件实现+1操作
                    increment(){
                        this.counter++;
                        //注册监听事件increment,并传递值
                        this.$emit('increment',this.counter)
                    },
                    //子组件实现-1操作
                    decrement(){
                        this.counter--;
                        //注册监听事件decrement,并传递值
                        this.$emit('decrement',this.counter)
                    }
                }

            }
        }
    });

</script>
</html>

父子组件的访问

有时候我们需要父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问跟组件。

父组件访问子组件:使用$children$refs

子组件访问父组件:使用$parent

$children

  • this.$children是一个数组类型,它包含所有子组件对象。
  • 我们这里通过一个遍历,取出所有子组件的message状态。

$children的缺陷:

通过$children访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值。但是当子组件过多,我们需要拿到其中一个时,往往不能确定它的索引值,甚至还可能会发生变化。有时候,我们想明确获取其中一个特定的组件,这个时候就可以使用$refs

$refs

$refsref指令通常是一起使用的。首先,我们通过ref给某一个子组件绑定一个特定的ID。其次,通过this.$refs.ID就可以访问到该组件了。

<template id="parentCpn">
    <div>
        <child-cpn1 ref="child1"></child-cpn1>
        <child-cpn2  ref="child2"></child-cpn2>
        <button @click="showChildCpn">显示所有组件信息</button>
    </div>
</template>

showChildCpn(){   
    console.info(this.$ref.child1.message)
    console.info(this.$ref.child2.message)
}

$parent

如果我们想在子组件中直接访问父组件,可以通过$parent

注意事项:

尽管在Vue开发中,我们允许通过$parent来访问父组件,但是在真实开发中尽量不要这样做。子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了。如果我们将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题。另外,更不好做的是通过$parent直接修改父组件的状态,那么父组件中的状态将变得飘忽不定,很不利于我的调试和维护。

插槽slot

编译作用域

下面的代码是否最终是可以渲染出来的:isShow属性包含在组件中,也包含在Vue实例中。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>插槽slot:编译作用域</title>
    <script src="vue.min.js"></script>
</head>
<body>

<div id="app">
   <!--相当于MVVM的view视图-->
    <parent-cpn v-show="isShow"></parent-cpn>
</div>
</body>

<template id="parentCpn">
   <h2>我能不能显示出来?</h2>
</template>
<script>
    Vue.component('parent-cpn',{
        template: "#parentCpn",
        data(){
            return{
                isShow: false
            }
        }

    })
    //编写MVVM中的model部分及VM(ViewModel)部分
    var VM = new Vue({
        el:'#app',//vm接管了app区域的管理
        data:{
            isShow: true
        }

    });
</script>
</html>

最终可以渲染出来,也就是使用的是Vue实例的属性。

why?

官方给出了一条准则:**父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译。**而我们在使用 的时候,整个组件的使用过程是相当于在父组件中出现的。那么他的作用域就是父组件,使用的属性也是属于父组件的属性。因此,isShow使用的是Vue实例中的属性,而不是子组件的属性。

插槽的基本使用

匿名插槽
  1. 在子组件中,使用特殊的元素<slot>就可以为子组件开启一个插槽。
  2. 该插槽插入什么内容取决于父组件如何使用。
  3. <slot>中的内容表示,如果没有在该组件中插入任何其他内容,就默认显示该内容
<body>
 <div id="app">
    <h1> 我是父组件的消息:{{message}}</h1>
    <child></child>
    <hr>
    <child>
        <h2>我是插槽的替换内容h2</h2>
        <p>我是插槽的替换内容p</p>
    </child>
 </div>   

 <template id="child">
     <div>
        <h2>我是子组件Child</h2>
        <slot>我是插槽的默认内容</slot>
     </div>
   
 </template>

</body>
<script>    
    const VM = new Vue({
        el:"#app",
        data:{
            message: "Hello World!"
        },
        components:{
            "child": {
                template: "#child"
            }
        }
    })

</script>

在这里插入图片描述

具名插槽

当子组件的功能复杂时,子组件的插槽可能并非是一个。比如我们封装一个导航栏的子组件,可能就需要三个插槽,分别代表左边、中间、右边。那么,外面在给插槽插入内容时,如何区分插入的是哪一个呢?这个时候,我们就需要给插槽起一个名字,即使用具名插槽。

如何使用? 非常简单,只要给slot元素一个name属性即可,<slot name='myslot'></slot>

<body>
 <div id="app">
     <h1>没有传入如何内容</h1>
     <h2>如果没有传入内容,就使用子组件的默认的插槽内容</h2>
    <!-- 没有传入如何内容 -->
    <child></child>
    <hr>
    <h1>传入某一个内容</h1>
    <h2>如果传入某一个内容,就更改子组件对应的具名插槽内容,其他的使用默认的插槽</h2>
    <!-- 传入某一个内容 -->
    <child>
        <span slot="head">
            我是antd的头部
        </span>      
    </child>
    <hr>
    <h1>传入所有内容</h1>
    <h2>如果传入所有内容,就更改子组件对应的具名插槽内容</h2>
    <!-- 传入所有内容 -->
    <child>
      <span slot="head">
        我是antd的导航头部
    </span>
    <span slot="center">
        我是antd的内容部分
    </span>
    <span slot="footer">
        我是antd的底部
    </span>
    </child>
 </div>   
 <template id="child">
     <div>
        <h2>我是子组件Child</h2>
        <slot>我是插槽的默认内容</slot>
        <!-- 具名插槽 -->
        <slot name="head">我是头部</slot>
        <slot name="center">我是内容</slot>
        <slot name="footer">我是底部</slot>
     </div>
   
 </template>

</body>
<script>    
    const VM = new Vue({
        el:"#app",
        data:{
            message: "Hello World!"
        },
        components:{
            "child": {
                template: "#child"
            }
        }
    })
</script>

在这里插入图片描述

作用域插槽

作用域插槽是slot一个比较难理解的点,而且官方文档说的又有点不清晰。这里,我们用一句话对其做一个总结:父组件替换插槽的标签,但是内容由子组件来提供。

demo: 我们先提一个需求:子组件中包括一组数据,比如:pLanguages: [‘JavaScript’, ‘Python’, ‘Swift’, ‘Go’, ‘C++’],需要在多个界面进行展示:

  1. 某些界面是以水平方向一一展示的,
  2. 某些界面是以列表形式展示的,
  3. 某些界面直接展示一个数组内容在子组件,

希望父组件告诉我们如何展示,怎么办呢?利用slot作用域插槽就可以了

 <template id="child">
     <div>
        <h2>我是子组件Child</h2>
        <slot :data="pLanguages"></slot>
     </div>
 </template> 
<script>    
    const VM = new Vue({
        el:"#app",
        data:{
            message: "Hello World!"
        },
        components:{
            "child": {
                template: "#child",
                data(){
                    return {
                        pLanguages: ['JavaScript', 'Python', 'Swift', 'Go', 'C++']
                    }
                }
            }
        }
    })
</script>

在父组件使用我们的子组件时,从子组件中拿到数据:

  1. 我们通过<template slot-scope="slotProps">获取到slotProps属性,这里的slotProps只是一个临时变量,可以定义任何值
  2. 在通过slotProps.data就可以获取到刚才我们传入的data了
<div id="app">
 <child>
        <template slot-scope="slotProps">
            <ul>
                <li v-for="info in slotProps.data">{{info}}</li>
            </ul>
        </template>
    </child>
    <hr/>
    <!-- 2. 水平展示 -->
    <child>
        <template slot-scope="slotProps">
            <span v-for="info in slotProps.data">{{info}}   </span>
        </template>
    </child>
 </div>   

在这里插入图片描述

上面结果就验证了使用作用域插槽的作用:父组件替换插槽的标签,但是内容由子组件来提供。,可以拿到子组件的信息,让父组件为其展示

前端模块化

为什么要模块化

JavaScript原始功能

在网页开发的早期,js制作作为一种脚本语言,做一些简单的表单验证或动画实现等,那个时候代码还是很少的。
那个时候的代码是怎么写的呢?直接将代码写在

随着ajax异步请求的出现,慢慢形成了前后端的分离客户端需要完成的事情越来越多,代码量也是与日俱增。
为了应对代码量的剧增,我们通常会将代码组织在多个js文件中,进行维护。但是这种维护方式,依然不能避免一些灾难性的问题。

比如全局变量同名问题;另外,这种代码的编写方式对js文件的依赖顺序几乎是强制性的,但是当js文件过多,比如有几十个的时候,弄清楚它们的顺序是一件比较同时的事情。而且即使你弄清楚顺序了,也不能避免上面出现的这种尴尬问题的发生。

//aaa.js文件中韩信定义了一个变量flag=true
flag = true
//bbb.js文件中李白也喜欢用flag变量,并定义了false
flag = false
//main.js文件中,韩信想通过flag进行判断,完成开发
if(flag){
    console.info("我是野王")
}
//韩信后来发现代码不能正常运行,去检查自己的变量,发现确实为true,最后杯具发生了,韩信加班到2点还是没有找到问题出在哪里

匿名函数的解决方案

我们可以使用匿名函数来解决方面的重名问题在aaa.js文件中,我们使用匿名函数

(function(){
    var flag = true
})()

但是如果我们希望在main.js文件中,用到flag,应该如何处理呢?显然,另外一个文件中不容易使用,因为flag是一个局部变量。

使用模块作为出口

我们可以使用将需要暴露到外面的变量,使用一个模块作为出口,什么意思呢?

非常简单,在匿名函数内部,定义一个对象。给对象添加各种需要暴露到外面的属性和方法(不需要暴露的直接定义即可)。最后将这个对象返回,并且在外面使用了一个MoudleA接受。

var ModuleA = (function(){
   1. 定义一个对象
   var obj = {};
   2. 在对象内部添加对象和方法
   obj.flag = true;
   obj.myFunc = function(info){
       consolr.info(info)
   }
   //3. 将对象返回
   return obj;
})()

if(ModuleA.flag){
   console.info("我最帅") 
}
ModuleA.myFunc('我最帅')
console.info(ModuleA)

接下来,我们在man.js中怎么使用呢?我们只需要使用属于自己模块的属性和方法即可;这就是模块最基础的封装,事实上模块的封装还有很多高级的话题。

前端模块化开发已经有了很多既有的规范,以及对应的实现方案。常见的模块化规范:

CommonJS、AMD、CMD,也有ES6的Modules

CommonJS(了解)

模块化有两个核心:导出和导入

CommonJS的导出:

#moduleA.js文件
module.exports = {
    flag: true,
    test(a,b){
        return a+b;
    },
    demo(a,b){
        return a+b;
    }
}

CommonJS的导入

# moduleA.js文件
let {test,demo,flag}= require('moduleA')

ES6的export指令

export基本使用

export指令用于导出变量,比如下面的代码:

//info.js
export let name = '韩信'
export let ag = 18
export let height = 180

上面的代码还有另外一种写法:

let name = '韩信'
let ag = 18
let height = 180
export {name,age,height}
导出函数或类

上面我们主要是输出变量,也可以输出函数或者输出类上面的代码也可以写成这种形式:

# 导出
export function test(content){
    console.log(content)
}
export class Person{
    constructor(name,age){
    	this.name = name;
        this.age = age;
    }
    run(){
        console.log(this.name+"在奔跑");
    }
}

#导入
var {add} = require('./add.js');
export default

某些情况下,一个模块中包含某个的功能,我们并不希望给这个功能命名,而且让导入者可以自己来命名;这个时候就可以使用export default

//info.js
export default functin(){
    console.info('default function')
}

我们来到main.js中,这样使用就可以了这里的myFunc`是我自己命名的,你可以根据需要命名它对应的名字

import myFunc from './info.js'
myFunc()

另外,需要注意:export default在同一个模块中,不允许同时存在多个。

生命周期函数

vue中所有的钩子函数

  • beforeCreate(创建前)
  • created(创建后)
  • beforeMount(载入前)
  • mounted(载入后)
  • beforeUpdate(更新前)
  • updated(更新后)
  • beforeDestroy(销毁前)
  • destroyed(销毁后)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李熠漾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值