vue2.0

推荐大家安装的 VScode 中的 Vue 插件

  1. Vue 3 Snippets https://marketplace.visualstudio.com/items?itemName=hollowtree.vue-snippets

  2. Vetur https://marketplace.visualstudio.com/items?itemName=octref.vetur

    vue优势:MVVM 在svue中,程序员不需要操作DOM。只需要把数据维护好即可

    不建议在vue中安装jquery

day1

在项目中安装webpack

在终端运行如下的命令,

安装webpack 相关的两个包:npm install [email protected] [email protected]

安装webpack-dev-server

运行如下的命令,即可在项目中安装此插件:npm install [email protected]

安装babel-loader 相关的包

运行如下的命令安装对应的依赖包:npm i [email protected]@babel/[email protected]@babel/[email protected]

day2什么是 vue

  1. 构建用户界面
    • 用 vue 往 html 页面中填充数据,非常的方便
  2. 框架
    • 框架是一套现成的解决方案,程序员只能遵守框架的规范,去编写自己的业务功能!
    • 要学习 vue,就是在学习 vue 框架中规定的用法!
    • vue 的指令、组件(是对 UI 结构的复用)、路由、Vuex、vue 组件库
    • 只有把上面老师罗列的内容掌握以后,才有开发 vue 项目的能力!

vue 的两个特性

  1. 数据驱动视图:

    • 数据的变化会驱动视图自动更新
    • 好处:程序员只管把数据维护好,那么页面结构会被 vue 自动渲染出来!
  2. 双向数据绑定:

    在网页中,form 表单负责采集数据,Ajax 负责提交数据

    • js 数据的变化,会被自动渲染到页面上
    • 页面上表单采集的数据发生变化的时候,会被 vue 自动获取到,并更新到 js 数据中

注意:数据驱动视图和双向数据绑定的底层原理是 MVVM(Mode 数据源、View 视图、ViewModel 就是 vue 的实例)

vue 指令

1. 内容渲染指令

  1. v-text 指令的缺点:会覆盖元素内部原有的内容!
  2. {{ }} 插值表达式:在实际开发中用的最多,只是内容的占位符,不会覆盖原有的内容!
  3. v-html 指令的作用:可以把带有标签的字符串,渲染成真正的 HTML 内容!

2. 属性绑定指令

注意:插值表达式只能用在元素的内容节点中,不能用在元素的属性节点中!

  • 在 vue 中,可以使用 v-bind: 指令,为元素的属性动态绑定值;

  • 简写是英文的 :

  • 在使用 v-bind 属性绑定期间,如果绑定内容需要进行动态拼接,则字符串的外面应该包裹单引号,例如:

    <div :title="'box' + index">这是一个 divdiv>
    

3. 事件绑定

  1. v-on: 简写是 @

  2. 语法格式为:

    <button @click="add">button>
    
    methods: {
       add() {
    			// 如果在方法中要修改 data 中的数据,可以通过 this 访问到
    			this.count += 1
       }
    }
    
  3. $event 的应用场景:如果默认的事件对象 e 被覆盖了,则可以手动传递一个 $event。例如:

    <button @click="add(3, $event)">button>
    
    methods: {
       add(n, e) {
    			// 如果在方法中要修改 data 中的数据,可以通过 this 访问到
    			this.count += 1
       }
    }
    

    .prevent阻止默认行为(例如:阻止a 连接的跳转、阻止表单的提交等)

    .stop阻止事件冒泡.capture以捕获模式触发当前的事件处理函数.once绑定的事件只触发1次

    .self只有在event.target是当前元素自身时触发事件处理函数

  4. 事件修饰符:

    • .prevent

      <a @click.prevent="xxx">链接a>
      
    • .stop

      <button @click.stop="xxx">按钮button>
      

4. v-model 指令

  1. input 输入框
    • type=“radio”
    • type=“checkbox”
    • type=“xxxx”
  2. textarea
  3. select

.number自动将用户的输入值转为数值类型

<input v-model.number="age" />

.trim自动过滤用户输入的首尾空白字符

<input v-model.trim="msg" />

.lazy在“change”时而非“input”时更新

<input v-model.lazy="msg" />

5. 条件渲染指令

  1. v-show 的原理是:动态为元素添加或移除 display: none 样式,来实现元素的显示和隐藏
    • 如果要频繁的切换元素的显示状态,用 v-show 性能会更好
  2. v-if 的原理是:每次动态创建或移除元素,实现元素的显示和隐藏
    • 如果刚进入页面的时候,某些元素默认不需要被展示,而且后期这个元素很可能也不需要被展示出来,此时 v-if 性能更好

在实际开发中,绝大多数情况,不用考虑性能问题,直接使用 v-if 就好了!!!

v-if 指令在使用的时候,有两种方式:

  1. 直接给定一个布尔值 true 或 false

    <p v-if="true">被 v-if 控制的元素p>
    
  2. 给 v-if 提供一个判断条件,根据判断的结果是 true 或 false,来控制元素的显示和隐藏

    <p v-if="type === 'A'">良好p>
    
 <div v-if="type === 'A'">优秀div>
    <div v-else-if="type === 'B'">良好div>
    <div v-else-if="type === 'C'">一般div>
    <div v-else>div>

6 列表渲染

items 是待循环的数组

item 是被循环的每一项

v-for 指令还支持一个可选的第二个参数,即当前项的索引。语法格式为(item,index)in items

<div id="app">

    <table class="table table-bordered table-hover table-striped">
      <thead>
        <th>索引th>
        <th>Idth>
        <th>姓名th>
      thead>
      <tbody>
        
        
        
        
        <tr v-for="(item, index) in list" :key="item.id">
          <td>{{ index }}td>
          <td>{{ item.id }}td>
          <td>{{ item.name }}td>
        tr>
      tbody>
    table>
  div>

lab for属性

<input type="checkbox" id="cb1">
  <label for="cb1">label>
  <hr>
  <input type="checkbox" id="cb2">
  <label for="cb2">label>

day3

过滤器

过滤器的注意点

  1. 要定义到 filters 节点下,本质是一个函数

  2. 在过滤器函数中,一定要有 return 值

  3. 在过滤器的形参中,可以获取到“管道符”前面待处理的那个值

  4. 如果全局过滤器和私有过滤器名字一致,此时按照“就近原则”,调用的是”私有过滤器“

  5. <body>
      <div id="app">
        <p>message 的值是:{{ message | capi }}p>
      div>
    
      <script src="./lib/vue-2.6.12.js">script>
      <script>
        const vm = new Vue({
          el: '#app',
          data: {
            message: 'hello vue.js'
          },
          // 过滤器函数,必须被定义到 filters 节点之下
          // 过滤器本质上是函数
          filters: {
            // 注意:过滤器函数形参中的 val,永远都是“管道符”前面的那个值
            capi(val) {
              // 字符串有 charAt 方法,这个方法接收索引值,表示从字符串中把索引对应的字符,获取出来
              // val.charAt(0)
              const first = val.charAt(0).toUpperCase()
              // 字符串的 slice 方法,可以截取字符串,从指定索引往后截取
              const other = val.slice(1)
              // 强调:过滤器中,一定要有一个返回值
              return first + other
            }
          }
        })
      script>
    body>
    

    定义全局过滤器,不用定义到filter里面

     // 使用 Vue.filter() 定义全局过滤器
        Vue.filter('capi', function (str) {
          const first = str.charAt(0).toUpperCase()
          const other = str.slice(1)
          return first + other + '~~~'
        })
    

