01 Vue2.x语法篇

文章目录

  • 一、Vue.js简介
    • 1.1 MVC和MVVM设计模式
      • (1) MVC设计模式
      • (2) MVVM设计模式
    • 1.2 Vue.js的下载
  • 二、Vue实例
    • 2.1 Vue实例
    • 2.2 生命周期
    • 2.3 模板数据
    • 2.4 过滤器
  • 三、计算属性、方法属性、监听属性
    • 3.1 计算属性的用法和缓存
    • 3.2 监听属性
    • 3.3 开发案例:实时监控用户输入的内容
    • 3.4 开发案例:留言板
    • 3.5 开发案例:Vue模拟Ajax登录验证
  • 四、内置指令
    • 4.1 基本指令
    • 4.2 class与style绑定
      • (1) 绑定class
      • (2) 绑定内联样式
    • 4.3 条件渲染指令
    • 4.4 列表渲染指令
    • 4.5 方法与事件
    • 4.6 表单与v-model
    • 4.7 开发案例:购物车
  • 五、组件开发
    • 5.1 Vue组件
    • 5.2 父组件向子组件传递数据
    • 5.3 子组件向父组件传递数据
    • 5.4 同级组件之间如何传递数据
    • 5.5 父子组件的访问方式
    • 5.6 slot插槽
    • 5.7 开发小案例-组件实现用户点赞功能
    • 5.8 开发小案例-商品信息列表与计算
    • 5.9 开发小案例-用插槽快速的实现网页的布局
  • 六、import
    • 6.1 import(模块、文件)引入方式
    • 6.2 export,import和export default的关系
    • 6.3 部分导入和部分导出,全部导入和全部导出


一、Vue.js简介

1.1 MVC和MVVM设计模式

(1) MVC设计模式

01 Vue2.x语法篇_第1张图片

  • 背景:Web项目需求日益复杂,为了使项目简化,采用了MVC架构,实现了前端UI展示,与后端业务逻辑处理的完全分离,利于前后端开发人员分工协作,提升效率。
  • MVC:是Model-View-Controller的缩写:
    • M:Model,模型,主要处理与数据库相关的业务逻辑;
    • V:View,视图,主要负责数据在客户端/浏览器上的展示;
    • C:Controller,控制器,视图与模型之间传递消息,例如:接受请求,选择模型,渲染视图等;
  • MVC基本的运行流程是:
    • 视图发起请求–>控制器接收请求–>转发给对应模型处理–>模型将结果返回到控制器–>控制器返回到视图;
    • 简单描述:View–>Controller–>Model->Controller->View,即V-C-M-C-V;
    • 整个过程是封闭的,单向的,视图与模型之间不允许直接通讯,必须通过控制器进行;
  • MVC开发的基本原则:
    • 需要服务器端配合,模型Model与控制器Controller都在服务器端完成;
    • 服务器处理过的数据,必须通过JavaScript在前端进行渲染;
    • 厚模型,薄控制器,业务逻辑尽可能写到模型中,控制器仅调用模型提供的接口即可;

(2) MVVM设计模式

随着前端页面开发越来越复杂,用户与数据的交互也越来越繁杂,而很多交互都是临时性的,没必要每一次都要和服务器中的模型对象进行交互,但是传统的jQuery等函数库又力不从心,这时MVVM模式就应运而生了。

  • MVVM
    01 Vue2.x语法篇_第2张图片

    View<=>ViewModel<=>Model

    • M:Model(模型):需要展示的数据(Vue中的data)
    • V:View(视图层):HTML代码/模板(Vue中挂载的dom)
    • VM:ViewModel(视图模型层):Vue对象实例
  • 基本流程

    • VM层通过Data Binding根据Model数据来更新View,或者根据View来更新Model,这个过程是双向的,即双向数据绑定;
    • VM层通过DOM Listener来监听DOM事件,并且通过methods中的操作,来改变Model数据;
    • 服务器端只负责更新Model即可,而Model就是一个JavaScript对象,所以服务器只需要返回可被Model解析的数据即可,例如:json,html等,这样就完全实现了面向接口/API编程。
    • Vue.js就是一个完全采用了MVVM机制的前端开发框架,采用从底层向上的渐进式开发思想,易学易用。
  • 基本术语

