PHP 学习之路:第十八天——Vue.js 学习之旅

205 阅读1分钟

Vue.js 官网:cn.vuejs.org/

一、Vue/jquery/es6 的区别

1. ES6

<h2 class="title">{{words}}</h2>
<script>
  // ES6
  document.querySelector(".title").textContent = "Hello World";
</script>

2. jQuery

<h2 class="title">{{words}}</h2>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
<script>
  // jQuery
  $(".title").text("Hello World");
</script>

3. Vue

<h2 class="title">{{words}}</h2>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
  // Vue.js
  const vm = new Vue({
    el: document.querySelector(".title"),
    data: {
      words: "HELLO WORLD",
    },
  });
</script>

二、挂载点/数据注入/响应式

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- 创建一个Vue的根节点,Vue实例的作用域 -->
<div class="app">
  <p>用户名: {{username}}</p>
  <p>{{username + ', php中文网讲师'}}</p>
  <p>37 + 43 = {{37 + 43}}</p>
  <p>{{flag ? '高兴' : '睡觉'}}</p>
</div>

<script>
  // 创建Vue实例
  const vm = new Vue({
    // 当前vue实例的配置
    // 1. 挂载点
    //    el: document.querySelector('.app'),
    el: ".app",
    // 2. 数据注入/绑定,注入到了当前vue实例中
    data: {
      username: "天蓬老师",
      flag: false,
    },
  });
  console.log(vm.$el);
  // 既然$data已注入到vue实例中, 那么就可能当成vue的属性输出
  console.log(vm.$data.username);
  console.log(vm.username);
  // 3. 响应式
  vm.username = "灭绝师妹";
</script>

三、v-text/v-once/v-html

<div class="app">
  <!-- <p>用户名: {{username}}</p> -->
  <!-- vue中的指令以v-为前缀 -->
  <!-- v-text : textContent -->
  <p>用户名: <span v-text="username"></span></p>
  <!-- v-html : innerHTML -->
  <p>用户名: <span v-html="username"></span></p>
  <!-- <p>用户名: <span v-once="username"></span></p> -->
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
  const vm = new Vue({
    el: ".app",
    data: {
      username: "小张老师",
    },
  });

  vm.username = '<em style="color:red">西门老师</em>';
</script>

四、属性指令:v-bind/v-on

<!-- <div class="app" style="color: red" onclick="alert(this.textContent)">hello</div> -->

<div class="app">
  <p v-bind:style="style1">{{site}}</p>
  <!-- v-bind: 可简单化为":", 为元素绑定普通属性 -->
  <p v-bind:style="style1, style2">{{site}}</p>
  <p v-bind:style="['background: yellow', 'color: red']">{{site}}</p>

  <!-- v-on: 添加事件指令 -->
  <!-- <p><a href="https://php.cn" v-on:click="show">网站名称-1</a></p> -->
  <!-- v-on: @ -->
  <!-- prevent: 修饰符, 防止默认行 -->
  <p onclick="console.log(this.tagName)"><a href="https://php.cn" @click.prevent="show">网站名称-1</a></p>
  <!-- stop: 防止冒泡 -->
  <p onclick="console.log(this.tagName)"><a href="https://php.cn" @click.prevent.stop="show">网站名称-1</a></p>
  <!-- 事件传参 -->
  <button @click.stop="calc($event, 40, 200)">计算40 + 200 = ?</button>
  {{result}}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
  const vm = new Vue({
    el: ".app",
    data: {
      site: "php中文网",
      style1: "color: red",
      style2: "background: yellow",
      result: 0,
    },
    // 事件方法
    methods: {
      show() {
        // this --> vue实例
        console.log(this.site);
        // debugger;

        // ev.preventDefault();
      },
      calc(ev, x, y) {
        this.result = `${x} + ${y} = ${x + y}`;
      },
    },
  });
</script>

五、双向绑定:v-model

<div class="app">
  <p>模型中的数据: {{site}}</p>
  <p>双向绑定<input type="text" :value="site" @input="site=$event.target.value" /></p>

  <!-- vue提供一个语法糖来快速实现这个功能: v-model  -->
  <hr />
  <!-- 实时更新绑定 -->
  <p>双向绑定/实时更新绑定<input type="text" :value="site" v-model="site" /></p>
  <hr />
  <!-- 延迟更新绑定, 修饰符: lazy -->
  <p>双向绑定/延迟更新绑定<input type="text" :value="site" v-model.lazy="site" /></p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<script>
  const vm = new Vue({
    el: ".app",
    data: {
      site: "php中文网",
    },
  });

  //   vm.site = "php.cn";
</script>

六、列表渲染:v-for