watch 侦听器

侦听器的格式

  1. 方法格式的侦听器
    • 缺点1:无法在刚进入页面的时候,自动触发!!!
    • 缺点2:如果侦听的是一个对象,如果对象中的属性发生了变化,不会触发侦听器!!!
  2. 对象格式的侦听器
    • 好处1:可以通过 immediate 选项,让侦听器自动触发!!!
    • 好处2:可以通过 deep 选项,让侦听器深度监听对象中每个属性的变化!!!

普通侦听器

<body>
  <div id="app">
    <input type="text" v-model="username">
  </div>

  <script src="./lib/vue-2.6.12.js"></script>
  <script src="./lib/jquery-v3.6.0.js"></script>

  <script>
    const vm = new Vue({
      el: '#app',
      data: {
        username: 'admin'
      },
      // 所有的侦听器,都应该被定义到 watch 节点下
      watch: {
        // 侦听器本质上是一个函数,要监视哪个数据的变化,就把数据名作为方法名即可
        // 新值在前,旧值在后
        username(newVal) {
          if (newVal === '') return
          // 1. 调用 jQuery 中的 Ajax 发起请求,判断 newVal 是否被占用!!!
          $.get('https://www.escook.cn/api/finduser/' + newVal, function (result) {
            console.log(result)
          })
        }
      }
    })
  </script>
</body>

立即调用

默认情况下,组件在初次加载完毕后不会调用watch 侦听器。如果想让watch 侦听器立即被调用,则需要使用immediate选项。示例代码如下:

watch:{
    username:{
        //handler是固定写法,表示当username的值变化时,自动调用handler处理函数
        handler:asyncfunction(newVal){       
            if(newVal==='')return
            const{data:res}=awaitaxios.get('https://www.escook.cn/api/finduser/'+newVal)
            console.log(res)
        },
        //表示页面初次渲染好之后,就立即触发当前的watch侦听器
        immediate:true
    }
 }

对象侦听器

watch: {
        // 定义对象格式的侦听器
        username: {
          // 侦听器的处理函数
          handler(newVal, oldVal) {
            console.log(newVal, oldVal)
          },
          // immediate 选项的默认值是 false
          // immediate 的作用是:控制侦听器是否自动触发一次!
          immediate: true
        }
      }

深度侦听

watch: {
         /* info: {
          handler(newVal) {
            console.log(newVal)
          },
          // 开启深度监听,只要对象中任何一个属性变化了,都会触发“对象的侦听器”
          deep: true
        }*/
        // 如果要侦听的是对象的子属性的变化,则必须包裹一层单引号
        'info.username'(newVal) {
          console.log(newVal)
        }
      }

计算属性

计算属性指的是通过一系列运算之后,最终得到一个属性值。这个动态计算出来的属性值可以被模板结构或methods 方法使用。

特点:

  1. 定义的时候,要被定义为“方法”
  2. 在使用计算属性的时候,当普通的属性使用即可

好处:

  1. 实现了代码的复用
  2. 只要计算属性中依赖的数据源变化了,则计算属性会自动重新求值!

所有的计算属性,都要定义到 computed 节点之下

<body>
  <div id="app">
    <div>
      <span>R</span>
      <input type="text" v-model.number="r">
    </div>
    <div>
      <span>G</span>
      <input type="text" v-model.number="g">
    </div>
    <div>
      <span>B</span>
      <input type="text" v-model.number="b">
    </div>
    <hr>

    <!-- 专门用户呈现颜色的 div 盒子 -->
    <!-- 在属性身上,: 代表  v-bind: 属性绑定 -->
    <!-- :style 代表动态绑定一个样式对象,它的值是一个 {  } 样式对象 -->
    <!-- 当前的样式对象中,只包含 backgroundColor 背景颜色 -->
    <div class="box" :style="{ backgroundColor: rgb }">
      {{ rgb }}
    </div>
    <button @click="show">按钮</button>
  </div>

  <script>
    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
      el: '#app',
      data: {
        // 红色
        r: 0,
        // 绿色
        g: 0,
        // 蓝色
        b: 0
      },
      methods: {
        // 点击按钮,在终端显示最新的颜色
        show() {
          console.log(this.rgb)
        }
      },
      // 所有的计算属性,都要定义到 computed 节点之下
      // 计算属性在定义的时候,要定义成“方法格式”
      computed: {
        // rgb 作为一个计算属性,被定义成了方法格式,
        // 最终,在这个方法中,要返回一个生成好的 rgb(x,x,x) 的字符串
        rgb() {
          return `rgb(${this.r}, ${this.g}, ${this.b})`
        }
      }
    });

    console.log(vm)
  </script>
</body>

axios

axios 是一个专注于网络请求的库!

axios 的基本使用

axios() axios.get() axios.post() axios.delete() axios.put()

发起 GET 请求:

axios({
  // 请求方式
  method: 'GET',
  // 请求的地址
  url: 'http://www.liulongbin.top:3006/api/getbooks',
  // URL 中的查询参数
  params: {
    id: 1
  }
}).then(function (result) {
  console.log(result)
})

发起 POST 请求:

document.querySelector('#btnPost').addEventListener('click', async function () {
  // 如果调用某个方法的返回值是 Promise 实例,则前面可以添加 await!
  // await 只能用在被 async “修饰”的方法中
   //{data}解构赋值,只要返回的data数据 {data : res}结构赋值重新赋值res=data
  const { data: res } = await axios({
    method: 'POST', 
    url: 'http://www.liulongbin.top:3006/api/post',
     //请求体参数
    data: {
      name: 'zs',
      age: 20
    }
  })

  console.log(res)
})

直接请求

document.querySelector('#btnGET').addEventListener('click', async function () {
      /* axios.get('url地址', {
        // GET 参数
        params: {}
      }) */

      const { data: res } = await axios.get('http://www.liulongbin.top:3006/api/getbooks', {
        params: { id: 1 }
      })
      console.log(res)
    })

    document.querySelector('#btnPOST').addEventListener('click', async function () {
      // axios.post('url', { /* POST 请求体数据 */ })
      const { data: res } = await axios.post('http://www.liulongbin.top:3006/api/post', { name: 'zs', gender: '女' })
      console.log(res)
    })

vue-cli 的使用

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

使用 npm install -g @vue/cli 命令,即可方便的把它安装到自己的电脑上,cd到项目文件下 npm run serve 运行项目

单页面应用程序(英文名:Single Page Application)简称SPA,顾名思义,指的是一个Web 网站中只有唯一的一个HTML 页面,所有的功能与交互都在这唯一的一个页面内完成

  1. 在终端下运行如下的命令,创建指定名称的项目:

    vue cerate 项目的名称
    
  2. vue 项目中 src 目录的构成:

    assets 文件夹:存放项目中用到的静态资源文件,例如:css 样式表、图片资源
    components 文件夹:程序员封装的、可复用的组件,都要放到 components 目录下
    main.js 是项目的入口文件。整个项目的运行,要先执行 main.js
    App.vue 是项目的根组件。
    