{ {name}}

  1. 实例:Vue本身就是一个构造函数,可以用来创建对象,使用Vue第一步,就是要创建一个Vue实例:new Vue(),完成对挂载点的所有DOM操作:增删改查,条件判断与循环遍历,事件等;
  2. 实例参数:Vue()接受一个js字面量对象作为参数,所有的功能,都以对象属性的方式进行设置;
  3. 挂载点:Vue实例的作用域,本质上就是通过css选择器获取到的页面元素:
  4. 模板:带有HTML标签的字符串,挂载点内的内容, 实例中数据的载体/显示位置:

    { {site}}

  5. 值/变量:挂载点中的文本内容;
  6. 属性:描述模板或HTML标签;
  7. 事件:模板或元素上发生的系统或用户事件,例如:点击、移动等;
  8. 渐进式:就是你可以一步一步、有阶段性地来使用Vue.js,不必一开始就使用所有的东西;

1.2 Vue.js的下载

  • 本地:

二、Vue实例

2.1 Vue实例

通过构造函数Vue就可以创建一个Vue的根实例,el用于指定一个页面中已存在的DOM元素来挂载Vue实例,它可以是HTMLElement,也可以是CSS选择器:

id="app">
const app = new Vue({ el:document.getElementById('app') //或则是'#app' })

挂载成功后,我们可以通过app.$el来访问该元素。
创建Vue实例传入的options:

  • el:
    • 类型:string|HTMLElement
    • 作用:决定之后Vue实例会管理哪个DOM
  • data:
    • 类型:Object|Function (组件当中data必须是一个函数)
    • 作用:Vue实例对应的数据对象
  • methods:
    • 类型:{[key:string]:Function}
    • 作用:定义属于Vue的方法,可以在其他地方调用

2.2 生命周期

每个Vue实例创建时,都会经历一系列的初始化过程,同时也会调用相应的生命周期钩子,Vue的生命周期钩子比较常用的有:

  • created 实例创建完成后调用,此阶段完成了数据的观测等,但尚未挂载,$el还不可用,需要初始化处理一些数据时会比较有用。
  • mounted el挂载到实例上后调用,一般我们的第一个业务逻辑会在这里开始。
  • beforeDestory 实例销毁之前调用,主要解绑一些使用addEventListener监听的事件等。

这些钩子与el和data类似,也是作为选项写入Vue实例内,并且钩子的this指向的是调用它的Vue实例。
01 Vue2.x语法篇_第3张图片
01 Vue2.x语法篇_第4张图片

2.3 模板数据

  • 数据以挂载点中的模板为载体:

    XXX

  • 文本:插值表达式{ {XX}}(Mustache语法)或 v-text 指令
  • html:v-html 指令
  • 属性值:v-bind 指令
  • 表达式:算术运算符,三元运算符,js 函数,过滤器
<div id="app">
    
    <p>{
    {name}}p>
    
    <p v-text="sex">p>
    
    <p v-html="msg">p>
    
    <p v-bind:title="title">听妈妈的话p>
    <p v-bind:title="'赵雷'">成都p>
    
    <p>{
    {num * 2}}p>
    <p>{
    {name.slice(0,4).toUpperCase()}}p>
div>
<script>
const vm = new Vue({
      
    el:"#app",
    data:{
      
        name:'Thinco',
        sex:'man',
        msg:'hello world!',
        title:'周杰伦',
        num: 100
    }
})
script>

【注意】:

  • 如果将用户产生的内容用v-html输出后,有可能导致XSS攻击,所以要在服务端对用户提交的内容进行处理,一般可将尖括号“<>”转义。
  • 如果想显示{ {}}标签,而不进行替换,使用v-pre即可跳过这个元素和它的子元素的编译过程,例如:
    {
          {这里的内容是不会被编译的}}
    
  • Vue.js只支持单个表达式,不支持语句和流程控制,在表达式中不能使用用户自定义的全局变量,只能使用Vue白名单内的全局变量,例如:Math和Date。
    
    {
          {var book = 'Vue.js'}}
    
    {
          {if (ok) return msg}}
    

2.4 过滤器

Vue.js支持在{ {}}插值的尾部添加一个管道符“(|)”对数据进行过滤,经常用于格式化文本,比如字母全部大写、货币千位使用逗号分隔等。过滤的规则是自定义的,通过给Vue实例添加选项filters来设置。过滤器可以用在两个地方:双花括号插值和 v-bind 表达式。


{
    { message | capitalize }}


<div v-bind:id="rawId | formatId">div>
//你可以在一个组件的选项中定义本地的过滤器:
filters: {
     
  capitalize: function (value) {
     
    if (!value) return ''
    value = value.toString()
    return value.charAt(0).toUpperCase() + value.slice(1)
  }
}
//或者在创建 Vue 实例之前全局定义过滤器:
Vue.filter('capitalize', function (value) {
     
  if (!value) return ''
  value = value.toString()
  return value.charAt(0).toUpperCase() + value.slice(1)
})
new Vue({
     
  // ...
})

当全局过滤器和局部过滤器重名时,会采用局部过滤器。过滤器可以串联,也可以接收参数,例如:


{
    {message | filterA | filterB}}

{
    {message | filterA('arg1','arg2')}}

【注意】:过滤器应当用于处理简单的文本转换,如果要实现更为复杂的数据变换,应该使用计算属性。

三、计算属性、方法属性、监听属性

3.1 计算属性的用法和缓存

计算属性的定义与用途

  • 表达式如果过长,或逻辑更为复杂时,就会变得臃肿甚至难以阅读和维护,所以在遇到复杂的逻辑时应该使用计算属性
  • 实例中用: computed 属性定义,其值为一个对象,所有的计算属性都以函数的形式写在Vue实例的computed选项内,最终返回计算后的结果。
  • 该属性依赖于实例现有数据进行计算获取
  • 默认只有获取器(getter),但是可以自定义设置器(setter)

计算属性用法
在一个计算属性里可以完成各种复杂的逻辑,包括运算、函数调用等,只要最终返回一个结果就可以。
每一个计算属性都包含一个getter和一个setter,绝大多数情况下,我们只会用默认的getter方法来读取一个计算属性,在业务中很少用到setter,所以在申明一个计算属性时,可以直接使用默认的写法,不必将getter和setter都声明。

姓名:{ {fullName}} 姓名:{ {allName}}

计算属性还有两个很实用的小技巧:

  • 计算属性可以依赖其他计算属性
  • 计算属性不仅可以依赖当前Vue实例的数据,还可以依赖其他实例的数据

计算属性缓存
你可能发现调用methods里的方法也可以与计算属性起到同样的作用。使用计算属性还是methods取决于你是否需要缓存,当遍历大数组和做大量计算时,应当使用计算属性,除非你不希望得到缓存。
计算属性与方法属性之间的关系:

  • 计算属性(computed)依赖缓存,如果多次使用时,计算属性只会调用一次,只有相关依赖数据发生变化才会重新计算,性能更好
  • 方法属性(methods)动态性更强,只要重新渲染,方法总会被调用,实时性更好
<div id="app">
    <p>
        品名:{
    {name}}<br>
        价格:<input type="number" v-model.number="price"><br>
        数量:<input type="number" v-model.number="number">
    p>
    
    <p>金额:{
    {total}}元p>
    
    <p>金额:{
    {total1()}}元p>
div>
<script>
    let vm = new Vue({
      
        el: "#app",
        data: {
      
            name: 'iphoneXR',
            price: 0,
            number: 0
        },
        //计算属性/派生属性
        computed: {
      
            total: function () {
      
                return this.price * this.number;
            }
        },
        //方法属性
        methods: {
      
            total1: function () {
      
                return this.price * this.number;
            }
        },
    })
script>

01 Vue2.x语法篇_第5张图片

3.2 监听属性

实例的监听属性: watch
watch: 该对象属性是要监听的实例数据对象,包括计算属性
监听属性的回调函数与传参

  • 监听属性由对象字面量创建, 其属性是被监听的数据,值为一个回调
  • 该回调支持二个参数(当前值,原值), 参数可省略,如果有须按这个顺序传递
<div id="app">
    <p>用户名:<input v-model="username" type="text">p>
    {
    {username}}
div>
<script>
const vm = new Vue({
      
    el:"#app",
    data:{
      
        username:'',
        userList:['jay','thinco']
    },
    //监听属性/监听器
    watch:{
      
        //监听回调函数的参数(当前的值/已更新值,第二个参数可选)
        username:function(newValue,oldValue){
      
            this.userList.forEach(function(value){
      
                if(newValue == value){
      
                    alert("该用户已经存在");
                }
            })
        }
    }
});
script>

3.3 开发案例:实时监控用户输入的内容

<div id="app">
    <textarea v-model="text">{
    {text}}textarea>
div>
<script>
    let vm = new Vue({
      
        el: "#app",
        data: {
      
            //用户输入的内容
            text: "",
            //敏感词汇
            sensitive: [
                '逃课', "发呆", "迟到", "早退"
            ]
        },
        watch: {
      
            text: function (value) {
      
                this.sensitive.forEach(function (words) {
      
                    if (value.indexOf(words) !== -1) {
       //存在敏感词
                        alert("请不要输入敏感词汇");
                        location.reload();
                    }
                })
            }
        }
    })
script>

3.4 开发案例:留言板

(1) 原生JS实现一个留言板

<div id="app">
    <input type="text" >
    <button>提交button>
    <ul>ul>
div>
<script>
//获取元素
let input = document.getElementsByTagName('input')[0];
let button = document.getElementsByTagName('button')[0];
let ul = document.getElementsByTagName('ul')[0];
button.onclick = function(){
      
    //创建元素
    let li = document.createElement('li');
    //添加内容
    li.innerHTML = input.value;
    //添加到页面
    if(ul.children.length == 0){
       //没有子节点
        ul.appendChild(li);
    }else{
      
        let first = ul.firstChild;
        ul.insertBefore(li,first);
    }
    //清空input
    input.value = '';
}
script>

01 Vue2.x语法篇_第6张图片

(2) Vue实现一个留言板

<h4>留言板h4>
<div id="app">
    <input type="text" v-model="input" placeholder="请输入内容" >
    <button @click="fill">提交button>
    <ul>
        <li v-for='(item,index) in list'>{
    {item}}
            <button @click="del(index)">删除button>
        li>
    ul>
div>
<script>
let vm = new Vue({
      
    el:"#app",
    data:{
      
        input:'',
        list:[]
    },
    methods:{
      
        //添加
        fill:function(){
      
            //尾部添加
            //this.list.push(this.input);
            //头部添加
            this.list.unshift(this.input);
            //清空留言区
            this.input = '';
        },
        //删除
        del:function(index){
      
            if(confirm('真的删除吗?')){
      
                this.list.splice(index,1);
            }
        }
    }
});
script>

3.5 开发案例:Vue模拟Ajax登录验证

<div id="app">
    <h4>用户注册h4>
    <p>
        <label for="username">用户名:label>
        <input type="text" id="username" v-model.lazy.trim="username" @input="input" @keyup.delete="resumeName">
        <span v-show="nameError" :style="error">{
    {username}}用户名已存在,请更换span>
    p>
    <p>
        <label for="email">邮箱:label>
        <input type="email" id="email" v-model.lazy.trim="email" @input="input" @keyup.delete="resumeEmail">
        <span v-show="emailError" :style="error">{
    {email}}邮箱已注册过,请更换span>
    p>
    <p>
        <button @click="submit">登录button>
        <span :style="error">{
    {tips}}span>
    p>
div>
<script>
    let vm = new Vue({
      
        el:'#app',
        data:{
      
            //用户名
            username:'',
            //邮箱
            email:'',
            //用户名验证提示状态
            nameError:false,
            //邮箱验证提示显示状态
            emailError:false,
            //设置错误信息的文本样式
            error:{
      color:'red'},
            //登录提示信息
            tips:'',
            //已存在用户名列表
            userList:['admin','thinco','jay'],
            //已注册过的邮箱
            emailList:['[email protected]','[email protected]']
        },
        //监听器
        watch:{
      
            //监听用户名
            username:function(value){
      
                //如果冲突则提示用户
                this.userList.forEach(function(name){
      
                    if(name === value){
      
                        this.nameError = true;
                    }
                },this)
            },
            //监听邮箱的更新
            email:function(value){
      
                //如果冲突则提示用户
                this.emailList.forEach(function(name){
      
                    if(name === value){
      
                        this.emailError = true;
                    }
                },this)
            },            
        },
        //事件响应方法
        methods:{
      
            submit:function(){
      
                //非空判断
                if(this.username.length === 0 && this.email.length === 0){
      
                    this.tips = '用户名和邮箱不能为空';
                //验证通过
                }else if(this.nameError === false && this.emailError === false){
      
                    this.error = {
      color: 'green'};
                    this.tips = '验证通过,正在跳转';
                    setTimeout(function(){
      
                        location.href = 'index.php';
                    },1000);
                }else{
      
                    this.tips = '用户名或邮箱错误';
                }
            },
            input:function(){
      
                this.tips = '';
            },
            //当按下删除或退格键时触发,应该关闭错误
            resumeName:function(){
      
                this.nameError = false;
            },
            resumeEmail:function(){
      
                this.emailError = false;
            }
        }
    });
script>

四、内置指令

4.1 基本指令

  • 指令必须写在 DOM 元素的属性位置上,以特殊的 v- 为前缀
  • 当表达式的值发生变化,动态的改变指令对应的 DOM 元素的内容

v-bind
可以缩写为::,基本用途是动态更新HTML元素上的属性。
v-on
可以缩写为:@,用来绑定事件监听器,v-on可以监听原生的DOM事件,表达式除了方法名,也可以直接是一个内联语句。
v-once
v-once是一个不需要表达式的指令,作用是定义它的元素或组件只渲染一次,包括元素或组件的所有子节点。首次渲染后,不再随着数据的变化重新渲染,将被视为静态内容。

<div id="app">
    <span v-once>{
    {message}}span>
div>
...

v-html
某些情况下,我们从服务器请求到的数据本身就是一个HTML代码
,可以使用此指令解析出HTML展示。
v-text
v-text作用和Mustache一致。
v-pre
跳过这个元素和它子元素的编译过程,用于显示原本的Mustache语法。
v-cloak
v-cloak是一个不需要表达式的指令。在某些情况下,网页还在加载 Vue.js ,而导致 Vue 来不及渲染,我们浏览器可能会直接显然出未编译的Mustache标签。我们可以使用 v-cloak 指令来解决这一问题。
原理是:在vue解析之后,会去除属性v-cloak。

<style>
[v-cloak]{
       display: none; }
style>

<div id="app" v-cloak>
    {
    {context}}
div>

【注意】:

在简单项目中,使用 v-cloak 指令是解决屏幕闪动的好方法。但在大型、工程化的项目中(webpack、vue-router)只有一个空的 div 元素,元素中的内容是通过路由挂载来实现的,这时我们就不需要用到 v-cloak 指令。

语法糖
语法糖是指在不影响功能的情况下,添加某种方法实现同样的效果,从而方便程序开发。Vue.js的v-bind和v-on指令都提供了语法糖,也可以说是缩写。






4.2 class与style绑定

(1) 绑定class

  • 对象语法
    
    <h2 :class="{
            'active': isActive}">Hello Worldh2>
    
    
    
    <h2 class="title" :class="{
            'active': isActive, 'line': isLine}">Hello Worldh2>
    
    
    
    <h2 class="title" :class="classes">Hello Worldh2>
    
    <h2 class="title" :class="getClasses()">Hello Worldh2>
    
    • :class设置一个对象,可以动态地切换class,对象中也可以传入多个属性,来动态切换class。另外,:class可以与普通class共存。:class内的表达式每项为真时,对应的类名就会加载。
    • 一般当条件多于两个时,都可以绑定到data或computed、methods。
  • 数组语法
    
    <h2 :class="['active']">Hello Worldh2>
    
    
    
    <h2 :class="[active, 'line']">Hello Worldh2>
    
    
    
    <h2 class="title" :class="['active', 'line']">Hello Worldh2>
    
    
    
    <h2 class="title" :class="classes">Hello Worldh2>
    
    • 当需要应用多个class时,可以使用数组语法,给:class绑定一个数组。
      <div id="app">
          <div :class="[activeCls,errorCls]">div>
      div>
      <script>
      let app = new Vue({
                
          el:'#app',
          data:{
                
              activeCls:'active',
              errorCls:'error'
          }
      })
      script>
      
    • 也可以根据三元表达式来切换class。
    • 当然,与对象语法一样,也可以使用data、computed、methods三种方法来实现。

