Vue相关

Vue01

WEB开发的发展史

  • 早期: 利用原生 DOM 实现

  • 中期: jQuery

    • 利用代码封装技巧, 简化了 DOM 操作的代码

    • 评价: 旧时代的王者

  • 现在: Vue

    • 自动化: DOM操作全自动 -- 自动查找到元素然后进行操作

Vue目前有3个版本, 从2014年开始

官网: API — Vue.js 中文文档

  • Vue1: 已淘汰

  • Vue2: 目前主流, 但是过渡期

  • Vue3: 未来的主流, 市场份额逐步深入

Vue以其简单著称, 特别适合新手

  • jQuery理念: Write Less, do More 写的少, 做的多

  • Vue理念: 不写, 也能做 -- 不会DOM 依然能开发

Vue的开发方式: 分两种

  • 脚本方式: 同jQuery, 适合入门阶段

  • 脚手架方式: 工程化,类似 express, 适合实际开发

插值语法

<!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>
</head>
​
<body>
  <!-- Vue为了简化DOM开发, 提供了大量的新语法, 需要背 -->
  <div id="app">
    <!-- DOM操作的难点之一: 如何选中要操作的元素 -->
    <!-- Vue提供了新的语法 {{}} 插值语法, 可以直观的把数据放在页面中 -->
    <div>亲爱的主人: {{name}}</div>
    <!-- {{}} : 相当于 模板字符串的 ${}, 把HTML当成是模板字符串来用就行 -->
    <!-- 可以在HTML中, 随意书写 JS 代码 -->
    <p>9*9 = {{9 * 9}}</p>
    <p>主人的资产: {{money}}</p>
  </div>
​
  <!-- 脚本分两种: 开发版vue.js 和 压缩版vue.min.js -->
  <!-- 开发版会提供更多的报错, 适合开发阶段使用 -->
  <script src="./vendor/vue.js"></script>
  <script>
    // Vue: 就是 vue.js 脚本中提供的构造函数
    // Vue会自动创建出一个对象, 来自动操作DOM元素 -- 钢铁侠的战甲
    // 固定的一些配置项, 需要设定
​
    // el : element元素, 值是 id选择器, 代表使用 Vue 管理的元素
    // 此时, 生成的 Vue对象, 就专为 id=app 的元素而服务
    const v = new Vue({
      el: '#app',
      // data: 数据,  给 el 关联的DOM元素使用的各种数据
      // data中的数据随便写
​
      // data属性中的元素, 会混入到 创建出来的vue对象里
      data: { money: 900000, name: "家乐" }
​
      // 我把 钱包(400块)交给了 家乐, 最终会混入到 家乐的口袋里
    })
​
    // 自动化: 数据变化时, 相关的DOM元素会 自动 更新
    // Vue框架核心竞争力: 数据驱动DOM的变化
​
    console.log(v)
  </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>
</head>
​
<body>
  <!-- vue代码只有在 vue管理的元素中使用, 才能生效 -->
  <div id="box">
    <div>姓名:{{name}}</div>
    <div>phone: {{phone}}</div>
  </div>
​
​
  <script src="./vendor/vue.js"></script>
  <script>
    // 1. 创建1个 Vue 对象, 来管理 id=box 的元素
    new Vue({
      el: '#box',
      data: { name: "楠姐", phone: '10086' }
    })
    // 2. 为元素提供一些数据, 例如 phone=10086, name=楠姐
    // 3. 把手机号 和 名字显示到 box 里 
  </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>
</head>
​
<body>
  <!-- 
    1个标签由: 标签名 + 内容 + 属性 组成
   -->
  <a href="http://www.baidu.com" id="a1" class="danger" title="百度一下">Baidu</a>
​
  <div id="app">
    <!-- 标签内容, 用 {{}} 来书写JS代码 -->
​
    <!-- vue1语法: v-bind:属性名="JS代码" -->
    <!-- vue2语法: :属性名="JS代码" -->
    <a v-bind:href="h" :title="c" :id="a" :class="b">{{d}}</a>
​
    <!-- 属性名不带: 就是HTML的原生语法, 值是字符串 -->
    <button id="8+8">11</button>
    <!-- 属性名带: 是vue的语法, 其中的值作为JS代码运行 -->
    <button :id="8+8">22</button>
  </div>
​
  <script src="./vendor/vue.js"></script>
  <script>
    new Vue({
      el: '#app', // 管理 id=app 的元素, el是固定的
      data: {
        a: 101,
        b: 'success',
        c: 'Tmooc',
        d: 'Welcome !',
        h: 'http://tmooc.cn'
      }
    })
  </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>
</head>
​
<body>
  <div id="box">
    <!-- :src="JS"  :代表属性的值是 JS代码 -->
    <img :src="a" alt="">
  </div>
​
  <script src="./vendor/vue.js"></script>
  <script>
    // 创建一个Vue对象, 管理 id=box 的元素
    // 添加数据项: a = http://www.codeboy.com:9999/img/index/banner1.png
    // 把图片地址 放到 img 里, 让图片显示出来
    new Vue({
      el: '#box',
      data: {
        a: 'http://www.codeboy.com:9999/img/index/banner1.png'
      }
    })
  </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>
</head>
​
<body>
  <div id="app">
    <!-- Vue1 语法: v-on:事件名="方法名" -->
    <button v-on:click="a">点我</button>
    <!-- Vue2 语法: @事件名="方法名" -->
    <button @click="a">来呀!</button>
    <button @click="b">再碰下试试!</button>
  </div>
​
  <script src="./vendor/vue.js"></script>
  <script>
    new Vue({
      el: '#app',
      // data: 专门存放数据的属性
      // methods: 专门存放方法的属性
      methods: {
        a: function () { alert('别碰我!') },
        // 语法糖: 可以省略 : function
        b() { alert('找揍啊你!') }
      }
    })
  </script>
​
</body>
​
</html>

事件的this

<!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>事件的this</title>
</head>
​
<body>
  <div id="app">
    <button @click="a">打我</button>
  </div>
​
  <script src="./vendor/vue.js"></script>
  <script>
    const v = new Vue({
      el: '#app',
      // function的this是 运行时所在对象
​
      // methods中的元素, 最终会混入到 创建的Vue实例对象中
      // 所以 函数中的this 就是 Vue 实例对象
      methods: {
        // 错误答案: this是methods
        // 家乐买了个锤子,  那么 用锤子打人 就一定是家乐吗??  -- 不, 要看使用者
        a() {
          console.log('this:', this)
          console.log(this == v);
        }
      }
    })
​
    console.log(v);
  </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>
</head>
​
<body>
  <div id="app">
    <button @click="add">{{num}}</button>
​
    <!-- 完全可以在 HTML中直接写JS 更方便 -->
    <button @click="num++">{{num}}</button>
  </div>
​
  <script src="./vendor/vue.js"></script>
  <script>
    const v = new Vue({
      el: '#app',
      // data中的元素, 最终会存储在 实例对象里  v.num
      data: { num: 1 },
      // methods中的元素, 最终会存储在 实例对象里  v.add
      methods: {
        add() {
          // 解:
          // 因为: this == v
          // 所以: v.num == this.num
          this.num++
        }
      }
    })
​
    console.log(v)
  </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>
</head>
​
<body>
  <div id="app">
    <!-- 当 num 是1, 则按钮不可用 disabled 属性 -->
    <!-- disabled: 不可用, 表单元素带有的属性 -->
    <!-- disabled=true 代表不可用状态生效, 即按钮不可用,  num是1  1==1 true -->
    <!-- disabled=false 代表 不不可用  就是可用 -->
    <button @click="num--" :disabled="num==1">-</button>
    <span>{{num}}</span>
    <!-- 当数量是10, num==10 为true, 不可用 -->
    <button @click="num++" :disabled="num==10">+</button>
    <p>单价: {{price}}</p>
    <!-- Vue的核心竞争力: 数据驱动DOM变化, 数据变化后, 凡是相关的DOM元素都自动变 -->
    <p>总价: {{price * num}}</p>
  </div>
​
  <script src="./vendor/vue.js"></script>
  <script>
    new Vue({ el: '#app', data: { num: 5, price: 2000 } })
  </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>
</head>

<body>
  <div id="app">
    <h2>选择你的英雄: {{hero}}</h2>
    <button @click="c">蔡文姬</button>
    <button @click="c">瑶</button>
    <button @click="c">猪八戒</button>
    <button @click="c">李白</button>
  </div>

  <script src="./vendor/vue.js"></script>
  <script>
    // 凡是页面上变化的东西, 必然和 数据挂钩
    new Vue({
      el: "#app",
      data: { hero: '待定' },
      methods: {
        c(e) {
          // 事件参数: 凡是事件触发的方法, 都会自带事件参数
          // alert("选择英雄!")
          console.log(e)
          // 修改数据项 = 触发事件的元素上的内容, 页面自然会更新
          this.hero = e.target.innerHTML
        }
      }
    })
  </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>
</head>

<body>
  <div id="app">
    <h2>{{price}}</h2>
    <button data-price="4399" @click="choose">8G+256G</button>
    <button data-price="4899" @click="choose">12G+256G</button>
    <button data-price="4499" @click="choose">8G+512G</button>
    <button data-price="5299" @click="choose">12G+512G</button>
  </div>

  <script src="./vendor/vue.js"></script>
  <script>
    // Vue的理念: 数据驱动 
    // 数据变化 驱动 页面的变化,  页面上变化的内容肯定绑定了数据
    new Vue({
      el: '#app',
      data: { price: 4399 },
      methods: {
        choose(e) {
          // 事件 e.target 是触发事件的DOM元素
          console.log(e)
          // dataset: 是存放自定义属性 data- 的
          this.price = e.target.dataset.price
        }
      }
    })
  </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>
</head>

<body>
  <!-- 
    事件传参分两种方式
    - 1. 利用DOM 的 自定义属性实现,  @事件名="方法名" ,方法的默认第一参 就是事件

    - 2. 抛弃DOM,使用函数传参方式  @事件名="方法名(值, 值...)"
          -- 由于指定了实参后, 则默认的事件参数会消失, 必须用关键词 $event 来传递
   -->

  <div id="app">
    <h2>{{price}}</h2>
    <!-- 为不会自定义传参的用户, 提供了 函数传参语法 -->
    <!-- 自定义传参之后, 默认的事件传参会消失 -->
    <!-- 可以利用 $event 关键词, 来显式传递事件参数 -->
    <button @click="choose(4399, $event)">8G+256G</button>
    <button @click="choose(4899, $event)">12G+256G</button>
    <button @click="choose(4499, $event)">8G+512G</button>
    <button @click="choose(5299, $event)">12G+512G</button>
  </div>

  <script src="./vendor/vue.js"></script>
  <script>
    new Vue({
      el: '#app',
      data: { price: 4399 },
      methods: {
        // Vue作者希望打造的效果: 让不会DOM的人也能书写
        // 自定义属性, 事件参数  都属于DOM知识点

        // 会DOM可以有更多做法, 不会DOM 也能实现 -- 灵活
        choose(p, e) {
          this.price = p
          console.log(e);
        }
      }
    })
  </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>

  <style>
    .ok {
      padding: 10px;
      background-color: green;
      color: white;
    }

    .err {
      padding: 10px;
      background-color: red;
      color: white;
    }
  </style>
</head>

<body>
  <!-- 样式分两类:  style  和 class -->
  <div id="app">
    <!-- 需求: 点击文字, 让字体变大 -->
    <!-- 让属性变化, 代码是JS, 必须用 : 改造 -->

    <!-- :style="{属性名: 值}"   值是对象类型 -->
    <!-- CSS的属性名, 常见 - 间隔, 例如 border-color  font-size  padding-top
    在JS的对象类型里, 属性名不允许中划线   { a-b: 1 }
    解决方案: 1.改小驼峰 fontSize   2.用字符串 'font-size'

    -->
    <div style="color:red" :style="{fontSize: size+'px'}" @click="size++">Hello: {{size}}</div>

    <div style="color:red" :style="{'font-size': size+'px'}" @click="size++">Hello: {{size}}</div>

    <hr>
    <!-- 希望: size是偶数, 就让 ok生效,  奇数就让 err 生效 -->

    <!-- :class="{类名: 布尔值}"  如果值是true就生效, false则不生效 -->
    <div :class="{ok: size%2==0, err: size%2==1}">成龙</div>
  </div>

  <script src="./vendor/vue.js"></script>
  <script>
    // Vue特点: 数据驱动, 凡是页面上会变化的 一定会绑定数据
    new Vue({
      el: '#app',
      data: { size: 17 }
    })
  </script>
</body>

</html>

总结

  • 跨域: 在网页中, 通过AJAX 发送请求到服务器获取数据, 如果不是同一个服务器就会跨域

    • 解决方案

      • CORS: 在服务器上添加白名单

      • 代理: 适用于服务器代码不可修改的场景

      • JSONP

  • Vue: 一款自动化的框架, 目前国内最火

    • 插值语法: {{}} 在双标签的内容中, 使用JS代码

    • 属性绑定

      • v-bind: vue1

      • :属性名: vue2

    • 事件

      • v-on: vue1

      • @事件名 vue2

      • 事件中的this: 指向vue对象

      • 事件参数: 默认情况下, 事件触发时的第一参是事件参数

      • 如果是自定义传参的场景: 需要用 $event 来传递事件参数

    • 固定的配置项:

      • data: 用于存储数据, 最终会混入到 vue 实例对象

      • el: vue对象管理的元素

      • methods: 用于存储方法, 最终会混入到 vue 实例对象

      • 样式

        • style: :style="{样式名:值, 样式名:值...}"

        • class: :class="{类名: true/false}" true就生效 false就失效

Vue02

Vue -- 尤雨溪

Vue.js 介绍 — Vue.js 中文文档

对比:

  • 曾经的王者: jQuery, 通过封装来简化DOM操作的代码

  • 现在的顶流: Vue, 自动化思想, 让用户不用写DOM代码

版本:

  • Vue1 -- 目前已经淘汰

  • Vue2 -- 目前主流, 但是逐步在过渡到 vue3

  • Vue3 -- 未来的趋势

开发的方式:

  • 脚本方式: 适合入门阶段

  • 脚手架方式: 适合实际开发

提前下载

百度网盘 请输入提取码 提取码: 6666

脚手架

脚手架是一类软件的总称: 用来生成完善的项目包, 类似一键安装

类似于:

  • 原始方式: 先安装电脑系统, 然后自己找软件个性化安装

  • 脚手架: 一键安装, 电脑系统 + 一套常见的软件

  • 先安装脚手架软件

    • 前提: node版本在 12 ~ 16 查看node -v

  • npm需要中国镜像 -- 查看jQuery03 的文档

  • 执行全局安装命令: npm i -g @vue/cli

    • 安装完毕后, 通过 vue --version vue -V 来查看版本号

  • 利用脚手架来生成项目包

    不是一定要自己生成, 可以使用别人生成的包, 例如 百度网盘上的 vue-pro

    • 在你要生成项目的目录下, 执行命令: vue create vue-pro

      • 范式: vue create 包名 , 即 vue-pro 是自定义的包名, 可以随便起

    • 个性化选项

    • 选择 vue2 版本

    • 直接回车

    • 成功

    • 如果安装了 Git 软件, 可能会有额外的提示, 不用管

    • 如果生错了, 则到文件夹里 删除掉, 然后重新生

    • 无法自己生成, 用百度网盘提供的包 或者 跟其他能生的同学要 都可以!

