vue.js基础知识学习

打包工具:webpack,Gulp

vue.js

  • vue.js React都是前端框架,都可以开发网站和手机APP,vue需要借助weex进行手机APP的开发
  • 前端三大主流框架:Vue.js Angular.js React.js
  • vue.js是一套构建用户界面的框架,只关注视图层

框架和库的区别

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

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

    • 1.从jquery切换到 Zepto
    • 2.从EJS 切换到 art-template

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

  • mvc是后端的分层开发概念
    • 后端的分层开发概念:为了保证模块的职能单一
      • 项目入口模块(app.js):一切的请求,都要先进入这里进行处理(注意:此模块并没有路由分发的功能,需要调用,路由模块进行路由的分发处理)
      • 路由模块(router.js):只负责分发路由,不负责具体业务逻辑的处理
      • 业务逻辑模块(controller):封装了一些具体的业务逻辑处理的逻辑代码,只负责处理业务,不负责处理数据的CRUD。,如果涉及到了CRUD,则需要调用Model层
      • Model层:只负责操作数据库,执行对应的js等语句,进行数据的CRUD:增删改查
        • C:create R:Read U:update D:Delete
      • View 视图层(请求过程):每当用户操作了界面,如果需要进行业务处理,就会通过网络请求,去请求后端的服务器,此时,我们的这个请求,就会被后端的App.js监听到
      • app.js \ router.js \ controller \ Model层就是我们的处理过程;View 视图层就是请求过程
      • router.js \ controller 是controller 层 、 Model层 、View 视图层 (MVC)
  • mvvm是前端视图层的概念,主要关注于视图层分离,也就是说:mvvm把前端的视图层分成了三部分 Model,View,VM ViewModel
    • mvvm是前端视图层的分层开发思想,主要把每个页面分成了 m、v、和vm ,其中,vm是mvvm思想的核心:因为vm是m和v之间的调度者
      • m (数据):这里的m保存的是每个页面中单独的数据
      • vm:它是一个调度者,分割了m和v(每当v层想要获取活保存数据的时候,都要由vm做中间的处理)
      • v(视图):就是每个页面中的html结构
  • 前端页面中使用mvvm的思想,主要是为了让我们的开发更加方便,因为mvvm提供了数据的双向绑定;数据的双向绑定是由vm提供的。