使用计算属性给元素设置类名,在业务中经常用到,尤其是在写复用的组件时,所以在开发过程中,如果表达式较长或逻辑复杂,应该尽可能地优先使用计算属性。

(2) 绑定内联样式

使用:style可以给元素绑定内联样式,方法与:class类似,也有对象语法和数组语法。

  • CSS属性名称可以:
    • 使用驼峰命名(camelCase),如:fontSize。
    • 或短横线分隔命名(kebab-case),记得用单引号括起来,如:‘font-size’,否则会当做变量去解析。
  • 大多数情况下,直接写一长串的样式不便于阅读和维护,所以一般写在data或computed里。
  • 在实际业务中,:style的数组语法并不常用,因为往往可以写在一个对象里面;而较为常用的应当是计算属性。
  • 使用:style时,Vue.js会自动给特殊的CSS属性名称增加前缀,比如transform。

【绑定方式一:对象语法】

<div :style="{
        color: currentColor, fontSize: fontSize + 'px'}" >div>

style后面跟的是一个对象类型:

  • 对象的key是CSS属性名称
  • 对象的value是具体赋的值,值可以来自于data中的属性

【绑定方式二:数组语法】

<div :style="[baseStyles, overridingStyles]">div>

style后面跟的是一个数组类型,多个值以分割即可。
在组件上使用
如果直接在自定义组件上使用class或:class,样式规则会直接应用到这个组件的根元素上。如果需要给具体的子元素设置类名时,应当使用组件的props来传递。这些用法同样适用于绑定内联样式style的内容。