编程方式

要求使用 VSCODE 软件 直接打开生成的项目包: vscode 专门为此项目包服务, 会有各种优化

自带服务器

脚手架生成的项目包中, 自带服务器

运行项目包中的服务器

  • 在项目包目录下运行 cmd

  • 命令: npm run serve

开启时 偶尔会卡住, 在命令行上按 回车 ,大概率能解决

端口号如果被占用, 会自动改成别的, 例如 8080 -> 8081 -> 8082...

VSCode提供傻瓜式开启方式

插件

项目包分析

public: 静态资源文件夹

  • favicon.ico : 标签栏上的图标

  • index.html: 固定名称的文件, 作为服务器默认的首页

src: 专门存放代码

  • main.js

App.vue

key

关于key:

  • 有可能重复的 不可以

  • 有可能变化的 不可以, 例如 序号, 除非实在找不到唯一标识, 可以凑合用

  • 例如: 数据库查询出来的元素的 id 可以

回顾

指令: 由 vue 提供的一些属性, 称为指令

  • v-text: innerText

  • v-html: innerHTML

  • v-show: 利用css的display:none 来隐藏元素

  • v-if: 通过删除元素 来隐藏元素

    • v-else, v-else-if 配合 v-if使用

  • v-for: 遍历

  • v-on: 事件绑定, @

  • v-bind: 属性绑定 :

  • v-pre: 原样显示代码, {{}}

  • v-once: 一次性, 把初始值显示后, 后续不会更新

脚手架: 一类软件的总称, 可以一键自动生成项目包

  • 先安装 -> 再生成

  • vue项目包: 自带服务器 + 带有很多模块能直接用

key:

  • 面试总问有什么用

  • 给数组遍历生成的DOM元素 加唯一标识

  • 数组发生增删时, 要生成的新元素 如果 标识和旧元素一样, 会直接复用

作业

作业1

  • 做个数组 放 3个字符串, 如下

  • 循环显示, 用 span 标签

  • 书写样式

  • 添加 current 变量, 记录当前项 默认1

  • 点击后 切换current 的值 和 动态样式

作业2:

效果参考 jQuery01 的标签栏

提示: 利用 v-show 和 序号, 切换元素的隐藏和显示

作业3 (难)

  • 用表格 table 展示

  • 数量为1 则 减法按钮不可用, 最大为 max 属性规定的值, 不可用

    • 提示, 需要用序号找到对应元素, 来修改其值

  • 总价随着变化

var products = [
    {pname:"香蕉", price:9, count:10, max:40},
    {pname:"苹果", price:15, count:1, max:30},
    {pname:"鸭梨", price:19, count:10, max:20},
    {pname:"荔枝", price:29, count:34, max:60},
    {pname:"葡萄", price:39, count:12, max:50},
] 

 

Vue03

复习

指令: 是 Vue 提供的一套 标签的属性, 都是以 v- 开头

  • v-text: 本质就是 innerText

  • v-html: 本质就是 innerHTML

  • v-show: 利用 css 的display:none 实现DOM元素的隐藏

  • v-if: 通过删除元素实现隐藏效果

    • v-else, v-else-if

  • v-for: for循环

    • 写法分两种:

      • v-for="值 in/of 数组/数字"

      • v-for="(值, 序号) in/of 数组"

  • v-on: 事件, 实际使用时, 用 @

  • v-bind: 属性, 实际使用时, 用 :

  • v-pre: 原样输出代码, 特指: {{}}

  • v-once: 一次性渲染, 后续不会因为数据变化而更新

特殊属性:

  • key: 搭配 for 循环使用, 提升数组内容增删后的重绘效率

    • 唯一标识, 重新绘制时, 相同唯一标识的元素会直接复用

脚手架:

  • 脚手架是一类软件的总称, 例如我们使用的 vue脚手架, 作用是一键生成完整的项目包

  • 自带服务器: 项目包中自带服务器, 使用 npm run serve 命令启动

    • 启动后, 根据提示 到浏览器的地址栏输入对应网址来访问

作业1

<template>
  <!-- 习惯把文件名 作为 根div 的class名使用 -->
  <div class="app">
    <!-- vscode强烈推荐 循环生成的DOM元素 带有唯一标识key -->
    <!-- key的值应该唯一的, 实在找不到 用 序号也行 -->
​
    <!-- 标签中, 用for循环生成的变量, 只能在标签里面用: 理解成作用域 -->
    <!-- 原生 for(let value of 数组) -->
    <span
      v-for="(spec, index) in specs"
      :key="spec"
      @click="current = index"
      :class="{ active: current == index }"
      >{{ spec }}</span
    >
    <!-- 动态class  :class="{类名: true/false}"  true生效,false无效
    假设: current 是 1,  则 index是1的项目  1 == 1  为真, 样式生效
     -->
​
    <hr />
    <h4>current:{{ current }}</h4>
  </div>
</template>
​
<script>
export default {
  // data: 专门用于提供数据的属性, 在脚手架中, 要求是函数类型--复用
  // data中的变量, 是全局的, 可以到处用
  // 标签中 v-for 生成的变量, 只能在标签里用
  data() {
    return {
      // 凡是页面上变化的, 必然和数据存在联系
      current: 1, //默认序号1高亮
      specs: [
        "[流光银] i5-7200u 4G 128 500G",
        "[溢彩金] i7-7500u 8G 128 1T",
        "[元気粉] i5-7200u 4G 128 500G",
      ],
    };
  },
};
</script>
​
<style lang="scss" scoped>
.app {
  user-select: none;
​
  span {
    display: inline-block;
    width: 350px;
    line-height: 35px;
    text-align: center;
    border-radius: 3px;
    border: 2px solid gray;
    color: gray;
    margin: 4px;
​
    &.active {
      border-color: orange;
      color: orange;
    }
  }
}
</style>

作业2

<template>
  <div class="app">
    <div id="tabs">
      <div>
        <span
          v-for="(item, index) in items"
          :key="index"
          @click="x = index"
          :class="{ active: x == index }"
        >
          {{ item.title }}
        </span>
      </div>
      <div>
        <!-- of 和 in 效果一样, 挑你喜欢的 -->
        <!-- i : 变量名随便起 -->
        <!-- 值如果是对象, 可以采用解构语法  {属性名, 属性名} = 对象 -->
​
        <!-- v-show="true/false"  false隐藏  true显示  -->
        <!-- 显示的条件  当前序号 == 要显示的元素序号 一样 -->
        <div v-for="({ desc }, i) of items" :key="i" v-show="i == x">
          {{ desc }}
        </div>
      </div>
    </div>
  </div>
</template>
​
<script>
export default {
  data() {
    return {
      x: 0, // 存储当前序号
      items: [
        { title: "商品介绍", desc: "商品介绍..." },
        { title: "规格与包装", desc: "规格与包装..." },
        { title: "售后保障", desc: "售后保障..." },
        { title: "家乐购物会", desc: "家乐购物会..." },
      ],
    };
  },
};
</script>
​
<style lang="scss" scoped>
#tabs {
  user-select: none;
  background-color: #eee;
  width: 600px;
​
  > div:last-child > div {
    height: 300px;
    padding: 5px;
    border: 1px solid gray;
  }
​
  > div:first-child {
    display: flex;
    span {
      padding: 10px 20px;
​
      &.active {
        background-color: orange;
        color: white;
      }
    }
  }
}
</style>

作业3 - 计算属性

<template>
  <div class="app">
    <table>
      <thead>
        <tr>
          <th>序号</th>
          <th>名称</th>
          <th>单价</th>
          <th>数量</th>
          <th>小计</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="({ pname, price, count, max }, i) in products" :key="i">
          <td>{{ i + 1 }}</td>
          <td>{{ pname }}</td>
          <td>{{ price }}</td>
          <td>
            <button @click="products[i].count--" :disabled="count == 1">
              -
            </button>
            <span class="count">{{ count }}</span>
            <!-- 修改数组中的元素的值: 必须通过数组一步步查到里面的值 -->
            <button @click="products[i].count++" :disabled="count == max">
              +
            </button>
          </td>
          <td>{{ price * count }}</td>
        </tr>
      </tbody>
      <tfoot>
        <tr>
          <!-- 计算属性: 适合用于书写不带参数的函数, 在使用时不需要() 会自动触发 -->
          <!-- methods中的, 需要()触发 -->
          <!-- computed中的, 不需要()触发 -->
          <!-- 理论上 计算属性更简单, 实战中 你的代码你做主! -->
          <td colspan="6">总价格: {{ total() }} -- {{ sum }}</td>
​
          <!-- @click="事件"   这个事件必须是 通过点击才能触发的 -->
          <!-- 事件不能写在计算属性里, 以为不应该自动触发, 必须是点击触发 -->
        </tr>
      </tfoot>
    </table>
  </div>
</template>
​
<script>
export default {
  // 计算属性的作用: 函数不用() 也能触发, 适合不带参的函数
  // 固定属性: 存放在这里的方法, 使用时不用(), 会自动触发
  computed: {
    sum() {
      // let s = 0;
      // this.products.forEach((p) => (s += p.price * p.count));
      // return s;
​
      // JS高级的 数组 高阶函数 reduce
      return this.products.reduce((s, p) => s + p.price * p.count, 0);
    },
  },
​
  methods: {
    total() {
      // 遍历products, 把每个商品的 单价x数量  累加到一起
      // 最佳的做法是 reduce
      let s = 0;
      this.products.forEach((p) => (s += p.price * p.count));
​
      return s;
    },
  },
  data() {
    return {
      products: [
        { pname: "香蕉", price: 9, count: 10, max: 40 },
        { pname: "苹果", price: 15, count: 1, max: 30 },
        { pname: "鸭梨", price: 19, count: 10, max: 20 },
        { pname: "荔枝", price: 29, count: 34, max: 60 },
        { pname: "葡萄", price: 39, count: 12, max: 50 },
      ],
    };
  },
};
</script>
​
<style lang="scss" scoped>
table {
  border-collapse: collapse;
  user-select: none;
​
  .count {
    display: inline-block;
    margin: 0 5px;
    min-width: 35px;
    text-align: center;
  }
​
  thead {
    background-color: #eee;
  }
​
  td,
  th {
    border: 1px solid gray;
    padding: 3px 30px;
  }
}
</style>

双向数据绑定

<template>
  <div>
    <!-- 双向数据绑定 -->
​
    <!-- {{}}: 是在双标签内容 <tag>{{}}</tag>  -->
    <!-- :属性名="JS代码"  : 让 引号中的内容变为JS代码 -->
​
    <!-- 数据传递方向1: 从 data 中, 传递到 DOM元素里 -->
    <!-- 数据传递方向2: 当用户修改表单元素时, 自动更新绑定的数据项 -->
    <!-- 这套操作就叫: 双向数据绑定 -->
    <input type="text" :value="kw" @input="kwChanged" />
    <!-- 相当于 onclick = function kwChanged() {} -->
​
    <!-- 指令: v-model, 自动完成双向绑定 -->
    <br />
    <input type="text" v-model="kw" />
​
    <!-- @input: 输入框实时变更的事件 -->
    <!-- e: 事件参数, 包含事件的各种信息 -->
    <!-- target: 触发事件的元素 -->
    <p>kw: {{ kw }}</p>
​
    <!-- Form表单元素有一个特色: 按钮 单选框 多选框 输入框 等, 都能和用户交互 -->
​
    <input type="text" v-model="x" />
​
    <div>{{ x }}</div>
    <button>{{ x }}</button>
  </div>
</template>
​
<script>
export default {
  methods: {
    kwChanged(e) {
      console.log(e.target.value);
      this.kw = e.target.value;
    },
  },
​
  data() {
    return {
      kw: "随便起",
      x: "111",
    };
  },
};
</script>
​
<style lang="scss" scoped>
</style>

练习

<template>
  <div>
    <!-- v-model: 输入框变化会实时更新到 uname 变量,  uname变量就存储的是输入框值 -->
    <input type="text" placeholder="请输入用户名" v-model="uname" />
    <br />
    <input type="password" placeholder="请输入您的密码" v-model="upwd" />
    <br />
    <button @click="login">登录</button>
  </div>
</template>
​
<script>
export default {
  data() {
    return {
      uname: "",
      upwd: "",
    };
  },
  methods: {
    login() {
      // 需求: 希望读取到两个输入框中的值, 然后发送登录请求
      // 原生DOM: 先 querySelector 找到输入框元素, 然后用 .value 读取
      // const uname = document.querySelector("input").value;
      // console.log(uname);
      console.log(this.uname, this.upwd);
    },
  },
};
</script>
​
<style lang="scss" scoped>
</style>

单选框

<template>
  <div>
    <!-- 有几个选项的数据类型, 在数据库中最好用 0 1 2 来代表 -->
    <span>男</span>
    <input type="radio" name="sex" value="1" v-model="x" />
    <br />
    <span>女</span>
    <input type="radio" name="sex" value="0" v-model="x" />
​
    <br />
    <span>保密</span>
    <input type="radio" name="sex" value="2" v-model="x" />
​
    <h3>x:{{ x }}</h3>
    <!-- 需求: x是0, 显示 girl   x是1 显示 boy  x是2 显示 secret -->
    <p>{{ ["girl", "boy", "secret"][x] }}</p>
​
    <!-- 常见操作: 数据库中 存储选项类型的变量, 用序号 0 1 2 3 ... -->
    <!-- 前端通常搭配 数组, 认为创造巧合 -->
​
    <!-- 数组[序号] -->
    <!-- ["girl", "boy", "secret"][0] -->
  </div>
</template>
​
<script>
export default {
  data() {
    return {
      x: 0,
    };
  },
};
</script>
​
<style lang="scss" scoped>
</style>

勾选框

<template>
  <div>
    <!-- 勾选 -->
    <input type="checkbox" v-model="agree" />
    <span>雷佳乐先生, 您愿意娶 如花 吗? {{ agree }}</span>
​
    <div v-show="agree">我愿意!</div>
​
    <input type="checkbox" @change="chb" />
  </div>
</template>
​
<script>
export default {
  methods: {
    chb(e) {
      // 勾选框的值 是 checked属性, 是开发HTML的人 设定的
      // 尤雨溪 在写v-model的时候, 自动判断所在的元素是什么类型, 如果是checkbox 就读取 checked 属性,  如果是 input 就读取value属性
      console.log(e.target.checked);
    },
  },
​
  data() {
    return {
      agree: false,
    };
  },
};
</script>
​
<style lang="scss" scoped>
</style>

多选框

<template>
  <div>
    <!-- 多选框 -->
    <h3>家乐: 选择你的爱好 {{ skills }}</h3>
    <ul>
      <li>
        <!-- 勾选框有两种作用, 单独使用代表是否勾选 -- checked属性就行 -->
        <!-- 勾选框做多选: 则需要设置 value, 代表其对应的值 -->
        <input type="checkbox" value="唱" v-model="skills" />
        <span>唱</span>
      </li>
      <li>
        <!-- 尤雨溪: 自动判断参数类型, 是数组就加入, 是boolean, 就是true/false -->
        <input type="checkbox" value="跳" v-model="skills" />
        <span>跳</span>
      </li>
      <li>
        <input type="checkbox" value="rap" v-model="skills" />
        <span>rap</span>
      </li>
      <li>
        <input type="checkbox" value="篮球" v-model="skills" />
        <span>篮球</span>
      </li>
    </ul>
  </div>