vue.js基本代码和mvvm之间的对应关系

  • <!-- 将来new的vue实例,会控制这个元素的所有内容 -->
    <!-- vue 实例所控制的这个元素区域  就是我们的v -->
    <div id="app">
            <p id="content">{{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>
    

    vue.js的语法结构

    <div id = "app">
    </div>
    
    //创建vue实例
    var vm = new Vue({
               el:'#app',   //绑定页面
               props:{ //进行组件之间的传值操作
                   
               }
               data:{  //这个 data 属性中定义了当前vue实例所有可用的数据
                   msg:'123',
                   msg2:'<h1>哈哈,我是一个大大的h1</h1>',
                   mytitle:'这是一个自己定义的title',
               },
               methods:{  //这个 methods 属性中定义了当前vue实例所有可用的方法
                  show:function(){
                      alert('Hello')
                  } 
               },   
                filters:{
                    //定制私有的过滤器
                },
               directives:{
                   //定制私有的指令
               },
               components:{
                   //定制实例内部私有的组件
               },    
              watch:{// 主要用于监听事件。使用这个属性,可以监听data中指定数据的变化,然后触发这个watch中对应的function处理函数
                  
              },
              computed:{ // 计算属性
                  
              },
              created(){ // 实例创建完成,还没有挂载在页面上
                 
              },
              mounted(){ // 挂载完成
                  
              }
               beforeCreate(){},
               created(){},
               beforeMount(){},
               mounted(){},
               beforeUpdate(){},
               updated(){},
               beforeDestroy(){},
               destoryed(){}   
       
           })
    
    
    
    
***vue.js相关的注意点
  1. 注意:在vm实例中,如果想要获取data上的数据,或者想要调用methods中的放法,必须通过this.数据属性名 或 this.方法名 来进行访问,这里的this 就表示我们new出来的vm实例对象
  2. 注意:vm实例,会监听自己身上data中所有数据的改变,只要数据一发生变化,就会自动把最新的数据,从data上同步到页面中去;[好处:程序员只需要关心数据,不需要考虑如何重新渲染DOM页面]
  3. 定时器中的this指向window。匿名函数的this指向window。箭头函数解决了函数中this指向的问题,使内部的this始终预外部的this保持一致

vue指令

vue之-基本代码结构和插值表达式、v-cloak
  • 使用v-cloak能够解决插值表达式的闪烁问题
    • 使用方法:

      +++++++ {{msg}} ----------


      使用的时候必须加,不加的话没用
vue指令之v-textv-html
  • 进行数据绑定
    • 默认v-text是没有闪烁问题的,v-text会覆盖元素中原本的内容,但是插值表达式只会替换自己的这个占位符,不会把整个元素的内容清空
    • v-html可以绑定的html格式的数据,会自动解析html格式的数据。v-text只能绑定纯文本形式的数据,不会解析html语法。
vue指令之v-bind的三种用法
  • 绑定属性的指令
    • v-on vue提供的属性绑定机制 缩写是
    • 使用方法
      第一种:<input type="button" value="按钮" v-bind:title="mytitle">
      第二种:<input type="button" value="按钮" v-bind:title="mytitle + '123'">
      第三种:<input type="button" value="按钮" :title="mytitle + '123'">
    • v-bind:是vue中提供的用于绑定属性的指令
    • 注意:v-bind指令可以被简写为:要绑定的属性
    • v-bind中,可以写合法的js表达式

vue指令之v-on和跑马灯效果

  • 绑定事件的指令
    • v-on vue提供的事件绑定机制 缩写是@
    • 使用方法
      <input type="button" value="按钮" :title="mytitle + '123'" v-on:click = "alert('hello')">
      上面这个是错误的写法:会把后面的当成变量来执行
      第一种:<input type="button" value="按钮" v-on:click = "show">
      第二种:<input type="button" value="按钮" @click = "show">
  • 跑马灯效果
  1. HTML结构
       <div id="app">
               <input type="button" value="浪起来" @click = "lang">
               <input type="button" value="低调" @click = "stop">
               <h4>{{msg}}</h4>
        </div>
  1. javascript代码
      var vm = new Vue({
              el:"#app",
              data:{
                  msg:'猥琐发育,别浪~',
                  intervalId:null,  //在data上定义 定时器Id
              },
              methods:{
                  lang(){
                      if(this.intervalId != null) return;
                      // console.log(this.msg)
                      // var _this = this;                
                      // 定时器中的this指向window.匿名函数的this指向window.箭头函数解决了函数中this指向的问题,使内部的this始终预外部的this保持一致
                      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;
                  }
      
              }
          })        
vue指令之v-on的缩写和事件修饰符

事件修饰符:

  • .stop 阻止事件冒泡
  • .prevent 阻止默认行为
  • .capture 添加事件侦听器时使用事件捕获模式
  • .self只当事件在该元素本身(比如不是子元素) 触发时触发回调
  • .once 事件只触发一次
  • 使用方法:
       <!-- 使用.stop  阻止事件冒泡 -->
      <div class="inner" @click = "div1Handler">
                  <input type="button" value="戳他" @click.stop = "btnHandler">
      </div>
      <!-- 使用.prevent阻止默认行为 -->
      <a href="http://www.baidu.com" @click.prevent = "linkClick">有问题,先去百度</a>
      <!-- 使用.capture实现捕获触发事件的机制 -->
      <!-- 冒泡机制:从里向外触发 -->
      <!-- 捕获机制:从外向里面触发 -->
      <div class="inner" @click.capture = "div1Handler">
                      <input type="button" value="戳他" @click = "btnHandler">
      </div>
      <!-- 使用.self实现只有点击当前元素后才会触发事件处理函数 -->
      <!-- 使用.once只触发一次事件 -->
      <!-- .stop和.self的区别:  .self只会阻止自己身上冒泡行为的触发,并不会真正的阻止冒泡的行为 -->
      
vue指令之v-model和双向数据绑定
  • v-bind 只能实现数据的单向绑定,从m 自动绑定到 v,无法实现数据的双向绑定
  • 使用v-model指令可以实现表单元素和 model 中数据的双向数据绑定
  • 注意: v-model只能运用在表单元素中
  • input{redio, text, address, email...} select checkbox textarea
  • 使用方法:

简易计算器案例

  1. HTML代码
       <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>

.2 Vue实例代码

      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);
                   }
               }
           })
       
在vue中使用样式
使用class样式
  1. 数组
        <!-- 第一种使用方式,直接传递一个数组,注意:这里的class需要使用v-bind做数据绑定 -->
      <h1 :class="['red','thin']">这是一个很大的h1,大到你无法想象</h1> 
  1. 数组中使用三元表达式
       <!-- 在数组中使用三元表达式 -->
       <h1 :class="['red','thin',flag ? 'active':'']">这是一个很大的h1,大到你无法想象</h1>
  1. 数组中嵌套对象
       <!-- 在数组中使用对象来代替三元表达式,提高代码的可读性 -->
       <h1 :class="['red','thin',{'active':flag}]">这是一个很大的h1,大到你无法想象</h1>
  1. 直接使用对象
        <!-- 在为class使用v-bind绑定对象的时候,对象的属性是类名,由于对象的属性可带引号,也可不带引号,所以这里没写引号 : 属性的值,是一个标识符 -->
       <h1 :class="{red:true,thin:true,active:false,italic:true}">这是一个很大的h1,大到你无法想象</h1>
       
