持续更新 – Vue2.5-2.6-3.0 开发去哪儿网App 从零入门到项目实战
本文最后更新于 830 天前,其中的信息可能已经有所发展或是发生改变。

断断续续学了好长好长时间了,这次又开始了,坚持学完?

官网文档:https://cn.vuejs.org/index.html

详细笔记:https://blog.xiaodongxier.com/mkw-vue-qnew-develop

Vue2.5-2.6-3.0开发去哪儿网App 零基础入门到实战项: https://coding.imooc.com/class/203.html

1.1 课程介绍

中文文档完善/代码轻量/上手简单/容易使用

NUXT 服务器渲染
weex vue语法编写原生app

  • 基础内容

    • 基础语法
    • MVVM模式
    • 前端组件化
    • 声明周期函数
    • 动画特效
  • 实战项目

    • 环境搭建
    • 使用git
    • 数据模拟
    • 本地开发
    • 前后端代码联挑
    • 真机测试
    • 上线

实战项目介绍

多区域轮博/多区域展示/城市展示/城市搜索/右侧字母与左侧联动…..
公用的画廊组件/递归展示的列表组件…

使用的内容

  • Axios 数据获取
  • Vuex
  • Vue Router
  • 异步组件
  • Stylus
  • 递归组件
  • 插件 swiper…
  • 公用组件

课程安排

  • 第1章 课程介绍
  • 第2章 Vue初探
  • 第3-5章 基础知识精讲
  • 第6-9章 Vue项目实战
  • 第10章 项目测试上线流程几后续学习指南

学习前提

  • js
  • es6
  • webpack
  • npm

收获

  • 入门Vue的使用
  • 理解vue开发流程
  • 移动端布局技巧
  • 上手公司中型/大型项目
  • 规范代码编写

本章将快速讲解部分 Vue 基础语法,通过 TodoList 功能的编写,在熟悉基础语法的基础上,扩展解析 MVVM 模式及前端组件化的概念及优势。

2-1 课程学习方法

官网文档:https://cn.vuejs.org/index.html

最好的学习资料就是官方文档

视频+官方文档 进行学习

2-2 hello world

  • 开发版 – 代码提示 代码报错
  • 生产版

Vue 不支持 IE8 及以下版本,因为 Vue 使用了 IE8 无法模拟的 ECMAScript 5 特性。但它支持所有兼容 ECMAScript 5 的浏览器。

2-3 开发TodoList(v-model、v-for、v-on)

标签指令

  • v-for
  • v-on
  • v-model

MVVM 不关心DOM,只关注数据,数据发生改变,DOM结构就发生改变

2-4 MVVM模式

MVP设计模式

MVP设计模式

面向DOM开发

大多时间在操作DOM结构

<div>
  <input type="text" id="input">
  <button id="btn">提交</button>
  <ul id="list">

  </ul>
</div>
<script>
  // 构造函数
  function Page() {}
  $.extend(Page.prototype, {
    init: function () {
      this.bindEvents()
    },
    bindEvents: function () {
      var btn = $("#btn");
      btn.on("click", $.proxy(this.handleBtnClick, this))
    },
    handleBtnClick: function () {
      var inputElem = $("#input");
      var inputVal = inputElem.val();
      var eulElem = $("#list");
      if (inputVal == '') {
        alert("不能为空")
      } else {
        eulElem.append('<li>' + inputVal + '</li>')
        inputElem.val('')
      }
    }
  })
  var page = new Page();
  page.init()
</script>

MVVM设计模式

MVVM设计模式

vue 就是 vm 层

面向数据开发

只需要修改M层数据,无需操作DOM

2-5 【讨论题】你所知道的常用设计模式有哪些?

2-6 前端组件化

组件:页面的一个部分,由原来的一个整体,切成一个一个的部分,每个部分可以称为一个组件

  • 顶部轮播
  • 搜索框
  • 等等

都可以称之为一个组件

合理的拆分组件,可以把一个大型的项目像拼积木一样拼接起来

一个项目业务逻辑可能会很复杂,拆分成组件以后,每一个组件就会变得很轻巧,后期维护起来也会更加容易

每一个组件就是页面上的一个区域

2-7 使用组件改造TodoList

  • 创建全局组件
Vue.component() 
TodoItem == todo-item
<TodoItem></TodoItem>
等于
<todo-item></todo-item>

全局组件

 <!DOCTYPE html>
 <html lang="en">
 <head>
   <meta charset="UTF-8">
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>全局组件</title>
   <script src="./static/vue.js"></script>
 </head>
 <body>
   <div id="root">
     <div>
       <input type="text" v-model="todoVal">
       <button @click="handleBtnClick">提交</button>
     </div>
     <ul>
       <!-- <li v-for="item in list">
         {{item}}
       </li> -->
       <todo-item v-bind:content="item" 
                  v-for="item in list">
       </todo-item>
     </ul>
   </div>
   <script>
    Vue.component("TodoItem",{
      props: ['content'],
      template: "<li>{{content}}</li>"
    })
     new Vue({
       el:"#root",
       data: {
         todoVal:"",
         list: []
       },
       methods: {
         handleBtnClick: function(){
           this.list.push(this.todoVal)
           this.todoVal = ''
         }
       },
     })
   </script>
 </body>
 </html>

局部组件

 <!DOCTYPE html>
 <html lang="en">
 <head>
   <meta charset="UTF-8">
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>局部组件</title>
   <script src="./static/vue.js"></script>
 </head>
 <body>
   <div id="root">
     <div>
       <input type="text" v-model="todoVal">
       <button @click="handleBtnClick">提交</button>
     </div>
     <ul>
       <todo-item v-bind:content="item" 
                  v-for="item in list">
       </todo-item>
     </ul>
   </div>
   <script>
    var TodoItem = {
      props: ['content'],
      template: "<li>{{content}}</li>"
    }
     new Vue({
       el:"#root",
       data: {
         todoVal:"",
         list: []
       },
       methods: {
         handleBtnClick: function(){
           this.list.push(this.todoVal)
           this.todoVal = ''
         }
       },
       components: {
        TodoItem:TodoItem
       }
     })
   </script>
 </body>
 </html>

关于keys的问题暂时记录下,后期补充

控制台关于keys的提示问题

2-8 简单的组件间传值

父向子传值

通过v-bind传值,通过props接收该值

子向父传值

添加todolist删除的功能

通过$emit()向外触发事件,父组件监听事件,监听的时候获取子组件带回的内容,实现子组件向父组件传值

v-bind简写 :

<todo-item v-bind:content="item" 
          v-bind:index="index"
          v-for="(item,index) in list"  // item 数组的内容;index数组的下标
          @delete="handelItemDelete"
          >