<!-- 挂载点 -->
<div class="app">
  <!-- 遍历数组 -->
  <ul>
    <!-- <li>{{items[0]}}</li>
<li>{{items[1]}}</li>
<li>{{items[2]}}</li> -->

    <!-- v-for : for of  -->
    <!-- :key 自定义键属性,主要是功能是干扰diff算法 -->
    <li v-for="(item,index) of items" :key="index">[ {{index}} ] => {{item}}</li>
  </ul>

  <!-- 遍历对象 -->
  <ul>
    <!-- prop: 字符属性, index: 数值属性 -->
    <li v-for="(item,prop, index) of user" :key="index">{{prop}}: {{index}} => {{item}}</li>
  </ul>

  <!-- 遍历对象数组 -->
  <ul>
    <li v-for="(user, index) of users" :key="user.id">{{user.id}}: {{user.name}} : [{{user.email}}]</li>
  </ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
  new Vue({
    el: ".app",
    data: {
      // 数组
      items: ["合肥", "苏州", "杭州"],
      //   对象
      user: {
        name: "朱老师",
        email: "laozhu@qq.com",
      },
      // 对象数组
      users: [
        { id: 1, name: "朱老师", email: "laozhu@qq.com" },
        { id: 2, name: "西门老师", email: "ximeng@qq.com" },
        { id: 3, name: "灭绝老师", email: "miejue@qq.com" },
      ],
    },
  });

  //   v-for : (循环变量, 索引/键) in/of 数组/对象   :key
  //   for - of
  //   for - in
  //   for , while
</script>

七、条件渲染:v-if/v-show

<div class="app">
  <!-- 单分支 -->
  <p v-if="flag">{{msg}}</p>
  <button @click="flag = !flag" v-text="tips = flag ? `隐藏`: `显示`"></button>

  <!-- 多分支 -->
  <p v-if="point > 1000 && point < 2000">{{grade[0]}}</p>
  <p v-else-if="point >= 2000 && point < 3000">{{grade[1]}}</p>
  <p v-else-if="point >= 3000 && point < 4000">{{grade[2]}}</p>
  <p v-else-if="point >= 4000">{{grade[3]}}</p>
  <p v-else>{{grade[4]}}</p>

  <!-- v-show: 元素依然在源码中,只是display:none  -->
  <p v-show="state">前端马上就要结束了</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<script>
  new Vue({
    el: ".app",
    data() {
      return {
        msg: "明晚我请大家吃饭,好吗?",
        flag: false,
        tips: "隐藏",
        grade: ["纸片会员", "木头会员", "铁皮会员", "金牌会员", "非会员"],
        point: 500,
        state: false,
      };
    },
  });
</script>

八、键盘修饰符:todolist

<div class="app">
  <!-- <input type="text" @keyup="submit($event)" /> -->
  <!-- enter: 回车的修饰符 -->
  <input type="text" @keyup.enter="submit($event)" />
  <ul>
    <li v-for="(item, index) of list" :key="index">{{item}}</li>
  </ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
  new Vue({
    el: ".app",
    data() {
      return {
        list: [],
      };
    },
    methods: {
      submit(ev) {
        // console.log(ev.key);
        // if (ev.key === "Enter") {
        //   this.list.unshift(ev.target.value);
        //   ev.target.value = null;
        // }

        this.list.unshift(ev.target.value);
        ev.target.value = null;
      },
    },
  });
</script>

九、计算属性