</template>
​
<script>
export default {
  data() {
    return {
      // 多选操作, 值是多个, 要存储在数组里
      skills: [],
    };
  },
};
</script>
​
<style lang="scss" scoped>
</style>

下拉选框

<template>
  <div>
    <!-- 下拉选框 -->
    <h3>请家乐先生选择您的座驾: {{ car }}</h3>
    <select v-model="car">
      <option value="0">思域</option>
      <option value="1">凯迪拉克</option>
      <option value="2">奥迪</option>
      <option value="3">宝马</option>
      <option value="4">奔驰</option>
      <option value="5">保时捷</option>
      <option value="6">法拉利</option>
    </select>
  </div>
</template>

<script>
export default {
  data() {
    return {
      car: 5,
    };
  },
};
</script>

<style lang="scss" scoped>
</style>

购物车

<template>
  <div>
    <table>
      <thead>
        <tr>
          <th>
            <!-- 当 勾选状态变化时, 触发 change 事件 -->
            <!-- 勾选状态属性: checked , 其值是计算属性计算而来的-->
            <input type="checkbox" @change="checkAll" :checked="chb_all" />
            <span>全选</span>
          </th>
          <th>商品名</th>
          <th>单价</th>
          <th>数量</th>
          <th>小计</th>
          <th>操作</th>
        </tr>
      </thead>

      <tbody>
        <tr v-for="({ name, price, num }, i) in products" :key="i">
          <td>
            <!-- v-model: 双向数据绑定, 绑定的一定是 data 中的数据项 -->
            <input type="checkbox" v-model="products[i].checked" />
          </td>
          <td>{{ name }}</td>
          <td>{{ price }}</td>
          <td>
            <button @click="products[i].num--" :disabled="num == 1">-</button>
            <!-- 双向绑定, 必须绑定 data 中的 -->
            <input type="text" v-model="products[i].num" />
            <button @click="products[i].num++">+</button>
          </td>
          <td>{{ price * num }}</td>
          <td>
            <!-- 数组.splice(i, n): 删除序号i开始 的 n个元素 -->
            <!-- ['小马', '小雷', '小蔡'].splice(0, 1) 删除序号0开始的1个元素 -->
            <button @click="products.splice(i, 1)">删除</button>
            <button @click="removeP(i)">删除</button>
          </td>
        </tr>
      </tbody>

      <tfoot>
        <tr>
          <td colspan="6">总价格: {{ sum }}</td>
        </tr>
      </tfoot>
    </table>
  </div>
</template>

<script>
export default {
  methods: {
    //全选
    checkAll(e) {
      // e.target 看 DOM03 事件冒泡部分
      console.log(e.target.checked); // 全选按钮的勾选状态 true/false

      // 遍历数组, 把每个元素的 checked 都变成和 全选一样
      this.products.forEach((p) => (p.checked = e.target.checked));
      // 关于参数p: 必须复习 JS高级的 高阶函数函数部分

      var obj = { num: 4 };
      var b = obj.num;
      b = 10; //不会影响 obj.num,  值传递
      //修改obj的num属性, 必须是 obj.num = 10

      // 类似: 给班级每个同学 发个雪糕
      // 同学们.forEach( 每个同学 => 每个同学.手里 = 雪糕)
    },

    removeP(i) {
      // this代表当前vue对象, 详见 vue01 的 this 部分讲解
      this.products.splice(i, 1);
    },
  },

  // 计算属性
  computed: {
    // 全选状态:  数组中的每一个(every)元素 都是勾选, 则是全选
    chb_all() {
      // every: 检测数组中, 每个元素的 checked 属性, 都是真的 结果才是true
      return this.products.every((p) => p.checked);
      // JS高级 的 高阶函数, 大概在 Day04
    },

    sum() {
      // return this.products.reduce((a, p) => a + p.price * p.num, 0);

      let s = 0;
      // 只累加勾选的, 即 checked 是 true 的;  checked是false , 就不会累加
      // 因为数学运算中, true->1  false->0   任何数字x0 都是0
      this.products.forEach((p) => (s += p.price * p.num * p.checked));
      return s;
    },
  },

  data() {
    return {
      products: [
        { name: "兰蔻小黑瓶", price: 1080, num: 5, checked: true },
        { name: "欧莱雅套装", price: 339, num: 1, checked: false },
        { name: "SK-II", price: 1540, num: 10, checked: true },
        { name: "香奈儿5号", price: 568, num: 3, checked: true },
        { name: "海洋之谜", price: 4080, num: 1, checked: false },
        { name: "six god", price: 12, num: 20, checked: false },
      ],
    };
  },
};
</script>

<style lang="scss" scoped>
table {
  user-select: none;
  border-collapse: collapse;

  thead {
    background-color: #eee;
  }

  td,
  th {
    border: 1px solid gray;
    padding: 5px 30px;
    text-align: center;
  }

  //属性选择器
  [type="text"] {
    width: 50px;
    text-align: center;
  }
}
</style>

作业

  • 购物车属于一个 综合性的练习, 值得你做很多次才能掌握

    • 关联 JS高级 和 DOM 相关的各种知识

  • 待办事项

     

Vue04

复习

配置项

  • data: 用于存储数据, 这里的数据可以全局使用

    • 当数据发生变化时, 会自动更新相关DOM元素

  • methods: 用于存储各种方法

    • 方法中的this 是 当前vue 对象 -- 详见 vue01 的 this

    • 关于方法的 自定义传参默认 -- 复习 Day01的方法部分

  • computed: 计算属性

    • 特点: 在使用时不需要()自动触发

    • 注意: 不适合事件触发的函数, 因为事件触发的函数必然是 手动触发

指令: 就是 vue 提供的一套属性, 都是 v- 开头

  • v-text: innerText

  • v-html: innerHTML

  • v-show: 就是css的display:none

  • v-if: 删除DOM元素实现隐藏

    • v-else, v-else-if

  • v-for: 循环遍历

    • v-for="值 in/of 数组"

    • v-for="(值, 序号) in/of 数组"

    • 支持遍历数字 v-for="值 in 数字"

  • v-on: 事件绑定

    • 原生:onclick, vue是 v-on:click

    • 简化: @

  • v-bind: 属性绑定

    • :属性名="JS代码"

  • v-model: 双向数据绑定

    • 方向1: 传统 把data中的数据传递到 DOM元素里

    • 方向2: 必须是Form表单元素 才能和用户交互, 用户修改DOM元素,可以同步更新数据项

  • v-pre: 原样显示代码, 特指{{}}

  • v-once: 一次性, 首次渲染之后, 后续数据变化不会刷新DOM

特殊属性:

  • key: 唯一标识, 当数组发生增删操作时, 提高重新渲染的效率, 复用已存在的元素

作业

<template>
  <div>
    <!-- 回车  keyup   编号13  名字enter -->
    <!-- 事件修饰符 @事件.修饰符     大概:  家乐.单身狗 -->
    <!-- @keyup.enter : 按键抬起.回车 -->
    <input
      @keyup.enter="
        todoList.push(kw);
        kw = '';
      "
      type="text"
      placeholder="请输入待办事项"
      v-model="kw"
    />
    <button
      :disabled="kw == ''"
      @click="
        todoList.push(kw);
        kw = '';
      "
    >
      确定
    </button>
​
    <ul>
      <!-- HTML代码, 有HTML的代码规范, 作者为了大家容易理解, 所以设计的像JS -->
      <li v-for="(todo, i) in todoList" :key="i">
        <span>{{ todo }}</span>
        <button @click="todoList.splice(i, 1)">删除</button>
        <button @click="removeTodo(i)">删除</button>
      </li>
    </ul>
​
    <!-- 数组长度 == 0, 代表数组里没有数据了 -->
    <div v-show="todoList.length == 0" class="warning">暂无待办事项</div>
  </div>
</template>
​
<script>
export default {
  methods: {
    // 方法参数: 接收来自HTML的值
    removeTodo(i) {
      // JS代码的语法要求: 用 this 从当前对象取值
      this.todoList.splice(i, 1);
    },
  },
  data() {
    return {
      todoList: ["吃饭", "睡觉", "打亮亮"],
      // 输入框的值: 通过双向绑定捆绑数据
      kw: "",
    };
  },
};
</script>
​
<style lang="scss" scoped>
.warning {
  background-color: orange;
  width: 200px;
  text-align: center;
  line-height: 40px;
  color: white;
  border-radius: 4px;
}
</style>
​

自定义指令

<template>
  <div>
    <!-- 高级操作: 自定义指令 -- 要求使用者必须熟练使用 原生DOM -->
    <!-- vue官方默认提供了很多指令  例如 v-text  v-show  v-html... -->
    <!-- 作为使用者, 可以根据自身的项目需要, 来自定义指令 -->
    <ul>
      <!-- v-xx="JS代码"  指令的值是JS代码 -->
      <li v-color="'purple'">凯凯</li>
      <li v-color="'blue'">小马</li>
      <li v-color="'orange'">小婷</li>
​
      <!-- 仿写系统的 v-text, 让值原样显示到 标签里 -->
      <li v-textH="'<h1>Hello World</h1>'"></li>
​
      <!-- 自定义指令 v-green, 作用是让DOM元素变绿 -->
      <!-- v- 是指令的固定前缀 -->
      <li v-green>亮亮</li>
      <li v-red>家乐</li>
    </ul>
  </div>
</template>
​
<script>
export default {
  // directive: 指令
  directives: {
    textH(sui, bian) {
      sui.innerText = bian.value;
    },
​
    // v-color="'purple'"
    color(el, bindings) {
      // el: 参数1, 代表当前元素
      // bindings: 参数2, 绑定的值
      console.log("bindings:", bindings);
​
      el.style.color = bindings.value; //value是什么,详见后台打印
    },
    // v-red
    red(suibian) {
      suibian.style.color = "red";
    },
​
    // v-green:  v- 固定前缀   green 名称
    green(el) {
      // 参数1: 指令所在的元素
      console.log("el:", el);
      console.dir(el);
      el.style.color = "green";
    },
  },
};
</script>
​
<style lang="scss" scoped></style>
​

指令

<template>
  <div>
    <!-- 指令的生命周期 -->
    <!-- 生命周期: 拟人的说法 丛生到死经历的过程 -->
    <!-- 例如: 备孕->怀孕->待产->出生->学习中..->学习完->快死了->死了 -->
    <!-- 指令: 创建->绑定在DOM元素->寄生在DOM上 -> 销毁 -->
    <input type="text" />
    <br />
    <!-- v-focus: 调用DOM元素的 focus 方法, 让其获得焦点 -->
    <input type="text" v-focus />
    <br />
    <input type="text" />
  </div>
</template>
​
<script>
export default {
  directives: {
    // DOM元素: 先创建 -> 设置各种属性 -> 添加到页面上显示
    // 详见 : 案例.html
    focus: {
      // 自选生命周期, 来触发函数
      inserted(el) {
        // insert: 插入, 代表 DOM元素 插入到 页面上显示
        el.focus();
        // 指令所在的元素, 添加到页面上的时候, 触发 焦点
      },
    },
​
    // 下方写法: 是指令的语法糖写法, 其触发时机 是 DOM元素创建和更新时
    // focus(el) {
    //   console.dir(el); // 找 原型 -> 原型 -> 原型 里, 有focus方法,
    //   // 作用: 让DOM元素获取焦点
    //   el.focus();
    // },
  },
};
</script>
​
<style lang="scss" scoped>
</style>

案例

<!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>
</head>
​
<body>
  <div id="box"> </div>
​
  <script>
    const inp = document.createElement('input')
    inp.id = 'b1';
    inp.type = 'text'
    inp.className = 'danger';
    inp.innerText = "xxx"
    // inp.focus() //位置1
​
    // 以上的代码, 都是在内存中构建元素 -- 在肚子里
​
    box.appendChild(inp) // 添加到页面上-- 降世
    inp.focus() //位置2
  </script>
</body>
​
</html>

ref

<template>
  <div>
    <input type="text" />
    <br />
    <!-- ref: 可以把一个变量 绑定在元素上 -->
    <input type="text" ref="inp" />
    <br />
    <input type="text" />
    <br />
    <button @click="doFocus">获得焦点</button>
    <p ref="suibian">Hello World!</p>
  </div>
</template>
​
<script>
export default {
  methods: {
    doFocus() {
      // 让第二个输入框获得焦点, 即 调用其 focus 方法
      // const inp = document.querySelectorAll("input")[1];
      // inp.focus();
​
      console.log("this:", this);
      console.log("$ref:", this.$refs);
      // 使用 ref 属性, 可以把变量绑定在 DOM元素上, 变量存储在 $refs 中
      this.$refs.inp.focus();
​
      this.$refs.suibian.style.color = "red";
    },
  },
};
</script>
​
<style lang="scss" scoped>
</style>

练习

<template>
  <div>
    <button @click="doSome">表达心意</button>
​
    <!-- 本质上自动完成下方两行: -->
    <!-- const x = document.querySelector('p') -->
    <!-- $refs['map'] = x -->
    <p ref="map">小马666</p>
    <!-- 把变量和元素绑定在一起, 然后存储在 $refs 里 -->
  </div>
</template>
​
<script>
export default {
  methods: {
    doSome() {
      this.$refs.map.style.color = "green";
    },
  },
};
</script>
​
<style lang="scss" scoped>
</style>

组件

<template>
  <div>
    <!-- 组件: component  -->
    <!-- 含义: 组成页面的零件 -->
    <!-- 作用: 拆分 + 复用, 把一个大型的网页拆分成 零碎的部件 -->
    <!-- 在开发领域的重要程度相当于: 活字印刷术 -->
    <!-- 把大型网页中的内容, 拆分成独立的一个个模块, 最后再组合到一起 -->
    <!-- components: 专门放组件的文件夹 -->
​
    <!-- 使用: 单标签必须闭合  <标签名 /> -->
    <one-com />
​
    <!-- 作者为了满足不同人的癖好: 提供了各种语法, 挑你喜欢的 -->
    <one-com></one-com>
    <OneCom />
    <OneCom></OneCom>
  </div>
</template>
​
<script>
// 使用组件分3步:  引入  -> 注册  -> 使用
​
// 模块导入语法(旧)
// const OneCom = require("./components/OneCom.vue")
​
// 模块导入语法(新)
import OneCom from "./components/OneCom.vue";
​
export default {
  // 把 OneCom 组件, 注册到当前 App.vue 里
  components: { OneCom },
};
</script>
​
<style lang="scss" scoped>
// 使用组件时, 通常需要为 外来的组件 进行定位操作: 调整位置
​
// 习惯: 给组件最外层div 一个 class, 与组件名相关
// 使用组件时, 就能猜到组件的class名
.one-com {
  border-radius: 3px;
  margin-top: 10px;
​
  &:last-child {
    position: fixed;
    bottom: 0;
  }
}
</style>
<template>
  <!-- 组件名要求 大驼峰 格式 -->
  <!-- 习惯给根div, 添加class 名称与文件名相同 -->
  <div class="one-com">
    <h1>你好, 我的第一个组件</h1>
  </div>
</template>
​
<script>
export default {};
</script>
​
<style lang="scss" scoped>
.one-com {
  padding: 10px;
  background-color: aqua;
}
</style>

练习

<template>
  <div>
    <!-- 以为组件过于常用,所以VSCode软件提供了人性化的 自动导入操作 -->
    <!-- 先写 <  然后写组件名, 通过代码提示可以自动生成 -->
    <!-- 但是: 小概率会生成失败, 还是需要查看下代码的 -->
    <two-com />
    <two-com />
    <two-com />
  </div>