</todo-item>
 <!DOCTYPE html>
 <html lang="en">
 <head>
   <meta charset="UTF-8">
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>组件传值</title>
   <script src="./static/vue.js"></script>
 </head>
 <body>
   <div id="root">
     <div>
       <input type="text" v-model="todoVal">
       <button @click="handleBtnClick">提交</button>
     </div>
     <ul>
       <todo-item v-bind:content="item" 
                  v-bind:index="index"
                  v-for="(item,index) in list"
                  @delete="handelItemDelete"
                  >
       </todo-item>
     </ul>
   </div>
   <script>
    var TodoItem = {
      props: ['content','index'], //  父传子接收
      template: '<li @click="handleItemClick">{{content}}</li>',
      methods: {
        handleItemClick: function(){
          // 向外触发一个事件,通过v-on监听事件
          this.$emit("delete",this.index)
        }
      }
    }
     new Vue({
       el:"#root",
       data: {
         todoVal:"",
         list: []
       },
       methods: {
         handleBtnClick: function(){
           this.list.push(this.todoVal)
           this.todoVal = ''
         },
         handelItemDelete: function(index){
            console.log(index)
            this.list.splice(index,1)
         }
       },
       components: {
        TodoItem:TodoItem
       }
     })
   </script>
 </body>
 </html>

2-9 章节小结

  • 通过script标签引入vuejs进行基础代码编写
  • 数据的双向绑定
  • 父子组件传值

阅读官网介绍部分内容:https://v2.cn.vuejs.org/v2/guide/index.html

个人总结

  • 不支持ie8及以下版本,因为使用了 ECMAScript 5 特性,ie8及以下版本无法模拟这些特性

  • vuejs介绍

  • 声明式渲染

    • v-bind
  • 条件与循环

    • v-if ,dom节点不存在
    • v-show ,dom节点存在,display:none / block
    • v-for ,数据循环
  • 处理用户输入

    • v-on ,事件监听器
    • v-model ,双向数据绑定
  • 组件化应用构建

    • 全局组件
    • 局部组件
    • 组件数据传递:父组件-> 子组件 (props)
    • 组件数据传递:子组件-> 父组件 (v-bind,v-on)

本章通过精挑细选的案例,精讲 Vue 中的基础知识,包括实例、生命周期、指令、计算属性、方法、侦听器,表单等部分内容。

3-1 Vue实例

创建组件的时候,vue底层也是把他编译成vue的一个实例

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Vue实例</title>
  <script src="./static/vue.js"></script>
</head>
<body>
  <div id="root">
    <div v-on:click="handleBtnClick">
      {{mess}}
    </div>
    <item></item>
  </div>
  <script>
    Vue.component("item",{
      template: "<div>hello item</div>"
    })
    var vm = new Vue({
      el: "#root",
      data: {
        mess:"hello world"
      },
      methods: {
        handleBtnClick: function(){
          alert(this.mess)
        }
      },
    })
  </script>
</body>
</html>
  • vm 一般指一个 vue 的实例
  • el 接管的标签
  • data 存放的数据
  • {{mess}} 插值表达式
  • 事件必须定义在 methods 里面
  • v-on 简写 @
  • 除了eldatamethods方法, 还有其他的,比如 propswatch
  • 一个vue项目是有很多个小的组件(实例)组成的
  • vm.$ 凡是 $ 开头的一般指 vue 的实例属性(方法),例如 vm.$data,vm.$el
  • vm.$destroy 销毁实例,销毁后数据发生变化的时候,页面不会跟着变,但是 之前的事件还是存在的

3-2 Vue实例生命周期

生命周期函数,就是vue实例在 某一个时间 点自动执行的函数
不行药手动的去调用,vue的内部会在某个时刻自动的去执行这些函数

生命周期

生命周期中文对照

  • beforeCreate: 无法通过实例访问data数据和methods方法
  • created: 可以通过实例访问data数据和methods方法
  • beforeMount: hello world 并未渲染到页面上
  • mounted: hellow world 已经渲染到页面上
  • beforeDestroy: 实例销毁之前执行
  • destroyed: 实例销毁之后执行
  • beforeUpdate: 数据发生改变,还未渲染之前
  • updated: 数据发生改变,重新渲染之后
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>生命周期</title>
  <!-- <script src="./static/vue.js"></script> -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
</head>
<body>
  <div id="app">
    <h1>通过控制台查看生命周期函数的执行过程</h1>
      <div id="mydomid">
        {{number}}
      </div>
      <input type="text" v-model="number"><hr>
      <button @click="upDataClick">更新数据</button>
      <button @click="delVueClick">销毁实例</button>
  </div>
  <script>
    var vm = new Vue({
      el: "#app",
      data: {
        mess:"hello world",
        number: 0
      },
      methods: {
        upDataClick: function(){
          this.number ++
        },
        delVueClick: function(){
          this.$destroy()
        }
      },
      beforeCreate: function(){   //无法通过实例访问data数据和methods方法
        console.table([{
          生命周期: "beforeCreate",
          解释说明: "无法通过实例访问data数据和methods方法",
          时间戳: new Date().getTime(),
          data数据: this.number,
          渲染数据: document.getElementById("mydomid").innerText,
          挂载节点:this.$el
        }])
      },  
      created: function(){    //可以通过实例访问data数据和methods方法
        console.table([{
          生命周期: "created",
          解释说明: "可以通过实例访问data数据和methods方法",
          时间戳: new Date().getTime(),
          data数据: this.number,
          渲染数据: document.getElementById("mydomid").innerText,
          挂载节点:this.$el
        }])
      },
      beforeMount: function(){  // data数据并未渲染到页面上
        console.table([{
          生命周期: "beforeMount",
          解释说明: "data数据未渲染到页面上",
          时间戳: new Date().getTime(),
          data数据: this.number,
          渲染数据: document.getElementById("mydomid").innerText,
          挂载节点:this.$el
        }])
      },
      mounted: function(){    //  data数据已经渲染到页面上
        console.table([{
          生命周期: "mounted",
          解释说明: "data数据已渲染到页面上",
          时间戳: new Date().getTime(),
          data数据: this.number,
          渲染数据: document.getElementById("mydomid").innerText,
          挂载节点:this.$el
        }])
      },
      beforeDestroy: function(){  // 实例销毁之前执行
        console.table([{
          生命周期: "beforeDestroy",
          解释说明: "实例销毁之前执行",
          时间戳: new Date().getTime(),
          data数据: this.number,
          渲染数据: document.getElementById("mydomid").innerText,
          挂载节点:this.$el
        }])
      },
      destroyed: function(){  // 实例销毁之后执行
        console.table([{
          生命周期: "destroyed",
          解释说明: "实例销毁之后执行",
          时间戳: new Date().getTime(),
          data数据: this.number,
          渲染数据: document.getElementById("mydomid").innerText,
          挂载节点: this.$el
        }])
      },
      beforeUpdate: function(){    // 数据发生改变,还未渲染之前
        console.table([{
          生命周期: "beforeUpdate",
          解释说明: "数据发生改变,还未渲染之前",
          时间戳: new Date().getTime(),
          data数据: this.number,
          渲染数据: document.getElementById("mydomid").innerText,
          挂载节点:this.$el
        }])
      },
      updated: function(){    // 数据发生改变,重新渲染之后
        console.table([{
          生命周期: "updated",
          解释说明: "数据发生改变,重新渲染之后",
          时间戳: new Date().getTime(),
          data数据: this.number,
          渲染数据: document.getElementById("mydomid").innerText,
          挂载节点:this.$el
        }])
      },
    })
  </script>
