乐趣区

关于vue.js:vue学习整理


title: Vue 学习笔记
date: 2020-7-1
tags: [vue2.0]
categories: 前端技术
toc: true
cover: https://gitee.com/hyj12704338…


Vue 2.0 前言

Vue 的官网文档可能是我见过的最好的开发文档了,所以如果是学习 Vue 倡议还是浏览官网文档吧。在这里仅仅是基于我本人的了解对于 Vue2.0 常识的整顿,便于梳理知识结构

1. 网站交互方式

1.1 单页利用 SPA

  • 多页面

    • 点击跳转刷新,用户体验不好
    • 有利于 SEO 搜索引擎搜寻
  • 单页面利用(Single Page Application,简称 SPA)

    • 开发方式好,前后端拆散,开发效率高,可维护性好

      • 服务端不关怀页面,只关怀数据处理
      • 客户端不关怀数据库操作,只通过接口和服务器交互数据
    • 用户体验好,就像原生客户端软件一样应用
    • 只须要加载渲染部分视图即可,不须要整页刷新
    • 单页利用开发技术简单,所以诞生了一堆开发框架

      • AngularJS

        • google 开发
        • 为前端带来了 MVVM 开发模式
        • MVVM(Model-View-ViewModel):数据驱动视图

      • ReactJS

        • facebook
        • 提出组件化
      • VueJS

        • Vue 借鉴了前两种,舍短取长
    • 单页面技术曾经很成熟,然而大部分不兼容低版本游览器
    • 单页利用因为数据都是异步加载过去的,不利于 SEO(当初有基于 Vue 的服务端渲染框架 nuxt)

1.2 单页利用 SPA 实现原理

前后端拆散 + 前端路由

  • 后端 Nodejs,应用 Express 监督对应申请

    app=express()
    app.get("/",function(request,response){
        // 解决
        // 而后把后果增加到 response 中
        response.json()})
  • 前端工作(以下例子应用原生 js 实现,然而在 Vue 框架中用 vue-router 插件更加简略)

    • 前台申请数据,并渲染页面

      <!-- 引入资源 -->
      <script  src="模板引擎地位"> </script>
      <script  src="jquer 地位"> </script>
      <!-- 页面 -->
      <script is="tp1" type="text/template">
            {{each student_front}}
            <li> {{value.name}}</li>
            {{/each}}
      </script>
      <!-- 申请数据,并渲染到页面 -->
      <script>
            $.get("接口,如 http://127.0.0.1:3000/student",function(data){
                template("tp1,{student_front:data}")
        })
            <!-- $("#id 名")能够获取 dom 元素 -->
        </script>
    • 前端路由不同 url 装载不同页面

      find-music,my-music,friend 多个页面, 在其页面向服务端取数据进行渲染,而后放入 index 的容器 <div id=”container”> 中显示

      留神:下载 jquery;sublime 装置 sublimeServer 实现启动本地服务器(不装置就是间接关上本地文件,不反对跨域找下载的 jquery.js 文件)

      <!--index.html-->
      <!DOCTYPE html>
      <html>
      <head>
          <title> 音乐 </title>
          <mata charset="utf-8">
      </head>
      <body>
          <div class="top"> 头部 </div>
          <div class="aside">
              <ul>
                  <!-- a 标签会跳转刷新,用锚点不会刷新, 点击敌人,url 扭转浏览器显示:"网址 #/friend"。用 window.onhashchange,同一个 a 标签点击屡次,只有第一次触发 -->
                  <!-- 通过 #/friend 变动,渲染 -->
                  
                  <li><a href="#/"> 发现音乐 </a></li>
                  <li><a href="#/my-music"> 我的音乐 </a></li>
                  <li><a href="#/friend"> 敌人 </a></li>
              </ul>
              <div id="container">
                  <!-- 把 其余页面 渲染进来 -->
              </div>
              <script>
                  window.onhashchange=function(){
                      //location 中的 hash 字段蕴含 锚点标识 #/friend
                      //substr(1)标识从 string 的 1 地位向后截取 
                      var hash=window.location.hash.substr(1)
                  
                      
                      if(hash==="/"){$.get("./find-music.html",function(data){$("#container").html(data)
                          })
      
                      }else if(hash==="/my-music"){$.get("./my-music.html",function(data){console.log(data)
                              $("#container").html(data)
                          })
      
                      }else if(hash==="/friend"){$.get("./friend.html",function(data){$("#container").html(data)
                          })
                      }
                  }
              </script>
          </div>
          <!-- 装置 jquery 命令 npm install jquery-->
          <script src="node_modules/jquery/dist/jquery.js"></script>
      </body>
      </html>
      <!--find-music.html-->
      <div> 查找音乐 </div>
      
      <!--my-music-->
      <div> 我的音乐 </div>
      
      <!--friend-->
      <div> 敌人 </div>
      
    • 以上的形式构建单页面利用太简单,所以呈现了 Vue 等框架

2. 初识 Vue

官网:https://cn.vuejs.org/

装置:npm install vue

Vue 是什么?

  • 优良的前端 js 开发框架
  • 能够轻松构建SPA 单页面利用
  • 通过 指令 扩大了 HTML,通过 表达式 绑定数据到 HTML
  • 极大水平解放DOM 操作

2.1 Vue 的特点

Vue 是为了克服 HTML 在构建利用上的有余而设计的。其外围特点:

  • MVVM
  • 双向数据绑定
  • 组件化
  • 渐进式