</template>

<script>
import TwoCom from "./components/TwoCom.vue";
export default {
  components: { TwoCom },
};
</script>

<style lang="scss" scoped>
</style>
<template>
  <div class="two-com">我是古家乐, 是兄弟就来砍我!</div>
</template>

<script>
export default {};
</script>

<style lang="scss" scoped>
.two-com {
  background-color: orange;
  color: white;
  padding: 10px;
  border-radius: 4px;
}
</style>

组件传参

<template>
  <div>
    <!-- 希望组件复用时, 能够接收参数, 产生变化 -->
    <!-- 知识点: 组件传参 -- 和函数传参套路相同! -->
    <!-- 函数传参的套路: 形参 实参  -->
    <three-com who="Moon" where="艾欧尼亚" />
    <three-com who="马鑫鑫" where="祖安" />
    <three-com who="文豪" where="白鹿书院" />
  </div>
</template>

<script>
import ThreeCom from "./components/ThreeCom.vue";
export default {
  components: { ThreeCom },
};
</script>

<style lang="scss" scoped>
</style>
<template>
  <div class="three-com">
    <div>我是{{ who }}, 今晚在 {{ where }} 等我!</div>
  </div>
</template>

<script>
export default {
  // props: 用于声明组件接收的参数, 即 形参
  // 参数用字符串类型标识
  props: ["who", "where"],
};
</script>

<style lang="scss" scoped>
.three-com {
  border: 2px solid blue;
  color: blue;
  padding: 10px;
  border-radius: 4px;
  margin-top: 10px;
}
</style>

练习

<template>
  <div>
    <four-com
      name="家乐"
      where="南京雷氏一族"
      :age="age"
      hobby="唱, 跳, rap, 篮球"
    />
    <!-- age=""  是HTML的语法, 值是 字符串 -->
    <!-- :age="" 是vue的语法, 值是JS代码 -->
  </div>
</template>

<script>
import FourCom from "./components/FourCom.vue";
export default {
  components: { FourCom },
  data() {
    return {
      age: 30,
    };
  },
};
</script>

<style lang="scss" scoped>
</style>
<template>
  <div class="four-com">
    <p>我是{{ name }}, 来自{{ where }}, 今年{{ age }}, 爱好:{{ hobby }}</p>
  </div>
</template>

<script>
export default {
  props: ["name", "where", "age", "hobby"],
};
</script>

<style lang="scss" scoped>
// 通常: 组件中适合添加组件自身的样式
// App.vue: 给组件加定位
.four-com {
  border: 2px solid blue;
  color: blue;
  padding: 10px;
  margin-top: 10px;
}
</style>

data是函数

<template>
  <div>
    <five-com />
    <five-com />
    <five-com />
  </div>
</template>

<script>
import FiveCom from "./components/FiveCom.vue";
export default {
  components: { FiveCom },
};
</script>

<style lang="scss" scoped>
</style>
<template>
  <div class="five-com">
    <button @click="num++">{{ num }}</button>
  </div>
</template>

<script>
export default {
  // 面试题: data为什么是函数类型?
  // 当组件被复用的时候, 每次调用data函数, 都会返回一个 全新的对象
  // 所以: 不同组件之间的数据 不会互相影响!
  data() {
    return {
      num: 10,
    };
  },
};
</script>

<style lang="scss" scoped>
.five-com {
  border: 1px dashed blue;
}
</style>

插槽

<template>
  <div>
    <!-- 组件有两种传参方式 -->
    <!-- 1. 属性传参: 利用props声明 属性, 来接收参数 -->
    <!-- 2. 内容传参: 利用slot属性来接收参数 -->
    <six-com>
      <div>两只黄鹂树上叫</div>
      <div>一行白鹭天上飞</div>
      <p>--雷佳乐大作</p>

      <!-- 命名插槽: 有 vue1 2 两种语法 -->
      <!-- vue1语法: 把内容显示在指定名称的 slot 中 -->
      <div slot="jiale">
        雷佳乐, 华夏南京雷氏家族 天才少年, 斗气三段. 天下第一武道会 亚军;
        <br />
        9岁静脉堵塞, 家族废材, 被退婚..
      </div>
    </six-com>
  </div>
</template>

<script>
import SixCom from "./components/SixCom.vue";
export default {
  components: { SixCom },
};
</script>

<style lang="scss" scoped>
</style>
<template>
  <div class="six-com">
    <div>
      <!-- slot 插槽: 一个占位符, 在实际使用时, 会被替换成 组件标签的内容 -->
      <!-- 默认插槽: 代表 组件标签使用时的内容 -->
      <slot />
    </div>
    <div>
      <!-- 命名插槽 -->
      <slot name="jiale" />
    </div>
    <div>
      <slot name="xinxin" />
    </div>

    <!-- 插槽:  一个组件 -- 化妆盒  -->
    <!-- 已经放置了很多插槽, 固定的摆放顺序 -- 布局完善 -->
    <!-- 就差往里面放化妆品 -->

    <!-- 
      插槽的作用:
      - 1个组件负责把 布局全写好, 每个空位都是一个插槽

      - 使用时: 只需要向 指定的插槽放东西就可以
     -->

    <!-- 命名插槽 -->
    <!-- <化妆盒>
      面膜, 柔肤水, 面霜...

      <div slot="香水">
        香奈儿, 迪奥...
      </div>

      <div slot="眼霜">SKII, 海洋之谜...</div>
    </化妆盒> -->
  </div>
</template>

<script>
export default {};
</script>

<style lang="scss" scoped>
.six-com > div {
  height: 200px;
  background-color: yellowgreen;
  margin-top: 10px;
}
</style>

化妆盒

<template>
  <div>
    <hua-zhuang-he>
      <!-- 直接书写在组件标签内容中的, 会出现在 组件的 默认插槽 slot 里 -->
      <div>日霜, 晚霜, 眼影, 润肤露, 水乳, 洗面奶</div>

      <!-- vue1 的语法 -->
      <div slot="香水">香奈儿, dior, 古龙</div>

      <!-- vue2 的语法, 必须搭配 vue 提供的 template 标签 -->
      <!-- template: 一个虚拟的容器, 不参与css样式 -->
      <template v-slot:口红>
        Gucci, dior, 纪梵希, 雅诗兰黛, 兰蔻, 香奈儿
      </template>

      <!-- vue2 的 语法糖, 类似 @ : , 此处用 # -->
      <template #首饰> 周大福, 翡翠, DR, 老凤祥 </template>
    </hua-zhuang-he>
  </div>
</template>

<script>
import HuaZhuangHe from "./components/HuaZhuangHe.vue";
export default {
  components: { HuaZhuangHe },
};
</script>

<style lang="scss" scoped>
</style>
<template>
  <div class="hua-zhuang-he">
    <!-- 放普通物品 -->
    <div>
      <slot />
    </div>

    <div>
      <h3>香水</h3>
      <slot name="香水" />
    </div>

    <div>
      <h3>口红</h3>
      <slot name="口红" />
    </div>

    <div>
      <h3>首饰</h3>
      <slot name="首饰" />
    </div>
  </div>
</template>

<script>
export default {};
</script>

<style lang="scss" scoped>
.hua-zhuang-he {
  > div {
    display: inline-block;
    width: 300px;
    min-height: 300px;
    background-color: aquamarine;
    padding: 10px;
    border-radius: 4px;
    margin: 10px;
  }
}
</style>

axios

<template>
  <div>
    <button @click="suibian">获取数据</button>
    <!-- data的初始值是null, 需要点击按钮后才能请求到实际的值 -->
    <!-- 所以: null.pageCount 会报错, 以为不能对null读取属性-->

    <!-- 用 v-if 判断: true就添加元素  false就删除元素 -->
    <!-- 在 if 判断中, null -> false, 此时 刚开始 这个p标签不会加载 -->
    <!-- 懒加载机制: DOM元素没用之前,不加载, 可以节省首次页面显示时的DOM数据, 可以加速页面的首次显示 -->

    <!-- 整体一起判断, 但是不要用div -- div会影响css布局 -->
    <!-- vue提供了一个虚拟容器, 不影响css布局 -->
    <template v-if="data">
      <p>页数: {{ data.pageCount }}</p>
      <p>当前页: {{ data.pageNum }}</p>
      <p>每页数量: {{ data.pageSize }}</p>
      <p>总数量: {{ data.totalRecord }}</p>
    </template>
  </div>
</template>

<script>
// axios使用时, 分两种方式
// 1. 单独使用 - 在哪个组件中用, 就在哪里引入
import axios from "axios";
// 2. 全局使用
export default {
  data() {
    return {
      // 提前准备一个变量: 用来存储请求得到值
      data: null, // data:数据
    };
  },

  methods: {
    suibian() {
      const url = "http://www.codeboy.com:9999/mfresh/data/news_select.php";
      // 在 Promise 的语法里, then代表成功  catch代表失败
      axios.get(url).then((res) => {
        // 参数res: 是 response的缩写, 代表 服务器响应的数据
        console.log(res);
        // 返回的数据 存储在 res.data 属性里

        // 请求的数据 如何显示到页面上??
        // -- 页面上显示的数据 应该存在哪里?  -- data属性
        this.data = res.data;
        console.log(this);
      });
    },
  },
};
</script>

<style lang="scss" scoped>
</style>

axios

网络请求有多实现方式

  • 原生的AJAX

  • jQuery提供的封装: $.get(地址, data=>{})

    • jQuery采用 回调函数 来实现 异步请求封装 -- 存在回调地狱风险

  • axios: 另一款网络请求的第三方, 采用 Promise 实现了封装操作, 规避了 回调地狱 风险

安装模块

在项目包下, 执行安装命令: npm i axios vue-axios

无法安装的, 直接用 百度网盘 提供的项目包, 已经安装完毕

回顾

自定义指令:

  • v- 开头的, 书写在 directives 属性里

    • 难: 如果要在显示出来以后, 再触发, 需要生命周期:inserted

  • ref: 快速把一个变量绑定在元素上

    • 存储在 $refs 的属性里

  • 组件: 重中之重

    • 拆分代码到外部存放 -- 实现复用, 模块化

    • 传参: 利用 props 属性声明参数

    • 插槽: slot 在组件中提前布局, 然后让用户通过插槽向 组件中传递数据

  • axios

    • 安装 npm i axios vue-axios

    • 使用

      • 单独使用: axios.get(地址).then(响应的数据 => {})

      • 数据必须存在 data 中, 才能在页面上用

      • HTML中使用时, 要用 v-if 判断, 实现一种懒加载的效果 -- 数据有的时候再显示

作业

接口网站: Index of /

根据效果图, 实现对应页面

图片有防盗链, 需要在 public/index.html 里添加防盗链的代码

<meta name='referrer' content='no-referrer' />

Vue05

复习

选项/数据

  • data

    • 存储在data中的数据, 可以全局到处使用

    • 面试问题: data为什么是函数?

      组件被复用的时候, 每次调用data函数来获取数据, 数据是通过函数临时生成的, 多个组件之间的数据互相不会影响

      export default {
          data(){
              return {
                  ...
              }
          }
      }
  • props

    • 组件中, 通过 props 属性来声明参数, 用于接收外来的传参

      export default {
          props: ['name', 'age']
      }
      ​
      <组件 name='xx'  age='xxx' />
  • computed: 计算属性

    • 特点: 方法不用() 就能自动触发

    • 适用场景: 你希望能够自动触发的位置

      • 不适用的场景: 事件触发的方法, 都应该是手动触发, 方法放在 methods 里

  • methods

    • 存放用于手动触发的方法, 常见于 事件绑定

指令: 就是vue提供的一套 标签的属性, 外观上:v- 开头

  • v-text: innerText

  • v-html: innerHTML

  • v-show: 利用css 的 display:none 实现隐藏

  • v-if: 利用 删除DOM 来实现隐藏

    • v-else, v-else-if

  • v-for: 遍历

    • v-for="变量 of/in 数组"

    • v-for="(变量, 序号) in/of 数组"

    • v-for="变量 in 数字"

  • v-on: 事件, @

    • 事件修饰符 @事件名.修饰符 例如 enter 代表回车

  • v-bind: 属性 :

  • v-model: 双向数据绑定

    • 方向1: data中的数据, 绑定传递到 DOM元素里

    • 方向2: DOM元素发生变化时, 同步修改绑定的数据 -- Form表单元素

  • v-slot: 插槽

    • 用于获取 组件使用时, 其标签中的内容部分

    • 作用: 组件进行布局, 用插槽做占位符. 使用时再把实际的数据传入

  • v-pre : 原样显示代码, 例如 {{}}

  • v-once: 一次性渲染, 后续就算数据变化 也不会更新

特殊属性:

  • key: 唯一标识, 用于通过数组 for循环生成的DOM元素

    • 效果: 提高数组内容变更后的 重新渲染的效率

  • ref

    • 快速把1个变量 和 元素绑定在一起, 变量会存储在 $refs 属性中

高级操作:

  • directives: 自定义指令

    • 自定义指令, 有两种书写格式

      directives:{
          // 在组件 创建和 更新时触发
          指令名(元素, 相关参数){},
          // 指定 在指令的哪个周期触发相关操作
          指令名:{
              inserted(el, 相关参数){}
          }
      }

axios:

  • 网络请求模块, 利用Promise进行封装得到的. 没有回调地狱风险

  • 使用时, 需要提前安装: npm i axios vue-axios

    axios.get(接口地址).then(响应值 => {})

关于项目包的启动

  • 有些同学: 电脑里有很多个 vue-pro 项目包, 经常出现: 正在操作的项目包 和 你当前运行的项目包不是同一个

  • 推荐采用傻瓜式启动

  • VSCODE 的代码有任何报错, 都是你插件的问题 -- 尝试删除插件

练习

<template>
  <div>
    <button @click="getData">获取数据</button>
    <br />
    <br />
​
    <!-- 网络请求数据分两个阶段 -->
    <!-- 阶段1: 请求前 -- 数据值是null -->
    <!-- 阶段2: 请求完毕 -- 数据值才是真正的数据 -->
​
    <!-- 懒加载: 最大化节省性能消耗 -- 数据没有之前 先不加载DOM -->
​
    <!-- template: 1个虚拟的容器, 对CSS布局无影响 -->
    <template v-if="data">
      <div class="item" v-for="ar in data.data.archives" :key="ar.aid">
        <!-- 在 public/index.html 添加 去除防盗链 -->
        <img :src="ar.pic" alt="" />
        <span>{{ ar.title }}</span>
        <div>
          <span>{{ ar.stat.view }}</span>
          <span>{{ ar.stat.danmaku }}</span>
        </div>
      </div>
    </template>
  </div>
</template>
​
<script>
// axios有两种使用方式:
// 1. 单独引入: 哪个组件用 就在哪个组件引入
import axios from "axios";
​
export default {
  // data: 用于存储本地数据
  data() {
    return {
      // 属性名可以随便起, 但是 见名知意 是基本要求
      data: null,
    };
  },
  // 点击事件触发方法 --  methods 和 computed 都能写方法
  // methods: 适合手动触发
  methods: {
    getData() {
      const url = "http://api.xin88.top/bilibili/news.json";
​
      axios.get(url).then((res) => {
        console.log(res);
        // 把请求的数据存储在本地
        this.data = res.data;
      });
    },
  },
};
</script>
​
<style lang="scss" scoped>
.item {
  display: inline-block;
  user-select: none;
  width: 200px;
  margin: 4px;
​
  > img {
    width: 100%;
    border-radius: 4px;
  }
​
  > span {
    // 超出部分: 隐藏
    overflow: hidden;
    // 超出宽度部分: 不换行
    white-space: nowrap;
    // 文本-超出部分: 用...表示
    text-overflow: ellipsis;
    width: 100%;
    display: inline-block;
  }
​
  > div > span {
    display: inline-block;
    width: 50%;
    color: gray;
    font-size: 0.8em;
  }
}
</style>