</body>
</html>

生命周期函数代码演示地址

生命周期函数执行过程

生命周期函数不是放在 methods 对象里,而是单独的放在 vue 的实例里面

vue 一共有11个生命周期函数,上面只讲解了8个生命周期函数,另外3个( activated , deactivated ,errorCaptured)参考官方文档API 选项 / 生命周期钩子

课下阅读官方文档 Vue实例 章节内容

不要在选项 property 或回调上使用箭头函数,比如 created: () => console.log(this.a)vm.$watch('a', newValue => this.myMethod())。因为箭头函数并没有 thisthis 会作为变量一直向上级词法作用域查找,直至找到为止,经常导致 Uncaught TypeError: Cannot read property of undefinedUncaught TypeError: this.myMethod is not a function 之类的错误。

3-3 Vue的模版语法

插值表达式 {{ 开始,}} 结束

v-***="value"value 指的不在是某个值,而是 js 变量 或者 表达式

v-text="" 等于 插值表达式

v-html="" 类似插值表达式, 但是v-html="" 能解析标签内容,而插值表达式和 v-text 是不可以的

<div id="app">
    <div v-text="mess + ' 表达式拼接'"></div>
    <div v-html="mess + ' 表达式拼接'"></div>
    <div v-html="messhtml + ' 表达式拼接'"></div>
      {{mess + ' 表达式拼接'}}
</div>
<script>
  var vm = new Vue({
    el: "#app",
    data: {
      mess:"hello world",
      messhtml:"<h1>hello world</h1>",
    }
  })
</script>

3-4 计算属性,方法与侦听器

计算属性 computed

计算属性存在 缓冲机制

  • 当计算的内容没有发生变化时,页面重新渲染的时候不会重新进行计算渲染该元素
  • 当计算的内容发生变化时,页面重新渲染的时候会重新进行计算渲染该元素
<div id="app">
  {{fullName}}
  {{age}}
  <hr>
  <button @click="count">计算属性缓冲机制,当计算的内容没有发生变化时,页面重新渲染的时候不会重新进行计算渲染该元素/弹窗只会在运行的时候弹出1次</button>
  <br><br>
  <button @click="upName">计算属性缓冲机制,当计算的内容发生变化时,页面重新渲染的时候会重新进行计算渲染该元素/弹窗会在运行的时候再次弹出1次</button>
</div>
<script>
  var app = new Vue({
    el: "#app",
    data: {
      firstName: "xiao",
      lastName: "dongxier",
      age: "28"
    },
    // 计算属性,内置缓存
    computed: {
      fullName: function(){
        alert("计算了1次")
        return this.firstName + " " + this.lastName
      }
    },
    methods: {
      count: function(){
        this.age ++
      },
      upName: function(){
        this.firstName += "xiao"
      }
    },
  })
</script>

方法 methods

虽然改变的是 age ,但是只要页面重新渲染都会再次执行计算一次

相同的效果 计算属性方法 都可以实现的情况下,计算属性 的性能更高

  • 当计算的内容没有发生变化时,页面重新渲染的时候也会重新进行计算渲染该元素,性能方面不如 计算属性
<div id="app">
  {{fullName()}}
  {{age}}
  <hr>
  <button @click="count">方法,当计算的内容没有发生变化时,页面重新渲染的时候会重新进行计算渲染该元素/弹窗会在每次改变的时候弹出1次</button>
</div>
<script>
  var app = new Vue({
    el: "#app",
    data: {
      firstName: "xiao",
      lastName: "dongxier",
      age: "28"
    },
    methods: {
      fullName: function(){
        alert("计算了1次")
        return this.firstName + " " + this.lastName;
      },
      count: function(){
        this.age ++
      }
    },
  })
</script>

侦听器 watch

存在 缓冲机制,与 计算属性 computed 类似,如果同一个需求两种方法都能实现,那么优先使用 计算属性 computed ,因为相比 侦听器 watch 更简洁,性能更高

<div id="app">
    {{fullName}} {{age}}
    <hr>
    <button @click="count">侦听器缓冲机制,当计算的内容没有发生变化时,页面重新渲染的时候不会重新进行计算渲染该元素/控制台只会在运行的时候打印1次</button>
    <br><br>
    <button @click="upName">侦听器缓冲机制,当计算的内容发生变化时,页面重新渲染的时候会重新进行计算渲染该元素/控制台会在运行的时候再打印1次</button>
</div>
<script>
    var app = new Vue({
        el: "#app",
        data: {
            firstName: "xiao",
            lastName: "dongxier",
            fullName: "xiao dongxier",
            age: "28"
        },
        watch: {
            firstName: function() {
                console.log("计算了1次")
                this.fullName = this.firstName + " " + this.lastName
            },
            lastName: function() {
                console.log("计算了1次")
                this.fullName = this.firstName + " " + this.lastName
            }
        },
        methods: {
            count: function() {
                this.age++
            },
            upName: function() {
                this.firstName += "xiao"
            }
        }
    })
</script>

课下阅读官方文档 计算属性和侦听器 章节内容

  • 模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。

3-5 计算属性的 getter 和 setter

计算属性 computed 上的 get 方法通过其他值算一个新值,还可以通过 set 方法 通过设置一个值,来改变相关联的值,改变相关联值的同时又会引起计算属性的相关计算,重新渲染页面上的内容

<div id="app">{{fullName}}</div>
<script>
var app = new Vue({
  el: "#app",
  data: {
    firstName: "xiao",
    lastName: "dongxier"
  },
  computed: {
    fullName: {
      get: function(){
        return this.firstName + " " + this.lastName
      },
      set: function(val){
        console.log(val)
        var arr = val.split(" ");
        this.firstName = arr[0]
        this.lastName = arr[1]
      }
    }
  }
})