2.2 HelloWorld

  • 相似于模板引擎, 有 {{变量名}} 语法
  • 不同于模板引擎的是 能够通过 app1这个变量间接操作 DOM 元素,不用再 $(#id 名) 来获取 DOM 元素了

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
    <div id="app">
        <h1>{{1+1}}</h1>
        <h1>{{"hello"+"world"}}</h1>
        <h1>{{message}}</h1>
    </div>

    <script src="node_modules/vue/dist/vue.js"></script>
    <script>
        const app1=new Vue({el:"#app",//el 通知 vue 治理模板的入口,div 中的 {{}} 的模板语法都会被渲染,el 不能是 body 和 html
            data:{// 绑定的成员数据,这种数据被称为响应式数据。// 什么是响应式数据?数据驱动视图,当数据发生变化时,所有绑定该数据的 DOM 都会跟着扭转
                message:"Hello vue.js"
            }
        })
    </script>
</body>
</html>


<!-- 后果:2
helloworld
Hello vue.js
-->

2.3 双向数据绑定

什么是双向数据绑定?

当数据发生变化时,DOM 元素会自动更新数据

当表单发生变化时,数据也会自动更新

v-model 指令

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
    <div id="app">
        <h1>{{message}}</h1>
        <input type="text" v-model="message">
        <!--v-model 是 Vue 提供的一个非凡属性,在 Vue 中称为指令
            它的作用是:双向绑定表单控件
        -->
    </div>

    <script src="node_modules/vue/dist/vue.js"></script>
    <script>
        const app1=new Vue({
            el:"#app",
            data:{message:"Hello vue.js"}
        })
    </script>
</body>
</html>

3.Vue 的基本知识

  • 在 html 页面,通过 script 形式引入的 vue.js

    <!DOCTYPE html>
    <html>
    <head>
        <title></title>
    </head>
    
    
    <body>
        <!--Vue 管制的 div--> 
        <div id="app"></div>
        
        <!-- 通过 CDN 引入 vue.js-->
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <!-- 通过 npm 下载 vue.js 到本地,依据 vue.js 所在目录引入 -->
        <script src="node_modules/vue/dist/vue.js"></script>
        <script>
            new Vue({
              el: '#app',
              data: {selected: ''}
            })        
        </script>
    
    </body>
    </html>
  • 应用 vue-cli 工具,构建更加简单的 Vue 我的项目,能够在我的项目中应用 .vue 单文件进行 vue 代码的编写(在大型项目中应用这种形式)

    <template>
        <!-- 留神 template 标签下,只能有一个根标签 -->
          <div id="app">
            <span>1</span>
            <span>2</span>
        </div>
    </template>
    
    <script>
        // 在以后 vue 页面,引入其余 js 包或者其余 vue 页面自定义的组件
        import XX from "XXX.js"
        import YY from "YY.vue"
        
        
        //
        export default {
            // 绑定的数据
            data(){
                return{
                    msg1:"xx",
                    msg2:"yy"
                }
            },
            // 在这路引入 YYY.vue 组件
            componemts:{YY}
            // 生命周期函数
            created(){},
            methods:{fun1(){},
                fun2(){}
            }
            //watch,computed 等
        }
    </script>
    
    <!--lang 指定预编译语言,scoped 指定 style 中的款式只能在以后.vue 文件中起作用 -->
    <style lang="less" scoped>
    </style>
    

3.1 Vue 实例

把 Vue 实例,看成一个函数 Vue(),这个函数的参数是一个对象Vue({}),而el,data,created,methods,computed,watch 看成是对象的属性

new Vue({//el 通知 vue 治理模板的入口,div 中的 {{}} 的模板语法都会被渲染,el 不能是 body 和 html
    el: '#app',
    
    // 绑定的成员数据,这种数据被称为响应式数据。(什么是响应式数据?数据驱动视图,当数据发生变化时,所有绑定该数据的 DOM 都会跟着扭转)
    data: {msg: ''},
   // 生命周期函数, 前面会介绍。本来应该是对象的属性模式 created:function(){},然而个别简写成以下模式
   created(){},
    
   //vue 实例中的办法全副写在外面,通过 v -on 将办法绑定在 Dom 元素上触发
   methods:{fun1(){
            //vue 中能够通过 this.msg 获取 data 中变量的值
              // 也能够通过 this.fun2()拜访到 methods 中的函数}
    },
    
    // 计算属性, 与绑定函数不同的是,计算属性优先从缓存加载。a 是变量名,且不必再 data 中申明,间接用于 {{a}} 之中
    computed:{a:function(){// 肯定有 return,return 的值放到 a 中,通过 {{ a}} 应用
       }
    },
    // 侦听属性,用来监听 data 中变量的变动
    watch:{msg:function(newMessage,oldMessage){// 默认参数是:扭转前的值,扭转后的值}         
    }
})        

属性 data

data 的值是一个对象,对象中的变量只有发生变化,则视图就会马上相应,视图中的数据就会变成新的值。

留神:

  • 只有 data 中的变量才是响应式的
  • 应用 Object.freeze(),这会阻止批改 data 中的对象类型的变量,也意味着响应零碎无奈再追踪变动。

    var obj = {
      name:"张三",
      age:18
    }
    
    Object.freeze(obj)
    
    new Vue({
      el: '#app',
      data:{stu:obj}
    })

生命周期函数(待深入分析)

<template>
  <div>

  </div>
</template>

<script>
import OtherComponent from '@/components/OtherComponent'

export default {
  name: 'MyName',
  components: {OtherComponent},
  directives: {},
  filters: {},
  extends: {},
  mixins: {},
  props: {},
  data () {return {}
  },
  computed: {},
  watch: {},
  beforeCreate () {// 生命周期钩子:组件实例刚被创立,组件属性计算之前,如 data 属性等},
  created () {
    // 生命周期钩子:组件实例创立实现,属性已绑定,但 DOM 还未生成,el 属性还不存在
    // 初始化渲染页面
  },
  beforeMount () {// 生命周期钩子:模板编译 / 挂载之前},
  mounted () {// 生命周期钩子:模板编译、挂载之后(此时不保障已在 document 中)},
  beforeUpdate () {// 生命周期钩子:组件更新之前},
  updated () {// 生命周期钩子:组件更新之后},
  activated () {// 生命周期钩子:keep-alive 组件激活时调用},
  deactivated () {// 生命周期钩子:keep-alive 组件停用时调用},
  beforeDestroy () {// 生命周期钩子:实例销毁前调用},
  destroyed () {// 生命周期钩子:实例销毁后调用},
  errorCaptured (err, vm, info) {
    // 生命周期钩子:当捕捉一个来自子孙组件的谬误时被调用。此钩子会收到三个参数:谬误对象、产生谬误的组件实例以及一个蕴含谬误起源信息的字符串。console.log(err, vm, info)
  },
  methods: {}}
</script>

<style lang="scss" scoped></style>
var vm=new Vue({
  data: {a: 1},
    
  //// 生命周期函数
    
  // 属性写法
  created: function () {
    // this 指向 vm 实例
    console.log('a is:' + this.a)
  },
    
  // 简写模式
  mounted(){},

})

留神:不要再 Vue 实例的属性上应用箭头函数,否则会报错。因为箭头函数中 this 的指向和上下文无关,并不一定会指向 Vue 的实例化后的对象

// 都会报错
created: () => console.log(this.a),
vm.$watch('a', (oldValue,newValue) =>{})

Vue 生命周期详解

beforeCreate=> 创立以后页面 vue 实例
|
=>
|
created=>data,method,watch,computed 可用
|
=> 调用 render 函数,生成虚构 Dom
|
beforeMount=> 虚构 Dom 创立实现
|
=> 调用 patch 函数,创立实在 Dom
|
mounted => 实在 Dom 创立实现,并渲染到页面

如果数据变动,不肯定会触发 beforeUpdate,updated 函数,只有绑定在视图上的数据变动才会触发

=> 绑定视图的数据产生变动,依据实在的 Dom,生成一个虚构 Dom,将虚构 Dom 上对应节点的数据进行同样变动
|
beforeUpdate
|
=> 调用 patch 函数(应用 diff 算法),将虚构 Dom 的改变更新到实在 Dom 上
|
updated

响应式原理(数据变动如何触发视图更新)

diff 算法

计算属性 computed

{{}}或者是 v -bind 绑定的数据,中有简单的计算时,能够写在计算属性中。

咱们须要数据 B,然而数据 B 依赖了数据 A, 一旦数据 A 发生变化,B 也会发生变化 —–> 数据 B 改成计算属性

计算属性感知的是函数体外部 data 数据的变动

计算属性,与绑定函数不同的是,计算属性优先从缓存加,当数据 A 未发生变化时, 拜访计算属性 B 会立刻返回之前的计算结果,而不用再次执行函数。只有依赖的数据 A 产生扭转时, 它才会从新求值。

B 是变量名,且不必再 data 中申明,可间接用于 {{}} 之中

计算属性 B 的 getter:

computed: {B: function () {// 函数体,肯定有 return,return 的值放到 B 中,通过 {{ B}} 应用
        return '扭转后'+this.A
          
     // 简写
      B(){// 函数体肯定有 return,return 的值放到 B 中,通过 {{ B}} 应用
         return '扭转后'+this.A
      }
}

计算属性默认只有 getter,不过在须要时你也能够提供一个 setter:

computed: {
  fullName: {
    // getter
    get: function () {return this.firstName + ' ' + this.lastName},
    // setter
    set: function (newValue) {//newValue 是 fullName 变动后的新值
      var names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}

助记: getter 是函数外部变量变动触发,fullName 失去 return 的值.setter 是 fullName 变动触发, 外部执行操作

侦听器 watch

当须要在数据变动时执行异步或开销较大的操作时, 通常应用 watch

监听的数据是 data 中的值

watch 监听变量对应的函数,不能应用箭头函数

<template>
  <div>

  </div>
</template>

<script>
export default {data () {
    return {question:null}
  },
  watch: {question: function (newValue, oldValue) {// 不能应用箭头函数
      // 监测到 question 变动了,进行的操作  
    }
  }
}
</script>

<style lang="scss" scoped></style>

留神:

如果监听的是一个对象

student: {
  name: "",
  score: {
    math: 32,
    english: 80,
  },
  selectedCourse: ["操作系统", "数据结构"],
}

须要采纳上面的办法,只有 student 外面的任何内容变动,就会触发

watch: {
     student: {handler: function() {// 监测到 question 变动了,进行的操作},
         deep: true
     }
}

如果只是监听对象的某个键, 下面的办法太节约性能了

watch: {
  "student.name": {handler:function () {console.log("触发执行");
    }
  }
},

或者

watch: {"student.name":function () {console.log("触发执行");
  }
},

计算属性和监听器

留神:

watch 是监听 data 中的数据,数据变动,触发 function 中

compute 是 function 外部数据变动,返回给里面的值(默认的 getter,也能够设置 setter)

3.2 Vue 指令

1.html 中绑定 data 数据

应用{{}}

  • 文本

    应用“Mustache”语法 (双大括号) , 会将双大括号中的值当作变量,把在 data 中的具体数据渲染进去

    <span>{{msg}}</span>

    双大括号之间还能够做简略的 js 运算(简单的运算,个别会放到 computed 中)

    <span>{{num++}}</span>
    <span>{{arr.split('').reverse().join('') }}</span>

    通过应用 v-once 指令,你也能执行一次性地插值,当数据扭转时,插值处的内容不会更新

    <span v-once> {{msg}}</span>
  • v-text

    <span v-text="msg"></span>
    
    <!-- 和上面的一样 -->
    <span>{{msg}}</span>
  • v-html 若 message 是 html 代码,不必这个属性只会间接把 html 代码当成字符串显示
  • v-pre 跳过这个元素和它的子元素的编译过程。间接显示{{message}}

2. 标签属性绑定 data 数据

不应用{{}}, 间接写变量名即可

有一些指定绑定时须要参数,放在指令名称之后的冒号前面

url 是 data 中的变量

<a v-bind:href="url">...</a>
// 简写
<a :href="url">...</a>

3. 属性绑定 v -bind

根本用法

绑定属性

v-bind:管制 html 标签的属性值,将 属性 变量 绑定。

// 绑定属性 disabled
<button v-bind:disabled="isButtonDisabled">Button</button>
// 简写 
<button :disabled="isButtonDisabled">Button</button>
// 绑定属性 href
<a v-bind:href="url">...</a>
// 简写
<a :href="url">...</a>

动静参数

[]中是变量 attributeName,能够动静的指定绑定的属性是什么, 比方:href

<a :[attributeName]="url"> ... </a>

留神:

  • 当某个属性值是布尔值时,间接 disable=”true” 相当于,把一个字符串赋值给属性。用绑定赋值,会把字符串 ture 转变为布尔值

    <button :disable="true"></button>//true 是不可点击
  • 绑定的属性的值是对象

    对象的属性值是 true,则保留该属性;属性值是 flase,则去除该属性;

    <div :class="obj"></div>
    data: {
        obj:{
            active: true, 
            text-danger: false
         }
    }

    对应的理论的成果是

    <div  class="active"></div>
  • 绑定的属性的值是数组

    <div :class="arr"></div>
    data: {arr:['active','text-danger']
    }

    对应的理论的成果是

    <div class="active text-danger"></div>

用于绑定 class

对象语法:动静切换 class, 只能切换该属性是否存在,对象属性值为真,则该属性存在,都则不存在

  • 对象中传入一个字段(定义的 class 名,要用引号引起来)

    <div v-bind:class="{"active": isActive}"></div>

    下面的语法示意 active 这个 class 存在与否将取决于数据 property isActive 的 truthiness。

  • 对象中传入更多字段来动静切换多个 class。此外,v-bind:class 指令也能够与一般的 class 属性共存

    <div
      class="static"
      v-bind:class="{"active": isActive,"text-danger": hasError}"
    ></div>

    和如下 data:

    data: {
      isActive: true,
      hasError: false
    }

    后果渲染为:

    <div class="static active"></div>
  • 绑定的数据对象不用内联定义在模板里:

    <div class="static" v-bind:class="classObject"></div>
    data: {
      classObject: {
        active: true,
        'text-danger': false
      }
    }

    渲染的后果和下面一样。

  • 咱们也能够在这里绑定一个返回对象的 计算属性。这是一个罕用且弱小的模式:

    <div v-bind:class="classObject"></div>
    data: {
      isActive: true,
      error: null
    },
    computed: {classObject() {
        return {
          active: this.isActive && !this.error,
          'text-danger': this.error && this.error.type === 'fatal'
        }
      }
    }

数组语法: 切换 class 属性,能够搁置三元表达式,切换两个不同属性,而对象是切换 class 某个属性是否存在

  • 根底用法

    <div v-bind:class="['active','text-danger']"></div>

    渲染为:

    <div class="active text-danger"></div>
  • 咱们能够把一个数组元素替换为变量

    <div v-bind:class="[activeClass, errorClass]"></div>
    data:{
      activeClass: 'active',
      errorClass: 'text-danger'
    }

    渲染为:

    <div class="active text-danger"></div>
  • 如果你也想依据条件切换列表中的 class,能够用三元表达式:

    <div v-bind:class="[isActive ? activeClass :'', errorClass]"></div>

    这样写将始终增加 errorClass,然而只有在 isActive 是 truthy 时才增加 activeClass

  • 三元表达式有些繁琐。所以在数组语法中也能够应用对象语法:

    <div v-bind:class="[{active: isActive}, errorClass]"></div>

用于绑定 style

对象语法

v-bind:style 的对象语法非常直观——看着十分像 CSS,但其实是一个 JavaScript 对象。CSS 属性名能够用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名:

<div v-bind:style="{color: activeColor, fontSize: fontSize +'px'}"></div>
data: {
  activeColor: 'red',
  fontSize: 30
}

间接绑定到一个款式对象通常更好,这会让模板更清晰:

<div v-bind:style="styleObject"></div>
data: {
  styleObject: {
    color: 'red',
    fontSize: '13px'
  }
}

同样的,对象语法经常联合返回对象的计算属性应用。

数组语法

v-bind:style 的数组语法能够将多个款式对象利用到同一个元素上:

<div v-bind:style="[baseStyles, overridingStyles]"></div>

主动增加前缀

v-bind:style 应用须要增加浏览器引擎前缀的 CSS property 时,如 transform,Vue.js 会主动侦测并增加相应的前缀。

4. 事件绑定 v -on

v-on: 绑定处理事件 =“处理函数”,v-on:简写@

  • 绑定办法

    v-on:管制 html 标签的办法,将 办法 函数 绑定。

    <button v-on:click="addNum"></button> //addNum 是 methods 中定义的办法
    
    <!-- 简写 -->
    <button @click="addNum"></button>
  • 动静参数

    []中是变量 attributeName,能够动静的指定绑定的办法, 比方:click

    <button @[attributeName]="url"> ... </buttom>
  • 绑定的办法能够传递参数

    <!-- 绑定 click 办法,触发 add()函数,add()不须要传入参数时能够省略括号 -->
    <div id="example">
      <button v-on:click="add"> 点击了 {{count}} 次 </button>
    </div>
    <script>
        var example = new Vue({
          el: '#example',
          data: {count: 0},
          methods: {add: function () {
              // `this` 在办法里指向以后 Vue 实例
              this.count++;
            }
          }
        })
    </script>

    也能够用 JavaScript 间接调用办法 example.add()

    有时候绑定的函数须要拜访原始的 DOM 事件,能够用非凡变量 $event 把它作为参数传入办法

    <button v-on:click="warn(' 正告 ', $event)"> 点击 </button>
    // ...
    methods: {warn: function (message, event) {// 第个参数接管到“正告”字符串,第二个参数是 $event
        // 当初咱们能够拜访原生事件对象
        if (event) {event.preventDefault()
        }
        alert(message)
      }
    }

    在事件处理程序中调用 event.preventDefault()【阻止 Dom 元素默认行为】或 event.stopPropagation()【阻止冒泡传递】是十分常见的需要。只管咱们能够在办法中轻松实现这点,但更好的形式是:办法只有纯正的数据逻辑,而不是去解决 DOM 事件细节。为了解决这个问题,Vue.js 为 v-on 提供了 事件修饰符 按键修饰符

    事件修饰符

    • .stop
    • .prevent
    • .capture
    • .self
    • .once
    • .passive
    <!-- 阻止单击事件持续流传 -->
    <a v-on:click.stop="doThis"></a>
    
    <!-- 提交事件不再重载页面 -->
    <form v-on:submit.prevent="onSubmit"></form>
    
    <!-- 修饰符能够串联 -->
    <a v-on:click.stop.prevent="doThat"></a>
    
    <!-- 只有修饰符 -->
    <form v-on:submit.prevent></form>
    
    <!-- 增加事件监听器时应用事件捕捉模式 -->
    <!-- 即外部元素触发的事件先在此解决,而后才交由外部元素进行解决 -->
    <div v-on:click.capture="doThis">...</div>
    
    <!-- 只当在 event.target 是以后元素本身时触发处理函数 -->
    <!-- 即事件不是从外部元素触发的 -->
    <div v-on:click.self="doThat">...</div>
    
    <!-- 点击事件将只会触发一次 -->
    <a v-on:click.once="doThis"></a>
    
    <!-- 滚动事件的默认行为 (即滚动行为) 将会立刻触发 -->
    <!-- 而不会期待 `onScroll` 实现  -->
    <div v-on:scroll.passive="onScroll">...</div>

    留神:

    • 修饰符能够串联,然而应用修饰符时,程序很重要;相应的代码会以同样的程序产生。因而,用 v-on:click.prevent.self 会阻止 所有的点击,而 v-on:click.self.prevent 只会阻止对元素本身的点击。
    • 不要把 .passive.prevent 一起应用,因为 .prevent 将会被疏忽,同时浏览器可能会向你展现一个正告。请记住,.passive 会通知浏览器你 不想阻止事件的默认行为

    按键修饰符

    <!-- 只有按下的键是 `Enter`,开释按键时,调用 `submit()` 函数 -->
    <input v-on:keyup.enter="submit">
    
    <!-- 只有按下的键是 `PageDown`,开释按键时,调用 `onPageDown()` 函数 -->
    <input v-on:keyup.page-down="onPageDown">
    
    
  • 内联解决

    <div id="example">
      <button v-on:click="count++"> 点击了 {{count}} 次 </button>
    </div>
    <script>
        var example= new Vue({
          el: '#example',
          data: {count: 0}
        })
    </script>
    

5. 表单绑定 v -model

v-model 指令在表单 <input><textarea><select> 元素上创立双向数据绑定。

v-model实质上是 v-bind 绑定元素属性和 v-on 绑定元素事件的组合

  • text 和 textarea 元素应用 value 属性 和 input 事件;
  • checkbox 和 radio 应用 checked 属性 和 change 事件;
  • select 字段将 value 作为 prop 并将 change 作为事件。

举例:

  • 文本框

    输入框显示的是 message 绑定的值

    <input v-model="message" placeholder="edit me">
  • 多行文本

    <textarea v-model="message" placeholder="add multiple lines"></textarea>

    留神:在文本区域插值 (<textarea>{{text}}</textarea>) 并不会失效

  • 复选框

    • 单个复选框。这外面的 v -modle 是布尔值,复选框选中时 checked 变量是 true,反之是 flase

      <input type="checkbox" id="checkbox" v-model="checked">
    • 多个复选框。这外面的 v -modle 值是空数组,复选框选中时,会把对应的 value 值按选中的程序 push 到空数组中;勾销选中,会间接把数组中对应的 value 值删除

      <div id='example'>
          
        <input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
        <label for="jack">Jack</label>
          
        <input type="checkbox" id="john" value="John" v-model="checkedNames">
        <label for="john">John</label>
        
        <br>
        <span>Checked names: {{checkedNames}}</span>
      </div>
      new Vue({
        el: '#example',
        data: {checkedNames: [] // 因为是多选,所以是数组
        }
      })
    • 单选按钮。这外面的 v -modle 值,选中时是对应的 value 值

      <div id="example">
          
        <input type="radio" id="one" value="One" v-model="picked">
        <label for="one">One</label>
        <br>
        <input type="radio" id="two" value="Two" v-model="picked">
        <label for="two">Two</label>
        <br>
        <span>Picked: {{picked}}</span>
      </div>
      new Vue({
        el: '#example',
        data: {picked: ''}
      })
  • 下拉抉择框。选中时 v -model 值是对应 <option> 中的值,增加 value 属性,则是对用 option 中 value 的值

    <div id="example">
      <select v-model="selected">
          // 第一个是默认选项,要设置成 disable
        <option disabled value=""> 请抉择 </option>
        <option>A</option>
        <option>B</option>
        <option>C</option>
      </select>
      <span>Selected: {{selected}}</span>
    </div>
    new Vue({
      el: '#example',
      data: {selected: ''}
    })

    v-for 渲染 下拉抉择框。v-model 的值是选中 option 的 value 值

    <select v-model="selected">
      <option v-for="option in options" v-bind:value="option.value">
        {{option.text}}
      </option>
    </select>
    <span>Selected: {{selected}}</span>
    new Vue({
      el: '...',
      data: {
        selected: 'A',
        options: [{ text: 'One', value: 'A'},
          {text: 'Two', value: 'B'},
          {text: 'Three', value: 'C'}
        ]
      }
    })
  • 修饰符:.lazy .number .trim

    // 输出实现后失去焦点后,才渲染数据,不是只有输出变动,就始终从新渲染
    <input v-model.lazy="msg" >
    // 转成数字
    <input v-model.number="age" type="number">
    // 去掉空白字符
    <input v-model.trim="msg">

6. 条件渲染 v -if

  • v-ifv-else

    // 能够管制 html 元素和 <template>
    // 变量只能是布尔值, 能够管制元素是否可见
    <h1 v-if="awesome">Vue is awesome!</h1>
    <h1 v-else-if>1</h1>
    <h1 v-else>2</h1>
  • v-showv-if 不同的是,html 会被保留,只是暗藏了。

    <h1 v-show="ok">Hello!</h1>
  • 用 key 治理可复用的元素

    Vue 会尽可能高效地渲染元素,通常会 复用已有元素 而不是从头开始渲染 Dom 元素。这么做使 Vue 变得十分快

    <template v-if="loginType">
      <label> 用户名 </label>
      <input placeholder="请输出用户名">
    </template>
    
    <template v-else>
      <label> 邮箱 </label>
      <input placeholder="请输出邮箱">
    </template>

    留神:通过管制 loginType 的虚实,来管制显示哪局部。Vue 只是替换 label 和 input 的内容,并不会从新渲染 Dom 元素,所以一旦在输入框中输出数据,即便切换 loginType 的值,数据也不会被革除,依然会被显示在输入框. 比方 loginType 为真时,输出用户名 A,loginType 为假时,输出用户名 B, 相互切换 loginType 的值,input 输入框会保留对应的数据

    强制更新组件的方法(还能够通过扭转 key 值, 可一让同一个组件每次 key 变动就会从新渲染 Dom 元素): 通过给元素增加惟一的 key,该元素就会被从新渲染。以下代码,label 未应用惟一的 key,所以依然不会被从新渲染 Dom, 仅仅是替换内容

    <template v-if="loginType">
      <label> 用户名 </label>
      <input placeholder="请输出用户名" key="username">
    </template>
    
    <template v-else>
      <label> 邮箱 </label>
      <input placeholder="请输出邮箱" key="email">
    </template>

    新旧 children 中的节点只有程序是不同的时候,最佳的操作应该是通过挪动元素的地位来达到更新的目标。

    须要在新旧 children 的节点中保留映射关系,以便可能在旧 children 的节点中找到可复用的节点。key 也就是 children 中节点的惟一标识。

    留神:key 要是字符串或者数值类型

7. 列表渲染v-for

  • 遍历数组v-for="item in items"

    <!-- 还有第二个参数,即索引 item,v-for="(item, index) in items"-->
    <ul id="example-1">
      <li v-for="item in items">
        {{item.message}}
      </li>
    </ul>
    var example1 = new Vue({
      el: '#example-1',
      data: {
        items: [{ message: 'Foo'},
          {message: 'Bar'}
        ]
      }
    })

    你也能够用 of 代替 in 作为分隔符,因为它更靠近 JavaScript 迭代器的语法:

    <div v-for="item of items"></div>
  • 遍历对象

    <!-- 还有第二个参数,即对象中的 key 值,v-for="(value, name) in object-->
    <!-- 还有第三个参数,即索引 index,v-for="(value, name, index) in object"-->
    <ul id="example2" class="demo">
      <li v-for="value in object">
        {{value}}<!-- 是对象的值 -->
      </li>
    </ul>
    new Vue({
      el: '#v-for-object',
      data: {
        object: {
          title: 'How to do lists in Vue',
          author: 'Jane Doe',
          publishedAt: '2016-04-10'
        }
      }
    })
  • 当 Vue 正在更新应用 v-for 渲染的元素列表时,它默认应用“就地更新”的策略, 即渲染元素变动 , 对应地位的 Dom 间接更新。所以,如果数据项的程序被扭转,Vue 不会挪动 DOM 元素 的程序来匹配数据项的程序的扭转,而是就地更新每个元素,并且确保它们在每个索引地位正确渲染。

    这个默认的模式是高效的,然而 只实用于不依赖子组件状态或长期 DOM 状态 (例如:表单输出值) 的列表渲染输入

    为了给 Vue 一个提醒,以便它能跟踪每个节点的身份,从而复用和从新排序现有元素,你须要为每项提供一个惟一 key

    <div v-for="item in items" v-bind:key="item.id">
      <!-- 内容 -->
    </div>

    留神:key 要是字符串或者数值类型

6. 自定义指令

定义自定义指令

  • 全局

    // 注册一个全局自定义指令 `v-focus`
    Vue.directive('focus', {// 定义的指令名,不要加 v -
      // 当被绑定的元素插入到 DOM 中时……
      inserted: function (el) {//el 指的是该自定义指令的调用者,即增加该指令的 dom 元素(有点像函数中的形参)// 写具体性能:对聚焦元素
        el.focus()}
    })
    
    new Vue()
  • 部分定义

    成果: 页面一载入,input 就会主动取得焦点(默认须要本人点击 input 获取焦点)

    <template>
      <div id="app">
        <input v-focus>
      </div>
    </template>
    
    <script>
    
    export default {
      name: 'App',
      data() {return {};
      },
      directives: {
        focus: {inserted(el) {//el 是增加指令的元素 
            el.focus(); //focus 是 dom 的办法},
        },
      },
    };
    </script>

应用自定义指令

<input type="text" v-focus>

3.3 过滤器

Vue2.x 版本没有内置过滤器

用于对文本格式化解决

过滤器能够用在两个中央:双花括号插值和 v-bind 表达式 (后者从 2.1.0+ 开始反对)。过滤器应该被增加在 JavaScript 表达式的尾部,由“管道”符号“|”批示:

定义过滤器

  • 在 Vue选项 中定义部分过滤器

    filters: {capitalize: function (value) {if (!value) return ''
        value = value.toString()
        return value.charAt(0).toUpperCase() + value.slice(1)
      }
    }
  • 全局过滤器(在 new Vue 之前)

    Vue.filter('capitalize', function (value) {if (!value) return ''
      value = value.toString()
      return value.charAt(0).toUpperCase() + value.slice(1)
    })
    
    new Vue({// ...})

过滤器的应用

<!——"|" 前是变量名,"|" 后是过滤器名。变量绑定的值作为过滤器的第一个参数
     ——>
<!-- 在双花括号中 -->
{{message | capitalize}}
{{message | filterA | filterB}}<!--filterA 的 return 作为 filterB 的一个参数 -->
{{message | filterA('arg1', arg2) }}<!--filter 的参数是 message 和本人传的参数 arg1 arg2-->   
    
    
<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>
  • 后果: 把输出值的第一个字符大写

3.4 Vue 组件

组件特点

  • 组件是一种封装
  • 是一种非凡的 Vue 实例
  • 组件能够复用,每用一次组件就会有一个新的实例被创立,实例间互不影响

理论开发中,应用第三方组件

定义及应用组件

留神:命名组件名倡议应用 ’ abc-xyz ‘ 模式(小写字母用 – 连贯)

  • 注册全局组件(写在 new Vue({}) 之前)

    <!DOCTYPE html>
    <html>
        <head>
            <title></title>
            <!-- 通过 CDN 引入 vue.js-->
            <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        </head>
    
    
        <body>
            <div id="app">
                <!-- 应用自定义的组件 -->
                <button-counter></button-counter>
            </div>
            <script>
                // 定义一个名为 button-counter 的新组件
                Vue.component('button-counter', {data() { //data 必须是函数
                        return {count: 0}
                    },
                    template: '<button v-on:click="changeCount">You clicked me {{count}} times.</button>',
                    methods: {changeCount() {this.count++;}
    
                    }
                })
                new Vue({
                    el: "#app",
                    data: {}})
            </script>
    
        </body>
    </html>
    
  • 注册部分组件(写在 Vue 选项中)

    <!DOCTYPE html>
    <html>
        <head>
            <title></title>
            <!-- 通过 CDN 引入 vue.js-->
            <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        </head>
    
    
        <body>
            <div id="app">
                <!-- 应用自定义的组件 -->
                <button-counter></button-counter>
            </div>
            <script>
                new Vue({
                    el: '#app',
                    data: { },
                    // 部分组件
                    components: {
                        "button-counter": { // 组件名
                            data() { //data 必须是函数
                                return {count: 0}
                            },
                            template: '<button v-on:click="changeCount">You clicked me {{count}} times.</button>',
                            methods: {changeCount() {this.count++;}
    
                            }
                        }
                    }
                })
            </script>
    
        </body>
    </html>
  • 模块化零碎中的部分注册

    有些时候,咱们将封装的一个通用组件放在一个独自的 .vue 文件中,能够通过以下形式引入到以后 .vue 文件中,在 vue 实例的 components 字段援用

组件模板的定义

好文

即 template 字段内定义的组件模板

  • html 文件里,在组件内抉择 template 字段选项, 间接在字段前面写
  • X-Template

    html 文件里,这种形式是在一个 <script> 元素中,并为其带上 text/x-template 的类型,而后通过一个 id 将模板援用过来。

      <!DOCTYPE html>
      <html>
          <head>
              <title></title>
              <!-- 通过 CDN 引入 vue.js-->
              <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
          </head>
      
      
          <body>
              <div id="app">
                  <!-- 应用自定义的组件 -->
                  <button-counter></button-counter>
              </div>
              <script type="text/x-template" id="hello-world-template">
                    <p>Hello hello hello</p>
              </script>
              <script>
                  new Vue({
                      el: '#app',
                      data: { },
                      // 部分组件
                      components: {
                          "button-counter": { // 组件名
                              data() { //data 必须是函数
                                  return {count: 0}
                              },
                              template: '#hello-world-template',
                              methods: {changeCount() {this.count++;}
      
                              }
                          }
                      }
                  })
              </script>
      
          </body>
      </html>                                  
  • .vue 文件里的一个 <template> 元素来定义模板。

每个组件实例互不影响

组件嵌套

无论是全局还是部分组件,都能够间接嵌套,即在定义时,template 字段中能够写别的组件

组件通信

官网文档—拜访元素 & 组件

组件通信总结 + 原理剖析

父组件向子组件传递数据

props 是自定义组件时的一个 字段 ,参数是一个数组,每个数组元素都是一个组件的属性,通过 props 你能够在自定义的组件上注册一些自定义属性。应用自定义组件时,能够把值通过自定义的属性,传递给 template 中的子组件。 留神:每个实例化的组件,传递参数互不影响

<!-- 把参数通过 title 属性,传递到组件中的 template 中的{{title}}-->
<div id="app">
    <blog-post title="第一章"></blog-post>
    <blog-post title="第二章"></blog-post>
</div>
<script>
        Vue.component('blog-post', {props: ['title'],
            template: '<h3>{{title}}</h3>'
        })
        new Vue({
              el: '#app',
              data: {message:200}         
        })        
</script>    

循环渲染自定义组件,并把值通过自定义属性title,传递到 template 中

<blog-post
  v-for="post in posts"
  v-bind:key="post.id"
  v-bind:title="post.title"
></blog-post>
<script>
    Vue.component('blog-post', {props: ['title'],
                template: '<h3>{{title}}</h3>'
    })
    
    new Vue({
      el: '#app',
      data: {
        posts: [{ id: 1, title: '第一章'},
          {id: 2, title: '第二章'},
          {id: 3, title: '第三章'}
        ]
      }
    })
</script>

留神:HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你应用 DOM 中的模板时,应用驼峰命名法的 prop 名,在应用该属性时,须要用等价的 kebab-case (短横线分隔命名)。【如果你应用字符串模板,那么这个限度就不存在了。即单 vue 页面,html 代码写在 <template> 标签中】

<blog-post post-title="hello!"></blog-post>
<!--props 中的属性如果应用驼峰命名,应用时改成 kebab-case 模式 -->
<script>
    Vue.component('blog-post', {props: ['postTitle'],// 这里应用的驼峰命名法
      template: '<h3>{{postTitle}}</h3>'
    })
</script>

子组件向父组件传递数据<span id=” 子组件向父组件传递数据 ”></span>

<div id="app">
    <!-- 两个子组件的 count 值也是独立的,不会相互影响的 -->
    <button-counter @clicknow="clicknow"></button-counter>
    <button-counter @clicknow="clicknow"></button-counter>
</div>
<script>
     // 第一子组件
    Vue.component('button-counter', {data(){
        return {count: 0}
      },
      template: '<button v-on:click="add"> 点击了 {{count}} 次 </button>',
      methods:{add(){
               this.count++; 
               // 子组件触发的 add 办法,通过 this.$emit,把 count 的值放入 clicknow 函数之中,并在父组件调用,就会把子组件的数据传输到父级组件
               this.$emit('clicknow',this.count);
            }

      }
    })
    
    // 实例化 Vue 对象
    new Vue({
        el:"#app",
        data:{},
        methods:{clicknow(e){
                // 这里的 e 就是子组件的 count
                console.log(e);
            }
        }
        
    })
</script>

兄弟组件通信

Event Bus 实现跨组件通信 Vue.prototype.$bus = new Vue

Vuex

跨级组件通信

Vuex

$attrs、$listeners

Provide、inject

插槽

在自定义组件的 template 字段中,增加 <slot></slot>,那么应用子组件时就会将子组件开闭标签间的内容填充到 template 中 <slot></slot> 所在的地位

<!DOCTYPE html>
<html>
    <head>
        <title></title>
        <!-- 通过 CDN 引入 vue.js-->
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    </head>


    <body>
        <div id="app">
            <button-counter @clicknow="clicknow"> 这里是插槽 </button-counter>
            <button-counter @clicknow="clicknow"></button-counter>
        </div>
        <script>
            // 第一子组件
            Vue.component('button-counter', {data() {
                    return {count: 0}
                },
                template: '<div style="border: solid red 1px; margin-bottom: 10px;"> <slot></slot> <button v-on:click="add"> 点击了 {{count}} 次 </button></div>',
                methods: {add() {
                        this.count++;
                        // 子组件触发的 add 办法,通过 this.$emit,把 count 的值放入 clicknow 函数之中,并在父组件调用,就会把子组件的数据传输到父级组件
                        this.$emit('clicknow', this.count);
                    }

                }
            })

            // 实例化 Vue 对象
            new Vue({
                el: "#app",
                data: {},
                methods: {clicknow(e) {
                        // 这里的 e 就是子组件的 count
                        console.log(e);
                    }
                }

            })
        </script>

    </body>
</html>

组件上反对 v -model

这样解决自定义组件,就能够反对 v -model 了

<script>
    Vue.component('custom-input', {props: ['value'],
      template: `
        <input
          v-bind:value="value"
          v-on:input="$emit('input', $event.target.value)"
        >
      `
    })
</script>
<custom-input v-model="searchText"></custom-input>

动静组件

点击不同的按钮,上面对应就会显示不同的组件

上述内容能够通过 Vue 的 <component> 元素加一个非凡的 is 属性来实现:

即,点击不同按钮时,更改 currentTabComponent 的值为对应组件名,就能够把 component 标签变成对应的自定义标签

<component v-bind:is="currentTabComponent"></component>

官网给出的示例代码

<!DOCTYPE html>
<html>
  <head>
    <title>Dynamic Components Example</title>
    <script src="https://unpkg.com/vue"></script>
    <style>
      .tab-button {
        padding: 6px 10px;
        border-top-left-radius: 3px;
        border-top-right-radius: 3px;
        border: 1px solid #ccc;
        cursor: pointer;
        background: #f0f0f0;
        margin-bottom: -1px;
        margin-right: -1px;
      }
      .tab-button:hover {background: #e0e0e0;}
      .tab-button.active {background: #e0e0e0;}
      .tab {
        border: 1px solid #ccc;
        padding: 10px;
      }
    </style>
  </head>
  <body>
    <div id="dynamic-component-demo" class="demo">
      <button
        v-for="tab in tabs"
        v-bind:key="tab"
        v-bind:class="['tab-button', { active: currentTab === tab}]"
        v-on:click="currentTab = tab"
      >
        {{tab}}
      </button>

      <component v-bind:is="currentTabComponent" class="tab"></component>
    </div>

    <script>
      Vue.component("tab-home", {template: "<div>Home component</div>"});
      Vue.component("tab-posts", {template: "<div>Posts component</div>"});
      Vue.component("tab-archive", {template: "<div>Archive component</div>"});

      new Vue({
        el: "#dynamic-component-demo",
        data: {
          currentTab: "Home",
          tabs: ["Home", "Posts", "Archive"]
        },
        //currentTab 变动触发,返回给 component 元素的 is 属性,渲染出对应的组件标签  
        computed: {currentTabComponent: function() {return "tab-" + this.currentTab.toLowerCase();
          }
        }
      });
    </script>
  </body>
</html>

在动静组件上应用 keep-alive

在下面例子中,但在这些组件之间切换的时候,你有时会想放弃这些组件的状态,以防止重复重渲染导致的性能问题。(比方,在一个组件中选中某个 checkbox,然而切换到另一个组件,再切换回来,选中状态就会隐没,keep-alive 能够放弃状态)

<!-- 失活的组件将会被缓存!-->
<keep-alive>
  <component v-bind:is="currentTabComponent"></component>
</keep-alive>

异步组件(待补充)

文档 - 异步组件

在大型利用中,咱们可能须要将利用宰割成小一些的代码块,并且只在须要的时候才从服务器加载一个模块。为了简化,Vue 容许你以一个工厂函数的形式定义你的组件,这个工厂函数会异步解析你的组件定义。Vue 只有在这个组件须要被渲染的时候才会触发该工厂函数,且会把后果缓存起来供将来重渲染。

3.5 非凡个性

Vue 实例裸露了一些有用的实例属性和办法。它们都有前缀 $,以便与用户定义的属性辨别开来。

var data = {a: 1}
var vm = new Vue({
  el: '#example',
  data: data
})

vm.$data === data // => true
vm.$el === document.getElementById('example') // => true

// $watch 是一个实例办法
vm.$watch('a', function (newValue, oldValue) {// 这个回调将在 `vm.a` 扭转后调用})

还有 $refs$emit()

1. ref 操作 dom 元素

<div id="app">
        <input ref="a1"  v-model="message">
          <p ref="a2"> 咱们 </p>
</div>
new Vue({
          el: '#app',
          data: {message:''},
          mounted(){
              // 生命周期函数,加载结束
              console.log(this.$refs)// 能够依据 ref 值来获取对用的 dom 元素
              this.$refs.a1.focus()// 让 ref=a1 的 input 获取焦点}
        })        

2.emit 定义事件

程序化的事件侦听器

emit 用法查看这里

$emit 的定义一个事件,通过事件还能够传递参数,它能够被 v-on 侦听,然而 Vue 实例同时在其事件接口中提供了其它的办法。咱们能够(eventHandler 是侦听到事件后,的处理函数):

  • 通过 $on(eventName, eventHandler) 侦听一个事件
  • 通过 $once(eventName, eventHandler) 一次性侦听一个事件
  • 通过 $off(eventName, eventHandler) 进行侦听一个事件

你通常不会用到这些,然而当你须要在一个组件实例上手动侦听事件时,它们是派得上用场的。

4. 过渡和动画(待补充)

列表适度

动画过渡

点击按钮后,文字缓缓淡出淡入

<div id="demo">
  <button v-on:click="show = !show">
    Toggle
  </button>
  <transition name="fade">
    <p v-if="show">hello</p>
  </transition>
</div>
new Vue({
  el: '#demo',
  data: {show: true}
})
.fade-enter-active, .fade-leave-active {transition: opacity .5s;}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {opacity: 0;}

5.Vue 发送网络申请

接口服务器

这里咱们不搭建服务端,而是应用 json-server,这个工具能够把 json 文件托管成一个 WEB 服务器。特点:基于 Express,反对 GET,POST,PUT,DELETE 办法,反对跨域

  • 官网:https://www.npmjs.com/package…
  • 装置:npm install -g json-server(全局装置,cmd 处于任意目录下就能够)
  • 创立 db.json 文件
{
  "posts": [{ "id": 1, "title": "json-server", "author": "typicode"},
    {"id": 2, "title": "json-server2", "author": "typicode2"}
    {"id": 3, "title": "json-server3", "author": "typicode3"}

  ],
  "comments": [{ "id": 1, "body": "some comment", "postId": 1}
  ],
  "profile": {"name": "typicode"}
}
  • 启动 Json 服务:json-server --watch db.json(时 cmd 在 db.json 所在的目录下输出,json-server --watch --port 3000 json-server更改端口)
  • 拜访http://localhost:3000/posts/1, 会返回{"id": 1, "title": "json-server", "author": "typicode"}

明确接口规定是什么

RESTful 接口规定标准,参见这篇文章 http://www.ruanyifeng.com/blo…

特点:只须要关怀申请形式,不必必关怀 增删改查时应用不同的 url

json-server 工具利用 RESTful 接口规定:(以下是 json-server 运行后的接口,曾经实现了能够间接用)

  • routers(路由)
GET 申请页面      
POST 增加数据(上传数据),数据在申请头中
PUT 批改数据,数据在申请体中
DELETE 删除数据 
GET    /posts     查问所有数据
GET    /posts/1   查问 id 为 1 的数据
POST   /posts
PUT    /posts/1   批改 id 为 1 的数据

DELETE /posts/1  删除 id 为 1 的数据
  • 分页查问
//10 items are returned by default
GET /posts?_page=7
// 一页返回 20 条数据
GET /posts?_page=7&_limit=20
  • 含糊查问
//title 是字段,查问 title 中和 server 类似的记录
GET /posts?title_like=server
  • 其余更多标准

postman 测试接口

postman 是一款接口测试工具

  • 官网:下载
  • 界面

  • 应用

POST 状态码 201,其余都是 200

发送申请

应用 Ajax 还是 axios 发送网络申请的实质都是 XMLHttpRequest,咱们能够应用插件不便操作

axios: 能够在任何中央用于发送申请,举荐应用

中文文档

github 官网

npm install axios装置到对应目录

根本应用规定

<script src="axios.js 的地位"></script>
<script>
    //GET
    axios
        .get(url)//  /posts 查所有,/posts/id 查指定 id
        .then((res)=>{res.data// 获取的数据})
        .catch((error)=>{// 出错后的解决})
    
    
    
    //POST
    axios
        .post(url,{提交的数据})//  /posts
        .then((res)=>{res.data// 获取的数据})
         .catch((error)=>{// 出错后的解决})
        
    
    
     //PUT
    axios
        .put(url,{提交的数据})//  /put/id 批改指定 id 的数据
        .then((res)=>{res.data// 获取的数据})
         .catch((error)=>{// 出错后的解决})
    
    
    //DELETE
    axios
        .delete(url)//  /delete/id 删除指定 id 的数据
        .then((res)=>{res.data// 获取的数据})
         .catch((error)=>{// 出错后的解决})
    
</script>

在网页中应用

<script src="node_modules/axios/dist/axios.js"></script>
    <script>
        axios
            .get("http://localhost:3000/posts")
            .then((res)=>{const {status,data}=res
                if(status==200){console.log(data)
                }
            })
            .catch((err)=>{})
            
        axios
            .post("http://localhost:3000/posts",{
                titlt:"hh",
                author:"hh"
            })
            .then((res)=>{const {status,data}=res
                if(status==201){console.log(data)
                }
            })
            .catch((err)=>{})

        axios
            .put("http://localhost:3000/posts/5",{
                titlt:"hh2",
                author:"hh2"
            })
            .then((res)=>{const {status,data}=res
                if(status==200){console.log(data)
                }
            })
            .catch((err)=>{})
            
        axios
            .delete("http://localhost:3000/posts/5")
            .then((res)=>{const {status,data}=res
                if(status==200){console.log(data)
                }
            })
            .catch((err)=>{})
    </script>

在 Vue 我的项目中应用

new Vue({
    el: '#app',
    data: {lists: [],
    },
    mounted() {this.getAll() // 查问所有数据在网页加载后调用,用生命周期
    },
    methods: {
        // 把增删改查函数写在 methods 中
        // 网页加载后调用,放到 mounted 中
        // 通过触发工夫调用的, 给对应的 html 元素增加 v-on: 指令
        getAll() {
            axios
                .get("http://localhost:3000/posts")
                .then((res) => {
                    const {
                        status,
                        data
                    } = res
                    if (status == 200) {this.lists = data}
                })
                .catch((err) => {})
        }
    }
})

其余

import axios from 'axios'
// 给 axios 拜访的 API 后面增加固定前缀
axios.defaults.baseURL="http://8.131.105.244:3000"

// 通过 axios 拦截器增加 token 验证
axios.interceptors.request.use(config=>{
    // 给 config 的 headers 减少新的字段 Authorization,把 token 增加进去
    config.headers.Authorization=window.sessionStorage.getItem('token')
    return config
})

6.vue-cil 工具

vue-cli: 生成一套规范的 vue 我的项目目录

vue-cil 称为脚手架

vue-cli 是全局命令行工具

vue-cli 反对热更新,删除代码,自动更新启动服务器;如果批改我的项目配置文件,须要从新npm run dev

启动 UI 界面:vue ui

官网:https://cli.vuejs.org/zh/

应用步骤

  • 装置:前提是要求曾经装置 nodejs
// 装置 vue-cli3
npm install -g @vue/cli
// 使得 vue-cli3 能够应用 vue-cli2 的 vue-init 指令
npm install -g @vue/cli-init
  • 创立我的项目步骤

在须要的文件夹下, 执行以下指令。起名用英文名,不能有 vue webpack 等关键字

//vue init 我的项目模板 我的项目名
vue init webpack-simple heroes

依据须要抉择,抉择默认就间接回车

// 进入我的项目目录
// 装置创立我的项目的依赖包,主动找到 package.json 文件中的记录进行装置
npm install
// 运行自带的服务器,弹出网页
npm run dev

目录构造剖析

  • src 下

    • assets:搁置 css,字体,图标等动态资源
    • App.vue:.vue是组件文件,组成:tempalte script style。App.vue 相当于整个我的项目的根组件,所有其余组件都要放到 App.vue 中,并导出,在 main.js 导入
    • main.js : 程序的入口文件,导入各种包(App.vue 组件,路由 router.js)
    //ES6 对于模块的应用
    // 导出
    export default{}
    // 导入, 本人写的导入写地址, 别的间接写包名
    import App from './App.vue'
    //Nodejs 中
    module.exports
    require()
    // 默认 main.js
    import App from './App.vue'
    
    new Vue({
      el: '#app',
      render: h => h(App)
    })

  • .babelrc : 因为有的浏览器不反对 ES6,应用 babel 进行转换,这个文件是 bable 的配置文件
  • .editorconfig 编辑器的配置文件
  • .gitignore: 记录的 git 的排除疏忽文件
  • index.html 的代码中引入了 build.js,vue-cli 中应用了 webpack,webpack 把资源解决放在在 build.js 中
  • package.json 记录我的项目依赖的包和其余我的项目信息
//package.json 中有一个 scripts 的字段,key 是 value 命令的简写,cmd 调用时写 "npm run 简写"
"scripts": {
    "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
    "build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
  }
  • package-lock.json 记录我的项目依赖的包所应用的版本和下载地址
  • webpack.config.js:webpack 的配置文件,vue-cli 中应用了 webpack

.vue 文件构造

<template>
     <div>
         // 页面模板
    </div>
</template>

<script>
    // 引入页面所有的包
    import echarts from "echarts"
    //
    export default{
        // 生命周期函数
        created(){},
        mounted(){// 这时候页面的 dom 元素曾经渲染结束了},
        // 数据
        data(){
            return{a:"xx"}
        },
        // 办法
        methods:{}}
</script>

<style lang="less" scoped>
    //lang="less" 反对 less 语法
    //scoped 示意 style 款式只在组件内起作用
    // 款式
</style>

7.vue-router

路由根底

vue-router 是 Vue 的官网插件, 性能是实现前端路由

  • 装置 npm install vue-router

援用前必须引入 vue.js

<script src="/path/to/vue.js"></script>
<script src="/path/to/vue-router.js"></script>
  • 应用

<div id="app" >
        <!--1. 设置跳转 -->
        <!--      网址 url 变成 '....#/a' . to 属性的值中 / 能够省略   -->
         <router-link to="/a"> 首页 </router-link>
         <router-link to="/b"> 热点 </router-link>
         <router-link to="/c"> 视频 </router-link>
        <!--2. 容器 -->
        <router-view></router-view>
    </div>

<script src="node_modules/vue/dist/vue.js"></script>
    <script src="node_modules/vue-router/dist/vue-router.js"></script>
    <script>
        //3. 设置渲染页面
        var comA = {template: '<div> 首页 </div>'}
        var comB = {template: '<div> 热点 </div>'}
        var comC = {template: '<div> 视频 </div>'}
        //4. 路由配置(设置路由匹配规定)var routes = [{ path: '/a', component: comA},
          {path: '/b', component: comB},
          {path: '/c', component: comC}
          
        ]
        //5. 实例化路由
        const router = new VueRouter({routes // (缩写) 相当于 routes: routes
        })



        new Vue({
              el: '#app',
              //6. 挂载路由, 当引入 vue-router 后就 vue 多了一个选项 router
               router // (缩写) 相当于 router: router                           
        })        
</script>    

动静路由

适宜于不同标识(url 中 #之后的局部)渲染雷同页面,例如:详情页

<div id="app" >
        <!--1. 设置跳转 -->
         <router-link to="/baskball"> 篮球 </router-link>
         <router-link to="/football"> 足球 </router-link>
         <router-link to="/pp"> 乒乓球 </router-link>
        <!--2. 容器 -->
        <router-view></router-view>
    </div>
    <script src="node_modules/vue/dist/vue.js"></script>
    <script src="node_modules/vue-router/dist/vue-router.js"></script>
    <script>
        //3. 设置渲染页面
            //$route 是获取 routes 对象
        var Ball= {template: '<div> 球组件{{$route.params.id}}</div>' }
    
        //4. 配置路由(设置路由匹配规定)var routes = [
            // 动静路由的写法,id 是个参数
          {path: '/:id', component: Ball},
          
        ]
        //5. 实例化路由
        const router = new VueRouter({routes // (缩写) 相当于 routes: routes
        })



        new Vue({
              el: '#app',
              //6. 挂载路由
               router // (缩写) 相当于 router: router                           
        })        
    </script>    
    

从数据中获取 url 标识

data 中绑定 user 的值为 home,同时通过应用 v-bind 绑定 to 属性,赋值跳转的链接

<div id="app" >
        <!--1. 设置跳转 -->
        <!--      网址 url 变成 '....#/a'     -->
         <router-link v-bind:to="user"> 家 </router-link>
         
        <!--2. 容器 -->
        <router-view></router-view>
    </div>
<script src="node_modules/vue/dist/vue.js"></script>
    <script src="node_modules/vue-router/dist/vue-router.js"></script>
    <script>
        //3. 设置渲染页面
        var Ball= {template: '<div> 球组件 </div>'}
    
        //4. 配置路由(设置路由匹配规定)var routes = [{ path: '/home', component: Ball}
          
        ]
        //5. 实例化路由
        const router = new VueRouter({routes // (缩写) 相当于 routes: routes
        })



        new Vue({
              el: '#app',
              data:{user:"home"},
              //6. 挂载路由
               router // (缩写) 相当于 router: router                           
        })        
    </script>    

总结下

// 固定
<router-link to="固定的链接"></router-link>

// 动静路由, 点击不同链接,加载同一个页面,只不过页面数据不同(适宜做详情页)<router-link to="/a"></router-link>
<router-link to="/b"></router-link>
var Ball= {template: '<div> 球组件{{$route.params.id}}</div>' }
var routes = [{path: '/:id', component: Ball}]

//user 的值由 data 确定
<router-link v-bind:to="uer 的值"></router-link>
// 后面的例子,配置路由只有 path,component 字段,能够增加 name 字段
<router-link v-bind:to="{path:' 指定配置的路由中的 path 字段值 '}"></router-link>

<router-link v-bind:to="{name:' 指定配置的路由中的 name 字段值 '}"></router-link>

重定向

//4. 配置路由(设置路由匹配规定)var routes = [
            // 形式一:指定 path
            // {path:"/",redirect:{//     path:"/home"}
            // },
            // 形式二:只当 name
            {path:"/",redirect:{name:"aa"}
                
            },
            {path: '/home',name:"aa", component: Ball},
            // 写在最初,如果拜访的地址不是下面的,就重定向
            { path: '*',name:"aa", redirect:{path:"/home"} 
            }
          
        ]

编程式导航

如果不能应用 <router-link> 进行路由,能够用 v -bind 绑定办法,在办法里,获取路由对象 $router.push({path:'门路'} ) 进行路由,参数{“门路”} {path:” 门路 ”} {name:”routes 中的 name”}

<div id="app" >
        <!--1. 设置点击跳转 -->
         <button @click="urlchange()"> 点击跳转 </button>
        <!--2. 容器 -->
        <router-view></router-view>
    </div>
<script src="node_modules/vue/dist/vue.js"></script>
    <script src="node_modules/vue-router/dist/vue-router.js"></script>
    <script>
        //3. 设置渲染页面
        var Ball= {template: '<div> 球组件 </div>'}
    
        //4. 配置路由(设置路由匹配规定)var routes = [{ path: '/home',name:"aa", component: Ball},
        
        ]
        //5. 实例化路由
        const router = new VueRouter({routes // (缩写) 相当于 routes: routes
        })



        new Vue({
              el: '#app',
              data:{user:"home"},
              //6. 挂载路由
              router:router,
            // 跳转办法
              methods:{urlchange(){this.$router.push({path:'/home'})
                  }
              }
                              
                              
        })        
    </script>    

嵌套路由

<div id="app" >
        <!--1. 设置跳转 -->
        <router-link to="/a"> 音乐 </router-link>
        <!--2. 容器 -->
        <router-view></router-view>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
    <script src="node_modules/vue-router/dist/vue-router.js"></script>
    <script>
        //3. 设置渲染页面
        var music= { template: `<div>
            <router-link to="/music/rock"> 摇滚 </router-link>
            <router-link to="/music/pop"> 风行 </router-link>
            <router-view></router-view>
        </div>`
        }
            // 二级渲染页面            
        var musicSub={template:`<div>
            我是 music 音乐组件
        </div>`
        }
        //4. 配置路由(设置路由匹配规定)var routes = [
            { path: '/a',
              component:music,
              // 二级路由,children 和 routes 的用法一样
              children:[{
                  path:"/music/:id",
                  component:musicSub
              }] 
            },
            
        ]

        //5. 实例化路由
        const router = new VueRouter({routes // (缩写) 相当于 routes: routes
        })



        new Vue({
              el: '#app',
              data:{user:"home"},
              //6. 挂载路由
              router // (缩写) 相当于 router: router                                                      
        })        
    </script>    

路由守卫

在路由跳转之前 咱们次要是利用 vue-router 提供的钩子函数 beforeEach()对路由进行判断。

// 路由守卫
router.beforeEach((to,from,next)=>{if(to.matched.some(res=>res.meta.isLogin)){// 判断是否须要登录
            if (sessionStorage['username']) {next();
            }else{
                next({
                    path:"/login",
                    query:{redirect:to.fullPath}
                });
            }

        }else{next()
        }
    });

export default router;

to 示意将要跳转到的组件 (指标组件)
console.log(from); //(源组件)
next();
next 是一个函数
next() 进入下一个组件的钩子函数
next(false) 阻止跳转 中断导航
next(“/login”) 进入指定的组件的钩子函数

补充

let routeData = this.$router.resolve({path:'exteriorColourPicList',query:{albumId:albumId}});
window.open(routeData.href);

8.Vuex

Vuex 官网文档

Vuex 是一个专为 Vue.js 利用程序开发的 状态管理模式。它采纳集中式存储管理利用的所有组件的状态,并以相应的规定保障状态以一种可预测的形式发生变化。

看以下例子

state 和 getters 的用法

mutations 的用法:

更改 Vuex 的 store 中的状态的惟一办法是提交 mutation。且不能是异步办法

action 的用法:

Action 是用来解决异步操作的, 提交的是 mutation,而不是间接变更状态

const store = new Vuex.Store({
  state: {count: 0},
  mutations: {increment (state) {state.count++}
  },
  actions: {incrementASync (context) {context.commit('increment')
    }
  }
})
this.$store.dispatch('incrementASync')

9. 案例

## 我的项目起步

应用 vue-cli 新建我的项目

组件化

  1. 在 boostrap 官网找到 bootstrap3 的后盾模板(https://v3.bootcss.com/exampl…),右键“查看网页源代码”,复制 body 中的 html 代码,粘贴到 App.vew 文件的 template 下
  2. 在 main.js 中引入款式文件
npm install bootstrap@3// 下载 bootstrp
//style.css 请到网页源码中找出引入款式的链接,进入,复制源码,新建文件 style.css 放在 assets 目录下

// 导入款式文件
import "../node_modules/bootstrap/dist/css/bootstrap.css"
import "./assets/style.css"

报错,有不能辨认的文件,须要需改 webpack.config.js

增加以下文件(察看文档构造,就晓得加在哪里了)

{test:/\.(ttf|woff2|woff|eot)$/,
        loader:'file-loader',
            options:{name:'[name].[ext]?[hash]'
            }
}
  1. npm run dev

  1. 在原来的根底上新建

  1. 把之前粘贴的到 App.vue 的 template 中的 html,划分成一个一个组件,别离放在 .vue 文件中的 template 中
  2. 【5】的具体步骤,在 App.vue 文件中 template 提取 nav 头部,放到 appNav.vue 的 template 中(特地留神一点:template 中内容必须在同一个根 div 下),script 中减少 export default{}进行导出。接下来在 App.vue 中操作,如下:
<!--App.vue 文件 -->

<template>
  <div id="app">
    <!-- 顶部栏 -->
    <!-- 3. 通过组件名,应用组件,命名是驼峰标识 appNav , 能够应用 app-nav,不是的话,间接写 -->
    <app-nav></app-nav>
    // 其余部分不变
<template>
    
<script>
      // 在 App.vue 中应用 appnav.vue 组件
      //1. 引入组件
      import appNav from './components/common/appnav.vue'
      
    export default {
    name: 'app',
    //2. 通过选项注册组件
    components:{
      //appNav:appNav 简写成 appNav
      appNav
    },
    data() {
      return {msg: 'Welcome to Your Vue.js App'}
    }
  }
</script>

<style>
 // 不变
</style>

提取路由模块

src 下新建 router 文件,在其下建 router.js

//router.js

// 模块:路由, 官网文档中给出的应用形式
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
// 导入要渲染的组件
import List from "../components/list/list.vue"
import Bar from "../components/bar/bar.vue"
import Foo from "../components/foo/foo.vue"

var routes=[{name:"heroes",path:"/heroes",component:List},
  {name:"bar",path:"/bar",component:Bar},
  {name:"foo",path:"/foo",component:Foo}
]

var router=new VueRouter({
  // 全局设置激活 router-link 后,给对应 router-link 的 class 属性减少的值. 点击后减少 active 选中成果
  linkExactActiveClass:'active',
  routes
})

// 导出 router 在 main.js 中应用
export default router
//main.js 中减少

//1. 导入路由
import router from "./router/router.js"

new Vue({
  el: '#app',
  render: h => h(App),
  //2. 挂载导入的对象
  router
})

appslider.vue 侧边栏组件

<template>
  <ul class="nav nav-sidebar">
      <!--tag="li" 是让 router-link 在渲染 html 页面是时渲染成 <li>-->
    <router-link to="/heroes" tag="li"><a> 英雄列表 </a></router-link>
    <router-link to="/bar" tag="li"><a> 武器列表 </a></router-link>
    <router-link to="/foo" tag="li"><a> 配备 </a></router-link>

  </ul>
</template>

<script>
  export default{ }
</script>

<style>
</style>

应用接口服务器 server-json

在 heroes 我的项目外建设 server 文件夹,新建 db.json

{
    "heroes":[{"id":1,"name":"张 1","gender":"男"},
        {"id":2,"name":"张 2","gender":"男"},
        {"id":3,"name":"张 3","gender":"女"},
        {"id":4,"name":"张 4","gender":"男"},
        {"id":5,"name":"张 5","gender":"男"},
        {"id":6,"name":"张 6","gender":"女"},
        {"id":7,"name":"王 1","gender":"男"},
        {"id":8,"name":"王 2","gender":"女"}
    ]
}

启动 json-server(装置啥的看之前的【4】)

json-server --watch db.json

list.vue 显示取出数据, 删除数据

在我的项目中装置 axio,用于发送申请

npm install axios

在 list.vue 中

<template>
   <div>
     <h2 class="sub-header">Section title</h2>
     <a class="btn btn-success"> 减少 </a>
     <div class="table-responsive">

         <thead>
           <tr>
             <th>ID</th>
             <th> 姓名 </th>
             <th> 性别 </th>
             <th> 操作 </th>
           </tr>
         </thead>
         <tbody>
           <tr v-for="list in lists" v-bind:key="list.id">
             <td>{{list.id}}</td>
             <td>{{list.name}}</td>
             <td>{{list.gender}}</td>
             <td>
                <a v-on:click="deleteById(list.id)"> 删除 </a>
             </td>
           </tr>
         </tbody>
       </table>
     </div>
   </div>
</template>

<script>
  //from 后间接写包名
  import axios from 'axios'

  export default{data(){
      return{lists:[]
      }
    },
    mounted(){this.getData()
    },
    methods:{
      // 查问所有
      getData(){
        axios
              .get("http://localhost:3000/heroes")
              .then(res=>{const {status,data}=res

                if(status==200){this.lists=data}

              })
      },

      deleteById(id){
        axios
              .delete("http://localhost:3000/heroes/"+id)
              .then((res)=>{const {status,data}=res
                if(status==200){console.log("删除胜利")
                  this.getData()}
              })

      }

    }
  }
</script>

<style>
</style>

优化

//1. 不必在每个须要网络申请的 vue 组件中引入 axios
//2. 增删改查,同一个 api,只不过申请形式不同和加不加 id 值,把根底 url 提取进去,放到全局

// 以下代码均写在 main.js 中
import axios from 'axios'
// 解决问题 1,把 axios 进行全局配置,在任意一个 vue 组件中能够间接应用,不必 import
Vue.prototype.axios=axios
// 各个组件文件中,不用引入 axios, 用 axios 的中央换成 this.axios.get/post/put/delete 即可

// 解决问题 2
axios.defaults.baseURL="api 中根底 url,比方:http://localhost:3000/"
//this.axios.get("要拼接的内容") 函数会主动把 baseURL 拼接上

10. 综合案例

前端初始化

前提装置好 vue-cli

// 形式一:命令启动(要建我的项目的目录下)vue init webpack 我的项目名
// 呈现的让选的内容, 只写了重点的选啥

// 选 runtime+compiler
vue build 

// 查看代码标准,不标准报错。不启用
use ESlint to lint you code? n

// 开启单元测试
set up unit tests? n
// 开启端对端的测试
setup e2e tests with Nigtwatch? n

// 启动我的项目
npm run dev

/////////////////////////////////////////////////

// 形式二:界面启动(要建我的项目的目录下)vue ui

初始化 git 仓库倡议  init project

选手动

装置 Babel,Router, 应用配置文件

预设保不保留均可 

创立我的项目后,进入我的项目仪表盘,找到插件,装置 vue-cli-plugin-element。导入形式抉择按需导入

装置依赖,axios(装置到运行依赖)启动我的项目 工作 -》server-》运行

我的项目托管

增加公钥

登录码云 www.gitee.com

// 本地任意目录 cmd 输出后,连按 3 次回车
ssh-keygen -t rsa -C "xxxxx@xxxxx.com"

// 例如,我的输出
ssh-keygen -t rsa -C "1270433876@qq.com" 

// 在本地找到【用户 / 电脑名 /.ssh 文件夹】,关上其中的 id_ras.pub, 复制内容粘贴到 码云 -》设置 -》ssh 公钥 -》增加公钥中,增加公钥
// 本地任意目录 cmd
ssh -T git@gitee.com 
抉择 yes,增加以后主机到可信赖列表

托管本我的项目

登录码云 -》新建仓库

在我的项目目录关上 cmd,

// 在任意地位下,做全局配置
git config --global user.name "用户名"
git config --global user.email "邮箱"

// 在我的项目目录下
git status
git add .
git commit -m"第一次提交"

// 在我的项目目录下,把本地新建的 vue 我的项目上传到码云
git remote add origin https://gitee.com/hyj1270433876/test.git 
git push -u origin master

// 弹出窗口,输入码云的邮箱,明码,确定就上传胜利了
//// 本地提交到指定近程分支上

// 如果本地以后所在分支与近程分支同名
git push 近程仓库名 近程分支名

// 不同名须要建设本地分支与近程分支之间的映射
git push 近程仓库名 本地分支名: 近程分支名

登陆组件

token 维持登录状态原理

http 是无状态的

  • 通过 cookie 在客户端记录登录状态
  • 通过 session 在服务端登录记录状态
  • 通过 token 形式维持登录状态(存在跨域问题时应用)

因为浏览器前后台之间存在跨域问题,所以应用 token

用户点击登录验证明码,服务端返回 token,客户端接管到保留在 sessionStorage(会话存在期间存在,完结销毁)中

以下是 token 原理图

开发登录性能

  • git status查看是否有未提交的内容,有的话 git addgit commit -m "提交标注"
  • 创立新分支 git checkout -b 分支名, 并切换到该分支,git branch 查看所有分支,加 * 的是以后分支
  • 输出 vue ui 进入 vue 面板,工作 -》server-》运行,待编译实现后,vue 我的项目就能跑起来了,点击“启动 app”
  • 整顿目录把本来默认的我的项目目录清理下
  • 在 components 中新建组件login.vue
  • 应用反对 less 语言,在 vue-cli 面板中,依赖 -》增加依赖 -》搜寻 less-loader , 抉择 ” 开发依赖 ”-》装置,在搜寻 less 装置到“开发依赖”。在 工作 -》server-》点击进行后,再启动
<!--login.vue-->
<template>
    <div> 登陆组件 </div>
</template>

<script>
    export default{}
</script>

<style lang="less" scoped>
    /*
    lang="less" 是使 style 反对 less 语言
    scoped 是款式尽在组件内失效
    */
    
</style>
  • 在 router.js 中注册 login.vue 组件。import 和增加 routes
import VueRouter from 'vue-router'

import Login from './components/login.vue'


Vue.use(VueRouter)

const routes = [{path: '/',redirect:'/login'},
    {path: '/login',name: 'login',component: Login}
    
 
]

const router = new VueRouter({routes})

export default router
  • 因为应用 element-ui 时,应用的按需导入,所以在 plugins/element.js 进行导入,注册
import Vue from 'vue'

// 因为应用 element-ui 时,应用的按需导入,所以应用什么组件都要导入
import {Button} from 'element-ui'
import {Form,FormItem} from 'element-ui'
import {Input} from 'element-ui'

// 注册成全局可用的标签
Vue.use(Button)
Vue.use(Form)
Vue.use(FormItem)
Vue.use(Input)

  • 进一步欠缺 login.vue, element 文档中给出了 from 组件的办法,如何应用?
// 给按钮绑定 v-on="d()" 给表单组件绑定 ref="a" 

<script>
    // 能够有生命周期函数
    
    data(){// 必须有 return 的函数
        return{}},
      methods:{// 对象
          d(){
              //this 是以后的 vue 对象
            this.$refs.a. 文档中给出的办法    
          }
      }
</script>
  • 最终 login.vue
<template>
    <div class="login_container">
        <div class="login_box">
            <!-- 头像 -->
            <div class="avatar">
                <img src="../assets/logo.png"/>
            </div>
            <!-- 表单区 -->
            <el-form ref="form"  :model="loginForm" :rules="loginFormRules" label-width="0px" class="login_form" >
              <!-- 账号 -->
              <el-form-item prop="manager_email" >
                <el-input v-model="loginForm.manager_email"  prefix-icon="czs-user"></el-input>
              </el-form-item>
              <!-- 明码 -->
              <el-form-item  prop="manager_password">
                <el-input v-model="loginForm.manager_password"  prefix-icon="czs-lock" show-password></el-input>
              </el-form-item>
              <!-- 按钮 -->
              <el-form-item  class="btns">
                 <el-button type="primary" @click="login()"> 登录 </el-button>
                 <el-button type="info" disabled> 重置 </el-button>
              </el-form-item>
            </el-form>
        </div>
        
    </div>
</template>

<script>
    export default{data(){
            return{
                // 登录表单的数据绑定对象
                loginForm:{
                    manager_email:'',
                    manager_password:''
                },
                // 表单验证规定
                loginFormRules:{
                    //email
                    manager_email:[//trigger:blur 失去焦点时触发
                        {required: true, message: '请输出登陆邮箱', trigger: 'blur'}
                    ],
                    manager_password:[{ required: true, message: '请输出明码', trigger: 'blur'}
                    ]
                    
                }
            } 
        },
        methods:{login(){        
                this.$refs.form.validate(valid=>{if(!valid){// 格局验证不对,res 为 flase
                        return
                    }
                    console.log("表单验证胜利")
                    var res= this.$axios
                                        .post('/login',this.loginForm)        
                                        .then((res)=>{    
                                            // 特地留神,后盾 api 接口返回的内容放在了 res.data 字段了,res.date.date 才有 manager_id,token 等字段
                                            // console.log(res.data.err_code)
                                            // console.log(res.data.message)
                                            // console.log(res.data.data.token)
                        
                                            if(res.data.err_code==1){
                                                // 登陆失败
                                                this.$message.error(res.data.message)
                                            }else{
                                                // 登陆胜利
                                                this.$message.success(res.data.message)
                                                // 把 token 保留到本地
                                                window.sessionStorage.setItem("token",res.data.data.token)
                                                // 编程式导航
                                                this.$router.push("/home")
                                            }
                                                
                                            
                                        })
                                
                })
            }
        }
        
    }
</script>

<style lang="less" scoped>
.login_container{
    background-color:#274a6c;
    height: 100%;
}
.login_box{
    width: 450px;
    height: 300px;
    background-color: white;
    border-radius: 3px;
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%,-50%);
}
.avatar{
    height: 130px;
    width: 130px;
    border: 1px  solid #eee;
    border-radius: 50%;
    padding: 10px;
    box-shadow:  0 0 10px #ddd;
    position: absolute;
    left:50%;// 相对定位,该元素的左边缘挪动到父元素的 50% 的
    transform: translate(-50%,-50%);// 横向,纵向 挪动本元素宽,高的 50%
    background-color: white;
    img{
        width: 100%;
        height: 100%;
        border-radius: 50%;
        background-color: #eee;
    }
    
}
    
.btns{
    display: flex;
    justify-content: flex-end;
}
.login_form{
    position: absolute;
    bottom: 0;
    width: 100%;
    padding: 0 30px;
    box-sizing: border-box;
}
    
</style>
  • 路由导航守卫
// 挂载路由导航守卫
router.beforeEach((to,from,next)=>{
    //to 示意要拜访的门路
    //from 示意从哪个门路跳转而来
    //next 是一个函数,示意放行
    
    // 如果拜访登录页,间接放行
    if(to.path=="/login") return next()
    // 拜访的不是登录页,查看是否有 token, 没有返回登录页
    const tokenStr=window.sessionStorage.getItem("token")        
    if(!tokenStr){console.log("尚未登陆,主动跳转回首页")
        return next('/login')
    }
    next()})
  • 实现退出登录,实际上就是清空本地存储的 token

    在 home.vue 组件中

<template>
    <el-button type="info" @click="logout"> 退出 </el-button>
</template>

<script>
        
    export default{
        methods:{logout(){window.sessionStorage.clear()
                this.$router.push("/login")
            }
        }
    }
</script>

<style lang="less" scoped>
</style>

git 提交到近程仓库

git status // 查看本地分支
git add .  // 显示有文件,就用这个提交到暂存区
git commit -m"实现登录模块" // 提交到本地仓库

git branch  // 查看以后分支,因为之前写我的项目时,进入的 login 分支,所以会显示 login 前有个 * 号



/////////////////////////////////////////
//1. 切换到主分支 master, 再从主分支合并 login 分支
git checkout master // 切换到主分支
git branch // 查看以后分支是否为 master
git merge login // 在 master 上执行,合并 login 分支
git push // 提交到近程仓库
/////////////////////////////////////////
//2. 处在 login 分支。因为 login 分支是第一次提交所以用以下命令
git push -u origin login// 网站上就能够看到 login 分支了,之后提交间接 git push 就好

// 如果是克隆指定分支 git clone -b 近程分支名称 代码仓库地址 
// 如果我的项目文件夹不是从近程分支上拉下来的,本人在本地写的,先初试化成 git 目录,git init,就会生成.git 文件夹,能够自动记录变动了。

补充个小插曲, 我在应用 git 时呈现了谬误,所有文件都回滚了

git log // 查 commit 提交本地仓库的记录,我找到了对应记录的 commit 后的一串码即 sha 码,应用如下命令复原
git reset --hard sha 码 // 执行后本地文件复原

后盾主页组件

通过 axios 拦截器增加 token 验证

// 在 main.js 中增加,在 import axios from 'axios' 之后

// 每次 axios 申请时,header 会带着 token 传到服务端,服务端能够通过对 token 的判断确定用户身份

axios.interceptors.request.use(config=>{
    // 给 config 的 headers 减少新的字段 Authorization,把 token 增加进去
    config.headers.Authorization=window.sessionStorage.getItem('token')
    return config
})

home 开发

留神下,vue 中有些标签属性的值为布尔型或是数字,然而属性值只能是 string。比方:unique-opened 的属性值是布尔值,一种:间接写 unique-opened; 另一种:v-bind:unique-opened=”true”,Vue 会把字符串 true 转成布尔值。对于数值,只有一种形式:v-bind:XXXX=”20″,Vue 会把字符串 20 转成数字

<el-menu background-color="#333744"  text-color="#fff" active-text-color="#4787d5" v-bind:default-active="active" unique-opened>
    

home.vue.

<template>
    <el-container>
        <!-- 头部区 -->
        <el-header>
            <div>
                <span> 电商后盾管理系统 </span>
            </div>
            <el-button type="info" @click="logout"> 退出 </el-button>
        </el-header>
        <!-- 主体区 -->
        <el-container>
            <!-- 侧边栏 -->
            <el-aside width="200px">
                <el-menu background-color="#333744"  text-color="#fff" active-text-color="#4787d5" v-bind:default-active="active" unique-opened router>
                    <!-- 一级菜单  index 接管的是字符串,所以用 数字 +''-->
                    <el-submenu v-bind:index="menulist.id+''" v-for="menulist in menulists" v-bind:key="menulist.id">
                        <!-- 一级菜单模板区 -->
                        <template slot="title">
                            <!-- 图标 -->
                            <i v-bind:class="menulist.icon"></i>
                            <!-- 文本 -->
                            <span style="margin-left: 10px;">{{menulist.menuName}}</span>
                        </template>                
                        <!-- 二级菜单 -->
                        <el-menu-item v-on:click="saveActive(second_item.path)" v-bind:index="second_item.path+''" v-for="second_item in menulist.children" v-bind:key="second_item.id">
                            <template slot="title">
                                <!-- 图标 -->
                                <i class="el-icon-menu"></i>
                                <!-- 文本 -->
                                <span>{{second_item.secondMenuName}}</span>
                            </template>    
                        
                        </el-menu-item>

                    </el-submenu>
                    
                    
                </el-menu>
            </el-aside>

            <!-- 右侧内容 -->
            <el-main>
                <router-view></router-view>
            </el-main>
        </el-container>
    </el-container>
</template>

<script>
    export default {created(){this.getMenuList()
            this.active=window.sessionStorage.getItem("active")
        },
        
        data(){
            return{
                // 左侧菜单数据
                menulists:[],
                active:''
            }
        },
        methods: {logout() {window.sessionStorage.clear()
                this.$router.push("/login")
            },
            getMenuList(){
                this.$axios
                            .get("/menus")
                            .then(backinfo=>{
                                // 要用的数据在 backinfo.data 中, 把这个值给 res。该写法是 ES6 解构对象。const {data:res}=backinfo                                
                                if(res.err_code==0){console.log(res)
                                    this.menulists=res.data
                                    //return this.$message.success(res.message)
                                }
                            })
                
            },
                
            saveActive(active){
                // 因为一刷新,选中项就会不高亮,所以存下来,每次 created 执行,再从 sessionStorage 中取
                window.sessionStorage.setItem("active",active)
                this.active=window.sessionStorage.getItem("active")
            }
        }
    }
</script>

<style lang="less" scoped>
    /*el 组件名,能够间接当类名用 */
    .el-container {height: 100%;}

    .el-header {
        background-color: #373c41;
        display: flex;
        justify-content: space-between;
        align-items: center;
        /* 外部文本 */
        color: white;
        font: 20px;

        >div {
            display: flex;
            align-items: center;

            span {margin-left: 15px;}
        }
    }

    .el-aside {
        background-color: #323744;
        .el-menu{border-right: none;}
    }
        

    .el-main {background-color: #f6f8fa;}
</style>

新建一个 customer.vue。给 router.js 的 routes 中配置路由,home 中有一个 router-viwe, 给其配置子路由

// 拜访后盾时,主动加载上“用户子组件”{path: '/home',name: 'home',component: Home, redirect:"/customer" ,children:[{path: '/customer',name: 'customer',component: Customer}
    ]}

这里的 customer.vue 曾经写了局部内容,咱们在前面会具体写出

即时消息告诉

树形构造的生成

以下性能

有时候咱们要做一个树形构造,然而往往树的层数是不定的,如何在一个表中存下来呢? 应用如下的表构造,parent_id 就是父层级的 order_type_id,is_endnode 如果是 1 则为最终结点

如何把把表格读出来后变成树形的构造呢

exports.getAllOrderType=function(callback){function treeData(source){let cloneData = JSON.parse(JSON.stringify(source))    // 对源数据深度克隆
          return cloneData.filter(father=>{let branchArr = cloneData.filter(child=>father.order_type_id == child.parent_id)    // 返回每一项的子级数组
            branchArr.length>0 ? father.children = branchArr : ''   // 如果存在子级,则给父级增加一个 children 属性,并赋值
            return father.parent_id==0;      // 返回第一层
          })
    }
    queryStr="select * from  order_type"
    sql.query(connectionString, queryStr, (err, rows) => {if(err){ }else{var a=treeData(rows)
            callback({err_code:0,message:'获取订单类型胜利',data:a,affectedRows:0})
        }
    })
}

以下例子是把树形数据,扁平化的计划

let res = []        // 用于存储递归后果(扁平数据)// 递归函数
const fn = (source)=>{
    source.forEach(el=>{res.push(el)
        el.children && el.children.length>0 ? fn(el.children) : ""        // 子级递归
    })
}
 
// 树形数据
const arr = [{ id: "1", rank: 1},
    { id: "2", rank: 1,
        children:[{ id: "2.1", rank: 2},
            {id: "2.2", rank: 2} 
        ] 
    },
    { id: "3", rank:1,
        children:[ 
            { id: "3.1", rank:2, 
                children: [ 
                    { id:'3.1.1', rank:3,
                        children:[ 
                            { id: "3.1.1.1", rank: 4, 
                                children:[{ id: "3.1.1.1.1", rank: 5}
                                ] 
                            } 
                        ] 
                    } 
                ] 
            } 
        ] 
    }
]
 
fn(arr)             // 执行递归函数
console.log(res)    // 查看后果

文件的上传与下载

Vue+element+Nodejs

pdf 文件的在线预览和打印

间接点击存储在服务器的文件的直链,如果是 pdf。chrome 会间接关上 pdf,如果是 doc 会间接进入下载页面

统计图表组件

关上 vue 的图形化面板【vue ui 命令进入】=> 依赖 => 增加依赖 => 运行依赖 => 搜寻 echart=> 装置

Echart 的官网:https://echarts.apache.org/zh/tutorial.html#5%20%E5%88%86%E9%92%9F%E4%B8%8A%E6%89%8B%20ECharts

// 在.vue 文件中 不能应用 <script src="xx"></script> 的形式引入  要在 .vue 文件的 <script> 标签中退出 import xxx from "xxx"【写在 export default】

myChart.setOption(result); 中 result 是配置和数据,在这里咱们把配置写在 data()中, 数据是发动网络申请返回的,应用 Lodash.js 来进行深拷贝,合并在一起并赋值给 result。

Lodash.js 的官网:https://www.lodashjs.com/

<template>
    <!-- 2. 为 ECharts 筹备一个具备大小(宽高)的 Dom -->
    <div id="main" style="width: 600px;height:400px;"></div>
</template>

<script>
    //1. 导入 echarts
    import echarts from "echarts"
    // 深拷贝, 创立一个新对象, 把两个对象合并成一个放进去
    //lodash 提供了一个 merge(对象 1, 对象 2)函数, 返回一个新对象
    import _ from "lodash"
    export default {mounted() { //dom 元素曾经渲染结束
            // 3. 基于筹备好的 dom,初始化 echarts 实例
            var myChart = echarts.init(document.getElementById('main'));
            // 4. 指定图表的【配置项】和【数据】const result = _.merge(this.option, res.data)
            // 5. 应用刚指定的【配置项】和【数据】显示图表。myChart.setOption(result);

        },
        data() {
            return {
                // 图的配置项
                option : {
                    title: {text: 'ECharts 入门示例'},
                    tooltip: {},
                    legend: {data: ['销量']
                    },
                    xAxis: {data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"]
                    },
                    yAxis: {},
                    series: [{
                        name: '销量',
                        type: 'bar',
                        data: [5, 20, 36, 10, 10, 20]
                    }]
                }

            }
        },
        method:{// 取回图的数据}
    }
</script>

<style>
</style>

vue 结构设计

https://www.processon.com/min…

举荐文章

《深入浅出 Vue.js》读书笔记

退出移动版