练习

<template>
  <div>
    <button @click="getData">获取数据</button>
    <br />
    <br />
    <template v-if="data">
      <!-- 随便写 in 看返回值,服务器决定 -->
      <!-- v-for中声明的变量, 只能在对应标签中使用! -->
      <div class="cell" v-for="s in data.data.season" :key="s.season_id">
        <img :src="s.new_ep.cover" alt="" />
        <div>
          <span>{{ s.title }}</span>
          <div>
            <span>{{ s.new_ep.index_show }}</span>
            <span>{{ s.stat.view }}万播放 · {{ s.stat.danmaku }}万弹幕</span>
          </div>
        </div>
      </div>
    </template>
  </div>
</template>
​
<script>
// 常见错误:
// 1. axios没引入
// 2. 没打印
// 3. data 和 methods 级别错误
​
import axios from "axios";
​
export default {
  data() {
    return {
      data: null,
    };
  },
  // data 和 methods: 同级别关系 -- 兄弟
  methods: {
    getData() {
      const url = "http://api.xin88.top/bilibili/recommend.json";
​
      axios.get(url).then((res) => {
        this.data = res.data;
        console.log(res);
      });
    },
  },
};
</script>
​
<style lang="scss" scoped>
.cell {
  display: flex;
​
  > img {
    width: 150px;
    border-radius: 3px;
    margin: 6px;
  }
​
  > div {
    display: flex;
    flex-direction: column;
    padding: 7px;
    justify-content: space-between;
​
    > div {
      color: gray;
      display: flex;
      flex-direction: column;
      font-size: 0.9em;
    }
  }
}
</style>

过滤器

<template>
  <div>
    <!-- 过滤器: filter -->
    <!-- 服务器返回的数据 很有可能 与我们想要展示的数据不一样: 采用过滤器处理 -->
    <!-- 服务器返回性别 0 1 2,  我们想要显示: 女 男 保密 -->
​
    <!-- 语法 {{ 值 | 过滤器 }}   | 是 shift+回车上面的按钮  -->
    <p>{{ 0 | sex }}</p>
    <p>{{ 1 | sex }}</p>
    <p>{{ 2 | sex }}</p>
​
    <!-- 练习:  显示出 12万   9.9万 这种效果 -->
    <p>{{ 120000 | wan }}</p>
    <p>{{ 99000 | wan }}</p>
    <p>{{ 145555 | wan }}</p>
    <!-- 不过万 就不转 -->
    <p>{{ 5000 | wan }}</p>
  </div>
</template>
​
<script>
export default {
  // filters: 过滤器们,  用于声明过滤器
  filters: {
    wan(v) {
      return v > 10000 ? (v / 10000).toFixed(1) + "万" : v;
    },
​
    // {{ 值 | 过滤器}}
    sex(v) {
      // 值 会作为参数, 传递给过滤器
      // 返回值 就是过滤器的结果
      return ["女", "男", "保密"][v]; // v是序号, 下标取值
    },
  },
};
</script>
​
<style lang="scss" scoped>
</style>

生命周期

<template>
  <div>
    <!-- 生命周期: 组件从 创建 到 出生 .. 销毁整套过程 -->
    <button @click="show = true">添加</button>
    <button @click="show = false">移除</button>
    <!-- hello-world组件: 从生 到 死 到底经历了什么? -->
    <hello-world v-if="show" />
  </div>
</template>
​
<script>
import HelloWorld from "./components/HelloWorld.vue";
export default {
  components: { HelloWorld },
  data() {
    return {
      show: false, // 配合 v-if 使用
    };
  },
};
</script>
​
<style lang="scss" scoped>
</style>
hello-word

<template>
  <div>
    <h1>Hello World!</h1>
    <button @click="num++">{{ num }}</button>
  </div>
</template>

<script>
// 组件的生命周期  和  人的一生相同 -- 是一种常识
// 面试几乎必考题 --- 必须背
//
// 如果希望组件在显示时, 自动发送请求 -- mounted 周期

export default {
  data() {
    return {
      num: 1,
    };
  },

  // 钩子(hook)函数: 一种特殊的函数, 会在特殊的事件发生时, 自动触发
  // 1个组件的生命  和 人的生命历程十分相似
  // 备孕->怀孕->待产->出生->开始学习->学习完毕->快死了->死了
  // 准备创建->创建完毕-准备显示到页面->显示完毕->准备更新->更新完毕->准备销毁->销毁完毕

  // 每个重要的时间节点, 都会自动触发一个固定名称的函数 -- 称为 钩子函数
  // before: 在...之前
  beforeCreate() {
    console.log("beforeCreate: 创建前 -- 备孕");
  },
  created() {
    console.log("created: 创建完毕 -- 怀孕");
  },
  beforeMount() {
    console.log("beforeMount: 准备出生 -- 将要添加到页面上");
  },
  mounted() {
    console.log("mounted: 出生 -- 显示到页面");
  },

  beforeUpdate() {
    console.log("beforeUpdate: 将要更新");
  },
  updated() {
    console.log("updated: 更新完毕");
  },

  beforeDestroy() {
    console.log("beforeDestroy: 将要销毁 -- 快死了");
  },
  destroyed() {
    console.log("destroyed: 销毁完毕 -- 死了");
  },
};
</script>

<style lang="scss" scoped>
</style>

周期的应用

<template>
  <div>
    <!-- <button @click="getData">获取数据</button> -->
    <!-- <br /> -->
    <!-- <br /> -->
    <template v-if="data">
      <!-- 随便写 in 看返回值,服务器决定 -->
      <!-- v-for中声明的变量, 只能在对应标签中使用! -->
      <div class="cell" v-for="s in data.data.season" :key="s.season_id">
        <img :src="s.new_ep.cover" alt="" />
        <div>
          <span>{{ s.title }}</span>
          <div>
            <span>{{ s.new_ep.index_show }}</span>
            <span>{{ s.stat.view }}万播放 · {{ s.stat.danmaku }}万弹幕</span>
          </div>
        </div>
      </div>
    </template>
  </div>
</template>

<script>
// 常见错误:
// 1. axios没引入
// 2. 没打印
// 3. data 和 methods 级别错误

import axios from "axios";

export default {
  data() {
    return {
      data: null,
    };
  },

  // data 和 methods: 同级别关系 -- 兄弟
  methods: {
    getData() {
      const url = "http://api.xin88.top/bilibili/recommend.json";

      axios.get(url).then((res) => {
        this.data = res.data;
        console.log(res);
      });
    },
  },

  // 其他周期使用极少
  // mounted: 最常用, 代表组件显示在页面上时
  mounted() {
    this.getData();
  },
};
</script>

<style lang="scss" scoped>
.cell {
  display: flex;

  > img {
    width: 150px;
    border-radius: 3px;
    margin: 6px;
  }

  > div {
    display: flex;
    flex-direction: column;
    padding: 7px;
    justify-content: space-between;

    > div {
      color: gray;
      display: flex;
      flex-direction: column;
      font-size: 0.9em;
    }
  }
}
</style>

电影

<template>
  <div>
    <template v-if="data">
      <div class="cell" v-for="sub in data.subjects" :key="sub.id">
        <img :src="sub.cover" alt="" />
        <div>
          <span v-show="sub.is_new">新</span>
          <span>{{ sub.title }}</span>
          <span>{{ sub.rate }}</span>
        </div>
      </div>
    </template>
  </div>
</template>

<script>
// axios使用方式分两种:  单独引入 和 全局引入
// 单独引入: 在每个使用网络操作的组件中, 进行import
// import axios from "axios";
// 全局引入: 在 main.js 中, 把 axios 注入到 Vue的原型上

export default {
  data() {
    return {
      data: null,
    };
  },
  // 组件显示到页面上时, 立刻发请求
  mounted() {
    console.log(this); // 看后台, 找到其中的axios
    this.getData();
  },
  methods: {
    getData() {
      const url = "http://api.xin88.top/douban/movies.json";
      // this: 当前的vue对象, 因为我们把 axios 存放在vue对象上, 所以可以直接读取
      this.axios.get(url).then((res) => {
        console.log(res);
        this.data = res.data;
      });
    },
  },
};
</script>

<style lang="scss" scoped>
.cell {
  display: inline-block;
  margin: 10px;
  width: 170px;

  > img {
    width: 100%;
    height: 250px;
  }

  > div {
    display: flex;
    justify-content: center;
    align-items: center;

    > span:first-child {
      padding: 4px;
      background-color: aquamarine;
    }
  }
}
</style>

英雄联盟

<template>
  <div>
    <!-- ref: 把1个变量 和 元素绑定在一起, 会存储在 $refs 里 -->
    <audio ref="au" />

    <template v-if="data">
      <div v-for="h in data.hero" :key="h.heroId" class="cell">
        <div>
          <button @click="playBan(h.banAudio)">ban</button>
          <button @click="playSelect(h.selectAudio)">select</button>
        </div>
        <!-- <img
          :src="`https://game.gtimg.cn/images/lol/act/img/champion/${h.alias}.png`"
          alt=""
        /> -->

        <!-- 利用字符串替换,得到图片地址 -->
        <img :src="data.baseURL.replace('{alias}', h.alias)" alt="" />
        <span>{{ h.name }}</span>
      </div>
    </template>
  </div>
</template>

<script>
export default {
  data() {
    return {
      data: null,
    };
  },
  mounted() {
    this.getData();
  },
  methods: {
    playBan(ban_src) {
      // 此处要使用 audio 元素
      const au = this.$refs.au;
      au.src = ban_src; //音频地址
      au.play(); // 开始播放
    },
    playSelect(src) {
      const au = this.$refs.au;
      au.src = src;
      au.play();
    },

    getData() {
      const url = "https://api.xin88.top/game/heros.json";
      this.axios.get(url).then((res) => {
        console.log(res);

        this.data = res.data;
      });
    },
  },
};
</script>

<style lang="scss" scoped>
.cell {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  margin: 10px;
  user-select: none;
}
</style>

关于拼接

<!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>Document</title>
</head>

<body>
  <script>
    var n = '马鑫鑫'

    var words = `吃饭, 睡觉, 打${n}.png`
    console.log(words);

    var words = '吃饭, 睡觉, 打name.png'
    words = words.replace('name', n)
    console.log(words);

    //  :属性名="JS代码"
    //  :属性名="`模板字符串`"
  </script>
</body>

</html>

POST请求

<template>
  <div>
    <input type="text" placeholder="请输入您的用户名" v-model="uname" />
    <br />
    <!-- 回车登录效果 -->
    <input
      @keyup.enter="login"
      type="password"
      placeholder="请输入您的密码"
      v-model="upwd"
    />
    <br />
    <button @click="login">登录</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      uname: "", // v-model: 双向绑定
      upwd: "",
    };
  },
  methods: {
    login() {
      console.log(this.uname, this.upwd);

      const url = "http://www.codeboy.com:9999/data/user/login.php";

      // GET请求: 适合用于从服务器获取数据的接口 -- 服务器决定的
      // POST请求: 合适向服务器传输数据, 例如图片上传  -- 服务器决定

      // 使用上的差异: 在于参数的传递方式
      // get:   路径?参数=值&参数=值
      // post:  路径 和 参数 分开传递,  axios没有对参数进行处理, 只能放字符串类型

      // 服务器规定的参数名: uname 和 upwd
      const params = `uname=${this.uname}&upwd=${this.upwd}`;

      // post(路径, 参数)
      this.axios.post(url, params).then((res) => {
        console.log(res);
        // 账号:  doudou     123465
      });
    },
    // 全局引入分两种方式:
    // 简单粗暴: 直接操作原型, 但是不受到vue承认, 所以没有代码提示
    // 优雅规矩(推荐): 利用 vue-axios 模块来 按照规矩进行引入
    // 安装命令: npm i axios vue-axios
    // 到main.js
    getData() {
      // this.axios.get().then((res) => {
      //   console.log(res);
      // });
    },
  },
};
</script>

<style lang="scss" scoped>
</style>

内容回顾

过滤器

  • filters, 可以对数据进行处理之后, 返回处理结果

  • {{ 值 | 过滤器 }}

生命周期

  • 一个常识性的知识, 面试高频问题

  • 一个组件从出生到显示 -> 更新->销毁 这个过程中触发的各种周期函数

    • 创建前

    • 创建完毕

    • 显示前 -- 挂载 mounted

    • 显示完毕

    • 更新前

    • 更新完毕

    • 销毁前

    • 销毁完毕

axios的引入方式

  • 单独引入: 适合使用网络请求的组件较少的场景

  • 全局引入:

    • 简单粗暴: 直接操作原型 Vue.prototype.axios = axios

      • 缺点: 使用时 没有代码提示

    • 优雅规矩: 用 use方法 和 vue-axios 模块配合

      • Vue.use(VueAxios, axios)

      • 优点: 有代码提示

  • POST请求:axios.post(路径, 参数字符串)

Vue06

复习

选项/数据

  • data: 存放共享的数据 -- 全局使用

  • props: 声明组件的形参 -- 组件接收外来传参

  • computed: 计算属性 -- 使用时自动触发, 不需要()

  • methods: 方法们 -- 需要手动触发; 常见在 事件相关

选项/资源

  • directives: 指令们

    • 自定义指令 v- 开头

  • filters: 过滤器们

    • {{ 值 | 过滤器 }}

  • components: 组件们

    • 把页面上的一部分, 抽离成独立的 .vue 文件, 实现复用

选项/生命周期钩子

  • beforeCreate: 创建之前

  • created: 创建完毕

  • beforeMount: 开始挂载到页面

  • mounted: 挂载到页面 -- 显示出来

  • beforeUpdate: 开始更新

  • updated: 更新结束

  • beforeDestroy: 开始销毁

  • destroyed: 销毁结束

指令

  • v-text: innerText

  • v-html: innerHTML

  • v-show: css 的 display:none

  • v-if: 移除/添加元素

    • v-else; v-else-if;

  • v-for: 遍历

    • v-for="值 in/of 数组"

    • v-for="(值,序号) in/of 数组"

    • v-for="值 in 数字"

  • v-on: 事件 @

  • v-bind: 属性 :

  • v-model: 双向数据绑定, 用于获取表单元素的值

  • v-slot: 插槽, 一个预留位, 使用时通过标签内容传入

  • v-pre: 原样显示 {{}}

  • v-once: 一次性渲染

特殊属性

  • key: 配合 v-for 使用的 唯一标识属性, 提升数组变更后的重新渲染效率

  • ref: 把1个变量 和 DOM元素绑定在一起, 存储在 $refs

路由系统

Vue中提供的制作多页网站效果的技术

  • 原生开发中, 通过切换html文件来实现多个页面的效果 -- 整个网页切换

  • 现代化的开发方式, 流行局部切换

    • 原生相当于-- 笔记本电脑 -- 集成化高, 更换CPU 就必须更换整个电脑

    • 现代化的方式 -- 台式机 -- 模块化, 更换CPU 就只需要换CPU即可

      • 用最小的消耗 来实现内容的变换

    • 专业称呼: SPA -- Single Page Application 单页应用

      • 整个网站只有一个页面, 然后通过局部的切换来实现 多页 效果