工作原理

在工程化的项目中,vue 要做的事情很单纯:通过main.js 把App.vue 渲染到index.html 的指定区域中

①App.vue 用来编写待渲染的模板结构

②index.html 中需要预留一个el 区域

③main.js 把App.vue 渲染到了index.html 所预留的区域中

vue 组件

组件化开发指的是:根据封装的思想,把页面上可重用的UI 结构封装为组件,从而方便项目的开发和维护。

  1. vue 组件的三个组成部分

​ template-> 组件的模板结构

​ script-> 组件的JavaScript 行为

​ style-> 组件的样式

  1. template 是vue 提供的容器标签,只起到包裹性质的作用,它不会被渲染为真正的DOM 元素template 中只能包含唯一的根节点

总结

export default{
// 所有的变量,都应该被定义到 data 节点下
data() {
  return{
      
  }
}

// 所有的侦听器,都应该被定义到 watch 节点下
watch: {

}

// 所有的过滤器,都应该被定义到 filters 节点下
filters: {
 
}

// 所有的函数,都应该被定义到 methods 节点下
method: {

}

// 所有的计算属性,都应该被定义到 computed 节点下
computed: {

}
}

day4/day5

vue组件

组件使用

在根组件(一般App.vue作为根组件)里面导入其他子组件

  1. 使用import 语法导入需要的组件

    import Left from '@/components/Left.vue'
    import Right from '@/components/Right.vue'
    import Test from '@/components/Test.vue'  //组件目录
    
  2. 使用components节点注册组件

    export default {
      // 2. 注册组件
      components: {
        Left,
        Right,
        Test
      }
    }
    
  3. 以标签形式使用刚才注册的组件

    <Left></Left>
    <Right></Right>
    

通过components 注册的是私有子组件例如:

在组件A的components 节点下,注册了组件F。则组件F 只能用在组件A 中;不能被用在组件C 中。

注册全局组件

在vue 项目的main.js 入口文件中,通过Vue.component() 方法

// 导入需要被全局注册的那个组件
import Count from '@/components/Count.vue'
//参数1:字符串格式,表示组件的“注册名称”
//参数2:需要被全局注册的那个组件
Vue.component('MyCount',Count)

组件的props

vue 规定:组件中封装的自定义属性是只读的,程序员不能直接修改props 的值。否则会直接报错:要想修改props 的值,可以把props 的值转存到data 中,因为data 中的数据都是可读可写的!

export default {
  // props 是"自定义属性",允许使用者通过自定义属性,为当前组件指定初始值
  // 自定义属性的名字,是封装者自定义的(只要名称合法即可)
  // props 中的数据,可以直接在模板结构中被使用
  // 注意:props 是只读的,不要直接修改 props 的值,否则终端会报错!
  // props: ['init'],
  props: {
    // 自定义属性A : { /* 配置选项 */ },
    // 自定义属性B : { /* 配置选项 */ },
    // 自定义属性C : { /* 配置选项 */ },
    init: {
      // 如果外界使用 Count 组件的时候,没有传递 init 属性,则默认值生效
      default: 0,
      // init 的值类型必须是 Number 数字
      type: Number,
      // 必填项校验
      required: true
    }
  },

  data() {
    return {
      // 把 props 中的 init 值,转存到 count 上
      count: this.init
    }
  },
  methods: {
    show() {
      console.log(this)
    }
  }
}
/*在组件使用者传参时在init前加:传数字否则为字符串*/

自定义属性选择器

<h3 data-v-001>Left 组件</h3>   //data-v-编号
<style>
    h3[data-v-001]{
        color:red;
    }//选中其时要加上自定义属性
</syle>

less样式的scoped属性会自动为每一个组件自动生成一个自定义属性

<style lang="less" scoped>
h3 {
  color: red;
}
</style>

组件之间的样式冲突

默认情况下,写在.vue 组件中的样式会全局生效,

导致组件之间样式冲突的根本原因是:

①单页面应用程序中,所有组件的DOM 结构,都是基于唯一的index.html 页面进行呈现的

②每个组件中的样式,都会影响整个index.html 页面中的DOM 元素

在父组件中修改子组件时要用到/deep/

// h5[data-v-3c83f0b7]   不使用/deeep/
// [data-v-3c83f0b7] h5  使用/deeep/

// 当使用第三方组件库的时候,如果有修改第三方组件默认样式的需求,需要用到 /deep/
/deep/ h5 {
  color: pink;
}

组件的生命周期

生命周期(Life Cycle)是指一个组件从创建-> 运行-> 销毁的整个阶段,强调的是一个时间段。

生命周期函数:是由vue 框架提供的内置函数,会伴随着组件的生命周期,自动按次序执行。

注意:生命周期强调的是时间段,生命周期函数强调的是时间点。

组件生命周期函数的分类

常用生命周期函数: create(), mounted()

// 创建阶段的第1个生命周期函数
  beforeCreate() {
    // console.log(this.info)
    // console.log(this.message)
    // this.show()
  },
  created() {
    // created 生命周期函数,非常常用。
    // 经常在它里面,调用 methods 中的方法,请求服务器的数据。
    // 并且,把请求到的数据,转存到 data 中,供 template 模板渲染的时候使用!
    this.initBookList()
  },
  beforeMount() {
    // console.log('beforeMount')
    // const dom = document.querySelector('#myh3')
    // console.log(dom)
  },
  // 如果要操作当前组件的 DOM,最早,只能在 mounted 阶段执行
  mounted() {
    // console.log(this.$el)
    // const dom = document.querySelector('#myh3')
    // console.log(dom)
  },
  beforeUpdate() {
    // console.log('beforeUpdate')
    // console.log(this.message)
    // const dom = document.querySelector('#pppp')
    // console.log(dom.innerHTML)
  },
  // 当数据变化之后,为了能够操作到最新的 DOM 结构,必须把代码写到 updated 生命周期函数中
  updated() {
    // console.log('updated')
    // console.log(this.message)
    // const dom = document.querySelector('#pppp')
    // console.log(dom.innerHTML)
  },
  beforeDestroy() {
    console.log('beforeDestroy')
    this.message = 'aaa'
    console.log(this.message)
  },
  destroyed() {
    console.log('destroyed')
    // this.message = 'aaa'
  }
}

组件之间的数据共享

组件之间的关系

在项目开发中,组件之间的最常见的关系分为如下两种:

①父子关系

②兄弟关系

EventBus的使用步骤

①创建eventBus.js 模块,并向外共享一个Vue 的实例对象

②在数据发送方,调用bus.$emit(‘事件名称’, 要发送的数据) 方法触发自定义事件

③在数据接收方,调用bus.$on(‘事件名称’, 事件处理函数) 方法注册一个自定义事件

父组件向子组件共享数据

//子组件
<script>