<div class="app">
  <p>数量: <input type="number" v-model="num" style="width: 5em" min="0" /></p>
  <p>单价: {{price}} 元</p>
  <!-- <p>金额: {{num * price}}</p> -->
  <!-- <p>金额: {{res}}</p> -->
  <p>金额: {{amount}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
  const vm = new Vue({
    el: ".app",
    data() {
      return {
        num: 0,
        price: 50,
        res: 0,
      };
    },
    // 计算属性
    computed: {
      amount: {
        get() {
          return this.num * this.price;
        },
        set(value) {
          console.log(value);
          if (value >= 2000) this.price = 40;
        },
      },
    },
    methods: {},
  });

  vm.amount = 2000;
</script>

十、侦听器属性

<div class="app">
  <p>数量: <input type="number" v-model="num" style="width: 5em" min="0" :max="max"/></p>
  <p>单价: {{price}} 元</p>
  <p>金额: {{res}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
  const vm = new Vue({
    el: ".app",
    data() {
      return {
        num: 0,
        price: 50,
        res: 0,
        max: 100,
  
      };
    },
    // 侦听器属性
    watch: {
      // 是用来监听某一个属性的值的变化,属性名要和data字段中的变量名相同
      // 例如,我要监听"num"变量的变化
      // num(新的值,原来的值)
      num(newVal, oldVal) {
        console.log(newVal,oldVal);
        // this.res = this.num * this.price;
        // this.num  --> newVal, this.price
        this.res = newVal * this.price;

        // 监听库存
        if (newVal >= 20) {
          this.max = newVal;
          alert('库存不足, 请进货')
        }
      }
    }
  });
</script>

十一、组件的创建与注册

<!-- 挂载点: 根组件 -->
<div id="root">
  <child-component></child-component>
</div>
<script>
  // 组件本质上就是一组自定义的标签,可以在html文档出现
  // 这个自定义的标签,需要用户自定义的功能,作用,行为

  // 创建
  //   const h2 = document.createElement("h2");
  //   h2.textContent = "hello vue.js";
  // 注册
  //   document.body.append(h2);

  // 组件是可复用的vue实例,
  // 1. 创建组件
  //   const childComponent = Vue.extend({
  //     template: `<h2> "hello vue.js"</h2> `,
  //   });
  // 2. 注册组件
  //   Vue.component("child-component", childComponent);

  //   二合一
  Vue.component("child-component", {
    template: `<h2>hello vue.js</h2>`,
  });

  // 3. 挂载组件
  new Vue({
    el: "#root",
  });

  // 注册(直接将创建合并到注册中) + 挂载
</script>

十二、全局组件

<div id="root">
  <button-inc></button-inc>
</div>

<div id="root1">
  <button-inc></button-inc>
</div>
<script>
  // 全局组件: 声明在Vue实例的外部,全局可见
  // 全局组件: 在多个Vue实例中均有效,所以它是全局的
  // 以后推荐不要用了, 尽可能用:局部组件
  Vue.component("button-inc", {
    template: `
          <div>
            <button @click="count++">点赞:   {{count}}</button>
        </div>
      `,
    data() {
      return {
        count: 0,
      };
    },
  });

  new Vue({
    el: "#root",
  });

  //   再创建一个vue实例,这个实例接管的是#root1的挂载点,根组件
  new Vue({
    el: "#root1",
  });
</script>

十三、局部组件

<!-- 挂载点 -->
<div id="root">
  <hello></hello>
</div>

<!-- <div id="root1">
  <hello></hello>
</div> -->

<template id="hello">
  <p>Hello {{site}}</p>
</template>

<script>
  const hello = {
    //   可以将大段的html字符串写到html文档的template标签中,并用id进行关联
    template: "#hello",
    data() {
      return {
        site: "php中文网",
      };
    },
  };

  // 局部组件必须声明在Vue的实例中
  new Vue({
    el: "#root",
    components: {
      //   可以同时声明多个局部组件
      //   hello: 组件名称,它的值是一个对象
      //   hello: {
      //     template: `<p>Hello {{site}}</p>`,
      //     data() {
      //       return {
      //         site: "php中文网",
      //       };
      //     },
      //   },
      // 当属性值变量与属性名相同时,且在同一个作用域中则可以省去值变量名称
      //   hello: hello,
      hello,
    },
  });

  //   new Vue({
  //     el: "#root1",
  //   });
</script>

十四、父组件与子组件传参

<div id="app">
  <btn-inc :my-name="username" :my-count="count"></btn-inc>
</div>

<script>
  // 父组件向子组件传参是通过自定义属性传参
  const vm = new Vue({
    el: document.querySelector("#app"),
    data() {
      return {
        username: "为中国女足喝彩!!!",
        count: 0,
      };
    },
    // 局部组件
    components: {
      btnInc: {
        props: ["myName", "myCount"],
        template: `
        <div>
            <button @click="num++">点赞:   {{num}}</button>
            <span>{{myName}}</span>
        </div>
            `,
        data() {
          return {
            num: this.myCount,
          };
        },
      },
    },
  });
</script>

十五、子组件与父组件传参

<div id="app">
  <btn-inc :my-name="username" :my-count="count" @click-count="handle"></btn-inc>
</div>

<script>
  // 子组件向父组件传参是通过声明一个"同名事件"来实现
  // 组件之间的传参,理论来说是"单向"的
  const vm = new Vue({
    el: document.querySelector("#app"),
    data() {
      return {
        username: "为中国女足喝彩!!!",
        count: 0,
      };
    },
    // 局部组件
    components: {
      btnInc: {
        props: ["myName", "myCount"],
        template: `
      <div>
          <button @click="$emit('click-count', 1)">点赞:   {{myCount}}</button>
          <span>{{myName}}</span>
      </div>
          `,
      },
    },

    methods: {
      handle(value) {
        console.log(value);
        this.count += value;
        this.username = "今天是我老猪的最后一课,不要忘了我";
      },
    },
  });

  // 总结 :
  // 1. 父组件 ---> 子组件:   自定义属性  :name, :count
  // 2. 子组件 ---> 父组件:   自定义事件方法:
  // 子组件: $emit('父组件中的自定义方法', '附加参数值')
  // 父组件: @父组件中的自定义方法="事件方法"
</script>

十六、基于锚点实现的路由模式

<!-- 选项卡 -->
<div class="container">
  <!-- 导航 -->
  <nav>
    <a href="#/list1">国际新闻</a>
    <a href="#/list2">中国新闻</a>
  </nav>

  <!-- 这里显示与导航对应的内容 -->
  <div class="route-view"></div>
</div>
<script>
  let list1 = `
  <ul>
      <li><a href="">美高官说出了美军撤离阿富汗的真正目的:与中国有关!</a></li>
      <li><a href="">美高官说出了美军撤离阿富汗的真正目的:与中国有关!</a></li>
      <li><a href="">美高官说出了美军撤离阿富汗的真正目的:与中国有关!</a></li>
    </ul>
    `;

  let list2 = `
  <ul>
      <li><a href="">多地猪肉价格跌破20元/斤 国家统计局:有望进一步回落</a></li>
      <li><a href="">多地猪肉价格跌破20元/斤 国家统计局:有望进一步回落</a></li>
      <li><a href="">多地猪肉价格跌破20元/斤 国家统计局:有望进一步回落</a></li>
    </ul>
    `;

  const routeView = document.querySelector(".route-view");
  //   console.log(window.location.href); // url信息

  //   hashchang: 监听url中的锚点的变化
  window.addEventListener("hashchange", show);
  //   window.addEventListener("load", show);
  //   推荐DOMContentLoaded 代替 load,因为它只要创建好dom树就可以触发了
  window.addEventListener("DOMContentLoaded", show);

  function show() {
    console.log(location.hash);
    switch (location.hash) {
      case "#/list1":
        routeView.innerHTML = list1;
        return;
      case "#/list2":
        routeView.innerHTML = list2;
        return;
      default:
        routeView.innerHTML = list1;
    }
  }
</script>

十七、vue 路由原理与实现

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>vue路由原理与实现</title>
    <link rel="stylesheet" href="style.css" />
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <!-- 路由模块加载 -->
    <script src="vue-router-dev/dist/vue-router.js"></script>
  </head>
  <body>
    <!-- 选项卡 -->
    <div class="container">
      <!-- 导航 -->
      <nav>
        <!-- router-link: 就是a标签 -->
        <!-- to : a 标签 中 的href  -->
        <router-link to="/list1">国际新闻</router-link>
        <router-link to="/list2">国内新闻</router-link>
      </nav>

      <!-- 这里显示与导航对应的内容 -->
      <router-view class="route-view"></router-view>
    </div>
    <script>
      let list1 = {
        template: `
      <ul>
          <li><a href="">美高官说出了美军撤离阿富汗的真正目的:与中国有关!</a></li>
          <li><a href="">美高官说出了美军撤离阿富汗的真正目的:与中国有关!</a></li>
          <li><a href="">美高官说出了美军撤离阿富汗的真正目的:与中国有关!</a></li>
      </ul>
        `,
      };

      let list2 = {
        template: `
      <ul>
          <li><a href="">多地猪肉价格跌破20元/斤 国家统计局:有望进一步回落</a></li>
          <li><a href="">多地猪肉价格跌破20元/斤 国家统计局:有望进一步回落</a></li>
          <li><a href="">多地猪肉价格跌破20元/斤 国家统计局:有望进一步回落</a></li>
      </ul>
        `,
      };

      // 1. 创建路由对象
      const router = new VueRouter({
        // 路由配置
        routes: [
          // 每一个路由参数都是一个对象,每一个对象对应着一个路由
          { path: "/list1", component: list1 },
          { path: "/list2", component: list2 },
        ],
      });

      // 2. 注册路由
      new Vue({
        // el: ".container",
        // 注册路由
        router,
      }).$mount(".container");
    </script>
  </body>
</html>

style.css 文件:

a {
  text-decoration: none;
  color: #555;
}
li {
  list-style: none;
}
ul {
  margin: 0;
  padding: 1em;
}
.container {
  width: 30em;
  display: flex;
  flex-flow: column nowrap;
}
nav {
  height: 2em;
  line-height: 2em;
  padding: 0 2em;
  background-color: skyblue;
  display: flex;
}
nav a {
  padding: 0 0.5em;
}

nav a:hover {
  background-color: green;
  color: white;
}

.route-view {
  background-color: #efefef;
}

.route-view ul li {
  line-height: 1.5em;
}
.route-view ul li a:hover {
  color: coral;
}