路由总结

作用: 局部切换组件, 让我们的网页产生一种 多页的效果, 此技术称为 SPA - 单页应用

  • router-view: 一个占位符, 会根据路径 切换到 对应的组件

  • views: 路由切换的组件, 存放在这里

  • router/index.js : 路由配置

    • 配置 什么路径 对应 什么组件

    • 注意组件的加载方式

      • import: 适合使用频率高的组件

      • 箭头: 适合使用频率低的组件

  • router-link: 类似超链接 a 标签, 用来实现点击切换

    • 自带两个激活样式

    • router-link-active: 模糊

    • router-link-exact-active :精确

  • $router: 路由对象, 存储在 vue 实例中, 可以操作路由

  • 路由传参:

    • 旧: 通过 ? 来传递 to="/路径?参数=值&参数=值..."

      • 组件中如何读取参数的值?? $route.query

    • 新: 简化书写

      • 路径配置中, 需要用 : 来标识参数 path: '/路径/:参数/:参数'

      • 使用时 to="/路径/值/值"

      • 组件中如何读取参数的值?

        • 方式1: params $route.params

        • 方式2: 利用 props

          • 注意: 必须在配置文件中开启此功能 props:true

路由传参

配置文件

import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/HomeView.vue'
​
// 目前: 假设网站中有 100 个页面
// 做法1: 网站刚加载, 就把100个页面全加载完毕, 然后等着使用
//  - 优点: 用户进入不同页面, 瞬间切换
//  - 缺点: 首次进入网站, 要进行大量操作, 首次慢;  用户如果没浏览其他页面, 亏了
// 做法2: 网站刚加载, 就加载用户使用的, 其他页面等到用户使用时再加载
//  - 优点: 首次加载速度快
//  - 缺点: 每个新的页面浏览时, 都要临时加载
​
// 最佳方案: 先加载最常用的几个页面, 把不常用的用懒加载方式进行
​
// 非懒加载: 网站加载时, 默认自动加载 -- 适合最常用的几个页面
import JiaLe from '../views/JiaLe.vue'
import FeiFan from '../views/FeiFan.vue';
​
// 假设 yufei 和 yumeng 使用频率低 -- 改造成懒加载
// import YuFei from '../views/YuFei.vue';
// import YuMeng from '../views/YuMeng.vue';
​
// 总结: 根据页面的使用频率
// -- 频率高: 用 import
// -- 频率低: 懒加载  箭头函数方式
​
Vue.use(VueRouter)
// 配置文件: 配置 路径 和 组件的对应关系
const routes = [
  // vroute-named
  {
    // 路径声明时, 可以用 : 来指定参数
    path: '/dy/:type/:id',
    name: 'dy',
    // 小马的狂化状态: true
    props: true, // props功能: 开启,  默认是 false, 不开启
    component: () => import('../views/DY.vue'),
  },
  {
    path: '/douyu',
    name: 'douyu',
    component: () => import('../views/DouYu.vue'),
  },
​
  {
    // 路径必须带 / 开头
    // 区分大小写吗?? 代码非常严谨, 永远区分大小写
​
    path: '/yumeng',
    // 懒加载语法: 用箭头函数, 在被调用时才会临时引入
    component: () => import("../views/YuMeng.vue")
  },
  {
    meta: { title: "雨飞" },
    path: '/yufei',
    component: () => import("../views/YuFei.vue")
  },
  {
    path: '/feifan',
    component: FeiFan,
    meta: { title: "非凡" },
  },
  {
    // path: 路径, 
    path: "/jiale",
    // component: 组件
    component: JiaLe,
    // name属性:  为这个路由配置项起个名字, 后期调试时 找BUG用, 加不加都行
    name: '家乐',
    // meta: 固定的属性, 称为 元数据:  可以存放各种自定义的内容
    meta: {
      x: '12121',
      y: 433,
      suibian: true,
      title: "家乐"
    }
  },
​
  {
    path: '/',
    name: 'home',
    component: HomeView,
    meta: { title: "首页" }
  },
  {
    meta: { title: "关于" },
    path: '/about',
    name: 'about',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
  }
]
​
const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})
​
// 必须在 router 赋值后, 书写代码
// 路由守卫: 看大门的 -- 监听路由的各种跳转操作
​
// beforeEach: 路由前置守卫, 在路由跳转前触发
router.beforeEach((to, from, next) => {
  console.log('to:', to); // 到哪去
  console.log('from:', from); //从哪来
​
  // DOM中, 如何修改 标签的标题??
  document.title = to.meta.title
​
  //next: 放行, 允许路由继续运行
  next()
})
​
export default router
​

路由入门

<template>
  <div>
    <!-- 路由系统: 属于组件的高级应用方式 -->
    <!-- 希望: 能够根据路径 切换 页面上显示的组件 -->
    <!-- views文件夹: 专门存放 路由系统切换的组件 -->
​
    <!-- router-link: vue对 a 标签进行了封装, 进而得到了更加强大的 router-link-->
​
    <!-- 虽然最终呈现在页面上的是 a 标签, 但是其在切换路径时, 不会导致重新加载 -->
    <router-link to="/">首页</router-link>
    <router-link to="/about">关于</router-link>
    <router-link to="/jiale">家乐</router-link>
    <!-- 把 yumeng  yufei  也做成链接跳转 -->
    <router-link to="/yumeng">雨萌</router-link>
    <router-link to="/yufei">雨飞</router-link>
​
    <!-- 不要直接用a标签, 会导致页面重新加载 -->
    <a href="/feifan" target="_blank">非凡</a>
​
    <div id="box">
      <!-- router:路由 -- 通过路径 可以找到什么 -->
      <!-- router-view: 路由的占位符, 会根据具体的路径 替换成 对应的组件 -->
      <!-- 例如 localhost:8080/   会显示 HomeView 组件 -->
      <!-- localhost:8080/about  会显示 AboutView 组件 -->
      <router-view />
    </div>
  </div>
</template>
​
<script>
export default {};
</script>
​
<style lang="scss" scoped>
#box {
  background-color: orange;
  padding: 10px;
}
​
// router-link 最终表现出来的是 a 标签
// 所以直接给 a 标签添加样式即可
a {
  margin: 10px;
  display: inline-block;
  padding: 10px 20px;
  // 文本-修饰  即  上中下 3个线
  text-decoration: none;
  color: white;
  background-color: #666;
  opacity: 0.8;
  transition: 0.3s;
​
  &:hover {
    opacity: 1;
    border-radius: 4px;
  }
}
​
// router-link: 会自动为当前激活项添加class
​
// router-link-active: 模糊匹配
// 例如 路径是 /a/b/c , 与之模糊匹配的路径有4种  /  /a  /a/b   /a/b/c
// 即: 1个路径的 父级路径 都会匹配成激活状态 -- 一人得道鸡犬升天/株连九族
a.router-link-active {
  background-color: orange;
}
// 因为模糊匹配, 所有  /about 会导致 /  和 /about 都带上橘黄色背景
​
// router-link-exact-active
// exact: 精确的
a.router-link-exact-active {
  // background-color: orange;
}
// 因为精确匹配, 所以只有 /about 带有背景色
</style>

编程式跳转

<template>
  <div>
    <!-- 标签式跳转 -->
    <router-link to="/">Home</router-link>
    <router-link to="/about">About</router-link>
    <!-- 编程式跳转 -->
    <button @click="goMeng">3秒后,切换到雨萌</button>
​
    <!-- 占位符: 被替换成对应组件 -->
    <router-view />
  </div>
</template>
​
<script>
export default {
  methods: {
    goMeng() {
      var num = 3;
      console.log(this); // 在后台找到 router
​
      // 在 vue 对象中, 有很多 $ 开头的属性
      // Vue自带的属性, 都是 $ 开头, 一个标识, 用户看到就知道是系统的
      // $router: 就是 路由对象, 其中包含了路由相关的各种操作
​
      const a = setInterval(() => {
        num--;
        console.log("num:", num);
        // push: 推入一个新的页面
        if (num == 0) {
          this.$router.push("/yumeng");
          clearInterval(a);
        }
      }, 1000);
    },
  },
};
</script>
​
<style lang="scss" scoped>
</style>

当前路由信息

<template>
  <div>
    <!-- 需求: 在输入框中按回车, 切换到 /about 路径 -->
    <!-- 实际开发时, 往往会跳转到 搜索页面 -->
    <input type="text" placeholder="搜索..." @keyup.enter="goAbout" />
​
    <router-view />
  </div>
</template>
​
<script>
export default {
  methods: {
    goAbout() {
      // 编程式跳转: 如果重复跳转, 会报错 -- 避免重复导航到 当前路径
​
      // 解决方案: 在跳转之前, 添加判断, 当前路径 和 目标路径(要往哪里跳) 不相同再跳转
​
      // 当前路径信息的读取方式
      // $router.currentRoute
​
      // 由于 当前路径 信息经常被读取使用, 所以作者好心的提供了 $route 的属性, 用于快速读取路径配置信息
      console.log(this.$route);
​
      // 注意区分:
      // $router : 路由对象, 包含路由的所有信息 和 操作
      // $route : 当前路由信息, 属于 $router 的一部分
​
      console.log(this);
​
      // 如果当前路径的 path 和 要跳转的地址不同, 再跳转
      if (this.$route.path != "/about") {
        this.$router.push("/about");
      }
    },
  },
};
</script>
​
<style lang="scss" scoped>
</style>

路由传参

<template>
  <div>
    <!-- 路由参数 -->
    <div>
      <router-link to="/">首页</router-link>
      <!-- 路由传参借鉴了 GET传参的语法, 利用? 来间隔 路径?参数=值 -->
      <router-link to="/douyu?type=yz">颜值</router-link>
      <router-link to="/douyu?type=LOL">英雄联盟</router-link>
      <router-link to="/douyu?type=wzry">王者荣耀</router-link>
    </div>
​
    <router-view />
  </div>
</template>
​
<script>
export default {};
</script>
​
<style lang="scss" scoped>
a {
  margin: 10px;
  display: inline-block;
  text-decoration: none;
  color: black;
​
  &.router-link-exact-active {
    color: orange;
  }
}
</style>
<template>
  <div>
    <h2>斗鱼: type - {{ $route.query.type }}</h2>
​
    <template v-if="data">
      <!-- 从返回值中找 数组 进行遍历 -->
      <div
        class="cell"
        v-for="{ rid, roomName, roomSrc } in data.data.list"
        :key="rid"
      >
        <img :src="roomSrc" alt="" />
        <span>{{ roomName }}</span>
      </div>
    </template>
  </div>
</template>
​
<script>
// 安装axios模块: npm i axios vue-axios
// https://douyu.xin88.top/api/room/list?type=??
// 使用时, 需要读取路由参数 type, 然后拼接到路径里, 才能请求到对应数据
// 1. methods  -> getData -> 拼接出url -> axios 请求
// 2. data 中, 声明 data 属性, 存储数据
// 3. mounted中, 调用 getData
// 4. 遍历展示数据到页面,  有房间名和图片就行
import axios from "axios";
​
export default {
  data() {
    return {
      data: null,
    };
  },
  // mounted: 挂载, 组件显示在页面上时触发
  // 组件存活期间, 只会触发一次 -- 生出来时
  mounted() {
    // $route: 当前路由的相关配置信息
    console.log(this.$route);
    // 路由的参数存放在 query 属性里
​
    this.getData();
  },
​
  // 监听器: watch
  watch: {
    // 监听 路由的参数 type
    // 因为变量名不能有 点, 只能用字符串书写
    "$route.query.type"(to, from) {
      // 自带两个参数: 新值, 旧值
      console.log("新值to:", to);
      console.log("旧值from:", from);
      // 当变化时, 再次触发 getData 获取最新的数据
      this.getData();
    },
  },
​
  methods: {
    // getData: 必须触发才能执行
    getData() {
      // 接口的参数: 是服务器指定的, type属于固定参数
      const type = this.$route.query.type;
      const url = "https://douyu.xin88.top/api/room/list?type=" + type;
      console.log("url:", url);
​
      axios.get(url).then((res) => {
        console.log(res);
        this.data = res.data; // 本地数据 = 远程的
      });
    },
  },
};
</script>
​
<style lang="scss" scoped>
.cell {
  width: 250px;
  display: inline-flex;
  margin: 10px;
  flex-direction: column;
​
  img {
    width: 100%;
  }
}
</style>

路由传参

<template>
  <div>
    <router-link to="/">Home</router-link>
    <!-- ? 传参语法 结构复杂, 实际开发中有更好的方案 -->
    <router-link to="/dy/DNF/33">DNF</router-link>
    <!-- <router-link to="/dy?type=ecy&id=11">二次元</router-link> -->
    <router-link to="/dy/ecy/11">二次元</router-link>
    <router-link to="/dy/hpjy/22">和平精英</router-link>
    <!-- 路由配置 path:"/dy/:type/:id" -->
​
    <!-- 占位符 -->
    <router-view />
  </div>
</template>
​
<script>
export default {};
</script>
​
<style lang="scss" scoped>
a {
  margin: 10px;
  text-decoration: none;
​
  &.router-link-exact-active {
    color: orange;
  }
}
</style>
<template>
  <div>
    <h2>DY 欢迎您</h2>
    <p>type: {{ $route.params.type }}</p>
    <p>type: {{ type }}</p>
  </div>
</template>
​
<script>
export default {
  // 作者贴心的帮你 简化了 路由参数的读取方式 :  仅限于新式传参
  // props: 可以用来声明参数接收路由传参
  // 这个用 props 接收路由传参的功能, 必须手动开启 -- 配置文件  router/index.js
  props: ["type"],
​
  // watch: 监听器, 可以监听 vue的任意属性的变化
  watch: {
    // 通过 ? 传参 : 存储在query里
    // 通过新式传参 : 存储在params里
    $route(to, from) {
      console.log("to:", to);
      this.getData();
    },
  },
  mounted() {
    this.getData();
  },
  methods: {
    // 触发场景分两种:
    // 1. 页面首次展示
    // 2. 路由参数变化时
    getData() {
      // const type = this.$route.params.type;
​
      // 当使用 props 来接收参数, 只需要 this.type 就可以使用
      const url = "https://douyu.xin88.top/api/room/list?type=" + this.type;
​
      console.log("url:", url);
    },
  },
};
</script>
​
<style lang="scss" scoped>
</style>

meta 和 路由守卫

<template>
  <div>
    <router-link to="/">Home</router-link>
    <router-link to="/about">About</router-link>
    <router-link to="/jiale">家乐</router-link>
    <router-link to="/feifan">非凡</router-link>
    <router-link to="/yufei">雨飞</router-link>

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

<script>
export default {};
</script>

<style lang="scss" scoped>
a {
  margin: 10px;
  display: inline-block;
  text-decoration: none;

  &.router-link-exact-active {
    color: orange;
  }
}
</style>

作业

接口地址

http://www.codeboy.com:9999/data/product/list.php?kw=关键词

提示:

  • 一个组件 Products.vue

  • 配置路由 /products 跳转

  • 接收路由参数, 发请求, 展示出来

  • 关于图片: 需要手动拼接前缀域名才可见 http://www.codeboy.com:9999/

    • 拼接后的效果:http://www.codeboy.com:9999/img/product/md/xxxx.jpg

 