export default {
  props: ['msg', 'user'],
  data() {
  },
  methods: {

  }
}
</script>
<Left :msg="message" :user="userinfo"></Left>
//父组件
export default {
  data() {
    return {
      message: 'hello 132 的宝们!',
      userinfo:{ name:'wsc',age:18},
    }
  },
  methods: {
  },
  components: {
    Left,
    Right
  }
}
</script>

子向父

this.$emit(‘numchange’, this.count) 发送数据

//子组件
<script>

export default {
  data() {
    return {
      // 子组件自己的数据,将来希望把 count 值传给父组件
      count: 0,
    }
  },

  methods: {
    add() {
      // 让子组件的 count 值自增 +1
      this.count += 1
      // 把自增的结果,传给父组件
      this.$emit('numchange', this.count)   //发送数据
    }
  }
}
</script>
//父组件
<template>
<Right @numchange="getNewCount"></Right>   //numchange自定义事件,事件发生触发getNewCount函数
</template>
<script>
import Left from '@/components/Left.vue'
import Right from '@/components/Right.vue'

export default {
  data() {
    return {
      // 定义 countFromSon 来接收子组件传递过来的数据
      countFromSon: 0
    }
  },
  methods: {
    // 获取子组件传递过来的数据
    getNewCount(val) {
      console.log('numchange 事件被触发了!', val)
      this.countFromSon = val
    }
  },
  components: {
    Left,
    Right
  }
}
</script>

兄弟之间

bus.$on():接收数据方法

在vue2.x中,兄弟组件之间数据共享的方案是EventBus。

//发送方
import bus from'./eventBus.js’
 exportdefault{
     data(){
        return{
            msg:'hellovue.js'
         }
     },
     methods:{
        sendMsg(){
            bus.$emit('share',this.msg)
        }
     }
}
//eventBus.js
importVuefrom'vue'
//向外共享Vue的实例对象
exportdefaultnewVue()
//数据接收方
importbusfrom'./eventBus.js'

exportdefault{
  data(){
    return{
       msgFromLeft:''
    }
 },
 created(){
   bus.$on('share',val=>{
         this.msgFromLeft=val
      })
   }
}

ref引用

ref 用来辅助开发者在不依赖于jQuery 的情况下,获取DOM 元素或组件的引用。

每个vue 的组件实例上,都包含一个 r e f s 对 象 , 里 面 存 储 着 对 应 的 D O M 元 素 或 组 件 的 引 用 。 默 认 情 况 下 , 组 件 的 refs 对象,里面存储着对应的DOM 元素或组件的引用。默认情况下,组件的 refsDOMrefs 指向一个空对象。

<h1 ref="myh12">App 根组件</h1>
<script>
export default {
  methods:{
    showThis(){
      //当前app的组件实例对象
      // console.log(this.$refs.myh12)
      this.$refs.myh12.style.color ='pink'
    }
  }
}
</script>

TypeError: Cannot read property ‘focus’ of undefined 错误 'focus’之前的属性为undefined

this.$nextTick(cb) 方法

组件的$nextTick(cb) 方法,会把cb 回调推迟到下一个DOM 更新周期之后执行。通俗的理解是:等组件的DOM 更新完成之后,再执行cb 回调函数。从而能保证cb 回调函数可以操作到最新的DOM 元素。

//获取DOM 让文本框自动获得焦点
// this.$refs.iptRef.focus()
this.$nextTick(()=>{
  this.$refs.iptRef.focus()
})

数组知识

arr.foreach循环

const arr = ['小红', '你大红', '苏大强', '宝']

// forEach 循环一旦开始,无法在中间被停止
arr.forEach((item, index) => {
  console.log('object')
  if (item === '苏大强') {
    console.log(index)
  }
}) 

arr.some循环

 arr.some((item, index) => {
  console.log('ok')
  if (item === '苏大强') {
    console.log(index)
    // 在找到对应的项之后,可以通过 return true 固定的语法,来终止 some 循环
    return true
  }
}) 

arr.every

const arr = [
  { id: 1, name: '西瓜', state: true },
  { id: 2, name: '榴莲', state: false },
  { id: 3, name: '草莓', state: true },
]

// 需求:判断数组中,水果是否被全选了!true为选中
const result = arr.every(item => item.state)
console.log(result)

reduce函数

const arr = [
  { id: 1, name: '西瓜', state: true, price: 10, count: 1 },
  { id: 2, name: '榴莲', state: false, price: 80, count: 2 },
  { id: 3, name: '草莓', state: true, price: 20, count: 3 },
]

// 需求:把购物车数组中,已勾选的水果,总价累加起来!
 let amt = 0 // 总价
// arr.filter(item => item.state) 选出已选中的水果并返回一个新的数组
    arr.filter(item => item.state).forEach(item => {
      amt += item.price * item.count
    })
    console.log(amt) 

// arr.filter(item => item.state).reduce((累加的结果, 当前循环项) => { }, 初始值)
const result = arr.filter(item => item.state).reduce((amt, item) => amt += item.price * item.count, 0)

console.log(result)

案例

<template>
  <div class="app-container">
    <!-- Header 头部区域 -->
    <Header title="购物车案例"></Header>
    <!-- 循环渲染每一个商品的信息 -->
    <Goods
      v-for="item in list"
      :key="item.id"
      :id="item.id"
      :title="item.goods_name"
      :pic="item.goods_img"
      :price="item.goods_price"
      :state="item.goods_state"
      :count="item.goods_count"
      @state-change="getNewState"
    ></Goods>

    <!-- Footer 区域 -->
    <Footer :isfull="fullState" :amount="amt" :all="total" @full-change="getFullState"></Footer>
  </div>
</template>

<script>
// 导入 axios 请求库
import axios from 'axios'
// 导入需要的组件
import Header from '@/components/Header/Header.vue'
import Goods from '@/components/Goods/Goods.vue'
import Footer from '@/components/Footer/Footer.vue'

import bus from '@/components/eventBus.js'

export default {
  data() {
    return {
      // 用来存储购物车的列表数据,默认为空数组
      list: []
    }
  },
  // 计算属性
  computed: {
    // 动态计算出全选的状态是 true 还是 false
    fullState() {
      return this.list.every(item => item.goods_state)
    },
    // 已勾选商品的总价格
    amt() {
      // 1. 先 filter 过滤
      // 2. 再 reduce 累加
      return this.list
        .filter(item => item.goods_state)
        .reduce((total, item) => (total += item.goods_price * item.goods_count), 0)
    },
    // 已勾选商品的总数量
    total() {
      return this.list.filter(item => item.goods_state).reduce((t, item) => (t += item.goods_count), 0)
    }
  },
  created() {
    // 调用请求数据的方法
    this.initCartList()

    bus.$on('share', val => {
      this.list.some(item => {
        if (item.id === val.id) {
          item.goods_count = val.value
          return true
        }
      })
    })
  },
  methods: {
    // 封装请求列表数据的方法
    async initCartList() {
      // 调用 axios 的 get 方法,请求列表数据
      const { data: res } = await axios.get('https://www.escook.cn/api/cart')
      // 只要请求回来的数据,在页面渲染期间要用到,则必须转存到 data 中
      if (res.status === 200) {
        this.list = res.list
      }
    },
    // 接收子组件传递过来的数据
    // e 的格式为 { id, value }
    getNewState(e) {
      this.list.some(item => {
        if (item.id === e.id) {
          item.goods_state = e.value
          // 终止后续的循环
          return true
        }
      })
    },
    // 接收 Footer 子组件传递过来的全选按钮的状态
    getFullState(val) {
      this.list.forEach(item => (item.goods_state = val))
    }
  },
  components: {
    Header,
    Goods,
    Footer
  }
}
</script>