使用内联样式
  1. 直接在元素上通过:style 的形式,书写样式对象
    <h1 :style="{color:'red','font-weight':200}">这是一个h1</h1>

  2. 将样式对象,定义到 data 中,并直接引用到 :style 中

    • 在data上定义样式
      data:{styleObj1:{color:'red','font-weight':200}}

    • 在元素上,通过属性绑定的形式,将样式对象应用到元素中
      <h1 :style="styleObj1">这是一个h1</h1>

  3. 在:style中通过数组,引用多个data上的样式对象

    • 在data上定义样式

      data:{
                        styleObj1:{color:'red','font-weight':200},
                        styleObj2:{'font-style':'italic'}
                    },
      
    • 在元素上,通过属性绑定的形式,将样式对象应用到元素中
      <h1 :style="[styleObj1,styleObj2]">这是一个h1</h1>

vue指令之v-for和key属性
  1. 迭代数组

    • 循环普通数组

      索引值:{{i}}----------每一项:{{item}}

      var vm = new Vue({
                  el:'#app',
                  data:{
                      list:[1,2,3,4,5,6]
                  },
                  methods:{}
              })
      
    • 循环对象数组

      ID:{{user.id}}-------Name:{{user.name}}-----索引:{{i}}

      data:{
                      list:[
                          {id:1,name:'zs1'},
                          {id:2,name:'zs2'},
                          {id:3,name:'zs3'},
                          {id:4,name:'zs4'},
                      ]
                  }
      
  2. 迭代对象中的属性

    • 循环对象

      值是:{{val}}----键是:{{key}}----索引是:{{i}}

       data: {
                      user: {
                          id: 1,
                          name: '托尼',
                          gender: '男'
                      }
                  }
      
  3. 迭代数字

     <!-- in后面我们放过  普通数组,对象数组,对象,还可以放数字 -->
               <!-- 注意:如果使用v-for迭代数字的话,前面的count值从1开始 -->
              <p v-for = "count in 10">这是第 {{count}} 次循环</p>

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

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

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

  • 例子:
             <div>
                    <label for="">
                        ID:
                        <input type="text" v-model="id">
                    </label>
                    <label for="">
                        Name:
                        <input type="text" v-model="name">
                    </label>
                    <label for="">
                        <input type="button" value="添加" @click = "add">
                    </label>
                </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" name="" id="">
                    {{item.id}}-------{{item.name}}
                </p>
        
        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})
                        }
                    }
                })
        
    
    
vue指令之v-if 和v-show

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

<!-- <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>
vue调试工具vue-devtools的安装步骤和使用

Vue.js devtools - 翻墙安装方式 - 推荐

另一种安装方式:打开谷歌浏览器设置—>扩展程序–》勾选开发者模式—》加载已解压的扩展程序—》选择“chrome扩展”文件夹,至此恭喜已经安装成功!!!

过滤器

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

私有过滤器
  1. HTML元素:

    <td>{{item.ctime | dataFormat('yyyy-mm-dd')}}</td>

  2. 私有 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}`;

  }

});

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

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

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

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

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

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

    Vue.config.keyCodes.f2 = 113;

  2. 使用自定义的按键修饰符:

    <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;
    
         }
    
       }
    
  2. 自定义指令的使用方式:

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

    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 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
vue-resource 实现 get, post, jsonp请求

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

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

  2. 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);
      });
    }
配置本地数据库和数据接口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">

  2. 定义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;
    }

vue组件

定义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)来进行包裹!

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

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

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

  1. 通过计数器案例演示
  2. 不能返回一个外部定义好的一个对象,因为对象是引入进来的,所以如果多次使用此组件,每次指向的都是同一个对象,会出现一个组件中的数据改变,其他地方引入此组件的数据也会改变,所以return{}必须要在里面写。
使用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>
    

使用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>
    

使用: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>
    
父组件向子组件传值
  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>
子组件向父组件传值
  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>
    

评论列表案例

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

使用 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改变来切换页面的方式,称作前端路由(区别于后端路由);
在 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 属性来使用路由规则
        });

设置路由高亮

设置路由切换动效

在路由规则中定义参数

  1. 在规则中定义参数:

    { path: '/register/:id', component: register }

  2. 通过 this.$route.params来获取路由中的参数:

    var register = Vue.extend({
          template: '<h1>注册组件 --- {{this.$route.params.id}}</h1>'
        });

使用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>

命名视图实现经典布局

  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>
    
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. 定义有getter和setter的计算属性:
    <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>
    
watch、computed和methods之间的对比
  1. computed属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。主要当作属性来使用;
  2. methods方法表示一个具体的操作,主要书写业务逻辑;
  3. watch一个对象,键是需要观察的表达式,值是对应回调函数。主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作;可以看作是computed和methods的结合体;

nrm的安装使用

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

  1. 运行npm i nrm -g全局安装nrm包;
  2. 使用nrm ls查看当前所有可用的镜像源地址以及当前所使用的镜像源地址;
  3. 使用nrm use npmnrm use taobao切换不同的镜像源地址;

注意:nrm只是单纯的提供了几个常用的下载包的URL地址,并能够让我们在这几个地址之间,很方便地进行切换,但是,我们每次装包的时候,使用的装包工具,都是npm

CCF大数据与计算智能大赛-面向电信行业存量用户的智能套餐个性化匹配模型联通赛-复赛第二名-【多分类,embedding】.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值