Vue07

复习

路由系统: 径的变化 让页面局部发生变更, 营造出一种页面切换的效果

  • SPA: Single Page Application 单页应用 -- 一个页面, 局部切换, 实现整个网站

  • router-view : 占位符

    • 例如: 用1本书之类 放在桌子上占位, 人来了以后 再换成真人

    • 根据路径的变化, 切换占位符对应的组件

  • views文件夹:

    • 专门存储配合路由使用的组件

  • router/index.js: 路由配置文件

    • path: 路径, 要求 / 开头

      • 新传参方式: 使用 : 来代表参数, 例如: /about/:title/:nid

    • props: 代表使用开启组件的 props 接收路由参数功能, 默认 false 不开启

    • component:组件

      • 普通加载: 在最上方 import, 然后再用

        • 适合使用频繁的组件, 例如 首页

      • 懒加载: 适合不频繁的页面组件

        • 利用 ()=> import() 使用时再临时调用箭头函数

    • name: 为这段路由关系起名 --- 调试用

    • meta: 元数据, 存放用户自定义的数据

  • 路由守卫:

    • 路由前置守卫:beforeEach 可以在路由跳转之前触发

      • to: 要前往的路由信息

      • from: 当前所在的路由信息

      • next: 放行, 调用next() 才能解开当前守卫拦截器, 让路由继续进行

  • router-link

    • 标签式跳转: 本质是对 a 标签进行了封装

    • <router-link to="/路径"

    • 特点:

      • 跳转时, 不会重载当前网页

      • 自带激活样式

        • router-link-active: 模糊匹配, 所有的父级路径全都带此样式

        • router-link-exact-active: 精确匹配, 只有当前项激活样式

  • 编程式跳转: 通过代码触发跳转

    • $router: 整个路由对象, 包含路由的所有操作 和 信息

      • push: 推入新的页面

    • $route: 为了便于用户使用, 把 $router 中 currentRoute 属性抽离出来

      • 当前路由的信息

  • 路由参数:

      • to='/路径?参数=值&参数=值'

      • 读取: $route.query.参数名

      • 到配置文件中配置: /路径/:a/:b/:c

      • to="/路径/11/哈/家乐"

      • 读取时

        • $route.params.a

        • 利用 props 读取

          • 到配置文件中启动此功能: props:true

          • props:['a', 'b', 'c']

  • watch

    • 监听器, 可以监听 vue 对象中所有属性的变化

      watch:{
          属性名(to, from){
              to: 变化后的值
              from: 之前的值
          }
      }

作业

<template>
  <div>
    <!-- 标签跳转 -->
    <router-link to="/products/apple">Apple</router-link>
    <router-link to="/products/戴尔">戴尔</router-link>
    <router-link to="/products/联想">联想</router-link>
​
    <!-- 占位符 -->
    <router-view />
  </div>
</template>
​
<script>
export default {};
</script>
​
<style lang="scss" scoped>
a {
  margin: 10px;
  display: inline-block;
  text-decoration: none;
  color: #333;
​
  &.router-link-exact-active {
    color: orangered;
  }
}
</style>

外部文件使用

<template>
  <div>
    <img src="./assets/logo.png" alt="" />
    <one-com />
    <two-com />
​
    <div class="danger">DANGER</div>
​
    <div>
      <p>家乐</p>
      <p>小马</p>
    </div>
  </div>
</template>
​
<script>
import OneCom from "./components/OneCom.vue";
import TwoCom from "./components/TwoCom.vue";
// 外部文件的使用
// JS CSS 图片
// 图片: 本地图片应该存储在 assets 目录下
// css: 存储在 assets 目录下
// JS: 放哪里都可以, 在 src 目录下即可
​
import my from "./suibian/my"; // .js 后缀名可以省略
​
export default {
  components: { OneCom, TwoCom },
  mounted() {
    my.show();
  },
};
</script>
​
<style lang="scss" scoped>
</style>

Vuex

Vuex: 全局状态共享

Vuex 是什么? | Vuex

打比方:

  • 以前: 小马单身 -- 钱都放在自己身上, 只能自己用

  • 现在: 小马有媳妇,有儿子 -- 钱就要存储到 家庭资金卡里, 给媳妇儿子 自己 一起用

多个页面 或 组件中共享的数据 存储在 共享的对象中: Vuex

  • store: 存储共享的数据

    • 使用时: $store.state.数据

  • 修改分两种方式:

    • 简单粗暴(不推荐,不安全): 直接修改 $store.state

    • 规矩(安全):

      • 必须在 mutations 中声明方法, 来进行允许的修改操作

      • 通过 commit 方法提出申请, 触发对应的修改操作

作者为了方便大家使用, 提供了简化的操作方式 -- 辅助函数

Vuex:

  • 简单: 应用方式极其容易, 因为 有 辅助函数 大杀器的存在

  • 难:

    • 团队合作时:你要照顾猪队友 -- 不会辅助函数 则原生的写法就偏难

      • $store.state.xxx

      • 或者 自己写 映射

    • 辅助函数的原理: 不需要掌握, 但是最好知道

vuex初步

<template>
  <div>
    <!-- Vuex: 全局状态管理 -- 组件间的数据共享 -->
    <!-- store文件夹: 仓库, 放共享数据 -->
    <three-com />
    <four-com />
    <hr />
    <button @click="addNum">num+1</button>
    <p>num: {{ $store.state.num }}</p>
    <p>uname: {{ $store.state.uname }}</p>
    <p>isLogin: {{ $store.state.isLogin }}</p>
  </div>
</template>
​
<script>
import FourCom from "./components/FourCom.vue";
import ThreeCom from "./components/ThreeCom.vue";
export default {
  components: { ThreeCom, FourCom },
  methods: {
    addNum() {
      // commit:提交
      // 向 $store 提交申请, 触发 名字是 numAdd1 的方法
      this.$store.commit("numAdd1");
      // 共享数据的修改, 必须保障安全 -- 必须提供专业渠道来修改
    },
  },
  mounted() {
    console.log(this); //查看其中有没有 store 这个仓库
​
    // this.$store.state
  },
};
</script>
​
<style lang="scss" scoped>
</style>
import Vue from 'vue'
import Vuex from 'vuex'
​
Vue.use(Vuex)
​
// 场景模拟:
// 小马把自己的工资上缴 家库, 全家人都能用
// 小马想给 女主播 刷礼物 -- 500元
// 做法1: 小马直接从 家库 中拿钱
// 如果启动严格模式: 则做法1 依然可以,但是会报错
//
// 做法2: 规矩 -- 要想修改共享的数据, 必须通过 指定的方法
​
export default new Vuex.Store({
  // 严格模式:
  strict: true,
  // state: 状态 -- 共享数据都存储在这里
  state: {
    num: 1,
    isLogin: true, //当前是否登录
    uname: '小马哥'
  },
  // 计算属性:
  getters: {
    // 通过已有的属性, 计算返回新的值
    status(state) {
      // 参数1: 固定的 state, 代表共享的数据项
      return state.isLogin ? '登录' : '未登录'
    }
  },
  // 变化: 在这个属性中, 书写用于修改 共享数据的方法
  mutations: {
    // 登录分 true 和 false 两种值, 则需要传参
    updateLogin(state, x) {
      state.isLogin = x
    },
    numAdd1(state) {
      // 参数1: 固定传入的,共享数据所在的对象
      state.num++
    }
  },
  actions: {
  },
  modules: {
  }
})

辅助函数原理

<template>
  <div>
    <!-- 传统写法 -->
    <div>num: {{ $store.state.num }}</div>
    <div>uname: {{ $store.state.uname }}</div>
    <div>isLogin: {{ $store.state.isLogin }}</div>
​
    <hr />
    <!-- 利用计算属性简化 -->
    <div>num: {{ num }}</div>
    <div>uname: {{ uname }}</div>
    <div>isLogin: {{ isLogin }}</div>
  </div>
</template>
​
<script>
// 把传入的数组, 转换成 对象类型
function mapState(arr) {
  var obj = {};
  // name是数组中每个元素的值
  for (let name of arr) {
    // 假设 name 是 num
    // 则下方代码:  obj.num = function (){ return this.$store.state.num }
    // 因为name是变量, 所以必须用中括号语法才能赋值
    obj[name] = function () {
      return this.$store.state[name];
    };
  }
  return obj;
}
​
console.log(mapState(["num", "uname", "isLogin"]));
​
export default {
  // 利用计算属性简化
  computed: {
    // 展开符 ...  破掉对象类型 {}
    // mapState: 自动帮你生成所有的函数
    ...mapState(["num", "uname", "isLogin"]),
​
    // 方法在使用时, 自动触发 不需要()
    // num: function () {
    //   return this.$store.state.num;
    // },
    // uname: function () {
    //   return this.$store.state.uname;
    // },
    // isLogin: function () {
    //   return this.$store.state.isLogin;
    // },
  },
};
</script>
​
<style lang="scss" scoped>
</style>

辅助函数

<template>
  <div>
    <!-- 传统写法 -->
    <div>num: {{ $store.state.num }}</div>
    <div>uname: {{ $store.state.uname }}</div>
    <div>isLogin: {{ $store.state.isLogin }}</div>
​
    <hr />
    <!-- 利用计算属性简化 -->
    <div>num: {{ num }}</div>
    <div>uname: {{ uname }}</div>
    <div>isLogin: {{ isLogin }}</div>
  </div>
</template>
​
<script>
// vuex: 提供了辅助函数 mapState, 帮你自动生成计算属性, 简化state的使用
import { mapState } from "vuex";
​
// 辅助函数的原理, 参考 App.4  : 感兴趣了解下
// 不感兴趣: 直接背语法即可 :  在 computed 中, 书写 ...mapState([名称, 名称])
​
export default {
  // 利用计算属性简化
  computed: {
    // 展开符 ...  破掉对象类型 {}
    // mapState: 自动帮你生成所有的函数
    ...mapState(["num", "uname", "isLogin"]),
​
    // 方法在使用时, 自动触发 不需要()
    // num: function () {
    //   return this.$store.state.num;
    // },
    // uname: function () {
    //   return this.$store.state.uname;
    // },
    // isLogin: function () {
    //   return this.$store.state.isLogin;
    // },
  },
};
</script>
​
<style lang="scss" scoped>
</style>
<template>
  <div>
    <p>登录状态: {{ $store.getters.status }}</p>
    <!-- commit的参数2, 会传递给 方法的参数2 -->
    <button @click="$store.commit('updateLogin', true)">登录</button>
    <!-- 发申请commit, 触发指定的事件 -->
    <button @click="updateLogin(false)">退出</button>
    <hr />
    <button @click="numAdd1">{{ $store.state.num }}</button>
  </div>
</template>
​
<script>
// 在使用vuex 的内容时, 分3种写法
// 1. 直接用 $store.state.xx   $store.commit(...)
// 2. 手动通过 计算属性 或 methods 来映射
// 3. 使用辅助函数 mapXxxx 来实现映射
​
// 辅助函数有些人不会
// 团队合作时很麻烦, 不一定你的队友会哪种, 所以你 都要会才能兼容别人!
​
import { mapMutations } from "vuex";
export default {
  methods: {
    // 作者提供了辅助函数, 帮你自动实现映射代码
    // 在数组中书写 要映射的mutations 中的方法名即可
    ...mapMutations(["numAdd1", "updateLogin"]),
    // numAdd1() {
    //   this.$store.commit("numAdd1");
    // },
    // updateLogin(x) {
    //   this.$store.commit("updateLogin", x);
    // },
  },
  mounted() {
    console.log(this); // 找到 getters.status
  },
};
</script>
​
<style lang="scss" scoped>
</style>

vuex总结

<template>
  <div>
    <div>count:{{ count }}</div>
    <div>num: {{ num }}</div>
    <div>age: {{ age }}</div>
    <button @click="countJian10">更新count</button>
    <button @click="numJia1">更新num</button>
    <button @click="ageJiaN(10)">更新age</button>
    <hr />
    <p>{{ age_db }}</p>
    <p>{{ num_db }}</p>
    <p>{{ count_db }}</p>
    <!-- double 双倍 db -->
  </div>
</template>

<script>
import { mapGetters, mapMutations, mapState } from "vuex";
export default {
  computed: {
    // 辅助函数, 把 state 中的值映射到组件中使用
    ...mapState(["count", "num", "age"]),
    // 自动触发的, 放计算属性
    ...mapGetters(["age_db", "num_db", "count_db"]),
  },
  // mutations: 属于主动触发的方法, 应该放在 methods里
  methods: {
    // 辅助函数: 把 mutations中的方法映射到组件里用
    ...mapMutations(["numJia1", "countJian10", "ageJiaN"]),
  },
};
</script>

<style lang="scss" scoped>
</style>
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)


export default new Vuex.Store({
  // 共享的数据存储在state里
  state: {
    count: 100,
    num: 1,
    age: 30
  },
  // 用于存储修改 state 中的值的方法
  mutations: {
    numJia1(state) {
      state.num += 1
    },
    countJian10(state) {
      state.count -= 10
    },
    ageJiaN(state, n) {
      state.age += n
    }
  },
  // 计算属性: 把 state 中的值 ,计算后返回一个新的值
  getters: {
    age_db(state) {
      return state.age * 2
    },
    num_db(state) {
      return state.num * 2
    },
    count_db(state) {
      return state.count * 2
    }
  }
})

项目阶段

FTP和 百度网盘都提供了项目资源, 选择你喜欢的方式下载 xin88.top

项目包准备

  • vue create xuezi-pro

  • 手动选择项目

  • 后续选项, 都直接回车

  • 项目包下, 安装axios

  • swiper: 轮播图第三方模块

    Vue2必须安装指定的版本才能兼容

    • swiper 必须是 5

    • vue-awesome-swiper 必须是 4

    package.json 中可以查看扩展库的版本

    npm install swiper@5.* vue-awesome-swiper@4.*

如果你无法安装: 则

  • 百度网盘提供的 vue-pro 已经安装过

  • FTP一会会上传包, 可以在FTP下载

Vue08

多开VSCode

网页制作

组件

大型网页: 通常要拆分成不同的组件, 然后分别进行开发

  • 如果是团队合作: 把模块分给不同的人去做

  • 组件放 : components

多页效果

路由系统: 通过路径的变化让局部页面切换

  • 配合路由使用的组件放: views

Swiper

官网: Swiper中文网-轮播图幻灯片js插件,H5页面前端开发

安装:

npm install swiper@5.* vue-awesome-swiper@4.*

API文档:

中文api - Swiper中文网

案例库:

vue-awesome-swiper | Homepage

鼠标悬停暂停

原理: 鼠标进入时 调用停止方法, 鼠标离开时调用 开始方法

  • 绑定两个方法给 swiper 组件

    • swiper是自定义组件, 默认无法绑定原生的事件方法

    • 但是: 可以通过事件修饰符 .native 来强制绑定原生事件

  • 利用 ref 把变量 和 swiper 组件绑定在一起

    • 通过 变量, 找到 swiper中的 autoplay 中的 相关方法进行调用

组件的复用

  • 当页面上有多个部分: 结构相同, 样式相同, 但是 值不同

    • 组件的复用: 重复使用组件, 通过传参的方式 传递不同的数据

思考题

服务器开发人员负责: 后端开发人员 -- JAVA/PHP