<style lang="less" scoped>
.app-container {
  padding-top: 45px;
  padding-bottom: 50px;
}
</style>

day6 组件库Vant vant-contrib.gitee.io/vant/#/zh-CN/nav-bat

动态组件

<!-- 渲染 Left 组件和 Right 组件 -->
<!-- 1. component 标签是 vue 内置的,作用:组件的占位符 -->
<!-- 2. is 属性的值,表示要渲染的组件的名字 -->
<!-- 3. is 属性的值,应该是组件在 components 节点下的注册名称 -->

<!-- keep-alive 会把内部的组件进行缓存,而不是销毁组件 -->
<!-- 在使用 keep-alive 的时候,可以通过 include 指定哪些组件需要被缓存; -->
<!-- 或者,通过 exclude 属性指定哪些组件不需要被缓存;但是:不要同时使用 include 和 exclude 这两个属性 -->
<keep-alive exclude="MyRight">     //myRight右面组件  参数组件名
  <component :is="comName"></component>
</keep-alive>
//在Right组件里写
export default {
  // 当提供了 name 属性之后,组件的名称,就是 name 属性的值
  // 对比:
  // 1. 组件的 “注册名称” 的主要应用场景是:以标签的形式,把注册好的组件,渲染和使用到页面结构之中
  // 2. 组件声明时候的 “name” 名称的主要应用场景:结合  标签实现组件缓存功能;以及在调试工具中看到组件的 name 名称
  name: 'MyRight'
}

keep-alive 对应的生命周期函数

当组件被缓存时,会自动触发组件的deactivated生命周期函数。当组件被激活时,会自动触发组件的activated生命周期函数

expor default{
    deactivated(){
    console.log('组件被缓存了')
    },
    activated(){
    console.log('组件被激活了')
    }
}

插槽

插槽(Slot)是vue 为组件的封装者提供的能力。允许开发者在封装组件时,把不确定的、希望由用户指定的部分定义为插槽。可以把插槽认为是组件封装期间,为用户预留的内容的占位符。

//left组件
<template>
  <div class="left-container">//组件用div包裹,建议为div起名字为"组件名-container"
    <h3>Left 组件</h3>
    <hr />
    <!-- 声明一个插槽区域 -->
    <!-- vue 官方规定:每一个 slot 插槽,都要有一个 name 名称 -->
    <!-- 如果省略了 slot 的 name 属性,则有一个默认名称叫做 default -->
    <slot name="default">    //带名字的插槽叫具名插槽
      <h6>这是 default 插槽的后备内容</h6>
    </slot>
  </div>
</template>
//主组件app
<Left>
  <!-- 默认情况下,在使用组件的时候,提供的内容都会被填充到名字为 default 的插槽之中 -->
  <!-- 1. 如果要把内容填充到指定名称的插槽中,需要使用 v-slot: 这个指令 -->
  <!-- 2. v-slot: 后面要跟上插槽的名字 -->
  <!-- 3. v-slot: 指令不能直接用在元素身上,必须用在 template 标签上 -->
  <!-- 4. template 这个标签,它是一个虚拟的标签,只起到包裹性质的作用,但是,不会被渲染为任何实质性的 html 元素 -->
  <!-- 5. v-slot: 指令的简写形式是 # -->
  <template v-slot:default>   //#default
    <p>这是在 Left 组件的内容区域,声明的 p 标签</p>
  </template>
</Left>

作用域插槽:插槽中带有数据

//APP中
<template #content="{ msg, user }">//解构用法,多个参数传过来的是一个对象msg  user为里面的数据
  <div>
    <p>啊,大海,全是水。</p>
    <p>啊,蜈蚣,全是腿。</p>
    <p>啊,辣椒,净辣嘴。</p>
    <p>{{ msg }}</p>
    <p>{{ user.name }}</p>
  </div>
</template>
//article中
<template>
    <!-- 文章的内容 -->
    <div class="content-box">
      <!-- 在封装组件时,为预留的 <slot> 提供属性对应的值,这种用法,叫做 “作用域插槽” -->
      <slot name="content" msg="hello vue.js" :user="userinfo"></slot>
    </div>
</template>
/*在data中定义
data() {
    return {
      // 用户的信息对象
      userinfo: {
        name: 'zs',
        age: 20
      }
    }
  }*/

自定义指令

自定义指令的分类

vue 中的自定义指令分为两类,分别是:

⚫私有自定义指令

⚫全局自定义指令

私有自定义指令

只能自己用

在每个vue 组件中,可以在directives节点下声明私有自定义指令。

注意bind()和update()

export default{
directives: {
    // 定义名为 color 的指令,指向一个配置对象
    /* color: {
      // 当指令第一次被绑定到元素上的时候,会立即触发 bind 函数
      // 形参中的 el 表示当前指令所绑定到的那个 DOM 对象
      bind(el, binding) {
        console.log('触发了 v-color 的 bind 函数')
        el.style.color = binding.value
      },
      // 在 DOM 更新的时候,会触发 update 函数
      update(el, binding) {
        console.log('触发了 v-color 的 update 函数')
        el.style.color = binding.value
      }
    } */
    //当bind()和update()函数里的代码一样时可简写为
    color(el, binding) {
      el.style.color = binding.value
    }
  }
}

使用

<h1 v-color="color">App 根组件</h1>
<p v-color="'red'">测试</p>

<button @click="color = 'green'">改变 color 的颜色值</button>

全局自定义指令

全局共享的自定义指令需要通过“Vue.directive()”进行声明

//写在main.js中
// 全局自定义指令
/* Vue.directive('color', {
  bind(el, binding) {
    el.style.color = binding.value
  },
  update(el, binding) {
    el.style.color = binding.value
  }
}) */

Vue.directive('color', function(el, binding) {
  el.style.color = binding.value
})

ESLint

官网 https://eslint.bootcss.com/

anxios

//main.js里
import Vue from 'vue'
import App from './App.vue'
import axios from 'axios'

Vue.config.productionTip = false

// 全局配置 axios 的请求根路径
axios.defaults.baseURL = 'http://www.liulongbin.top:3006'
// 把 axios 挂载到 Vue.prototype 上,供每个 .vue 组件的实例直接使用
Vue.prototype.$http = axios

// 今后,在每个 .vue 组件中要发起请求,直接调用 this.$http.xxx
// 但是,把 axios 挂载到 Vue 原型上,有一个缺点:不利于 API 接口的复用!!!

