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 webpack@5.42.1 webpack-cli@4.7.2-D

安装webpack-dev-server

运行如下的命令,即可在项目中安装此插件:npm install webpack-dev-server@3.11.2-D

安装babel-loader 相关的包

运行如下的命令安装对应的依赖包:npm i babel-loader@8.2.2@babel/core@7.14.6@babel/plugin-proposal-decorators@7.14.5-D

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">这是一个 div</div>
    
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-bordered边框 table-hover放上变色  table-striped不同行变色-->
    <table class="table table-bordered table-hover table-striped">
      <thead>
        <th>索引</th>
        <th>Id</th>
        <th>姓名</th>
      </thead>
      <tbody>
        <!-- 官方建议:只要用到了 v-for 指令,那么一定要绑定一个 :key 属性 -->
        <!-- 而且,尽量把 id 作为 key 的值 -->
        <!-- 官方对 key 的值类型,是有要求的:字符串或数字类型 -->
        <!-- key 的值是千万不能重复的,否则会终端报错:Duplicate keys detected -->
        <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)
    }
  }
}
/*<MyCount :init="9"></MyCount>在组件使用者传参时在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” 名称的主要应用场景:结合 <keep-alive> 标签实现组件缓存功能;以及在调试工具中看到组件的 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 值为空字符串,则这条路由规则,叫做“默认子路由”
//用默认路由要把<router-link to="/about/tab1">tab1</router-link>改为<router-link to="/about">tab1</router-link>
        { 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 值为空字符串,则这条路由规则,叫做“默认子路由”
//用默认路由要把<router-link to="/about/tab1">tab1</router-link>改为<router-link to="/about">tab1</router-link>
        { 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;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue 2.0项目源码是指使用Vue 2.0框架开发的项目的源代码。Vue是一套用于构建用户界面的渐进式框架,它的核心库只关注视图层,易于集成到现有项目中。Vue 2.0相对于1.x版本进行了许多重大改进和优化,使得开发者能够更高效地构建复杂的Web应用程序。 Vue 2.0项目源码通常包含以下几个主要部分: 1. 组件:Vue框架基于组件化的思想,项目中的组件是独立的、可复用的功能单元。每个组件通常由一个Vue实例构成,包含HTML模板、JavaScript逻辑和样式。组件之间可以通过props和events进行数据的传递和通信。 2. 路由:Vue Router是Vue框架的官方路由库,它可以实现SPA(单页面应用)的路由功能。在项目源码中,通常会定义路由表,包含每个URL路径对应的组件和相应的逻辑处理。 3. 状态管理:Vuex是Vue框架的官方状态管理库,用于管理应用的状态和数据流。在项目源码中,可能会使用Vuex来管理全局的数据、状态和业务逻辑。 4. 构建工具:Vue CLI是官方的快速原型开发工具,可以帮助开发者创建、构建和打包Vue项目。在项目源码中,可能会使用Vue CLI来生成项目骨架、配置开发环境和打包最终的生产代码。 5. 网络请求:在项目中,通常会使用第三方的HTTP库(如axios、fetch等)来实现与后端API的交互,进行数据的获取和提交。 以上是关于Vue 2.0项目源码的一些基本概述。项目源码的具体实现细节和结构会根据项目的规模和需求而有所不同,但基于Vue 2.0框架进行开发的项目通常都会遵循以上的主要特点和模块。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值