问题:

  • 我们目前项目中, 使用的图片托管服务器是: http://www.codeboy.com:9999/

  • 如果我们的项目在未来某个时间: 更换了托管服务器的地址

    • http://www.codegirl.com:9999/

    • 看看你现在的代码, 要修改多少个位置? -- Index.Vue 和 IndexFloor.vue 中都用过

    • 如何解决, 在修改的时候更加便捷??

解决:

  • 如果一个值在多个组件中都要使用 而且 以后可能会变化 -- 最好是共享存储和使用

  • 具体办法:

    • Vuex : 作为共享的仓库

今日内容总结

  • 首页内容特别多, 所以: 拆分成模块 开发

    • components 目录中, 新建

      • MyHeader.vue

      • MyFooter.vue

      其中书写了 HTML 和 CSS

    • App.vue中, 使用了 组件

  • 多页的制作: 网站拥有很多个页面 -- 首页 商品列表 商品详情 登录 注册...

    • 需要在点击不同的按钮时, 呈现出不同的页面, 所以我们采用了: 路由

    • views目录: 路由切换的组件存储在这里, Index.vue

    • 路由的配置: path:'/' 对应 Index.vue

    • 通配符:* ,就是404页面的制作, 所有没有设置过的路径, 都会进入小马哥关爱页面

  • axios

    • main.js中 优雅的全局注入了 axios

    • Index.vue 中 利用axios 请求了数据

  • swiper

    • 一款非常有名的 轮播图 第三方

    • 安装 -- 必须是固定的版本 才能适配 vue2

    • 集成 -- main.js 中书写固定代码

    • 使用: 只要按照官方文档, 填入指定的信息, 就能触发滚动栏的固定技能

      技能: 自动滚动; 特效; 循环滚动; 页面指示器; 上下页箭头...

      麻烦: 鼠标悬浮进入时, 需要暂停自动滚动

      • 事件修饰符:  native 强制给自定义组件绑定原生DOM事件

      <swiper :options="配置信息">
          <swiper-slide></swiper-slide>
          <swiper-slide></swiper-slide>
          <swiper-slide></swiper-slide>
          ...
      </swiper>
  • 楼层: 首页中有3个楼层, 他们的 页面结构 都一样, 只是具体展示的数据不一样

    • 所以: 通过组件实现模块化, 然后声明了 title 和 items 作为参数

    • 首页在使用 楼层组件时, 通过传递 不同的 标题 数据, 进而展现出不同的内容

  • 共享图片的基础路径

    • 多个组件中 公共的数据 应该放在 Vuex 中进行共享

作业

  • Products.vue 中声明 props:['kw'] 接收路由参数

  • 声明getData方法, 发送网络请求

    请求地址: http://www.codeboy.com:9999/data/product/list.php?kw=+关键词

  • mounted 和 watch 都要触发, 因为输入框变化后 点击搜索按钮, 路由参数会变化

  • 把请求数据展示到页面上

    利用 v-if 配合 v-else, 数据不存在时显示 loading图, 否则显示数据们

  • 数据的展示UI, 在原版网站的 js/products.js

  • 组件复用(选做): 需要在props里声明一个 变量p 来接收传参

    上图中的HTML代码作为 template 的根div

    需要局部私有的引入 css/products.css , 参考 Products.vue

    不会组件复用的同学, 可以把 上方的HTML 直接复制到 Products.vue 里, 用v-for遍历就行

  • 分页(超难挑战-选做)

    • 请求返回值中有 pageCount 属性代表页数, 通过遍历展示如下效果

      详见原版网站 http://www.codeboy.com:9999

      需要自己添加样式, 把 class从 pager 改成 pages, 因为原版 pager 的样式, 可能会干扰你的CSS

      请求参数增加

      http://www.codeboy.com:9999/data/product/list.php?pno=页数&kw=关键词

      默认请求页数1 的数据, 点击不同的页号, 请求对应页码的数据

Vue09

搜索栏路由传参流程

1个数据项, 对应1个组件

上午作业总结

分页

做项目分两个环节

  • 思路: 你要做什么?

    • 利用循环 遍历了 页数 pageCount, 生成了 页数

    • css样式美化

    • 页数操作

      • 点击后: 发送请求, 给服务器传递 pno 页数, 服务器返回对应页数的数据给你

        服务器是怎么实现分数数据查找的吗?? SQL的重点语句是 Limit

  • 代码: 熟练度 -- 大量练习

详情页

今日内容总结

  • 产品搜索页面

    • 路由参数读取

      • 路由配置文件中, 设置props:true , 路由组件中才能用props 来接收参数

      • 可选参数: 利用 ? 进行标识, path: '/路径/:参数?'

        • 对应: 输入框是空的, 一样可以回车跳转

      • props默认值语法

        props:{
          属性名:{
            type: String, // 类型
            default: '', // 默认值
          }
        }
      • 组件也可以循环遍历: 详见 Products.vue 里

        <ProductCell v-for="p in 数组"  :p = "p"/>
  • 商品详情

    • 通过 router-link to="/pd/lid" 实现跳转操作

Vue: 学会了 就能找到工作, 后续两个月的课程会非常轻松!

  • 只要把项目最终能够 独立的 不参考的 写出来, 麻雀虽小,五脏俱全

    最起码得 3-5遍

    以后 CSS 和 HTML 有 类似 Bootstrap 的框架, 可以直接用 -- 第四阶段的 ElementUI

作业

作业1(简单):

  • 通过路由前置守卫 和 路由的meta 属性, 实现切换到 首页 详情页 产品页, 显示不同的标题

作业2(难, 选做):

完成规格部分: 到返回值的 family 属性里找到他们

  • 遍历生成, 修改成router-link方式, 需要给 to属性动态绑定 lid, 实现规格的点击效果

    • 可能需要自己书写css样式

  • 自己书写样式, 带有激活状态

  • 通过判断数据的lid 和 当前页面的lid属性, 相同的带有激活样式

作业3(难, 选做)

  • 实现注册页面(Register.vue) 和 登录页面(Login.vue)的跳转操作

  • 在 views 里制作组件, 复制原版代码进入

  • 路由配置中, 配置路径的跳转

  • 在 头部组件中, 完成 登录注册 路径的配置, 实现 router-link 的跳转

超难操作:

登录注册页面带有自己的头, 需要配合路由的 meta 和 v-if 在 App.vue 里进行判断 $route.meta.隐藏头 , 来隐藏 My-Header

Vue10

学子商城制作流程已整理完毕, FTP下载 xuezi_all.pdf

更换标题栏图标

assets/img/favicon.ico 图片, 替换到 public 目录下, 删除旧的

ctrl+F5 清理缓存方式刷新页面

详情内的图片路径补充

登录注册

这两个页面特殊, 他们拥有自己的头

App.vue中, 要判断如果需要隐藏头部组件, 则隐藏头部

抽象: 为某些路由添加个性化配置, 例如隐藏头

  • meta: 用于设置个性化

登录状态问题

BUG: 刷新页面会导致登录状态被重置

  • 需要 第四阶段的 铭铭老师的 浏览器持久化技术来解决 --session

  • 单点登录: 铭铭的 session 和 token

提前下载 vue3-pro.zip 下午用

地址在 www.xin88.top

Vue3

  • 目前的主流开发版本: vue2

  • 目前正处于新旧交接的阶段: vue3 逐渐在流行

    • 每个月都会新增新的特性 -- 学习成本较高

与vue2相比, 采用TS 代替了 JS

TypeScript

是 微软公司 在JS基础上, 混合了大量的 JAVA 语言特征制作而成 : 混血生物

官网: TypeScript中文网 · TypeScript——JavaScript的超集

全局安装 typescript 编译器 : 安装与否不影响学习

 

安装后, 利用 tsc -v 可以查看版本

作用: TypeScript书写的代码 必须通过编译器 转换成 JS 代码才能运行在浏览器上

  • 类似于 小马说英语, 翻译器转换成中文 -> 家乐才能理解

权限词

Vue3项目包

  • 可以到FTP下载获取

  • 可以到百度网盘下载获取

  • 也可以自己生

生成命令: vue create vue3-pro

注意检查你要生成项目包的目录下, 是否有重名的文件夹, 可以通过改名规避

安装axios模块: npm i axios

vue3 不支持优雅方式, 只能单独引入

setup

<template>
  <!-- 快捷: v3ts -->
  <!-- vue3 和 vue2 的 html 代码部分几乎无差异 -->
  <!-- vue3 不再要求只有一个子元素 -->
  <div>
    <p>num:{{ num }}</p>
    <p>count:{{ count }}</p>
    <p>uname:{{ uname }}</p>
    <button @click="show">点我</button>
  </div>
  <!-- <div>二儿子</div>
  <div>三儿子</div> -->
</template>
​
<script lang="ts">
import { defineComponent } from "vue";
​
export default defineComponent({
  // setup: 就是 created 周期, 组件创建完毕时
  // vue3: 就是把 vue2 的data 改名成 setup
  setup() {
    console.log("setup: 组件创建完毕");
​
    // vue2: 要显示在页面上的数据, 必须存储在 data 属性里
    // vue3: 通过 setup的 return 返回到页面上展示
    return {
      num: 10,
      count: 20,
      uname: "小马",
      show() {
        alert("我会vue3!");
      },
    };
  },
​
  // vue2: methods  computed  watch
  //       方法      计算属性   监听器
  // 作者的想法: 不同功能的代码 存放在不同的对象里
  // 小马吐槽: 记不住
  // vue3: 全都放在 setup 里返回
  // setup: 就是 data methods  computed  watch  的集合体
​
  // data(){
  //   return {
  //     num: 10
  //   }
  // }
});
</script>
​
<style scoped>
</style>

变量

  • 变量不再自动更新DOM, 必须手动设定为更新DOM

  • 单个变量: ref()

    • JS中操作 ref的变量值, 必须用value

    • 在HTML中不需要, 作者对语法上做了简化

  • 对象类型: reactive()

    • 操作属性不需要 value, 因为是 Proxy代理模式实现的

  • 对象类型解构成单个变量: toRefs()

<template>
  <!-- v3ts -->
  <div>
    <div>num:{{ num }}</div>
    <button @click="addNum">num+1</button>
  </div>
</template>
​
<script lang="ts">
import { defineComponent, ref } from "vue";
​
export default defineComponent({
  setup() {
    // 网络喷子-小马哥: 老由, 每个data中的变量在变化时都会更新DOM元素
    // 每个变量在底层都做了相关的处理--监听器, 浪费性能!
​
    // 鱿鱼须: vue3 就改成手动添加监听器模式, 为想更新DOM的值手动加监听器
​
    // 小马: vue2中, 所有的东西都加载在全局的vue对象里, 导致对象臃肿
    // 鱿鱼须: vue3是按需引入, 不再有全局对象, vue3没有 this 关键词
    // -- 追求极致的性能 (废人)
​
    var num = ref(20);
    // 钢铁侠战甲(小马) : 把小马放到战甲中, 拥有了强大功能
    // ref(): 把 20 包装成 Ref 对象, 拥有了监听器等技能, 能自动更新DOM
    console.log("num:", num);
​
    var addNum = () => {
      // 必须修改 value属性, 才能触发更新操作
      num.value++;
      console.log(num);
    };
​
    return { num, addNum };
  },
});
</script>
​
<style scoped>
</style>
<template>
  <!-- v3ts -->
  <div>
    <button @click="data.num++">num: {{ data.num }}</button>
    <button @click="data.count++">count: {{ data.count }}</button>
    <button @click="data.age++">age: {{ data.age }}</button>
    <hr />
    <!-- 在 html中, 不需要用 .value 来修改值, 作者做了处理 -->
    <button @click="x++">x: {{ x }}</button>
    <!-- 更简单: -->
    <button @click="num++">num: {{ num }}</button>
  </div>
</template>
​
<script lang="ts">
import { defineComponent, reactive, ref, toRefs } from "vue";
​
export default defineComponent({
  setup() {
    // 喷子马: 单独修改一个个属性, 真麻烦
    // 鱿鱼须: 允许把属性放在对象中, 一起修改
    var data = reactive({ num: 1, count: 10, age: 30 });
​
    // 喷子马: 对象类型的值在使用时, 必须是 对象.属性名 来用, 太烦了!!
    //       我还是喜欢 ref 的直接使用方式
    // 鱿鱼须: 提供一个 toRefs, 帮你把对象展开, 类似解构
    console.log(toRefs(data));
​
    var x = ref(1000);
​
    // reactive: 利用了 ES6 的 Proxy 代理特性, 来为对象类型添加监听
    // Proxy是啥: 面试时可能会问 -- 面试前看老师的扩展视频
​
    // ... :展开符,  把data中转化出来的内容展开, 放到 return 里
    return { data, x, ...toRefs(data) };
  },
});
</script>
​
<style scoped>
</style>

计算属性和监听器

<template>
  <div>
    <button @click="num++">{{ num }}</button>
    <p>num翻倍: {{ num_2 }}</p>
  </div>
</template>
​
<script lang="ts">
import { computed, defineComponent, ref, watch } from "vue";
​
export default defineComponent({
  setup() {
    var num = ref(10);
​
    // watch(要监听的变量, (新值, 旧值)=>{})
    watch(num, (to, from) => {
      console.log("to:", to);
      console.log("from:", from);
    });
​
    return {
      num,
      // 计算属性: 值通过函数计算得到
      num_2: computed(() => num.value * 2),
    };
  },
});
</script>
​
<style scoped>
</style>

生命周期+axios

<template>
  <div v-if="data">
    <div v-for="{ title, nid } in data.data" :key="nid">
      {{ title }}
    </div>
  </div>
</template>
​
<script lang="ts">
import { defineComponent, onMounted, ref } from "vue";
​
// axios: 因为vue3取消了全局的vue对象 -- 过于臃肿, 没有this
// 导致axios 没有全局引入, 只能单独引入
// 前提要安装: npm i axios
import axios from "axios";
// axios是对象, 改一次就行, 其他组件使用时, 都是同一个对象
axios.defaults.baseURL = "http://www.codeboy.com:9999/mfresh/data/";
​
export default defineComponent({
  setup() {
    const data = ref(null);
​
    // 生命周期: 输入v3 看提示
    onMounted(() => {
      console.log("onMounted: 挂载完毕");
​
      const url = "news_select.php";
​
      axios.get(url).then((res) => {
        console.log(res);
​
        data.value = res.data;
      });
    });
​
    return { data };
  },
});
</script>
​
<style scoped>
</style>

vuex和路由

<template>
  <div></div>
</template>
​
<script lang="ts">
import { defineComponent } from "vue";
import { useRoute, useRouter } from "vue-router";
import { useStore } from "vuex";
​
export default defineComponent({
  setup() {
    // 目前vue3 不支持 map 辅助函数
    // vue2: this.$store
    // vue3: 必须用 useStore() 来获取 $store
    const $store = useStore();
    console.log($store);
​
    // $route
    const $route = useRoute();
    // $router
    const $router = useRouter();
​
    console.log($route);
    console.log($router);
​
    return {};
  },
});
</script>
​
<style scoped>
</style>

第三阶段

  • JS高级

    • 面试必考理论: 闭包 原型 声明提升 作用域...

    • ES5 ES6 的新语法

  • DOM

    • 经典理论, 作为前端开发程序员, 必须要知道

  • jQuery

    • 旧时代的王者: 通过封装 简化DOM操作的代码

  • Vue

    • 新时代的王者: 自动化思想

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值