new Vue({
  render: h => h(App)
}).$mount('#app')
//left
<template>
  <div class="left-container">
    <h3>Left 组件</h3>
    <button @click="getInfo">发起 GET 请求</button>
    <button @click="btnGetBooks">获取图书列表的数据</button>
  </div>
</template>

<script>
// import axios from 'axios'

export default {
  methods: {
    async getInfo() {
      const { data: res } = await this.$http.get('/api/get')
      console.log(res)
    },
    // 点击按钮,获取图书列表的数据
    async btnGetBooks() {
      const { data: res } = await this.$http.get('/api/getbooks')
      console.log(res)
    }
  }
}
</script>
//right
<template>
  <div class="right-container">
    <h3>Right 组件</h3>
    <button @click="postInfo">发起 POST 请求</button>
    <button @click="btnGetBooks">获取图书列表的数据</button>
  </div>
</template>

<script>
// import axios from 'axios'

export default {
  methods: {
    async postInfo() {
      const { data: res } = await this.$http.post('/api/post', { name: 'zs', age: 20 })
      console.log(res)
    },
    // 点击按钮,获取图书列表的数据
    async btnGetBooks() {
      const { data: res } = await this.$http.get('/api/getbooks')
      console.log(res)
    }
  }
}
</script>
//app
<template>
  <div>
    <h1>App 根组件</h1>
    <button @click="btnGetBooks">获取图书列表的数据</button>

    <hr />

    <div class="box">
      <Left></Left>
      <Right></Right>
    </div>
  </div>
</template>

<script>
import Left from '@/components/Left.vue'
import Right from '@/components/Right.vue'

export default {
  methods: {
    // 点击按钮,获取图书列表的数据
    async btnGetBooks() {
      const { data: res } = await this.$http.get('/api/getbooks')
      console.log(res)
    }
  },
  components: {
    Left,
    Right
  }
}
</script>

day7

路由

路由(英文:router)就是对应关系。

前端路由的工作方式

①用户点击了页面上的路由链接

②导致了URL 地址栏中的Hash 值发生了变化

③前端路由监听了到Hash 地址的变化

④前端路由把当前Hash 地址对应的组件渲染都浏览器中

点击换组件

<template>
  <div class="app-container">
    <h1>App 根组件</h1>

    <a href="#/home">首页</a>
    <a href="#/movie">电影</a>
    <a href="#/about">关于</a>
    <hr />

    <component :is="comName"></component>
  </div>
</template>

<script>
// 导入组件
import Home from '@/components/Home.vue'
import Movie from '@/components/Movie.vue'
import About from '@/components/About.vue'

export default {
  name: 'App',
  data() {
    return {
      // 在动态组件的位置,要展示的组件的名字,值必须是字符串
      comName: 'Home'
    }
  },
  created() {
    // 只要当前的 App 组件一被创建,就立即监听 window 对象的 onhashchange 事件
    window.onhashchange = () => {
      console.log('监听到了 hash 地址的变化', location.hash)
      switch (location.hash) {
        case '#/home':
          this.comName = 'Home'
          break
        case '#/movie':
          this.comName = 'Movie'
          break
        case '#/about':
          this.comName = 'About'
          break
      }
    }
  },
  // 注册组件
  components: {
    Home,
    Movie,
    About
  }
}
</script>

vue-router

vue-router是vue.js 官方给出的路由解决方案。它只能结合vue 项目进行使用,能够轻松的管理SPA 项目中组件的切换。

vue-router 的官方文档地址:https://router.vuejs.org/zh/

在src源代码目录下,新建router/index.js 路由模块,所有的声明规则都写在index.js里

路由的嵌套在rentes[]写里children[]来存储子路由的路由


// src/router/index.js 就是当前项目的路由模块
import Vue from 'vue'
import VueRouter from 'vue-router'

// 导入需要的组件
import Home from '@/components/Home.vue'
import Movie from '@/components/Movie.vue'
import About from '@/components/About.vue'

import Tab1 from '@/components/tabs/Tab1.vue'
import Tab2 from '@/components/tabs/Tab2.vue'

import Login from '@/components/Login.vue'
import Main from '@/components/Main.vue'

// 把 VueRouter 安装为 Vue 项目的插件
// Vue.use() 函数的作用,就是来安装插件的
Vue.use(VueRouter)



// 创建路由的实例对象
const router = new VueRouter({
  // routes 是一个数组,作用:定义 “hash 地址” 与 “组件” 之间的对应关系  3.声明的规则
  routes: [
    // 重定向的路由规则
    { path: '/', redirect: '/home' },
    // 路由规则
    { path: '/home', component: Home },
    // 需求:在 Movie 组件中,希望根据 id 的值,展示对应电影的详情信息
    // 可以为路由规则开启 props 传参,从而方便的拿到动态参数的值
    { path: '/movie/:mid', component: Movie, props: true },//加props: true可以在movie.vue里用props['mid']接收
   
      {
      path: '/about',
      component: About,
      // redirect: '/about/tab1',   重定向,进去about页面时展示ta1路由
      children: [
        // 子路由规则
        // 默认子路由:如果 children 数组中,某个路由规则的 path 值为空字符串,则这条路由规则,叫做“默认子路由”
//用默认路由要把tab1改为tab1
        { path: '', component: Tab1 },
        { path: 'tab2', component: Tab2 }
      ]
    },
      
    { path: '/login', component: Login },
    { path: '/main', component: Main }
  ]
})



// 为 router 实例对象,声明全局前置导航守卫
// 只要发生了路由的跳转,必然会触发 beforeEach 指定的 function 回调函数
router.beforeEach(function(to, from, next) {
  // to 表示将要访问的路由的信息对象
  // from 表示将要离开的路由的信息对象
  // next() 函数表示放行的意思
  // 分析:
  // 1. 要拿到用户将要访问的 hash 地址
  // 2. 判断 hash 地址是否等于 /main。
  // 2.1 如果等于 /main,证明需要登录之后,才能访问成功
  // 2.2 如果不等于 /main,则不需要登录,直接放行  next()
  // 3. 如果访问的地址是 /main。则需要读取 localStorage 中的 token 值
  // 3.1 如果有 token,则放行
  // 3.2 如果没有 token,则强制跳转到 /login 登录页
  if (to.path === '/main') {
    // 要访问后台主页,需要判断是否有 token
    const token = localStorage.getItem('token')
    if (token) {
      next()
    } else {
      // 没有登录,强制跳转到登录页
      next('/login')
    }
  } else {
    next()
  }
})

export default router

routes-view

1.路由链接

<router-link to="/home">首页</router-link>

routes-link

2.占位符

<!-- 只要在项目中安装和配置了 vue-router,就可以使用 router-view 这个组件了 -->
<!-- 它的作用很单纯:占位符 -->
<router-view></router-view>

嵌套路由

{
      path: '/about',
      component: About,
      // redirect: '/about/tab1',   重定向,进去about页面时展示ta1路由
      children: [
        // 子路由规则
        // 默认子路由:如果 children 数组中,某个路由规则的 path 值为空字符串,则这条路由规则,叫做“默认子路由”
//用默认路由要把tab1改为tab1
        { path: '', component: Tab1 },{ path: 'tab2', component: Tab2 }]},