4.3 条件渲染指令

v-if、v-else-if、v-else指令
控制模板元素的存在或移除,移除是真正的删除了该元素。
与JavaScript的条件语句if、else、else if类似,Vue.js的条件指令可以根据表达式的值在DOM中渲染或销毁元素/组件。

<div id="app">
    <p v-if="status === 1">当status为1时显示该行p>
    <p v-else-if="status === 2">当status为2时显示该行p>
    <p v-else>否则显示该行p>
div>
...

Vue在渲染元素时,出于效率考虑,会尽可能地复用已有的元素而非重新渲染,比如:

<div id="app">
    <template v-if="type === 'name'">
        <label for="">用户名:label>
        <input type="text" placeholder="请输入用户名">
    template>
    <template v-else>
        <label for="">邮箱:label>
        <input type="text" placeholder="请输入邮箱">
    template>
    <button @click="handleToggleClick">切换输入类型button>
div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
let vm = new Vue({
      
    el:'#app',
    data:{
      
        type:'name'
    },
    methods:{
      
        handleToggleClick: function(){
      
            this.type = this.type === 'name' ? 'mail' : 'name';
        }
    }
})
script>


键入内容后,点击切换按钮,虽然DOM变了,但是之前输入框中键入的内容没有改变,只是替换了placeholder的内容,说明元素被复用了。
如果你不希望这样做,可以使用Vue.js提供的key属性,它可以让你自己决定是否要复用元素,key的值必须是唯一的。

v-show指令
控制模板元素的显示与隐藏,仅仅是用css控制(display:none;)。
当v-show表达式的值为false时,元素会隐藏,查看DOM结构会看到元素上加载了内联样式display:none。v-show不能在