• vue学习


    什么是Vue.js

    • Vue.js 是目前最火的一个前端框架,React是最流行的一个前端框架(React除了开发网站,还可以开发手机App, Vue语法也是可以用于进行手机App开发的,需要借助于Weex)

    • Vue.js 是前端的主流框架之一,和Angular.js、React.js 一起,并成为前端三大主流框架!

    • Vue.js 是一套构建用户界面的框架,只关注视图层,它不仅易于上手,还便于与第三方库或既有项目整合。(Vue有配套的第三方类库,可以整合起来做大型项目的开发)

    • 前端的主要工作?主要负责MVC中的V这一层;主要工作就是和界面打交道,来制作前端页面效果;

     

     

    为什么要学习流行框架

    • 企业为了提高开发效率:在企业中,时间就是效率,效率就是金钱;

    • 企业中,使用框架,能够提高开发的效率;

     

    • 提高开发效率的发展历程:原生JS -> Jquery之类的类库 -> 前端模板引擎 -> Angular.js / Vue.js(能够帮助我们减少不必要的DOM操作;提高渲染效率;双向数据绑定的概念【通过框架提供的指令,我们前端程序员只需要关心数据的业务逻辑,不再关心DOM是如何渲染的了】)

    • 在Vue中,一个核心的概念,就是让用户不再操作DOM元素,解放了用户的双手,让程序员可以更多的时间去关注业务逻辑;

     

    • 增强自己就业时候的竞争力

    • 人无我有,人有我优

    • 你平时不忙的时候,都在干嘛?

    框架和库的区别

     

    • 框架:是一套完整的解决方案;对项目的侵入性较大,项目如果需要更换框架,则需要重新架构整个项目。

    • node 中的 express;

     

    • 库(插件):提供某一个小功能,对项目的侵入性较小,如果某个库无法完成某些需求,可以很容易切换到其它库实现需求。

      1. 从Jquery 切换到 Zepto

      1. 从 EJS 切换到 art-template

     

     

     

    Node(后端)中的 MVC 与 前端中的 MVVM 之间的区别

    • MVC 是后端的分层开发概念;

    • MVVM是前端视图层的概念,主要关注于 视图层分离,也就是说:MVVM把前端的视图层,分为了 三部分 Model, View , VM ViewModel

    • 为什么有了MVC还要有MVVM

     

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

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <!-- 1. 导入Vue的包 -->
      <script src="./lib/vue-2.4.0.js"></script>
    </head>
    
    <body>
      <!-- 将来 new 的Vue实例,会控制这个 元素中的所有内容 -->
      <!-- Vue 实例所控制的这个元素区域,就是我们的 V  -->
      <div id="app">
        <p>{{ msg }}</p>
      </div>
    
      <script>
        // 2. 创建一个Vue的实例
        // 当我们导入包之后,在浏览器的内存中,就多了一个 Vue 构造函数
        //  注意:我们 new 出来的这个 vm 对象,就是我们 MVVM中的 VM调度者
        var vm = new Vue({
          el: '#app',  // 表示,当前我们 new 的这个 Vue 实例,要控制页面上的哪个区域
          // 这里的 data 就是 MVVM中的 M,专门用来保存 每个页面的数据的
          data: { // data 属性中,存放的是 el 中要用到的数据
            msg: '欢迎学习Vue' // 通过 Vue 提供的指令,很方便的就能把数据渲染到页面上,程序员不再手动操作DOM元素了【前端的Vue之类的框架,不提倡我们去手动操作DOM元素了】
          }
        })
      </script>
    </body>
    
    </html>
    View Code

    Vue之 - 基本的代码结构插值表达式v-cloak

     使用 v-cloak 能够解决 插值表达式闪烁的问题

    Vue指令之v-textv-html

    Vue指令之v-bind的三种用法

    1. 直接使用指令v-bind

    2. 使用简化指令:

    3. 在绑定的时候,拼接绑定内容::title="btnTitle + ', 这是追加的内容'"4

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <style>
        [v-cloak] {
          /* display: none; */
        }
      </style>
    </head>
    
    <body>
      <div id="app">
        <!-- 使用 v-cloak 能够解决 插值表达式闪烁的问题 -->
        <p v-cloak>++++++++ {{ msg }} ----------</p>
        <h4 v-text="msg">==================</h4>
        <!-- 默认 v-text 是没有闪烁问题的 -->
        <!-- v-text会覆盖元素中原本的内容,但是 插值表达式  只会替换自己的这个占位符,不会把 整个元素的内容清空 -->
    
        <div>{{msg2}}</div>
        <div v-text="msg2"></div>
        <div v-html="msg2">1212112</div>
    
        <!-- v-bind: 是 Vue中,提供的用于绑定属性的指令 -->
        <!-- <input type="button" value="按钮" v-bind:title="mytitle + '123'"> -->
        <!-- 注意: v-bind: 指令可以被简写为 :要绑定的属性 -->
        <!-- v-bind 中,可以写合法的JS表达式 -->
    
        <!-- Vue 中提供了 v-on: 事件绑定机制 -->
        <!-- <input type="button" value="按钮" :title="mytitle + '123'" v-on:click="alert('hello')"> -->
    
    
        <input type="button" value="按钮" @click="show">
      </div>
    
    
      <script src="./lib/vue-2.4.0.js"></script>
    
      <script>
        var vm = new Vue({
          el: '#app',
          data: {
            msg: '123',
            msg2: '<h1>哈哈,我是一个大大的H1, 我大,我骄傲</h1>',
            mytitle: '这是一个自己定义的title'
          },
          methods: { // 这个 methods属性中定义了当前Vue实例所有可用的方法
            show: function () {
              alert('Hello')
            }
          }
        })
    
    
        /* document.getElementById('btn').onclick = function(){
          alert('Hello')
        } */
      </script>
    </body>
    
    </html>
    
    
    
    
    <!-- 1. 如何定义一个基本的Vue代码结构 -->
    <!-- 2. 插值表达式 和  v-text   -->
    <!-- 3. v-cloak -->
    <!-- 4. v-html -->
    <!-- 5. v-bind   Vue提供的属性绑定机制   缩写是 : -->
    <!-- 6. v-on     Vue提供的事件绑定机制   缩写是 @ -->
    View Code

    Vue指令之v-on跑马灯效果

    v-bind  是Vue提供的属性绑定机制   缩写是 : 
     v-on     Vue提供的事件绑定机制   缩写是 @ -->
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <!-- 1. 导入Vue包 -->
      <script src="./lib/vue-2.4.0.js"></script>
    </head>
    
    <body>
      <!-- 2. 创建一个要控制的区域 -->
      <div id="app">
        <input type="button" value="浪起来" @click="lang">
        <input type="button" value="低调" @click="stop">
    
        <h4>{{ msg }}</h4>
    
      </div>
    
      <script>
        // 注意:在 VM实例中,如果想要获取 data 上的数据,或者 想要调用 methods 中的 方法,必须通过 this.数据属性名  或  this.方法名 来进行访问,这里的this,就表示 我们 new 出来的  VM 实例对象
        var vm = new Vue({
          el: '#app',
          data: {
            msg: '猥琐发育,别浪~~!',
            intervalId: null // 在data上定义 定时器Id
          },
          methods: {
            lang() {
              // console.log(this.msg)
              // 获取到头的第一个字符
              // this
    
              if (this.intervalId != null) return;
    
              this.intervalId = setInterval(() => {
                var start = this.msg.substring(0, 1)
                // 获取到 后面的所有字符
                var end = this.msg.substring(1)
                // 重新拼接得到新的字符串,并赋值给 this.msg
                this.msg = end + start
              }, 400)
    
              // 注意: VM实例,会监听自己身上 data 中所有数据的改变,只要数据一发生变化,就会自动把 最新的数据,从data 上同步到页面中去;【好处:程序员只需要关心数据,不需要考虑如何重新渲染DOM页面】
            },
            stop() { // 停止定时器
              clearInterval(this.intervalId)
              // 每当清除了定时器之后,需要重新把 intervalId 置为 null
              this.intervalId = null;
            }
          }
        })
    
    
        // 分析:
        // 1. 给 【浪起来】 按钮,绑定一个点击事件   v-on   @
        // 2. 在按钮的事件处理函数中,写相关的业务逻辑代码:拿到 msg 字符串,然后 调用 字符串的 substring 来进行字符串的截取操作,把 第一个字符截取出来,放到最后一个位置即可;
        // 3. 为了实现点击下按钮,自动截取的功能,需要把 2 步骤中的代码,放到一个定时器中去;
      </script>
    </body>
    
    </html>
    View Code

    Vue指令之v-model双向数据绑定

     

    简易计算器案例

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
    </head>
    
    <body>
      <div id="app">
        <input type="text" v-model="n1">
    
        <select v-model="opt">
          <option value="+">+</option>
          <option value="-">-</option>
          <option value="*">*</option>
          <option value="/">/</option>
        </select>
    
        <input type="text" v-model="n2">
    
        <input type="button" value="=" @click="calc">
    
        <input type="text" v-model="result">
      </div>
    
      <script>
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {
            n1: 0,
            n2: 0,
            result: 0,
            opt: '+'
          },
          methods: {
            calc() { // 计算器算数的方法  
              // 逻辑:
              /* switch (this.opt) {
                case '+':
                  this.result = parseInt(this.n1) + parseInt(this.n2)
                  break;
                case '-':
                  this.result = parseInt(this.n1) - parseInt(this.n2)
                  break;
                case '*':
                  this.result = parseInt(this.n1) * parseInt(this.n2)
                  break;
                case '/':
                  this.result = parseInt(this.n1) / parseInt(this.n2)
                  break;
              } */
    
              // 注意:这是投机取巧的方式,正式开发中,尽量少用
              var codeStr = 'parseInt(this.n1) ' + this.opt + ' parseInt(this.n2)'
              this.result = eval(codeStr)
            }
          }
        });
      </script>
    </body>
    
    </html>
    View Code

    在Vue中使用样式

     

    使用class样式

    1. 数组

    <h1 :class="['red', 'thin']">这是一个邪恶的H1</h1>
    1. 数组中使用三元表达式

    <h1 :class="['red', 'thin', isactive?'active':'']">这是一个邪恶的H1</h1>
    1. 数组中嵌套对象

    <h1 :class="['red', 'thin', {'active': isactive}]">这是一个邪恶的H1</h1>
    1. 直接使用对象

    <h1 :class="{red:true, italic:true, active:true, thin:true}">这是一个邪恶的H1</h1>

     

    使用内联样式

    1. 直接在元素上通过 :style 的形式,书写样式对象

    <h1 :style="{color: 'red', 'font-size': '40px'}">这是一个善良的H1</h1>
    1. 将样式对象,定义到 data 中,并直接引用到 :style

    • 在data上定义样式:

    data: {
          h1StyleObj: { color: 'red', 'font-size': '40px', 'font-weight': '200' }
    }
    • 在元素中,通过属性绑定的形式,将样式对象应用到元素中:

    <h1 :style="h1StyleObj">这是一个善良的H1</h1>
    1. :style 中通过数组,引用多个 data 上的样式对象

    • 在data上定义样式:

    data: {
          h1StyleObj: { color: 'red', 'font-size': '40px', 'font-weight': '200' },
          h1StyleObj2: { fontStyle: 'italic' }
    }
    • 在元素中,通过属性绑定的形式,将样式对象应用到元素中:

    <h1 :style="[h1StyleObj, h1StyleObj2]">这是一个善良的H1</h1>

     vue中样式-style.html

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
    </head>
    
    <body>
      <div id="app">
        <!-- 对象就是无序键值对的集合 -->
        <!-- <h1 :style="styleObj1">这是一个h1</h1> -->
    
        <h1 :style="[ styleObj1, styleObj2 ]">这是一个h1</h1>
      </div>
    
      <script>
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {
            styleObj1: { color: 'red', 'font-weight': 200 },
            styleObj2: { 'font-style': 'italic' }
          },
          methods: {}
        });
      </script>
    </body>
    
    </html>
    View Code

    vue中样式-class.html

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
      <style>
        .red {
          color: red;
        }
    
        .thin {
          font-weight: 200;
        }
    
        .italic {
          font-style: italic;
        }
    
        .active {
          letter-spacing: 0.5em;
        }
      </style>
    </head>
    
    <body>
      <div id="app">
        <!-- <h1 class="red thin">这是一个很大很大的H1,大到你无法想象!!!</h1> -->
    
        <!-- 第一种使用方式,直接传递一个数组,注意: 这里的 class 需要使用  v-bind 做数据绑定 -->
        <!-- <h1 :class="['thin', 'italic']">这是一个很大很大的H1,大到你无法想象!!!</h1> -->
    
        <!-- 在数组中使用三元表达式 -->
        <!-- <h1 :class="['thin', 'italic', flag?'active':'']">这是一个很大很大的H1,大到你无法想象!!!</h1> -->
    
        <!-- 在数组中使用 对象来代替三元表达式,提高代码的可读性 -->
        <!-- <h1 :class="['thin', 'italic', {'active':flag} ]">这是一个很大很大的H1,大到你无法想象!!!</h1> -->
    
        <!-- 在为 class 使用 v-bind 绑定 对象的时候,对象的属性是类名,由于 对象的属性可带引号,也可不带引号,所以 这里我没写引号;  属性的值 是一个标识符 -->
        <h1 :class="classObj">这是一个很大很大的H1,大到你无法想象!!!</h1>
    
    
      </div>
    
      <script>
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {
            flag: true,
            classObj: { red: true, thin: true, italic: false, active: false }
          },
          methods: {}
        });
      </script>
    </body>
    
    </html>
    View Code

    Vue指令之v-forkey属性

    1. 迭代数组

    <ul>
    <li v-for="(item, i) in list">索引:{{i}} --- 姓名:{{item.name}} --- 年龄:{{item.age}}</li>
    </ul>
    1. 迭代对象中的属性


    <!-- 循环遍历对象身上的属性 -->

      <div v-for="(val, key, i) in userInfo">{{val}} --- {{key}} --- {{i}}</div>
    1. 迭代数字


    <p v-for="i in 10">这是第 {{i}} 个P标签</p>

     

    2.2.0+ 的版本里,当在组件中使用 v-for 时,key 现在是必须的。

     

    当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用 “就地复用” 策略。如果数据项的顺序被改变,Vue将不是移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。

     

    为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性。

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
    </head>
    
    <body>
      <div id="app">
        <!-- <p>{{list[0]}}</p>
        <p>{{list[1]}}</p>
        <p>{{list[2]}}</p>
        <p>{{list[3]}}</p>
        <p>{{list[4]}}</p> -->
    
        <p v-for="(item, i) in list">索引值:{{i}} --- 每一项:{{item}}</p>
    
      </div>
    
      <script>
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {
            list: [1, 2, 3, 4, 5, 6]
          },
          methods: {}
        });
      </script>
    </body>
    
    </html>
    View Code
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
        <script src="./vue-2.4.0.js"></script>
    </head>
    
    <body>
        <div id="app">
            <p v-for="(item,i) in list">索引值:{{i}}--每一项:{{item}}</p>
            <p v-for="(user ,i) in users">Id:{{user.id}}--名字:{{user.name}}--索引:{{i}}</p>
        </div>
        <script>
            var vm=new Vue({
               el:'#app',
               data:{
                   list:[1,2,3,4,5,6],
                   users:[
                       {id:1,name:'zs1'},
                       {id:2,name:'zs2'},
                       {id:3,name:'zs3'},
                       {id:4,name:'zs4'}
                   ]
               },
               methods:{}
            });
        </script>
    </body>
    
    </html>
    View Code
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
    </head>
    
    <body>
      <div id="app">
    
        <div>
          <label>Id:
            <input type="text" v-model="id">
          </label>
    
          <label>Name:
            <input type="text" v-model="name">
          </label>
    
          <input type="button" value="添加" @click="add">
        </div>
    
        <!-- 注意: v-for 循环的时候,key 属性只能使用 number获取string -->
        <!-- 注意: key 在使用的时候,必须使用 v-bind 属性绑定的形式,指定 key 的值 -->
        <!-- 在组件中,使用v-for循环的时候,或者在一些特殊情况中,如果 v-for 有问题,必须 在使用 v-for 的同时,指定 唯一的 字符串/数字 类型 :key 值 -->
        <p v-for="item in list" :key="item.id">
          <input type="checkbox">{{item.id}} --- {{item.name}}
        </p>
      </div>
    
      <script>
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {
            id: '',
            name: '',
            list: [
              { id: 1, name: '李斯' },
              { id: 2, name: '嬴政' },
              { id: 3, name: '赵高' },
              { id: 4, name: '韩非' },
              { id: 5, name: '荀子' }
            ]
          },
          methods: {
            add() { // 添加方法
              this.list.unshift({ id: this.id, name: this.name })
            }
          }
        });
      </script>
    </body>
    
    </html>
    View Code

    Vue指令之v-ifv-show

     

     一般来说,v-if 有更高的切换消耗而 v-show 有更高的初始渲染消耗。因此,如果需要频繁切换 v-show 较好,如果在运行时条件不大可能改变 v-if 较好。

     

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
    </head>
    
    <body>
      <div id="app">
    
        <!-- <input type="button" value="toggle" @click="toggle"> -->
        <input type="button" value="toggle" @click="flag=!flag">
    
        <!-- v-if 的特点:每次都会重新删除或创建元素 -->
        <!-- v-show 的特点: 每次不会重新进行DOM的删除和创建操作,只是切换了元素的 display:none 样式 -->
    
        <!-- v-if 有较高的切换性能消耗 -->
        <!-- v-show 有较高的初始渲染消耗 -->
    
        <!-- 如果元素涉及到频繁的切换,最好不要使用 v-if, 而是推荐使用 v-show -->
        <!-- 如果元素可能永远也不会被显示出来被用户看到,则推荐使用 v-if -->
        <h3 v-if="flag">这是用v-if控制的元素</h3>
        <h3 v-show="flag">这是用v-show控制的元素</h3>
    
      </div>
    
      <script>
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {
            flag: false
          },
          methods: {
            /* toggle() {
              this.flag = !this.flag
            } */
          }
        });
      </script>
    </body>
    
    </html>
    View Code

     品牌列表案例:

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
      <link rel="stylesheet" href="./lib/bootstrap-3.3.7.css">
      <!-- 需要用到Jquery吗??? -->
    </head>
    
    <body>
      <div id="app">
        <!-- {{1+1}} -->
    
    
        <div class="panel panel-primary">
          <div class="panel-heading">
            <h3 class="panel-title">添加品牌</h3>
          </div>
          <div class="panel-body form-inline">
            <label>
              Id:
              <input type="text" class="form-control" v-model="id">
            </label>
    
            <label>
              Name:
              <input type="text" class="form-control" v-model="name" @keyup.f2="add">
            </label>
    
            <!-- 在Vue中,使用事件绑定机制,为元素指定处理函数的时候,如果加了小括号,就可以给函数传参了 -->
            <input type="button" value="添加" class="btn btn-primary" @click="add()">
    
            <label>
              搜索名称关键字:
              <!-- 注意: Vue中所有的指令,在调用的时候,都以 v- 开头 -->
              <input type="text" class="form-control" v-model="keywords" id="search" v-focus v-color="'green'">
            </label>
          </div>
        </div>
    
    
    
        <table class="table table-bordered table-hover table-striped">
          <thead>
            <tr>
              <th>Id</th>
              <th>Name</th>
              <th>Ctime</th>
              <th>Operation</th>
            </tr>
          </thead>
          <tbody>
            <!-- 之前, v-for 中的数据,都是直接从 data 上的list中直接渲染过来的 -->
            <!-- 现在, 我们自定义了一个 search 方法,同时,把 所有的关键字,通过传参的形式,传递给了 search 方法 -->
            <!-- 在 search 方法内部,通过 执行 for 循环, 把所有符合 搜索关键字的数据,保存到 一个新数组中,返回 -->
            <tr v-for="item in search(keywords)" :key="item.id">
              <td>{{ item.id }}</td>
              <td v-text="item.name"></td>
              <td>{{ item.ctime | dateFormat() }}</td>
              <td>
                <a href="" @click.prevent="del(item.id)">删除</a>
              </td>
            </tr>
          </tbody>
        </table>
    
    
    
      </div>
    
    
      <div id="app2">
        <h3 v-color="'pink'" v-fontweight="900" v-fontsize="50">{{ dt | dateFormat }}</h3>
      </div>
    
      <script>
        // 全局的过滤器, 进行时间的格式化
        // 所谓的全局过滤器,就是所有的VM实例都共享的
        Vue.filter('dateFormat', function (dateStr, pattern = "") {
          // 根据给定的时间字符串,得到特定的时间
          var dt = new Date(dateStr)
    
          //   yyyy-mm-dd
          var y = dt.getFullYear()
          var m = dt.getMonth() + 1
          var d = dt.getDate()
    
          // return y + '-' + m + '-' + d
    
    
    
          if (pattern.toLowerCase() === 'yyyy-mm-dd') {
            return `${y}-${m}-${d}`
          } else {
            var hh = dt.getHours()
            var mm = dt.getMinutes()
            var ss = dt.getSeconds()
    
            return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
          }
        })
    
    
        // 自定义全局按键修饰符
        Vue.config.keyCodes.f2 = 113
    
        // 使用  Vue.directive() 定义全局的指令  v-focus
        // 其中:参数1 : 指令的名称,注意,在定义的时候,指令的名称前面,不需要加 v- 前缀, 
        // 但是: 在调用的时候,必须 在指令名称前 加上 v- 前缀来进行调用
        //  参数2: 是一个对象,这个对象身上,有一些指令相关的函数,这些函数可以在特定的阶段,执行相关的操作
        Vue.directive('focus', {
          bind: function (el) { // 每当指令绑定到元素上的时候,会立即执行这个 bind 函数,只执行一次
            // 注意: 在每个 函数中,第一个参数,永远是 el ,表示 被绑定了指令的那个元素,这个 el 参数,是一个原生的JS对象
            // 在元素 刚绑定了指令的时候,还没有 插入到 DOM中去,这时候,调用 focus 方法没有作用
            //  因为,一个元素,只有插入DOM之后,才能获取焦点
            // el.focus()
          },
          inserted: function (el) {  // inserted 表示元素 插入到DOM中的时候,会执行 inserted 函数【触发1次】
            el.focus()
            // 和JS行为有关的操作,最好在 inserted 中去执行,放置 JS行为不生效
          },
          updated: function (el) {  // 当VNode更新的时候,会执行 updated, 可能会触发多次
    
          }
        })
    
        // 自定义一个 设置字体颜色的 指令
        Vue.directive('color', {
          // 样式,只要通过指令绑定给了元素,不管这个元素有没有被插入到页面中去,这个元素肯定有了一个内联的样式
          // 将来元素肯定会显示到页面中,这时候,浏览器的渲染引擎必然会解析样式,应用给这个元素
          bind: function (el, binding) {
            // el.style.color = 'red'
            // console.log(binding.name)
            // 和样式相关的操作,一般都可以在 bind 执行
    
            // console.log(binding.value)
            // console.log(binding.expression)
    
            el.style.color = binding.value
          }
        })
    
    
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {
            id: '',
            name: '',
            keywords: '', // 搜索的关键字
            list: [
              { id: 1, name: '奔驰', ctime: new Date() },
              { id: 2, name: '宝马', ctime: new Date() }
            ]
          },
          methods: {
            add() { // 添加的方法
              // console.log('ok')
              // 分析:
              // 1. 获取到 id 和 name ,直接从 data 上面获取 
              // 2. 组织出一个对象
              // 3. 把这个对象,调用 数组的 相关方法,添加到 当前 data 上的 list 中
              // 4. 注意:在Vue中,已经实现了数据的双向绑定,每当我们修改了 data 中的数据,Vue会默认监听到数据的改动,自动把最新的数据,应用到页面上;
    
              // 5. 当我们意识到上面的第四步的时候,就证明大家已经入门Vue了,我们更多的是在进行 VM中 Model 数据的操作,同时,在操作Model数据的时候,指定的业务逻辑操作;
    
              var car = { id: this.id, name: this.name, ctime: new Date() }
              this.list.push(car)
              this.id = this.name = ''
            },
            del(id) { // 根据Id删除数据
              // 分析:
              // 1. 如何根据Id,找到要删除这一项的索引
              // 2. 如果找到索引了,直接调用 数组的 splice 方法
    
              /* this.list.some((item, i) => {
                if (item.id == id) {
                  this.list.splice(i, 1)
                  // 在 数组的 some 方法中,如果 return true,就会立即终止这个数组的后续循环
                  return true;
                }
              }) */
    
    
              var index = this.list.findIndex(item => {
                if (item.id == id) {
                  return true;
                }
              })
    
              // console.log(index)
              this.list.splice(index, 1)
            },
            search(keywords) { // 根据关键字,进行数据的搜索
              /* var newList = []
              this.list.forEach(item => {
                if (item.name.indexOf(keywords) != -1) {
                  newList.push(item)
                }
              })
              return newList */
    
              // 注意:  forEach   some   filter   findIndex   这些都属于数组的新方法,
              //  都会对数组中的每一项,进行遍历,执行相关的操作;
              return this.list.filter(item => {
                // if(item.name.indexOf(keywords) != -1)
    
                // 注意 : ES6中,为字符串提供了一个新方法,叫做  String.prototype.includes('要包含的字符串')
                //  如果包含,则返回 true ,否则返回 false
                //  contain
                if (item.name.includes(keywords)) {
                  return item
                }
              })
    
              // return newList
            }
          }
        });
    
    
        // 如何自定义一个私有的过滤器(局部)
        var vm2 = new Vue({
          el: '#app2',
          data: {
            dt: new Date()
          },
          methods: {},
          filters: { // 定义私有过滤器    过滤器有两个 条件  【过滤器名称 和 处理函数】
            // 过滤器调用的时候,采用的是就近原则,如果私有过滤器和全局过滤器名称一致了,这时候 优先调用私有过滤器
            dateFormat: function (dateStr, pattern = '') {
              // 根据给定的时间字符串,得到特定的时间
              var dt = new Date(dateStr)
    
              //   yyyy-mm-dd
              var y = dt.getFullYear()
              var m = (dt.getMonth() + 1).toString().padStart(2, '0')
              var d = dt.getDate().toString().padStart(2, '0')
    
              if (pattern.toLowerCase() === 'yyyy-mm-dd') {
                return `${y}-${m}-${d}`
              } else {
                var hh = dt.getHours().toString().padStart(2, '0')
                var mm = dt.getMinutes().toString().padStart(2, '0')
                var ss = dt.getSeconds().toString().padStart(2, '0')
    
                return `${y}-${m}-${d} ${hh}:${mm}:${ss} ~~~~~~~`
              }
            }
          },
          directives: { // 自定义私有指令
            'fontweight': { // 设置字体粗细的
              bind: function (el, binding) {
                el.style.fontWeight = binding.value
              }
            },
            'fontsize': function (el, binding) { // 注意:这个 function 等同于 把 代码写到了 bind 和 update 中去
              el.style.fontSize = parseInt(binding.value) + 'px'
            }
          }
        })
    
    
        // 过滤器的定义语法
        // Vue.filter('过滤器的名称', function(){})
    
        // 过滤器中的 function ,第一个参数,已经被规定死了,永远都是 过滤器 管道符前面 传递过来的数据
        /* Vue.filter('过滤器的名称', function (data) {
          return data + '123'
        }) */
    
    
    
        // document.getElementById('search').focus()
    
      </script>
    </body>
    
    </html>
    
    
    
    <!-- 过滤器调用时候的格式    {{ name | 过滤器的名称 }} -->
    View Code

    过滤器

    概念:Vue.js 允许你自定义过滤器,可被用作一些常见的文本格式化。过滤器可以用在两个地方:mustache 插值和 v-bind 表达式。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符指示;

    私有过滤器

    1. HTML元素:


    <td>{{item.ctime | dataFormat('yyyy-mm-dd')}}</td>
    1. 私有 filters 定义方式:


    filters: { // 私有局部过滤器,只能在 当前 VM 对象所控制的 View 区域进行使用

      dataFormat(input, pattern = "") { // 在参数列表中 通过 pattern="" 来指定形参默认值,防止报错

        var dt = new Date(input);

        // 获取年月日

        var y = dt.getFullYear();

        var m = (dt.getMonth() + 1).toString().padStart(2, '0');

        var d = dt.getDate().toString().padStart(2, '0');



        // 如果 传递进来的字符串类型,转为小写之后,等于 yyyy-mm-dd,那么就返回 年-月-日

        // 否则,就返回 年-月-日 时:分:秒

        if (pattern.toLowerCase() === 'yyyy-mm-dd') {

          return `${y}-${m}-${d}`;

        } else {

          // 获取时分秒

          var hh = dt.getHours().toString().padStart(2, '0');

          var mm = dt.getMinutes().toString().padStart(2, '0');

          var ss = dt.getSeconds().toString().padStart(2, '0');



          return `${y}-${m}-${d} ${hh}:${mm}:${ss}`;

        }

      }

    }

     

    使用ES6中的字符串新方法 String.prototype.padStart(maxLength, fillString='') 或 String.prototype.padEnd(maxLength, fillString='')来填充字符串;

    全局过滤器


    // 定义一个全局过滤器

    Vue.filter('dataFormat', function (input, pattern = '') {

    var dt = new Date(input);

    // 获取年月日

    var y = dt.getFullYear();

    var m = (dt.getMonth() + 1).toString().padStart(2, '0');

    var d = dt.getDate().toString().padStart(2, '0');



    // 如果 传递进来的字符串类型,转为小写之后,等于 yyyy-mm-dd,那么就返回 年-月-日

    // 否则,就返回 年-月-日 时:分:秒

    if (pattern.toLowerCase() === 'yyyy-mm-dd') {

      return `${y}-${m}-${d}`;

    } else {

      // 获取时分秒

      var hh = dt.getHours().toString().padStart(2, '0');

      var mm = dt.getMinutes().toString().padStart(2, '0');

      var ss = dt.getSeconds().toString().padStart(2, '0');



      return `${y}-${m}-${d} ${hh}:${mm}:${ss}`;

    }

    });

     注意:当有局部和全局两个名称相同的过滤器时候,会以就近原则进行调用,即:局部过滤器优先于全局过滤器被调用!

     

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
    </head>
    
    <body>
      <div id="app">
        <p>{{ msg | msgFormat('疯狂+1', '123') | test }}</p>
      </div>
    
      <script>
        // 定义一个 Vue 全局的过滤器,名字叫做  msgFormat
        Vue.filter('msgFormat', function (msg, arg, arg2) {
          // 字符串的  replace 方法,第一个参数,除了可写一个 字符串之外,还可以定义一个正则
          return msg.replace(/单纯/g, arg + arg2)
        })
    
        Vue.filter('test', function (msg) {
          return msg + '========'
        })
    
    
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {
            msg: '曾经,我也是一个单纯的少年,单纯的我,傻傻的问,谁是世界上最单纯的男人'
          },
          methods: {}
        });
      </script>
    </body>
    
    </html>
    View Code

     

    键盘修饰符以及自定义键盘修饰符

    1.x中自定义键盘修饰符【了解即可】


    Vue.directive('on').keyCodes.f2 = 113;

    2.x中自定义键盘修饰符

    1. 通过Vue.config.keyCodes.名称 = 按键值来自定义案件修饰符的别名:


    Vue.config.keyCodes.f2 = 113;
    1. 使用自定义的按键修饰符:


    <input type="text" v-model="name" @keyup.f2="add">

     

     

    自定义指令

    1. 自定义全局和局部的 自定义指令:


      // 自定义全局指令 v-focus,为绑定的元素自动获取焦点:

      Vue.directive('focus', {

        inserted: function (el) { // inserted 表示被绑定元素插入父节点时调用

          el.focus();

        }

      });



      // 自定义局部指令 v-color 和 v-font-weight,为绑定的元素设置指定的字体颜色 和 字体粗细:

        directives: {

          color: { // 为元素设置指定的字体颜色

            bind(el, binding) {

              el.style.color = binding.value;

            }

          },

          'font-weight': function (el, binding2) { // 自定义指令的简写形式,等同于定义了 bind 和 update 两个钩子函数

            el.style.fontWeight = binding2.value;

          }

        }
    1. 自定义指令的使用方式:


    <input type="text" v-model="searchName" v-focus v-color="'red'" v-font-weight="900">

     

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
        <script src="vue-2.4.0.js"></script>
    </head>
    
    <body>
        <div id="app">
    
            <input type="text" v-model="searchName" v-focus v-color="'red'" v-font-weight="900">
        </div>
    
        <script>
            
        // 自定义全局指令 v-focus,为绑定的元素自动获取焦点:
    
        Vue.directive('focus', {
    
            inserted: function (el) { // inserted 表示被绑定元素插入父节点时调用
    
            el.focus();
    
            }
    
        });
    
            var vm=new Vue({
               el:'#app',
               data:{
                searchName:'hahaha',
               },
               methods:{
                   msg:'hello'
               },
                // 自定义局部指令 v-color 和 v-font-weight,为绑定的元素设置指定的字体颜色 和 字体粗细:
    
          directives: {
    
            color: { // 为元素设置指定的字体颜色
    
            bind(el, binding) {
    
            el.style.color = binding.value;
    
             }
    
            },
    
            'font-weight': function (el, binding2) { // 自定义指令的简写形式,等同于定义了 bind 和 update 两个钩子函数
    
                el.style.fontWeight = binding2.value;
    
            }
    
    }
    
            });
        </script>
    </body>
    
    </html>
    View Code

    Vue 1.x 中 自定义元素指令【已废弃,了解即可】

    Vue.elementDirective('red-color', {
    bind: function () {
      this.el.style.color = 'red';
    }
    });

    使用方式:

    <red-color>1232</red-color>

    vue实例的生命周期

    • 什么是生命周期:从Vue实例创建、运行、到销毁期间,总是伴随着各种各样的事件,这些事件,统称为生命周期!

    • 生命周期钩子:就是生命周期事件的别名而已;

    • 生命周期钩子 = 生命周期函数 = 生命周期事件

    • 主要的生命周期函数分类:

    • 创建期间的生命周期函数:

    • beforeCreate:实例刚在内存中被创建出来,此时,还没有初始化好 data 和 methods 属性

    • created:实例已经在内存中创建OK,此时 data 和 methods 已经创建OK,此时还没有开始 编译模板

    • beforeMount:此时已经完成了模板的编译,但是还没有挂载到页面中

    • mounted:此时,已经将编译好的模板,挂载到了页面指定的容器中显示

    • 运行期间的生命周期函数:

    • beforeUpdate:状态更新之前执行此函数, 此时 data 中的状态值是最新的,但是界面上显示的 数据还是旧的,因为此时还没有开始重新渲染DOM节点

    • updated:实例更新完毕之后调用此函数,此时 data 中的状态值 和 界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了!

    • 销毁期间的生命周期函数:

    • beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。

    • destroyed:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
    </head>
    
    <body>
      <div id="app">
        <input type="button" value="修改msg" @click="msg='No'">
        <h3 id="h3">{{ msg }}</h3>
      </div>
    
      <script>
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {
            msg: 'ok'
          },
          methods: {
            show() {
              console.log('执行了show方法')
            }
          },
          beforeCreate() { // 这是我们遇到的第一个生命周期函数,表示实例完全被创建出来之前,会执行它
            // console.log(this.msg)
            // this.show()
            // 注意: 在 beforeCreate 生命周期函数执行的时候,data 和 methods 中的 数据都还没有没初始化
          },
          created() { // 这是遇到的第二个生命周期函数
            // console.log(this.msg)
            // this.show()
            //  在 created 中,data 和 methods 都已经被初始化好了!
            // 如果要调用 methods 中的方法,或者操作 data 中的数据,最早,只能在 created 中操作
          },
          beforeMount() { // 这是遇到的第3个生命周期函数,表示 模板已经在内存中编辑完成了,但是尚未把 模板渲染到 页面中
            // console.log(document.getElementById('h3').innerText)
            // 在 beforeMount 执行的时候,页面中的元素,还没有被真正替换过来,只是之前写的一些模板字符串
          },
          mounted() { // 这是遇到的第4个生命周期函数,表示,内存中的模板,已经真实的挂载到了页面中,用户已经可以看到渲染好的页面了
            // console.log(document.getElementById('h3').innerText)
            // 注意: mounted 是 实例创建期间的最后一个生命周期函数,当执行完 mounted 就表示,实例已经被完全创建好了,此时,如果没有其它操作的话,这个实例,就静静的 躺在我们的内存中,一动不动
          },
    
    
          // 接下来的是运行中的两个事件
          beforeUpdate() { // 这时候,表示 我们的界面还没有被更新【数据被更新了吗?  数据肯定被更新了】
            /* console.log('界面上元素的内容:' + document.getElementById('h3').innerText)
            console.log('data 中的 msg 数据是:' + this.msg) */
            // 得出结论: 当执行 beforeUpdate 的时候,页面中的显示的数据,还是旧的,此时 data 数据是最新的,页面尚未和 最新的数据保持同步
          },
          updated() {
            console.log('界面上元素的内容:' + document.getElementById('h3').innerText)
            console.log('data 中的 msg 数据是:' + this.msg)
            // updated 事件执行的时候,页面和 data 数据已经保持同步了,都是最新的
          }
        });
      </script>
    </body>
    
    </html>
    View Code

    vue-resource 实现 get, post, jsonp请求

    除了 vue-resource 之外,还可以使用 axios 的第三方包实现实现数据的请求

    1. 之前的学习中,如何发起数据请求?

    2. 常见的数据请求类型? get post jsonp

    3. 测试的URL请求资源地址:

    1. JSONP的实现原理

    • 由于浏览器的安全性限制,不允许AJAX访问 协议不同、域名不同、端口号不同的 数据接口,浏览器认为这种访问不安全;

    • 可以通过动态创建script标签的形式,把script标签的src属性,指向数据接口的地址,因为script标签不存在跨域限制,这种数据获取方式,称作JSONP(注意:根据JSONP的实现原理,知晓,JSONP只支持Get请求);

    • 具体实现过程:

    • 先在客户端定义一个回调方法,预定义对数据的操作;

    • 再把这个回调方法的名称,通过URL传参的形式,提交到服务器的数据接口;

    • 服务器数据接口组织好要发送给客户端的数据,再拿着客户端传递过来的回调方法名称,拼接出一个调用这个方法的字符串,发送给客户端去解析执行;

    • 客户端拿到服务器返回的字符串之后,当作Script脚本去解析执行,这样就能够拿到JSONP的数据了;

    • 带大家通过 Node.js ,来手动实现一个JSONP的请求例子;

        const http = require('http');
      // 导入解析 URL 地址的核心模块
      const urlModule = require('url');

      const server = http.createServer();
      // 监听 服务器的 request 请求事件,处理每个请求
      server.on('request', (req, res) => {
        const url = req.url;

        // 解析客户端请求的URL地址
        var info = urlModule.parse(url, true);

        // 如果请求的 URL 地址是 /getjsonp ,则表示要获取JSONP类型的数据
        if (info.pathname === '/getjsonp') {
          // 获取客户端指定的回调函数的名称
          var cbName = info.query.callback;
          // 手动拼接要返回给客户端的数据对象
          var data = {
            name: 'zs',
            age: 22,
            gender: '男',
            hobby: ['吃饭', '睡觉', '运动']
          }
          // 拼接出一个方法的调用,在调用这个方法的时候,把要发送给客户端的数据,序列化为字符串,作为参数传递给这个调用的方法:
          var result = `${cbName}(${JSON.stringify(data)})`;
          // 将拼接好的方法的调用,返回给客户端去解析执行
          res.end(result);
        } else {
          res.end('404');
        }
      });

      server.listen(3000, () => {
        console.log('server running at http://127.0.0.1:3000');
      });
    1. vue-resource 的配置步骤:

    • 直接在页面中,通过script标签,引入 vue-resource 的脚本文件;

    • 注意:引用的先后顺序是:先引用 Vue 的脚本文件,再引用 vue-resource 的脚本文件;

    1. 发送get请求:

    getInfo() { // get 方式获取数据
    this.$http.get('http://127.0.0.1:8899/api/getlunbo').then(res => {
      console.log(res.body);
    })
    }
    1. 发送post请求:

    postInfo() {
    var url = 'http://127.0.0.1:8899/api/post';
    // post 方法接收三个参数:
    // 参数1: 要请求的URL地址
    // 参数2: 要发送的数据对象
    // 参数3: 指定post提交的编码类型为 application/x-www-form-urlencoded
    this.$http.post(url, { name: 'zs' }, { emulateJSON: true }).then(res => {
      console.log(res.body);
    });
    }
    1. 发送JSONP请求获取数据:

    jsonpInfo() { // JSONP形式从服务器获取数据
    var url = 'http://127.0.0.1:8899/api/jsonp';
    this.$http.jsonp(url).then(res => {
      console.log(res.body);
    });
    }

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
      <!-- 注意:vue-resource 依赖于 Vue,所以先后顺序要注意  -->
      <!-- this.$http.jsonp -->
      <script src="./lib/vue-resource-1.3.4.js"></script>
    </head>
    
    <body>
      <div id="app">
        <input type="button" value="get请求" @click="getInfo">
        <input type="button" value="post请求" @click="postInfo">
        <input type="button" value="jsonp请求" @click="jsonpInfo">
      </div>
    
      <script>
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {},
          methods: {
            getInfo() { // 发起get请求
              //  当发起get请求之后, 通过 .then 来设置成功的回调函数
              this.$http.get('http://vue.studyit.io/api/getlunbo').then(function (result) {
                // 通过 result.body 拿到服务器返回的成功的数据
                // console.log(result.body)
              })
            },
            postInfo() { // 发起 post 请求   application/x-wwww-form-urlencoded
              //  手动发起的 Post 请求,默认没有表单格式,所以,有的服务器处理不了
              //  通过 post 方法的第三个参数, { emulateJSON: true } 设置 提交的内容类型 为 普通表单数据格式
              this.$http.post('http://vue.studyit.io/api/post', {}, { emulateJSON: true }).then(result => {
                console.log(result.body)
              })
            },
            jsonpInfo() { // 发起JSONP 请求
              this.$http.jsonp('http://vue.studyit.io/api/jsonp').then(result => {
                console.log(result.body)
              })
            }
          }
        });
      </script>
    </body>
    
    </html>
    View Code

    客户端jsonp页面.html

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
    </head>
    
    <body>
      <script>
        function showInfo123(data) {
          console.log(data)
        }
      </script>
    
    
    <script src="http://127.0.0.1:3000/getscript?callback=showInfo123"></script>
    <!-- <script>
      show()
    </script> -->
    
    </body>
    
    </html>
    View Code
     

    配置本地数据库和数据接口API

    1. 先解压安装 PHPStudy;

    2. 解压安装 Navicat 这个数据库可视化工具,并激活;

    3. 打开 Navicat 工具,新建空白数据库,名为 dtcmsdb4;

    4. 双击新建的数据库,连接上这个空白数据库,在新建的数据库上右键 -> 运行SQL文件,选择并执行 dtcmsdb4.sql 这个数据库脚本文件;如果执行不报错,则数据库导入完成;

    5. 进入文件夹 vuecms3_nodejsapi 内部,执行 npm i 安装所有的依赖项;

    6. 先确保本机安装了 nodemon, 没有安装,则运行 npm i nodemon -g 进行全局安装,安装完毕后,进入到 vuecms3_nodejsapi目录 -> src目录 -> 双击运行 start.bat

    7. 如果API启动失败,请检查 PHPStudy 是否正常开启,同时,检查 app.js 中第 14行 中数据库连接配置字符串是否正确;PHPStudy 中默认的 用户名是root,默认的密码也是root

    品牌管理改造

    展示品牌列表

    添加品牌数据

    删除品牌数据

    Vue中的动画

    为什么要有动画:动画能够提高用户的体验,帮助用户更好的理解页面中的功能;

    使用过渡类名

    1. HTML结构:

    <div id="app">
      <input type="button" value="动起来" @click="myAnimate">
      <!-- 使用 transition 将需要过渡的元素包裹起来 -->
      <transition name="fade">
        <div v-show="isshow">动画哦</div>
      </transition>
    </div>
    1. VM 实例:

    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
    el: '#app',
    data: {
      isshow: false
    },
    methods: {
      myAnimate() {
        this.isshow = !this.isshow;
      }
    }
    });
    1. 定义两组类样式:

    /* 定义进入和离开时候的过渡状态 */
      .fade-enter-active,
      .fade-leave-active {
        transition: all 0.2s ease;
        position: absolute;
      }

      /* 定义进入过渡的开始状态 和 离开过渡的结束状态 */
      .fade-enter,
      .fade-leave-to {
        opacity: 0;
        transform: translateX(100px);
      }

    使用第三方 CSS 动画库

    1. 导入动画类库:

    <link rel="stylesheet" type="text/css" href="./lib/animate.css">
    1. 定义 transition 及属性:

    <transition
    enter-active-class="fadeInRight"
      leave-active-class="fadeOutRight"
      :duration="{ enter: 500, leave: 800 }">
    <div class="animated" v-show="isshow">动画哦</div>
    </transition>

    使用动画钩子函数

    1. 定义 transition 组件以及三个钩子函数:

    <div id="app">
      <input type="button" value="切换动画" @click="isshow = !isshow">
      <transition
      @before-enter="beforeEnter"
      @enter="enter"
      @after-enter="afterEnter">
        <div v-if="isshow" class="show">OK</div>
      </transition>
    </div>
    1. 定义三个 methods 钩子方法:

    methods: {
          beforeEnter(el) { // 动画进入之前的回调
            el.style.transform = 'translateX(500px)';
          },
          enter(el, done) { // 动画进入完成时候的回调
            el.offsetWidth;
            el.style.transform = 'translateX(0px)';
            done();
          },
          afterEnter(el) { // 动画进入完成之后的回调
            this.isshow = !this.isshow;
          }
        }
    1. 定义动画过渡时长和样式:

    .show{
        transition: all 0.4s ease;
      }

    v-for 的列表过渡

    1. 定义过渡样式:

    <style>
      .list-enter,
      .list-leave-to {
        opacity: 0;
        transform: translateY(10px);
      }

      .list-enter-active,
      .list-leave-active {
        transition: all 0.3s ease;
      }
    </style>
    1. 定义DOM结构,其中,需要使用 transition-group 组件把v-for循环的列表包裹起来:

      <div id="app">
      <input type="text" v-model="txt" @keyup.enter="add">

      <transition-group tag="ul" name="list">
        <li v-for="(item, i) in list" :key="i">{{item}}</li>
      </transition-group>
    </div>
    1. 定义 VM中的结构:

        // 创建 Vue 实例,得到 ViewModel
      var vm = new Vue({
        el: '#app',
        data: {
          txt: '',
          list: [1, 2, 3, 4]
        },
        methods: {
          add() {
            this.list.push(this.txt);
            this.txt = '';
          }
        }
      });

    列表的排序过渡

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

    • v-movev-leave-active 结合使用,能够让列表的过渡更加平缓柔和:

    .v-move{
    transition: all 0.8s ease;
    }
    .v-leave-active{
    position: absolute;
    }

     

    相关文章

    1. vue.js 1.x 文档

    2. vue.js 2.x 文档

    3. String.prototype.padStart(maxLength, fillString)

    4. js 里面的键盘事件对应的键码

    5. pagekit/vue-resource

    6. navicat如何导入sql文件和导出sql文件

    7. 贝塞尔在线生成器

     改造品牌的小案例:

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
      <script src="./lib/vue-resource-1.3.4.js"></script>
      <link rel="stylesheet" href="./lib/bootstrap-3.3.7.css">
    </head>
    
    <body>
      <div id="app">
    
    
    
        <div class="panel panel-primary">
          <div class="panel-heading">
            <h3 class="panel-title">添加品牌</h3>
          </div>
          <div class="panel-body form-inline">
    
            <label>
              Name:
              <input type="text" v-model="name" class="form-control">
            </label>
    
            <input type="button" value="添加" @click="add" class="btn btn-primary">
          </div>
        </div>
    
    
    
        <table class="table table-bordered table-hover table-striped">
          <thead>
            <tr>
              <th>Id</th>
              <th>Name</th>
              <th>Ctime</th>
              <th>Operation</th>
            </tr>
          </thead>
          <tbody>
            <tr v-for="item in list" :key="item.id">
              <td>{{item.id}}</td>
              <td>{{item.name}}</td>
              <td>{{item.ctime}}</td>
              <td>
                <a href="" @click.prevent="del(item.id)">删除</a>
              </td>
            </tr>
          </tbody>
        </table>
    
    
      </div>
    
      <script>
        // 如果我们通过全局配置了,请求的数据接口 根域名,则 ,在每次单独发起 http 请求的时候,请求的 url 路径,应该以相对路径开头,前面不能带 /  ,否则 不会启用根路径做拼接;
        Vue.http.options.root = 'http://vue.studyit.io/';
        // 全局启用 emulateJSON 选项
        Vue.http.options.emulateJSON = true;
    
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {
            name: '',
            list: [ // 存放所有品牌列表的数组
            ]
          },
          created() { // 当 vm 实例 的 data 和 methods 初始化完毕后,vm实例会自动执行created 这个生命周期函数
            this.getAllList()
          },
          methods: {
            getAllList() { // 获取所有的品牌列表 
              // 分析:
              // 1. 由于已经导入了 Vue-resource这个包,所以 ,可以直接通过  this.$http 来发起数据请求
              // 2. 根据接口API文档,知道,获取列表的时候,应该发起一个 get 请求
              // 3. this.$http.get('url').then(function(result){})
              // 4. 当通过 then 指定回调函数之后,在回调函数中,可以拿到数据服务器返回的 result
              // 5. 先判断 result.status 是否等于0,如果等于0,就成功了,可以 把 result.message 赋值给 this.list ; 如果不等于0,可以弹框提醒,获取数据失败!
    
              this.$http.get('api/getprodlist').then(result => {
                // 注意: 通过 $http 获取到的数据,都在 result.body 中放着
                var result = result.body
                if (result.status === 0) {
                  // 成功了
                  this.list = result.message
                } else {
                  // 失败了
                  alert('获取数据失败!')
                }
              })
            },
            add() {  // 添加品牌列表到后台服务器
              // 分析:
              // 1. 听过查看 数据API接口,发现,要发送一个 Post 请求,  this.$http.post
              // 2. this.$http.post() 中接收三个参数:
              //   2.1 第一个参数: 要请求的URL地址
              //   2.2 第二个参数: 要提交给服务器的数据 ,要以对象形式提交给服务器 { name: this.name }
              //   3.3 第三个参数: 是一个配置对象,要以哪种表单数据类型提交过去, { emulateJSON: true }, 以普通表单格式,将数据提交给服务器 application/x-www-form-urlencoded
              // 3. 在 post 方法中,使用 .then 来设置成功的回调函数,如果想要拿到成功的结果,需要 result.body
    
              /* this.$http.post('api/addproduct', { name: this.name }, { emulateJSON: true }).then(result => {
                if (result.body.status === 0) {
                  // 成功了!
                  // 添加完成后,只需要手动,再调用一下 getAllList 就能刷新品牌列表了
                  this.getAllList()
                  // 清空 name 
                  this.name = ''
                } else {
                  // 失败了
                  alert('添加失败!')
                }
              }) */
    
              this.$http.post('api/addproduct', { name: this.name }).then(result => {
                if (result.body.status === 0) {
                  // 成功了!
                  // 添加完成后,只需要手动,再调用一下 getAllList 就能刷新品牌列表了
                  this.getAllList()
                  // 清空 name 
                  this.name = ''
                } else {
                  // 失败了
                  alert('添加失败!')
                }
              })
            },
            del(id) { // 删除品牌
              this.$http.get('api/delproduct/' + id).then(result => {
                if (result.body.status === 0) {
                  // 删除成功
                  this.getAllList()
                } else {
                  alert('删除失败!')
                }
              })
            }
          }
        });
      </script>
    </body>
    
    </html>
    View Code

    动画-不使用动画

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
    </head>
    
    <body>
      <div id="app">
        <input type="button" value="toggle" @click="flag=!flag">
        <!-- 需求: 点击按钮,让 h3 显示,再点击,让 h3 隐藏 -->
        <h3 v-if="flag">这是一个H3</h3>
      </div>
    
      <script>
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {
            flag: false
          },
          methods: {}
        });
      </script>
    </body>
    
    </html>
    View Code

    使用过渡类名实现动画

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
      <!-- 2. 自定义两组样式,来控制 transition 内部的元素实现动画 -->
      <style>
        /* v-enter 【这是一个时间点】 是进入之前,元素的起始状态,此时还没有开始进入 */
        /* v-leave-to 【这是一个时间点】 是动画离开之后,离开的终止状态,此时,元素 动画已经结束了 */
        .v-enter,
        .v-leave-to {
          opacity: 0;
          transform: translateX(150px);
        }
    
        /* v-enter-active 【入场动画的时间段】 */
        /* v-leave-active 【离场动画的时间段】 */
        .v-enter-active,
        .v-leave-active{
          transition: all 0.8s ease;
        }
      </style>
    </head>
    
    <body>
      <div id="app">
        <input type="button" value="toggle" @click="flag=!flag">
        <!-- 需求: 点击按钮,让 h3 显示,再点击,让 h3 隐藏 -->
        <!-- 1. 使用 transition 元素,把 需要被动画控制的元素,包裹起来 -->
        <!-- transition 元素,是 Vue 官方提供的 -->
        <transition>
          <h3 v-if="flag">这是一个H3</h3>
        </transition>
      </div>
    
      <script>
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {
            flag: false
          },
          methods: {}
        });
      </script>
    </body>
    
    </html>
    View Code

    修改v-前缀

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
      <!-- 2. 自定义两组样式,来控制 transition 内部的元素实现动画 -->
      <style>
        /* v-enter 【这是一个时间点】 是进入之前,元素的起始状态,此时还没有开始进入 */
        /* v-leave-to 【这是一个时间点】 是动画离开之后,离开的终止状态,此时,元素 动画已经结束了 */
        .v-enter,
        .v-leave-to {
          opacity: 0;
          transform: translateX(150px);
        }
    
        /* v-enter-active 【入场动画的时间段】 */
        /* v-leave-active 【离场动画的时间段】 */
        .v-enter-active,
        .v-leave-active{
          transition: all 0.8s ease;
        }
    
    
    
    
        .my-enter,
        .my-leave-to {
          opacity: 0;
          transform: translateY(70px);
        }
    
        .my-enter-active,
        .my-leave-active{
          transition: all 0.8s ease;
        }
      </style>
    </head>
    
    <body>
      <div id="app">
        <input type="button" value="toggle" @click="flag=!flag">
        <!-- 需求: 点击按钮,让 h3 显示,再点击,让 h3 隐藏 -->
        <!-- 1. 使用 transition 元素,把 需要被动画控制的元素,包裹起来 -->
        <!-- transition 元素,是 Vue 官方提供的 -->
        <transition>
          <h3 v-if="flag">这是一个H3</h3>
        </transition>
    
    
        <hr>
    
        <input type="button" value="toggle2" @click="flag2=!flag2">
        <transition name="my">
          <h6 v-if="flag2">这是一个H6</h6>
        </transition>
      </div>
    
      <script>
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {
            flag: false,
            flag2: false
          },
          methods: {}
        });
      </script>
    </body>
    
    </html>
    View Code

    使用三次方类实现动画

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
      <!-- 2. 自定义两组样式,来控制 transition 内部的元素实现动画 -->
      <style>
        /* v-enter 【这是一个时间点】 是进入之前,元素的起始状态,此时还没有开始进入 */
        /* v-leave-to 【这是一个时间点】 是动画离开之后,离开的终止状态,此时,元素 动画已经结束了 */
        .v-enter,
        .v-leave-to {
          opacity: 0;
          transform: translateX(150px);
        }
    
        /* v-enter-active 【入场动画的时间段】 */
        /* v-leave-active 【离场动画的时间段】 */
        .v-enter-active,
        .v-leave-active{
          transition: all 0.8s ease;
        }
    
    
    
    
        .my-enter,
        .my-leave-to {
          opacity: 0;
          transform: translateY(70px);
        }
    
        .my-enter-active,
        .my-leave-active{
          transition: all 0.8s ease;
        }
      </style>
    </head>
    
    <body>
      <div id="app">
        <input type="button" value="toggle" @click="flag=!flag">
        <!-- 需求: 点击按钮,让 h3 显示,再点击,让 h3 隐藏 -->
        <!-- 1. 使用 transition 元素,把 需要被动画控制的元素,包裹起来 -->
        <!-- transition 元素,是 Vue 官方提供的 -->
        <transition>
          <h3 v-if="flag">这是一个H3</h3>
        </transition>
    
    
        <hr>
    
        <input type="button" value="toggle2" @click="flag2=!flag2">
        <transition name="my">
          <h6 v-if="flag2">这是一个H6</h6>
        </transition>
      </div>
    
      <script>
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {
            flag: false,
            flag2: false
          },
          methods: {}
        });
      </script>
    </body>
    
    </html>
    View Code

    使用钩子函数实现小球半场动画

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
      <style>
        .ball {
           15px;
          height: 15px;
          border-radius: 50%;
          background-color: red;
        }
      </style>
    </head>
    
    <body>
      <div id="app">
        <input type="button" value="快到碗里来" @click="flag=!flag">
        <!-- 1. 使用 transition 元素把 小球包裹起来 -->
        <transition
          @before-enter="beforeEnter"
          @enter="enter"
          @after-enter="afterEnter">
          <div class="ball" v-show="flag"></div>
        </transition>
      </div>
    
      <script>
    
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {
            flag: false
          },
          methods: {
            // 注意: 动画钩子函数的第一个参数:el,表示 要执行动画的那个DOM元素,是个原生的 JS DOM对象
            // 大家可以认为 , el 是通过 document.getElementById('') 方式获取到的原生JS DOM对象
            beforeEnter(el){
              // beforeEnter 表示动画入场之前,此时,动画尚未开始,可以 在 beforeEnter 中,设置元素开始动画之前的起始样式
              // 设置小球开始动画之前的,起始位置
              el.style.transform = "translate(0, 0)"
            },
            enter(el, done){
              // 这句话,没有实际的作用,但是,如果不写,出不来动画效果;
              // 可以认为 el.offsetWidth 会强制动画刷新
              el.offsetWidth
              // enter 表示动画 开始之后的样式,这里,可以设置小球完成动画之后的,结束状态
              el.style.transform = "translate(150px, 450px)"
              el.style.transition = 'all 1s ease'
    
              // 这里的 done, 起始就是 afterEnter 这个函数,也就是说:done 是 afterEnter 函数的引用
              done()
            },
            afterEnter(el){
              // 动画完成之后,会调用 afterEnter
              // console.log('ok')
              this.flag = !this.flag
            }
          }
        });
      </script>
    </body>
    
    </html>
    View Code

    列表动画:

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
      <style>
        li {
          border: 1px dashed #999;
          margin: 5px;
          line-height: 35px;
          padding-left: 5px;
          font-size: 12px;
           100%;
        }
    
        li:hover {
          background-color: hotpink;
          transition: all 0.8s ease;
        }
    
    
    
        .v-enter,
        .v-leave-to {
          opacity: 0;
          transform: translateY(80px);
        }
    
        .v-enter-active,
        .v-leave-active {
          transition: all 0.6s ease;
        }
    
        /* 下面的 .v-move 和 .v-leave-active 配合使用,能够实现列表后续的元素,渐渐地漂上来的效果 */
        .v-move {
          transition: all 0.6s ease;
        }
        .v-leave-active{
          position: absolute;
        }
      </style>
    </head>
    
    <body>
      <div id="app">
    
        <div>
          <label>
            Id:
            <input type="text" v-model="id">
          </label>
    
          <label>
            Name:
            <input type="text" v-model="name">
          </label>
    
          <input type="button" value="添加" @click="add">
        </div>
    
        <!-- <ul> -->
          <!-- 在实现列表过渡的时候,如果需要过渡的元素,是通过 v-for 循环渲染出来的,不能使用 transition 包裹,需要使用 transitionGroup -->
          <!-- 如果要为 v-for 循环创建的元素设置动画,必须为每一个 元素 设置 :key 属性 -->
          <!-- 给 ransition-group 添加 appear 属性,实现页面刚展示出来时候,入场时候的效果 -->
          <!-- 通过 为 transition-group 元素,设置 tag 属性,指定 transition-group 渲染为指定的元素,如果不指定 tag 属性,默认,渲染为 span 标签 -->
          <transition-group appear tag="ul">
            <li v-for="(item, i) in list" :key="item.id" @click="del(i)">
              {{item.id}} --- {{item.name}}
            </li>
          </transition-group>
        <!-- </ul> -->
    
      </div>
    
      <script>
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {
            id: '',
            name: '',
            list: [
              { id: 1, name: '赵高' },
              { id: 2, name: '秦桧' },
              { id: 3, name: '严嵩' },
              { id: 4, name: '魏忠贤' }
            ]
          },
          methods: {
            add() {
              this.list.push({ id: this.id, name: this.name })
              this.id = this.name = ''
            },
            del(i) {
              this.list.splice(i, 1)
            }
          }
        });
      </script>
    </body>
    
    </html>
    View Code

    定义Vue组件

    什么是组件: 组件的出现,就是为了拆分Vue实例的代码量的,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可以去调用对应的组件即可; 组件化和模块化的不同:

    • 模块化: 是从代码逻辑的角度进行划分的;方便代码分层开发,保证每个功能模块的职能单一;

    • 组件化: 是从UI界面的角度进行划分的;前端的组件化,方便UI组件的重用;

    全局组件定义的三种方式

    1. 使用 Vue.extend 配合 Vue.component 方法:

    var login = Vue.extend({
        template: '<h1>登录</h1>'
      });
      Vue.component('login', login);
    1. 直接使用 Vue.component 方法:

    Vue.component('register', {
        template: '<h1>注册</h1>'
      });
    1. 将模板字符串,定义到script标签种:

    <script id="tmpl" type="x-template">
        <div><a href="#">登录</a> | <a href="#">注册</a></div>
      </script>

    同时,需要使用 Vue.component 来定义组件:

    Vue.component('account', {
        template: '#tmpl'
      });

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

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
        <script src="./vue-2.4.0.js"></script>
    </head>
    
    <body>
        <div id="app">
            <mycomm1></mycomm1>
        </div>
        <script>
            Vue.component('mycomm1',Vue.extend({
                template:'<h3>这是使用Vue.extend创建的组件</h3>'
            }))
            var vm=new Vue({
               el:'#app',
               data:{},
               methods:{}
            });
        </script>
    </body>
    
    </html>
    View Code
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
    </head>
    
    <body>
      <div id="app">
        <!-- 还是使用 标签形式,引入自己的组件 -->
        <mycom2></mycom2>
      </div>
    
      <script>
        // 注意:不论是哪种方式创建出来的组件,组件的 template 属性指向的模板内容,必须有且只能有唯一的一个根元素
        Vue.component('mycom2', {
          template: '<div><h3>这是直接使用 Vue.component 创建出来的组件</h3><span>123</span></div>'
        })
    
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {},
          methods: {}
        });
      </script>
    </body>
    
    </html>
    View Code

    创建组件方式三

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
    </head>
    
    <body>
      <div id="app">
        <mycom3></mycom3>
        <!-- <login></login> -->
      </div>
    
    
      <div id="app2">
        <mycom3></mycom3>
        <login></login>
      </div>
    
      <!-- 在 被控制的 #app 外面,使用 template 元素,定义组件的HTML模板结构  -->
      <template id="tmpl">
        <div>
          <h1>这是通过 template 元素,在外部定义的组件结构,这个方式,有代码的只能提示和高亮</h1>
          <h4>好用,不错!</h4>
        </div>
      </template>
    
      <template id="tmpl2">
        <h1>这是私有的 login 组件</h1>
      </template>
    
      <script>
        Vue.component('mycom3', {
          template: '#tmpl'
        })
    
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {},
          methods: {}
        });
    
    
        var vm2 = new Vue({
          el: '#app2',
          data: {},
          methods: {},
          filters: {},
          directives: {},
          components: { // 定义实例内部私有组件的
            login: {
              template: '#tmpl2'
            }
          },
    
          beforeCreate() { },
          created() { },
          beforeMount() { },
          mounted() { },
          beforeUpdate() { },
          updated() { },
          beforeDestroy() { },
          destroyed() { }
        })
      </script>
    </body>
    
    </html>
    View Code

    组件中展示数据和响应事件

    1. 在组件中,data需要被定义为一个方法,例如:

    Vue.component('account', {
        template: '#tmpl',
        data() {
          return {
            msg: '大家好!'
          }
        },
        methods:{
          login(){
            alert('点击了登录按钮');
          }
        }
      });
    1. 在子组件中,如果将模板字符串,定义到了script标签中,那么,要访问子组件身上的data属性中的值,需要使用this来访问;

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

    1. 通过计数器案例演示

    使用components属性定义局部子组件

    1. 组件实例定义方式:

    <script>
      // 创建 Vue 实例,得到 ViewModel
      var vm = new Vue({
        el: '#app',
        data: {},
        methods: {},
        components: { // 定义子组件
          account: { // account 组件
            template: '<div><h1>这是Account组件{{name}}</h1><login></login></div>', // 在这里使用定义的子组件
            components: { // 定义子组件的子组件
              login: { // login 组件
                template: "<h3>这是登录组件</h3>"
              }
            }
          }
        }
      });
    </script>
    1. 引用组件:

    <div id="app">
      <account></account>
    </div>


    组件中的data和methods方法

    1. 组件可以有自己的 data 数据
    2. 组件的 data 和 实例的 data 有点不一样,实例中的 data 可以为一个对象,但是 组件中的 data 必须是一个方法
    3. 组件中的 data 除了必须为一个方法之外,这个方法内部,还必须返回一个对对象才行;
    4. 组件中 的data 数据,使用方式,和实例中的 data 使用方式完全一样!!!

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
    </head>
    
    <body>
      <div id="app">
        <mycom1></mycom1>
      </div>
    
      <script>
        // 1. 组件可以有自己的 data 数据
        // 2. 组件的 data 和 实例的 data 有点不一样,实例中的 data 可以为一个对象,但是 组件中的 data 必须是一个方法
        // 3. 组件中的 data 除了必须为一个方法之外,这个方法内部,还必须返回一个对象才行;
        // 4. 组件中 的data 数据,使用方式,和实例中的 data 使用方式完全一样!!!
        Vue.component('mycom1', {
          template: '<h1>这是全局组件 --- {{msg}}</h1>',
          data: function () {
            return {
              msg: '这是组件的中data定义的数据'
            }
          }
        })
    
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {},
          methods: {}
        });
      </script>
    </body>
    
    </html>
    View Code
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
    </head>
    
    <body>
      <div id="app">
        <counter></counter>
        <hr>
        <counter></counter>
        <hr>
        <counter></counter>
      </div>
    
    
      <template id="tmpl">
        <div>
          <input type="button" value="+1" @click="increment">
          <h3>{{count}}</h3>
        </div>
      </template>
    
      <script>
        var dataObj = { count: 0 }
    
        // 这是一个计数器的组件, 身上有个按钮,每当点击按钮,让 data 中的 count 值 +1
        Vue.component('counter', {
          template: '#tmpl',
          data: function () {
            // return dataObj
            return { count: 0 }
          },
          methods: {
            increment() {
              this.count++
            }
          }
        })
    
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {},
          methods: {}
        });
      </script>
    </body>
    
    </html>
    View Code
     

    使用flag标识符结合v-ifv-else切换组件

    1. 页面结构:

    <div id="app">
      <input type="button" value="toggle" @click="flag=!flag">
      <my-com1 v-if="flag"></my-com1>
      <my-com2 v-else="flag"></my-com2>
    </div>
    1. Vue实例定义:

    <script>
      Vue.component('myCom1', {
        template: '<h3>奔波霸</h3>'
      })

      Vue.component('myCom2', {
        template: '<h3>霸波奔</h3>'
      })

      // 创建 Vue 实例,得到 ViewModel
      var vm = new Vue({
        el: '#app',
        data: {
          flag: true
        },
        methods: {}
      });
    </script>

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
    </head>
    
    <body>
      <div id="app">
        <a href="" @click.prevent="flag=true">登录</a>
        <a href="" @click.prevent="flag=false">注册</a>
    
        <login v-if="flag"></login>
        <register v-else="flag"></register>
    
      </div>
    
      <script>
        Vue.component('login', {
          template: '<h3>登录组件</h3>'
        })
    
        Vue.component('register', {
          template: '<h3>注册组件</h3>'
        })
    
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {
            flag: false
          },
          methods: {}
        });
      </script>
    </body>
    
    </html>
    View Code

    组件切换方式二:

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
    </head>
    
    <body>
      <div id="app">
        <a href="" @click.prevent="comName='login'">登录</a>
        <a href="" @click.prevent="comName='register'">注册</a>
    
        <!-- Vue提供了 component ,来展示对应名称的组件 -->
        <!-- component 是一个占位符, :is 属性,可以用来指定要展示的组件的名称 -->
        <component :is="comName"></component>
    
        <!-- 总结:当前学习了几个 Vue 提供的标签了??? -->
        <!-- component,  template,  transition,  transitionGroup  -->
    
      </div>
    
      <script>
        // 组件名称是 字符串
        Vue.component('login', {
          template: '<h3>登录组件</h3>'
        })
    
        Vue.component('register', {
          template: '<h3>注册组件</h3>'
        })
    
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {
            comName: 'login' // 当前 component 中的 :is 绑定的组件的名称
          },
          methods: {}
        });
      </script>
    </body>
    
    </html>
    View Code
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
      <style>
        .v-enter,
        .v-leave-to {
          opacity: 0;
          transform: translateX(150px);
        }
    
        .v-enter-active,
        .v-leave-active {
          transition: all 0.5s ease;
        }
      </style>
    </head>
    
    <body>
      <div id="app">
        <a href="" @click.prevent="comName='login'">登录</a>
        <a href="" @click.prevent="comName='register'">注册</a>
    
        <!-- 通过 mode 属性,设置组件切换时候的 模式 -->
        <transition mode="out-in">
          <component :is="comName"></component>
        </transition>
    
      </div>
    
      <script>
        // 组件名称是 字符串
        Vue.component('login', {
          template: '<h3>登录组件</h3>'
        })
    
        Vue.component('register', {
          template: '<h3>注册组件</h3>'
        })
    
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {
            comName: 'login' // 当前 component 中的 :is 绑定的组件的名称
          },
          methods: {}
        });
      </script>
    </body>
    
    </html>
    View Code
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
      <style>
        .v-enter,
        .v-leave-to {
          opacity: 0;
          transform: translateX(150px);
        }
    
        .v-enter-active,
        .v-leave-active {
          transition: all 0.5s ease;
        }
      </style>
    </head>
    
    <body>
      <div id="app">
        <a href="" @click.prevent="comName='login'">登录</a>
        <a href="" @click.prevent="comName='register'">注册</a>
    
        <!-- 通过 mode 属性,设置组件切换时候的 模式 -->
        <transition mode="out-in">
          <component :is="comName"></component>
        </transition>
    
      </div>
    
      <script>
        // 组件名称是 字符串
        Vue.component('login', {
          template: '<h3>登录组件</h3>'
        })
    
        Vue.component('register', {
          template: '<h3>注册组件</h3>'
        })
    
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {
            comName: 'login' // 当前 component 中的 :is 绑定的组件的名称
          },
          methods: {}
        });
      </script>
    </body>
    
    </html>
    View Code
     

    使用:is属性来切换不同的子组件,并添加切换动画

    1. 组件实例定义方式:

      // 登录组件
      const login = Vue.extend({
        template: `<div>
          <h3>登录组件</h3>
        </div>`
      });
      Vue.component('login', login);

      // 注册组件
      const register = Vue.extend({
        template: `<div>
          <h3>注册组件</h3>
        </div>`
      });
      Vue.component('register', register);

      // 创建 Vue 实例,得到 ViewModel
      var vm = new Vue({
        el: '#app',
        data: { comName: 'login' },
        methods: {}
      });
    1. 使用component标签,来引用组件,并通过:is属性来指定要加载的组件:

      <div id="app">
      <a href="#" @click.prevent="comName='login'">登录</a>
      <a href="#" @click.prevent="comName='register'">注册</a>
      <hr>
      <transition mode="out-in">
        <component :is="comName"></component>
      </transition>
    </div>
    1. 添加切换样式:

      <style>
      .v-enter,
      .v-leave-to {
        opacity: 0;
        transform: translateX(30px);
      }

      .v-enter-active,
      .v-leave-active {
        position: absolute;
        transition: all 0.3s ease;
      }

      h3{
        margin: 0;
      }
    </style>
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
      <style>
        .v-enter,
        .v-leave-to {
          opacity: 0;
          transform: translateX(150px);
        }
    
        .v-enter-active,
        .v-leave-active {
          transition: all 0.5s ease;
        }
      </style>
    </head>
    
    <body>
      <div id="app">
        <a href="" @click.prevent="comName='login'">登录</a>
        <a href="" @click.prevent="comName='register'">注册</a>
    
        <!-- 通过 mode 属性,设置组件切换时候的 模式 -->
        <transition mode="out-in">
          <component :is="comName"></component>
        </transition>
    
      </div>
    
      <script>
        // 组件名称是 字符串
        Vue.component('login', {
          template: '<h3>登录组件</h3>'
        })
    
        Vue.component('register', {
          template: '<h3>注册组件</h3>'
        })
    
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {
            comName: 'login' // 当前 component 中的 :is 绑定的组件的名称
          },
          methods: {}
        });
      </script>
    </body>
    
    </html>
    View Code
     

    父组件向子组件传值

    1. 组件实例定义方式,注意:一定要使用props属性来定义父组件传递过来的数据

    <script>
      // 创建 Vue 实例,得到 ViewModel
      var vm = new Vue({
        el: '#app',
        data: {
          msg: '这是父组件中的消息'
        },
        components: {
          son: {
            template: '<h1>这是子组件 --- {{finfo}}</h1>',
            props: ['finfo']
          }
        }
      });
    </script>
    1. 使用v-bind或简化指令,将数据传递到子组件中:

    <div id="app">
      <son :finfo="msg"></son>
    </div>
     
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
    </head>
    
    <body>
      <div id="app">
        <!-- 父组件,可以在引用子组件的时候, 通过 属性绑定(v-bind:) 的形式, 把 需要传递给 子组件的数据,以属性绑定的形式,传递到子组件内部,供子组件使用 -->
        <com1 v-bind:parentmsg="msg"></com1>
      </div>
    
      <script>
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {
            msg: '123 啊-父组件中的数据'
          },
          methods: {},
    
          components: {
            // 结论:经过演示,发现,子组件中,默认无法访问到 父组件中的 data 上的数据 和 methods 中的方法
            com1: {
              data() { // 注意: 子组件中的 data 数据,并不是通过 父组件传递过来的,而是子组件自身私有的,比如: 子组件通过 Ajax ,请求回来的数据,都可以放到 data 身上;
                // data 上的数据,都是可读可写的;
                return {
                  title: '123',
                  content: 'qqq'
                }
              },
              template: '<h1 @click="change">这是子组件 --- {{ parentmsg }}</h1>',
              // 注意: 组件中的 所有 props 中的数据,都是通过 父组件传递给子组件的
              // props 中的数据,都是只读的,无法重新赋值
              props: ['parentmsg'], // 把父组件传递过来的 parentmsg 属性,先在 props 数组中,定义一下,这样,才能使用这个数据
              directives: {},
              filters: {},
              components: {},
              methods: {
                change() {
                  this.parentmsg = '被修改了'
                }
              }
            }
          }
        });
      </script>
    </body>
    
    </html>
    View Code

    子组件向父组件传值

    1. 原理:父组件将方法的引用,传递到子组件内部,子组件在内部调用父组件传递过来的方法,同时把要发送给父组件的数据,在调用方法的时候当作参数传递进去;

    2. 父组件将方法的引用传递给子组件,其中,getMsg是父组件中methods中定义的方法名称,func是子组件调用传递过来方法时候的方法名称

    <son @func="getMsg"></son>
    1. 子组件内部通过this.$emit('方法名', 要传递的数据)方式,来调用父组件中的方法,同时把数据传递给父组件使用

    <div id="app">
      <!-- 引用父组件 -->
      <son @func="getMsg"></son>

      <!-- 组件模板定义 -->
      <script type="x-template" id="son">
        <div>
          <input type="button" value="向父组件传值" @click="sendMsg" />
        </div>
      </script>
    </div>

    <script>
      // 子组件的定义方式
      Vue.component('son', {
        template: '#son', // 组件模板Id
        methods: {
          sendMsg() { // 按钮的点击事件
            this.$emit('func', 'OK'); // 调用父组件传递过来的方法,同时把数据传递出去
          }
        }
      });

      // 创建 Vue 实例,得到 ViewModel
      var vm = new Vue({
        el: '#app',
        data: {},
        methods: {
          getMsg(val){ // 子组件中,通过 this.$emit() 实际调用的方法,在此进行定义
            alert(val);
          }
        }
      });
    </script>
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
    </head>
    
    <body>
      <div id="app">
        <!-- 父组件向子组件 传递 方法,使用的是 事件绑定机制; v-on, 当我们自定义了 一个 事件属性之后,那么,子组件就能够,通过某些方式,来调用 传递进去的 这个 方法了 -->
        <com2 @func="show"></com2>
      </div>
    
      <template id="tmpl">
        <div>
          <h1>这是 子组件</h1>
          <input type="button" value="这是子组件中的按钮 - 点击它,触发 父组件传递过来的 func 方法" @click="myclick">
        </div>
      </template>
    
      <script>
    
        // 定义了一个字面量类型的 组件模板对象
        var com2 = {
          template: '#tmpl', // 通过指定了一个 Id, 表示 说,要去加载 这个指定Id的 template 元素中的内容,当作 组件的HTML结构
          data() {
            return {
              sonmsg: { name: '小头儿子', age: 6 }
            }
          },
          methods: {
            myclick() {
              // 当点击子组件的按钮的时候,如何 拿到 父组件传递过来的 func 方法,并调用这个方法???
              //  emit 英文原意: 是触发,调用、发射的意思
              // this.$emit('func123', 123, 456)
              this.$emit('func', this.sonmsg)
            }
          }
        }
    
    
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {
            datamsgFormSon: null
          },
          methods: {
            show(data) {
              // console.log('调用了父组件身上的 show 方法: --- ' + data)
              // console.log(data);
              this.datamsgFormSon = data
            }
          },
    
          components: {
            com2
            // com2: com2
          }
        });
      </script>
    </body>
    
    </html>
    View Code
     

    评论列表案例

    目标:主要练习父子组件之间传值

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

      <div id="app">
      <div>
        <input type="button" value="获取元素内容" @click="getElement" />
        <!-- 使用 ref 获取元素 -->
        <h1 ref="myh1">这是一个大大的H1</h1>

        <hr>
        <!-- 使用 ref 获取子组件 -->
        <my-com ref="mycom"></my-com>
      </div>
    </div>

    <script>
      Vue.component('my-com', {
        template: '<h5>这是一个子组件</h5>',
        data() {
          return {
            name: '子组件'
          }
        }
      });

      // 创建 Vue 实例,得到 ViewModel
      var vm = new Vue({
        el: '#app',
        data: {},
        methods: {
          getElement() {
            // 通过 this.$refs 来获取元素
            console.log(this.$refs.myh1.innerText);
            // 通过 this.$refs 来获取组件
            console.log(this.$refs.mycom.name);
          }
        }
      });
    </script>

    什么是路由

    1. 对于普通的网站,所有的超链接都是URL地址,所有的URL地址都对应服务器上对应的资源;

    2. 对于单页面应用程序来说,主要通过URL中的hash(#号)来实现不同页面之间的切换,同时,hash有一个特点:HTTP请求中不会包含hash相关的内容;所以,单页面程序中的页面跳转主要用hash实现;

    3. 在单页面应用程序中,这种通过hash改变来切换页面的方式,称作前端路由(区别于后端路由);

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
      <!-- 1. 安装 vue-router 路由模块 -->
      <script src="./lib/vue-router-3.0.1.js"></script>
      <style>
        .router-link-active,
        .myactive {
          color: red;
          font-weight: 800;
          font-style: italic;
          font-size: 80px;
          text-decoration: underline;
          background-color: green;
        }
    
        .v-enter,
        .v-leave-to {
          opacity: 0;
          transform: translateX(140px);
        }
    
        .v-enter-active,
        .v-leave-active {
          transition: all 0.5s ease;
        }
      </style>
    </head>
    
    <body>
      <div id="app">
    
        <!-- <a href="#/login">登录</a> -->
        <!-- <a href="#/register">注册</a> -->
    
        <!-- router-link 默认渲染为一个a 标签 -->
        <router-link to="/login" tag="span">登录</router-link>
        <router-link to="/register">注册</router-link>
    
    
        <!-- 这是 vue-router 提供的元素,专门用来 当作占位符的,将来,路由规则,匹配到的组件,就会展示到这个 router-view 中去 -->
        <!-- 所以: 我们可以把 router-view 认为是一个占位符 -->
        <transition mode="out-in">
          <router-view></router-view>
        </transition>
    
      </div>
    
      <script>
        // 组件的模板对象
        var login = {
          template: '<h1>登录组件</h1>'
        }
    
        var register = {
          template: '<h1>注册组件</h1>'
        }
    
    
        /*  Vue.component('login', {
           template: '<h1>登录组件</h1>'
         }) */
    
        // 2. 创建一个路由对象, 当 导入 vue-router 包之后,在 window 全局对象中,就有了一个 路由的构造函数,叫做 VueRouter
        // 在 new 路由对象的时候,可以为 构造函数,传递一个配置对象
        var routerObj = new VueRouter({
          // route // 这个配置对象中的 route 表示 【路由匹配规则】 的意思
          routes: [ // 路由匹配规则 
            // 每个路由规则,都是一个对象,这个规则对象,身上,有两个必须的属性:
            //  属性1 是 path, 表示监听 哪个路由链接地址;
            //  属性2 是 component, 表示,如果 路由是前面匹配到的 path ,则展示 component 属性对应的那个组件
            // 注意: component 的属性值,必须是一个 组件的模板对象, 不能是 组件的引用名称;
            // { path: '/', component: login },
            { path: '/', redirect: '/login' }, // 这里的 redirect 和 Node 中的 redirect 完全是两码事
            { path: '/login', component: login },
            { path: '/register', component: register }
          ],
          linkActiveClass: 'myactive'
        })
    
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {},
          methods: {},
          router: routerObj // 将路由规则对象,注册到 vm 实例上,用来监听 URL 地址的变化,然后展示对应的组件
        });
      </script>
    </body>
    
    </html>
    View Code

    在 vue 中使用 vue-router

    1. 导入 vue-router 组件类库:

    <!-- 1. 导入 vue-router 组件类库 -->
    <script src="./lib/vue-router-2.7.0.js"></script>
    1. 使用 router-link 组件来导航

    <!-- 2. 使用 router-link 组件来导航 -->
    <router-link to="/login">登录</router-link>
    <router-link to="/register">注册</router-link>
    1. 使用 router-view 组件来显示匹配到的组件

    <!-- 3. 使用 router-view 组件来显示匹配到的组件 -->
    <router-view></router-view>
    1. 创建使用Vue.extend创建组件

        // 4.1 使用 Vue.extend 来创建登录组件
      var login = Vue.extend({
        template: '<h1>登录组件</h1>'
      });

      // 4.2 使用 Vue.extend 来创建注册组件
      var register = Vue.extend({
        template: '<h1>注册组件</h1>'
      });
    1. 创建一个路由 router 实例,通过 routers 属性来定义路由匹配规则

    // 5. 创建一个路由 router 实例,通过 routers 属性来定义路由匹配规则
      var router = new VueRouter({
        routes: [
          { path: '/login', component: login },
          { path: '/register', component: register }
        ]
      });
    1. 使用 router 属性来使用路由规则

    // 6. 创建 Vue 实例,得到 ViewModel
      var vm = new Vue({
        el: '#app',
        router: router // 使用 router 属性来使用路由规则
      });
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./vue-2.4.0.js"></script>
      <!-- 1. 安装 vue-router 路由模块 -->
      <script src="./vue-router-3.0.1.js"></script>
      <style>
        .router-link-active,
        .myactive {
          color: red;
          font-weight: 800;
          font-style: italic;
          font-size: 80px;
          text-decoration: underline;
          background-color: green;
        }
    
        .v-enter,
        .v-leave-to {
          opacity: 0;
          transform: translateX(140px);
        }
    
        .v-enter-active,
        .v-leave-active {
          transition: all 0.5s ease;
        }
      </style>
    </head>
    
    <body>
      <div id="app">
    
        <!-- <a href="#/login">登录</a> -->
        <!-- <a href="#/register">注册</a> -->
    
        <!-- router-link 默认渲染为一个a 标签 -->
        <router-link to="/login" tag="span">登录</router-link>
        <router-link to="/register">注册</router-link>
    
    
        <!-- 这是 vue-router 提供的元素,专门用来 当作占位符的,将来,路由规则,匹配到的组件,就会展示到这个 router-view 中去 -->
        <!-- 所以: 我们可以把 router-view 认为是一个占位符 -->
        <transition mode="out-in">
          <router-view></router-view>
        </transition>
    
      </div>
    
      <script>
        // 组件的模板对象
        var login = {
          template: '<h1>登录组件</h1>'
        }
    
        var register = {
          template: '<h1>注册组件</h1>'
        }
    
    
        /*  Vue.component('login', {
           template: '<h1>登录组件</h1>'
         }) */
    
        // 2. 创建一个路由对象, 当 导入 vue-router 包之后,在 window 全局对象中,就有了一个 路由的构造函数,叫做 VueRouter
        // 在 new 路由对象的时候,可以为 构造函数,传递一个配置对象
        var routerObj = new VueRouter({
          // route // 这个配置对象中的 route 表示 【路由匹配规则】 的意思
          routes: [ // 路由匹配规则 
            // 每个路由规则,都是一个对象,这个规则对象,身上,有两个必须的属性:
            //  属性1 是 path, 表示监听 哪个路由链接地址;
            //  属性2 是 component, 表示,如果 路由是前面匹配到的 path ,则展示 component 属性对应的那个组件
            // 注意: component 的属性值,必须是一个 组件的模板对象, 不能是 组件的引用名称;
            // { path: '/', component: login },
            { path: '/', redirect: '/login' }, // 这里的 redirect 和 Node 中的 redirect 完全是两码事
            { path: '/login', component: login },
            { path: '/register', component: register }
          ],
          linkActiveClass: 'myactive'
        })
    
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {},
          methods: {},
          router: routerObj // 将路由规则对象,注册到 vm 实例上,用来监听 URL 地址的变化,然后展示对应的组件
        });
      </script>
    </body>
    
    </html>
    View Code
     

    设置路由高亮

    设置路由切换动效

    在路由规则中定义参数

    1. 在规则中定义参数:

    { path: '/register/:id', component: register }
    1. 通过 this.$route.params来获取路由中的参数:

    var register = Vue.extend({
        template: '<h1>注册组件 --- {{this.$route.params.id}}</h1>'
      });
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
      <script src="./lib/vue-router-3.0.1.js"></script>
    </head>
    
    <body>
      <div id="app">
    
        <!-- 如果在路由中,使用 查询字符串,给路由传递参数,则 不需要修改 路由规则的 path 属性 -->
        <router-link to="/login?id=10&name=zs">登录</router-link>
        <router-link to="/register">注册</router-link>
    
        <router-view></router-view>
    
      </div>
    
      <script>
    
        var login = {
          template: '<h1>登录 --- {{ $route.query.id }} --- {{ $route.query.name }}</h1>',
          data(){
            return {
              msg: '123'
            }
          },
          created(){ // 组件的生命周期钩子函数
            // console.log(this.$route)
            // console.log(this.$route.query.id)
          }
        }
    
        var register = {
          template: '<h1>注册</h1>'
        }
    
        var router = new VueRouter({
          routes: [
            { path: '/login', component: login },
            { path: '/register', component: register }
          ]
        })
    
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {},
          methods: {},
          // router: router
          router
        });
      </script>
    </body>
    
    </html>
    View Code
     

    使用 children 属性实现路由嵌套

      <div id="app">
      <router-link to="/account">Account</router-link>

      <router-view></router-view>
    </div>

    <script>
      // 父路由中的组件
      const account = Vue.extend({
        template: `<div>
          这是account组件
          <router-link to="/account/login">login</router-link> |
          <router-link to="/account/register">register</router-link>
          <router-view></router-view>
        </div>`
      });

      // 子路由中的 login 组件
      const login = Vue.extend({
        template: '<div>登录组件</div>'
      });

      // 子路由中的 register 组件
      const register = Vue.extend({
        template: '<div>注册组件</div>'
      });

      // 路由实例
      var router = new VueRouter({
        routes: [
          { path: '/', redirect: '/account/login' }, // 使用 redirect 实现路由重定向
          {
            path: '/account',
            component: account,
            children: [ // 通过 children 数组属性,来实现路由的嵌套
              { path: 'login', component: login }, // 注意,子路由的开头位置,不要加 / 路径符
              { path: 'register', component: register }
            ]
          }
        ]
      });

      // 创建 Vue 实例,得到 ViewModel
      var vm = new Vue({
        el: '#app',
        data: {},
        methods: {},
        components: {
          account
        },
        router: router
      });
    </script>
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
      <script src="./lib/vue-router-3.0.1.js"></script>
    </head>
    
    <body>
      <div id="app">
    
        <router-link to="/account">Account</router-link>
    
        <router-view></router-view>
    
      </div>
    
      <template id="tmpl">
        <div>
          <h1>这是 Account 组件</h1>
    
          <router-link to="/account/login">登录</router-link>
          <router-link to="/account/register">注册</router-link>
    
          <router-view></router-view>
        </div>
      </template>
    
      <script>
    
        // 组件的模板对象
        var account = {
          template: '#tmpl'
        }
    
        var login = {
          template: '<h3>登录</h3>'
        }
    
        var register = {
          template: '<h3>注册</h3>'
        }
    
        var router = new VueRouter({
          routes: [
            {
              path: '/account',
              component: account,
              // 使用 children 属性,实现子路由,同时,子路由的 path 前面,不要带 / ,否则永远以根路径开始请求,这样不方便我们用户去理解URL地址
              children: [
                { path: 'login', component: login },
                { path: 'register', component: register }
              ]
            }
            // { path: '/account/login', component: login },
            // { path: '/account/register', component: register }
          ]
        })
    
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {},
          methods: {},
          router
        });
      </script>
    </body>
    
    </html>
    View Code
     

    命名视图实现经典布局

    1. 标签代码结构:

    <div id="app">
      <router-view></router-view>
      <div class="content">
        <router-view name="a"></router-view>
        <router-view name="b"></router-view>
      </div>
    </div>
    1. JS代码:

    <script>
      var header = Vue.component('header', {
        template: '<div class="header">header</div>'
      });

      var sidebar = Vue.component('sidebar', {
        template: '<div class="sidebar">sidebar</div>'
      });

      var mainbox = Vue.component('mainbox', {
        template: '<div class="mainbox">mainbox</div>'
      });

      // 创建路由对象
      var router = new VueRouter({
        routes: [
          {
            path: '/', components: {
              default: header,
              a: sidebar,
              b: mainbox
            }
          }
        ]
      });

      // 创建 Vue 实例,得到 ViewModel
      var vm = new Vue({
        el: '#app',
        data: {},
        methods: {},
        router
      });
    </script>
    1. CSS 样式:

      <style>
      .header {
        border: 1px solid red;
      }

      .content{
        display: flex;
      }
      .sidebar {
        flex: 2;
        border: 1px solid green;
        height: 500px;
      }
      .mainbox{
        flex: 8;
        border: 1px solid blue;
        height: 500px;
      }
    </style>
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./lib/vue-2.4.0.js"></script>
      <script src="./lib/vue-router-3.0.1.js"></script>
      <style>
        html,
        body {
          margin: 0;
          padding: 0;
        }
    
        .header {
          background-color: orange;
          height: 80px;
        }
    
        h1 {
          margin: 0;
          padding: 0;
          font-size: 16px;
        }
    
        .container {
          display: flex;
          height: 600px;
        }
    
        .left {
          background-color: lightgreen;
          flex: 2;
        }
    
        .main {
          background-color: lightpink;
          flex: 8;
        }
      </style>
    </head>
    
    <body>
      <div id="app">
    
        <router-view></router-view>
        <div class="container">
          <router-view name="left"></router-view>
          <router-view name="main"></router-view>
        </div>
    
      </div>
    
      <script>
    
        var header = {
          template: '<h1 class="header">Header头部区域</h1>'
        }
    
        var leftBox = {
          template: '<h1 class="left">Left侧边栏区域</h1>'
        }
    
        var mainBox = {
          template: '<h1 class="main">mainBox主体区域</h1>'
        }
    
        // 创建路由对象
        var router = new VueRouter({
          routes: [
            /* { path: '/', component: header },
            { path: '/left', component: leftBox },
            { path: '/main', component: mainBox } */
    
    
            {
              path: '/', components: {
                'default': header,
                'left': leftBox,
                'main': mainBox
              }
            }
          ]
        })
    
        // 创建 Vue 实例,得到 ViewModel
        var vm = new Vue({
          el: '#app',
          data: {},
          methods: {},
          router
        });
      </script>
    </body>
    
    </html>
    View Code
     

    watch属性的使用

    考虑一个问题:想要实现 两个文本框的内容改变,则全名的文本框中的值也跟着改变;(用以前的知识如何实现???)

    1. 监听data中属性的改变:

    <div id="app">
      <input type="text" v-model="firstName"> +
      <input type="text" v-model="lastName"> =
      <span>{{fullName}}</span>
    </div>

    <script>
      // 创建 Vue 实例,得到 ViewModel
      var vm = new Vue({
        el: '#app',
        data: {
          firstName: 'jack',
          lastName: 'chen',
          fullName: 'jack - chen'
        },
        methods: {},
        watch: {
          'firstName': function (newVal, oldVal) { // 第一个参数是新数据,第二个参数是旧数据
            this.fullName = newVal + ' - ' + this.lastName;
          },
          'lastName': function (newVal, oldVal) {
            this.fullName = this.firstName + ' - ' + newVal;
          }
        }
      });
    </script>
    1. 监听路由对象的改变:

    <div id="app">
      <router-link to="/login">登录</router-link>
      <router-link to="/register">注册</router-link>

      <router-view></router-view>
    </div>

    <script>
      var login = Vue.extend({
        template: '<h1>登录组件</h1>'
      });

      var register = Vue.extend({
        template: '<h1>注册组件</h1>'
      });

      var router = new VueRouter({
        routes: [
          { path: "/login", component: login },
          { path: "/register", component: register }
        ]
      });

      // 创建 Vue 实例,得到 ViewModel
      var vm = new Vue({
        el: '#app',
        data: {},
        methods: {},
        router: router,
        watch: {
          '$route': function (newVal, oldVal) {
            if (newVal.path === '/login') {
              console.log('这是登录组件');
            }
          }
        }
      });
    </script>

    computed计算属性的使用

    1. 默认只有getter的计算属性:

    <div id="app">
      <input type="text" v-model="firstName"> +
      <input type="text" v-model="lastName"> =
      <span>{{fullName}}</span>
    </div>

    <script>
      // 创建 Vue 实例,得到 ViewModel
      var vm = new Vue({
        el: '#app',
        data: {
          firstName: 'jack',
          lastName: 'chen'
        },
        methods: {},
        computed: { // 计算属性; 特点:当计算属性中所以来的任何一个 data 属性改变之后,都会重新触发 本计算属性 的重新计算,从而更新 fullName 的值
          fullName() {
            return this.firstName + ' - ' + this.lastName;
          }
        }
      });
    </script>
    1. 定义有gettersetter的计算属性:

    <div id="app">
      <input type="text" v-model="firstName">
      <input type="text" v-model="lastName">
      <!-- 点击按钮重新为 计算属性 fullName 赋值 -->
      <input type="button" value="修改fullName" @click="changeName">

      <span>{{fullName}}</span>
    </div>

    <script>
      // 创建 Vue 实例,得到 ViewModel
      var vm = new Vue({
        el: '#app',
        data: {
          firstName: 'jack',
          lastName: 'chen'
        },
        methods: {
          changeName() {
            this.fullName = 'TOM - chen2';
          }
        },
        computed: {
          fullName: {
            get: function () {
              return this.firstName + ' - ' + this.lastName;
            },
            set: function (newVal) {
              var parts = newVal.split(' - ');
              this.firstName = parts[0];
              this.lastName = parts[1];
            }
          }
        }
      });
    </script>

    watchcomputedmethods之间的对比

    1. computed属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。主要当作属性来使用;

    2. methods方法表示一个具体的操作,主要书写业务逻辑;

    3. watch一个对象,键是需要观察的表达式,值是对应回调函数。主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作;可以看作是computedmethods的结合体;

    nrm的安装使用

    作用:提供了一些最常用的NPM包镜像地址,能够让我们快速的切换安装包时候的服务器地址; 什么是镜像:原来包刚一开始是只存在于国外的NPM服务器,但是由于网络原因,经常访问不到,这时候,我们可以在国内,创建一个和官网完全一样的NPM服务器,只不过,数据都是从人家那里拿过来的,除此之外,使用方式完全一样;

    1. 运行npm i nrm -g全局安装nrm包;

    2. 使用nrm ls查看当前所有可用的镜像源地址以及当前所使用的镜像源地址;

    3. 使用nrm use npmnrm use taobao切换不同的镜像源地址;

    相关文件

    URL中的hash(井号)

    在网页中会引用哪些常见的静态资源?

    • JS

    • .js .jsx .coffee .ts(TypeScript 类 C# 语言)

    • CSS

    • .css .less .sass .scss

    • Images

    • .jpg .png .gif .bmp .svg

    • 字体文件(Fonts)

    • .svg .ttf .eot .woff .woff2

    • 模板文件

    • .ejs .jade .vue【这是在webpack中定义组件的方式,推荐这么用】

    网页中引入的静态资源多了以后有什么问题???

    1. 网页加载速度慢, 因为 我们要发起很多的二次请求;

    2. 要处理错综复杂的依赖关系

    如何解决上述两个问题

    1. 合并、压缩、精灵图、图片的Base64编码

    2. 可以使用之前学过的requireJS、也可以使用webpack可以解决各个包之间的复杂依赖关系;

    什么是webpack?

    webpack 是前端的一个项目构建工具,它是基于 Node.js 开发出来的一个前端工具;

    如何完美实现上述的2种解决方案

    1. 使用Gulp, 是基于 task 任务的;

    2. 使用Webpack, 是基于整个项目进行构建的;

    • 借助于webpack这个前端自动化构建工具,可以完美实现资源的合并、打包、压缩、混淆等诸多功能。

    • 根据官网的图片介绍webpack打包的过程

    • webpack官网

    webpack安装的两种方式

    1. 运行npm i webpack -g全局安装webpack,这样就能在全局使用webpack的命令

    2. 在项目根目录中运行npm i webpack --save-dev安装到项目依赖中

    emmet:vscode内置了这个,但是没有开启,要在设置里面"emmet.triggerExpansionOnTab": true,

    vscoed-icons插件:图标插件

    初步使用webpack打包构建列表隔行变色案例

    1. 运行npm init初始化项目,使用npm管理项目中的依赖包

    2. 创建项目基本的目录结构

    3. 使用cnpm i jquery --save安装jquery类库

    4. 创建main.js并书写各行变色的代码逻辑:

        // 导入jquery类库
      import $ from 'jquery'

      // 设置偶数行背景色,索引从0开始,0是偶数
      $('#list li:even').css('backgroundColor','lightblue');
      // 设置奇数行背景色
      $('#list li:odd').css('backgroundColor','pink');
    1. 直接在页面上引用main.js会报错,因为浏览器不认识import这种高级的JS语法,需要使用webpack进行处理,webpack默认会把这种高级的语法转换为低级的浏览器能识别的语法;

    2. 运行webpack 入口文件路径 输出文件路径main.js进行处理:

    webpack src/js/main.js dist/bundle.js

    使用webpack的配置文件简化打包时候的命令

    1. 在项目根目录中创建webpack.config.js

    2. 由于运行webpack命令的时候,webpack需要指定入口文件和输出文件的路径,所以,我们需要在webpack.config.js中配置这两个路径:

        // 导入处理路径的模块
      var path = require('path');

      // 导出一个配置对象,将来webpack在启动的时候,会默认来查找webpack.config.js,并读取这个文件中导出的配置对象,来进行打包处理
      module.exports = {
          entry: path.resolve(__dirname, 'src/js/main.js'), // 项目入口文件
          output: { // 配置输出选项
              path: path.resolve(__dirname, 'dist'), // 配置输出的路径
              filename: 'bundle.js' // 配置输出的文件名
          }
      }

    实现webpack的实时打包构建

    1. 由于每次重新修改代码之后,都需要手动运行webpack打包的命令,比较麻烦,所以使用webpack-dev-server来实现代码实时打包编译,当修改代码之后,会自动进行打包构建。

    2. 运行cnpm i webpack-dev-server --save-dev安装到开发依赖

    3. 安装完成之后,在命令行直接运行webpack-dev-server来进行打包,发现报错,此时需要借助于package.json文件中的指令,来进行运行webpack-dev-server命令,在scripts节点下新增"dev": "webpack-dev-server"指令,发现可以进行实时打包,但是dist目录下并没有生成bundle.js文件,这是因为webpack-dev-server将打包好的文件放在了内存中

    • bundle.js放在内存中的好处是:由于需要实时打包编译,所以放在内存中速度会非常快

    • 这个时候访问webpack-dev-server启动的http://localhost:8080/网站,发现是一个文件夹的面板,需要点击到src目录下,才能打开我们的index首页,此时引用不到bundle.js文件,需要修改index.html中script的src属性为:<script src="../bundle.js"></script>

    • 为了能在访问http://localhost:8080/的时候直接访问到index首页,可以使用--contentBase src指令来修改dev指令,指定启动的根目录:

     "dev": "webpack-dev-server --contentBase src"

    同时修改index页面中script的src属性为<script src="bundle.js"></script>

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

    由于使用--contentBase指令的过程比较繁琐,需要指定启动的目录,同时还需要修改index.html中script标签的src属性,所以推荐大家使用html-webpack-plugin插件配置启动页面.

    1. 运行cnpm i html-webpack-plugin --save-dev安装到开发依赖

    2. 修改webpack.config.js配置文件如下:

        // 导入处理路径的模块
      var path = require('path');
      // 导入自动生成HTMl文件的插件
      var htmlWebpackPlugin = require('html-webpack-plugin');

      module.exports = {
          entry: path.resolve(__dirname, 'src/js/main.js'), // 项目入口文件
          output: { // 配置输出选项
              path: path.resolve(__dirname, 'dist'), // 配置输出的路径
              filename: 'bundle.js' // 配置输出的文件名
          },
          plugins:[ // 添加plugins节点配置插件
              new htmlWebpackPlugin({
                  template:path.resolve(__dirname, 'src/index.html'),//模板路径
                  filename:'index.html'//自动生成的HTML文件的名称
              })
          ]
      }
    1. 修改package.jsonscript节点中的dev指令如下:

    "dev": "webpack-dev-server"
    1. 将index.html中script标签注释掉,因为html-webpack-plugin插件会自动把bundle.js注入到index.html页面中!

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

    注意:热更新在JS中表现的不明显,可以从一会儿要讲到的CSS身上进行介绍说明!

    方式1:

    • 修改package.json的script节点如下,其中--open表示自动打开浏览器,--port 4321表示打开的端口号为4321,--hot表示启用浏览器热更新:

    "dev": "webpack-dev-server --hot --port 4321 --open"

    方式2:

    1. 修改webpack.config.js文件,新增devServer节点如下:

    devServer:{
          hot:true,
          open:true,
          port:4321
      }
    1. 在头部引入webpack模块:

    var webpack = require('webpack');
    1. plugins节点下新增:

    new webpack.HotModuleReplacementPlugin()

    使用webpack打包css文件

    1. 运行cnpm i style-loader css-loader --save-dev

    2. 修改webpack.config.js这个配置文件:

    module: { // 用来配置第三方loader模块的
          rules: [ // 文件的匹配规则
              { test: /.css$/, use: ['style-loader', 'css-loader'] }//处理css文件的规则
          ]
      }
    1. 注意:use表示使用哪些模块来处理test所匹配到的文件;use中相关loader模块的调用顺序是从后向前调用的;

    使用webpack打包less文件

    1. 运行cnpm i less-loader less -D

    2. 修改webpack.config.js这个配置文件:

    { test: /.less$/, use: ['style-loader', 'css-loader', 'less-loader'] },

    使用webpack打包sass文件

    1. 运行cnpm i sass-loader node-sass --save-dev

    2. webpack.config.js中添加处理sass文件的loader模块:

    { test: /.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] }

    使用webpack处理css中的路径

    1. 运行cnpm i url-loader file-loader --save-dev

    2. webpack.config.js中添加处理url路径的loader模块:

    { test: /.(png|jpg|gif)$/, use: 'url-loader' }
    1. 可以通过limit指定进行base64编码的图片大小;只有小于指定字节(byte)的图片才会进行base64编码:

    { test: /.(png|jpg|gif)$/, use: 'url-loader?limit=43960' },

    使用babel处理高级JS语法

    1. 运行cnpm i babel-core babel-loader babel-plugin-transform-runtime --save-dev安装babel的相关loader包

    2. 运行cnpm i babel-preset-es2015 babel-preset-stage-0 --save-dev安装babel转换的语法

    3. webpack.config.js中添加相关loader模块,其中需要注意的是,一定要把node_modules文件夹添加到排除项:

    { test: /.js$/, use: 'babel-loader', exclude: /node_modules/ }
    1. 在项目根目录中添加.babelrc文件,并修改这个配置文件如下:

    {
      "presets":["es2015", "stage-0"],
      "plugins":["transform-runtime"]
    }
    1. 注意:语法插件babel-preset-es2015可以更新为babel-preset-env,它包含了所有的ES相关的语法;

    相关文章

    babel-preset-env:你需要的唯一Babel插件 Runtime transform 运行时编译es6

  • 相关阅读:
    ASP.NET Core 返回 Json DateTime 格式
    IdentityServer4 SigningCredential(RSA 证书加密)
    IdentityServer4 指定角色授权(Authorize(Roles="admin"))
    ASP.NET Core 指定环境发布(hosting environment)
    ASP.NET Core 使用 Redis 客户端
    IdentityServer4 实现 OAuth 2.0(密码模式
    IdentityServer4 登录成功后,跳转到原来页面
    IdentityServer4 退出登录后,跳转到原来页面
    C#面向插件级别的软件开发
    使用XML序列化实现系统配置
  • 原文地址:https://www.cnblogs.com/wq-9/p/12237782.html
Copyright © 2020-2023  润新知