注意

<!-- 注意1:在 hash 地址中, / 后面的参数项,叫做“路径参数” -->
<!-- 在路由“参数对象”中,需要使用 this.$route.params 来访问路径参数 -->

<!-- 注意2:在 hash 地址中,? 后面的参数项,叫做“查询参数” -->
<!-- 在路由“参数对象”中,需要使用 this.$route.query 来访问查询参数 -->

<!-- 注意3:在 this.$route 中,path 只是路径部分;fullPath 是完整的地址 -->
<!-- 例如: -->
<!-- /movie/2?name=zs&age=20 是 fullPath 的值 -->
<!-- /movie/2 是 path 的值 -->
<router-link to="/movie/1">洛基</router-link>
<router-link to="/movie/2?name=zs&age=20">雷神</router-link>
<router-link to="/movie/3">复联</router-link>
<router-link to="/about">关于</router-link>

声明式导航& 编程式导航

在浏览器中,点击链接实现导航的方式,叫做声明式导航。

例如:⚫普通网页中点击 链接、vue 项目中点击 都属于声明式导航

在浏览器中,调用API 方法实现导航的方式,叫做编程式导航。

例如:⚫普通网页中调用location.href 跳转到新页面的方式,属于编程式导航

vue-router 提供了许多编程式导航的API,其中最常用的导航API 分别是:

①this.$router.push(‘hash 地址’)

​ ⚫跳转到指定hash 地址,并增加一条历史记录

②this.$router.replace(‘hash 地址’)

​ ⚫跳转到指定的hash 地址,并替换掉当前的历史记录

③this.$router.go(数值n)

⚫实现导航历史前进、后退

在实际开发中,一般只会前进和后退一层页面。因此vue-router 提供了如下两个便捷方法:

①$router.back()

​ ⚫在历史记录中,后退到上一个页面

②$router.forward()

​ ⚫在历史记录中,前进到下一个页面

<!-- 在行内使用编程式导航跳转的时候,this 必须要省略,否则会报错! -->

全局前置导航守卫

每次发生路由的导航跳转时,都会触发全局前置守卫。因此,在全局前置守卫中,程序员可以对每个路由进行访问权限的控制:

// 为 router 实例对象,声明全局前置导航守卫
// 只要发生了路由的跳转,必然会触发 beforeEach 指定的 function 回调函数
router.beforeEach(function(to, from, next) {
  // to 表示将要访问的路由的信息对象
  // from 表示将要离开的路由的信息对象
  // next() 函数表示放行的意思
  //当前用户拥有后台主页的访问权限,
  //直接放行:next()当前用户没有后台主页的访问权限,
  //强制其跳转到登录页面:next('/login')当前用户没有后台主页的访问权限,
  //不允许跳转到后台主页:next(false)
  // 分析:
  // 1. 要拿到用户将要访问的 hash 地址
  // 2. 判断 hash 地址是否等于 /main。
  // 2.1 如果等于 /main,证明需要登录之后,才能访问成功
  // 2.2 如果不等于 /main,则不需要登录,直接放行  next()
  // 3. 如果访问的地址是 /main。则需要读取 localStorage 中的 token 值
  // 3.1 如果有 token,则放行
  // 3.2 如果没有 token,则强制跳转到 /login 登录页
  if (to.path === '/main') {
    // 要访问后台主页,需要判断是否有 token
    const token = localStorage.getItem('token')
    if (token) {
      next()
    } else {
      // 没有登录,强制跳转到登录页
      next('/login')
    }
  } else {
    next()
  }
})

day8

vant http://vant-contrib.gitee.io/vant/#/zh-CN/theme

代码优化 http://doc.toutiao.liulongbin.top

less逛网 https://less.bootcss.com/

load https://www.lodashjs.com/

cmtCount 可以写为cmt-count

cmtCount: {
  // 通过数组形式,为当前属性定义多个可能的类型,可以是数字也可以是字符串
  type: [Number, String],
  default: 0
},
const newArr = [...Arr1, ...Arr2]     //数组拼接

定制样式

 //APP.vue
<template>
  <div>
    <!-- 路由占位符 -->
    <router-view></router-view>

    <!-- Tabbar 区域 -->
    <van-tabbar route>
      <van-tabbar-item icon="home-o" to="/">首页</van-tabbar-item>
      <van-tabbar-item icon="user-o" to="/user">我的</van-tabbar-item>
    </van-tabbar>
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>

<style lang="less" scoped></style>
//Home.vue
<template>
  <div class="home-container">
    <!-- 头部区域 -->
    <van-nav-bar title="黑马头条" fixed />

    <!-- 导入,注册,并使用 ArticleInfo 组件 -->
    <!-- 在使用组件的时候,如果某个属性名是“小驼峰”形式,则绑定属性的时候,建议改写成“连字符”格式。例如: -->
    <!-- cmtCount 建议写成 cmt-count -->
    <van-pull-refresh v-model="isLoading" :disabled="finished" @refresh="onRefresh">
      <van-list v-model="loading" :finished="finished" finished-text="没有更多了" @load="onLoad">
        <ArticleInfo
          v-for="item in artlist"
          :key="item.id"
          :title="item.title"
          :author="item.aut_name"
          :cmt-count="item.comm_count"
          :time="item.pubdate"
          :cover="item.cover"
        ></ArticleInfo>
      </van-list>
    </van-pull-refresh>
  </div>
</template>

<script>
// 按需导入 API 接口
import { getArticleListAPI } from '@/api/articleAPI.js'

// 导入需要的组件
import ArticleInfo from '@/components/Article/ArticleInfo.vue'

export default {
  name: 'Home',
  data() {
    return {
      // 页码值
      page: 1,
      // 每页显示多少条数据
      limit: 10,
      // 文章的数组
      artlist: [],
      // 是否正在加载下一页数据,如果 loading 为 true,则不会反复触发 load 事件
      // 每当下一页数据请求回来之后,千万要记得,把 loading 从 true 改为 false
      loading: true,
      // 所有数据是否加载完毕了,如果没有更多数据了,一定要把 finished 改成 true
      finished: false,
      // 是否正在下拉刷新
      isLoading: false
    }
  },
  created() {
    this.initArticleList()
  },
  methods: {
    // 封装获取文章列表数据的方法
    async initArticleList(isRefresh) {
      // 发起 GET 请求,获取文章的列表数据
      const { data: res } = await getArticleListAPI(this.page, this.limit)

      if (isRefresh) {
        // 证明是下拉刷新;新数据在前,旧数据在后
        // this.artlist = [新数据在后, 旧数据在前]
        this.artlist = [...res, ...this.artlist]
        this.isLoading = false
      } else {
        // 证明是上拉加载更多;旧数据在前,新数据在后
        // this.artlist = [旧数据在前, 新数据在后]
        this.artlist = [...this.artlist, ...res]
        this.loading = false
      }

      if (res.length === 0) {
        // 证明没有下一页数据了,直接把 finished 改为 true,表示数据加载完了!
        this.finished = true
      }
    },
    // 只要 onLoad 被调用,就应该请求下一页数据
    onLoad() {
      console.log('触发了 load 事件!')
      // 1. 让页码值 +1
      this.page++
      // 2. 重新请求接口获取数据
      this.initArticleList()
    },
    // 下拉刷新的处理函数
    onRefresh() {
      console.log('触发了下拉刷新!')
      // 1. 让页码值 +1
      this.page++
      // 2. 重新请求接口获取数据
      this.initArticleList(true)
    }
  },
  // 注册组件
  components: {
    ArticleInfo
  }
}
</script>