课下阅读官方文档 [计算属性的 setter](https://v2.cn.vuejs.org/v2/guide/computed. <html#%E8%AE%A1%E7%AE%97%E5%B1%9E%E6%80%A7%E7%9A%84-setter) 章节内容/wangyongjie>

3-6 Vue中的样式绑定

DOM 绑定样式

class 对象绑定

<div id="app">
  <div @click="handleDivClick"
        :class="{actived: isActived}"
  >
    hello world
  </div>
</div>
<script>
  var app = new Vue({
    el: "#app",
    data: {
      isActived: false
    },
    methods: {
      handleDivClick: function(){
        this.isActived = !this.isActived
      }
    },
  })
</script>

class 数组绑定

动态的新增 class 或删除 class,实现页面效果的变更

<div id="app">
  <div @click="handleDivClick"
      :class="[activated, activatedOne]"
  >
    hello world
  </div>
</div>
<script>
  var app = new Vue({
    el: "#app",
    data: {
    activated: "",
    activatedOne: "activated-one"
    },
    methods: {
      handleDivClick: function(){
      // if(this.activated === "activated") {
      //   this.activated = ""
      // } else {
      //   this.activated = "activated"
      // }
      console.log(this.activated === "activated")
      this.activated = (this.activated === "activated") ? "" : "activated"
      }
    }
  })
</script>

style 改变样式 (内链)

对象方式

  <div id="app">
    <div @click="handleDivClick"
        :style="styleObj"
    >
      hello world
    </div>
  </div>
  <script>
    var app = new Vue({
      el: "#app",
      data: {
      styleObj: {
        color: '#000'
      }
      },
      methods: {
        handleDivClick: function(){
        this.styleObj.color = (this.styleObj.color === "#000") ? "red" : "#000"
        }
      }
    })
  </script>

数组方式

由数组里的对象所决定,数组内可以添加多个对象

<div id="app">
    <div @click="handleDivClick" :style="[styleObj, {fontSize: '30px'}]">
        hello world
    </div>
</div>
<script>
    var app = new Vue({
        el: "#app",
        data: {
            styleObj: {
                color: '#000'
            }
        },
        methods: {
            handleDivClick: function() {
                this.styleObj.color = (this.styleObj.color === "#000") ? "red" : "#000"
            }
        }
    })
</script>

3-7 Vue中的条件渲染

v-if

DOM节点移除或添加

v-ifv-else-ifv-else 同时使用的时候要紧挨着使用,否则会报错

<div v-if="show === 'a'">This is A</div>
<div v-else-if="show === 'b'">This is B</div>
<div v-else>This is others</div>

v-show

DOm节点一直存在,display:none/blcok

  <div id="app">
    <!-- 不存在dom节点/移除节点 -->
    <div v-if="show === 'a'">This is A</div>
    <div v-else-if="show === 'b'">This is B</div>
    <div v-else>This is others</div>
    <!-- 存在dom节点/display:none -->
    <div v-show="show" >{{mess}}</div>
  </div>
  <script>
    var app = new Vue({
      el: "#app",
      data: {
        show: "a",
        mess: "hello world"
      }
    })
  </script>

  • 频繁的显示或者隐藏情况下 v-show 性能优于 v-if

key

Vue 重新渲染页面时,会优先复用页面上已经存在的 DOM (内容不清空,如果输入了内容切换的时候,input 中的内容还是存在),例子如下

<div id="app">
  <div v-if="show">
    用户名:<input type="text">
  </div>
  <div v-else>
    密码:<input type="text">
  </div>
  <button @click="btnClick">切换</button>
</div>
<script>
  var app = new Vue({
    el: "#app",
    data: {
      show: true,
    },
    methods: {
    btnClick: function() {
      this.show = !this.show
    }
    },
  })
</script>

通过添加 key 可以解决上面存在的问题

当给元素添加 key 的时候,Vue 认为是页面上唯一的一个元素,如果两个元素的 key 不一样,Vue 就不会尝试去复用该元素,就可以避免上面的情况发生

<div id="app">
  <div v-if="show">
    用户名:<input type="text" key="username">
  </div>
  <div v-else>
    密码:<input type="text" key="password">
  </div>
  <button @click="btnClick">切换</button>
</div>
<script>
  var app = new Vue({
    el: "#app",
    data: {
      show: true,
    },
    methods: {
    btnClick: function() {
      this.show = !this.show
    }
    },
  })
</script>

课下阅读官方文档 条件渲染 章节内容

  • 因为 v-if 是一个指令,所以必须将它添加到一个元素上。但是如果想切换多个元素呢?此时可以把一个 <template> 元素当做不可见的包裹元素,并在上面使用 v-if。最终的渲染结果将不包含 <template> 元素。
<template v-if="ok">
  <h1>Title</h1>
  <p>Paragraph 1</p>
  <p>Paragraph 2</p>
</template>
  • v-else 元素必须紧跟在带 v-if 或者 v-else-if 的元素的后面,否则它将不会被识别。
  • 类似于 v-elsev-else-if 也必须紧跟在带 v-if 或者 v-else-if 的元素之后。
  • Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这么做除了使 Vue 变得非常快之外,还有其它一些好处。例如,如果你允许用户在不同的登录方式之间切换。<input> 不会被替换掉——仅仅是替换了它的 placeholder

注意,v-show 不支持 <template> 元素,也不支持 v-else

v-if vs v-show

  • v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
  • v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
  • 相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
  • 一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。

v-if 与 v-for 一起使用

不推荐同时使用 v-ifv-for。请查阅风格指南以获取更多信息。

v-ifv-for 一起使用时,v-for 具有比 v-if 更高的优先级。请查阅列表渲染指南以获取详细信息。

3-8 Vue中的列表渲染

key

key 值唯一,最好不要用 index 下标作为 key ,正式项目一般后端会返回前端一个唯一的值

<div id="app">
  <ul>
    <li v-for="(item,index) of list"
        :key="item.id">
      {{item.name}} --- {{index}}
    </li>
  </ul>
</div>
<script>
  var app = new Vue({
    el: "#app",
    data: {
      list: [
        {
          name: "xiao",
          id: "00001"
        },
        {
          name: "dong",
          id: "00002"
        },
        {
          name: "xi",
          id: "00003"
        },
        {
          name: "er",
          id: "00004"
        }
      ]
    }
  })
</script>

数组的循环

push / pop / shift / unshift / splice – 数组的截取/ sort – 对数组进行排序 / reverse -对数组进行取反

修改数组内容的时候, 不能通过下标的方式进行修改,这样操作打印 data 数据的时候,数据虽然添加成功了,但是不会渲染到页面上,只能通过 Vue 提供的几个变异方法来操作数组才能数据发生变化

数据改变的方法
  • slice("截取开始位置下标", "删除条数", "增加的内容") 或者 以上的其他的方法
  • 改变数组的引用
app.list = [
    {
      name: "xiao",
      id: "00001"
    },
    {
      name: "dong",
      id: "00002"
    },
    {
      name: "xi",
      id: "00003"
    },
    {
      name: "er",
      id: "00004"
    }
];
模版占位符 template

template 可以包裹一些元素,但在循环的过程中并不会渲染到页面上

<div id="app">
  <template v-for="(item,index) of list"
      :key="item.id">
    <div>
      {{item.name}} --- {{index}}
    </div>
    <span>
    {{item.name}} 
    </span>
  </template>
</div>
<script>
  var app = new Vue({
    el: "#app",
    data: {
      list: [
        {
          name: "xiao",
          id: "00001"
        },
        {
          name: "dong",
          id: "00002"
        },
        {
          name: "xi",
          id: "00003"
        },
        {
          name: "er",
          id: "00004"
        }
      ]
    }
  })
</script>

对象的循环

key : 键名 ; index :当前项的索引,类似数组的下标

<div id="app">
  <!-- key: 键值  index: 当前项的索引 -->
  <div v-for="(item,key,index) of list">
    {{item}}---{{key}} ---{{index}}
  </div>
</div>
<script>
  var app = new Vue({
    el: "#app",
    data: {
      list: {
        name: "xiaodonxgier",
        age: "18",
        gender: "male",
        salary: "secret"
      }
    }
  })

数据改变的方法

直接加值是无法渲然到页面上的

  • 改变引用
app.list = {
  name: "xiaodongxier",
  age: "18",
  gender: "male",
  salary: "secret",
  address: "Beijing"
}

课下阅读官方文档 列表渲染 章节内容

  • 你也可以用 of 替代 in 作为分隔符,因为它更接近 JavaScript 迭代器的语法:
<div v-for="item in items"></div>
<div v-for="item of items"></div>

在遍历对象时,会按 Object.keys() 的结果遍历,但是不能保证它的结果在不同的 JavaScript 引擎下都一致。

不要使用对象或数组之类的非基本类型值作为 v-forkey。请用字符串或数值类型的值。

  • 由于 JavaScript 的限制,Vue 不能检测数组和对象的变化。深入响应式原理中有相关的讨论。
  • v-for 也可以接受整数。在这种情况下,它会把模板重复对应次数。
<div>
  <span v-for="n in 10">{{ n }} </span>
</div>

输出结果

1 2 3 4 5 6 7 8 9 10
  • 2.2.0+ 的版本里,当在组件上使用 v-for 时,key 现在是必须的。

3-9 Vue中的set方法

即使 Vue 全局的一个方法,也是 Vue 实例上的方法

对象的 set 方法

<div id="app">
  <!-- key: 键值  index: 对象的位置 -->
  <div v-for="(item,key,index) of list">
    {{item}}---{{key}} ---{{index}}
  </div>
  <span v-for="n in 10">{{ n }} </span>
</div>
<script>
  var app = new Vue({
    el: "#app",
    data: {
      list: {
        name: "xiaodonxgier",
        age: "18",
        gender: "male",
        salary: "secret"
      }
    }
  })
</script>

Vue.set("实例数据", "新增的属性键", "新增的数据值")

通过 set 方法往上面案例添加数据的写法为:

全局方法:

Vue.set(app.list, "address", "Beijing")

实例方法:

app.$set(app.list, "address", "Beijing")

数组的 set 方法

<div id="app">
  <!-- key: 键值  index: 对象的位置 -->
  <div v-for="(item,key,index) of list">
    {{item}}---{{key}} ---{{index}}
  </div>
  <span v-for="n in 10">{{ n }} </span>
</div>
<script>
  var app = new Vue({
    el: "#app",
    data: {
        list: [1,2,3,4,5,6,7,8]
    }
  })
</script>

Vue.set("实例数据", "需要改变的数组下标", "更新的内容")

通过 set 方法向上面案例中的数据 2 变成 22 的方法为:

全局方法:

Vue.set(app.list, 1, 22)

实例方法:

app.$set(app.list, 1, 22)

课下阅读官方文档 Vue.set( target, propertyName/index, value ) 章节内容

3-10 (新)Vue中的事件绑定

<div id="app">
    <button @click="handleBtnClcik">Button</button>
    <button @click="handleBtnClcik()">Button()</button>
    <button @click="handleBtnClcik($event)">Button($event)</button>
    <button @click="handleBtnClcik($event,1,2,3)">Button($event)优势/可以传参</button>
</div>
<script>
  var app = new Vue({
    el: "#app",
    data: {
      list: [1,2,3,4,5,6,7,8]
    },
    methods: {
    handleBtnClcik:function(e,one,teo,three){
      console.log(e,one,teo,three)
    }
    },
  })
</script>

打印结果

事件修饰符

prevent

阻止跳转

stop

阻止向外冒泡

self

self要求,click事件只有在e.target = e.currenTaget 的时候才会执行/触发事件和绑定事件的元素相等的时候才会执行

once

once修饰符/事件执行一次之后自动解绑/事件只执行一次

capture

加了Capture修饰符/默认事件冒泡(内->外)被改变未捕获(外->内)/先执行父事件在执行子事件

<div id="app">
  <form action="/abc">
    <input type="submit">
    <span>未添加事件</span>
  </form>
  <form action="/abc" @click="handleBtnClcik">
    <input type="submit">
    <span>添加了阻止事件</span>
  </form>
  <form action="/abc" @click.prevent>
    <input type="submit">
    <span>添加了事件修复</span>
  </form>
  <form action="/abc" @click.prevent="handleBtnClcik">
    <input type="submit">
    <span>添加了事件修复/后面也可以添加其他事件不影响</span>
  </form>
  <form action="/abc" @click.stop>
    <input type="submit">
    <span>stop修饰符/阻止向外冒泡</span>
  </form>
  <form action="/abc" @click.self>
    <div @click="handleSelfClick">
      父元素
      <div>
        子元素
      </div>
    </div>
    <span>未加self修饰符/点击父元素和子元素都能触发事件</span>
  </form>
  <form action="/abc" @click.self>
    <div @click.self="handleSelfClick">
      父元素
      <div>
        子元素
      </div>
    </div>
    <span>加了self修饰符/只有点击父元素才都能触发事件,点击子元素是不会触发的</span>
    <br>
    <span>self要求,click事件只有在e.target = e.currenTaget 的时候才会执行/触发事件和绑定事件的元素相等的时候才会执行</span>
  </form>
  <form action="/abc" @click.self>
    <div @click.once="handleOnceClick">
      <div>点我触发事件</div>
    </div>
    <span>once修饰符/事件执行一次之后自动解绑/事件只执行一次</span>
  </form>
  <form action="/abc" @click.self>
    <div @click="handleCaptureClickA">
      <div @click="handleCaptureClickB">点我触发事件</div>
    </div>
    <span>未加Capture修饰符/默认事件冒泡(内->外)/先执行子事件在执行父事件</span>
  </form>
  <form action="/abc" @click.self>
    <div @click.capture="handleCaptureClickA">
      <div @click.capture="handleCaptureClickB">点我触发事件</div>
    </div>
    <span>加了Capture修饰符/默认事件冒泡(内->外)被改变未捕获(外->内)/先执行父事件在执行子事件</span>
  </form>
</div>
<script>
  var app = new Vue({
    el: "#app",
    methods: {
      handleBtnClcik: function (e,) {
        e.preventDefault();
      },
      handleSelfClick: function(){
        alert("handleSelfClick")
      },
      handleOnceClick: function(){
        alert("handleOnceClick")
      },
      handleCaptureClickA: function(){
        alert("父元素事件/外")
      },
      handleCaptureClickB: function(){
        alert("子元素事件/内")
      }
    },
  })
</script>

课下阅读官方文档 事件修饰符 章节内容

按键修饰符

enter/tab/delete/esc/shift/alt/….

当摁下哪个键的时候执行

<div id="app">
  <input type="text" @keydown="handleKeyDown">:输入的实时弹出内容
  <br><br>
  <input type="text" @keydown.enter="handleKeyDown">:回车/enter 的时候弹出结果
  <br><br>
  <input type="text" @keydown.delete="handleKeyDown">:delete的时候弹出结果
  <br><br>
  <input type="text" @keydown.esc="handleKeyDown">:esc的时候弹出结果
  <br><br>
  <input type="text" @keydown.shift="handleKeyDown">:shift的时候弹出结果
  <br><br>
  <input type="text" @keydown.alt="handleKeyDown">:alt的时候弹出结果
  <br><br>
  <input type="text" @keydown.tab="handleKeyDown">:tab的时候弹出结果
</div>
<script>
  var app = new Vue({
    el: "#app",
    methods: {
      handleKeyDown: function (e) {
        // e.target 指的是 input dom框这个节点,通过原生value可以把输入框的内容弹出来
        alert(e.target.value)
      }
    },
  })
</script>

课下阅读官方文档 按键修饰符 章节内容

系统修饰符

ctrl/alt/shift/meta

需要同时摁住才能执行

课下阅读官方文档 系统修饰键 章节内容

鼠标修饰符

right/left/middle

课下阅读官方文档 鼠标修饰键 章节内容

  • 有一些按键 (.esc 以及所有的方向键) 在 IE9 中有不同的 key 值, 如果你想支持 IE9,这些内置的别名应该是首选。
  • 注意:在 Mac 系统键盘上,meta 对应 command 键 (⌘)。在 Windows 系统键盘 meta 对应 Windows 徽标键 (⊞)。在 Sun 操作系统键盘上,meta 对应实心宝石键 (◆)。在其他特定键盘上,尤其在 MIT 和 Lisp 机器的键盘、以及其后继产品,比如 Knight 键盘、space-cadet 键盘,meta 被标记为“META”。在 Symbolics 键盘上,meta 被标记为“META”或者“Meta”。
  • 请注意修饰键与常规按键不同,在和 keyup 事件一起用时,事件触发时修饰键必须处于按下状态。换句话说,只有在按住 ctrl 的情况下释放其它按键,才能触发 keyup.ctrl。而单单释放 ctrl 也不会触发事件。如果你想要这样的行为,请为 ctrl 换用 keyCodekeyup.17

3-11 (新)Vue中的表单绑定

<div id="app">
<div @click.right="handleRightClick">handleRightClick</div>
<div @click.left="handleLeftClick">handleLeftClick</div>
<div @click.middle="handleMiddleClick">handleMiddleClick</div>
</div>
<script>
var app = new Vue({
  el: "#app",
  methods: {
    handleRightClick: function () {
      console.log("handleRightClick")
    },
    handleLeftClick: function () {
      console.log("handleRightClick")
    },
    handleMiddleClick: function () {
      console.log("handleRightClick")
    }
  },
})

3-12 (新)章节小节

  • 实例
  • 模版语法
    • v-if
    • v-text
    • v-html
    • v-for
    • v-if
    • v-else
    • v-else-if
    • v-show
    • v-bind
    • v-on
  • 计算属性
    • 避免代码冗余
  • 样式
    • class
    • style
    • 对象绑定
    • 数组绑定
  • 条件渲染
    • v-if
    • v-else
    • v-else-if
    • template 占位符
  • 列表渲染
    • 数组
      • value值
      • index值(索引值)
    • 对象
      • value值
      • key值(键值)
      • index值(索引值)
  • 事件处理
    • 事件绑定
    • 事件修饰符

  • 表单绑定
    • v-model
      • textarea
      • input
      • option

3-13 【讨论题】你对前端中的面向对象有怎样的了解?

什么是面向对象

  • Object:一切皆对象
  • 对象并不是计算机领域凭空造出来的概念,它是顺着人类思维模式产生的一种抽象
  • 人的成长过程中先认识到对象,然后才有过程、值的概念【一个苹果(实例)可以吃——所有苹果(类)可以吃——3(值)个苹果和3个梨的关系】
  • 所以面对对象编程是更接近人类思维的一种编程范式
  • 最成功的流派使用的方式来描述对象,如JAVA
  • JavaScript选择了较冷门的原型因为一些公司政治原因,JavaScript 推出之时受管理层之命被要求模仿 Java,JavaScript 创始人 Brendan Eich 在“原型运行时”的基础上引入了 new、this 等语言特性,使之“看起来更像 Java”
  • ES6之前,很多人试图将JavaScript变得更像基于类的语言,进而产生了很多所谓的“框架”,比如 PrototypeJS、Dojo事实上,它们成为了某种 JavaScript 的古怪方言,甚至产生了一系列互不相容的社群,显然这样做的收益是远远小于损失的
  • 从运行角度谈论对象:任何代码执行都必定绕不开运行时的对象模型【运行时类的概念都是被弱化的
  • JavaScript面向对象编程:
    • 命名空间是一个容器,它允许开发人员在一个独特的,特定于应用程序的名称下捆绑所有的功能。 JavaScript中,命名空间只是另一个包含方法、属性、对象的对象
    • 注意:需要认识到重要的一点是:与其他面向对象编程语言不同的是,Javascript中的普通对象和命名空间在语言层面上没有区别。这点可能会让JavaScript初学者感到迷惑。
    • 创造的JavaScript命名空间背后的想法很简单:一个全局对象被创建,所有的变量,方法和功能成为该对象的属性。使用命名空间也最大程度地减少应用程序的名称冲突的可能性。
    • JavaScript有包括在其核心的几个对象,例如,Math,Object,Array和String对象

JavaScript对象的特征

  1. 对象具有唯一标识性:即使是完全相同的两个对象,也并非是同一个对象
let o1 = {a: 1}
let o2 = {a: 1}
console.log(o1 == o2) //false
  1. 对象具有状态:同一对象可能处于不同的状态之下
  2. 对象具有行为:对象的状态可能因为它的行为而产生变迁
a) 状态和行为在不同的语言中使用不同的术语来描述
b) java中称为属性(状态)和方法(行为)
c) javascript中将状态和行为统一抽象为“属性”
d) javascript中函数是一种特殊的对象
let age = 20
let o  = {
    name: 'Lily',
    sayHi(){
        console.log('Hi')
    }
}
  1. 除了上述3个基本特征外,JavaScript的独特之处:高度的动态性,运行时改变对象的状态和行为(属性)的能力
  2. JavaScript的属性比别的语言更复杂,它提供了数据属性和访问属性(getter/setterJavaScript对象的两类属性

6.1Vue项目预热 – 环境配置

nodejs

https://nodejs.org/en/

LTS – 长期维护版
Current – 最新版

版本检测 node -v

创建vue项目

全局安装 vue-cli
$ npm install -g vue-cli

创建项目
$ vue init <template-name> <project-name>
$ vue init webpack mkw-vue-qnew-develop

项目初始化
$ vue init webpack my-project

项目初始化

6.2 (新)Vue项目预热 – 项目环境准备

慕课网源码:https://git.imooc.com/coding-203

卸载安装的新的版本 npm uninstall @vue/cli -g

淘宝镜像 https://developer.aliyun.com/mirror/NPM

安装

6.3 项目代码结构

  • README – 项目的说明文件
  • package – 第三方模块依赖包
  • package-lock – package的一个锁文件,确定安装第三方包的版本,保证团队编程的统一
  • LICENSE – 开源协议声明
  • index.html – 默认的项目首页模版文件
  • .postcssrc.js – post css配置项
  • .gitignore – git提交过滤,不想提交到仓库的文件可以进行配置
  • .eslintrc – 代码规范检测
  • .eslintignore – 文件不想被 eslintrc 进行检测可以配置到里面,写的不标准也不会进行提示
  • .editorconfig – 关于编辑器的一些配置,方便统一编程
  • .babelrc – 语法转换,转换成浏览器可以正常编译执行的代码
  • static – 静态资源
    • .gitkeep –
  • src – 整个项目的源代码
    • assets – 静态资源
    • components – 组件
    • router/index.js – 路由
    • App.vue – 最原始的根组建
    • main.js – 整个项目的入口文件
  • node_modules – 项目依赖的第三方包
  • config – 项目的配置文件
    • dev.env.js – 开发环境配置
    • index.js.js – 基础的配置信息
    • prod.env.js – 线上环境配置
  • build – webpack配置项/vue-cli自动构建的/无需修改
    • build.js –
    • check-versions.js –
    • utils.js –
    • vue-loader.conf.js –
    • webpack.base.conf.js – 基础的webpack配置项
    • webpack.dev.conf.js – 开发环境的webpack配置项
    • webpack.prod.conf.js – 线上环境的webpack配置项

6.4 单文件组件与Vue中的路由

components: { App }

es6的写法,同下面写法

components: { 
  App : App
} 

import App from ‘./App’

默认去找App.js/App.json/App.vue等文件

以.vue结尾的文件称之为单文件组件

单文件组件

路由

路由:根据网址的不同,返回不同的内容给用户

<router-view/>当前路由地址所对应的内容

注意

  1. style 后面要回车一行
  2. export defaultname的值要是单引号

6.4 单文件组件与Vue中的路由

多页面应用

页面跳转-》返回HTML

优点:首屏时间快/SEO优化号
缺点:页面切换慢

单页面应用

vue里一般不用a标签做页面跳转

template 里面像外暴露只能暴露一个跟标签

搜索引擎只认识html内容不认识js内容,所以seo差

页面跳转-》js渲染

优点:页面切换快
缺点:首屏时间稍慢,SEO差

后期会学到服务器端渲染等可内容以解决但页面应用目前存在的问题

6.5 项目代码初始化

viewport

viewport 的 meta 标签进行配置,移动端禁止手指缩放 ios safari浏览器目测不起作用

<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">

reset

引入reset.css文件import './assets/styles/reset.css',重置css默认样式进行浏览器之间样式的统一

@charset "utf-8";html{touch-action: manipulation;background-color:#fff;color:#000;font-size:12px}
body,ul,ol,dl,dd,h1,h2,h3,h4,h5,h6,figure,form,fieldset,legend,input,textarea,button,p,blockquote,th,td,pre,xmp{margin:0;padding:0}
body,input,textarea,button,select,pre,xmp,tt,code,kbd,samp{line-height:1.5;font-family:tahoma,arial,"Hiragino Sans GB",simsun,sans-serif}
h1,h2,h3,h4,h5,h6,small,big,input,textarea,button,select{font-size:100%}
h1,h2,h3,h4,h5,h6{font-family:tahoma,arial,"Hiragino Sans GB","微软雅黑",simsun,sans-serif}
h1,h2,h3,h4,h5,h6,b,strong{font-weight:normal}
address,cite,dfn,em,i,optgroup,var{font-style:normal}
table{border-collapse:collapse;border-spacing:0;text-align:left}
caption,th{text-align:inherit}
ul,ol,menu{list-style:none}
fieldset,img{border:0}
img,object,input,textarea,button,select{vertical-align:middle}
article,aside,footer,header,section,nav,figure,figcaption,hgroup,details,menu{display:block}
audio,canvas,video{display:inline-block;*display:inline;*zoom:1}
blockquote:before,blockquote:after,q:before,q:after{content:"\0020"}
textarea{overflow:auto;resize:vertical}
input,textarea,button,select,a{outline:0 none;border: none;}
button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}
mark{background-color:transparent}
a,ins,s,u,del{text-decoration:none}
sup,sub{vertical-align:baseline}
html {overflow-x: hidden;height: 100%;font-size: 50px;-webkit-tap-highlight-color: transparent;}
body {font-family: Arial, "Microsoft Yahei", "Helvetica Neue", Helvetica, sans-serif;color: #333;font-size: .28em;line-height: 1;-webkit-text-size-adjust: none;}
hr {height: .02rem;margin: .1rem 0;border: medium none;border-top: .02rem solid #cacaca;}
a {color: #25a4bb;text-decoration: none;}

border

引入border.css文件import './assets/styles/border.css',解决移动端1px问题

@charset "utf-8";
.border,
.border-top,
.border-right,
.border-bottom,
.border-left,
.border-topbottom,
.border-rightleft,
.border-topleft,
.border-rightbottom,
.border-topright,
.border-bottomleft {
    position: relative;
}
.border::before,
.border-top::before,
.border-right::before,
.border-bottom::before,
.border-left::before,
.border-topbottom::before,
.border-topbottom::after,
.border-rightleft::before,
.border-rightleft::after,
.border-topleft::before,
.border-topleft::after,
.border-rightbottom::before,
.border-rightbottom::after,
.border-topright::before,
.border-topright::after,
.border-bottomleft::before,
.border-bottomleft::after {
    content: "\0020";
    overflow: hidden;
    position: absolute;
}
/* border
 * 因,边框是由伪元素区域遮盖在父级
 * 故,子级若有交互,需要对子级设置
 * 定位 及 z轴
 */
.border::before {
    box-sizing: border-box;
    top: 0;
    left: 0;
    height: 100%;
    width: 100%;
    border: 1px solid #eaeaea;
    transform-origin: 0 0;
}
.border-top::before,
.border-bottom::before,
.border-topbottom::before,
.border-topbottom::after,
.border-topleft::before,
.border-rightbottom::after,
.border-topright::before,
.border-bottomleft::before {
    left: 0;
    width: 100%;
    height: 1px;
}
.border-right::before,
.border-left::before,
.border-rightleft::before,
.border-rightleft::after,
.border-topleft::after,
.border-rightbottom::before,
.border-topright::after,
.border-bottomleft::after {
    top: 0;
    width: 1px;
    height: 100%;
}
.border-top::before,
.border-topbottom::before,
.border-topleft::before,
.border-topright::before {
    border-top: 1px solid #eaeaea;
    transform-origin: 0 0;
}
.border-right::before,
.border-rightbottom::before,
.border-rightleft::before,
.border-topright::after {
    border-right: 1px solid #eaeaea;
    transform-origin: 100% 0;
}
.border-bottom::before,
.border-topbottom::after,
.border-rightbottom::after,
.border-bottomleft::before {
    border-bottom: 1px solid #eaeaea;
    transform-origin: 0 100%;
}
.border-left::before,
.border-topleft::after,
.border-rightleft::after,
.border-bottomleft::after {
    border-left: 1px solid #eaeaea;
    transform-origin: 0 0;
}
.border-top::before,
.border-topbottom::before,
.border-topleft::before,
.border-topright::before {
    top: 0;
}
.border-right::before,
.border-rightleft::after,
.border-rightbottom::before,
.border-topright::after {
    right: 0;
}
.border-bottom::before,
.border-topbottom::after,
.border-rightbottom::after,
.border-bottomleft::after {
    bottom: 0;
}
.border-left::before,
.border-rightleft::before,
.border-topleft::after,
.border-bottomleft::before {
    left: 0;
}
@media (max--moz-device-pixel-ratio: 1.49), (-webkit-max-device-pixel-ratio: 1.49), (max-device-pixel-ratio: 1.49), (max-resolution: 143dpi), (max-resolution: 1.49dppx) {
    /* 默认值,无需重置 */
}
@media (min--moz-device-pixel-ratio: 1.5) and (max--moz-device-pixel-ratio: 2.49), (-webkit-min-device-pixel-ratio: 1.5) and (-webkit-max-device-pixel-ratio: 2.49), (min-device-pixel-ratio: 1.5) and (max-device-pixel-ratio: 2.49), (min-resolution: 144dpi) and (max-resolution: 239dpi), (min-resolution: 1.5dppx) and (max-resolution: 2.49dppx) {
    .border::before {
        width: 200%;
        height: 200%;
        transform: scale(.5);
    }
    .border-top::before,
    .border-bottom::before,
    .border-topbottom::before,
    .border-topbottom::after,
    .border-topleft::before,
    .border-rightbottom::after,
    .border-topright::before,
    .border-bottomleft::before {
        transform: scaleY(.5);
    }
    .border-right::before,
    .border-left::before,
    .border-rightleft::before,
    .border-rightleft::after,
    .border-topleft::after,
    .border-rightbottom::before,
    .border-topright::after,
    .border-bottomleft::after {
        transform: scaleX(.5);
    }
}
@media (min--moz-device-pixel-ratio: 2.5), (-webkit-min-device-pixel-ratio: 2.5), (min-device-pixel-ratio: 2.5), (min-resolution: 240dpi), (min-resolution: 2.5dppx) {
    .border::before {
        width: 300%;
        height: 300%;
        transform: scale(.33333);
    }
    .border-top::before,
    .border-bottom::before,
    .border-topbottom::before,
    .border-topbottom::after,
    .border-topleft::before,
    .border-rightbottom::after,
    .border-topright::before,
    .border-bottomleft::before {
        transform: scaleY(.33333);
    }
    .border-right::before,
    .border-left::before,
    .border-rightleft::before,
    .border-rightleft::after,
    .border-topleft::after,
    .border-rightbottom::before,
    .border-topright::after,
    .border-bottomleft::after {
        transform: scaleX(.33333);
    }
}

fastclick

300ms click点击延迟的问题,安装fastclick sudo npm i fastclick --save 进行解决,安装到依赖之中,--save不管是开发环境,还是打包成线上版本的代码,都需要使用fastclick

安装存储在 package.json 中的 dependencies 里面

引入fastClick import fastClick from 'fastclick' ,使用fastClick fastClick.attach(document.body)

iconfont

https://www.iconfont.cn

创建项目

无用代码删除

代码提交线上

git add .     //本地修改提交到git缓存区
git commit -m "注释"      //本地缓冲区提交的本地仓库
git push      //本地代码提交到线上

7-1 Vue项目首页 – header区域开发

宽带750px
按照iPhone6的2倍图

stylus 安装

npm i stylus --save

https://www.stylus-lang.cn
CSS的预处理框架,即将stylus转换为css使用, 可以使用变量

stylus-loader 安装

npm i stylus-loader --save

让webpack理解stylus

header 位置开发

局部组建使用

组建下面声明局部组建

export default {
  name: 'Home',
  components: {
    HomeHeader: HomeHeader
  }
}

与下面相同

export default {
  name: 'Home',
  components: {
    HomeHeader
  }
}

home-header == HomeHeader

scoped css样式只对当前组建生效

1rem = html font-size = 50px
86/100 = .86rem

lang="stylus"报错

报错截图

报错

报错版本

报错版本

解决方法:https://segmentfault.com/q/1010000025218065 ,下面的评论

stylus-loader 版本太高 把版本降到3.0.2就解决了最后贴一个GitHub issues的地址https://github.com/vuejs/vue-cli/issues/5947

解决办法

卸载命令 npm uninstall stylus-loader

安装3.0.2 版本

安装指定版本命令 npm i stylus-loader@3.0.2 --save

其他解决方法

方法1

  1. 不卸载之前的高版本
  2. 运行命令 npm i stylus-loader@3.0.2 --save

方法2

  1. 先在package.json里修改好指定版本号,然后输入
  2. 运行命令 npm update stylus-loader@3.0.2

知识点

--save和--save-dev区别
一句话:--save-dev是你开发时依赖的东西,--save是发布后还依赖的东西.

注释事项

  1. 代码缩减问题
  2. 代码一行结束后面不能跟空格

header 位置开发

lang="stylus" css使用stylus语法

scoped css样式只对当前组建生效

iconfont的使用和代码优化

iconfont引入

import './assets/styles/iconfont.css'

css值放到一个变量里面 – 方便后期视觉主题维护 -提高可维护性

样式里面引入样式 ,css引用需要使用 @import

styl引用

@import '../../../assets/styles/varibles.styl';

@ 代表 src 这个目录,但是在css引用其他的css使用的时候需要前面加~

@import '~@/assets/styles/varibles.styl';

styles 文件夹路径使用比较高,起一个别名代替,类似上面的 @,使代码变得更加精简

找到build/webpack.base.conf.js这个文件(34行)

resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
    }
},

根据上面的写法可以为styles路径新增一个别名

resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
      'styles': resolve('src/assets/styles'),
    }
  },

Header.vue 中 ~styles使用

main.js中 styles使用

修改后会有报错提示

修改配置项报错

这是因为项目修改配置项,需要重启下服务器

标题:持续更新 – Vue2.5-2.6-3.0 开发去哪儿网App 从零入门到项目实战
地址:https://xiaodongxier.com/1602.html
作者:王永杰
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