<style lang="less" scoped>
.home-container {
  padding: 46px 0 50px 0;
}
</style>
//User.vue
<template>
  <div class="user-container">
    <van-nav-bar title="黑马头条" fixed />
    <!-- 用户基本信息面板 -->
    <div class="user-card">
      <!-- 用户头像、姓名 -->
      <van-cell>
        <!-- 使用 title 插槽来自定义标题 -->
        <template #icon>
          <img src="../../assets/logo.png" alt="" class="avatar" />
        </template>
        <template #title>
          <span class="username">用户名</span>
        </template>
        <template #label>
          <van-tag color="#fff" text-color="#007bff">申请认证</van-tag>
        </template>
      </van-cell>
      <!-- 动态、关注、粉丝 -->
      <div class="user-data">
        <div class="user-data-item">
          <span>0</span>
          <span>动态</span>
        </div>
        <div class="user-data-item">
          <span>0</span>
          <span>关注</span>
        </div>
        <div class="user-data-item">
          <span>0</span>
          <span>粉丝</span>
        </div>
      </div>
    </div>

    <!-- 操作面板 -->
    <van-cell-group class="action-card">
      <van-cell icon="edit" title="编辑资料" is-link />
      <van-cell icon="chat-o" title="小思同学" is-link />
      <van-cell icon="warning-o" title="退出登录" is-link />
    </van-cell-group>
  </div>
</template>

<script>
// 按需导入 API 接口
import { getArticleListAPI } from '@/api/articleAPI.js'

// const result = getArticleListAPI(1, 5)
// console.log(result)

export default {
  name: 'User',
  data() {
    return {
      page: 1,
      limit: 5
    }
  },
  created() {
    this.initArticleList()
  },
  methods: {
    async initArticleList() {
      const { data: res } = await getArticleListAPI(this.page, this.limit)

      console.log(res)
    }
  }
}
</script>

<style lang="less" scoped>
.user-container {
  .user-card {
    background-color: #007bff;
    color: white;
    padding-top: 20px;
    .van-cell {
      background: #007bff;
      color: white;
      &::after {
        display: none;
      }
      .avatar {
        width: 60px;
        height: 60px;
        background-color: #fff;
        border-radius: 50%;
        margin-right: 10px;
      }
      .username {
        font-size: 14px;
        font-weight: bold;
      }
    }
  }
  .user-data {
    display: flex;
    justify-content: space-evenly;
    align-items: center;
    font-size: 14px;
    padding: 30px 0;
    .user-data-item {
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      width: 33.33%;
    }
  }
}
</style>
//Ariticleinfo.vue  数据请求的方法封装并导出
<template>
  <div>
    <van-cell>
      <!-- 标题区域的插槽 -->
      <template #title>
        <div class="title-box">
          <!-- 标题 -->
          <span>{{ title }}</span>
          <!-- 单张图片 -->
          <img :src="cover.images[0]" alt="" class="thumb" v-if="cover.type === 1" />
        </div>
        <!-- 三张图片 -->
        <div class="thumb-box" v-if="cover.type === 3">
          <img :src="item" alt="" class="thumb" v-for="(item, i) in cover.images" :key="i" />
        </div>
      </template>
      <!-- label 区域的插槽 -->
      <template #label>
        <div class="label-box">
          <span>作者 {{ author }} &nbsp;&nbsp; {{ cmtCount }} 评论 &nbsp;&nbsp; 发布日期 {{ time }}</span>
          <!-- 关闭按钮 -->
          <van-icon name="cross" />
        </div>
      </template>
    </van-cell>
  </div>
</template>

<script>
export default {
  name: 'ArticleInfo',
  // 自定义属性
  props: {
    // 文章的标题
    title: {
      type: String,
      default: ''
    },
    // 作者名字
    author: {
      type: String,
      default: ''
    },
    // 评论数
    cmtCount: {
      // 通过数组形式,为当前属性定义多个可能的类型,可以时数字也可以是字符串
      type: [Number, String],
      default: 0
    },
    // 发布日期
    time: {
      type: String,
      default: ''
    },
    // 封面的信息对象
    cover: {
      type: Object,
      // 通过 default 函数,返回 cover 属性的默认值
      default: function() {
        // 这个 return 的对象就是 cover 属性的默认值
        return { type: 0 }
      }
    }
  }
}
</script>

<style lang="less" scoped>
.label-box {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.thumb {
  // 矩形黄金比例:0.618
  width: 113px;
  height: 70px;
  background-color: #f8f8f8;
  object-fit: cover;
}

.title-box {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
}

.thumb-box {
  display: flex;
  justify-content: space-between;
}
</style>
//request.js 请求地址
import axios from 'axios'

// 调用 axios.create() 函数,创建一个 axios 的实例对象,用 request 来接收
const request = axios.create({
  // 指定请求的根路径
  baseURL: 'https://www.escook.cn'
})

export default request

build之后

vue.config.js

// 这个文件是 vue-cli 创建出来的项目的配置文件
// 在 vue.config.js 这个配置文件中,可以对整个项目的打包、构建进行全局性的配置

// webpack 在进行打包的时候,底层用到了 node.js
// 因此,在 vue.config.js 配置文件中,可以导入并使用 node.js 中的核心模块
const path = require('path')
const themePath = path.join(__dirname, './src/theme.less')

module.exports = {
  publicPath: './',
  css: {
    loaderOptions: {
      less: {
        modifyVars: {
          // 直接覆盖变量
          // 'nav-bar-background-color': 'orange'
          // 或者可以通过 less 文件覆盖(文件路径为绝对路径)
          //  ../    ./    theme.less
          // 从盘符开始的路径,叫做绝对路径   C:\\Users\liulongbin\\theme.less
          hack: `true; @import "${themePath}";`
        }
      }
    }
  }
}module.exports = {
  publicPath: './',   //使打包后的文件可以在file运行不只是在http运行
  css: {
    loaderOptions: {
      less: {
        modifyVars: {
          // 直接覆盖变量
          // 'nav-bar-background-color': 'orange'
          // 或者可以通过 less 文件覆盖(文件路径为绝对路径)
          //  ../    ./    theme.less
          // 从盘符开始的路径,叫做绝对路径   C:\\Users\liulongbin\\theme.less
          hack: `true; @import "${themePath}";`
        }
      }
    }
  }
}
// 在 theme.less 文件中,覆盖 Vant 官方的 less 变量值
@blue: #007bff;

// 覆盖 Navbar 的 less 样式
@nav-bar-background-color: @blue;
@nav-bar-title-text-color: #fff;

你可能感兴趣的:(vue,笔记,vue)